Replication code and Typst manuscript for Lemishko, Landi & Caicedo-Llano,
Cointegration-Based Strategies in Forex Pairs Trading (August 2026 rewrite;
frozen sample through 2025-12-31). Working-paper landing page:
SSRN. If the SSRN PDF lags this repository,
treat paper/main.typ / paper/main.pdf and the frozen parquet here as
authoritative for current numbers.
Please cite this repository via GitHub Cite this repository /
CITATION.cff.
Requires uv and Python ≥ 3.12.
git clone https://github.com/QuantLandi/fx-cointegration.git
cd fx-cointegration
uv syncFrozen prices are in data/fx_prices.parquet. Re-download only if you need a new freeze:
uv run python scripts/01_download_prices.pyFrom the repo root:
uv run python scripts/02_backtest.py --clear-panels # EG → outputs/coint/
uv run python scripts/02_backtest.py --simple --clear-panels # → outputs/simple/
uv run python scripts/03_compare_strategies.py # → outputs/compare/
uv run python scripts/04_portfolio_tables.py # → outputs/paper/tables|portfolio
uv run python scripts/05_plot_figures.py # → outputs/paper/figures/04_portfolio_tables.py also refreshes pair metrics.csv from existing panels
(including the {0,1,2,5} bp cost grid), so you need not re-run EG just for
metrics/portfolio tables.
Runtime: full 02–05 on a laptop is typically on the order of tens of
minutes (Engle–Granger screens dominate; --simple is faster). Steps 03–05
are quick once panels exist.
Daily Yahoo Finance FX spots, 2007-01-01 to 2025-12-31, seven USD crosses (AUD, CAD, CHF, EUR, GBP, JPY, NZD), all quoted as XXXUSD.
- For each undirected pair (21 = C(7,2), alphabetical legs) and rolling
train/test window, screen log prices with Engle–Granger at 5%: both OLS
orientations are tested; among passes, keep the clearer residual ADF
(more negative t-stat) and map the hedge into
log_1 − β·log_2. - If cointegrated, form that spread and standardize with train mean/SD for an OOS z-score.
- Trade when |z| exceeds a threshold (long spread if z < −z*, short if z > z*). Signals are lagged two days.
- Strategy return = signal × (r₁ − r₂) (equal notional, not β-hedged).
Annualized Sharpe uses all calendar days (flat days as 0), so volatility
is diluted when often out of market. Metrics
trades= days with nonzero signal, not round-trips. - Transaction costs: round-trip κ bp of pair notional charged as
(κ/1e4)·|Δsignal|/2(open/close = κ/2 each; flip = κ). Grid{0, 1, 2, 5}bp; headline κ = 2. Panels store gross returns; costs are applied when building metrics and portfolio tables. - Paper portfolio: sum the 21 pair daily returns, divide by 21. Main tables are unlevered. A companion table scales each strategy ex-post to 10% annualized vol so return/MDD levels are comparable (Sharpe unchanged). Cumulative-return figures scale the EG path to equal ex-post daily vol vs simple (visuals only). Sortino uses the std of strictly negative daily returns; Calmar = ann return / |max DD|.
Paper subset: train=257, test=21, z* ∈ {1, 2, 3}, 21 undirected pairs,
cost κ ∈ {0, 1, 2, 5} bp (baseline 2). Simple benchmark: --simple (no EG gate).
outputs/
coint/
metrics.csv (+ .meta.json) # rows × z × cost_bp; panels are gross
panels/{leg1}_{leg2}/ # prices, returns, spread, zscore, signal, …
simple/
metrics.csv (+ .meta.json)
panels/{leg1}_{leg2}/
compare/
metrics.csv # EG vs simple join (ΔSharpe), keyed by cost_bp
paper/
tables/ # unlevered / target-vol / cost-sensitivity CSVs
portfolio/ # daily / cumulative series at baseline κ=2
figures/ # fig01–fig05
Under EG, spread / zscore are NaN outside Engle–Granger-pass OOS blocks.
Under --simple, almost all OOS blocks are filled (NaN only before the first
window or if train std is zero). Strategy return panels are gross (zero cost);
flat days are 0. Net returns apply (κ/1e4)·|Δsignal|/2 in metrics and 04.
outputs/ and local/ are gitignored. Optional local checks (JAE table
tolerances, notebook spot checks) live under local/.
Manuscript: paper/main.typ (compiled PDF: paper/main.pdf; figures via
paper/figures, regenerated from outputs/paper/figures).
- Code, scripts,
pyproject.toml,uv.lock, anddata/: MIT - Manuscript assets under
paper/(Typst source, PDF, figures, bibliography): CC BY 4.0
The frozen parquet in data/ is a derived replication sample built from Yahoo
Finance FX spots; underlying Yahoo quotes remain subject to Yahoo’s terms of use.