Case study
SharpEdge: probabilistic analytics for sports markets
A production full-stack analytics platform with calibrated ML models, daily drift monitoring, and closing-line-value as the honest objective. Ingests odds from 40+ books, serves edge detection through a React web app and a public REST API. Built solo end to end.
The problem
Recreational and semi-serious sports bettors want an honest read on whether their bets are actually beating the market. The retail tools in the space either sell unfalsifiable claims ("we're sharp, trust us") or bury the analytics behind a subscription that gates the math. Sportsbooks themselves have no incentive to tell you when you're losing slowly.
SharpEdge is an analytics-first product built the other way around. It computes edges against a devigged book consensus, sizes bets with quarter-Kelly capped at 2% of bankroll, and uses closing-line value as the long-term edge metric. Every prediction is graded against its closing line. Every model has a drift monitor that demotes it automatically when the real-world Brier score diverges from the training baseline.
What it does
1. Real-time odds ingestion across 40+ books
A pg_cron job in Supabase calls capture_all_sports_odds every 30 minutes, fetching current prices from The Odds API across NBA, NHL, MLB, and several soccer leagues. Pre-game, a separate closing_lines capture runs within minutes of tip-off. Result: 334,000+ odds snapshots in the warehouse, one row per (event, market, book, outcome).
2. Calibrated ML models with drift monitoring
Two production models: Dixon-Coles Poisson for NHL totals (xi-calibrated time-decay plus back-to-back covariates) and CatBoost GBT for NBA totals (with node2vec embeddings from a two-man-combo chemistry graph). A Python pipeline pushes daily predictions into model_predictions with full lineage back to model_runs. A nightly drift monitor computes 30-day Brier against the training baseline and demotes any model whose drift ratio exceeds 1.30.
3. Edge detection, Kelly sizing, and CLV feedback
The recommend-bets edge function merges three signals per event: market-arb opportunities (one book pricing off the consensus), model edges (where a prediction beats the fair line by a threshold), and cross-market reconciliation (no incoherent picks like "CLE -1.5 run-line + STL ML" on the same MLB game). Kelly sizing applies a quarter-Kelly fraction with a hard 2% bankroll cap. Every placed bet auto-grades against the Pinnacle closing line. CLV rolls up into a 7-bet trailing average on the user dashboard.
4. Public REST API
Three endpoints at api.sharpedge.app/v1 expose the math engine: /devig converts American prices to fair probabilities, /edge computes edge versus consensus for a given event, /clv returns the closing-line-value history for a user or API key. JWT-authenticated, rate-limited, documented with curl examples.
Architecture
External data
Odds API (40+ books, 20+ sports)
ESPN / NFL (results for grading)
|
v
Ingest (pg_cron in Supabase)
capture_all_sports_odds every 30 min
closing_lines capture pre-game
|
v
Postgres warehouse (clotho- and sharpedge-style schema isolation)
odds_snapshots 334k+ rows
closing_lines
model_runs versioned model registry
model_predictions with is_recommended_bet flag
bets
|
v
Offline ML (Python, daily push) Supabase edge functions (Deno)
Dixon-Coles Poisson (NHL) <---> recommend-bets (Kelly + reconcile)
CatBoost GBT (NBA) update-clv (nightly)
node2vec chemistry embeddings api-v1-devig / edge / clv
drift_monitor.py (demotes models)
|
v
React 19 + Vite frontend
Dashboard, Analytics, Settings, /claude-feed live-odds view
PWA with service worker, offline page, push-ready
Stripe billing, 7-day trial, annual + monthly plans, referral program
Engineering decisions that mattered
SECURITY DEFINER RPCs instead of service-role on the frontend
The nightly pipeline calls Supabase RPCs like get_pending_predictions, apply_prediction_grades, get_active_models, insert_drift_check, and demote_model_run. All are SECURITY DEFINER functions with explicit GRANT EXECUTE to authenticated, anon. That means the grading pipeline runs under the anon key on a local machine. The service-role key never touches a client. Administrative writes go through the backend, authenticated, then call Supabase with elevated privileges.
DISTINCT ON RPC to bypass PostgREST's 1000-row cap
A subtle production bug: supabase.from('odds_snapshots').select(...) silently truncates at 1000 rows when you hit the limit, in undefined order, with no error. A 4-sport slate's snapshot volume (30k+ rows/session) was getting truncated such that NHL and NBA dropped out entirely on some runs. Fix: migration 00040_latest_odds_snapshots_rpc.sql created a get_latest_odds_snapshots_json(p_sports text[]) RPC that does DISTINCT ON (event_id, market, bookmaker_key) ORDER BY fetched_at DESC server-side and returns the result as a single jsonb array. One PostgREST row, no cap. Row volume dropped 30k to 4k. Query time dropped 140s to 10s.
Pinnacle capture via regions=us,eu
Pinnacle exited the US market years ago and lives only in regions=eu. Early versions of the ingestor queried regions=us only. Consensus-weighted edge math used "sharp book" weights that Pinnacle would have anchored, but Pinnacle was missing from the feed entirely. Flipping to regions=us,eu doubled the API credit cost but restored the anchor. Defensive event-merge logic collapses cross-region duplicates.
ROI-based drift demotion, not Brier-only
The first drift monitor computed Brier across all graded predictions and called that "accuracy." It looked healthy. Then a deeper drill found that model_predictions stores BOTH sides of every market as separate rows. Brier across a paired winner and loser forces the aggregate toward 50/50 regardless of model quality. Every model looked healthy while underperforming on money. The fix: migration 00044_is_recommended_bet.sql added an is_recommended_bet boolean that flips TRUE only when edge is positive AND a real book price exists. The drift monitor now filters on that flag and measures Brier on the subset the model would actually bet. That subset is closer to what matters.
Row-Level Security on every table, enforced end to end
Every table in the public schema has RLS enabled with policy definitions in migrations. Service-role writes are scoped to the backend; authenticated user reads are restricted to their own rows via auth.uid() checks. A CIA-grade security audit in April caught 12 P0 issues (secret rotation, rate limits, prompt-injection surface areas) and all 12 shipped within a session. The full backup bundle lives at audit-2026-04-15/.
What this case study is really showing
This is what shipping a probabilistic analytics product looks like when you take the long metric (CLV) seriously instead of the vanity metric (win rate). Every production model ships with a drift monitor. Every bet auto-grades. Every migration is tracked. The math engine is separated from the UI, exposed through a versioned REST API, and deployable to other fronts without rewriting.
SharpEdge today runs as a personal tool for my own betting, a portfolio piece for enterprise buyers evaluating me for AI and data-platform work, and a public live demo of what calibrated model governance looks like on a single-developer budget. The code lives at the live URL above. The architecture doc, model governance doc, and bugfix case study all live in the repo under docs/.
Stack
- Frontend
- React 19, TypeScript, Vite 6, Tailwind CSS 4, Zustand, PWA
- Backend (edge)
- Supabase edge functions, Deno, JWT auth, Sentry, PostHog
- Database
- Supabase PostgreSQL, pg_cron, Row-Level Security, SECURITY DEFINER RPCs
- ML pipeline
- Python 3.12, Dixon-Coles Poisson, CatBoost, node2vec, Windows Task Scheduler
- External APIs
- The Odds API (40+ books), ESPN, Anthropic Claude Sonnet 4.6
- Billing
- Stripe subscriptions (monthly + annual + 7-day trial), customer portal, referral program
- Resend (welcome, trial-ending, payment-failed)
- Deploy
- Vercel (web), pg_cron (ingest), Windows Task Scheduler (nightly ML)
Why it matters for hiring
If you're looking for someone who can design a warehouse schema, wire ingestion from external APIs, ship ML models end to end with drift monitoring, build a production React frontend, handle Stripe billing plus referral programs plus transactional email, and harden the whole thing through a security audit, this is the shape of work I do. SharpEdge is the live proof.
If you're a procurement, finance, or manufacturing team thinking about AI features on top of data you already own, the same engineering pattern transfers. See the Clotho case study for the enterprise version of the same playbook.
Work with me
Fixed fee, written scope, working code as the deliverable. NDAs welcome. Enterprise pilots come with full source handoff.