d887664fce
- docs/V3-GIGA-PLAN.md: the v3 plan (data lake to 7+GB, GIGA ensemble, scoreboard, UI clarity) with the honest reframe: perfect accuracy = perfect calibration; 'beat the betting sites' = a live public head-to-head, scored with proper rules. - src/lib/odds.ts: American-ml → decimal, de-vig (normalize away the margin), overround; 3 vitest cases incl. tonight's real opening lines. - ESPN core odds adapter (server/src/ingest/espnOdds.ts): captures DraftKings 1X2 moneylines + O/U + spread per fixture; follows $ref pointer items. - odds_history table (insert-if-changed → clean line-movement history); scheduler odds sweep every 3h over a 14-day horizon, first sweep at boot; /api/odds + /api/odds/:num. Odds are a benchmark ONLY — never a model input. - Boot speed-up: fixture↔event mapping skips dates already fully mapped. - Verified: 54 fixtures captured with real moneylines locally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.4 KiB
5.4 KiB
Cup26 v3 — "GIGA": the data lake, the ensemble, the scoreboard
The three asks, translated honestly
| Ask | Reality | What we build |
|---|---|---|
| Perfect accuracy | Impossible — match outcomes are irreducibly random. A true 60% favourite loses 40% of the time. | Perfect calibration (ECE → ~0.01, proven on the reliability diagram) + maximal sharpness from an ensemble. "Perfect" = our probabilities mean exactly what they say. |
| More accurate than betting sites | The closing line is the strongest public forecaster; nobody beats it systematically. | A live public Model-vs-Market scoreboard: capture DraftKings odds (free via ESPN core API) for every WC match, de-vig them, score market vs our model with RPS/Brier after each result. Target: within noise of the market; stretch: beat it on slices (draws, knockouts). Nobody else shows this publicly — win or lose, we show the gap. The model itself stays pure (odds are a benchmark, never an input — per the earlier decision). |
| Literal gigabytes of data | Verified: StatsBomb open data alone is 7.06 GB of event JSON. | A real data lake: 7 GB StatsBomb corpus + ESPN historical sweep + odds-history capture + Transfermarkt squads/values/injuries. Raw lake lives on the dev machine; compact aggregates (tens of MB) ship to prod; a /data page shows live counters (bytes, events, matches, players, freshness) — "show all the data." |
Pillar A — The Data Lake (Phase A)
- StatsBomb full corpus (
github.com/statsbomb/open-data, ~7 GB): clone locally, process EVERY competition (WC 2018/2022, Euros, La Liga, EPL, WSL, Copa, …) into:player_profiles— per-90 xG, xA, shots, key passes, pressures, duels, set-piece involvement for every player in the corpus; matched to 2026 squads by name.team_fingerprints— directness, press height, set-piece share, xG-by-zone (the "gaps & strengths" engine, now from real event data).score_state_rates— empirical goal rates by score state/minute (powers in-play v2).shootout/penalty priors— conversion rates (with martj42shootouts.csv).
- ESPN historical sweep: scoreboards + summaries for WC 2010–2022 (verified working) → fills H2H/form archive with venues, stats.
- ESPN odds capture (
sports.core.api.espn.com/.../odds, verified live): VPS scheduler snapshots odds for every 2026 fixture a few times daily + near kickoff →odds_history(line movement!). Benchmark only. - Transfermarkt adapter (planned in v2, now built): squads, market values, injuries/suspensions →
squad,injuriestables (daily cadence). /api/data/stats+/datapage: total bytes processed, events, matches, players, per-source freshness/health. The gigabytes, visible.
Pillar B — Model v3: the GIGA ensemble (Phase B)
Three independent ratings, ensembled:
- Elo–Dixon-Coles v2 (current model + covariates): tuned time-decay, rest days, venue geo/altitude, stage effects.
- Squad-value model: Transfermarkt market values + age curves + injury deductions → goal expectations (literature: rivals Elo; low correlation with it → great ensemble partner).
- Player-aggregate model: squad xG production/defence from StatsBomb player profiles (coverage-weighted; honest about sparse squads).
Then: log-opinion-pool ensemble (weights tuned on 2018–2022 via the existing backtest harness) → isotonic calibration on held-out years → only ship what wins the bake-off. Plus: shootout model in the Monte Carlo (knockouts), in-play v2 (score-state intensity, red cards).
Measurement: extended walk-forward backtest scores every variant (RPS/Brier/log-loss/ECE); the Methodology page shows the bake-off table. Live: the scoreboard (below) is the ultimate test.
Pillar C — UI: clear, clean, labelled (Phase C)
- Glossary + tooltips: every metric (xG, Elo, RPS, ECE…) gets an inline "?" → plain-language explainer; one shared
<Term>component. - Provenance chips everywhere: "ESPN · 2 min ago", "150-year archive", "StatsBomb corpus", "model v3".
/scoreboard— Model vs Market: per-match cards (our probs vs de-vigged DraftKings), running RPS/Brier totals, "who was closer" badges. The headline feature./data— the lake, visible: counters, coverage, freshness, browsable source tables.- Squad views on team pages: players with club, age, market value, injury status, corpus stats where available.
- Clarity pass: consistent units/labels, collapsible advanced sections, density/mobile polish.
Phasing
- Phase A — Lake (StatsBomb ingest + Transfermarkt + ESPN history/odds capture + /data) ← start here; odds capture must begin ASAP (every day of line history is unrecoverable).
- Phase B — Ensemble (covariates, squad-value + player models, ensemble + calibration, bake-off, shootout, in-play v2).
- Phase C — UI (scoreboard, data explorer, glossary/tooltips, squads, clarity pass).
- Phase D — Deploy & live validation through the group stage; scoreboard accumulates.
Honest success criteria
- Calibration: ECE ≤ 0.015 out-of-sample (already 0.01; keep it while adding sharpness).
- Sharpness: beat v2's RPS 0.171 out-of-sample (every point matters; the market sits only a little better).
- Market: publish the head-to-head; target = statistical tie; stretch = ahead on any meaningful slice after ≥48 matches.
- Data: > 7 GB processed, with the proof on
/data.