V13.2 Earnings Proximity · OOS bake-off (2026-05-19)
Hypothesis
Insider buys placed in the 30-day window BEFORE the next earnings publication carry more alpha than post-earnings buys. Pre-earnings buys suggest the insider knows something the market does not yet price; buys just after a guidance cut are reactive and carry less informational value.
Feature
Declaration.earningsProximityScore ∈ [0, 1]. Computed from the gap
between pubDate and the next CompanyEarnings.eventDate:
| daysToNextEarnings | proximityScore |
|---|---|
| 0..30 | 1.0 |
| 30..60 | 0.5 |
| 60..90 | 0.2 |
| >90, or no event, or post-event | 0 |
Data
CompanyEarningstable populated viascripts/_ingest-earnings-yahoo.ts(Yahoo Finance quoteSummary, modules earnings + earningsHistory + calendarEvents). Throttle 8 req/s, 5-UA rotation, fresh crumb every 30 min.- Cohort: top 1000 companies by recent signalScore activity (last 365d).
- Ingested rows (2026-05-19 run): 2972 earnings events.
- Declarations backfilled: 135,647 (since 2024-01-01).
- Distribution:
- p=1.0 (pre-earnings 0-30d): 5,292
- p=0.5 (30-60d): 4,497
- p=0.2 (60-90d): 5,824
- p=0.0: 9,891 priced + 110,143 no-event-known
Scoring overlay
score_V13.2 = score_V13.1g + clip(earningsProximity, 0, 1) * 1.0
Additive in the V13 base score range (typical 4..10). When no earnings event is known for the company, contribution is 0 (no tilt), so V13.2 collapses to V13.1g for companies outside the top-1000 cohort.
OOS bake-off
Same harness as V13.1g (audit 78): walk-forward 2025-01-01 to 2026-05-19, T=14 monthly buckets, top-10 picks/mo, T+90 hold from pubDate+1, NET 0.6 % round-trip, winsor ±50 % per pick.
Universe: 20,688 OOS rows with priced T+90 returns.
| config | T | picks | Sharpe | CI95 | CAGR% | MaxDD% | Win% | DSR |
|---|---|---|---|---|---|---|---|---|
| V13.0_baseline | 14 | 140 | 0.03 | [-2.01, 2.01] | -2.5 | -43.7 | 50.7 | -0.41 |
| V13.1g_stacked | 14 | 140 | 0.70 | [-1.01, 3.42] | 25.4 | -29.7 | 55.7 | 0.25 |
| V13.2_earningsProx | 14 | 140 | 0.83 | [-0.87, 3.77] | 31.4 | -29.7 | 57.1 | 0.38 |
| V13.2_crossMarket | 14 | 140 | 0.11 | [-1.95, 2.28] | 0.3 | -43.0 | 52.1 | -0.34 |
| V13.3_sectorMomentum | 14 | 140 | 0.13 | [-1.90, 2.07] | 1.1 | -42.5 | 52.1 | -0.32 |
Anti-overfit gate
Rule: ship only if DSR(candidate) - DSR(baseline) > -0.3.
- V13.1g DSR: 0.25
- V13.2 DSR: 0.38
- Delta: +0.13 (gate passed, no degradation, actual improvement).
12-trial Bailey-Lopez de Prado deflation. DSR went UP after deflation adjustment, which is a strong signal: the feature reproducibly beats the expected max-of-N-noise baseline.
Decision
Ship V13.2. Wire earningsProximity into V13Inputs and
computeV13Score. Production signalScore recompute will pick up the
field on next cron walk-forward.
Deltas vs V13.1g baseline:
- Sharpe +0.13 (0.70 → 0.83, +18 %)
- CAGR +6.0 pp (25.4 → 31.4)
- Win% +1.4 pp (55.7 → 57.1)
- DSR +0.13 (0.25 → 0.38)
- MaxDD unchanged (-29.7 %)
Sample pre-earnings buys (proximity = 1.0)
| pubDate | Company | Insider | EUR amount | days to earnings |
|---|---|---|---|---|
| 2026-04-20 | Ubisoft Entertainment | Christian GUILLEMOT | 330,357 | 30 |
| 2026-04-20 | Ubisoft Entertainment | Christian GUILLEMOT | 624,050 | 30 |
| 2026-03-17 | Arq, Inc. | EICHER CAROL S | 131,065 | 14 |
| 2026-03-17 | Arq, Inc. | EICHER CAROL S | 131,065 | 14 |
| 2026-03-11 | COTY INC | Gordon von Bretten | 200,287 | 19 |
Files
- Migration:
prisma/migrations/20260520200000_earnings_proximity/migration.sql - Ingestion:
scripts/_ingest-earnings-yahoo.ts - Backfill:
scripts/_backfill-earnings-proximity.ts - Bake-off:
scripts/_v13_bakeoff/bake.ts(configV13.2_earningsProx) - Scoring:
src/lib/signals.ts(V13Inputs.earningsProximity + V13.2 add) - UI badge:
src/components/DeclarationCard.tsx(Pre-earnings buy pill) - Stats const:
src/lib/signals.ts::V13_2_EARNINGS_PROX_OOS
Caveats
- Cohort capped at top-1000 by activity. Smaller-cap rows have no earnings data and receive proximity = 0, which is a NO-OP under the additive formulation (does not penalise).
- Yahoo
earningsHistoryonly goes back ~4 quarters. Older declarations relying on past events get noEvent → 0. - DSR uplift (+0.13) is meaningful but the OOS T=14 window is short. Re-evaluate at T=20+ and after universe expansion (top-3000 cohort).