A full-stack NBA analytics platform with a live/replay game dashboard, historical prop-line movement charts, and a machine-learning prop-bet screener.
Stack: React · Node.js · WebSockets · Python · PostgreSQL
┌─────────────┐ REST/WebSocket ┌───────────────┐
│ React │ ◄──────────────────────► │ Node.js │
│ Dashboard │ │ API + WS │
└─────────────┘ └──────┬────────┘
│ internal
│ HTTP push
┌──────▼────────┐
│ Python │
│ Pipeline │
│ - Backfill │
│ - Live poller │
│ - Replay eng │
│ - ML screener │
└──────┬────────┘
│
┌──────▼────────┐
│ PostgreSQL │
└───────────────┘
Live and replay events flow through the same pathway (Python → internal Node endpoint → WebSocket broadcast), so the frontend never branches on whether data is real or replayed.
| API | Purpose | Env var | Notes |
|---|---|---|---|
stats.nba.com (via nba_api) |
Season backfill, schedule sync, nightly boxscores | — | Free, no key |
| The Odds API v4 | Live player-prop lines from real sportsbooks | THE_ODDS_API |
Free tier: 500 credits/month |
| ESPN injury endpoint | Injury status feature for ML model | — | Free, no key |
Without API keys the system uses a deterministic synthetic season (1,230 games) so it runs fully offline.
- Node.js 20+
- Python 3.10+
- PostgreSQL 14+ (Homebrew or Docker)
# Homebrew
brew services start postgresql@18
psql postgres -c "CREATE USER sideline WITH PASSWORD 'sideline'"
psql postgres -c "CREATE DATABASE sideline OWNER sideline"
# Or Docker
docker compose up -dcd pipeline
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env # add API keys if available
python -m sideline_pipeline.migrate # apply schema
python -m sideline_pipeline.backfill --seasons=2020,2021,2022,2023,2024,2025 # 6 seasons of real NBA data
python -m sideline_pipeline.augment --events-games 40 # build synthetic odds + events
python -m sideline_pipeline.screener train # train XGBoost + score screenercd server
npm install
cp .env.example .env
npm run dev # starts on :4000cd frontend
npm install
cp .env.example .env
npm run dev # starts on :5173cd pipeline
python -m sideline_pipeline.replay --loop # cycles through historical gamesOpen http://localhost:5173 — the ticker should start scrolling player stats within seconds.
| Page | Path | Description |
|---|---|---|
| Dashboard | / |
Live/replayed game scores, WebSocket-driven updates |
| Line Movement | /lines |
Search any player → step chart of prop line history across DraftKings & FanDuel |
| Screener | /screener |
ML-ranked prop edges, filterable by stat/side/min edge |
| Game Picks | /picks |
Top ML plays for any selected game with probability bars |
| Parlay Builder | /parlay |
Auto-build or hand-pick optimal multi-leg parlays across games |
| Game Detail | /game/:id |
Full box score + ML picks for a single game (from Dashboard cards) |
| Player Profile | /player/:id |
Rolling averages, recent games, favored picks, most likely hits |
| About | /about |
Project overview, features, model metrics, tech stack |
# Python (10 unit tests)
DATABASE_URL=postgresql://sideline:sideline@localhost:5433/sideline \
python -m pytest pipeline/tests/ -v
# Node (2 integration tests — WebSocket push pipeline)
cd server && node --test src/ws.test.js
# Playwright e2e (4 tests — UI smoke tests)
cd frontend && npx playwright testXGBoost classifiers with isotonic calibration (CalibratedClassifierCV), one per prop category, trained on 6 real NBA seasons (2020-2025) — 127K+ player-game rows across 6,000+ games. Time-ordered train/test split (not random) for honest generalization numbers.
| Category | AUC | Brier | Simulated ROI |
|---|---|---|---|
| Points | 0.784 | 0.189 | +38.6% |
| Rebounds | 0.742 | 0.203 | +31.7% |
| Assists | 0.727 | 0.207 | +28.7% |
| 3-Pointers | 0.760 | 0.186 | +26.3% |
Test set: 28,823 games. Blended ROI across categories: +31.3%.
Features: L5/L10/season rolling averages (season-aware groupby), cross-season career average, minutes, home/away, rest days, back-to-back flag, opponent defensive rating, injury severity (out=1.0, doubtful=0.75, questionable=0.5, probable=0.25).
Parlay math: joint probability assumes leg independence — P(parlay) = ∏ P(leg). Auto-Build ranks candidates by quality = model_prob × (1 + edge × 2)^1.2 and greedily picks the top N unique players.
Three modules make the system self-updating once the NBA season is live:
| Module | Cost | What it does |
|---|---|---|
schedule_sync |
Free | Pulls the next N days of scheduled games from stats.nba.com; auto-detects season rollover (Oct 1) |
nightly_retrain |
Free | Ingests yesterday's completed boxscores, upserts players/games/stats, retrains XGBoost on all seasons |
odds_poll |
Uses Odds API credits | Fetches live player-prop odds from featured games, re-scores the screener |
Cost model for odds_poll (Odds API v4):
/eventsenumeration → FREE/event/{id}/oddsfor 4 markets × 1 region (us) → 4 credits/game- Default
ODDS_POLL_FEATURED_GAMES=2→ 8 credits per poll - Free tier = 500 credits/month → ~62 game days of coverage
Safety layers (all must pass):
ODDS_POLL_ENABLED=truein.env(default: false — no credits burn until you flip it)THE_ODDS_APIkey present- Current month's usage under
ODDS_MONTHLY_CREDIT_CAP(default 450, tracked inodds_api_usagetable) ODDS_POLL_FEATURED_GAMEScaps games polled per run
Enabling for the 2026-27 season:
# 1. Add to pipeline/.env
THE_ODDS_API=your_key_here
ODDS_POLL_ENABLED=true
# 2. Install cron jobs (macOS launchd or crontab). Example crontab:
# crontab -e
crontab -l | cat << 'EOF' | crontab -
0 5 * * * /path/to/SideLine/pipeline/scripts/run_pipeline_job.sh schedule_sync --days 7
0 3 * * * /path/to/SideLine/pipeline/scripts/run_pipeline_job.sh nightly_retrain --days 1
0 17 * * * /path/to/SideLine/pipeline/scripts/run_pipeline_job.sh odds_poll
EOF
# 3. Verify without burning credits
./pipeline/scripts/run_pipeline_job.sh odds_poll --dry-runLogs land in pipeline/logs/{module}_YYYYMMDD.log. Credit burn rate is queryable:
SELECT date_trunc('day', called_at) AS day, SUM(credits_used) AS credits
FROM odds_api_usage
GROUP BY 1 ORDER BY 1 DESC;| Component | Recommended host |
|---|---|
| React frontend | Vercel / Netlify |
| Node API + WebSocket | Render / Railway / Fly.io |
| Python pipeline (worker) | Same host as Node, or separate cron worker |
| PostgreSQL | Neon / Supabase (free tier) |