1. Overview
This project produces rolling out-of-sample (OOS) directional forecasts for FX pairs across daily, weekly, hourly, and 4-hour bars. The core question is: given everything knowable at bar close t, can a non-linear classifier predict whether the next bar closes higher or lower than the current one?
The pipeline is intentionally conservative: all model selection decisions are made on data strictly before the OOS window, no future information is used in feature construction, and performance is measured only on held-out steps.
2. Data inputs
Market data
Daily FX OHLC bars are sourced from FXCM public candledata (bid prices), with Yahoo Finance fills appended for any missing tail through the current calendar date. This produces a continuous series from approximately 2000 onward for major pairs.
An optional synthetic "today" bar is injected after New York 5 PM close
(FXNL_INJECT_LIVE_QUOTE), allowing same-day forecast generation
before official bar close.
Macro fundamentals
A macro panel is drawn from the FRED API: rates, yields, spreads, and broad indices from 2000-01-01. The panel is built at daily frequency and forward-filled to align with FX bar dates. Key rationale: macro regime features provide cross-asset context that pure price-based models miss, particularly around rate differentials and yield curve dynamics.
Universe
The default universe covers 10 major FX pairs (G10). Extended sweeps additionally include non-FX instruments (equity indices, commodities) as context features or standalone forecast targets.
3. Feature panel construction
For each primary pair the modeling frame is assembled as follows:
- Merged panel — load primary pair OHLC and FRED macro columns into a joint date-indexed frame.
-
Cross-pair context — for each context pair in the universe,
join only its prefixed columns (e.g.
EURUSD_rsi_14). This brings in correlated pairs' momentum and volatility state without duplicating raw prices. -
Drop raw closes — all
*_closecolumns are removed from X. The model never sees the raw price level, only derived features. This prevents trivial level-based leakage. - Rolling volatility — GARCH-style rolling volatility columns are added per pair to give the model explicit regime context.
-
Label construction — binary target:
y = sign(close[t+lead] / close[t] − 1). Ternary variant adds a neutral band (±0.5% threshold). The forward returnforward_ris retained separately for strategy-level evaluation.
PCA pre-processing
Within each rolling OOS window the feature matrix is pre-processed identically to the backtest pipeline: median impute → StandardScaler → PCA (k=5 components) → re-scale. This reduces the effective feature space and mitigates multicollinearity from the high-dimensional technical/macro panel.
PCA variance analysis confirms that 5 components typically explain a meaningful share of cross-sectional variance in the merged technical + macro feature matrix, with marginal gains from additional components. The k=5 default is validated by a dedicated sweep over k ∈ {1, 2, 3, 5, 10, 15, 20}.
4. Rolling OOS backtest design
The core evaluation uses a rolling batch holdout scheme, not a simple train/test split. This is the most important methodological choice in the pipeline.
Default parameters
5. Model selection grid
Three model families are evaluated at each tune step under the default
faster grid. Larger grids (fast, all)
include expanded hyperparameters and optional XGBoost/LightGBM.
| Model | Key hyperparameters (faster) | Rationale |
|---|---|---|
| Logistic Regression | C=1.0 |
Strong baseline; calibrated probabilities; interpretable coefficients |
| Histogram Gradient Boosting | max_depth=4, max_iter=80, lr=0.1 |
Non-linear, fast on small panels, handles mixed-scale features well |
| Random Forest | max_depth=8, n_estimators=80 |
Ensemble diversity; robust to outliers; natural feature importance |
Selection criterion: highest accuracy on the 50-bar inner test.
The sticky-champion rule prevents unnecessary model churn: if the previous
batch's winner ties or beats all challengers on the new inner test, it is retained.
Parallel tuning (n_jobs=−1) is used throughout.
6. Sweep dimensions & artifacts
The full batch export sweep crosses four dimensions, producing a comprehensive OOS record across pairs, forecast horizons, target types, and return bases.
Output artifacts per sweep
run_summary— one row per run: OOS accuracy, lead, target, pair, chosen modelall_batches— per-batch model selection and inner-test scoresall_steps— per-step OOS predictions, correctness, and forward returnsper_run_model_stability— model family switching frequency per runmodel_stability_global— cross-run champion distribution- PDF summary — headline results and aggregated stability charts
7. Intraday extension (H1 / H4)
The same pipeline is extended to hourly (H1) and 4-hour (H4) bars. The design intentionally mirrors the daily setup: same PCA pipeline, same OOS model selection logic, same export artifacts. Key differences are the lookback and lead ranges, calibrated to the higher bar frequency.
| Timeframe | Lookback | Leads | PCA k |
|---|---|---|---|
| Daily | 1 000 bars | 1 – 10 | 5 |
| H4 | 2 500 bars | 1 – 8 | 5 |
| H1 | 3 000 bars | 1 – 12 | 5 |
Intraday bars are sourced from cached FXCM data with the same macro/technical feature merge applied to the intraday timestamps. Cross-pair context is joined at the same bar resolution.
8. Daily forecast report
The daily report pipeline filters sweep results through a quality gate before generating live predictions:
- Select only binary close-basis runs with no errors.
- Apply minimum OOS accuracy threshold (default ≥ 60%).
- Optional endorsed-run filter from a reviewed
qualified_models.json. - For each retained run: refit the latest chosen model on the trailing 1 000 bars, predict the newest eligible bar.
- Apply NY 5 PM session calendar rule to determine the correct data-through date.
- Output signal, direction probability, and confidence to the daily workbook.
This keeps the production signal set conservative: only models that have demonstrated sustained OOS accuracy are included in daily forecasts.
9. Limitations & open questions
- Transaction costs: the core backtest measures raw directional accuracy. Bid-ask spread and execution slippage are modelled separately in the stop-loss overlay but not embedded in the main OOS metric.
- Regime non-stationarity: rolling retraining mitigates but does not eliminate the risk that calibrated feature relationships break down in novel macro regimes (e.g. post-2022 rate cycle). Model stability tables help monitor this.
- Cross-pair correlation: pairs share macro features, so individual run OOS accuracy scores are not fully independent. Portfolio-level diversification calculations should account for this.
-
Binary framing: predicting direction, not magnitude. A correct
directional call on a 2-pip move and a 200-pip move are treated equally by the
accuracy metric. The
forward_rcolumns allow magnitude-weighted evaluation separately. -
k selection (PCA): k=5 is empirically validated but a fixed
global default. The variance sweep (
pca_variance_analysis.py) supports per-pair, per-timeframe tuning if needed.