How the projections work
The bracket's Projected mode and the Predictions page are
driven by a Monte-Carlo simulation that plays the rest of the tournament many thousands of
times and tallies how often each outcome occurs. It is deliberately simple and transparent
so the assumptions are easy to see — and to argue with. Finished results are held fixed; only
matches not yet played are simulated, so the numbers sharpen as the tournament unfolds.
1 · Team strength (dynamic Elo)
Every team starts from a prior rating on the World-Football-Elo scale (≈2100+ elite, ≈1900 strong,
≈1750 mid, ≈1600 weaker) — a roughly early-2026 snapshot. Co-hosts (USA, Canada, Mexico) get a
+60 home-advantage edge; unknown teams fall back to 1700.
Those priors then update from in-tournament results: after every finished match both
teams' ratings shift by the standard Elo update, so an over- or under-performing side carries
that form into the simulation of its remaining matches.
R′ = R + K · G · (W − We)
W is the actual result (1 / 0.5 / 0), We the Elo win-expectancy,
G a goal-margin multiplier (bigger wins move more), and K = 60 — the World-Cup tier,
deliberately responsive. Early on the ratings sit at their priors and diverge as results land;
the live movers are shown on the Predictions page's Form tracker. (Set
PREDICT_ELO_K=0 to freeze the priors.)
2 · One match at a time
Goals (group stage). A rating gap is turned into an expected-goals supremacy,
then each side's goals are drawn from independent Poisson distributions:
s = clamp( (RA − RB) / 200, −2.5, +2.5 )
λA = max( 0.18, (μ + s) / 2 ) λB = max( 0.18, (μ − s) / 2 ) with μ = 2.7
GA ∼ Poisson(λA) GB ∼ Poisson(λB)
So ~200 Elo points ≈ one goal of expected supremacy; evenly-matched teams
average 1.35 goals each (2.7 combined, near the historical World-Cup norm). Scorelines — not
just win/draw/loss — fall out of this, which is what the group tiebreakers need.
Draw correction (Dixon–Coles). Plain independent Poisson under-produces draws, so the
four lowest scorelines are reweighted by the Dixon–Coles factor τ (with ρ = −0.12), nudging
0–0 and 1–1 up and 1–0 / 0–1 down. This lifts the average group draw rate from ~22% to a more
realistic ~24% without otherwise disturbing the goal model:
τ(0,0) = 1 − λAλBρ τ(1,1) = 1 − ρ
τ(1,0) = 1 + λBρ τ(0,1) = 1 + λAρ (τ = 1 otherwise)
P(x,y) ∝ τ(x,y) · Poisson(x; λA) · Poisson(y; λB)
Knockouts. The same goal model decides the match; if it ends level, the tie goes to a
single Elo-weighted coin flip standing in for a penalty shootout, using the standard Elo
win-expectancy:
EA = 1 / ( 1 + 10−(RA − RB) / 400 )
3 · Simulating the whole tournament
Each of N iterations (default 4,000) plays out end to end:
- Simulate every unplayed group match; keep finished ones as-is.
- Build the 12 group tables with FIFA's tiebreakers in order — points → goal difference →
goals scored → head-to-head (points/GD/GF among the tied teams) → team name as a stable
final fallback (in lieu of fair-play points / drawing of lots).
- Rank the twelve 3rd-placed teams and take the best 8; assign them to the
Round-of-32 "3X/Y" slots with a backtracking matcher that respects each slot's eligible
groups.
- Resolve the Round of 32 and play each knockout round through to the Final, carrying winners
forward (and the two semi-final losers into the third-place match).
Across all iterations the model tallies how often each team finishes 1st/2nd/3rd in its group,
reaches each round, and wins the title. Dividing by N gives the probabilities. Results are
cached and only recomputed when a new match result lands.
4 · From frequencies to the bracket
The odds tables are these tallies directly: e.g. champion % is the share of
simulations a team won the Final, advance % the share it reached the Round of 32.
The Projected bracket needs more care. Picking each slot's single most-likely team
independently does not yield a valid bracket — a dominant team can be the favorite
in two places that can't both happen (famously, the same team in both the Final and the
third-place match, or in two different Round-of-32 slots). Instead the model assembles
one internally consistent bracket: it derives a coherent Round-of-32 field from the
simulated standings (each group's expected finishing order, plus the best-8 thirds matched to
their slots), then advances the favored team of each projected matchup forward, so every
later slot is fed by a real earlier result. The confidence shown on a slot is still the honest
marginal — the share of simulations in which that exact team reached that exact slot.
Make it your own. In Projected mode you can click any projected team to force it to
advance from that match. The bracket is then re-walked around your pick: that team is carried
forward and every later slot it feeds is re-resolved, so you can explore "what if" paths. Your
picks only steer the single drawn bracket — the simulation itself is unchanged, so the confidence
beside each slot stays the model's own marginal (a forced upset can sit next to a low %, which is
exactly the point). Reset picks returns to the model's chalk bracket.
5 · Uncertainty, assumptions & how to critique it
This is a teaching-grade model, not a betting market. Known limitations, roughly in order of
impact:
- Sampling noise. A probability p from N simulations has a standard error of
about √(p(1−p)/N) — ≈±0.8 percentage points at p=0.5,
N=4,000. It shrinks only as 1/√N, so small gaps between teams may be noise. Raise
PREDICT_SIMS to tighten.
- Ratings: subjective start, responsive update. The pre-tournament snapshot is
hand-set — challenge it freely. Ratings now adapt to results via a World-Cup-tier Elo update
(K=60), which is deliberately responsive: over a handful of games it can overreact to a fluke
result as readily as it captures real form. Lower
PREDICT_ELO_K to damp it, or
set 0 to freeze the priors.
- Simplified goal model. Goals use Poisson with a Dixon–Coles low-score correction
(ρ = −0.12) — better than independent Poisson on draws, but the correction is modest and
still doesn't capture full game-state dynamics (red cards, game-management when ahead, late
pushes). The total-goals baseline (2.7) and the linear, clamped Elo→supremacy mapping are
convenient choices, not estimated fits. ρ is calibrated to the historical ~24% draw norm, so
an unusually drawy run (real tournaments swing roughly 18–30% on small samples) is treated as
variance, not chased.
- Thin knockout model. A level match is decided by a single Elo coin — no separate
extra-time phase, no penalty-specific skill.
- Home advantage is coarse. A flat +60 for all three hosts; no travel, altitude,
climate, rest-day, injury, or squad-news effects at all.
- Third-place allocation is a valid matching, not FIFA's official fixed table. The set
of qualifying thirds is correct, but which slot each lands in can differ from FIFA's
published lookup.
- The projected bracket is the favored-path ("chalk") reconciliation. Because the
favorite advances each projected matchup, the single drawn bracket understates upsets — read
the per-slot confidence to see how shaky a pick is, and the odds tables for the true spread.
- Not yet back-tested. The model hasn't been calibrated against historical tournaments,
so treat the absolute numbers as indicative rather than validated.
6 · Reproducibility
Pure-Python, no external numerical libraries. Runs are deterministic given a fixed random seed.
The full implementation lives in predict.py (simulation), ratings.py
(priors), and compute.py (standings & tiebreakers) — and is documented in the
project README for line-by-line review.