A no-lookahead, cost-aware backtest across 1,004 US & AU stocks: 45 US large-caps, 37 US small/mid-caps, 13 ASX large-caps, then a survivorship-bias-free expansion to the full Jan-2016 Russell 2000 (909 names with data) — and finally a point-in-time re-run with the actual Russell 2000 membership at each announcement date (4,220-name union, 104 monthly IWM snapshots). Does a YouTube-style “concordant” filter (beat + price rose / miss + price fell) add value — or is it just the magnitude of the earnings surprise (Bernard–Thomas / Livnat–Mendenhall)?
Four numbers that summarise 8.6 years and 27,564 earnings events.
LONG EPS beats ≥10% outpace the universe baseline 60 trading days out — p = 0.0001 (n = 11,858) on the broad Russell 2000. Magnitude is monotonic: the bigger the beat, the stronger the drift.
Filtering on announcement-day price direction adds nothing and actively loses money in the realistic portfolio model at every holding period (30–90d).
US large-caps and ASX large-caps are efficiently priced. Big compounded $ on large caps is leveraged beta, not drift.
The edge survives ~20 bps round-trip costs, dies at 50 bps. Realistic portfolio returns (+26% to +71%) underperform equal-weight buy & hold (+799%).
Event-driven backtest, strict point-in-time handling, no lookahead. Reaction window uses only prices known before entry; entry at the open of the session after the full reaction; exit at the close of the 60th trading day.
| Component | Definition |
|---|---|
| Concordant signal | LONG: beat consensus and announcement-day return > 0 · SHORT: miss and return < 0 |
| Magnitude signal | Surprise buckets: 0–2% / 2–5% / 5–10% / >10% of consensus EPS |
| Timing | BMO/AMC from announcement timestamp (07:00–12:00 → BMO, afternoon → AMC). Midnight/unknown stamps are AMC (conservative). |
| Entry / exit | Open of session after full reaction · close of Nth trading day. Portfolio exits are stored on filled positions only (skipped overlaps cannot close a later trade). |
| Execution models | Fixed-fraction (10% of initial, no compounding) + concurrent portfolio with daily MTM and 100% gross-exposure cap |
| Costs | Round-trip bps charged to cash on entry and exit (0 / 10 / 20 / 50). Equity, Sharpe and maxDD move with costs; PF is computed from the same net pnl. |
| Beta control | Every event-study result is a differential vs the complement (Welch two-sample t-test), plus a weekly calendar-time cluster. Not a one-sample test against the same sample's mean. |
Three curated universes first, then a Jan-2016 Russell 2000 snapshot (Yahoo coverage is incomplete for delistings) and a true point-in-time membership re-run.
| Universe | Names | Events | Consensus source | Notes |
|---|---|---|---|---|
| US large-cap | 45 | 1,524 | Yahoo analyst estimates | Mega-caps: no edge |
| US small/mid-cap | 37 | 1,231 | Yahoo analyst estimates | Where the edge first appeared (p=0.002) |
| ASX large-cap | 13 | 173 | Time-series YoY EPS from PDFs | No free keyless ASX consensus |
| Russell 2000 (2016 snapshot) | 909 | 26,674 | Yahoo analyst estimates | 1,909 listed; Yahoo served 909 — delistings drop out |
60-trading-day forward returns vs universe baseline, gross.
| Signal | n | Avg 60d fwd | Diff vs baseline (+4.72%) | p | Verdict |
|---|---|---|---|---|---|
| beat >10% (LONG) | 462 | +8.12% | +3.40pp | 0.0021 | The edge |
| beat 5–10% | 191 | +5.08% | +0.37pp | n.s. | not significant |
| beat 2–5% | 164 | +1.02% | −3.70pp | n.s. | not significant |
| beat 0–2% | 72 | +1.95% | −2.77pp | n.s. | not significant |
| beat_up (concordant) | 503 | +4.90% | +0.18pp | 0.84 | dead — no value |
| miss_down (concordant short) | 188 | +1.54% | −3.18pp | 0.035 | sig differential, poor $ |
| Universe | Baseline /60d | beat_up diff | beat >10% diff | Verdict |
|---|---|---|---|---|
| US large | +4.21% | +0.05pp (p=0.93) | +1.38pp (p=0.073) | No exploitable edge |
| ASX large | +0.84pp beats | n.s. | — | No PEAD edge; all variants < buy & hold (+104%) |
909 tickers, 26,674 events, survivorship-bias-free. Realistic concurrent-portfolio model (100% gross cap, daily MTM, 10bps).
Avg 60d forward return by EPS-surprise bucket (beats only, broad R2000).
Portfolio-model total return by holding period: beat>10% long vs base_concordant (incl. shorts).
| Hold | beat>10% long | PF | base_concordant | PF |
|---|---|---|---|---|
| 30d | +39.2% | 1.08 | −44.7% | 0.85 |
| 45d | +42.6% | 1.11 | −4.0% | 0.96 |
| 60d | +26.4% | 1.08 | −11.7% | 0.92 |
| 90d | +70.5% | 1.29 | −23.0% | 0.90 |
| Min ADV | Events | Portfolio total | PF |
|---|---|---|---|
| none | 26,560 | +71.6% | 1.23 |
| $2M | 22,113 | +26.4% | 1.08 |
| $10M | 13,238 | −8.7% | 0.94 |
| $50M | 3,247 | +67.5% | 1.17 |
Profit factor vs round-trip cost (60d, ≥$2M ADV). Re-run after the cash-cost fix to refresh equity paths; PF in the saved JSON did move with bps.
60d forward-return differential by signal group (broad R2000, baseline +3.14%).
The headline R2000 result used a fixed Jan-2016 snapshot. This section re-runs the whole 2018–2026 window with the actual index membership at each announcement date — 104 monthly IWM holdings snapshots, 4,220-name union universe, membership gate = nearest snapshot on or before the announcement (no lookahead).
component=holdings&asOfDate=YYYYMMDD) — monthly snapshots from 2016 to today, ~2,000 names each. Actual Russell 2000 membership is free and keyless; no Wayback, no paid vendor.| Universe | Names | Note |
|---|---|---|
| Fixed Jan-2016 snapshot | 909 | the original study's pool |
| PIT union (2018–2026) | 4,220 | every name that was a member at any point |
| Churned via reconstitution | ~1,547+ | added/removed (e.g. Jun-2024: +228 / −131) |
| Case | Hold | Trades | Net | PF | Win% | Sharpe |
|---|---|---|---|---|---|---|
| PIT membership-gated | 60d | 897 | +62.2% | 1.19 | 54.0% | 0.32 |
| PIT membership-gated | 90d | 792 | +80.5% | 1.25 | 50.4% | 0.36 |
| Union, no gate | 60d | 955 | +3.4% | 0.99 | 52.7% | 0.15 |
| Union, no gate | 90d | 840 | +42.7% | 1.14 | 49.2% | 0.27 |
| Fixed 2016 pool (artifact) | 60d | 846 | +1.99%/60d | 1.08 | 54.4% | — |
| PIT benchmark (actual members) | — | — | +167% | — | — | 0.56 |
Big-beat (≥10%) event forward returns at 5–90d holds: raw (drift) vs excess over the same-window equal-weight membership index. Raw is the tape; excess is the timing edge.
OLS excess ~ new + momentum_z + new×momentum_z at 30/60/90d. The 2023–26 interaction signal does NOT survive 2018–2026; only momentum itself is robust.
src/collect_iwm_membership.py (IWM API collector), src/build_r2000_pit_data.py (union build), src/run_r2000_pit.py (gated backtest + controls), src/analyze_drift_curve.py, src/analyze_new_vs_existing.py, src/analyze_momentum_match.py (splits + momentum-matched OLS). Full narrative: docs/PIT_R2000_BACKTEST_RESULTS.md.
Everything is free-data and keyless. Full pipeline, engine and results live in the pead_strategy/ workspace.
src/backtest_engine.py (engine), src/build_r2000_data.py (data), src/run_r2000.py (edge analysis + sweeps), src/collect_iwm_membership.py + src/build_r2000_pit_data.py + src/run_r2000_pit.py (point-in-time membership pipeline), results/us_r2000_results.json (full JSON), results/trades_r2000_bigbeat_portfolio.csv (trade-by-trade). Full narrative: docs/PIT_R2000_BACKTEST_RESULTS.md + Research/StrategyBacktests/pead-post-earnings-announcement-drift.md in the companion vault.