2026 FIFA World Cup Winner Mathematical Forecast (June 29, 2026)
By Ju Lin · Published June 30, 2026
Probabilistic model using Bradley-Terry ratings and Monte Carlo simulation (20,000 trials) to forecast World Cup 2026 winner probabilities based on Elo ratings and group-stage performance.
- sports-analytics
- monte-carlo
- probability-modeling
- forecasting
- elo-ratings
Inside this notebook
# 2026 FIFA World Cup Winner — Mathematical Forecast **Forecast date:** June 29, 2026 (Round of 32 underway) **Method:** Bradley-Terry probability model + Monte Carlo simulation (20,000 trials) **Data:** World Football Elo Ratings · FIFA Rankings · Group-stage performance stats --- This notebook builds a probabilistic model using pre-tournament Elo ratings adjusted for observed World Cup 2026 performance, then simulates the full knockout bracket 20,000 times to estimate each team's chance of lifting the trophy. **Already decided in the Round of 32:** Canada ✓, Brazil ✓, Paraguay ✓ (upset over Germany on penalties). Netherlands vs Morocco kicks off tonight.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
import seaborn as sns
from collections import Counter, defaultdict
import warnings
warnings.filterwarnings('ignore')
np.random.seed(42)
print(f"NumPy: {np.__version__}")
print(f"Pandas: {pd.__version__}")
print("Notebook ready.")NumPy: 2.1.2 Pandas: 2.2.3 Notebook ready.
# ============================================================
# 1. DATA — Team strengths & tournament state (June 29, 2026)
# ============================================================
# World Football Elo ratings (current, post-group-stage — eloratings.net)
elo_ratings = {
'Argentina': 2148, 'Spain': 2144, 'France': 2123,
'England': 2038, 'Brazil': 2009, 'Colombia': 2004,
'Portugal': 1990, 'Netherlands': 1980, 'Norway': 1918,
'Switzerland': 1914, 'Mexico': 1912, 'Croatia': 1905,
'Ecuador': 1902, 'Belgium': 1884, 'Morocco': 1877,
'Senegal': 1842, 'Austria': 1836, 'Paraguay': 1815,
'Australia': 1800, 'Algeria': 1785, 'USA': 1781,
'Canada': 1764, 'Ivory Coast': 1743, 'Sweden': 1742,
'Egypt': 1742, 'DR Congo': 1652, 'Cape Verde': 1622,
'Bosnia': 1620, 'Ghana': 1584,
}
…Teams alive: 29
Elo range: 1,584 – 2,148
Avg GD/game: +0.84
elo gd_pg fifa_rank
team
Argentina 2148 2.333333 1
Spain 2144 1.666667 2
France 2123 2.666667 3
England 2038 1.333333 4
Brazil 2009 2.000000 6
Colombia 2004 1.000000 13
Portugal 1990 1.666667 5
Netherlands 1980 2.000000 8
Norway 1918 0.333333 31
Switz…## 2. Modeling Approach ### Bradley-Terry Model Each match is treated as a pairwise comparison. The probability that team A beats team B is given by: \[ P(A \text{ beats } B) = \frac{1}{1 + 10^{-(\text{Elo}_A - \text{Elo}_B + \text{form}) / 400}} \] where **Elo** is the team's current World Football Elo rating, and **form** is a confidence-weighted adjustment from group-stage goal differential per game: \[ \text{form} = 50 \times (\text{GD}_{\text{per game}, A} - \text{GD}_{\text{per game}, B}) \] ### Monte Carlo Simulation The full bracket is simulated 20,000 times. Already-decided Round-of-32 results (Canada, Brazil, Paraguay) are baked in; every other match is a probabilistic coin-flip weighted by the Bradley-Terry probabilities. The result is a distribution over the eventual winner.
# ============================================================
# 2. BRADLEY-TERRY MODEL
# ============================================================
def win_prob(elo_a, elo_b, form_a=0, form_b=0, form_weight=50):
"""
Probability team A beats team B.
Parameters
----------
elo_a, elo_b : int — Current Elo ratings
form_a, form_b : float — GD per game in group stage
form_weight : float — Scaling factor for the form adjustment
Returns
-------
float between 0 and 1
"""
…Sanity checks — Bradley-Terry probabilities -------------------------------------------------- France vs Canada → France wins: 91.3% Paraguay vs France → Paraguay wins: 6.1% Argentina vs Brazil → Argentina wins: 71.0% Spain vs Portugal → Spain wins: 70.8% England vs DR Congo → England wins: 92.5% Model ready for simulation.
# ============================================================
# 3. FULL BRACKET SIMULATION (20,000 trials)
# ============================================================
def simulate_tournament(df):
"""Simulate the full knockout bracket once. Returns the winner."""
# ---- Round of 32 ----
r32 = {
# Already decided
'M73': 'Canada', # beat South Africa 1-0
'M74': 'Paraguay', # beat Germany on penalties
'M76': 'Brazil', # beat Japan 2-1
# Still to play — simulate each
'M75': simulate_match('Netherlands', 'Morocco', df),
'M77': simulate_match('Ivory Coast', 'Norway', df),
'M78': simulate_match('France', 'Sweden', df),
'M79': simulate_match('Mexico', 'Ecuador', df),
…2026 WORLD CUP WINNER — Monte Carlo Forecast (20,000 simulations) ====================================================== Team Prob Wins Elo GD/g ------------------------------------------------ Argentina 32.0% 6405 2148 +2.33 France 28.3% 5657 2123 +2.67 Spain 17.5% 3506 2144 +1.67 Brazil 6.2% 1239 2009 +2.00 England 4.9% 973 2038 +1.33 Netherlands…
## 4. Results & Visualization The Monte Carlo simulation reveals a clear hierarchy among the 29 teams still alive. Below we visualise the win probabilities, semifinal/final pathways, and bracket-tier breakdown.
# ============================================================
# 4a. Win-Probability Bar Chart — Top 16 Teams
# ============================================================
# ---- Aesthetic setup ----
sns.set_style('white')
plt.rcParams.update({
'font.size': 12,
'axes.labelcolor': '#2c3e50',
'axes.edgecolor': '#bdc3c7',
'axes.spines.top': False,
'axes.spines.right': False,
'xtick.color': '#7f8c8d',
'ytick.color': '#4a4a4a',
'figure.facecolor': 'white',
})
PALETTE = {
…