Version: 2.0 Updated: 2026-04-03 Status: DEPLOYED — all core systems operational
The NBA desk detects mispricings between Kalshi alt line prices and Pinnacle sharp book fair value. Pinnacle is the anchor. When Kalshi's price diverges from Pinnacle's fair value by more than 4 cents after fees, that's a tradeable edge.
NBA is higher-scoring than NHL (~220 ppg), which means more alt lines per game and more opportunities.
7% on profit (not on wager). Net edge formula:
win_amount = 100 - kalshi_price
fee_on_win = 0.07 * win_amount
expected_profit = (model_prob * (win_amount - fee_on_win)) - ((1 - model_prob) * kalshi_price)
| Market | Alt Lines/Game | Sigma | Status |
|---|---|---|---|
| Totals | ~10-12 | 14 (game-level) | DEPLOYED |
| Spreads | ~6-8 | 14 (game-level) | DEPLOYED |
| Moneyline | 1 | N/A (direct de-vig) | DEPLOYED |
| Team Totals | ~8-10 per team | Team-specific (10-12.5) | DEPLOYED |
Each team uses its own scoring standard deviation from nba_team_variance instead of the hardcoded 14. Tight teams (CHI: 10.1) get narrower curves, volatile teams (CHA: 12.4) get wider. Stored in team_sigma column on every team total edge.
Matchup pace from nbastuffer_stats adjusts implied team totals before curve building. Stored in pace_factor column. Values >1.0 = fast matchup, <1.0 = slow matchup.
When direct Pinnacle team totals are unavailable:
Home TT = (game_total + spread) / 2
Away TT = (game_total - spread) / 2
| Source | What | Schedule |
|---|---|---|
| Pinnacle (Odds API) | ML, spread, total | Adaptive (2h/15m/5m) |
| Pinnacle Team Totals (Arcadia) | Per-team lines | Adaptive with Pinnacle |
| Kalshi API | All alt-line prices | Every 30min |
| ESPN Scoreboard | Live scores | Every 60s during games |
| Source | What | Schedule |
|---|---|---|
| DRatings | Win prob + spread predictions | 11 AM |
| Sagarin | Team power ratings | 11 AM |
| Dimers | Pythagorean win prob | 11 AM |
| GameSim | Monte Carlo predictions | 12 PM |
| ESPN BPI | Team power index | 11 AM |
| NBA Injury Report | Player status | 11 AM + 5 PM |
| Source | What | Schedule |
|---|---|---|
| SBR (6 books) | Spreads, totals, ML | 4x daily |
| Pregame.com | Sharp money, RLM | 3x daily |
Advanced stats, four factors, shooting, shot profile, opponent stats, home/away splits, last 5/10, clutch, hustle. All 30 teams, updated daily at 11 AM.
Monitors injury report every 30 minutes during game window (2PM-8PM ET). When any player averaging 25+ minutes changes status:
nba_injury_changes tablerunScanCycle('nba', 'injury-alert') re-scan| Market | Threshold | Action |
|---|---|---|
| Spread | >0.5 pts/hour | Immediate re-scan |
| Total | >0.5 pts/hour | Immediate re-scan |
Steam-triggered edges are flagged with steam_detected = 1 and magnitude/direction stored.
| Time | What |
|---|---|
| 8:00 AM | Early edge scan (8am scan type) |
| 9:00 AM | Pinnacle + Kalshi snapshot |
| 10:00 AM | Edge scan (10am scan type) |
| 11:00 AM | DRatings, Sagarin, BPI, team stats, injuries |
| 12:00 PM | GameSim predictions |
| 2:00 PM | Mid-day edge scan + Pinnacle update |
| 2:00-8:00 PM | Injury alert checks (every 30 min) |
| 5:00 PM | NBA injury report (evening games) |
| 6:00 PM | Evening edge scan |
| Pre-game | Closing snapshot (10 min before tipoff) |
| During game | Live scores (60s), live Kalshi tracking |
| Post-game | Settlement, CLV calculation, Brier scores |
| Condition | Reason |
|---|---|
| Key player injury breaking mid-scan | Model inputs stale |
| Deep tail lines (5+ rungs from anchor) | Model unreliable |
| Kalshi mid < 10c or > 90c | Extreme tails |
| Spread > 20c | Illiquid |
| Net edge < 4 cents | Below threshold |
| Star player GTD without update | Too much uncertainty |