The 30-second version
We taught a computer to study every international football match since 1872 (~49,000 games). It learned what makes one team beat another and roughly how many goals each side tends to score. Then we simulated the entire 2026 World Cup tens of thousands of times — using the actual FIFA bracket, official tiebreaker rules, and a calibrated goal model — and reported how often each team wins. The dashboard shows those frequencies, with 5-seed simulation ranges (sampling noise across independent rollouts, not parameter CIs), sensitivity audit, and walk-forward backtests on the last four World Cups.
The pipeline in one picture
Gather data
49k matches · FIFA rankings · groups · schedule · venues · squad values
Rate teams
Elo + time-decayed form + squad-value prior
Goal model
Poisson regressors + Negative Binomial dispersion + Dixon-Coles τ
Simulate
Official bracket · Annex C · full tiebreakers · ET + penalties
Honest probs
With 5-seed simulation ranges + concentration sanity + sensitivity audit
How we rate teams
We use Elo, originally a chess rating system. Every team starts at 1500 points. Beat a stronger team — you gain more. Beat a weaker team — you gain less. Lose to a weaker team — you lose more.
We replay every international match in history and update ratings after each one. Important matches (World Cup, Euros, Copa América) count much more than friendlies. Big wins count more than narrow ones, but with a cap so a 7-0 doesn't get vastly more credit than a 4-0.
We add two smaller boosts: recent form (180-day exponential half-life — a match from last week matters far more than one from a year ago), and a squad-value prior from Transfermarkt (captures young-talent surges before Elo catches up).
The goal model — predicting scorelines
For every match we predict two numbers: the expected goals each team will score. Two XGBoost Poisson regressors trained on 27,000+ post-1990 matches do this.
Then we turn (λ_h, λ_a) into a realistic scoreline distribution:
- Negative Binomial marginals instead of plain Poissons — adds the "long-tail" variance football scores actually have.
- Dixon-Coles τ correction on the joint matrix for (0-0, 0-1, 1-0, 1-1) — fixes the chronic under-prediction of low-score draws.
- Per-match λ noise via a Gamma multiplier per simulation — captures "off days" and "purple patches" beyond the dispersion baseline.
Mean λ_h = 2.8, λ_a = 0.5. Independent Poissons would say Spain wins ~90%. With Negative Binomial dispersion + τ correction + match-day noise, Spain wins ~76%, draws ~14%, Saudi pulls a shock ~10%. That extra 14 percentage points of "non-blowout" outcomes is what real football looks like.
The bracket and tiebreakers
The 2026 World Cup is the first with 48 teams. FIFA published the exact Round of 32 bracket in advance: which group-stage finishing position fills which knockout slot.
For the 8 best third-placers — which 8 groups produce them changes the slot assignment. FIFA's regulations include a 495-row Annex C lookup table. We use it exactly: zero misses across all simulations.
For group ties we apply the full FIFA 2026 cascade:
- Most points
- Best goal difference
- Most goals scored
- Head-to-head points among tied teams
- Head-to-head goal difference
- Head-to-head goals scored
- Fair-play points (approximated as zero in simulation)
- FIFA World Ranking (new for 2026 — replaces drawing of lots)
Geography matters — venue effects
The 2026 tournament spans 16 cities across 3 countries, from Vancouver to Mexico City. Travel, altitude, and climate matter.
/injuries every 3h, capped ±25 Elo (extreme ±35). Names cross-referenced against the hand-curated data/raw/key_players_2026.json whitelist: headline stars auto-promote to tier_1_star (-30 Elo) or tier_1_keeper (-25 Elo); unknown names default to tier_2_starter (-12 Elo). Aliases handle API name drift ("Vini Jr", "Mo Salah", "Memo Ochoa"). Manual team_adjustments.json overlay still supported for operator tier-1 calls./fixtures/lineups within 4h of kickoff. Conservative deltas (-8 GK swap, -3 heavy rotation) capped ±20 Elo; first XI of the tournament is display-only.All hand-coded assumptions are stress-tested in the sensitivity analysis (Robustness section) — top-team rankings stay stable across 27 different parameter scenarios.
Is the model honest? Calibration + walk-forward
A model that says "70% home win" should be right ~70% of the time on matches like that. We test this two ways:
- Calibration on 4,839 unseen modern matches. Predicted 0.85 home win matches actual 0.85 — excellent (see calibration chart).
- Walk-forward backtest on past World Cups. Trained on pre-each-WC data, tested on actual WC. Average log-loss across WC 2010/14/18/22 is 0.98 vs naive baseline 1.09 — meaningful but honest lift. The 2022 result (Argentina won an upset over Brazil/France) had only +0.01 lift — honest evidence the model has limits in upset-heavy tournaments.
Sensitivity audit — are these numbers stable?
Every hand-coded assumption (host boost, altitude penalty, heat penalty, squad-value cap, penalty-shootout slope, Negative Binomial dispersion) was varied across 27 scenarios. For top contenders:
- Spain's champion probability stays in 23.2% — 28.2%.
- Argentina's stays in 18.6% — 22.0%.
- France's stays in 8.0% — 10.3%.
- Top-4 rank ordering (Spain > Argentina > France > England) is identical across all 27 scenarios; positions 5–6 (Brazil / Colombia) swap under altitude-penalty extremes.
The precise percentages move a little with assumption choices, but the broad ranking story doesn't. The numbers aren't a coincidence of one chosen parameter setting.
What this model still can't see
- Refereeing patterns — controversial calls swing tight matches.
- Tactical match-ups — some coaches simply have a manager's number.
- News + social signals — locker-room turmoil, federation politics, and tournament-day weather drama aren't modelled beyond the structured feeds.
- True per-shot xG — our post-match stats proxy uses shots-on-target + possession + corner deltas; we deliberately don't claim shot quality information the providers don't expose.
- Historical Elo baseline mid-tournament — pre-tournament Elo is held static; matchday intelligence adjusts the effective Elo per match but doesn't retrain the underlying rating.
Live matchday intelligence (injuries, weather, lineups, post-match stats proxy) does now auto-feed
the simulator via scripts/live/apply_matchday_adjustments.py. Aggregate cap per team-match is
±35 Elo (excluding live form delta); grand total with live form is ±45. Every component is logged to
data/live/matchday_intelligence_log.jsonl.
That's why the top contender doesn't exceed ~25% even with all the modelling improvements. The World Cup is structurally an upset machine.
Where the data comes from
- 49,450 historical international results — open
martj42/international_results(CC0) on GitHub. - FIFA World Ranking (June 2026 live tracker) — feeds the ultimate tiebreaker.
- 2026 schedule + groups — FIFA.com (final draw 5 Dec 2025).
- R32 bracket + Annex C lookup table (495 entries) — FIFA Regulations Annex C.
- Squad market values — Transfermarkt via Sportingpedia / GiveMeSport (May-June 2026).
- Stadium / city metadata — coordinates, altitude, climate for all 16 host cities.
- Live results, injuries, lineups, post-match stats — API-Football (Pro plan) — fetcher cadence is 10 min for results, 3h for matchday intelligence.
- Live weather forecasts — Open-Meteo (no key, fair-use ~10k req/day) — 16-day forecast horizon with static climate bucket as fallback.