Chapter 48 Advanced ~50 min read

Win Probability Models

Building real-time win probability models for in-game analysis.

Win Probability Fundamentals

Win probability models estimate the likelihood of winning given current game state: score, time remaining, and possession. These models enable evaluation of in-game decisions (was that three-pointer a good choice given the situation?), broadcast enhancement, and strategic optimization.

Building Win Probability Models

Win probability models train on historical game data, learning how score margin and time relate to eventual outcomes. The relationship is roughly logistic: small leads are uncertain early but increasingly decisive as time decreases. Very large leads are near-certain regardless of time.

import numpy as np

def calculate_win_probability(score_margin, seconds_remaining, possession=0):
    """
    Calculate win probability based on game state

    possession: 1 if home has ball, -1 if away, 0 if neutral/unknown
    """
    # Logistic model parameters (fitted from historical data)
    intercept = 0.0
    margin_coef = 0.15
    time_margin_interaction = -0.0002

    # Adjust margin for possession value (~0.5 points)
    adjusted_margin = score_margin + possession * 0.5

    # Calculate log-odds
    log_odds = intercept + margin_coef * adjusted_margin + 
               time_margin_interaction * adjusted_margin * seconds_remaining

    # Convert to probability
    win_prob = 1 / (1 + np.exp(-log_odds))

    # Bound probabilities
    win_prob = max(0.001, min(0.999, win_prob))

    return round(win_prob, 3)

Applications

Win probability enables "expected wins added" analysis: how much did each play change win probability? Clutch players should show high win probability added in close games. Similarly, we can evaluate coaching decisions by whether they increased or decreased win probability.

Late Game Strategies

Win probability informs late game strategy. When should trailing teams foul? When should leading teams run clock versus attack? Win probability models provide quantitative answers to these questions, though actual implementation requires player-specific considerations (free throw ability, etc.).

Implementation in R

# Draft pick value analysis
library(tidyverse)

analyze_draft_value <- function(draft_history) {
  draft_history %>%
    group_by(pick) %>%
    summarise(
      n = n(),
      avg_ws = mean(career_ws, na.rm = TRUE),
      avg_vorp = mean(career_vorp, na.rm = TRUE),
      star_rate = mean(career_ws >= 40) * 100,
      bust_rate = mean(career_ws < 5) * 100,
      .groups = "drop"
    )
}

draft <- read_csv("draft_history.csv")
pick_value <- analyze_draft_value(draft)

# Value by pick
ggplot(pick_value, aes(x = pick, y = avg_ws)) +
  geom_line(color = "#1d428a", size = 1) +
  geom_point(aes(size = n), color = "#c8102e") +
  labs(title = "Career Win Shares by Draft Pick",
       x = "Pick", y = "Average Career Win Shares") +
  theme_minimal()

Implementation in R

# Draft pick value analysis
library(tidyverse)

analyze_draft_value <- function(draft_history) {
  draft_history %>%
    group_by(pick) %>%
    summarise(
      n = n(),
      avg_ws = mean(career_ws, na.rm = TRUE),
      avg_vorp = mean(career_vorp, na.rm = TRUE),
      star_rate = mean(career_ws >= 40) * 100,
      bust_rate = mean(career_ws < 5) * 100,
      .groups = "drop"
    )
}

draft <- read_csv("draft_history.csv")
pick_value <- analyze_draft_value(draft)

# Value by pick
ggplot(pick_value, aes(x = pick, y = avg_ws)) +
  geom_line(color = "#1d428a", size = 1) +
  geom_point(aes(size = n), color = "#c8102e") +
  labs(title = "Career Win Shares by Draft Pick",
       x = "Pick", y = "Average Career Win Shares") +
  theme_minimal()
Chapter Summary

You've completed Chapter 48: Win Probability Models.