Pinkfish is a lightweight Python backtester for daily OHLC research, portfolio notebooks, and simple strategy loops. FXMacroData fits into that workflow when the research question starts with daily FX reference rates rather than equities or ETFs.
fetch_fxmacrodata_timeseries() from pinkfish.fetch to request FXMacroData forex rows, convert them into Pinkfish-style open, high, low, close, adj_close, and volume columns, then run the strategy against that cached dataframe. This is useful for daily EUR/USD research, carry filters, simple trend tests, and sanity checks before moving to a heavier trading engine.
Pinkfish is deliberately direct: fetch data, add indicators, loop over rows, and call buy or sell at the price your rule chooses. FXMacroData adds the FX reference-rate input so the same style can be used for simple currency research.
Fit
Use this for
Daily FX reference-rate tests, notebook research, spreadsheet-style signal review, and small fixed-symbol experiments.
Choose Pinkfish when
You want a simple pandas-native loop and explicit open or close fills instead of a full event-driven simulation engine.
Avoid
Calling live APIs inside the trading loop or changing the data snapshot between optimization runs.
Why Pinkfish Works for Daily FX Tests
Pinkfish is strongest when the dataset is small, daily, and easy to inspect. Its repository describes it as a backtester and spreadsheet library for stocks, ETFs, portfolios, and swing-trading strategies. The distinguishing pattern is simple: the strategy decides whether to use the open, close, or another price from the current row.
That maps naturally to daily FX reference rates. The FXMacroData fetch helper reads the val field from the FXMacroData forex endpoint and copies it into Pinkfish's OHLC-style columns. The resulting dataframe behaves like a daily bar series even though the source is a reference-rate series.
| Pinkfish column | FXMacroData source | How to read it |
|---|---|---|
open, high, low, close |
val |
The daily FX reference value copied into OHLC fields for Pinkfish compatibility. |
adj_close |
val |
The same reference value, available for indicators that expect adjusted close. |
volume |
0 | A placeholder because reference-rate data is not exchange volume data. |
Visual Check: What Pinkfish Receives
Before running a strategy loop, plot the fetched dataframe. The chart below shows a fixed sample window of EUR/USD reference-rate rows in the same daily shape that Pinkfish receives from fetch_fxmacrodata_timeseries().
Pinkfish input series
EUR/USD reference-rate sample
Twenty official-source EUR/USD observations from 2026-06-10 through 2026-07-07, rendered as the dataframe input a Pinkfish notebook should inspect before a backtest.
Latest
1.1433
Rows
20
Window
-0.92%
Low
1.1340
The chart is a fixed article sample, not a live market panel. In a notebook, this is the same visual check to run after fetching and before evaluating any trade rule.
Takeaway: the first visual check is not performance. It is input sanity: dates are ordered, the window is fixed, and the reference-rate path looks plausible before any Pinkfish trade rule is evaluated.
Workflow Shape
1. Fetch
Request a fixed date range from FXMacroData and write it to the Pinkfish cache.
2. Prepare
Add indicators such as moving averages, rate-of-change, or macro filters to the dataframe.
3. Test
Loop over rows, trade at the selected reference price, and compare runs with the same cached input.
You can inspect the underlying REST call directly before using Pinkfish. Public examples should send the API key in the X-API-Key header:
curl -H "X-API-Key: YOUR_API_KEY" "https://api.fxmacrodata.com/v1/forex/EUR/USD?start_date=2024-01-01&end_date=2026-06-30&limit=5000"
Step 1: Install Pinkfish
Install Pinkfish from the open-source repository so the FXMacroData fetch helper is available:
git clone https://github.com/fja05680/pinkfish.git
cd pinkfish
python -m venv venv
source venv/bin/activate
pip install setuptools
python setup.py develop
On Windows, activate the environment with venv\Scripts\activate. If you already keep Pinkfish in a research environment, update that checkout before running the new helper.
Step 2: Fetch FXMacroData into Pinkfish
Use fetch_fxmacrodata_timeseries() with a fixed start and end date. Passing api_root is optional, but it makes the public API host explicit.
import os
from pinkfish.fetch import fetch_fxmacrodata_timeseries
eurusd = fetch_fxmacrodata_timeseries(
"EUR/USD",
"2024-01-01",
"2026-06-30",
api_key=os.environ["FXMD_API_KEY"],
api_root="https://api.fxmacrodata.com/v1",
use_cache=False,
)
print(eurusd.tail())
The helper accepts pair strings such as EUR/USD, EURUSD, EUR-USD, or EUR_USD. It normalizes the pair, writes a CSV cache under fxmacrodata-cache, and returns a pandas dataframe indexed by date.
For repeatable research, fetch once with use_cache=False, review the dataframe, then rerun with the cache enabled. That keeps parameter tests from quietly changing because the input window moved.
Step 3: Run a Small Strategy Loop
The snippet below shows the shape of a minimal Pinkfish strategy after the FXMacroData dataframe has been fetched. It adds a simple moving-average regime and trades the reference close when the regime turns positive or negative.
import datetime as dt
import pinkfish as pf
start = dt.datetime(2024, 1, 1)
end = dt.datetime(2026, 6, 30)
ts = pf.select_tradeperiod(eurusd, start, end, check_fields=["close"])
ts["regime"] = pf.CROSSOVER(ts, timeperiod_fast=20, timeperiod_slow=60)
ts, start = pf.finalize_timeseries(ts, start, dropna=True)
tlog = pf.TradeLog("EURUSD")
dbal = pf.DailyBal()
Then loop through the rows and let Pinkfish record trades and daily balances:
pf.TradeLog.cash = 100000
for i, row in enumerate(ts.itertuples()):
date = row.Index.to_pydatetime()
close = row.close
end_flag = pf.is_last_row(ts, i)
if tlog.shares == 0 and row.regime > 0:
tlog.buy(date, close)
elif tlog.shares != 0 and (row.regime < 0 or end_flag):
tlog.sell(date, close)
dbal.append(date, close)
Finally, inspect the logs and stats in the normal Pinkfish style:
trade_log = tlog.get_log()
daily_balance = dbal.get_log(trade_log)
stats = pf.stats(ts, trade_log, daily_balance, 100000)
print(trade_log.tail())
print(stats)
What to inspect first
- Do the first and last dates match the research window?
- Did the cache file contain the same number of rows across repeated runs?
- Does the strategy trade only after indicators have enough lookback history?
- Does the result still hold when the fast and slow windows are moved slightly?
Backtest Guardrails
Pinkfish makes it easy to test an idea quickly, which is exactly why the data discipline matters. Treat the FXMacroData fetch as an experiment input, not as a live dependency inside the strategy loop.
| Guardrail | Reason |
|---|---|
| Fetch before the backtest | The simulation should not depend on a network call for every parameter run. |
| Keep the cache with the notebook | A reviewer should be able to rerun the test with the same input rows. |
| Do not overread the OHLC fields | The helper maps a reference value into OHLC columns for compatibility. |
| Add macro context separately | Use the release calendar when the question is about event timing rather than daily reference direction. |
If the strategy starts to depend on intraday execution, changing symbol universes, broker events, or live order handling, Pinkfish may no longer be the right layer. Keep Pinkfish for compact daily research, then move the surviving idea into a more complete engine if the execution assumptions become more complex.
Related Links
- Open source FXMacroData integrations
- FXMacroData API quickstart
- FXMacroData MCP server
- FX sessions dashboard
Sources and References
- Pinkfish open-source repository
- Pinkfish fetch module
- Pinkfish FXMacroData fetch test
- FXMacroData OpenAPI schema
With that setup, Pinkfish becomes a fast notebook layer for testing daily FX reference-rate ideas. Fetch the data once, keep the cache stable, and use the result to decide whether the idea deserves a heavier research stack.