Python Sports Modelling

Turning NFL Elo Ratings Into Win Probabilities

Introduction

Throughout this series we have progressively developed an NFL Elo model that estimates the underlying strength of every team. After each game, the ratings are updated as new evidence becomes available, producing our best estimate of each team's ability before the next round of fixtures.

That naturally raises the next modelling question:

"How do we turn an estimate of team strength into a prediction?"

An Elo rating is extremely useful, but it is not a forecast. Knowing that one team is rated higher than another tells us which team the model believes is stronger, but it does not directly answer the question we care about before kick-off:

"What is each team's probability of winning?"

To answer that question, we need to convert the difference between two Elo ratings into a win probability. This transformation allows the model to move beyond describing team strength and begin forecasting future outcomes.

In this article we introduce the standard Elo probability equation, explain why it produces its characteristic S-shaped curve, and show how Elo rating differences can be transformed into practical pre-game win probabilities.

Why Ratings Need Probabilities

Predictive models often estimate an underlying quantity that cannot be observed directly. In our Elo model, that hidden quantity is team strength.

Estimating team strength is an important step, but it is rarely the final objective. Analysts, coaches and decision-makers are usually interested in the likelihood of future events rather than the rating itself.

Elo therefore operates in two distinct stages. First, it estimates the relative strengths of the teams. Second, it converts those estimated strengths into the probability of each team winning before the game begins.

"Ratings estimate strength. Probabilities turn those estimates into forecasts."

From Ratings to Probabilities

Once we have estimated the relative strengths of two teams, the next step is to convert that information into a probability of winning.

A sensible probability model should satisfy several intuitive properties. If two teams have identical Elo ratings, they should each have approximately a 50% chance of winning. As the rating difference increases, the stronger team's probability should also increase. At the same time, probabilities must always remain between 0% and 100%.

These simple requirements place important constraints on the shape of the probability function. It should increase smoothly as Elo differences become larger, while becoming progressively less sensitive once one team is already a strong favourite.

"A good probability model increases confidence gradually while always remaining between 0% and 100%."

The standard Elo system satisfies these requirements using a logistic function. This produces the familiar S-shaped curve that maps every Elo rating difference onto a valid win probability.

The Elo Probability Equation

The standard Elo probability equation is:


p_home = 1 / (1 + 10 ** (-elo_diff / 400))

Here, elo_diff is the pre-match Elo rating difference between the home and away teams. Positive values favour the home team, while negative values favour the away team.

Regardless of how large or small the rating difference becomes, the equation always produces a probability between 0 and 1 while preserving the ordering implied by the Elo ratings.

"Elo ratings estimate team strength. The Elo probability equation converts those estimates into forecasts."

Before looking at why the Elo curve has its characteristic shape, try adjusting the ratings below. Notice how the predicted probability changes rapidly when the teams are evenly matched, but much more slowly once one team becomes a strong favourite.

Interactive Demo

Try It Yourself: Turning Elo Ratings Into Win Probabilities

Adjust the two team ratings to see how the Elo probability equation converts a rating difference into a forecast.

Raw Elo Difference +100 Elo
Adjusted Elo Difference +165 Elo
Predicted Win Probability
Home
72.1%
Away
27.9%
Probability Mapping
1600 + 65 − 1500
+165 Elo difference
72.1% home win probability
Live Equation 1 / (1 + 10 ^ (−165 / 400))
= 72.1%
Elo Probability Curve
100% 50% 0%
−300 0 Elo +300
Chart shown over −300 to +300 Elo.
Why did this happen?

The home team has a clear Elo advantage after applying home advantage, so the probability moves above 50%, but the logistic curve still leaves meaningful room for an upset.

Understanding the Elo Scale

One value in the Elo probability equation immediately stands out: 400. Although it may appear arbitrary, this constant determines how rapidly predicted probabilities change as the Elo rating difference increases.

The value of 400 controls the steepness of the logistic curve. Smaller values produce a steeper curve, meaning relatively small rating differences lead to more confident predictions. Larger values flatten the curve, making the predicted probabilities change more gradually as Elo differences increase.

In the standard Elo framework, the scale is fixed at 400. Throughout this series we use the standard formulation so that our probability estimates remain directly linked to the Elo rating system developed in the previous articles.

"The Elo scale determines how quickly confidence increases as the rating difference grows."

Applying Home Advantage

So far we have considered only the difference between the two team ratings. However, NFL games are not played on neutral fields.

Home teams have a measurable historical advantage, so before converting Elo differences into probabilities we adjust the home team's rating by a fixed number of Elo points. This shifts the starting position on the probability curve while leaving the shape of the curve unchanged.

"Home advantage shifts the probability curve. It does not change the curve itself."

For the purposes of this article we use a home advantage of 65 Elo points to illustrate the calculation. This is an educational example rather than an optimisation exercise. In production forecasting systems, this parameter is typically estimated from historical data and may differ depending on the sport, competition or modelling approach.

We therefore calculate an adjusted Elo difference before applying the probability equation.

Preparing the Data

To apply the Elo probability equation, we first need the pre-match ratings for both teams. These ratings were generated by the Elo model developed throughout the previous articles and represent our best estimate of each team's strength immediately before kick-off.

For this analysis we focus on non-neutral games and construct three variables that will be used throughout the remainder of the article:

  • Raw Elo difference — the difference between the home and away pre-match Elo ratings.
  • Home-adjusted Elo difference — the same rating difference after applying the home advantage adjustment.
  • Home win indicator — a binary variable recording whether the home team won the game.

These variables allow us to calculate Elo probabilities and compare those predictions with the outcomes that actually occurred across thousands of historical NFL games.

"A probability model is judged by how closely its predictions match reality."

The complete Python implementation is shown below.


# ============================================================
# ARTICLE 7 — CODE BLOCK 1
# Prepare the Elo dataset for probability calculations
# ============================================================

import polars as pl

# ------------------------------------------------------------------
# Configuration
# ------------------------------------------------------------------

ELO_PATH = "nfl_elo_game_level_home_mov_season.pq"

# Educational example used throughout this article.
# Production forecasting systems will typically estimate this value
# from historical data.

HOME_ADVANTAGE = 65.0

# Standard Elo probability scale
ELO_SCALE = 400.0

# ------------------------------------------------------------------
# Load the game-level Elo dataset
# ------------------------------------------------------------------

df = pl.read_parquet(ELO_PATH)

# ------------------------------------------------------------------
# Restrict the analysis to non-neutral games
# ------------------------------------------------------------------

df = df.filter(~pl.col("neutral"))

# ------------------------------------------------------------------
# Calculate the match outcome and Elo rating differences
# ------------------------------------------------------------------

df = df.with_columns(

    # Binary indicator showing whether the home team won
    (pl.col("home_score") > pl.col("away_score"))
        .cast(pl.Int8)
        .alias("home_win"),

    # Raw pre-match Elo difference
    (pl.col("home_elo_pre") - pl.col("away_elo_pre"))
        .alias("elo_diff_raw"),

    # Home-adjusted Elo difference used when calculating
    # deterministic Elo probabilities
    (pl.col("home_elo_pre") + HOME_ADVANTAGE - pl.col("away_elo_pre"))
        .alias("elo_diff_adj")
)

# ------------------------------------------------------------------
# Display a small sample of the prepared dataset
# ------------------------------------------------------------------

print(
    df.select([
        "home_team",
        "away_team",
        "home_elo_pre",
        "away_elo_pre",
        "elo_diff_raw",
        "elo_diff_adj",
        "home_win"
    ]).head(5)
)

Calculating Elo Probabilities

Once the pre-match Elo differences have been prepared, converting them into win probabilities becomes a straightforward calculation.

The Elo probability equation is deterministic. Given the same Elo rating difference, it will always produce exactly the same predicted probability. There are no random elements and no parameters are learned during this stage of the modelling process.

We therefore calculate probabilities in two ways:

  • Raw Elo probability — calculated directly from the pre-match Elo difference.
  • Home-adjusted Elo probability — calculated after applying the home advantage adjustment described earlier.

Comparing these two versions allows us to see the practical effect of introducing a home advantage baseline while leaving every other part of the Elo model unchanged.

"Deterministic models always produce the same prediction when given the same inputs."

The complete Python implementation is shown below.


# ============================================================
# ARTICLE 7 — CODE BLOCK 2
# Convert Elo rating differences into deterministic probabilities
# ============================================================

# ------------------------------------------------------------------
# Apply the standard Elo probability equation
# ------------------------------------------------------------------

df = df.with_columns(

    # Probability calculated using the raw Elo difference
    (
        1.0 /
        (1.0 + (10.0 ** (-pl.col("elo_diff_raw") / ELO_SCALE)))
    ).alias("p_home_raw"),

    # Probability calculated after applying the home
    # advantage adjustment
    (
        1.0 /
        (1.0 + (10.0 ** (-pl.col("elo_diff_adj") / ELO_SCALE)))
    ).alias("p_home_adj")
)

# ------------------------------------------------------------------
# Summarise the predicted probabilities
# ------------------------------------------------------------------

summary = df.select([
    pl.len().alias("games"),
    pl.mean("home_win").alias("avg_actual_home_win"),
    pl.mean("p_home_raw").alias("avg_pred_raw"),
    pl.mean("p_home_adj").alias("avg_pred_home_adj"),
]).to_dicts()[0]

# ------------------------------------------------------------------
# Display the summary statistics
# ------------------------------------------------------------------

print(summary)

Understanding the Elo Probability Curve

Before comparing Elo probabilities with historical NFL results, it is helpful to understand how the probability model behaves across the full range of possible rating differences.

The Elo probability equation produces a smooth relationship between rating differences and predicted win probabilities. When two teams have identical ratings, the model predicts they are evenly matched. As one team's Elo advantage increases, its predicted probability of winning also increases, but at a gradually decreasing rate.

This behaviour produces the characteristic S-shaped logistic curve. Small changes in Elo have the greatest effect when teams are closely matched, while increasingly larger rating differences are required once one team is already a strong favourite.

"Confidence increases rapidly between evenly matched teams, but more gradually as one team becomes dominant."

To illustrate the effect of home advantage, we plot the probability curve twice:

  • Raw Elo mapping — using only the difference between the two pre-match Elo ratings.
  • Home-adjusted mapping — after applying the home advantage adjustment before calculating the probability.

Comparing the two curves demonstrates an important property of the Elo model. Home advantage shifts the probability calculation horizontally along the Elo scale, while leaving the shape of the logistic curve completely unchanged.

"Home advantage changes where we start on the probability curve. It does not change the curve itself."

The complete Python implementation is shown below.


# ============================================================
# ARTICLE 7 — CODE BLOCK 3
# Visualise the Elo probability curve with and without home advantage
# ============================================================

import matplotlib.pyplot as plt
import numpy as np

# ------------------------------------------------------------------
# Configuration
# ------------------------------------------------------------------

SAVE_PATH = "logistic_home_shift.png"

# Evaluate a wide range of Elo rating differences
xs = np.arange(-300, 301, 5)

# ------------------------------------------------------------------
# Calculate the Elo probability curves
# ------------------------------------------------------------------

# Standard Elo probability curve
p_raw = 1.0 / (1.0 + (10.0 ** (-xs / ELO_SCALE)))

# Probability curve after applying the home advantage adjustment
p_adj = 1.0 / (1.0 + (10.0 ** (-(xs + HOME_ADVANTAGE) / ELO_SCALE)))

# ------------------------------------------------------------------
# Create the comparison chart
# ------------------------------------------------------------------

plt.figure(figsize=(10, 6))

plt.plot(
    xs,
    p_raw,
    label="Raw Elo mapping"
)

plt.plot(
    xs,
    p_adj,
    label=f"Home-adjusted (+{HOME_ADVANTAGE:.0f} Elo)"
)

plt.xlabel("Elo difference (Home − Away)")
plt.ylabel("Home win probability")
plt.title("Elo Logistic Mapping With Home Advantage")
plt.grid(alpha=0.30)
plt.legend()
plt.tight_layout()

# ------------------------------------------------------------------
# Save and display the completed chart
# ------------------------------------------------------------------

plt.savefig(SAVE_PATH, dpi=200)
plt.show()

print("Saved:", SAVE_PATH)

Elo logistic probability curve with and without home advantage

Comparing Predictions with Historical Results

The logistic curve describes how the Elo model converts rating differences into win probabilities. The next step is to compare those predicted probabilities with what actually happened across thousands of historical NFL games.

To do this, we group games with similar adjusted Elo differences and calculate two quantities for each group:

  • Empirical home win percentage — the proportion of games actually won by the home team.
  • Average Elo probability — the average probability predicted by the Elo model for the same group of games.

Plotting these two relationships together allows us to compare the deterministic Elo probability model with the historical outcomes observed across the dataset.

"A probability model becomes meaningful when its predictions can be compared with real outcomes."

This is not yet a formal calibration exercise. Instead, it provides an intuitive visual comparison between the probabilities implied by the Elo model and the outcomes observed in historical NFL games.

The complete Python implementation is shown below.


# ============================================================
# ARTICLE 7 — CODE BLOCK 4
# Compare empirical win rates with Elo probabilities
# ============================================================

import matplotlib.pyplot as plt

# ------------------------------------------------------------------
# Configuration
# ------------------------------------------------------------------

BIN_SIZE = 25
MIN_ELO_DIFF = -300
MAX_ELO_DIFF = 300

SAVE_PATH = "empirical_vs_logistic.png"

# ------------------------------------------------------------------
# Restrict extreme Elo differences
# ------------------------------------------------------------------

# Large Elo differences are relatively uncommon.
# Clipping the values keeps the comparison focused on the range where
# sufficient historical data is available.

tmp = df.with_columns(
    pl.col("elo_diff_adj")
      .clip(MIN_ELO_DIFF, MAX_ELO_DIFF)
      .alias("elo_diff_clipped")
)

# ------------------------------------------------------------------
# Group games into 25-point Elo intervals
# ------------------------------------------------------------------

tmp = tmp.with_columns(
    (
        (pl.col("elo_diff_clipped") / BIN_SIZE)
        .floor()
        * BIN_SIZE
    ).alias("elo_diff_bin")
)

# ------------------------------------------------------------------
# Calculate empirical outcomes and average Elo probabilities
# ------------------------------------------------------------------

rates = (
    tmp.group_by("elo_diff_bin")
       .agg([
           pl.len().alias("games"),

           # Historical home win percentage
           pl.mean("home_win").alias("emp_home_win_pct"),

           # Average deterministic Elo probability
           pl.mean("p_home_adj").alias("avg_pred_prob"),
       ])
       .sort("elo_diff_bin")
)

# ------------------------------------------------------------------
# Display a sample of the aggregated results
# ------------------------------------------------------------------

print(rates.head(10))

# ------------------------------------------------------------------
# Create the comparison chart
# ------------------------------------------------------------------

plt.figure(figsize=(10, 6))

plt.plot(
    rates["elo_diff_bin"],
    rates["emp_home_win_pct"],
    label="Empirical home win rate"
)

plt.plot(
    rates["elo_diff_bin"],
    rates["avg_pred_prob"],
    label="Average Elo probability"
)

plt.xlabel("Adjusted Elo difference (Home − Away)")
plt.ylabel("Home win rate / predicted probability")
plt.title("Empirical Outcomes vs Deterministic Elo Probabilities")
plt.grid(alpha=0.30)
plt.legend()
plt.tight_layout()

# ------------------------------------------------------------------
# Save and display the completed chart
# ------------------------------------------------------------------

plt.savefig(SAVE_PATH, dpi=200)
plt.show()

print("Saved:", SAVE_PATH)

Empirical home win rates compared with Elo predicted probabilities

Interpreting the Results

The chart shows that the probabilities produced by the standard Elo equation closely follow the historical home win rates across the full range of adjusted Elo differences.

The empirical win rates fluctuate slightly from one Elo interval to the next, which is expected because each interval contains a finite number of historical games. This effect is most noticeable at the extreme ends of the Elo scale, where large rating differences occur relatively infrequently.

Despite this natural variation, the overall relationship remains remarkably consistent. As the adjusted Elo difference increases, both the predicted probability and the historical home win percentage increase smoothly, demonstrating that the Elo probability equation captures the broad relationship between team strength and match outcomes.

"Good forecasting models capture the underlying signal, not every fluctuation in the data."

This is an encouraging result because the probabilities are generated directly from the standard Elo equation. No additional optimisation or curve fitting has been performed against the historical NFL results presented here.

The purpose of this article is not to demonstrate perfect calibration. Rather, it shows that the standard Elo probability mapping provides a logical and interpretable baseline for converting rating differences into pre-game win probabilities.

Why This Matters

Building a forecasting model involves two separate steps. First, we estimate the relative strengths of the teams. Second, we convert those strength estimates into probabilities and evaluate how well those probabilities describe real outcomes.

This article completes the first stage of that process. We now have a complete, deterministic forecasting model capable of producing pre-game win probabilities for every NFL match.

The next step is to evaluate those probabilities objectively using proper forecasting metrics rather than visual inspection alone.

What Have We Learned?

Throughout this article we have completed the transition from Elo ratings to probability forecasting. Rather than stopping at an estimate of team strength, we have shown how Elo rating differences can be converted into practical pre-game win probabilities using the standard Elo probability equation.

"How do we turn an estimate of team strength into a forecast?"

The Elo probability equation provides a simple and elegant answer. Every Elo rating difference maps directly to a win probability, allowing the model to move from describing team strength to making quantitative predictions about future games.

We also demonstrated that home advantage shifts the probability calculation without changing the underlying logistic relationship, producing probabilities that remain consistent with the interpretation of the Elo rating scale developed throughout the previous articles.

Key Takeaways

  • Elo ratings estimate the relative strengths of competing teams.
  • The Elo probability equation converts those strength estimates into win probabilities.
  • The logistic mapping produces smooth probabilities that always remain between 0% and 100%.
  • Home advantage shifts the probability baseline without changing the shape of the logistic curve.
  • Forecasting begins by estimating team strength, but it is probabilities that make those estimates useful.

Next Steps

We now have a complete Elo forecasting model capable of producing a pre-game win probability for every NFL game.

The next step is not to change those probabilities, but to measure how well they perform against historical results using objective forecasting metrics.

That naturally raises our next modelling question:

"How do we measure whether a forecasting model is making good predictions?"

In the next article, Evaluating NFL Elo Forecast Accuracy , we introduce calibration analysis, proper scoring rules and objective forecasting metrics to assess how well the Elo probabilities describe real NFL outcomes.

Continue the Series