Forensic Hockey
About

Our methods

Every modeled stat on this site leans on expected goals, so here is how accurate the model actually is. Every headline number below is scored only on 251,523 held-out shots the model never saw during training (20% of all shots since 2010-11) - true validation, not the model grading its own homework. Of the shots we call ~10%, do ~10% really go in? Yes.

Holdout scope · shots unseen in training

Predicted vs actual

1.003

24,540 xG vs 24,466 goals

Brier score

0.083

lower is better

ROC-AUC

0.723

0.5 = coin flip

Held-out shots

251,523

2010-11 to present

0%0%10%10%20%20%30%30%40%40%50%50%Predicted goal probabilityActual goal rate
xG bucketShotsPredictedActual
0-2%8,5671.4%3.6%
2-4%48,2623.1%2.6%
4-6%44,5434.9%3.9%
6-8%32,9947.0%6.9%
8-10%24,4948.9%9.6%
10-15%40,64512.3%13.5%
15-20%26,07817.3%18.5%
20-30%20,69123.6%23.1%
30-50%5,22534.8%27.9%
50-101%2451.4%37.5%

Year-by-year stability

Predicted goals (sum of xG) vs actual goals each season, still held-out shots only - the model should track reality every year, not just on average.

SeasonShotsPredicted goalsActual goalsRatio
2010-1116,2551,5771,5361.027
2011-1215,7451,5321,3701.118
2012-139,5619189370.979
2013-1415,9051,5321,5001.021
2014-1515,9711,5291,5261.002
2015-1615,9201,5231,4041.084
2016-1716,1581,5451,4571.060
2017-1817,7421,6981,6601.023
2018-1917,1981,6601,6650.997
2019-2015,2591,4781,4721.004
2020-2111,6661,1441,1500.995
2021-2217,9451,7801,8170.980
2022-2317,6371,8081,7591.028
2023-2416,9831,6671,7090.975
2024-2516,0511,5611,7410.897
2025-2615,5271,5901,7630.902

In deployment. Across every scored shot the site actually serves (1,256,487, including the 80% the model trained on): predicted ÷ actual 1.000, Brier 0.083, ROC-AUC 0.724 - essentially identical to the holdout. A five-feature logistic model is too simple to memorize shots, which is exactly why we can trust it out of sample.

Candidate · not cut over

xG v2 temporal holdout

A richer gradient-boosted candidate is now trained against a latest-season temporal holdout. The site still serves v1 xG until the rescore and regression gate is complete.

157,558 holdout shots

Brier

0.087

v1 0.095 · -0.00845

Log loss

0.298

v1 0.328 · -0.02970

ROC-AUC

0.755

v1 0.704 · +0.0510

Predicted vs actual

0.987

v1 0.892

Headline numbers are scored on a temporal holdout - seasons withheld from training. `baseline` carries the current five-feature logistic retrained on the same split, and `deployed` covers the v2 candidate refit on every eligible shot. Downstream xG surfaces still use v1 until the full rescore/cutover gate.

Parallel rescore gate

v2 has been scored against the full shot table for drift testing. Production surfaces still read v1.

1,243,738 regular reliable shots

Total xG drift

-524.15

-0.4% vs v1

v2 total xG

121482.4

v1 122006.5

Brier drift

-0.00657

lower is better

Log loss drift

-0.02596

lower is better

Empty-net correction

+5308.35

v1 treated empty nets like ordinary shots

20252026 goalie-only drift

+179.86

non-empty-net shots only

snap correction

+3465.88

largest shot-type movement

Honest notes. The model is unbiased in aggregate (predicted ≈ actual goals) and well-calibrated across most buckets. On held-out shots it over-predicts the rarest high-danger looks (30%+, where samples are thin) and under-predicts the very lowest bucket (long-range shots score more than it expects). xG covers unblocked shots with coordinates, 2010-11 onward; it is a model, not the official record.