How Our Expected Goals (xG) Model Works

2026-07-04 · methodology · expected goals · xG · model

A transparent look inside Hockey Alchemy's xG model: four situation-specific XGBoost models, 53 features, 16 seasons and 1.6M shots, with cross-validated log loss, AUC, and calibration.

An expected-goals (xG) model is only as good as the data underneath it — and in the NHL, that data keeps moving. The league's shot tracking has shifted over the years: how missed shots are logged, where shots get recorded from, which shot types exist. The game itself keeps changing too. And that's a problem, because an xG model frozen on old seasons quietly drifts out of calibration — it starts expecting goals modern hockey doesn't produce, and crediting goalies and shooters for the wrong things. Public models have run into exactly this: as the tracking drifted, their goal expectations drifted with it. Staying accurate isn't a one-time training job; it's a rebuild-and-retrain discipline.

That's the lens for this post. xG is the backbone of everything we publish — goalie work above expected (GSAx), on-ice shot quality, and the expected-goals component of GAR/WAR — so if the model drifts, all of it drifts with it. Here's how we keep ours honest: what it measures, the four separate models under the hood, the features it reads, how we validate it, and where it still falls short.

What xG measures

xG assigns every unblocked shot attempt a probability — from 0 to 1 — that it becomes a goal. A shot with xG = 0.15 is the kind of chance that historically scores about 15% of the time. We model P(goal | unblocked shot attempt), the Fenwick-based definition used across public hockey analytics. The probability comes entirely from the shot's context:

Why four models, not one

Scoring behaves very differently by game state. A power-play shot from the slot is a different animal than a 5-on-5 shot from the same spot — different passing lanes, different goalie movement, different rebound dynamics. Rather than force one model to average across all of it, we train four situation-specific models so each can learn its own patterns:

ModelSituationShots trained on
Even strengthEqual skaters — the bulk of the game1,334,000
Power playShooting team has the advantage143,081
ShorthandedShooting team is down a skater129,276
Empty netOpposing goalie pulleddedicated formula

A unified model trained on all 1.6 million shots serves as a fallback when a situation-specific model isn't appropriate.

The features

The model reads 53 features per shot — a deliberately rich feature set. They fall into six groups:

GroupExamples
Core shot geometryDistance, angle, coordinates, shot type, period, time
Situation5v5 / PP / PK / 4v4 / 3v3, strength differential, empty net
Rebound & rushRebound (≤3s after a save), rush (shot within 4s of zone entry), shot speed
Prior eventType of the previous event, which team, time / distance / speed to the shot
Power-play timingSeconds elapsed since the power play or penalty kill began
Score stateGoal differential, trailing / leading (teams behind take riskier chances)

The prior-event, power-play-timing, and score-state groups are the biggest additions over our earlier model — they capture how the play developed, not just where the puck ended up.

How it's trained and validated

The models are XGBoost gradient-boosting classifiers trained on 16 NHL seasons (2010-11 through 2025-26) — 1,606,355 unblocked shots and 108,080 goals. That's more history than most public models train on, which matters for stable calibration across eras.

We evaluate with 5-fold, season-stratified cross-validation: every shot is scored by a model that never saw it during training, and folds are drawn so a season isn't split across the train/test boundary in a way that leaks information. Three metrics do the judging:

Results

Cross-validated performance by model:

ModelLog lossAUCCalibration
Even strength0.1660.8570.999
Power play0.2410.998
Shorthanded0.2400.998
Unified (all shots)0.1781.000

Calibration is the number we're proudest of: within a fraction of a percent of perfect at every strength, which means our xG totals can be trusted as goal-equivalents rather than a relative index that needs rescaling.

An honest caveat: the shorthanded model is our weakest. Small samples and the chaos of 4-on-5 sequences make it the hardest state to model, and it's the area we'd most like to improve.

Better than our previous model

The current model cut log loss by roughly 28% versus our previous version, and added about +0.12 of AUC — a large jump, and a consistent one across all 16 seasons. Almost all of it came from the new context features (prior event, power-play timing, score state), not from more data alone: the model now understands the sequence of play leading into a shot, which is where a lot of shot quality actually lives.

SeasonOld log lossNew log lossOld AUCNew AUC
2013-140.2030.1460.7340.856
2018-190.2170.1590.7170.837
2021-220.2320.1600.7540.857
2024-250.2230.1520.7700.860
16-season avg0.2130.1550.7340.849

Lower log loss and higher AUC in every single season — the gain isn't a one-year artifact.

How it stacks up against other public models

The other question is how it lines up against the field. Start with the methodology, where the differences are structural:

Hockey AlchemyTypical public model
Models4 situation-specific (EV / PP / SH) + empty-net formulausually one blended model
Training data16 seasons · 1.6M shots~7–8 seasons
Features53 (incl. prior-event, PP timing, score state)~15–20

That shows up in the numbers. On a common set of shots, our even-strength model posts a 0.166 log loss and 0.857 AUC. The widely-used public gradient-boosted models we benchmarked against — scored on the same shots — came in around 0.185 and 0.215 log loss and roughly 0.78–0.79 AUC. More seasons of training and the extra context features are most of the difference.

Two honest caveats. First, those competitor figures are our computations on a shared shot set — an apples-to-apples test on our data, not numbers published by those sites, which is why we've left them unnamed. Second, privately-tracked models with camera and pre-shot-movement data remain a tier public models can't fully reach; that gap is real, even as public data improves.

Limitations

What it powers

Everything downstream. Expected goals feed GSAx (goalies saving above expected), on-ice xGF% and shot-quality metrics, and the expected-goals component of our GAR and WAR models. It's the single model that, when it improves, lifts the most of the site at once — which is why we keep it transparent and keep retraining it.

For the shorthand definitions, see the expected goals glossary entry; for how xG fits into the broader system, see our methodology.

Figures are 5-fold, season-stratified cross-validated results on 16 NHL seasons (2010-11 through 2025-26), 1,606,355 unblocked shot attempts. Point-in-time as of publication; the model is retrained as new data lands.

More from The Lab

How We Calculate NHL Power Rankings: The Elo System Behind Hockey Alchemy

A transparent look at the Elo rating system that powers our NHL power rankings, game predictions, and Stanley Cup odds. K=12, home ice = 50 points, 30% regression to the mean — and three different ways we account for margin of victory.

Introducing the Hockey Alchemy Chrome Extension

GAR, tier, and contract value for any NHL player — in a popup, a side panel, or highlighted right on the pages you already read. Free, no account, no tracking.

What to Expect With Jim Hiller

Toronto tried the Cup-pedigree hire and Auston Matthews cratered. What Jim Hiller's actual Kings numbers — and our metrics — say to expect from the Leafs' next coach.