Jump: Eligibility · NHLe · FlameWire Calc · Team Blueprint · DirtyXg · Data · Limits
NHL draft eligibility (age window)
The lab shows an Eligibility column based on the standard NHL Entry Draft age rules (practical approximation for scouting):
- Minimum: player is 18 on or before September 15 of the draft year.
- North American: not older than 20 on December 31 of the draft year (age on Dec 31 ≤ 20).
- International: same floor; max age 21 on Dec 31.
- Players past the max age who were never drafted follow the undrafted free agent (UFA) path — they stay “undrafted” in the draft sense.
- Players already matched to an NHL draft class are labeled Drafted.
Region (NA vs international) is inferred from leagues / hometown when possible; this universe defaults to North American. After July 1 we evaluate the next calendar draft year (e.g. Aug 2026 → 2027 draft).
NHLe (NHL equivalency)
NHLe translates points-per-game from other leagues into an NHL-scale rate using public league factors (classic NHLe tradition). Headline number is NHLe/82 — expected NHL points over 82 games if the translated rate held.
- Multi-year blend weights:
0.50 · 0.30 · 0.20(most recent first, up to 3 seasons). - Minimum ~15 GP per translated season (with small allowances for NHL cups).
- Youth / showcase leagues and Memorial Cup rows are skipped.
- Role ladder (ultimate / most likely / floor) is offense-led with age and NHL sample clamps.
League factors (v1)
| League | Factor | Notes |
|---|---|---|
| NHL | 1.00 | Baseline |
| AHL | 0.50 | Pro minors |
| KHL / SHL / Liiga / NCAA / … | 0.41–0.80 | Euro / college pro paths |
| OHL / WHL / QMJHL | 0.29–0.34 | Major junior |
| USHL | 0.28 | U.S. junior |
| ECHL | 0.32 | Pro minors |
| BCHL / AJHL / Jr A | 0.16–0.20 | Directional only — lower confidence |
Junior A factors are intentionally conservative. Scoring-leader lists over-sample offense and can inflate rates versus full rosters.
FlameWire Calc
Proprietary upside score on a 0–100 “OVR-style” scale:
FW = 0.40·P + 0.30·D + 0.30·A
(+ agreement · defense boost · young-elite · soft tiers; v0.9)
- P (production) — NHLe blend + trend + confidence + league-relative context. Confidence dings use schedule-relative sample quality (NCAA ~36 GP season treated as full; not raw GP vs CHL ~68). Relative = PPG percentile vs peers, with fallback chain: same age → age band (≤17 / 18 / 19 / 20+) → league+pos → league. Age from season row or birthdate. Season series (peak / GP-weighted mean / trend, age-adj + plain) is on the player payload. Score impact stays neutral on draft-day destiny CV (age-adj level did not beat NHLe+pick for hard tier accuracy on eventual NHLers).
- D (draft prior) — smooth exponential pick chart
100·e−0.011·(pick−1)with years-since-draft decay (Schuckers / pick-value chart family). Undrafted players use an eligibility ladder: ~42 (1st-yr) · ~32 (2nd) · ~24 (3rd/final) · ~16 (aged out / UFA) · ~22 (unknown); pre-eligible ~38. - A (age / path) — prospect window + path tier + NHL GP sample (draft-day destiny cares a lot about development clock).
- P↔D agreement — small soft term when production and draft capital align (or diverge). Encourages coherent profiles over mixed signals.
- Defense destiny boost — +6 for defensemen. Box-score NHLe understates D role outcomes at the same points rate; draft-day CV preferred this over F-only cutpoints.
- Young-elite ceiling — small age-gated boost when age ≤19–20 with top capital and real production, so Star path is reachable.
- Destiny objective: ordinal role path at draft day (see Regressions and Stress Testing), not exact NHL points. Cuts jointly maximize within-1 accuracy then exact tier hit.
- Soft tier probabilities (v0.9) — softmax over distance to band midpoints (temperature 15, Brier-tuned on draft-day set). Headline path stays hard cuts; soft %s express nearby-tier uncertainty (e.g. 48% middle-6 / 31% top-6). Hard one-hot Brier ~1.26 → soft ~0.75 on the destiny sample — better calibration, same ranking score.
- Hurdle two-stage (v0.9.1 side channel) — Liu/Schuckers-style
P(regular NHL path) × role intensity, fit on draft-day history
(
Data/cache/hurdle_destiny_model.json). Shown ashurdle.p_make+ hurdle soft mix. Does not replace the PDA hard path: on 5-fold / year-holdout destiny CV, hurdle hard tiers lost ~4–5pp within-1 vs FW. Paper metric (Spearman with first-7-year NHL GP) is essentially tied with FW (~0.47 vs draft order ~0.45).
Tiers (score floors): Star ≥96 · Top-6 / Top-4 D ≥74 · Middle-6 / 2nd pair ≥62 · Bottom-6 / 3rd pair ≥41 · Bubble ≥26 · else AHL path. Compared side-by-side with NHLe role ceiling.
Accuracy lab results
Draft-day destiny back-test: drafted skaters, age ≤21 at draft, ≥40 NHL GP later, seasons as-of draft only (n≈365). Metrics: exact tier hit and within-1 tier. Live baseline under v0.6–v0.8 PDA blend: ~37% exact · ~87% within-1 (bootstrap 95% CI roughly exact 32–42%, within-1 83–90%).
Literature-inspired formulas tested with 5-fold CV (pure data-only features — NHLe, pick, age/path, CS rank, relative peers, size, F/D):
- HockeyStats / Schuckers family — NHLe + log(CSS) logistic / linear blends; pick-heavy vs NHLe-heavy priors.
- Liu model-tree lite — separate PDA weights by pick band (1–15 / 16–32 / 33–64 / 65–100 / 100+).
- PCS-lite — peer mean destiny in pick × NHLe × position cells.
- Ridge → p82 → role map (and F/D-split ridge), ordinal multi-threshold, one-vs-rest tier classifier, two-stage zero-inflation.
- kNN, ensembles, active relative scales, age-residual relative, size residuals, CS blends into D, F/D-separate weight grids, cut re-optimization.
Result: nothing beat the current PDA blend by more than noise. Near-misses (agree scale 1.5, light CS blend 10–35%) moved within-1 or exact by ~0.3–0.9pp — well inside bootstrap CI, so hard score weights stay put. Aggressive relative / PCS / ridge / tree variants usually hurt within-1 (collinearity of CS≈pick≈0.91; relative≈NHLe on eventual NHLers).
Practical ceiling for 6 hard destiny tiers on draft day with public box data is about 35–40% exact / 85–88% within-1. Targets of 50% exact / 90% within-1 need either fewer tiers, post-draft development seasons, scouting text features, or a different label (e.g. binary NHLer / star as in public logistic models). Soft probabilities are the honest way to express the residual uncertainty without inventing hard precision.
From-scratch formulas without NHLe
We also rebuilt destiny models that never use league translation factors (no NHLe), following public draft research:
- Schuckers / Liu — raw GP/G/A/PPG, age, size, CSS, log(pick), path flags; ridge → NHL P/82 → role map; ordinal multi-threshold; one-vs-rest tier classifier.
- PCS-style cohorts — mean observed destiny in bins of age × raw PPG × height × pick × position.
- Hand blend — in-league relative %ile + age-weighted raw PPG + draft capital + age window (grid-searched weights).
- kNN / ensembles on the same non-NHLe feature set.
On the same draft-day set (n≈365), none beat FlameWire on within-1 (best non-NHLe ~85% vs FW ~87%). One ridge→P/82 path hit slightly higher exact (~38% vs ~37%) while losing ~4pp within-1 — not a joint win. Pure skill (no pick/CS) collapses (~28–37% exact / ~77–80% within-1). So NHLe is not the bottleneck: capital + multi-league context already do most of the ordinal work; dropping NHLe does not unlock a new accuracy regime on this label.
NHL draft status
Draft status is matched against public NHL draft classes from
api-web.nhle.com/v1/draft/picks/{year}/all
(default years 2005–current). Matching is by normalized full name, with
disambiguation on position, age-at-draft, and amateur league when multiple
picks share a name.
- Matched players get year / overall / round / team and a real FlameWire Calc D prior.
- Unmatched players stay undrafted (UDFA prior).
- Cache:
Data/cache/nhl_draft_index.json. Refresh via UI link orPOST /api/sync-draft?force=true. - Common-name collisions are possible — confidence is marked medium when candidates are close.
NHL Central Scouting rankings
Public midterm + final prospect rankings from
api-web.nhle.com/v1/draft/rankings/{year}/{category}
(NA skaters = 1, international skaters = 2; years ~2008–current).
Cached under Data/cache/nhl_cs_rankings.json.
- Matched onto the universe by name (+ DOB / position / draft year) similar to draft picks.
- On the draft-day destiny set (~93% CS coverage), CS final rank Spearman with destiny (~0.54) is close to pick (~0.54) and below full FW (~0.58). CS is nearly collinear with pick. Light CS→D blends (10–35%) were noise- level on CV; drafted D stays pure pick chart.
- Undrafted but CS-ranked players use CS final rank as D when it exceeds the eligibility ladder (data-only capital for ranked UDFAs).
- Height/weight from CS backfill missing bio when available.
Ages & birthdays
Missing ages are filled without HTML scraping, in this order:
- Spreadsheet — Age column when present.
- CHL LeagueStat JSON (OHL / WHL / QMJHL) — full Tier-1 skater boards
from 2010–11 onward (cached under
Data/chl/seasons/). Merged into player bios (DOB, height, weight, shoots, hometown) and multi-year season rows (GP/G/A/P/+/−/PIM) for NHLe. Refresh:POST /api/sync-chl-dumporpython -m app.chl_dump --from-year 2010. - NHL API — name search + player landing for official
birthDate. - NCAA D1 (College Hockey News) — primary college source:
full team rosters (DOB, height, weight, class, hometown, draft note) plus
full-season overall scoring (~1.3k–1.6k skaters/year), seasons
2010–11 → current including 2022–23.
Cached under
Data/chn/. Refresh:POST /api/sync-chnorpython -m app.chn_dump --from-year 2010. - NCAA D1 leaders (ncaa.com) — optional gap-fill via
ncaa-api.henrygd.me
(leaderboards only; no bios).
Data/ncaa/;POST /api/sync-ncaa. - NCAA class estimate — Fr/So/Jr/Sr/Gr + season year → approximate age (synthetic July 1 birthday). Marked with ≈. Directional only; CHN DOB preferred.
Cache: Data/cache/birthdays.json. Refresh via
POST /api/sync-chl-birthdays or POST /api/sync-birthdays.
Team Blueprint
Team Blueprint diagnoses NHL clubs against historical success ladders (regular-season tiers + playoff depth) using official NHL standings/summary rates, then layers NHL EDGE process metrics from season 2021–22 onward:
- DirtyXg / DirtyXgA per GP — chance quality for and against (v4 logistic; 5v5 + all-situations).
- DirtyXg% (5v5) — even-strength chance share (preferred process read; PP/PK less muddying).
- DirtyXg% (all) — all-situations share for full-game context.
- G − DirtyXg — finishing residual (5v5 and all-sits).
- HD SOG / GP and HD SOG share — high-danger volume and mix.
- OZ% (ES) — even-strength offensive-zone time share.
- CF diff (EDGE) — signed shot-attempt differential from EDGE (not a 0–1 Corsi%; higher = more attempts for).
EDGE dumps cache under Data/blueprint/edge/. Pre-2021–22 seasons
keep counting stats only (no EDGE fields).
DirtyXg (experimental) vs public xG
Expected goals (xG) in full public models (e.g. MoneyPuck, Natural Stat Trick, Evolving-Hockey) score every unblocked shot attempt with many features — distance, angle, shot type, rebound/rush, score effects, etc. — trained on large multi-season samples.
DirtyXg is Hockey Scout’s independent chance-quality estimate (FlameWire Lab lineage). Current Blueprint default is v4: a pure-Python L2 logistic fit on NHL play-by-play, trained on goals (not MoneyPuck coefficients). It is not a MoneyPuck/NST feed — think of it as a lab process metric for Blueprint ladders and finishing residuals. MoneyPuck remains a side-check only.
Formula (v4 preferred)
v4 (current): every unblocked PBP attempt (goal + SOG + miss) gets
P(goal) from a logistic model with continuous distance/angle (plus squares
and interaction), rebound (≤3s since prior shot event), rush (≤4s after NZ/DZ prior event),
5v5 flag, log time-since-last-event, and shot-type one-hots (wrist baseline).
Empty-net and miss flags are not features (EN exploded the intercept; miss is
label leakage — misses never score). Coefficients live in
Data/blueprint/pbp/dirty_xg_v4_model.json. Train calibration is fit so
Σ predicted ≈ Σ goals on the training sample (no constant post-hoc scale).
DirtyXg_for = Σ logistic(xi) · 1.0
· same sum for against · method tag pbp_v4
Lanes: every unblocked attempt is scored once, then rolled up as
all-situations and 5v5 only (situationCode = 1551).
Blueprint ladders and diagnosis prefer DirtyXg% (5v5) when present —
cleaner even-strength process — and still show all-sits for full-game context.
v3 (prior PBP): unblocked attempts mapped to EDGE bands, scored with EDGE league conversion rates + shot-type multipliers + miss discount, then ×0.684 so league Σ DirtyXg ≈ Σ goals.
DirtyXg_for = Σ xG_band(unblocked) · 0.684
v2.1 (EDGE-only fallback): season SOG counts by EDGE band
high / mid / long (+ residual), then ×1.048:
DirtyXg_raw = N_high·r_high + N_mid·r_mid + N_long·r_long + N_other·r_other
DirtyXg = DirtyXg_raw × 1.048
Typical EDGE rates: high ≈ 0.19 · mid ≈ 0.11 · long ≈ 0.03 · other ≈ 0.06.
Blueprint prefers PBP v4 when the model + season rollups are present;
EDGE rollup remains as dirty_xg_edge for comparison. Pre-EDGE / no-PBP
seasons keep counting stats only.
Spot-check vs MoneyPuck (v4)
We compared DirtyXg v4 to MoneyPuck team xGoalsFor /
xGoalsAgainst (situation = all) using the official
MoneyPuck data downloads
(2023–24 and 2024–25 regular season CSVs) — not HTML scraping.
Credit: MoneyPuck.com. Non-commercial research use. Goal is accuracy across the
board, not cloning MP; MP is a reference for rank/process fidelity.
| Check | DirtyXg v4 |
|---|---|
| Sample | 64 team-seasons (32 × 2 RS) |
| Pearson r / Spearman ρ vs MP xGF | 0.937 / 0.926 |
| Pearson r / Spearman ρ vs MP xGA | 0.915 / 0.908 |
| Pearson r / Spearman ρ vs MP xGF% | 0.974 / 0.960 |
| MAE vs MP xGF (goals) | ~9.9 |
| Level (Σ DirtyXg / Σ MP xGF) | 0.966 |
| Goals / DirtyXg (pair sample) | 1.013 |
| Finishing residual r (G−DirtyXg vs G−MP xGF) | 0.91 |
| Top-10 team overlap by season (xGF) | 8/10 (2023–24) · 9/10 (2024–25) |
Ladder sanity (DirtyXg%, EDGE-era RS): Presidents Trophy ~53% · playoff teams ~52% · missed playoffs ~49% · Bottom 5 ~45%. Example: CGY 2024–25 ≈ 225 DirtyXg vs 220 goals (slight underperformance residual).
Read: v4 tracks public chance creation closely enough for Blueprint process ladders, share metrics (DirtyXg%), and finishing residuals. Remaining gaps vs full commercial models are mostly score/venue adjustments, flurry effects, and richer sequence context — not basic location.
Lineage — what each version adds
| Idea | Source | Effect | Ship? |
|---|---|---|---|
| League band rates (goals/SOG) | EDGE high/mid/long | Core of v2 | Yes (v2) |
| Constant public-level scale (~1.05) | Calibration | EDGE-only level fix | Yes (v2.1) |
| Unblocked attempts (SOG + misses) | NHL PBP | Public xG unit of analysis | Yes (v3) |
| Continuous distance / angle | PBP coordinates | Main rank lift vs bands | Yes (v4) |
| Rebound / rush / strength / shot type | PBP sequences | Closes most residual vs MP ranks | Yes (v4) |
| Fit to goals (logistic L2) | PBP goals | Train cal ≈ 1.0; no MP labels | Yes (v4) |
| OZ% / CF diff as xG correction | EDGE | Blurs “chance quality” | Keep as separate ladder metrics |
| Score/venue/flurry adjustments | — | Commercial-model residual | Not in DirtyXg (by design) |
Inspired by the public expected-goals tradition (MoneyPuck and peers). DirtyXg is an independent lab estimate — not affiliated with or endorsed by those sites. Always credit MoneyPuck.com when citing their downloadable data.
Data source
Universe is built from the full CHL LeagueStat dump (2010+),
College Hockey News NCAA D1 (2010+), and
Data/Hockey_Scoring_Leaders_Master.xlsx (other leagues / sheet fills).
Players are merged by name (and CHN/CHL ids when present) for multi-year NHLe.
Team Blueprint uses official NHL APIs (standings, team summary, playoff carousel)
plus NHL EDGE team endpoints. Hit POST /api/reload (or restart)
after refreshing dumps; Blueprint/EDGE via
POST /api/sync-blueprint or Refresh on /blueprint.
Limits
- CHL / sheet scoring boards can still omit non-scorers; CHN rosters are full-team.
- CHN is a personal research scrape of public pages — be polite; do not republish wholesale.
- Goalie NHLe is not applied (same as FlameWire).
- A minority of roster rows lack DOB (esp. commits / early listings).
- DirtyXg is an experimental process metric — not a substitute for full public xG models (no score/venue/flurry adjustments).
- DirtyXg v4 needs cached PBP (EDGE era ~2021–22+); older seasons fall back or omit process xG.
- NHL EDGE history for Blueprint starts ~2021–22; endpoints are undocumented and can change.