worldcup-ai-forecaster
by tpomerance
README.md
# ⚽ 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`](docs/article.md) · 🔬 **Methodology:** [`docs/METHODOLOGY.md`](docs/METHODOLOGY.md) · 📊 **All results:** [`docs/RESULTS.md`](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
```bash
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`](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`](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](https://github.com/google-research/timesfm)
- [martj42/international_results](https://github.com/martj42/international_results) — match data
- [Polymarket](https://polymarket.com) public API — prediction-market odds
- Prior Labs — [TabPFN](https://github.com/PriorLabs/tabpfn)
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues