WC26 Dashboard: World Cup Pool Forecasting
Tracked five quiniela players’ live win odds across all 104 matches of WC2026, updating on every real result. Final score: Spain won the cup, and its owner took the pool with 46 points.
10,000 runs of the full 104-match tournament in under 3 s; odds cached in Cosmos by completed-match count so they served instantly between real matches and refreshed the moment one landed.
Situation
Five of us ran a World Cup quiniela: each picked five teams from a hat out of the best 25 we thought could win, and whoever’s team won the world cup, won the pool. Everyone wanted to know their live odds, and there was no public, inspectable model that updated as the real tournament unfolded.
Task
Build and ship a live dashboard that, as real results come in, gives each player their probability of finishing 1st-5th in the pool, and lets anyone explore what-if scenarios.
Action
- Built a pure, seedable simulation engine: a standard Elo win-probability drives a Monte Carlo over every remaining match, run 10,000× to tally each player’s finish distribution. It imports no cloud or IO, and a contract test fails the build if it ever does.
- Wrapped the engine in a Python Azure Functions handler with a Cosmos DB cache keyed by the completed-match count: baseline results are served instantly from the cache; a scenario override always recomputes fresh and is never cached.
- A timer-triggered job pulled live results from a sports API and updated team Elo ratings (K=32) after each match. The front end is a static-exported Next.js app on Azure Static Web Apps, bilingual es/en, with a scenario builder that let anyone fix a hypothetical result and watch the pool odds shift.
How it works
A pure simulation engine (Elo logistic + Monte Carlo, importing no cloud) behind a Python Azure Functions API, with a Cosmos cache keyed by tournament progress and a timer job that live-updates Elo from a sports feed. The front end is a static-exported Next.js app on Azure SWA.
Result
Live in three days from conception and served a real World Cup pool through all 104 matches: a Next.js + Azure Functions app with 138 tests across 10 files, green on every CI push. The pure engine simulates the full tournament 10,000× in under 3 seconds (a bound enforced on every CI run), so baseline odds were cached in Cosmos and recomputed only when a real match finished. After the final (Spain 1-0 Argentina), the last API responses were frozen into the site as static JSON and the backend was deleted; the dashboard survives as a static archive that costs nothing to keep online.
Learning
The value here was never model sophistication: a 400-point Elo logistic with a flat draw rate is the whole match model. It was engineering the boundary: a pure, seedable, dependency-free simulation engine wrapped in a serverless handler that caches by real-world state and recomputes only for hypotheticals. The artifact I'm proudest of is the contract test that fails the build if the word "azure" ever appears in the engine. It keeps the model something you can test and reason about offline, no cloud attached.
Tech Stack
Services
Status
Archived