Date: 2026-09-04
Status: Research complete. Verdict: REJECT (small positive tilt, doesn't meet the pre-registered pass bar).
Code: validation/eqmom_pead_overlay.py (pre-registered study), validation/eqmom_pead_overlay_robustness.py (12-cell grid), reports/eqmom_pead_overlay.md and reports/eqmom_pead_overlay_robustness.md.
Question
Does PEAD (post-earnings drift, validated Sharpe 1.04 OOS as a standalone edge) add value as a sizing overlay on the validated EqMom edge (long-only top-decile 6-month momentum, Sharpe 1.25 OOS on the liquid subset)? The combination is structurally different from the rejected edge_hunt15 PEAD x Momentum double-sort (which filtered the universe; the overlay here keeps the universe and modulates size).
Pre-registered protocol
- Universe: top-100 names by listing persistence (the liquid
subset, identical to
validation/momentum_liquid.py). - Window: 2016-01-01..2026-07-31.
- IS/OOS split: 2021-01-01 (midpoint of the earnings cache; pre-registered BEFORE seeing results).
- EqMom: top-decile trailing-126d return, 21d rebalance, lag=1, next_open fill, 10bps round trip.
- PEAD: most recent earnings event in trailing 60 calendar days; surprise_pct >= 5.0% boosts size to 1.25x; recent miss (any negative surprise) reduces size to 0.75x; otherwise passthrough.
- Pre-registered pass: OOS Sharpe lift >= 0.05 AND bootstrap p < 0.10 AND maxDD not increase by > 5pp.
Results (primary, pre-registered configuration)
| Variant | Ret | Sharpe | PF | MaxDD |
|---|---|---|---|---|
| baseline (EqMom long-only top-decile) | +310.07% | 1.29 | 1.21 | -28.07% |
| primary overlay (0.75/1.0/1.25 by PEAD) | +323.17% | 1.31 | 1.21 | -27.60% |
| Lift | +13.10% | +0.025 | 0.00 | +0.47pp |
- OOS bootstrap p(overlay beats baseline) = 0.201 (above the pre-registered 0.10 bar)
- Verdict: REJECT (Sharpe lift +0.025 < +0.05, p=0.201 > 0.10)
The lift is directionally positive (Sharpe +0.025, ret +13pp, maxDD slightly better) but does not meet the pre-registered pass bar. The signal is real but small.
Robustness grid (REPORTED, not selected)
12 cells sweeping (surprise_threshold, hold_window) ∈ {2.5, 5.0, 7.5, 10.0} × {30, 60, 90}. Pass rate: 0/12.
| surprise% | hold_d | Sharpe lift | ret delta | p |
|---|---|---|---|---|
| 2.5 | 30 | +0.011 | +6% | 0.299 |
| 2.5 | 60 | +0.025 | +13% | 0.209 |
| 2.5 | 90 | +0.038 | +18% | 0.149 |
| 5.0 | 30 | +0.013 | +8% | 0.260 |
| 5.0 | 60 | +0.025 | +13% | 0.201 (primary) |
| 5.0 | 90 | +0.045 | +23% | 0.099 |
| 7.5 | 30 | +0.001 | +1% | 0.482 |
| 7.5 | 60 | +0.018 | +8% | 0.320 |
| 7.5 | 90 | +0.038 | +17% | 0.169 |
| 10.0 | 30 | +0.001 | +1% | 0.447 |
| 10.0 | 60 | +0.022 | +10% | 0.253 |
| 10.0 | 90 | +0.038 | +18% | 0.150 |
Pattern: positive lifts across all 12 cells (no harm done);
magnitudes 0.01-0.045 (below the 0.05 bar in all cells). The closest
to passing is (5.0%, 90d) with +0.045 lift, p=0.099 — just under
the 0.10 bar.
Alternative asymmetries (diagnostic only)
Swept the boost/reduce multipliers across 10 configurations. None reliably crosses the strict p<0.10 bar. The pre-registered 0.75/1.0/1.25 is in the middle of the range; the wider 0.5/1.0/1.5 gives slightly more lift (+0.042) but p=0.213.
Honest read
The overlay is a small positive tilt, not a free-lunch edge:
- It does not destroy value in any tested configuration.
- The directional effect is consistent (positive lifts across the full 12-cell grid + 10-asymmetry grid = 22 configurations).
- The magnitude is below the pre-registered pass bar (lift < 0.05, p > 0.10).
- The mechanism is plausible (PEAD and EqMom have correlation ~0.39; agreement between two independent signals is joint information; conflict is a reason to reduce).
The honest verdict is REJECT, not "MARGINAL" or "INERT". A 0.025 Sharpe lift with p=0.201 is below the pre-registered bar, and the reason it fails is statistical: with ~1400 OOS days, the std error on Sharpe is small, so a +0.025 lift is detectable in principle but not at p<0.10.
This is not a recommendation to wire it up. The overlay would require:
- Adding a hook to
production/equity_momentum_runner.py(mirroring theEQMOM_NEWS_WEIGHTpattern that's already there). - An env flag (
EQMOM_PEAD_OVERLAY=0default off). - Tests for the sizing function.
- Live forward-OOS confirmation (the protocol here is in-sample selection of the threshold; live would need 30+ days of observation).
The cost of a 0.025 Sharpe improvement on a 1.29 base is real but small (~2%), and the operator-side risk (a new sizing hook on a production sleeve) is non-trivial. The expected-value calculation favors NOT deploying this overlay unless the operator specifically wants a tilt for diversification reasons.
Why this isn't a free lunch
The honest read: PEAD and EqMom are not independent enough. The correlation of 0.39 (per edge_hunt15.md) means the two signals mostly agree, so the "conflict" reduction cases (0.75x) are rare. The "agreement" boost cases (1.25x) are slightly more common, but the average effect is small because the underlying alpha is the PEAD-alpha (which is already captured by the dedicated PEAD sleeve running on its own paper account). The overlay's contribution is just the correlation structure between the two signals, not new information.
What this research DOES confirm
- The PEAD data is wired correctly into the validation harness (the study runs without errors and respects all the framework guarantees: lag>=1, no-lookahead, dead-stock-safe, cost_bps decimal).
- The meta-signal direction is correct: agreement helps, conflict hurts. This is a sanity check on the EqMom + PEAD combination.
- The validation framework can be extended to meta-signals without code changes to the framework itself.
- The free-data universe is still exhausted: even with two validated edges, the cross-signal lift is below the bar. This is consistent with the rejection ledger's claim that price-only data has no free lunch > 0.9 Sharpe, and meta-signals on validated edges are at most a small tilt.
Honest caveats
- Earnings cache depth: yfinance serves ~5 recent quarters per name. Pre-2020 PEAD signals are sparser than OOS-2021+. The IS sample (2016-2020) has fewer PEAD hits per name than the OOS.
- Asymmetric multipliers were heuristic (0.75/1.25) — not optimized. The diagnostic sweep shows the lift is similar across reasonable choices.
- The proposal pre-registered one set of buckets; the robustness grid and asymmetry sweep are REPORTED, not selected. Picking the best cell from the grid would be a selection bias (the look-ahead bug class that bit R10-13).
- The OOS sample is 1400 days — a reasonable size for a Sharpe test, but the +0.025 lift is small enough that even this sample has limited power. A larger sample (10+ years forward) could either confirm or refute the effect.
- The pre-registered pass bar of 0.05 lift + p<0.10 was chosen conservatively. A looser bar (e.g. 0.025 lift + p<0.20) would pass, but that's not the protocol.
Operator recommendation
- Do NOT wire this into production. The lift is below the pre-registered bar; the cost (new sizing hook, new tests, new live observation) is non-trivial.
- Do NOT re-research this with a different surprise threshold or hold window to find a passing configuration — that's selection bias.
- The PEAD sleeve (
peadbot) and the EqMom sleeve (eqmombot) are both live and producing independent alpha. The combined-book allocator already gives them combined exposure (book weight × per-sleeve deploy fraction). That's the right way to combine the two edges; a per-name meta-signal is not. - If the operator wants a meta-signal, the next genuinely unexplored territory is cross-asset timing: use the validated equity-mom signal to time the crypto gate60 sleeve (or vice versa). This would require a paid data source for real-time equity/crypto correlation, which is out of scope for the free-data constraint.