WorldCup26: Tempo: The World Cup Forecasting Engine That Refuses to Be a Black Box
A production-grade Python system for calibrated predictions, live bracket simulation, and explainable soccer odds that only promotes models after they beat Elo.
- Tempo makes complexity earn its right to exist by promoting only models that beat a simpler Elo baseline on the same frozen test set.
- Calibration is not a polish layer here, it is the thing that makes a forecast usable because the probabilities are meant to behave like real frequencies.
- The repository is built like a production system, with clean layer boundaries, daily refresh orchestration, and artifacts that move from training into prediction without notebook glue.
- Explainability is part of the product, because Tempo turns each match probability into factor-level reasoning that the UI can expose directly.
The model has to earn its place
Most sports predictors promise smarter numbers. Tempo starts somewhere more useful: it asks whether the smarter model is actually better than a simple Elo-logistic baseline. That gatekeeping logic is the article in miniature. Complexity is optional, but evidence is not.
| Dimension | Baseline Elo-logistic | Candidate XGBoost |
|---|---|---|
| Complexity | Small and interpretable | Richer feature interactions |
| Role | Reference point | Must prove incremental value |
| Deployment | Always available | Only promoted if it wins |
| Trust signal | Simplicity | Measured improvement |
| Failure mode | May miss nuance | Can be overfit or overconfident |
That structure matters because it changes the burden of proof. The baseline is not a fallback after things go wrong. It is a standing challenger that keeps the whole system honest.
Calibration is the difference between smart and usable
Raw probabilities are seductive and often wrong. Tempo uses isotonic calibration so that a forecast labeled 70 percent is meant to behave like a 70 percent event over time. In sports, that distinction is everything. A model that is merely sharp but poorly calibrated can still mislead coaches, fans, and analysts.
| Question | Uncalibrated score | Calibrated forecast |
|---|---|---|
| What does 70% mean? | A model output that may be overconfident | A probability tied to observed frequency |
| How should it be used? | Cautiously | Directly in decisions and simulations |
| What does it optimize? | Ranking or separation | Trust in numeric meaning |
| What does the UI show? | A number | A number that is easier to believe |
This is where Tempo stops feeling like a toy. The system does not just rank teams. It tries to make each number defensible.
Tempo’s pipeline is built like a production system, not a notebook
The repository is organized into three layers: data, intelligence, and presentation. That separation is not cosmetic. It keeps ingestion, modeling, and UI concerns from leaking into one another, which is exactly how a forecasting project avoids becoming a tangled script collection.
| Layer | Job | Why it matters |
|---|---|---|
| Data layer | Ingests results, fixtures, and overrides | Keeps inputs reproducible |
| Intelligence layer | Trains, calibrates, explains, simulates | Holds the forecasting logic |
| Presentation layer | Renders the dashboard and routing | Keeps the model visible without exposing internals |
The daily refresh flow is the heartbeat: ingest, feature build, train, calibrate, promote, predict. That sequence is what makes the repo feel durable. It is not asking a human to remember what to rerun.
The simulator turns a 48-team tournament into 50,000 plausible futures
Tempo does not stop at match probabilities. It pushes them through a Monte Carlo engine that simulates a tournament shape, including messy details like best third-place advancement and live-aware state. That means played matches are fixed, and only the remaining fixtures are allowed to fan out into thousands of futures.
This is the right way to think about a World Cup model. A bracket is not a prediction table. It is a branching system where one result changes the path of several later rounds.
| Simulation challenge | Naive approach | Tempo approach |
|---|---|---|
| Played matches | Simulate everything from scratch | Treat completed games as fixed |
| Third-place advancement | Hand-wave the edge cases | Encode the advancement rules explicitly |
| Tiebreakers | Ignore or simplify | Model goals scored and goal difference |
| Future paths | A single bracket | Thousands of Monte Carlo futures |
The UI behaves like a dashboard, not a Streamlit demo
Tempo’s frontend is still Streamlit, but it does not feel like a default Streamlit app. Custom routing through query parameters gives it bookmarkable navigation. The result is a dashboard that behaves more like a product surface than a quick prototype.
| Default Streamlit | Tempo presentation layer |
|---|---|
| Sidebar-centric navigation | Query-param routing with page state |
| Prototype feel | Broadcast-console style dashboard |
| Raw model output | Waterfall charts and explainability artifacts |
| Loose coupling | Layer boundaries that keep ML imports out of the UI |
That presentation choice is subtle and important. If the system is trying to earn trust, the interface has to make the reasoning easy to inspect.
Why this repo feels unusually mature
The maturity shows up in the boring places. There are leakage tests, Monte Carlo boundary tests, typed Python, timestamped artifacts, and automation that keeps the refresh cycle moving. None of that is flashy. All of it is what makes the system believable.
Tempo reads like a project built by someone who understands that forecasting is not only about better numbers. It is about model governance, reproducibility, and the willingness to let a simple baseline say no.
| Sign of maturity | What it signals |
|---|---|
| Leakage tests | The author cares about validity, not just accuracy |
| Timestamped model artifacts | Training is operational, not ad hoc |
| Layer boundaries | The codebase can grow without collapsing into a notebook |
| Daily refresh orchestration | The system is meant to live, not just demo |