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
10 cells1 experiment38 views0 forks

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 = {
…
2026 FIFA World Cup Winner Mathematical Forecast (June 29, 2026) | Clusy