Loading…
Loading…
MarketModel
Every shot in every game gets an expected-goals (xG) value: the chance that a shot from that spot, of that type, in that situation becomes a goal. Summed over a game, xG measures how many goals a team “should” have scored — a steadier signal of team quality than goals, which bounce around with luck and goaltending.
For tonight's games, the model combines each team's xG share (5-on-5 and all situations, regressed toward league average), Elo rating, points percentage, the starting goalies' goals saved above expected, rest and back-to-backs, and home ice. It produces a win probability that includes overtime and the shootout. Projected goals are derived from the published forecast, not a separate goal model.
Current modelPony xG logistic + Elo — Regularised logistic regression on pregame features (xG-share form, Elo, goaltending, points%, rest), blended with the de-vigged betting market.
Training seasons: 2022-23, 2023-24, 2024-25, 2025-26, 2026-27. Every change to the model is tested walk-forward (train on the past, predict the next season) and only goes live if it beats the current model on log loss.
The market probability comes from the moneyline odds with the bookmaker's margin (the “vig”, about 4%) removed, so the two sides add up to 100%. The published forecast (the bar's fill) is the model blended with that de-vigged market: the model carries 80% of the weight and the market 20%, every game. The model diamond shows the unblended model, so you can see where it disagrees. Fair odds is the moneyline that matches the published forecast exactly.
The betting market is a very good forecaster. We treat it as the benchmark to beat, not as noise — and we say so when we haven't beaten it.
Edge (EV) is the expected profit per unit staked: EV = forecast probability × decimal odds − 1, using the published, market-blended forecast. Edges are only highlighted, and units only suggested, when the model has shown over a meaningful sample that its disagreements with the market carry real information. Until that bet gate opens, new edges and stakes are hidden. The ledger on the Accuracy page grades every pick as a flat bet to win 1u at the price shown when the pick was published: a −120 favourite risks 1.2u, a +150 underdog 0.67u.
1 unit (1u) = 1% of bankroll. Stakes are quarter-Kelly, capped at 5u.
Gate closed
Bet sizes are hidden: the model has not yet proven an edge over the betting market.
Nothing here is betting advice. Probabilities are estimates and can be wrong. 21+, 1-800-GAMBLER.
On opening night nobody has played, so the model starts from a preseason prior: last season's ratings, regressed toward league average to account for roster changes. As games are played this season's data takes over; by roughly 20 games per team the prior matters little. The 2026-27 season is 84 games long.
League ranks (#1–#32) stay hidden until every team has played enough games to rank.
Every prediction is frozen before puck drop and graded after the final horn. We report three numbers, always next to simple baselines:
Overtime and shootout games count as wins and losses like any other.
2025-26 season, live predictions only (regular).
| Forecaster | n | Acc | Brier ↓ | Log loss ↓ |
|---|---|---|---|---|
| Previous site model (live) | 339 | 54.9% | 0.2451 | 0.6831 |
| Always home-rate | 339 | 51.0% | 0.2509 | model better0.6950 |
| Betting market (de-vigged) | 328 | 56.0% | 0.2418 | beats model0.6763 |
2026-27 so far.
| Forecaster | n | Acc | Brier ↓ | Log loss ↓ |
|---|---|---|---|---|
| Pony xG model | 311 | 63.3% | 0.2224 | 0.6346 |
| Always home-rate | 311 | 55.0% | 0.2477 | model better0.6885 |
| Betting market (de-vigged) | 311 | 62.5% | 0.2240 | model better0.6387 |
Walk-forward backtest. For each season below, the model was trained only on the seasons before it, then scored on every game of that season (regular season and playoffs), next to the model it replaced. In the first weeks of those seasons its log loss was 0.6761. Full seasons have no stored market prices; the MKT row compares the model with the de-vigged market on the games that do.
| Test season | n | Acc | Log loss ↓ | Prev. model | Home rate | Market |
|---|---|---|---|---|---|---|
| 2023-24 | 1,399 | 59.7% | 0.6581 | 0.6757 | 0.6910 | — |
| 2024-25 | 1,206 | 59.2% | 0.6606 | 0.6703 | 0.6867 | — |
| 2025-26 | 1,394 | 54.9% | 0.6792 | 0.6849 | 0.6935 | — |
| 2025-26 · MKT | 410 | — | 0.6689 | 0.6823 | — | 0.6744 |
Headline numbers use live pregame snapshots only. Retro (back-filled) rows are counted in n_retro_excluded and never mixed in. Market = de-vigged (power method).
Report 2026-11-15 00:04 UTC · Pony xG logistic + Elo
Goalie strength is measured by goals saved above expected (GSAx): expected goals against minus goals actually allowed. Starting goalies come from team and beat-reporter reports (DailyFaceoff, ESPN). The name's colour carries the status — blue confirmed, faded blue likely, grey projected — with no extra words on the card.
The open panel under a game adds context the model uses or that fans ask about: recent form (L7, or L1–L6 early in the season), head-to-head this season, power play and penalty kill, and rest (back-to-backs and compressed stretches). The “Why” bars show how much each factor pushes the forecast toward one team, diverging from the centre in team colours. They follow the same sample rules as above — this season first, prior-season values tagged.
Every finished or live game has a page with a story chart: goals pinned across the top, per-minute bars (home above the line, away below) and the running race underneath. Moving the playhead shows the score and the home team's win probability at that moment, from the score and the clock: it starts at our frozen pregame call and moves only when a goal goes in. Next to it sits the deserved win probability: every unblocked shot so far counted as a chance to score at its pony xG, with a level game split evenly. Where the two part, finishing and goaltending made the difference.
Under the chart sit our call, the de-vigged closing market, the xG totals and the goal that swung the game most. The coloured blocks in the strength row belong to the team with the extra skater (5v4, 5v3, 6v5 with the goalie pulled); amber ticks mark penalty calls. Dots at the edges of the bar lane are chances worth at least 0.20 xG that did not go in. The Share view replaces the bars with a rolling five-minute xG share.
The matchups grid sets every away skater against every home skater at 5v5: square size is time on the ice together, the split is the xG share over that time and white pips are goals. Rows and columns follow the lines, L1-L4 then D1-D3: the most used forward trios and pairs that share no player. The bars at the edges are each skater's own 5v5 time and xG share, and the Lines view rolls the same numbers up to whole lines. A hard match is two lines out together at least 1.75× as often as chance gives (each line's share of 5v5 time multiplied), over at least 3 minutes; for each line we mark only the opponent it saw most beyond chance. The home coach has the last change, so hard matches mostly read as the home bench's choices. The replay slider cuts the whole grid at any moment of the game.
The faceoffs board draws every faceoff on the dot it was taken at: one bead per draw in the winner's colour, clockwise from the top of the ring in game order. A spark marks a win that the winners turned into a shot attempt (on goal, missed or blocked) within 10 seconds and before the next faceoff. The board puts each team's own end on its side (the away team defends the left net) and tallies the draws by zone, labelled OZ, DZ and NZ for each team; zones beside each taker are from his own team's view. Picking a taker lights only his draws and shows his record against each opponent; hovering an opponent then lights the draws between the two. The strength filter splits out even strength and each team's power play (the draw's own situation, so a draw right after a penalty expires counts as even). On a team page's Breakdown the board covers the picked games. A bead is always one draw: while every dot's draws fit one ring (18 at the end and centre dots, 11 at the neutral ones) the board keeps them; past that each dot becomes a solid ring filled clockwise in the team's colour up to its win share there (a pointer marks 50%), thicker where more draws were taken.
A team page's Schedule tab reads the NHL's full schedule with venues. The strip puts every game on one day axis (home above the line, road below), so rest shows as spacing; a game ahead is a magenta bar whose length is our win %, past the dotted 50% guide when we favour the team; a played game keeps the length of our frozen pregame call in its result colour. Rest is whole days off between game dates, for both teams; a back-to-back is no day off. The amber axis thickens inside denser windows: 3 games in 4 days, 4 in 6, 5 in 8. A rest edge is a game where the opponent is on the second night of a back-to-back and the team is not (a deficit is the reverse).
Travel is great-circle miles between arenas (a static table of the 32 rinks plus outdoor and Global Series venues). A team flies venue to venue on a road trip and home between home games; with 3 or more days off between two road games it flies home and starts a new trip. A venue more than 100 miles from the home arena counts as the road, so a Winter Classic across town is a home game and a “home” game in Stockholm is a trip. Real charter routes differ a little; the ranking between teams is what to read.
Body clock is the puck drop on the team's home clock: 7 PM in Vancouver is 10 PM for a team from the East. We tag a late body clock from 9:30 PM, an early one before 12:30 PM, and a day game when local puck drop is before 4 PM.
Difficulty (0-100) is how hard a game is for an average team: the opponent's strength (this season's team ratings; a finished season uses its xG share and points %), home ice, and either side being on a back-to-back, through a logistic curve where 50 is an even game. The grey ribbon is its 5-game rolling mean; the toughest stretch is the hardest 8 games in a row. A trap is a game against a bottom-third team on a back-to-back, at the end of a road trip of four or more, or right before a top-five opponent. Win % on games ahead is our model (the day's published prediction, else the season simulation).
The projected record for a month, a road trip or the season adds the results so far to the model's outcome probabilities for the games ahead. Both forecasts split each game into a regulation win, overtime or a shootout, and a regulation loss, so the expected standings points are the sum of 2 × P(win) + 1 × P(OT or shootout loss), out of 2 per game. The record rounds the expected wins, losses and OT losses to whole games that still add up (largest remainder). Without a split we assume the league's 22.5% of games that reach OT (2022-23 to 2025-26). The range in brackets is the 10th to 90th percentile of points: the season simulation for a whole season, a normal approximation over the games for a month or a trip.
The Pony Score is a skater's game in goals, on the same footing as IMPACT (one is a single night, the other a rating). Every weight comes from our own data (three regular seasons, refit with pipeline/tools/pony_score_calibrate.py); the only fixed choice is that an on-ice chance is shared evenly by the five skaters on the ice.
Goalies score goals saved above expected. One game is a small sample: the score describes the night, it does not rate the player.
A single game is shown as its Pony Rating, 0 to 10, read against games at his position (forwards, defencemen and goalies apart, last season's games): the median game is 5.0, the 90th percentile 7.5, the 99th 9.0 and the top 0.1% 10.0, linear between. Bands: Incredible 9.0+ (about 1% of games), Great 7.5, Good 6.0, Average 4.0, Below avg 2.5, Horrible under 2.5 (about 5%). Season totals and the leaderboard stay in goals (a game log's Per GP row averages its games' ratings); the goals behind any rating show on hover.
Milestones (Players, Milestones) lists every rostered player who reaches one this season at his pace. Career totals, all-time and franchise lists are the NHL's own regular-season numbers (NHL stats API and records.nhl.com), refreshed after each game day; hits and blocks are counted from 2005-06 and faceoffs from 1997-98, as the NHL tracks them.
The projected game (G23: his club's 23rd game) is where his pace gets him: his per-game rate over this season and last together (his career rate with fewer than 10 games), applied to his club's schedule, starting after the expected return date of an injured player. Goalies move at their share of starts. A pace on fewer than 10 games carries an n= tag. One game means he can get there in a single game. Iron-man streaks are not tracked yet.
IMPACT is the goals a skater adds over 82 games compared with an average forward or defenceman: even strength, power play, penalty kill, finishing and penalties, each scaled by his expected ice time. IMPACT = OFF + DEF, all higher = better: OFF (goals/82) is even-strength and power-play chances created, finishing and penalties drawn; DEF (goals/82) is even-strength and penalty-kill chances prevented, minus penalties taken; PEN is the penalty part of both.
PROD: production score, a recency-weighted Game Score (goals, assists, shots, on-ice 5v5 shots and goals, penalties, faceoffs, blocks) per 82 games above his position's average. Descriptive, like The Athletic's; IMPACT is the predictive rating.
The per-60 rates behind it, against his position's average: EV OFF and EV DEF (xG for created, xG against prevented at even strength), PP and PK (the same on special teams, grey under 30 seconds a game) and FIN (goals above xG on his own shots, shrunk). A ridge regression (RAPM) separates each skater from his linemates, opponents, score, zone starts and home ice; a box-score prior steadies small samples and rookies start from a prior for their position. Recent games weigh most (90-game half-life) and nothing older than about three seasons counts; the date beside the ratings is the last game day in, and EV MIN shows the sample. The same ratings feed the team tables (as even-strength NET = EV OFF + EV DEF), the Lines tab and the game model's lineup term.
In a player's career tables, Age is his age on February 1 of that season (the Hockey-Reference convention).
Playoff odds come from simulating the rest of the season thousands of times with the same game model. On Standings:
A skater's 5-on-5 minutes (five skaters and a goalie a side, regular season) split three ways for each of his most-used teammates, after HockeyViz: together (filled dot), him apart, on the ice without that teammate (ring), and the mate apart, the teammate without him (diamond). Each point is a raw rate, pony xG for per 60 across and xG against per 60 down with fewer against at the top, never a share, so a pairing that slows both ends of the game looks different from one that speeds both up. Faint diagonals mark equal xG differential; the corners read GOOD (more for, less against), BAD, FUN (lots of both) and DULL (little of either).
The line through each teammate's three points passes through the +, the player's own 5-on-5 rates, because his minutes with and without that teammate average to it. A teammate is listed with 100 minutes together over a full season, scaled down with the share of the season played and never below 10 minutes; an apart sample under 10 minutes (or a quarter of that bar) gets no point. A traded player has one chart per team. Rates describe who he played with; they do not separate his effect from his linemates' (IMPACT does).
Where on the ice a skater changes the shots, after HockeyViz's isolated impact. Every 5-on-5 stretch with no line change gives two rows, one per attacking team: the unblocked shots of that stretch, binned on the offensive half of the rink, per hour. One ridge regression fits all the cells at once, with an offence column for each attacking skater, a defence column for each defender, and the score, home ice, a third-period shell and the faceoff zone that started the shift as controls. A player's map is the difference his presence makes, cell by cell, against an average skater in his place with the same teammates and opponents, smoothed with a 10-foot kernel. Orange means more shots than average come from there, blue fewer: on offence orange is good, on defence blue is. The net is at the top and the shooter's left is on the left. The defence map is the opponents' shots in his own zone while he is on the ice. Each map type (5-on-5 offence, defence, power play, penalty kill) is drawn on its own scale: the bands are quantiles of that type's cells across the league's regulars, the faintest from the median cell up, the same for every player, so two defence maps compare directly. Defence effects are smaller and less repeatable than offence ones, so read a defence map's faint bands as tendency, not certainty.
The number on each map is the same regression on pony xG instead of shot counts: xG for (or against) per 60 he adds and the share of the league rate. The power play (5-on-4) and the penalty kill have their own regression. A season's maps use that season and the two before it, each game weighted down by half per year of age, so a map is stable in October and still moves with every game. Under 500 5-on-5 minutes the sample is thin and marked.
The total is goals over a standard season: 1,000 minutes at 5-on-5, 125 on the power play and 125 on the penalty kill with average teammates and opponents, so players with very different roles compare on one scale. Its parts: the four xG impacts; finishing, goals above xG on his own shots shrunk toward average (60 xG of prior), times his shot volume; drawing and taking, minor penalties per 60 against his position's average (shrunk with 600 minutes), at the league's net power-play xG per minor. Passing (“setting”) is left out: without pass data, the best proxy, how often teammates' shots beat their own finishing with him on the ice, showed no out-of-sample signal.
Beside the parts, goal threat and penalties show where he sits among the regulars at his position (forwards or defence, 500+ minutes): the league as a mirrored density with a line at its median, his dot, and his percentile, with fewer penalties taken ranking higher. Goal threat is finishing (goals per xG) and shooting, his own 5-on-5 xG per 60, shrunk with 300 minutes; shooting is shown, not added to the total, because his own shots already count in his 5-on-5 offence.
From one season to the next the 5-on-5 offence impact repeats at r = 0.50-0.55, defence 0.36-0.40, the power play 0.31-0.53, the penalty kill 0.13-0.18, finishing 0.12-0.25, shooting 0.65-0.75, penalties drawn 0.58-0.64 and taken 0.69-0.71; the offence maps' shape repeats (0.25 per player, about 0 for two random players), defence less (0.11). The 5-on-5 impacts agree with IMPACT's even-strength ratings at r = 0.90 (offence) and 0.93 (defence).
The Props page takes one stat at a time (shots on goal, goals, points, assists, power-play points). Each row is a skater on tonight's slate, on the line and power-play unit DailyFaceoff lists for him.
Validation. Replayed over the 2025-26 season from mid-November (35,261 skater games, each priced from earlier games only), the fair chances beat both the league base rate and the skater's own last-10 hit rate on every prop by log loss: shots on goal o1.5 0.612 against 0.684 for the base rate and 0.786 for last-10, anytime goal 0.401 against 0.431 and 0.500, 1+ point 0.600 against 0.649 and 0.734. Predicted and observed rates agree within about a point in every 10% bin. A raw last-10 hit rate predicts worse than the league average: the tape shows form, the fair price is the number to bet against.
The data pipeline runs hourly from late morning to late evening Eastern on game days. The dot in the top bar shows when it last finished and turns red only if a scheduled run was missed. Times show in your time zone.