🏒 NHLForecasts.com
Data-Driven NHL Predictions & Analytics
2025-26 Season Live

Understanding Our Expected Goals (xG) Model

Expected Goals (xG) has become the gold standard for measuring shot quality in hockey analytics. Rather than treating all shots equally, xG assigns a probability to each shot based on factors like distance, angle, shot type, and game situation.

At NHLForecasts.com, we've built a sophisticated xG model that powers multiple components of our analytics pipeline: in-game win probability curves, pregame team features, and website shot quality rankings.

What is Expected Goals?

Expected Goals answers a simple question: "What is the probability this shot becomes a goal?"

A shot from 5 feet away directly in front of the net might have an xG of 0.40 (40% chance of scoring), while a shot from the blue line at a sharp angle might have an xG of 0.03 (3% chance).

By accumulating xG across all shots in a game, we can assess which team created better scoring opportunities, independent of whether those shots actually went in.

Model Architecture

Framework: Gradient Boosting Classifier with Isotonic Calibration

We use scikit-learn's GradientBoostingClassifier with carefully tuned hyperparameters:

  • 200 estimators: More trees than baseline to capture feature richness
  • Learning rate: 0.05 (conservative to prevent overfitting)
  • Max depth: 4 (shallow trees for generalization)
  • Calibration: 3-fold isotonic calibration for probability alignment

Performance Metrics: - ROC AUC: ~0.762 (strong discrimination between goals and non-goals) - Log Loss: ~0.228 (well-calibrated probabilities) - Brier Score: Monitored during evaluation

We also maintain a Logistic Regression variant for comparison, but Gradient Boosting is our primary production model due to superior performance on non-linear feature interactions.

The Six Input Features

Our model uses a parsimonious set of six features, each chosen for specific hockey analytics reasons:

1. Distance from Net (Continuous)

Range: 0-200 feet

Euclidean distance from the shot location to the goal center. This is the single most predictive feature - shots from closer to the net have dramatically higher goal probabilities.

The NHL rink is 200 feet long × 85 feet wide, with goals located at x = ±89 feet. We calculate distance using standard geometry: sqrt((goal_x - shot_x)² + (goal_y - shot_y)²)

2. Angle to Net (Continuous)

Range: 0-180 degrees

Absolute angle from the shot location to the goal center, normalized to 0-180°. Shots directly in front of the net have low angles (~0°), while shots from behind the net have high angles (~180°).

We use atan2(dy, dx) for proper quadrant handling - this ensures a shot from behind the net isn't confused with a slot shot.

Why angle matters: Even at the same distance, a shot from directly in front (0°) has much better scoring probability than a sharp-angle shot from the boards (60°+).

3. Shot Type (Categorical)

Values: Wrist, Slap, Snap, Backhand, Tip-In, Deflected, Wrap-around, etc.

Different shot techniques have inherent accuracy and power characteristics: - Wrist shots: Most accurate, moderate power - Slap shots: Maximum power, less accurate - Tip-ins/Deflections: Close to net, unpredictable for goalies - Backhands: Less accurate, harder to get power

The model learns these relative conversion rates from the training data.

4. Is Rebound (Binary)

Definition: Shot within 3 seconds of previous shot by same team

Rebound shots have significantly higher xG because: - Goalie may be out of position after the initial save - Puck is already loose in the crease area - Less time for defenders to recover

This is one of the highest-value binary features in the model.

5. Is Rush (Binary)

Definition: Shot within 10 seconds of a turnover, hit, or blocked shot

Rush chances (also called transition opportunities) occur when a team gains possession and quickly moves up ice before the defense is set. These shots have higher quality because: - Defensive structure hasn't been established - Odd-man rushes create numerical advantages - Higher shot velocity from speed

6. Strength State (Categorical)

Values: Even, PowerPlay, PenaltyKill, EmptyNet, Other

The game situation has enormous impact on shot quality:

  • Even strength (5v5): Baseline
  • PowerPlay: Attacking team has numerical advantage, significantly higher xG
  • PenaltyKill: Attacking team is short-handed, lower xG
  • EmptyNet: No goalie present, xG approaches 1.0 for clear shots
  • Other: 4-on-4, 3-on-3, or unusual situations

We parse the NHL API's situation codes (format: AwayGoalie-AwaySkaters-HomeSkaters-HomeGoalie) and determine the shooting team's context to assign the correct state.

Why These Features?

Our feature selection philosophy is parsimonious by design:

What We Include

  • Geometric factors: Distance and angle capture the fundamental physics of shooting
  • Technique: Shot type affects accuracy and power
  • Context: Rebounds, rushes, and strength states identify high-danger situations

What We Deliberately Exclude

Goalie-specific features: We tested adding goalie save percentages and advanced stats, but they decreased accuracy by 1-2%. Why? - Goalie performance is already encoded in team-level goal rates - Adding explicit goalie features risks data leakage (goalies are selected based on matchups) - Goalie performance is highly variable and introduces noise

Player-specific features: Similarly, shooter identity adds minimal value since: - Elite shooters get more ice time (already reflected in shot volume) - Talent largely manifests in shot selection (distance/angle) not conversion rate - Adds unnecessary model complexity

Excessive rolling windows: We avoid complex momentum indicators and time-weighted averages that often decrease generalization.

The result: A model that captures shot quality fundamentals without overfitting to noise.

Training Data & Methodology

Data Source: NHL API play-by-play events - Event types: shot-on-goal, goal, missed-shot - Database table: shot_events - Coverage: 141,407 shots across 1,439+ games (2023-2025 seasons) - Goal rate: 7.22% (realistic NHL conversion rate) - Metadata quality: 90.5% coverage for shooting_team and player_id

Training Process:

  1. Data Loading: Fetch all available shots from database via ShotDataClient
  2. Train/Test Split: 80/20 split, stratified by goal outcome (maintains 7.22% goal rate in both sets)
  3. Preprocessing Pipeline:
  4. Numeric features (distance, angle): StandardScaler normalization
  5. Categorical features (shot_type, strength_state): OneHotEncoder with unknown handling
  6. Binary features (is_rebound, is_rush): No preprocessing needed
  7. Model Training: Fit GradientBoostingClassifier on training set
  8. Calibration: Apply isotonic calibration using 3-fold cross-validation
  9. Evaluation: Test on held-out 20% with ROC AUC, log loss, and calibration curves

Model Artifact: Saved to nhl-revamp/models/xg_model.joblib for production use

Why Gradient Boosting over Logistic Regression?

We tested both approaches extensively:

Aspect Logistic Regression Gradient Boosting
Assumptions Linear relationships Non-linear interactions
ROC AUC ~0.70-0.71 ~0.762
Interpretability High (coefficients) Medium (feature importance)
Overfitting risk Low Controlled via depth/learning rate
Training time Fast Moderate

Decision: Gradient Boosting's superior AUC (~0.762 vs ~0.71) justifies the slight complexity increase. The model captures interactions like: - Distance × angle (close sharp-angle shots) - Rebound × distance (close rebounds are extremely dangerous) - PowerPlay × shot_type (slap shots on PP have different dynamics)

Why Isotonic Calibration?

Calibration ensures that when our model predicts a shot has a 25% chance of scoring, approximately 25% of such shots actually become goals.

Isotonic calibration is: - Non-parametric: No assumptions about probability distribution - Monotonic: Preserves model's rank ordering - Effective: Corrects systematic over/under-prediction in specific ranges

We validate calibration by: 1. Binning predictions into deciles (0-10%, 10-20%, ..., 90-100%) 2. Computing actual goal rate in each bin 3. Verifying predicted probabilities match observed frequencies

This is critical for applications like betting, coaching decisions, and win probability calculations where confidence levels matter.

Integration Points Across Our Analytics

1. In-Game Win Probability

The most sophisticated integration. As games progress, we: - Calculate xG for each shot event - Track cumulative home_xg and away_xg - Use xg_diff as a feature in the in-game probability model

This allows our win probability curves to reflect shot quality, not just the scoreboard.

2. Pregame Predictions

Our main win prediction model uses team-level xG features: - xg_per_shot: Team's average xG value per shot (rolling 20 games) - high_danger_shots: Count of shots with xG > 0.2

Teams that consistently generate high-quality chances (high xg_per_shot) earn better pregame win probabilities.

3. Website Analytics

We export xG data to power multiple website sections: - Game summaries: xG For/Against for each completed game - Team rankings: Season xG leaders and efficiency metrics - Player leaderboards: Top 50 players by cumulative xG (minimum 50 shots)

The Data Pipeline

Bringing xG from raw shot data to website visualizations involves multiple steps:

NHL API Play-by-Play
        ↓
backfill_shots.py → shot_events table
        ↓
backfill_xg_metrics.py → Predict xG for all shots
        ↓
aggregate_player_xg.py → Player-level season stats
        ↓
aggregate_team_xg.py → Team-level season stats
        ↓
export_xg_csvs.py → Three CSV files:
    ├─ game_xg_summary.csv (per-game home vs away xG)
    ├─ team_xg_summary.csv (season team rankings)
    └─ player_xg_leaderboard.csv (top players, min 50 shots)

Scripts location: nhl-revamp/scripts/

Typical update workflow:

# Train/update xG model
python nhl-revamp/train_xg.py

# Backfill metrics for new games
python nhl-revamp/scripts/backfill_xg_metrics.py --incremental

# Re-aggregate stats
python nhl-revamp/scripts/aggregate_player_xg.py
python nhl-revamp/scripts/aggregate_team_xg.py

# Export to CSVs
python nhl-revamp/scripts/export_xg_csvs.py

Or run the consolidated pipeline:

python nhl-revamp/scripts/run_xg_pipeline.py --season 20252026

Performance Benchmarks

Our xG model achieves strong performance across multiple metrics, trained with temporal validation to prevent data leakage:

  • ROC AUC: 0.755 - Excellent discrimination between goals and non-goals
  • Log Loss: 0.218 - Well-calibrated probability predictions
  • Brier Score: 0.058 - Low prediction error
  • Training data: 141K+ shots - Robust sample size (47K training, 12K test with temporal split)
  • Production finishing rate: 0.983 - Near-perfect calibration on live predictions
  • High-danger threshold: xG > 0.2 - Industry-standard 20% cutoff for quality chances

Temporal Training (December 2025): The model uses chronological train/test splits to prevent future data leakage, ensuring honest performance metrics that reflect true predictive capability. This temporal integrity fix (Issue #20) provides reliable, reproducible results.

For context: - Random guessing: AUC = 0.50 - Perfect model: AUC = 1.00 - Industry-leading xG models: AUC ~0.75-0.80

Our model sits comfortably in the competitive range while maintaining interpretability, temporal integrity, and avoiding overfitting.

Real-World Example

Let's examine a sample game to see xG in action:

Game 2024020011: Home team wins 6-4

Period Score Home xG Away xG Interpretation
1 3-2 0.85 0.46 Home generating quality chances
2 4-2 1.70 1.02 Home maintains xG advantage
3 6-4 2.85 2.42 Close game in shot quality despite score

Final: 6-4 score with 2.85 vs 2.54 xG

Key insight: The home team won decisively on the scoreboard (6-4), but xG reveals this was closer than it looks. The home team's xG advantage was only 0.31 - they were slightly better in shot quality but also got a bit lucky with their conversion rate.

This is the power of xG: revealing the underlying shot quality that raw scoring can mask.

Model Limitations & Future Work

While our xG model performs well, we acknowledge limitations:

Current Limitations

  1. No goalie context: We don't adjust for starting goalie quality (intentional design choice)
  2. Shot location only: Don't track pre-shot movement, passing sequences
  3. Binary outcome: Doesn't predict save type (glove, blocker, pad, etc.)
  4. Limited metadata: 9.5% of shots missing shooting_team/player_id

Future Enhancements We're Exploring

  • Shot location heatmaps: Visual representation of team xG patterns
  • Sequence detection: Multi-shot sequences and cycling plays
  • Zone entry tracking: Controlled entries vs dump-ins
  • Post-shot xG: Expected goals on rebounds (xG for subsequent shot given initial save)

However, we remain committed to the principle: simple, well-engineered features often outperform complex enhancements. Any new feature must demonstrate clear, robust improvement on held-out data.

Transparency & Methodology

At NHLForecasts.com, we believe in transparency:

  • Open methodology: This article documents our exact approach
  • Published metrics: We share ROC AUC, log loss, and calibration results
  • Validation: All models tested on held-out data, never trained on current season
  • Data quality: We disclose metadata coverage and data limitations

Expected Goals is a powerful tool, but it's not magic. It's a statistical model with assumptions and limitations. Our goal is to provide accurate xG values while being honest about what the model can and cannot tell us.

Conclusion

Our Expected Goals model represents a careful balance between sophistication and simplicity:

  • Six well-chosen features capture shot quality fundamentals
  • Gradient Boosting handles non-linear interactions
  • Isotonic calibration ensures probability accuracy
  • Multiple integration points enhance predictions across our platform

The model achieves competitive performance (ROC AUC ~0.762) while maintaining interpretability and avoiding the overfitting pitfalls of over-engineered solutions.

Whether you're exploring our in-game win probability dashboards, checking team shot quality rankings, or examining pregame prediction features, xG is working behind the scenes to provide deeper insight into NHL game dynamics.

For questions or feedback about our xG methodology, feel free to reach out through our contact page. We're always looking to improve our models while maintaining the transparency and rigor our users expect.

← Back to Articles