Slide home
slam picks

Side project · Fair MLB odds, every day

Slam Picks

Sportsbooks publish a price for every MLB game. Slam Picks builds its own, then shows exactly where the books disagree.

Live in productionslam-picks — live
Slam Picks priors stage: stat table and 7-way outcome distribution for a pitcher.
the priors stage · a pitcher's stat table and 7-way outcome distribution

01 · The elevator pitch

What it is

Slam Picks estimates every player's true talent, simulates each MLB game thousands of times, and prices every market from the results: moneyline, totals, run line, first five, and player props. The books charge vig; the model strips it out and compares fair numbers to fair numbers. The gap between the two prices is the product.

It runs itself: a morning job captures the slate and prices it, a sweep re-prices each game the moment its lineup is confirmed, and yesterday's calls are graded against what actually happened. The repo is private, but the whole methodology is public on the site.

02 · The priors

The talent layer

Small samples lie, so no raw stat enters the model. Every rate is shrunk toward a player-specific prior built from three years of weighted history, and how far it shrinks depends on the stat: strikeout rate stabilizes fast, contact quality slowly. A veteran's hot week barely moves him; a rookie with no track record moves fastest.

Contact quality gets extra care: the model anchors on Statcast expected-contact data (exit velocity and launch angle, with defense, park, and luck stripped out) instead of box-score results. Platoon splits regress by the size of the split itself, batter and pitcher merge through odds-ratio math that amplifies shared tendencies instead of averaging them away, and park factors apply per stat. That stabilized 7-way distribution (the hero shot above) is what every simulated plate appearance draws from.

03 · The engine room

The simulation

The simulator plays baseball, not statistics: one plate appearance at a time, drawn from each matchup's 7-way outcome distribution, through nine innings and extras. The starter tires as his estimated pitch count climbs, a manager model decides when to pull him (tuned against roughly 1,900 real starts), and a league-average bullpen finishes the game, re-merged against every batter it faces.

Before pricing, a root-finder tunes each starter's pull timing so his simulated workload lands on his real one, probing with the same random seed so only the pull moves. Outing length emerges from the simulated game instead of being patched on afterward.

Simulated gameIllustrative run
Sim #1,847123456789R
CHW0100200003
BOS20010020x5

One of thousands per matchup · every cell a chain of plate appearances

04 · The receipts

Proving it

Two engines, one baseball. The Monte Carlo simulator is paired with an exact Markov solver that enumerates every branch of an inning instead of sampling, and both share the same transition math, so the sim's average must converge to the closed-form answer. That check runs in the test suite over 60,000 innings, and again live on the site for every simulated game.

The paranoia goes deeper. A compiled C kernel runs the inner loop about 7x faster, but only after proving itself bit-for-bit identical to the Python loop on the machine it deploys to; if the platform's math library disagrees by a single bit, it silently steps aside. And backtests can't cheat: the schema stores per-day count deltas so a point-in-time read is a sum over dates (future leakage is unrepresentable, not just avoided), and a validator walks every stored pitch row to reject any snapshot that touches the future.

Inning oracle · expected runsIllustrative run
Analytic (exact)4.62
Monte Carlo4.64

Δ 0.02 · matchtwo algorithms, one answer

05 · The market

Pricing the disagreement

Odds come in from a commercial feed, get de-vigged, and line up against the model's fair prices. Ten markets are graded end to end against real outcomes, reconciled from two independent sources, and the model's closing-line odds are captured near first pitch for every game.

The scoreboard is honest: the site tracks ROI, closing-line value (did the market move toward the model's number by first pitch), and Brier score on win probabilities, with deeper error metrics checked in offline backtests. A strategies workbench replays graded history under different staking rules, fractional Kelly included. Live automated staking is deliberately still on the roadmap.

Model vs bookIllustrative run
BOS MLbook -145 · fair -158edge
O 8.5book -110 · fair -104pass
CHW +1.5book -122 · fair -125pass

06 · War stories

Production stories

Resolved in production

The deploy that dropped a third of its traffic

Production debugging

The site deploys itself from a Mac mini: push to main, Coolify builds the image, health-checks the new container, and swaps traffic behind a Cloudflare Tunnel with no open ports. Measuring the swap showed a third of requests hitting the dying container for 16 seconds. A hand-written Traefik retry middleware closed the gap: 138 of 138 probes clean through a rolling deploy.

Resolved in production

The test harness that lied

Performance work

Every speedup must prove it changed nothing: an equivalence harness demands byte-identical output before an optimization counts. It once reported PASS while comparing zero rows. Now every check needs a negative control that proves it can fail, and the performance notes keep a numbered list of sixteen methodology pitfalls that each produced a wrong answer at least once.

07 · The product

Screenshots

The whole methodology is public: the site walks its five-stage pipeline on a real game, and a derivation page shows any player's projection being built.

the live slate
Slam Picks live slate: game cards comparing model prices to no-vig book prices, with edges and projected totals.
the methodology
Slam Picks methodology page: model card with scope boundaries, and the five-stage pipeline walkthrough on a real game.

08 · The toolbox

Stack

Model
Python 3.12, NumPy, Pydantic v2, Cython kernel
API
FastAPI
Frontend
React 19, TypeScript, Vite
Data
Neon Postgres (Prisma migrations, SQLAlchemy runtime)
Infra
Docker, Coolify on a Mac mini, Cloudflare Tunnel, self-hosted CI runners

A clean-room rebuild of a v1 that grew up inside a monorepo: typed contracts instead of nested dicts, 1,216 unit tests, and a daily pipeline that runs without anyone watching.

Postgame

See it live

Fair MLB odds, every day, priced by a model that shows its work.