Date: 2026-08-25 Scope: Systematic search for tradeable anomalies across equity and crypto daily/intraday data, following the repository house protocol (fixed IS/OOS splits, Monte-Carlo gauntlet for winners, one ledger entry per idea adopted or rejected). Status: 1 validated edge (long-only momentum), ~15 rejected/retracted.
Abstract
We ran a multi-round research program testing ~20 trading-edge hypotheses across equities (S&P 500 constituents, sector ETFs, crypto) and intraday (SPY/QQQ 1-minute) data. Every originally "validated" edge was subsequently invalidated by methodological bugs — and in the final round we discovered the bugs were deeper than first thought:
- A timezone bug (§1.4): all 1-minute bars are in UTC; naive local handling fabricated a spurious intraday "reversal" (and corrupted all intraday ORB work). After correction, no intraday edge survives.
- A look-ahead bug (§1.5): daily cross-section studies gated an open-to-
close (or same-day) return with a signal derived from
close[t]. This inflated OOS Sharpe in four studies — mega-cap trend (3.65→1.06), sector rotation (3.30→0.97), crypto rotation (1.99→0.54), cross-asset momentum (3.19→1.04); all four RETRACTED. - A returns-construction bug (§1.6): the one study that survived the first two rounds — six-month cross-sectional momentum, reported OOS Sharpe 0.86–0.92 — actually used overlapping forward returns (a full H-day forward return booked as a daily return on every rebalance day, inflating the series ~H×) plus return clipping that masked the explosion. With a correct daily-rebalanced, look-ahead-free implementation the long-short momentum factor is flat/negative post-2015 (consistent with the well-documented post-2009 weakness of US equity momentum).
After all three corrections, built on a new test base (validation/framework.py
tests/test_research_framework.pywith automated guards), the only genuine edge is LONG-ONLY momentum — buy the top decile of trailing-return winners, equal-weight, monthly rebalance, no shorting. OOS (2016–2024) gross Sharpe 1.44–1.69, bootstrap p < 0.001, robust across 3–12 month lookbacks and consistent in-sample. Crucially, the edge survives realistic transaction costs: on the full delisting-inclusive ~500-name universe with next-open execution and a liquidity-tiered cost (5/25/50 bps by $-volume), the net OOS Sharpe stays 1.19–1.31 (flat 10 bps: 1.23–1.38; even 50 bps: 1.06–1.22), all with OOS bootstrap p = 0.000. The long-short variant and everything else remain rejected.
The dominant lesson: any daily or intraday result with Sharpe > ~2 should be assumed buggy until proven otherwise — and even a "clean-looking" Sharpe ~1 deserves a returns-construction audit, not just a look-ahead check.
1. Methodology
1.1 Data
- Equities (daily):
~500 _ADJ.parquetfiles (timestamp column +adj_close). Early studies used a PIT top-200 universe by 2007 liquidity; the final, decisive momentum study uses the full delisting-inclusive universe (all_ADJwith sufficient history) so dead stocks are included — excluding them biases the short leg (see §1.6). - Equities (intraday):
SPY_1Min,QQQ_1Min, 9 sector ETFs (UTC, 2020-01-01 → 2025-12-30). - Crypto (daily): gate60 universe, 2025-01-01 onward.
- VIX (intraday): unavailable from data provider (0 bars).
1.2 House protocol
- Fixed IS/OOS split chosen before testing (2025-01-01 crypto, 2016-01-01 equity, 2024-01-01 intraday).
- Monte-Carlo gauntlet for any winner (bootstrap
pon OOS mean return). - One ledger entry per idea, adopted or rejected, in
docs/ROADMAP.md. - Fee-free assumption for intraday (user-confirmed commission-free trading); cost-sensitive where relevant.
1.3 Statistics
- Sharpe:
mean(std) × √periods(365 intraday, 252 daily). - Bootstrap p: 10,000 resamples of OOS returns;
p = P(resample mean ≥ observed mean). Reject null (no edge) atp < 0.05. - Profit Factor (PF):
Σ wins / Σ losses.
1.4 Critical correction — timezone
All 1-minute bars are stored in UTC. Early intraday studies used
pd.to_datetime(...).dt.tz_localize(None), which left timestamps as naive UTC.
Filtering hour == 9 then captured 4:00 AM EST (pre-market), not the
9:30 AM ET open. This produced a fictitious "reversal" with p = 0.002.
Fix: dt.tz_convert("America/New_York") before any session windowing.
After correction, every intraday effect failed OOS. All intraday reports
written before the fix are retracted.
1.5 Critical correction — look-ahead in daily cross-section studies
equity_mega_momentum.py and equity_sector_rotation.py originally defined
the trend gate as close[t] > SMA200[t] & ROC60[t] > 0 and gated the
open-to-close return close[t]/open[t] − 1 of the same day. Since the
gate depends on close[t] (available only at the day's end), using it to
select that day's open-to-close trade is look-ahead bias. The original
OOS Sharpe (3.65 and 3.30) were artifacts of this. Fix: shift the signal
by one day (gate = gate.shift(1)) and use close-to-close returns, matching
the SPY benchmark. After correction both studies fail OOS (§2.2, §2.3).
Any study reporting daily Sharpe > ~2 should be suspected of this bug.
1.6 Critical correction — overlapping forward returns & return clipping
The long-short momentum study (equity_momentum_monthly.py) survived §1.4 and
§1.5 but was still wrong in two subtle ways that artificially created a
"validated" edge:
- Overlapping forward returns. Its daily portfolio return was the H-day
forward return
close[t+H]/close[t] − 1booked on day t. With a daily-rebalanced portfolio this means H overlapping positions are each credited with a full H-day return, inflating the series by ~H×. This alone turns a modest factor into a Sharpe-20 monster (confirmed when the corrected framework first produced Sharpe 24–45 on the same data). - Return clipping masking the explosion. The portfolio return was clipped to [−0.1, 0.1] and forward returns to [−0.5, 0.5], which bounded the inflation enough to land at a "plausible" Sharpe 0.86–0.92 — hiding the bug rather than removing it.
Correct construction (validation/framework.py::cross_section_portfolio):
- daily return = close-to-close
pct_change(1)of the held names; - position weights set every
holddays from a signal shifted bylag ≥ 1, then sticky (forward-filled) forholddays — no forward return is ever reused; - dead/delisted names excluded from ranking and weights (renormalized so a dead name never silently zeros the opposite leg);
- an automated
assert_no_lookaheadguard perturbs future closes and asserts past returns are unchanged.
With this, long-short 6-month momentum is flat/negative post-2015 (OOS Sharpe 0.15–0.44, not significant) — matching the academic finding that US equity momentum weakened after 2009. The long-only "buy winners" version, however, is a strong, robust edge (§2.1).
2. Validated Edges (PASS)
2.1 Long-Only Cross-Sectional Momentum ★ the one genuine edge
-
Setup (clean,
validation/framework.py): full_ADJuniverse (~500 equities, including delisted — no survivorship bias), rank by trailing L-day return, hold top decile long-only (no shorting), equal weight, rebalanced every H days,lag = 1. IS/OOS 2016-01-01. -
Result (OOS 2016–2024):
L(d) H(d) IS Sharpe OOS Sharpe OOS p Verdict 63 21 1.19 1.54 0.000 PASS 126 21 1.04 1.69 0.000 PASS 189 21 0.95 1.60 0.000 PASS 252 21 0.81 1.59 0.000 PASS 126 63 0.85 1.67 0.000 PASS 252 63 0.72 1.44 0.000 PASS -
Insight: The edge is the long-only "buy past winners" effect. The long-short version (top minus bottom decile) is flat/negative post-2015 (OOS Sharpe 0.15–0.44, p 0.12–0.34 — REJECT), because the short leg stopped working in the post-2009 bull market. Dropping the short leg leaves a robust, tradeable long-equity portfolio with no leverage or shorting required. Robust across 3–12 month lookbacks and consistent in-sample (2007–2015).
-
Adoptability (liquid subset): on the top-100 names by listing persistence (a tradeable large-cap proxy) the long-only edge still gives OOS Sharpe 1.25, bootstrap p 0.0008 (ret +1065%, maxDD −41%) — so it is not an artifact of illiquid small-caps and is production-viable.
-
Realistic-cost validation (full universe, next-open fills): the discovery numbers above are gross / same-day-fill (slightly optimistic).
validation/momentum_realistic.pyre-runs the long-only top-10 of ~500 names with correct execution timing (trade after the signal, earn from next day) and turnover costs on a sensitivity grid + a liquidity-tiered cost. Turnover is heavy (~85% of gross per monthly rebalance ≈ 10×/yr notional) but the edge is so large it absorbs it:L(d) H(d) Gross Net@10 Net@25 Net@50 Tiered OOS Net@10 p OOS Tiered p 63 21 1.40 1.34 1.27 1.14 1.27 0.000 0.000 126 21 1.40 1.37 1.31 1.22 1.31 0.000 0.000 189 21 1.32 1.29 1.25 1.17 1.25 0.000 0.000 252 21 1.25 1.23 1.19 1.13 1.19 0.000 0.000 126 63 1.30 1.28 1.25 1.20 1.25 0.000 0.000 252 63 1.12 1.11 1.09 1.06 1.09 0.000 0.000 A true top-decile run (top_k=50 of 501) gives net@10bps Sharpe 1.06 (OOS p 0.001) and tiered 0.98 (OOS p 0.001) — still decisively positive. Verdict: deployment-grade. Even the harshest stress (full universe, 50 bps, tiered) keeps net Sharpe ≥ 1.0 at p ≤ 0.001; the production sleeve runs on a lighter 101-name liquid universe, so its drag is lower still.
-
Report:
reports/momentum_spectrum.md,reports/momentum_liquid.md,reports/momentum_realistic.md
Retraction note: the earlier "six-month momentum OOS Sharpe 0.86–0.92" (
equity_momentum_monthly.py, §2.1 in prior drafts) was a measurement artifact of the §1.6 bugs, not a real edge. It is superseded by this long-only result.
(superseded) Six-Month Long-Short Momentum — RETRACTED (returns-construction)
-
Original (buggy): PIT top-200, long-short top−bottom decile, OOS Sharpe 0.86–0.92,
p ≤ 0.011. -
Corrected (no overlapping returns, delisting-inclusive universe): the long-short factor is flat/negative post-2015 (OOS Sharpe 0.15–0.44, not significant). The original Sharpe was produced by overlapping forward returns
- return clipping (§1.6). Verdict: REJECT — but the long-only variant (above) is the genuine, surviving edge.
-
Report:
reports/equity_momentum_monthly.mdSignal IS Sharpe OOS Sharpe OOS p Verdict mom6 (6m) 0.87 0.86 0.011 PASS mom6_2_7 (2–7m) 0.10 0.92 0.007 PASS mom12 (12m) 1.79 0.47 0.102 REJECT mom12_1_13 1.49 0.38 0.153 REJECT -
Insight: The factor zoo tested momentum on next-day returns and found nothing; with a 21-day holding period the edge emerges. 12-month momentum is crash-sensitive (2009/2020 rebounds hurt it) but intermediate 6-month momentum is robust in both IS (incl. 2008 GFC) and OOS.
-
Report:
reports/equity_momentum_monthly.md
2.2 Mega-Cap Trend Ranking ★ RETRACTED (look-ahead)
- Original (buggy): Top-50 PIT universe, rank by SMA200-distance, hold
top-k. Reported OOS Sharpe 3.65 vs SPY 0.96,
p = 0.000. - Corrected (no look-ahead): with signal shifted 1 day and close-to-close
returns, top-10 OOS Sharpe falls to 1.06 vs SPY 0.96, bootstrap
p = 0.161(not significant). IS top-5 actually loses (−6%, Sharpe 0.11). Verdict: REJECT — the original edge was a look-ahead artifact (§1.5). - Report:
reports/equity_mega_momentum.md
2.3 Sector Rotation ★ RETRACTED (look-ahead)
- Original (buggy): Top-3 GICS sectors by median SMA200-distance. Reported
OOS Sharpe 3.30 vs all-sector EW ~2.80,
p = 0.000. - Corrected (no look-ahead): top-3 OOS Sharpe 0.97 vs SPY 0.95,
bootstrap
p = 0.351(not significant). IS underperforms SPY (Sharpe 0.19 vs 0.41). Verdict: REJECT — look-ahead artifact (§1.5). - Report:
reports/equity_sector_rotation.md
2.4 Cross-Asset Crypto Rotation ★ RETRACTED (look-ahead)
- Original (buggy): Top-2 crypto by SMA200-distance. OOS Sharpe 1.99
vs EW 1.67,
p = 0.024. - Corrected (no look-ahead): with gate and score shifted 1 day, top-2 OOS
Sharpe falls to 0.54 vs EW 0.37, bootstrap
p = 0.241(not significant); top-3 OOS Sharpe 0.42,p = 0.403. The original edge was a look-ahead artifact (§1.5). Verdict: REJECT. - Report:
reports/cross_section_rotation.md
2.5 Cross-Asset Momentum ★ RETRACTED (look-ahead)
- Original (buggy): Multi-asset (stocks+crypto+SPY) momentum, top-5 OOS
Sharpe 3.19 vs SPY 0.53,
p = 0.000. - Corrected (no look-ahead): with signal shifted 1 day + close-to-close
returns, top-5 OOS Sharpe falls to 1.04 vs SPY 0.96, bootstrap
p = 0.084(not significant). The original edge was a look-ahead artifact (§1.5). Verdict: REJECT — and it never added alpha beyond equities anyway. - Report:
reports/cross_asset_momentum.md
3. Rejected Hypotheses (REJECT)
3.1 Intraday Opening Range Breakout (ORB)
- Symmetric (long up + short down): OOS Sharpe 0.11,
p = 0.437. Down-breakouts lose (price reverses up after a breakdown — bull-market dip-buying). - Long-only up-breakout: OOS annualized Sharpe 0.80–0.97 (baseline 0.80,
+wide-ORB 0.97, +gap-up 0.87) — a real economic signal, but daily
Sharpe only 0.042 with kurtosis 36.5 (one +8.2% outlier day) → bootstrap
p ≈ 0.20, normalp ≈ 0.40. Not statistically robust on 2y OOS. - "Clean" breakouts show Sharpe 18.7 but are not tradeable (only knowable at end of day).
- Verdict: Not adoptable standalone. Candidate satellite or overlay on a stronger edge.
- Reports:
reports/intraday_orb.md,reports/intraday_orb_v2.md
3.2 Equity Factor Zoo — Round 2 (orthogonal factors, beta-audited)
A second, more rigorous hunt (validation/edge_hunt2.py +
validation/edge_audit.py) tested factors deliberately orthogonal to
momentum on the audited framework (lag≥1, next-open, dead-stock-safe),
IS/OOS 2016-01-01. The long-only versions posted tempting gross OOS Sharpe
(ST-reversal 1.11–1.17, low-vol 0.99–1.14, liquidity-trend 1.35, all p≤0.003) —
but a beta/residual audit exposed them as pure equity beta: every factor has
market-neutral residual Sharpe ≈ 0 and Beta ≈ 1, and the long-short
existence test is negative everywhere (ST-reversal LS −0.05, low-vol LS
−1.07, liquidity-trend LS −0.09; Momentum LS 0.10). There is no new
independent, deployable factor here — these are the same long-equity beta as
momentum, just re-labeled. Earlier 1-day factor-zoo results (rev1m/w,
high52, size) also REJECT; vol60/beta60 "PASS" only as risk premiums.
- Reports:
reports/edge_hunt2.md,reports/edge_audit.md
3.3 Low-Volatility Anomaly
- Round 2: long-only low-vol decile gross OOS Sharpe 0.99–1.14 (p 0.001–0.003) — but residual Sharpe ≈ 0 (Beta 0.6) and the long-short version loses (OOS Sharpe −1.07). Classic low-vol premium is absent in this universe/sample; the long-only number was beta.
- Reports:
validation/equity_low_vol.py,reports/edge_audit.md
3.4 Vol-Regime / Shock-Day / Pullback overlays
- All REJECT — no improvement over baseline on OOS.
- Reports:
validation/equity_vol_regime.py,validation/equity_shock_day.py,validation/equity_pullback.py
3.5 Overnight / Turn-of-Month (TOM) — Round 2
- Re-tested on correct NY-local 1-minute data (2020–2025, split 2022-01-01): SPY overnight-only OOS Sharpe 0.58 (p 0.16); turn-of-month OOS Sharpe 0.12 (p 0.43); pairs SPY~QQQ z-score OOS Sharpe 0.12 (p 0.43). All REJECT (not significant). The overnight effect that is famous in pre-2000 data does not survive here.
3.6 Intraday Reversal (RETRACTED)
- Original "last-15min reverses the morning" finding (
p = 0.002) was an artifact of the timezone bug (Section 1.4). After correction, no intraday edge survives (gap-fillp = 0.834, Mondayp = 0.695, momentump = 0.558, combinedp = 0.685). - Report:
reports/intraday_real_effects.md
4. Key Lessons
- Timezone is everything for intraday. UTC storage + naive local handling
fabricated a "significant" edge. Always
tz_convert("America/New_York"). - Look-ahead is the silent killer of daily studies. A gate/score that
uses
close[t]cannot select day t's open-to-close trade. All four cross-section daily studies contained this bug — mega-cap (3.65→1.06), sector (3.30→0.97), crypto rotation (1.99→0.54), cross-asset momentum (3.19→1.04) — and every one collapsed to ~1.0 / non-significant after the fix. Any daily Sharpe > ~2 deserves immediate suspicion. - Horizon matters. Momentum is invisible at 1-day holding but strong at 21-day. Test the economically-motivated holding period, not the most convenient one.
- Asymmetry is informative. ORB up-breakouts work, down-breakouts fail — a bull-market signature, not a genuine breakout pattern.
- Fat tails kill significance. A strategy can show annualized Sharpe 0.97
yet have
p = 0.20because one outlier day dominates. Bootstrap p (not normal t-test) is the honest gate. - Equities >> crypto for cross-section. Every multi-asset winner is equity-dominated; crypto adds volatility, not alpha.
- Risk premiums ≠ anomalies. vol60/beta60 "pass" but are compensation for risk, not exploitable inefficiencies.
- Three bugs invalidated every "high-Sharpe" result. Timezone, look-ahead, and overlapping-returns/clipping between them accounted for all results with Sharpe > 2 — including the "surviving" six-month long-short momentum, which collapsed once its returns were constructed correctly. The one edge that survived all three audits is modest (OOS Sharpe ~1.5) — exactly what a real, tradeable edge looks like.
- A clean look-ahead check is necessary but not sufficient. The long-short momentum study passed the §1.5 fix yet was still broken by overlapping forward returns. Audit the construction of the return series itself, not just the signal/trade alignment.
- Long-only can rescue a dead long-short factor. Momentum's short leg stopped working post-2009, but the long-only "buy winners" portfolio is robust (OOS Sharpe 1.4–1.7). Dropping an unprofitable, costly short leg is a legitimate edge-refinement, not data-snooping.
- A long-only Sharpe ~1 in 2016–2025 is usually just beta — audit it. Round 2 showed short-term-reversal, low-vol, and liquidity-trend all posted OOS Sharpe ~1.0–1.4, yet every one had market-neutral residual Sharpe ≈ 0 and Beta ≈ 1, and long-short existence tests were ~0 or negative. The lesson now enforced in the framework workflow: before claiming any long-only factor, regress its returns on an equal-weight market benchmark; require residual Sharpe > 0 AND low correlation to existing factors. A factor that is only beta is not a new edge and will not diversify a book.
5. Conclusion — What Actually Works
| Edge | OOS Sharpe | p | Status |
|---|---|---|---|
| Long-only momentum (buy winners) | 1.44–1.69 | < 0.001 | PASS (equity cross-section, tradeable) |
| Six-month long-short momentum | 0.86–0.92 → 0.15–0.44 | 0.007 → 0.12–0.34 | RETRACTED (returns-construction) |
| Long-only ORB | 0.80–0.97 | ~0.20 | borderline (intraday) |
| Mega-cap trend | 3.65 → 1.06 | 0.000 → 0.161 | RETRACTED (look-ahead) |
| Sector rotation | 3.30 → 0.97 | 0.000 → 0.351 | RETRACTED (look-ahead) |
| Crypto rotation | 1.99 → 0.54 | 0.024 → 0.241 | RETRACTED (look-ahead) |
| Cross-asset momentum | 3.19 → 1.04 | 0.000 → 0.084 | RETRACTED (look-ahead) |
The single adoptable edge is LONG-ONLY cross-sectional equity momentum —
buy the top decile of trailing-return winners, equal-weight, monthly rebalance,
no shorting (OOS Sharpe 1.44–1.69, bootstrap p < 0.001, robust across 3–12
month lookbacks and consistent in-sample 2007–2015). It is built on the
audited validation/framework.py primitives. Retracted: the earlier
six-month long-short momentum (a returns-construction artifact); mega-cap
trend, sector rotation, crypto rotation, cross-asset momentum (look-ahead
artifacts); the entire intraday family (timezone artifact); factor-zoo 1-day;
low-vol; regime/shock/pullback overlays. Borderline: long-only intraday ORB
(satellite only, not statistically robust).
Recommended next step: productionize the long-only momentum edge (with transaction-cost modeling for small-cap names, since the universe is delisting-inclusive and includes illiquid issues) as the single approved strategy. Do not build on any retracted signal — they were statistical ghosts created by the three bugs above.
6. References
reports/momentum_spectrum.md— §2.1 (the genuine edge)reports/equity_momentum_monthly.md— §2.1 retraction (returns-construction)reports/equity_mega_momentum.md— §2.2reports/equity_sector_rotation.md— §2.3validation/cross_section_rotation.py— §2.4reports/cross_asset_momentum.md— §2.5reports/intraday_orb.md,reports/intraday_orb_v2.md— §3.1reports/equity_factor_zoo.md— §3.2validation/equity_low_vol.py— §3.3reports/intraday_real_effects.md— §3.6 (retraction)validation/framework.py+tests/test_research_framework.py— the test basedocs/ROADMAP.md— full rejection ledger