Here's the Monte Carlo bit. It's a five-step loop:
- Rate every team from results, xG and whatever else goes in.
- Price every remaining fixture with the match model above.
- Play each fixture once at random, drawing a scoreline from those probabilities and adding the points to the table. Matches already played stay exactly as they happened.
- Repeat the whole season thousands of times. 10,000 runs is a common choice, and each run is one possible version of 2026/27.
- Count. Title chance is the share of runs where a team finished top. Relegation chance is the share where they finished 18th, 19th or 20th.
Those famous percentages are just tallies. Win the league in 4,300 of 10,000 simulated seasons and you're on 43%. With 50 of the 380 matches played by 20 September, a Premier League model is replaying around 330 fixtures every run.
Below is a working version for a made-up four-team league that plays home and away. It takes a minute to read:
import random, math
random.seed(7)
teams = {"Reds": (1.9, 0.9), "Blues": (1.7, 1.0), "Whites": (1.4, 1.2), "Greens": (1.2, 1.3)} # (attack, defence)
AVG, HOME = 1.35, 1.15
def poisson(lam):
L, k, p = math.exp(-lam), 0, 1.0
while True:
p *= random.random()
if p <= L:
return k
k += 1
def play(h, a):
lam_h = AVG * (teams[h][0] / AVG) * (teams[a][1] / AVG) * HOME
lam_a = AVG * (teams[a][0] / AVG) * (teams[h][1] / AVG) / HOME
return poisson(lam_h), poisson(lam_a)
fixtures = [(h, a) for h in teams for a in teams if h != a]
N, titles = 10_000, dict.fromkeys(teams, 0)
for _ in range(N):
pts, gd = dict.fromkeys(teams, 0), dict.fromkeys(teams, 0)
for h, a in fixtures:
x, y = play(h, a)
gd[h] += x - y; gd[a] += y - x
if x > y: pts[h] += 3
elif x < y: pts[a] += 3
else: pts[h] += 1; pts[a] += 1
champion = max(teams, key=lambda t: (pts[t], gd[t], random.random()))
titles[champion] += 1
print({t: titles[t] / N for t in teams})
Run it and the Reds, comfortably the best side on paper, win the title in only about 53% of seasons. The Blues take roughly 31%, the Whites around 11% and the Greens about 4-5%. The Reds beat the Greens at home 70% of the time, and over a short season the best team still misses out almost half the time. That's how noisy football is.