Predict the most likely FIFA World Cup 2026 matchups for a selected scheduled match.
The project combines tournament-bracket rules, group-stage scenario search, FIFA rankings, and optional Polymarket market data to estimate which teams are most likely to meet in later knockout rounds.
- Accepts a target World Cup match as input, usually by scheduled
match_number. - Returns the most likely home/away matchup candidates for that match.
- Uses real completed match results when they are available in
data/matches_2026.json. - Serves predictions through both:
- a FastAPI API
- a Streamlit UI
The current default runtime path is:
FifaTeamPowerModelfor team strengthHybridPairwiseWinModelfor match probabilities- deterministic fallback to
RatingPairwiseWinModelwhenever market data is missing, stale, or low quality
That means the app is resilient even if Polymarket data is unavailable.
- Create and activate a virtual environment.
python -m venv .venv
.\.venv\Scripts\Activate.ps1- Install dependencies.
pip install -r requirements.txt- Run the API.
uvicorn src.api:app --reload- Run the UI.
streamlit run src/ui.pyFor live World Cup score refreshes, create a football-data.org API token and set:
$env:FOOTBALL_DATA_TOKEN = "<your token>"GET /health
Example response:
{
"status": "ok"
}POST /predict
Request body:
{
"match_id": "82"
}Example response:
{
"match_id": "82",
"status": "predicted",
"confirmed_matchup": null,
"top_candidates": [
{
"home_team": "Germany",
"away_team": "Mexico",
"score": 0.2145,
"reason": "Predicted via scenario-search simulation (group standings + knockout outcome branching)."
}
]
}Notes:
match_idshould match a key understood by the app. In practice, the prediction engine is designed around scheduled World Cupmatch_numbervalues such as"74"or"82".- The API currently returns
status="predicted"for prediction responses. - Group-stage matches are fixed in the UI and do not require probabilistic prediction.
POST /refresh-scores
Fetches the latest football-data.org World Cup scores into data/live_results_2026.json and clears cached
predictions.
GET /score-status
Returns score snapshot status, including matched, completed, unmatched, and last-error counts.
The Streamlit app provides:
- city and round/category filters
- a match picker for the 2026 tournament schedule
- fixed rendering for group-stage matches
- top matchup candidates for unresolved knockout matches
The UI entry point is src/ui.py.
The prediction engine combines tournament rules with probabilistic simulation.
-
Resolve the valid rule space. Only teams that can legally reach the selected match are considered.
-
Collapse uncertainty with completed results. If prerequisite matches have already been played and recorded, the candidate space narrows immediately.
-
Simulate group-stage outcomes. The engine uses beam search to explore likely group tables without exploding combinatorially.
-
Propagate knockout uncertainty. Winner and loser distributions flow through the bracket for unresolved matches.
-
Aggregate matchup probabilities. The final output is a ranked list of likely home/away pairings.
src/api.pyFastAPI app with/healthand/predict.src/ui.pyStreamlit frontend.src/engine.pyHigh-level prediction entry point and simulator wiring.src/tournament.pyTournament rule resolution, group-slot parsing, and valid-pair generation.src/world_ranking.pyScenario-search simulator and rating-based models.src/polymarket.pyMarket snapshot fetching, caching, and hybrid pairwise model.src/prediction_cache.pyStale-while-refresh prediction caching layer.src/models.pyRequest and response models.
Canonical tournament structure.
Expected to contain:
participantsgroupsschedule
Static ranking snapshot used by FifaTeamPowerModel.
Expected to contain:
participants_rankings
UI match list plus live/completed-result overrides.
Fields currently used by the app include:
match_number, or a parseablelabelcontainingMatch <n>statusorplayedconfirmed_home,confirmed_away- fallback team fields such as
home_team,away_team,home,away - score fields such as
home_goals,away_goals, withhome_score,away_scoreas fallback
Predictions are served through PredictionCacheService.
Current behavior:
- LRU cache with TTL
- stale-while-refresh reads
- per-match single-flight behavior on cold cache misses
- background refresh worker pool
- retry cooldown after refresh failures
Polymarket snapshots are cached separately in PolymarketSnapshotStore, including disk persistence of the last known good snapshot.
Live score snapshots are cached in data/live_results_2026.json. Failed refreshes keep the last known good snapshot.
Run tests:
pip install -r requirements-dev.txt
pytest -q testsRun lint:
.\.venv\Scripts\ruff.exe check src testsGitHub Actions runs:
- Ruff lint checks
- unit tests with
pytest
Workflow file:
- Predictions depend on the quality of the static tournament and ranking snapshots.
- Market-backed predictions fall back to rating-based estimates when market data is unavailable or unreliable.
- The API model supports
confirmed_matchup, but the current engine path primarily returns ranked predicted candidates.