Oracle
Verified backtest · sp500_momentum_126_2026

S&P 500 momentum,
walk-forward verified.

Real daily closes from the S&P 500 universe, 20192026. A 126-day momentum signal, rebalanced monthly, top-25 equal-weighted, with 5 bps round-trip cost. Walk-forward — no in-sample tuning leaks into the equity curve below.

Sharpe
1.70
Deflated Sharpe P
1.000
Annualized return
42.48%
Window
2019→2026
Headline metrics

The numbers, eight of them.

All figures computed from the equity curve below. No selection, no smoothing. Source: report.json.

Sharpe
1.70

Annualized · raw, before deflation

Annualized return
42.48%

Total 600.94% over the window

Annualized vol
24.94%

σ of monthly returns, annualized

Max drawdown
19.56%

Peak-to-trough on equity curve

Turnover
25.70×

25.70 portfolio rotations · 5 bps cost applied

Deflated Sharpe P
1.000

n_trials=1 · σ_SR=0.50

Skew
0.01

Of monthly returns

Kurtosis
2.09

Excess · monthly returns

Honest reading

What this is not.

Probability of Backtest Overfitting
0.557

Borderline — the in-sample winner is roughly coin-flip out of sample.

CSCV across 13 configs · 70 IS/OOS partitions · 35.00 aligned rebalances per config

Same engine, side-by-side
0.61 Sharpe

Survivorship correction; LightGBM model when available:

Momentum · survivors-only
Sharpe 1.73 · 22.48% AnnRet
Momentum · point-in-time S&P 500
Sharpe 1.11 · 14.74% AnnRet
LightGBM · trainer subset
Sharpe 1.41 · 16.06% AnnRet

Headline gap = “joined-after” forward bias in the survivors-only dataset. ML row, when shown, is on a smaller training universe — informative but not strictly apples-to-apples.

Brutal disclaimer — read once
  • ·Survivorship correction: the naive backtest Sharpe of 1.04 dropped to 0.71 once we re-ran on a point-in-time SP500 panel that includes every Wikipedia-removed name (683 symbols, 2.75M rows, 2008–2026). Pooled p(naive > PIT) = 0.0000 — survivorship inflation is statistically real. 0.71 is what the project actually expects net of survivorship.
  • ·These are backtest numbers, not live trading numbers. Live out-of-sample results are routinely lower than backtests for every strategy ever run.
  • ·Past performance does not predict future returns. Anyone who tells you a model can guarantee a profit on any specific stock tomorrow is selling you a lie.
  • ·This page is research output, not investment advice. No order routing, no real money, no recommendation to buy or sell any security. Treat it as a methodology demo.
  • ·The model is gradient-boosted decision trees (LightGBM). It is not “AI”, not a transformer, not a neural network. It outputs calibrated probabilities, not point price forecasts.
Equity curve

From $100,000 to $700,939.

66 monthly rebalance points · total return 532.44% · source: equity_curve.csv.

$748K$577K$406K$235K$64KNov 2020Dec 2021Jan 2023Feb 2024Mar 2025Apr 2026Initial · $100K
Things we killed
10 retractions

Strategies that looked promising in a single window, then failed the paired multi-seed gate or Hansen-SPA test. Each one is committed to git under the SHA below — verifiable, not a marketing list.

  • SEC 8-K LLM nowcasting on SP5002026-07-07TBD
    Retraction-first: gate scheduled 2026-07-14, verdict pending
    Gatepaired multi-seed on yfinance deep panel (test_days=21, top_k=20, LLM classifies 8-K filings via OpenRouter DeepSeek V4 Flash, min_confidence=0.7, lookback=5 trading days, hold=20 trading days)
    Result
    Why
  • Google Trends timing (pytrends interest spikes)2026-07-07TBD
    Retraction-first: gate scheduled 2026-07-14, verdict pending
    Gatepaired multi-seed on yfinance deep panel (test_days=21, top_k=20, z_threshold=2.0 over 30-day rolling window)
    Result
    Why
  • Chronos-Bolt-Base zero-shot as full replacement for momentum-1262026-07-07TBD
    Retraction-first: gate scheduled 2026-07-14, verdict pending
    Gatepaired multi-seed on yfinance deep panel (test_days=21, top_k=20, Chronos-Bolt-Base zero-shot forecast, 128d context / 20d horizon)
    Result
    Why
  • FinRL portfolio PPO (multi-seed)2026-07-07TBD
    Gate pending — Horikawa-Nakagawa 2024 predicts failure at ~85-90% probability
    Gatepaired multi-seed via scripts/challenger/finrl_significance.sh (env + PPO + 4-sub-test honest gate)
    ResultNot run yet. Sidecar shipped per honesty contract; result auto-appended when the gate runs.
    WhyHorikawa-Nakagawa (2024) shows portfolio-RL 'edge' is stat-arb overlay not risk reduction. Expected gate probability ~10-15%. Ship anyway; publish the retraction if it fails.
  • Sector momentum on 11 SPDR ETFs2026-06-30TBD
    Failed paired multi-seed gate vs SP500 momentum-126 2026-06-30
    Gatepaired multi-seed on yfinance 18.5-yr panel (test_days=21, top_k=5, 11-ETF universe)
    Result0/3 seeds win, pooled p(1-sided) = 1.0000 (extremely WRONG direction); sector Sharpe 0.717 (fixed — 11 ETFs no seed variation) vs SP500 momentum 0.94/1.29/0.90 — sector WORSE by 0.2-0.6 Sharpe per seed
    WhyAudit suggested sector momentum could lift +2-5pp annualized by reducing multiple-testing burden (11 ETFs vs 500 stocks → smaller DSR penalty). Reality: the 11-ETF universe is too narrow — top-5 picks have too little dispersion vs SP500's 500-name selection. The fewer-multiple-tests advantage doesn't compensate for the lost cross-sectional dispersion. Signal class kept for future ensemble experiments at higher top_k or with mean-reversion overlays.
  • Post-Earnings Announcement Drift (Bernard-Thomas)2026-06-25TBD
    Failed paired multi-seed gate vs momentum-126 baseline 2026-06-25
    Gatepaired multi-seed on yfinance deep panel (test_days=21, top_k=20, 12.5 yrs data, 23k earnings events)
    Result0/3 seeds win, pooled p(1-sided) = 0.9999 (WRONG direction, extremely significant); PEAD Sharpe 0.58/0.65/0.51 vs momentum 0.94/1.29/0.90 — PEAD WORSE per seed by 0.36-0.64
    WhyBall-Brown 1968 → Bernard-Thomas 1989 found PEAD lasts 30-60 days. The literature documented decay since 2010 and our 2014-2026 yfinance result confirms: at top-k=20 long-only with surprise+day0-confirmed filter, PEAD picks too few names too rarely vs an always-on momentum-126. The signal class (`PEADSignal`) stays in code so future variants (top-k=5, multi-horizon, short-side) can re-test.
  • Insider cluster-buying (SEC Form 4 P-codes)2026-06-25TBD
    Failed paired multi-seed gate vs momentum-126 baseline 2026-06-25
    Gatepaired multi-seed on yfinance deep panel (test_days=21, top_k=20, 8.5 yrs data, 586k insider tx)
    Result0/3 seeds win, pooled p(1-sided) = 0.9395 (WRONG direction); insider Sharpe 0.83/0.41/0.47 vs momentum 1.40/1.25/1.13 — insider WORSE per seed by 0.4-0.8
    WhyCohen-Malloy-Pomorski (2012) found real edge in cluster-buy signals in small/mid-cap names; in our SP500-only large-cap window the signal fires too rarely to compete with the always-on momentum signal. Real edge may exist with a wider universe or different threshold — wire stays in `signals.py` so future tuning can re-test.
  • Storm shield (SMA200 trend filter)2026-06-241284ee0
    Disabled on the live path 2026-06-24
    Gatepaired multi-seed on yfinance deep panel
    Result0/3 seeds win, pooled p(1-sided) = 0.9935 (WRONG DIRECTION); shield reduced Sharpe by ~0.33 each seed
    WhyA trend-following overlay should defend against bear-regime drawdown. On the corrected dataset it neither lifts Sharpe nor reduces drawdown materially. Wire stays in code (env-gated) so a future tuning can re-evaluate without re-implementing it.
  • LightGBM V2 (tuned Optuna 30-trial)2026-05-22e3d85fc
    Rolled back to V1 after failing vs V1 on multi-seed
    Gatepaired multi-seed: V2 vs V1 baseline
    ResultV2 lost 0/3 seeds; Optuna's in-sample tuning didn't generalise to held-out folds
    WhyHonest gate caught the selection-bias from a 30-trial hyperparameter search before live capital touched it.
  • FRED macro features2026-05-191137696
    Retracted after failing paired multi-seed robustness
    Gatepaired multi-seed (macro vs no-macro across seeds 42 / 7 / 99)
    ResultPooled p = 0.81, macro wins 1/3 seeds — single-window +43% Sharpe was selection bias
    WhyA single backtest window's improvement looked huge. The multi-seed gate caught that the +43% was a happy seed, not a real edge.
Reproducibility

The manifest. Click to copy.

No manifest, no publish. Same SHA + same data fingerprint reproduces these numbers exactly. Source: manifest.json.

Python
3.12.12
Pinned packages
7
Run created
What these numbers mean

Read the credibility contract.

A raw Sharpe of 1.70 on its own is not a claim. The number that matters is the Deflated Sharpe P-value — the probability that the true Sharpe is positive after correcting for selection bias across 1 candidate configs and the non-normality of returns (skew 0.01, kurtosis 2.09). For this run that probability is 1.000.

This is a momentum baseline — the null hypothesis any ML pipeline in this repo has to beat. It is what a 6-month price trend, equal- weighted across the top 25 names and rebalanced monthly with 5 bps transaction cost, actually returned on real S&P 500 daily closes. Walk-forward, no peeking. The full credibility contract — what is and is not used, how each mechanism prevents a specific way numbers get inflated — is in TRUST.md.