Chapter 52 Advanced ~50 min read

Salary Cap and Contract Analysis

Using analytics to evaluate contracts and optimize roster construction under the salary cap.

Contract Value Assessment

NBA contracts should be evaluated by comparing player value provided to salary cost. A player providing +5 WAR for $30 million may be a better contract than one providing +3 WAR for $15 million, depending on alternative uses of cap space. Analytics enables systematic contract evaluation.

Dollars Per Win Framework

def evaluate_contract(war, salary, market_rate_per_war=8_000_000):
    """Evaluate contract value using wins framework"""
    expected_cost = war * market_rate_per_war
    surplus_value = expected_cost - salary

    return {
        'war': war,
        'salary': salary,
        'expected_cost': expected_cost,
        'surplus_value': surplus_value,
        'contract_grade': 'Excellent' if surplus_value > 10_000_000 else
                          'Good' if surplus_value > 0 else
                          'Fair' if surplus_value > -5_000_000 else
                          'Poor'
    }

Multi-Year Projections

Contract evaluation requires projecting value across the deal's length. A player who is excellent value in Year 1 may become negative value by Year 4 due to aging and rising salary. Total contract value sums projected value minus cost across all years.

Cap Management Strategy

Salary cap rules create complex optimization problems. Teams must balance current competitiveness with future flexibility, rookie deals with veteran contracts, and player quality with salary efficiency. Analytics informs these decisions through systematic contract valuation and roster simulation.

Implementation in R

# Referee tendency analysis
library(tidyverse)

analyze_referee_tendencies <- function(ref_data) {
  ref_data %>%
    group_by(referee_id, referee_name) %>%
    summarise(
      games = n(),
      avg_total_fouls = mean(total_fouls),
      avg_home_fouls = mean(home_fouls),
      avg_away_fouls = mean(away_fouls),
      home_foul_bias = mean(away_fouls - home_fouls),
      avg_techs = mean(technical_fouls),
      avg_flagrants = mean(flagrant_fouls),
      avg_pace = mean(pace),
      home_win_pct = mean(home_win),
      .groups = "drop"
    )
}

ref_games <- read_csv("referee_game_data.csv")
ref_analysis <- analyze_referee_tendencies(ref_games)

# Refs with home bias
home_bias <- ref_analysis %>%
  filter(games >= 50) %>%
  arrange(desc(home_foul_bias)) %>%
  select(referee_name, games, home_foul_bias, home_win_pct)

print(home_bias)

Implementation in R

# Referee tendency analysis
library(tidyverse)

analyze_referee_tendencies <- function(ref_data) {
  ref_data %>%
    group_by(referee_id, referee_name) %>%
    summarise(
      games = n(),
      avg_total_fouls = mean(total_fouls),
      avg_home_fouls = mean(home_fouls),
      avg_away_fouls = mean(away_fouls),
      home_foul_bias = mean(away_fouls - home_fouls),
      avg_techs = mean(technical_fouls),
      avg_flagrants = mean(flagrant_fouls),
      avg_pace = mean(pace),
      home_win_pct = mean(home_win),
      .groups = "drop"
    )
}

ref_games <- read_csv("referee_game_data.csv")
ref_analysis <- analyze_referee_tendencies(ref_games)

# Refs with home bias
home_bias <- ref_analysis %>%
  filter(games >= 50) %>%
  arrange(desc(home_foul_bias)) %>%
  select(referee_name, games, home_foul_bias, home_win_pct)

print(home_bias)
Chapter Summary

You've completed Chapter 52: Salary Cap and Contract Analysis.