NHL Desk Strategy Spec
Version: 5.1 | Date: 2026-04-03 | Status: DEPLOYED
Core Strategy
Trade mispriced Kalshi NHL contracts using Pinnacle as the sharp anchor. De-vig Pinnacle odds to fair probability, compare against Kalshi price, execute when net edge exceeds 4 cents after 7% Kalshi fee.
De-Vig Methods
2-Way Markets (Spreads, Totals) — Shin Method:
fair_prob = (sqrt(z² + 4(1-z)p²) - z) / (2(1-z))
Solved numerically via brentq. When Power and Shin diverge > 2 cents, average them. Otherwise Shin only.
Moneylines — Shin Method (primary):
Same formula. Handles lopsided NHL lines better than multiplicative de-vig.
Kalshi Fee & Edge Calculation
win_amount = 100 - kalshi_price
fee_on_win = 0.07 * win_amount
net_edge = (model_prob * (win_amount - fee_on_win)) - ((1 - model_prob) * kalshi_price)
Minimum threshold: 4 cents net edge post-fees.
NHL Distribution Models
Poisson (Goal Totals):
- Lambda calibrated from Pinnacle O/U line
- Search range: [3.0, 9.0]
- OT adjustment: +0.15 to lambda
- Max goals cap: 15
Normal (Spreads):
- Mean calibrated from Pinnacle puckline
- Std dev: 2.0 (fixed)
- Alt lines: -0.5, -1.5, -2.5, -3.5
Market Priority
| Market |
Alt Lines/Game |
Priority |
NHL Edges |
| Totals |
~8 |
Highest |
3,216 (913 exec, avg 5.2c) |
| Spreads |
~4 |
Medium |
560 (289 exec) |
| Moneyline |
1 |
Lowest |
243 (31 exec) |
Edge Scanner Schedule
| Time (ET) |
Action |
| 8:00 AM |
Full scan — Pinnacle + Kalshi snapshot, edge detection |
| 10:00 AM |
Full scan — updated lines, steam detection |
| 2:00 PM |
Full scan — afternoon lines |
| 6:00 PM |
Pre-game scan — final pre-close |
| T-10 min |
Closing scan — CLV baseline capture |
Data Collection Schedule
| Time (ET) |
Data |
| Every 30 min |
Kalshi prices (15 series), implied probability curves |
| Adaptive (2h→15m→5m) |
Pinnacle NHL odds |
| 10AM / 2PM / 6PM |
SBR multi-book odds, Pregame.com sharp money |
| 11:00 AM |
MoneyPuck (team stats, goalies, predictions), DRatings, Sagarin, ScoutingTheRefs |
| 11:00 AM |
NHL computed stats: PP/PK, EWMA, PDO, schedule strength, team variance, fatigue, period scoring, flow baselines, empty net, venue adjustment, custom metrics |
| 11:00 AM |
NHL Tier2: goalie matchup history, H2H series, player game logs |
| 11AM/1PM/3PM/5PM |
DailyFaceoff goalie confirmations |
| 12:00 PM |
GameSim Monte Carlo predictions |
| Every 60s (during games) |
ESPN live game scores |
| Weekly |
Venue scoring adjustments |
Kalshi Series Tracked (15)
| Series |
Market |
Snapshots |
| KXNHLTOTAL |
Total goals alt lines |
443,475 |
| KXNHLSPREAD |
Puck line alt lines |
382,900 |
| KXNHLGAME |
Moneyline |
352,157 |
| KXNHLPTS |
Player points |
417,157 |
| KXNHLAST |
Player assists |
423,065 |
| KXNHLGOAL |
Player goals |
146,722 |
| KXNHLFIRSTGOAL |
First goal scorer |
87,206 |
| KXNHLSAVES |
Goalie saves |
(collecting) |
| KXNHLPLAYOFF |
Make playoffs |
20,992 |
| KXNHLHART |
Hart Trophy (MVP) |
4,206 |
| KXNHLVEZINA |
Vezina Trophy |
21,921 |
| KXNHLROSS |
Art Ross Trophy |
12,112 |
| KXNHLNORRIS |
Norris Trophy |
22,122 |
| KXNHLCALDER |
Calder Trophy |
17,703 |
| KXNHLRICHARD |
Maurice Richard |
13,272 |
| KXNHLADAMS |
Jack Adams Award |
12,293 |
NHL-Specific Edge Factors
Goalie Factor (Primary Edge Driver)
- Starter vs backup shifts outcome by 15-20 cents on Kalshi
- Source: DailyFaceoff (4x/day scrape) + NHL API
- AHL callup = +4-5 point O/U swing
- Confirmation tiers: Confirmed (full), Expected (50%), Unconfirmed (no bet)
Back-to-Back Games
- ~3-5 cents underperformance
- If BOTH teams on B2B, effects cancel — skip
Referee Assignments
- Source: ScoutingTheRefs (game day scrape)
- Penalty-heavy refs affect totals and pace
Custom Metrics (10)
- Goalie-Adjusted xGA —
Team_xGA × (Starter_sv% / League_avg_sv%)
- Special Teams Impact Score —
(PP% - Lg_PP%) × PP_opps/game + (PK% - Lg_PK%) × PK_times/game
- Net Goalie Matchup Delta —
Starter_A_GSAx/60 - Starter_B_GSAx/60
- Fatigue xG Penalty — B2B = -0.25 xG; 3-in-4 = -0.35 xG; with travel modifier
- PDO Regression Flag — 5v5 PDO outside 0.985-1.015 flagged
- EWMA Form — 10-game window, alphas 0.10/0.12/0.15 tracked simultaneously
- Comeback Probability Index — 3rd period scoring + trailing performance + EN pull tendencies
- Score-State Adjusted Metrics — Separate xGF/xGA when leading, tied, trailing
- Starter-Backup Delta — Save% gap between starter and backup
- Playoff Context Flags — Clinched/eliminated/magic number/games remaining
Position Sizing (Quarter Kelly)
full_kelly = (model_prob * net_odds - (1 - model_prob)) / net_odds
bet_size = min(bankroll * full_kelly * 0.25, bankroll * 0.05)
| Parameter |
Value |
| Max single bet |
5% bankroll |
| Max per game |
10% bankroll |
| Max daily NHL exposure |
25% bankroll |
| Min bet size |
0.5% bankroll |
Steam Detection
| Market |
Threshold |
Action |
| Spread |
> 0.25 pts/hr |
Immediate re-scan, 1.25x confidence |
| Moneyline |
> 15 cents move |
Immediate re-scan |
| Total |
> 0.5 pts/hr |
Immediate re-scan |
Rung Distance (Tail Confidence)
| Distance |
Confidence |
Action |
| 0-2 |
High |
Full confidence |
| 3-4 |
Medium |
0.75x sizing multiplier |
| 5+ |
Low |
Do not bet |
Hard No-Bet Rules
- Unconfirmed goalie
- 5+ rung distance
- Kalshi volume < $500
- Net edge < 4 cents
- Implied prob < 15% or > 85%
- Both teams on B2B
- Injury news breaking mid-scan
Key Constants
| Parameter |
Value |
| Poisson max goals |
15 |
| Spread std dev |
2.0 |
| OT lambda adjustment |
+0.15 |
| Min net edge |
4 cents |
| Kalshi fee rate |
7% on profit |
| Kelly fraction |
0.25 |
| De-vig agreement threshold |
2 cents |
| Goalie scrape times |
11AM, 1PM, 3PM, 5PM ET |
| B2B underperformance |
~3-5 cents |
Settlement & Learning
- Runs at 4AM/10AM/4PM ET
- Tracks per edge: game_id, market_type, kalshi_price, model_prob, raw_edge, net_edge, rung_distance, goalie_status, steam_flag
- Post-settlement: Brier score, CLV tracking vs Pinnacle closing
- Monthly review: P&L per market type, edge accuracy, rung distance performance
Deferred to Later
| Item |
Reason |
Status |
| Kalshi WebSocket |
REST polling adequate for scan-window edge detection; WebSocket needed for live execution |
Design phase |
| Negative Binomial model |
Poisson adequate for NHL totals; will compare calibration once more settled data accumulates |
Awaiting data |
| Data validation framework |
Automated ingestion checks, null-field thresholds, duplicate detection |
Will build after all pipelines fixed |
| In-play research |
Pre-game set-and-forget for now; mid-game research deferred |
Future |
| Morning edge hypothesis |
Should be tracked by time window on edge scanner. Data exists (3,244 settled edges across scan windows) |
Analysis needed |
| Dimers NHL predictions |
Scraper now fetches NHL (added Apr 3). Only 1 game today — will accumulate over time |
Collecting |
| Arena scorekeeper bias |
Public xG inherits rink-specific bias; no normalization planned per boss ruling |
Acknowledged, not acting |
Recently Completed (Apr 3, 2026)
| Item |
Result |
| NaturalStatTrick |
Removed — not a viable source. MoneyPuck covers all equivalent stats (Corsi%, Fenwick%, HDCF%, 5v5/PP/PK splits) |
| Massey ratings |
Built scraper (Puppeteer stealth, Cloudflare protected). 32 NHL teams. Added to Ratings & Predictions group |
| Shot-level xG data |
Built scraper for NHL API play-by-play. 3,450 shots from 40 games. x/y coords, shot type, shooter, goalie, zone, strength. Foundation for proprietary xG model |
| Goalie matchup history |
Fixed N>=5 filter (too strict mid-season) to N>=2. Now 655 rows across 72 goalies |
| Dimers NHL |
Added NHL URL to Dimers scraper. Now collecting NHL predictions |
Source: ~/edgeclaw/results/spec-panel/sports-desk/nhl-strategy.md