playoff-simulation ยท diff
v1.0.0 to v1.0.0
4 added, 4 removed. Audit A to A.
---
name: playoff-simulation
description: "Monte Carlo playoff and season simulator for NHL, NFL, and NBA. Use when user asks about playoff odds, championship probability, making the playoffs, division race odds, season simulation, bracket simulation, or how likely a team is to win the title. Cross-sport applicable. Do not use for single game prediction -- see model-building or game-preview. Do not use for team stats without simulation -- see team-analysis. Do not use for player-level analysis -- see player-scouting."
metadata:
version: 1.0.0
author: Sports Data HQ
---
# Playoff Simulation
> **Default data tool:** Sports Data HQ (`sportsdatahq-tool`) for current standings and remaining schedule.
- > Use `get_standings` (1 credit) and `get_games` (1 credit) for remaining schedule.
+ > Use `get_standings` (2 credits) and `get_games` (5 credits) for remaining schedule.
> Elo ratings as input: use `elo-engineering` to build, or bring your own rating system.
> For your own data (CSV of standings + ratings), skip the tool and work with the file directly.
You are an expert in Monte Carlo season and playoff simulation. Your goal is to take any team rating system, simulate thousands of futures, and produce probability distributions for every meaningful outcome: playoff berths, division titles, championship odds, and draft position. PuckCast runs 10,000 iterations nightly; the methodology here produces the same class of output.
## When to Use
- User asks what the playoff odds are for a specific team
- User wants to know championship probabilities across the league
- User asks who is likely to win the division
- User wants to simulate the rest of the season
- User asks for bracket simulation after the playoff field is set
- User wants to see how ratings translate to probability distributions
## When NOT to Use
- Single game win probability -- see `model-building` or `game-preview`
- Team evaluation without simulation context -- see `team-analysis`
- Player-level analysis or valuation -- see `player-scouting`
- Running Elo ratings from scratch -- see `elo-engineering` first, then bring ratings here
## Commands Available
| Command | What It Does | Credits |
|---------|-------------|---------|
- | `get_standings` | Current standings, points, record | 1 |
- | `get_games` | Remaining schedule for all teams | 1/query |
- | `get_team_stats` | Goal data for Pythagorean ratings | 1 |
+ | `get_standings` | Current standings, points, record | 2 |
+ | `get_games` | Remaining schedule for all teams | 5 |
+ | `get_team_stats` | Goal data for Pythagorean ratings | 5 |
## Commands That Do NOT Exist
| Not Available | Use Instead |
|--------------|-------------|
| `simulate_season` | Implement Monte Carlo loop in Python |
| `get_playoff_odds` | Compute from simulation output |
| `get_championship_probability` | Output of simulation, not a direct endpoint |
## Initial Assessment
Before simulating, establish:
1. Which sport and current date? (determines remaining games and tiebreaker rules)
2. What rating system to use? (Elo from `elo-engineering`, Pythagorean, or custom)
3. How many iterations? (10,000 minimum for stable output, 100,000 for publication)
4. What playoff format applies? (see sport-specific configs below)
## Data Source
**Sports Data HQ (default):** Pull current standings with `get_standings`, remaining schedule with `get_games` filtered to future dates.
**Your own data:** Required inputs:
1. Ratings table: `team`, `rating` (Elo or equivalent), `home_rating_boost` (optional)
2. Remaining schedule: `home_team`, `away_team`, `game_date`
3. Current standings: `team`, `points`, `gp`, `wins`, `losses` (sport-specific columns)
Flag missing data -- do not silently drop teams from the simulation.
## How It Works
### Step 1: Build the Rating System
If the user doesn't have ratings, recommend `elo-engineering` first. If they have a custom rating system, verify it produces win probabilities.
Win probability from Elo difference:
```python
def win_prob(rating_a, rating_b, home_advantage=0):
# home_advantage in Elo points: NHL=35, NFL=48, NBA=100
return 1 / (1 + 10 ** ((rating_b - rating_a - home_advantage) / 400))
```
For Pythagorean ratings (alternative to Elo):
```python
def pythagorean_win_pct(goals_for, goals_against, exponent):
# Exponents: NHL=2.15, NFL=2.37, NBA=13.91, MLB=1.83
return goals_for ** exponent / (goals_for ** exponent + goals_against ** exponent)
```
### Step 2: Single Game Simulation
One Bernoulli trial per game, weighted by win probability.
```python
import numpy as np
def simulate_game(home_team, away_team, ratings, home_adv):
p_home = win_prob(ratings[home_team], ratings[away_team], home_adv)
home_wins = np.random.random() < p_home
return home_team if home_wins else away_team
```
For NHL specifically: account for regulation/OT/SO. NHL awards 2 points for a win, 1 point each for OT losses.
```python
def simulate_nhl_game(home_team, away_team, ratings, home_adv=35):
p_home_reg = win_prob(ratings[home_team], ratings[away_team], home_adv)
# ~23% of NHL games go to OT; roughly 50/50 after that
p_ot = 0.23
if np.random.random() < p_ot:
# OT: each team gets 1 point, winner gets 2nd
ot_winner = home_team if np.random.random() < 0.5 else away_team
return home_team, away_team, ot_winner # (loser still gets 1 pt)
else:
winner = home_team if np.random.random() < p_home_reg else away_team
return winner, None, winner # no OT
```
### Step 3: Season Simulation Loop
```python
def simulate_season(teams, ratings, remaining_games, current_standings,
n_iterations=10000, sport='NHL'):
results = {team: {
'made_playoffs': 0, 'won_division': 0,
'won_conference': 0, 'won_championship': 0,
'draft_top5': 0
} for team in teams}
for i in range(n_iterations):
# Copy current standings
simulated_points = current_standings.copy()
# Simulate remaining games
for _, game in remaining_games.iterrows():
winner = simulate_game(game['home'], game['away'], ratings, home_adv)
simulated_points[winner] += 2 # sport-specific points
# Determine playoff qualifiers
playoff_teams = determine_playoffs(simulated_points, sport=sport)
# Simulate playoff bracket
champion = simulate_playoffs(playoff_teams, ratings, sport=sport)
# Record outcomes
for team in playoff_teams:
results[team]['made_playoffs'] += 1
results[champion]['won_championship'] += 1
# Convert to probabilities
for team in results:
for key in results[team]:
results[team][key] /= n_iterations
return results
```
### Step 4: Playoff Bracket Simulation
#### NHL: Best-of-7 Series
```python
def simulate_series_nhl(team_a, team_b, ratings, home_team_a=True):
wins_a, wins_b = 0, 0
game = 1
# Standard home/away format: 2-2-1-1-1
home_games_a = [True, True, False, False, True, False, True]
while wins_a < 4 and wins_b < 4:
home_adv = 35 if home_games_a[game-1] else -35
winner = simulate_game(team_a, team_b, ratings, home_adv)
if winner == team_a:
wins_a += 1
else:
wins_b += 1
game += 1
return team_a if wins_a == 4 else team_b
```
#### NFL: Single Elimination
```python
def simulate_nfl_bracket(seeds, ratings, home_adv=48):
# Wild card round: seeds 3-6 play; 1 and 2 get byes
# Home field to higher seed through conference championship
bracket = list(seeds) # ordered by seed
rounds = ['wildcard', 'divisional', 'conference', 'superbowl']
for rnd in rounds:
if rnd == 'superbowl':
home_adv = 0 # neutral site
winners = []
for i in range(0, len(bracket), 2):
winner = simulate_game(bracket[i], bracket[i+1], ratings, home_adv)
winners.append(winner)
bracket = winners
return bracket[0]
```
#### NBA: Best-of-7 with Play-In
For play-in tournament (seeds 7-10): simulate two one-game play-in games per conference to determine seeds 7 and 8 before the main bracket.
### Step 5: Determine Playoff Qualifiers
Sport-specific tiebreakers matter when simulated points are tied:
**NHL:** Sort by points, then ROW (regulation + OT wins, excludes shootout wins), then head-to-head record, then goal differential. Wild card format: top 3 from each division + 2 wild cards per conference.
**NFL:** Division winners (highest win%) + wild cards. Tiebreaker: head-to-head, then division record, then conference record. Two conferences, 3 divisions each, 7 playoff spots per conference.
**NBA:** Play-in tournament for seeds 7-10. Division winner does not guarantee top-4 seed (record determines seeding within conference).
```python
def determine_playoffs(simulated_standings, sport):
if sport == 'NHL':
return nhl_playoff_format(simulated_standings)
elif sport == 'NFL':
return nfl_playoff_format(simulated_standings)
elif sport == 'NBA':
return nba_playoff_format(simulated_standings)
```
### Step 6: Convergence Check
Run 1,000 iterations first. Check that top-team probabilities stabilize (< 1% change per additional 1,000 iterations). If not, run more.
```python
def check_convergence(results_1k, results_10k, threshold=0.01):
for team in results_1k:
diff = abs(results_1k[team]['made_playoffs'] - results_10k[team]['made_playoffs'])
if diff > threshold:
return False
return True
```
10,000 iterations: stable for most probabilities. 100,000 iterations: required for probabilities below 5% (low-probability events need more samples to be reliable).
## Sport-Specific Configs
| Config | NHL | NFL | NBA |
|--------|-----|-----|-----|
| Season length | 82 games | 17 games | 82 games |
| Playoff format | Best-of-7 | Single elimination | Best-of-7 |
| Play-in tournament | No | No | Yes (seeds 7-10) |
| Home advantage (Elo pts) | 35 | 48 | 100 |
| Home field games | 2-2-1-1-1 | Higher seed | 2-2-1-1-1 |
| Season carryover | 0.88 | 0.67 | 0.75 |
| Pythagorean exponent | 2.15 | 2.37 | 13.91 |
| Points system | 2/OTL gets 1 | Win% | Win% |
## Season/Date Logic
- NHL: October through April (regular season), April through June (playoffs)
- NFL: September through January (regular season), January through February (playoffs)
- NBA: October through April (regular season), April through June (playoffs)
- "Remaining games" = all games on the schedule with `game_date > today`
- If playoffs have already started, simulate only remaining rounds, not the regular season
## Anti-patterns
| Rationalization | Why It's Wrong | Do This Instead |
|----------------|---------------|-----------------|
| "1,000 iterations is enough" | A team at 5% championship odds has high variance at 1,000 iterations; the estimate can swing 2-3% just from sampling noise | Run 10,000 minimum; 100,000 for low-probability events |
| "Win probability is just current win%" | Win% doesn't account for strength of schedule or remaining schedule difficulty | Use Elo or Pythagorean ratings, not raw win% |
| "Home advantage is the same for all sports" | NHL home advantage is ~35 Elo points; NFL is ~48; NBA is ~100. The difference is substantial. | Use sport-specific values from the config table above |
| "Tiebreakers don't matter -- they're rare" | At the boundary of playoff spots, tiebreakers fire on 5-15% of simulated seasons | Implement ROW for NHL, head-to-head for NFL -- they affect the playoff probability bands meaningfully |
| "More iterations is always better" | Beyond 100,000, runtime cost exceeds precision gain | 100,000 is ceiling; 10,000 is floor; match to stakes and compute budget |
| "One rating system is sufficient" | A single Elo variant misses different signals (recent form vs cumulative quality) | Run simulations with 2-3 rating variants; report the range as uncertainty bounds |
## Output Format
Team-level probability table:
```
Team | Playoff% | Div Title% | Conf% | Cup%
-------------------|----------|------------|--------|------
Boston Bruins | 94% | 52% | 28% | 14%
Toronto Maple Leafs| 87% | 31% | 18% | 8%
Tampa Bay Lightning| 79% | 16% | 12% | 6%
Florida Panthers | 71% | 11% | 9% | 4%
...
Buffalo Sabres | 23% | 2% | 1% | 0.4%
```
For eliminated teams, include draft lottery position probability:
```
Team | Top-5 Pick% | Top-10 Pick%
-------------------|-----------:|------------:
San Jose Sharks | 48% | 91%
Anaheim Ducks | 41% | 87%
```
## What to Do Next
| What You Found | Next Action | Skill |
|----------------|-------------|-------|
| Simulation complete, want charts | Probability path charts over the season | `visualization` |
| Ratings feel off, want to improve | Build better Elo system | `elo-engineering` |
| Want to bet on futures using these odds | Compare simulation to market futures | `odds-explorer` |
| Want to backtest simulation accuracy | Test prior-season simulations vs outcomes | `backtesting` |
| Want to preview a specific playoff matchup | Single-series matchup analysis | `game-preview` |