S004 (Stage 2): Relative-strength leadership with persistence filter
ID: S004
Slug: rs-leadership-rotation-v1-stage2
Failed at: Stage 2 (Walk-forward validation)
Fail reason: robustness
Date: 2026-05-30
Stage 1 reference: lab/quickkill/rs-leadership-rotation-v1/result.md (PASSED)
What we tested
Stage 1 ran the rule (top 10% by 6-month relative strength vs S&P 500, with at least 4 of the last 6 months also in that top 10%) on roughly the 500 largest US stocks. It cleared all five Stage 1 criteria with an average +3.78% excess return per three-month holding period.
Stage 2 is the next-level test the lab pipeline always applies before a strategy can become a real signal. It changes three things from Stage 1:
- Wider universe. We expand from ~500 large-cap stocks to a top-2,000-by-trading-volume pool. Smaller stocks behave differently — they're less liquid, less institutionally followed, and leader-effects there can be more fragile.
- Realistic trading costs. Stage 1 reported gross numbers. Here we subtract 25 basis points per trade round-trip (the typical bid-ask + slippage cost for liquid US stocks), which works out to roughly 1% per year of drag at the strategy's quarterly rebalance cadence.
- Out-of-sample year-by-year testing. Instead of measuring the whole 2010-2026 sample at once, we treat each year from 2019 onwards as an independent held-out test against the prior years used as the empirical basis. The strategy should work in most individual years, not just on average.
The pre-set criteria — frozen before the test ran — required an annualised net excess of +5% over the S&P 500, at least 6 of 8 test years positive, no single year worse than −5%, plus parameter and friction-robustness checks and a regime split (does the strategy work in both bull and defensive market environments?).
What we found
Strategy failed on 5 of 7 criteria.
| Criterion | We needed | We got | |
|---|---|---|---|
| Annualised net excess return | ≥ +5% | +4.14% | ✗ |
| Positive-net test years | ≥ 6 of 8 | 4 of 8 | ✗ |
| Worst single year | ≥ −5% | −10.5% (2021) | ✗ |
| Top-decile sweep: 5% / 10% / 20% all ≥ +3% net | yes | 5%=+5.7%, 10%=+4.6%, 20%=+2.6% | ✗ |
| Persistence sweep: 3/6, 4/6, 5/6 all ≥ +3% net | yes | 3/6=+4.0%, 4/6=+4.6%, 5/6=+4.7% | ✓ |
| Friction robustness at 40 bps: ≥ 6/8 folds positive | yes | 4 of 8 | ✗ |
| Regime split: positive in BOTH above- and below-SMA200 | yes | above=+2.5%, below=+7.7% | ✓ |
Year-by-year, the wider-universe story becomes clear:
| Year | Trades | Net excess return |
|---|---|---|
| 2019 | 805 | −0.7% |
| 2020 | 921 | +22.4% |
| 2021 | 865 | −10.5% |
| 2022 | 758 | +1.5% |
| 2023 | 732 | −0.1% |
| 2024 | 822 | −1.2% |
| 2025 | 813 | +8.3% |
| 2026 (YTD) | 167 | +17.3% |
The arithmetic average across years is +4.6%, but the geometric annualised return (which is what an investor actually compounds) is +4.1% — below the gate. The pre-set arithmetic-only summary would have over-stated the realised compound return.
Why this matters / what surprised us
The Stage 1 result was correct but narrow. The +3.78% Stage 1 mean on the 500-stock universe was real. Adding the next 1,500 mid-cap and small-cap names dilutes the effect, not amplifies it. This is consistent with the long-running literature finding that classical momentum works best in liquid, institutionally-followed names — exactly where it gets arbitraged the most, but also where the persistence-filter story (slow institutional rotation) actually holds.
One great year is not a strategy. Without 2020's +22% blowout, the annualised number drops to roughly +1% net excess — clearly not tradeable. The strategy's edge is concentrated in dispersion peaks (years when leaders sharply outperform laggards). When dispersion is average, the effect washes out below the friction line.
The 2020 → 2021 round-trip is the killer pattern. +22% in 2020, −11% in 2021. The same names that ran hardest in the COVID-recovery dispersion peak gave back materially in 2021's mean-reversion. A strategy that loads up on momentum leaders right before the regime flips is structurally exposed to this whip-saw. Stage 1's "no single year > 30% of total" criterion didn't catch this because Stage 1 measured contribution to a positive total; here the issue is a positive year followed by a negative year, both large.
Regime split passed (notably). Net excess was positive both above and below SPY's 200-day moving average — and actually stronger below (+7.7% on 1,144 trades vs +2.5% on 4,739 trades above). This is unusual; classical momentum is supposed to work better in trending bull markets. The Stage 2 result suggests the persistence-filtered version is more defensive than the standard strategy. Worth noting for the next iteration (see below), but the strategy fails other gates regardless.
Persistence sweep passed. All three persistence thresholds (3/6, 4/6, 5/6) produced similar net excess (+4.0% to +4.7%). The strategy is not knife-edge-sensitive on that parameter, which is a genuinely good sign — just not enough to compensate for the rest.
What this doesn't tell us yet
- A narrower-universe version may still work. Restricting to the top 200 (Stage 1's universe) preserves the edge, just at much lower capacity. That's its own setup to pre-register, not a rescue of this one.
- A 12-month relative-strength version wasn't tested. Some
academic momentum literature finds 12-month windows more robust
than 6-month. Could be pre-registered separately as
rs-leadership-12m-v1. - A "leaders + earnings-quality" overlay (CANSLIM-style) wasn't tested. Mechaniq already has a CANSLIM module concept archived; combining the two could produce a stronger signal than either alone. Would need its own gates and its own H-NNN.
What happens next
The strategy is failed at Stage 2. The S004 setup is closed.
For the strategy lineage, three follow-ups worth flagging (none of which are this setup — each would be a new pre-registration):
- H-XXX
rs-leadership-top200-only-v1: rerun on the SP500+NDX top-200 universe with the same Stage 2 friction + walk-forward discipline. Tests whether Stage 1's edge holds at lower capacity. - H-XXX
rs-leadership-12m-v1: 12-month relative-strength lookback instead of 6-month. Standard academic spec. - H-XXX
rs-leadership-with-quality-overlay-v1: combine RS leaders with an earnings-growth or accruals overlay (when Tiingo Fundamentals becomes available).
For the lab pipeline itself, the most useful methodological lesson: arithmetic-mean reporting can flatter a strategy that depends on one or two extreme years. Future Stage 2 gates should default to geometric annualised + per-fold consistency, which is what this gate did — and which is exactly what caught the issue.
For the specialist — methodology details (click to expand)
Test setup
- Universe: monthly point-in-time top-2,000 by trailing 60-day dollar volume, from the ~4,900-ticker Finnhub US Common Stocks candidate pool. Effective per-month membership: ~2,000; total distinct tickers across all months: 3,976.
- Data: Tiingo Power EOD, 2010-01-01 → 2026-05-29, 12.4M bars total. Backfilled extension to 2010 was run on 2026-05-30 (216s parallel ingest for ~4,900 tickers × 12 years).
- Signal generation: top-decile of 6-month relative strength vs
SPY at each month-end, with rolling 6-month persistence count
computed via
.shift(1).rolling(6).sum()on the boolean top-decile flag (ensures signal at month M uses only M−1 through M−6 rankings). - Forward measure: 3-month excess return =
(ticker_close[M+3] − ticker_close[M]) / ticker_close[M] − (spy_close[M+3] − spy_close[M]) / spy_close[M]. - Friction: 25 bps round-trip subtracted from each trade's
excess_3mto produceexcess_3m_net. Applied uniformly; not per-stock-spread modelled.
Walk-forward design
- Training basis: 2010 → 2018 (inclusive), used as the implicit empirical basis for the rule (the strategy parameters are not tuned; the train period is the regime where the rule was authored).
- Test folds: 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026-YTD (8 annual out-of-sample windows).
- No per-fold re-fit: the strategy is rules-based, so each fold is purely a held-out measurement, not a re-tuning step. This makes the walk-forward a stricter test than a parameter-tuning version would be — we cannot adjust to a fold's regime.
Pre-registered gates (FROZEN 2026-05-30, all required)
1. Annualised net excess (geometric across 8 folds) ≥ +5%
2. Positive-net folds ≥ 6/8
3. Worst fold net excess ≥ −5%
4. Top-decile parameter sweep: 5%, 10%, 20% all ≥ +3% net (per-fold avg)
5. Persistence parameter sweep: 3/6, 4/6, 5/6 all ≥ +3% net (per-fold avg)
6. Friction robustness at 40 bps: ≥ 6/8 folds still positive
7. Regime split: net excess positive in BOTH SPY-above-SMA200 and
SPY-below-SMA200 fold-subsets
Detailed numbers
- Primary run (top-10%, 4/6 persistence, 25 bps friction): 13,050 trades
- Geometric annualised net excess: +4.141%
- Arithmetic per-fold mean net excess: +4.617%
- Per-fold table: see "What we found" above
- 40 bps robustness: identical fold-sign pattern (4/8 positive), with the magnitudes shifted ~15 bps lower (about a 60 bps additional annual drag from the 15 bps round-trip increment, applied to ~4 rebalances/year)
- Regime split: above-SMA200 trades 11,014 (84.4% of sample), below-SMA200 trades 2,036 (15.6%); OOS subsets: above n=4,739 net +2.50%, below n=1,144 net +7.69%
Artifacts
- Trades:
lab/postmortem/rs-leadership-rotation-v1-stage2/trades_stage2.parquet(13,050 rows: ticker, signal_ym, signal_date, ret_3m, spy_ret_3m, excess_3m, excess_3m_net) - Summary JSON:
stage2_summary.jsonin same directory - Histogram bins:
chart_data.jsonin same directory - Walk-forward module:
src/lab/walkforward.py - Run script:
/tmp/run_rs_stage2.py - Pre-registered gate:
lab/setups/gates.md§rs-leadership-rotation-v1 Stage 2 - Original Stage 1 result (passed):
lab/quickkill/rs-leadership-rotation-v1/result.md
Stage 2 ran 2026-05-30. Pre-registered gates frozen earlier the same day before the test ran. No claims, gates, or methodology adjusted post-hoc.