⚽ World Cup AI Forecaster
Can a time-series "foundation model" predict the 2026 FIFA World Cup? A hands-on, honest case study in when modern AI helps a prediction problem — and when a 50-year-old algorithm does just as well.
---
I tried to forecast the 2026 World Cup with the AI everyone's hyping — a foundation model (Google's TimesFM) that predicts any series of numbers, zero training required. A football rating formula from the 1960s (Elo) beat it. Then one small change — feeding it a smoother signal — flipped it from worst to best. The real twist: do it right, and everything ties — the old formula, the cutting-edge AI, a trained meta-model, even the live betting market — all jammed against the same ceiling. 📝 Read the full story:
docs/article.md· 🔬 Methodology:docs/METHODOLOGY.md· 📊 All results:docs/RESULTS.md
The one finding, in one chart
Same foundation model, two different inputs. Fed raw goal counts it's the worst model; fed a smooth signal (each team's Elo trajectory) the identical model becomes the best. The representation mattered more than the model.
What this is
A complete, reproducible forecaster for the 2026 World Cup that:
- Forecasts every match (win / draw / loss + expected score) from team strength,
- Simulates the whole tournament (groups → the official 48-team bracket → champion) via Monte-Carlo,
- Validates live against games already played and against a real prediction market (Polymarket),
- Pits a TimesFM foundation model against honest classical baselines (Elo, Dixon-Coles Poisson), a trained meta-model, and TabPFN (a tabular foundation model),
- Ships an MCP server so any LLM can use it as a forecasting agent.
Everything runs on free, no-key data (match history + fixtures from one public CSV; optional Polymarket odds from a public API).
Quickstart
git clone https://github.com/tpomerance/worldcup-ai-forecaster.git && cd worldcup-ai-forecaster
python -m venv .venv && source .venv/bin/activate
# install torch for your hardware first (https://pytorch.org), then:
pip install -r requirements.txt
# 1) pull data + show live group standings
python -m src.ingest
# 2) honest backtest: TimesFM vs Elo vs Poisson vs blend (add --timesfm)
python -m src.evaluate --timesfm
# 3) predict upcoming matches / a single match / the whole tournament
python predict.py --next 6
python predict.py --match "Spain vs Morocco"
python predict.py --simulate --sims 10000
# 4) full illustrated report (Markdown + PDF) and the figures
python -m src.report
python -m src.make_pdf
# tests
python tests/test_sanity.py
The forecaster's current call
| | Model (simulation) | Live market (Polymarket) | |---|---|---| | Favorite | Argentina ~30% | France ~18% | | Then | Spain · France · England | Argentina · Spain · England |
(Numbers update as the tournament progresses — re-run python refresh.py.)
How well does it actually do?
Validated pre-kickoff on games already played, scored with RPS (lower = better):
| | RPS | vs | |---|----:|----| | Our model | 0.168 | — | | Polymarket (the market) | 0.152 | sharper (markets are hard to beat) | | Clueless (1/3 each) | ~0.24 | we're far better than chance |
The model and the market agree on ~all match picks; its misses are genuine upsets that fooled the market too. See docs/RESULTS.md for the full tables and the surprise analysis.
Project structure
worldcup-ai-forecaster/
├── src/
│ ├── ingest.py # data: results + fixtures + live standings (no API key)
│ ├── elo.py # World-Football Elo ratings
│ ├── features.py # time-decayed attack/defense + per-team series
│ ├── match_model.py # Dixon-Coles / Poisson → W/D/L, scorelines
│ ├── forecast.py # TimesFM forecasting (goals & Elo trajectory)
│ ├── baselines.py # Elo, last-value Poisson, market-implied
│ ├── odds.py / polymarket*.py # bookmaker + Polymarket (champion & per-match)
│ ├── blend.py / meta_model.py # ensemble + trained stacker + TabPFN
│ ├── evaluate.py / validate_live.py # backtests & live validation (RPS/Brier)
│ ├── simulate.py / bracket2026.py # Monte-Carlo over the official bracket
│ ├── report.py / make_pdf.py / export_figures.py # reporting & figures
│ └── ...
├── predict.py # CLI: --match / --next / --simulate / --report
├── refresh.py # daily refresh of data + report + figures
├── polymarket_mcp.py # MCP server → LLM forecasting agent
├── agent_run.py # run the agent over the MCP tools
├── tests/ # sanity tests
├── docs/ # article, methodology, results, MCP/agent guide
└── assets/ # figures
The forecasting agent (MCP)
polymarket_mcp.py exposes the data + model + simulator as MCP tools, so any MCP-capable LLM client becomes a World Cup forecasting agent — pull live odds, get a model forecast, and simulate the bracket in plain English. Full wiring and an honest take on what the agent does (it relays the market, it doesn't out-predict it) in docs/MCP_AGENT.md.
Honest disclaimer
This is a study in forecasting methodology, not betting advice. The headline lesson is deliberately un-hyped: a foundation model is a tool with a shape — match it to the problem and it helps; otherwise a one-line baseline wins. The market remains the sharpest forecaster, and beating it is not the goal.
Acknowledgements
- Google Research — TimesFM
- martj42/international_results — match data
- Polymarket public API — prediction-market odds
- Prior Labs — TabPFN
License
MIT — see LICENSE.












