7-layer operator playbook for NBA prop analysis. Every claim query-grounded. No assumptions. Structured for both human operators and machine agents.
Only use queried data. No assumptions. Run every query before making claims.
league_id = nba · sport = basketball
match_id is the primary cross-table join.
Use side = 'under' and result IN ('won','lost') unless checking over leans.
DraftKings as sportsbook source from player_prop_bets.
1) Sample quality → 2) Layer alignment → 3) Miss magnitude vs line.
| Context | Min Sample |
|---|---|
| Team-level market profile | 50 props |
| Defender tier profile | 10 props |
| Defender-driven opponent environment | 3 props |
| Blowout split | 3 games |
| Market | Baseline Under % |
|---|---|
| Points | 52.8% |
| Assists | 53.5% |
| Rebounds | 52.8% |
| Threes Made | 55.5% |
| PRA | 53.4% |
SQL Query
SELECT m.id AS match_id, m.away_team, m.home_team, m.start_time AT TIME ZONE 'America/Los_Angeles' AS start_time_pt, m.odds_away_spread_safe AS away_spread, m.odds_home_spread_safe AS home_spread, m.odds_total_safe AS total, m.odds_away_ml_safe AS away_ml, m.odds_home_ml_safe AS home_ml FROM matches m WHERE m.sport = 'basketball' AND m.league_id = 'nba' AND m.away_team = '[AWAY]' AND m.home_team = '[HOME]' AND (m.start_time AT TIME ZONE 'America/Los_Angeles')::date = DATE '[YYYY-MM-DD]';
Output: GAME: [AWAY] @ [HOME] | Spread: [X] | Total: [X] | Win Prob: [X]%
SQL Query
SELECT pb.player_name, pb.bet_type, pb.line_value,
MAX(CASE WHEN pb.side='over' THEN pb.odds_american END) AS over_odds,
MAX(CASE WHEN pb.side='under' THEN pb.odds_american END) AS under_odds
FROM player_prop_bets pb
WHERE pb.sportsbook = 'DraftKings' AND pb.match_id = '[MATCH_ID]'
AND pb.bet_type IN ('points','assists','rebounds','threes_made','pra')
GROUP BY pb.player_name, pb.bet_type, pb.line_value
ORDER BY pb.team, pb.player_name, pb.bet_type;
If no current line returned for a player/market → do not make a play on it.
Key: Uses opponent as the team being profiled (defense layer). Min 50 props per market.
Output Format
[TEAM] suppresses [market] at [under_pct]% (+[delta] vs league) [TEAM] boosts [market] at [under_pct]% ([delta] vs league)
Qualifiers from player_game_stats + espn_athletes join. Season = 2025-10-01+.
Classification
erase tier = under_pct >= 60% neutral = 45% to 59% suppression-proof = under_pct < 40%
Tier Buckets
low = line < 15 mid = line >= 15 AND < 22 high = line >= 22
Join: player_prop_outcomes × player_game_stats on match_id where defender is starter with 15+ min.
If no rows clear sample threshold → No validated defender-driven opponent environment effect found (sample < 3).
Source: game_officials → official_tendencies. Use official_order = 1 (crew chief).
Source: game_context × matches. Uses closing_total for accuracy.
Gate: Only activate when projected spread is 8+ points. Blowout = margin ≥ 15.
Valid Flags (direct evidence only)
back-to-back → team appears in prior local date query injury return → injury rows reference the player being analyzed usage disruption→ injury rows show non-active teammate whose absence changes role
If evidence is indirect or not returned by query: No validated behavioral-state flag found.
Joins player_prop_bets (board) × player_prop_outcomes (exact-line history + opponent history).
Over lean requirements: under_pct < 50% AND positive avg_actual − line AND at least one supporting team/structural/blowout layer.
cold at half → suppression holding → stay hot at half → suppression failing → hedge / exit 1-7 pts above pace → system break 8+ above pace → individual heater; lower confidence
under_pct < 50% on historical under-side query
Positive avg_actual − line
At least one supporting team / structural / blowout layer
All three gates must clear for an over lean. If no validated play clears the evidence bar — say so explicitly.
Maximum 3 conviction plays
Maximum 2 skip players with reason
If nothing clears the evidence bar, state explicitly