Skip to content
Loading live market data…

Methodology

How the data is produced

Every step from raw venue data to a published signal — the sources, the indicator parameters, how labels are assigned, how models are validated, and where the pipeline is currently weak. Written from the implementation, not from an idealised description of it.

1. Sources

Two ingestion jobs write into one feature store. They are separate because their rate limits and cadences have nothing in common.

SourceCoversTimeframesCadence
Alpha VantageEUR/USD, GBP/USD, USD/JPY1dDaily, 22:15 UTC on trading days
KrakenBTC/USD, ETH/USD5m, 1hEvery 5 minutes

Crypto comes from Kraken, not Binance: Binance returns HTTP 451 from the hosting region, so those rows would never exist. Both venues are registered in the execution topology but disabled — see the infrastructure page.

2. Bar construction

Only closed bars are stored. The most recent candle a venue returns is dropped before any computation, because an incomplete bar trained on as though it were final leaks information that did not exist at decision time.

Forex bars carry volume: null rather than a fabricated number — forex has no single centralised tape, so there is no volume figure to report.

3. Indicators

Computed with the technicalindicators library at ingestion, over a 300-bar warmup window, and stored alongside the bar. A bar inside an indicator's warmup period stores null for it — never a back-filled estimate.

FieldIndicatorPeriod
rsi_14Relative Strength Index14
ema_20Exponential moving average20
ema_50Exponential moving average50
sma_200Simple moving average200
atr_14Average True Range14
macdMACD line12 / 26
macd_signalMACD signal line9
macd_histMACD histogram
bb_upperBollinger band, upper20, 2σ
bb_lowerBollinger band, lower20, 2σ

4. Derived features

Raw price level is deliberately not a feature. Close price is non-stationary — a model trained on 2008 EUR/USD levels learns nothing transferable to 2026. Everything below is a return or a ratio.

oc_return_pct

(close − open) / open × 100

Intrabar direction, scale-free.

hl_range_pct

(high − low) / close × 100

Realised volatility for the bar.

close_change_pct

pct_change(close) × 100

Bar-over-bar return.

volume_zscore

(volume − μ₅₀) / (σ₅₀ + 1e−9)

Unusual participation. Null for forex — see the limitations below.

5. Labelling

Each bar is labelled by the realised move over a configured horizon, against a configured threshold, into three classes:

ClassCondition
2 — uprealised move ≥ +threshold_pct
0 — downrealised move ≤ −threshold_pct
1 — flateverything between

The storedlabel column is direction-agnostic — it records only whether abs(move) ≥ threshold. Training on it directly would teach a model to predict volatility while presenting the output as a directional call. The training target is therefore rebuilt from the sign of the realised move, not from that column.

6. Validation

Expanding-window walk-forward, 5 folds, starting from the first 50% of the series. Each fold trains on everything before its test window and is evaluated only on data after it.

Random train/test splits are never used. Adjacent bars' indicators are autocorrelated, so a random split puts near-duplicate rows on both sides and reports a score the model cannot reproduce on unseen time.

Model: gradient-boosted trees (XGBoost), 300 estimators, max depth 4, learning rate 0.05, with balanced class weights — the flat class otherwise dominates and a model can score well by never making a call.

7. Promotion

A trained model enters the registry as candidate and is inert. It must be promoted explicitly through each stage; nothing self-promotes on a metric threshold.

StatusBehaviour
candidateRegistered, never evaluated live.
shadowPredictions computed and logged; nothing published.
liveRead by the inference service; may publish signals.
pausedWithdrawn from live. Reversible.

A live model is polled every 60 seconds. A prediction is published only if the class is directional and confidence clears 0.65; stop and target default to a symmetric 1:1 around the same threshold the label was built on.

Known limitations

Stated because a methodology page that lists only strengths is not a methodology page.

  • No gold in the feature store.

    Alpha Vantage's FX_DAILY endpoint rejects XAU — verified directly, it is not a documented currency there despite appearances. Gold has live pricing on the site but no daily OHLC history, so no gold model can be trained today.

  • volume_zscore is dead for forex.

    It is computed, but forex bars have no volume, so the feature is null for every forex row. It contributes nothing to those models and is retained only because crypto rows do populate it.

  • Crypto history is shallow.

    The Kraken job only stores a rolling recent window, so crypto has days of history against forex's 19 years. Crypto models are not currently trainable to the same standard.

  • Symmetric stops are a default, not a result.

    Stop and target are placed at ±threshold. ATR-scaled levels would very likely do better; this has not been tuned.

  • No model is live.

    The registry is functional and the promotion path works, but zero models are in the live state, so the pipeline is unproven in production.

Current coverage is on the infrastructure page; this data is queryable via the API.