Project the next game · Study any line · Pull live odds — straight from your terminal, notebook, or browser.
Quick Start · First 5 Minutes · Live Lines · Dashboard · CLI · Use from Claude (MCP) · Modeling · Roadmap
Hooplytics turns NBA game logs into a modern analytics workflow. Model projections, recent-form trends, historical threshold studies, and live market context — designed to help you explore, explain, and challenge the numbers, not hand you a magic prediction.
|
Click-driven analytics for the modern fan. Eight purpose-built pages, an AI scout (OpenAI or Claude), and printable PDF reports. hooplytics-web |
Rich-rendered tables, scriptable end to end, hooplytics --help |
Reproducible, narrative-first exploration with charts inline. jupyter lab hooplytics.ipynb |
Lines from The Odds API are treated as analytical thresholds — comparison points for projections, distributions, and historical outcomes. Hooplytics is analytics-first.
Already know what you want? Here is the fast path.
git clone https://github.com/texasbe2trill/hooplytics.git && cd hooplytics
python3 -m venv .venv && source .venv/bin/activate && pip install -e .| I want to… | Command |
|---|---|
| 🎛️ Open the dashboard | hooplytics-web |
| 🎯 Project a player's next game | hooplytics project "Jalen Brunson" |
| 🔄 Detect a role / usage shift | hooplytics role-shift "Jalen Brunson" |
| 📈 Compare a projection vs. a live line | hooplytics prop "Shai Gilgeous-Alexander" points |
| 📊 See the live line board | hooplytics lines --refresh |
| 📑 Generate a printable scouting report | Open the dashboard → Roster Report → Generate PDF |
| 🏋️ Generate a coaching performance report | Open the dashboard → Roster Report → Player Performance Analytics |
| 🤖 Ask the AI scout a question | Open the dashboard → Hooplytics Scout |
| 📰 Read tonight's AI slate brief | Open the dashboard → Home (needs an AI key) |
| 🔍 Explain why a specific edge exists | Open the dashboard → Analytics Dashboard → Edge Explainer |
| ❓ See all CLI commands | hooplytics --help |
| 📓 Open the notebook | jupyter lab hooplytics.ipynb |
💡 Tip: set
ODDS_API_KEYin.envandpropwill auto-fetch the line for you — no--lineneeded.
New here? This is the smoothest path to "oh, that's actually useful" — and you don't even have to install anything.
|
Open the live app — no install required Go to hooplytics.streamlit.app. The hosted Streamlit app loads instantly with a pre-shipped roster of stars and a high-accuracy RACE model bundle already in memory — zero training, zero waiting. Multiple bundles ship in bundles/ and you can switch between them in the sidebar (see Choosing a model bundle).
Prefer to run it locally? pip install -e . && hooplytics-web opens the same app at http://localhost:8501.
|
|
|
Land on Home — see today's slate at a glance You’ll see roster coverage, model-quality medians, and a roll-up of strong edges. This is your control tower. |
|
|
(Optional) Paste your Odds API key in the sidebar Get a free key at the-odds-api.com, paste it into the sidebar password field, and the entire app lights up with live market lines and a real edge board. The key stays in session memory — never written to disk or sent anywhere except The Odds API. |
|
|
Open Analytics Dashboard → read the slate This is the fastest way to find tonight’s biggest projection-vs-line gaps. Sort by signed edge, filter by call (MORE / LESS), and use the Strong badge to surface the highest-conviction rows. |
|
|
Drill into a player on Player Projection Pick a player from the sidebar to see their next-game projection across all 8 models, recent form trend, distribution context, and the model’s read versus the live line. |
|
|
Generate a printable scouting PDF Go to Roster Report → click Generate PDF. You get a branded, multi-page report with KPI tiles, a signal spotlight, R² lollipops, diverging edge charts, and per-player hero blocks — ready to share. |
|
|
(Optional) Talk to Hooplytics Scout Pick your AI provider in the sidebar (OpenAI or Anthropic Claude), paste a key, click Connect, and ask things like “Give me a MORE/LESS read on the largest edge tonight, with confidence and risk factors.” The Scout is grounded in your local data and structured for confidence + risk, not hot takes. |
🎯 In a hurry? The first three steps are the bare minimum. Steps 4–7 are where the real fun lives.
Most NBA tools either dump descriptive stats or hand you a single prediction with no context. Hooplytics sits in the middle.
|
Eight model families, side-by-side, with held-out diagnostics in plain view. The Odds API tells you what the market thinks; Hooplytics shows you how that compares to recent form, distributions, and history. Streamlit for visuals, CLI for speed, notebook for reproducibility. |
Feature importance, residuals, calibration plots, and per-stat health summaries are first-class citizens. The RoleShiftDetector watches for sudden changes in assists, scoring, usage, and minutes across every player projection. When a signal fires, it surfaces a WARN or SUPPRESS flag — and the model respects it automatically. Rolling features are computed from prior games only — no leakage, no surprises. Lower-signal categories (steals + blocks, turnovers) are framed as such — always. |
Drop an ODDS_API_KEY in .env and the entire CLI lights up with live market data.
# Live line board for the tracked roster, sorted by projection gap
hooplytics lines --refresh
# Single-stat: projection vs. auto-fetched live line
hooplytics prop "Shai Gilgeous-Alexander" points
# Full 8-stat decision view with live lines folded in
hooplytics decisions "Victor Wembanyama"What you see in your terminal:
| Column | Meaning |
|---|---|
model prediction |
The model's expected value for the next game |
posted line |
The market line pulled live from The Odds API |
5-game avg |
The player's recent-form baseline |
adj. threshold |
Vig-adjusted line used for the gap calculation |
edge |
Signed gap between the projection and the threshold |
call |
Directional signal (MORE / LESS) for analytical comparison |
🔓 No key? No problem. Every command still works — you just lose the live-line column.
If Hooplytics is useful to you, a ⭐ goes a long way — it helps others find the project.
The Streamlit app ships with eight purpose-built pages, each focused on a different analytics workflow.
📓 Notebook gallery — earlier-era visualizations from the Jupyter workflow
A player intelligence workbench is built to make data easier to explore, explain, and challenge — not to hand you a magic number.
- 🔮 What does this player's recent form actually look like?
- 📐 How does the model projection compare with tonight's posted line?
- 📈 Is the player trending above or below their season baseline?
- 🎚️ Which signals are stable, and which are noisy?
- 🗓️ How often has the player finished above similar thresholds historically?
- 🧭 Where do diagnostics suggest confidence — and where do they suggest caution?
|
|
| Area | Highlights |
|---|---|
| 🎛️ Streamlit dashboard | Eight purpose-built pages: Home, Player Projection, Analytics Dashboard, Compare Players, Player Line Lab, Model Diagnostics, Hooplytics Scout, Roster Report |
| 📑 PDF Roster Report | Editorial, magazine-style ReportLab PDF — Tonight's Slate cover, Tonight's Setup card stack, ranked Signal Board, Conviction Map with Signal Index legend and AI Scout Picks, Model Quality trust meter, and per-player profiles with latest context, sparklines, last-4 resolved lines, and active-player tag in the page chrome |
| 🏋️ PDF Player Performance Analytics | Second coach-focused PDF on the same page — 2 pages per player. KPI scorecards, Garmin-style activity rings, ML next-game projection with 80% prediction interval, trend sparklines, Points by Game · Last 10 tile strip, shooting & efficiency bars, skill-axis radar, floor/median/ceiling consistency, role & usage trends, hot/cold streak z-scores, and three accent-topped coaching cards (Strengths / Growth / Focus) |
| 🤖 Hooplytics Scout (AI) | BYO-key chatbot — pick OpenAI or Anthropic Claude in the sidebar; both providers run the same grounded prompts. Hybrid or Strict grounded modes, structured Confidence + Risk factors |
| 📰 AI Slate Brief | One-paragraph daily read of tonight's loudest mispricings on the Home page. Cached per-day so it costs a single API call, regardless of how many times you refresh. |
| 🔍 AI Edge Explainer | Pick any signal in the Analytics Dashboard dropdown and get a 2–3 sentence breakdown grounded in line, projection, recent form, and matchup context. |
| 📡 Live line context | Auto-fetched lines from The Odds API across CLI and dashboard, with session-only BYO-key support in the web app |
| 🎯 Edge board | Slate-wide projection-vs-line gap analysis, signed edges, MORE/LESS calls, and book counts — feeds the dashboard, the AI scout, and the PDF report |
| 👤 Player analysis | Recent form, rolling trends, distributions, player profiles, season averages, and recent-window comparisons |
| 🔄 Role Shift Detection | RoleShiftDetector monitors 4 signals (assists σ, scoring σ, usage FGA%, minutes%) against per-signal thresholds. WARN flags add a confidence note; SUPPRESS flags flip projections to NO_CALL for the affected stats. Suppression map is empirically validated via a full backtest attribution cross-table (403 games, +0.064 overall directional-accuracy lift). Visible in the sidebar Role Alerts widget, the role-shift CLI command, and the check_role_shift MCP tool. |
| 🧠 Modeling stack | RACE blend (Ridge + kNN + Random Forest pipelines) across eight target stats, role and context features |
| 🎚️ Market-anchored calibration | Two-layer calibration applied at inference (Huber per-market actual ≈ a + b·line + per-player residual mean clipped to ±20%) blended with the model via per-market weights — corrects systematic bias without retraining. Built with hooplytics-build-calibration and shipped as bundles/calibration_v1.json |
| 📦 Prebuilt RACE bundles | Multiple ready-to-use bundles ship in bundles/ (e.g. race_fast.joblib, race_playoffs.joblib). The Streamlit app auto-loads one on launch and lets you switch between them from the sidebar — zero cold-start training required |
| 🔬 Diagnostics | RMSE / MAE / R², predicted-vs-actual panels, residual views, feature importance, and per-stat health summaries |
| ⚡ CLI workflows | Single-player projection, prop comparison, scenario inputs, live line board, roster persistence, and prebuilt-bundle training |
| 🤖 MCP server | Use the full Hooplytics engine directly from Claude Desktop. Ten tools cover projections, prop analysis, role shift detection, scenario scoring, live line boards, scout reports, slate briefs, and roster management — see HOOPLYTICS_MCP_SETUP.md |
| 📓 Notebook workflow | Rich exploratory narrative with tables, charts, code, and reproducible analysis in one place |
Lines from The Odds API are treated as analytical inputs — thresholds to compare against projections, recent form, and historical outcomes. Hooplytics uses them to ask better questions:
- How far is the model projection from the current line?
- Is recent form above or below the season baseline?
- How volatile is the player around this threshold?
- How often has the player finished above similar thresholds?
- Does the model signal agree with historical performance?
Hooplytics is a statistical analysis project for learning, exploration, and visualization.
Hooplytics ships with a Typer-based CLI that renders to Rich tables and panels in your terminal. Reproducible, scriptable, and --json friendly.
| Command | Purpose |
|---|---|
hooplytics project |
Project a player's next game across all 8 models |
hooplytics role-shift |
Detect assist / scoring / usage / minutes role shifts with WARN / SUPPRESS severity |
hooplytics prop |
Compare a player projection against a posted line for a single stat |
hooplytics decisions |
8-stat projection summary with model-vs-line gap analysis |
hooplytics scenario |
Score a hypothetical box-score JSON payload |
hooplytics lines |
Live line board for the tracked roster, sorted by projection gap |
hooplytics train |
Pre-warm and cache the model bundle |
hooplytics-train-bundle |
Interactive prebuilt bundle trainer with progress bars and R2 validation gates |
hooplytics-build-calibration |
Fit the market-anchored calibration artifact (bundles/calibration_v1.json) from cached odds + game logs |
hooplytics roster list |
Show the tracked roster |
hooplytics roster add |
Add a player to the tracked roster |
hooplytics roster remove |
Remove a player from the tracked roster |
# Next-game projection across all 8 models
hooplytics project "Victor Wembanyama"
# Role shift check — prints WARN/SUPPRESS panel; exits 0 (NONE), 1 (WARN), 2 (SUPPRESS)
hooplytics role-shift "Jalen Brunson"
hooplytics role-shift "Donovan Mitchell" --json # machine-readable output
# Single-stat comparison — line auto-fetched from The Odds API
hooplytics prop "Shai Gilgeous-Alexander" points
# Same comparison with an explicit line override
hooplytics prop "Shai Gilgeous-Alexander" points --line 31.5
# 8-stat decision view, no live lines (offline mode)
hooplytics decisions "LeBron James" --no-live
# Live line board, freshly fetched
hooplytics lines --refresh
# Score a what-if box-score row
hooplytics scenario '{"fgm":8,"fga":15,"fg3m":3,"ftm":4,"min":34,"fg_pct":0.53,"ft_pct":1.0,"oreb":1,"dreb":5}'
# Roster + cache management
hooplytics roster add "Anthony Edwards"
hooplytics train
# Build and ship a prebuilt Streamlit bundle (defaults to bundles/race_fast.joblib)
hooplytics-train-bundle --mode exhaustive --players-source postseason-plus-anchors
# Fit the market-anchored calibration artifact from cached odds (used automatically by predict)
hooplytics-build-calibration build --season 2024-25 --season 2025-26 --verbose🔑
hooplytics linesand live-enabledprop/decisionsneedODDS_API_KEY(from.envor your shell). All commands support--help, and most reporting commands support--jsonfor scripting.
Hooplytics ships a Model Context Protocol server that exposes the full projection + analytics engine to Claude Desktop (and any MCP-compatible client). Once connected, Claude can answer questions like "project Anthony Edwards' next game and tell me which stat has the biggest edge" or "give me tonight's slate brief — which players have the loudest mispricings?" by calling Hooplytics tools directly.
What you get inside Claude:
- Next-game projections across all 8 RACE models, plus single-stat MORE/LESS calls vs sportsbook lines.
- Role shift detection (
check_role_shift) — ask Claude "is there a role shift concern for Jalen Brunson?" and get a full signal breakdown with WARN / SUPPRESS severity and per-stat NO_CALL flags. - Live line board sorted by projection gap, with book counts and consensus medians.
- Scenario scoring for hypothetical box scores ("what if he plays 36 minutes and shoots 55%?").
- AI Scout reports and AI Slate Briefs — the same prose engine the Streamlit dashboard uses, grounded in projection data.
- Player analytics (game logs, season vs recent averages, volatility, trend deltas) and roster management (add / remove / list / reset).
Setup: see HOOPLYTICS_MCP_SETUP.md for the Claude Desktop config block, prerequisites, and example prompts. Local stdio mode works out of the box; SSE mode is supported for remote clients.
ℹ️ Roster sharing. The MCP server reads and writes the same
~/.hooplytics/roster.jsonthe CLI uses, so adding a player via Claude shows up inhooplytics roster listand vice versa. The Streamlit dashboard keeps its own session-only roster and does not sync with either.
git clone https://github.com/texasbe2trill/hooplytics.git
cd hooplytics
python3 -m venv .venv
source .venv/bin/activate
pip install -e . |
pip install -e .[notebook]Adds Jupyter, ipywidgets, and notebook-only visualization helpers. |
pip install -r requirements.txtMinimal install for Streamlit Cloud or web-only deploys. Does not include notebook deps. |
Requires Python 3.11 or later.
Three optional API keys — none of them are required to launch the app.
| Variable | What it powers |
|---|---|
ODDS_API_KEY |
Live line context, edge board, prop comparisons (The Odds API) |
OPENAI_API_KEY |
Hooplytics Scout, AI Slate Brief, AI Edge Explainer, AI prose in both PDFs |
ANTHROPIC_API_KEY |
Same as above, but routed through Anthropic Claude when the sidebar provider is set to Claude |
Three safe ways to supply any of them:
| Method | How |
|---|---|
📄 Local .env |
Copy .env.example → .env, set ODDS_API_KEY=…, OPENAI_API_KEY=…, ANTHROPIC_API_KEY=… |
| 🐚 Shell session | export ODDS_API_KEY=… (etc.) |
| 🌐 Streamlit sidebar | Paste your key into the sidebar password field — session-only, never stored |
cp .env.example .env
# then edit .env and set the keys you want to use🔓 If no AI key is configured, the AI features (Scout, Slate Brief, Edge Explainer, PDF prose) cleanly disable and the rest of the app keeps working. If no Odds API key is set, you just lose the live-line column.
hooplytics-webThis launches the full multi-page dashboard at http://localhost:8501. A pre-trained RACE model bundle ships with the repo at bundles/race_fast.joblib and is auto-loaded — you get production-quality projections instantly, no training step required.
|
🔑 Sidebar setup (optional) • Paste your Odds API key to enable live lines and the edge board. • Under Hooplytics Scout, pick OpenAI or Anthropic Claude and paste the matching key. • Keys are session-only — never written to disk. |
📍 Where to go first • Home for the slate overview. • Analytics Dashboard for the live edge board. • Player Projection for a single-player deep dive. • Roster Report to export a branded PDF. • Hooplytics Scout to chat with your data. |
👥 Roster management The sidebar lets you add or remove tracked players. Changes are persisted locally between sessions, so your roster is ready to go next time you launch the app. |
hooplytics --help # browse all commands
hooplytics roster list # see who's tracked
hooplytics project "Jalen Brunson" --last-n 10
hooplytics lines --refresh # fresh live line boardHooplytics ships multiple pretrained bundles in bundles/ so you can switch model behavior without retraining. The default app launch uses race_fast.joblib.
In the Streamlit sidebar:
- Make sure Use prebuilt model bundle is checked (it is by default).
- A Bundle dropdown appears right below it listing every
.joblibfile found inbundles/. - Pick a bundle — the app reloads automatically. All projections, the edge board, the AI scout, and the PDF report immediately reflect the new bundle.
| Bundle | Best for |
|---|---|
🏃 race_fast.joblib |
Default. Broad regular-season coverage trained on a large rolling window. Fast to load, balanced across all 8 stat targets. |
🏆 race_playoffs.joblib |
Playoff-tuned variant. Weighted toward higher-stakes, lower-pace games — useful for postseason slates. |
💡 Power users: drop any additional
*.joblibyou trained withhooplytics-train-bundleintobundles/and it appears in the dropdown on next reload. To pin a non-default bundle headlessly, setHOOPLYTICS_PRETRAINED_BUNDLE=/abs/path/to/your.joblibin.envor your Streamlit secrets.
jupyter lab hooplytics.ipynbFollow the notebook top-to-bottom for the full narrative analysis, or jump to a section.
| Source | Used for |
|---|---|
🏀 nba_api |
NBA player game logs and player metadata |
| 📡 The Odds API | Optional live line and book-count context |
| 💾 Local cache | Parquet/JSON caching for faster repeated workflows |
Hooplytics does not redistribute NBA game data. All data is fetched at runtime.
Hooplytics emphasizes transparent, pregame-safe modeling rather than black-box outputs.
| Model name | Target |
|---|---|
points |
pts |
rebounds |
reb |
assists |
ast |
pra |
pts + reb + ast |
threepm |
fg3m |
stl_blk |
stl + blk |
turnovers |
tov |
fantasy_score |
fantasy_score |
🛡️ Pregame-safe. Rolling windows are computed from prior games only — no leakage.
🧱 Pipelines, not magic. Scaling and feature handling stay inside scikit-learn pipelines, train/test boundaries intact.
⚖️ Compare, don't hide. Multiple model families are shown side by side instead of collapsing to one number.
🔬 Diagnostics are first-class. Calibration, residuals, and feature importance are surfaced everywhere, not buried.
🧪 Honest about noise. Steals + blocks and turnovers are framed as lower-signal categories — always.
hooplytics/
├── hooplytics.ipynb # Narrative notebook workflow
├── README.md
├── pyproject.toml
├── requirements.txt
├── docs/
│ ├── index.html
│ ├── assets/ # Notebook-era visualizations
│ └── screenshots/ # Streamlit dashboard captures
├── bundles/
│ ├── race_fast.joblib # Default RACE bundle auto-loaded by the app
│ ├── race_playoffs.joblib # Playoff-tuned RACE bundle (selectable in sidebar)
│ └── calibration_v1.json # Market-anchored calibration artifact (auto-applied by predict)
├── hooplytics/
│ ├── cli.py # Typer CLI entry point (includes `role-shift` command)
│ ├── constants.py
│ ├── data.py # Game log ingestion + caching
│ ├── fantasy.py
│ ├── features_context.py # Pace / matchup / opponent context
│ ├── features_market.py # Market-aware features
│ ├── features_role.py # Role / usage features
│ ├── models.py # 8-stat RACE model training
│ ├── odds.py # The Odds API client
│ ├── backtest.py # Retro-projection accuracy backtest (pregame-safe)
│ ├── role_shift_detector.py # RoleShiftDetector: 4-signal WARN/SUPPRESS engine
│ ├── role_shift_validate.py # Empirical backtest validator (directional accuracy by severity)
│ ├── role_shift_attribute.py # Attribution cross-table: (signal × stat) directional-accuracy lift
│ ├── role_shift_sweep.py # Threshold sweep utility for signal calibration
│ ├── ai_agent.py # Provider-agnostic AI dispatcher (OpenAI ↔ Claude)
│ ├── openai_agent.py # OpenAI transport: chat, slate brief, edge explainer, PDF prose
│ ├── anthropic_agent.py # Anthropic Claude transport (mirrors openai_agent's API)
│ ├── predict.py # Projection + line comparison (auto-applies calibration)
│ ├── calibration.py # Two-layer market-anchored calibration
│ ├── calibration_cli.py # `hooplytics-build-calibration` entry point
│ ├── report.py # PDF Roster Report builder (ReportLab)
│ ├── report_performance.py # PDF Player Performance Analytics builder (ReportLab)
│ ├── train_bundle.py # Interactive prebuilt-bundle trainer
│ └── web/
│ ├── app.py # Streamlit multi-page app
│ ├── charts.py
│ ├── launcher.py # `hooplytics-web` entry point
│ ├── role_shift_widget.py # Role Alerts sidebar widget (WARN/SUPPRESS chips + signal cards)
│ └── styles.py
└── tests/
- 🌊 Streaming Scout responses — render tokens as they arrive in Hooplytics Scout for a snappier read on long replies. Both the OpenAI and Anthropic SDKs already support
stream=True; wire it through the dispatcher and surface viast.write_stream. - 🔀 Side-by-side provider comparison — send the same prompt to OpenAI and Claude in parallel and diff the picks. The provider abstraction already supports this; just needs a UI toggle for second-opinion mode.
- 🗣️ Conversational Line Lab — natural-language "what-ifs" on the scenario explorer ("what changes if pace drops 4?", "show me a back-to-back scenario"). Mutates the scenario dict and re-projects in place.
- 📝 Post-game recap memo — daily auto-generated readout pairing yesterday's model calls with actual outcomes ("model said 24.3 PRA on a 21.5 line, player went 28; the OVER lean held"). Closes the trust loop and makes the model's tracking visible.
- 🧑🎨 Custom system prompts — power-user override for Scout / PDF prose (per-roster voice, risk tolerance, format preferences) without forking the codebase.
- 🏷️ Player archetype clustering — auto-tag players ("primary creator", "stretch big", "off-ball wing") from role + shooting embeddings, surfaced in the grounding payload so AI prose can reason about role-shape directly.
- 👥 Multi-roster profiles — save and switch between named rosters from the sidebar (DFS lineup, season-long fantasy, futures slate) without losing each one's bundle / model state.
- 📡 Richer book-level line telemetry inside the Streamlit app — per-book vig, line movement, and fastest-mover attribution.
- 🔄 Retrain RACE bundles on enriched odds data — role shift detection lifts directional accuracy by +0.064 overall; the next step is retraining the core RACE models with cached sportsbook lines as features so the base projection absorbs market information before the suppression layer runs.
- 🎬 Fresh Streamlit dashboard screenshots and rendered demos for the Roster Report, Performance Analytics, and Hooplytics Scout pages.
- 🎨 Saveable / shareable PDF report templates with custom branding (team colors, logo, footer).
- 📦 Reproducible demo datasets plus broader player and season presets for faster onboarding.
Hooplytics is for statistical analysis, education, and entertainment.
Line values are used as contextual inputs for comparing model projections, recent form, and historical outcomes. The project is not an execution system and not a guarantee of future results.
Issues and pull requests are welcome, especially around:
- 🤖 model quality and calibration
- 🎨 UX improvements for the dashboard or CLI
- 📊 additional visualization layers
- 📚 documentation and reproducibility
MIT © 2026 Chris Campbell
Hooplytics is the Python evolution of hooplyticsR, with additional dashboarding, modeling, and CLI tooling.
Built on the shoulders of:
- 🏀 the
nba_apiproject for accessible NBA stats endpoints - 📡 The Odds API for optional live line context
- 📊 Plotly, Streamlit, Typer, Rich, pandas, and scikit-learn for the core application stack
If Hooplytics helps you think more clearly about player performance, consider giving the repo a ⭐.










