One sentence: it guesses the chance the home team wins from four numbers — how good each starting pitcher has been this season, and how many runs each team scores and allows. That's the whole model. This page shows the actual machinery, live, so changing it is a matter of knowing which part to touch.
Current verdict first, because this site leads with it: over 640out-of-sample games the model's Brier score is 0.2465vs the market's 0.2469 (t = -0.22) — a statistical tie. Baseball single games are close to coin flips; knowing that IS the finding.
ESPN box scores → the warehouse
Every finished game's full box score is scraped (arena:mlb-scrape) into the local DuckDB warehouse — every batter and pitcher line, raw. Source: ESPN's public API. Nothing paid, nothing secret.
Warehouse → season aggregates
For each starting pitcher: innings, earned runs, hits+walks, summed over the season. For each team: games, runs scored, runs allowed. Ratios of these sums ARE the ratings — starter ERA and WHIP, team runs per game. (src/server/arena/mlb-ratings.ts)
Gates: refuse to guess
A starter with fewer than 3 starts or 5 innings, or a team with fewer than 10 games, has no rating — and a game missing any rating is not predicted at all. Early-season noise is excluded by construction, not smoothed over.
Four features, home-minus-away
Starter ERA edge, starter WHIP edge, offense edge (runs scored), defense edge (runs allowed). Each is standardized — 'how unusual is this gap?' — so runs and runners live on one scale.
Logistic regression → P(home win)
Multiply each standardized feature by its learned weight, add the intercept (home advantage lives there), squash through the S-curve. Weights were fit on past seasons by gradient descent (fitLogistic, same file) and FROZEN — the cloud never refits on its own.
Published brain → the ledger
arena:mlb-publish (local) writes weights + aggregates to Neon; the daily cron (arena:mlb-predict) loads them, tops up aggregates from ESPN for games since the snapshot, and logs P(home win) BEFORE first pitch next to the market's price. Outcomes get scored later — the /sports/models ledger is that record.
These are the four learned weights in the published brain right now (season 2026, aggregates through 2026-07-25, published 2026-07-28). Bars point toward the side the feature pushes the prediction: a positive weight on a “home is better” feature raises P(home win). The intercept is +0.120— the model's built-in home advantage before it knows anything about the teams.
Away starter's ERA minus home starter's. ERA = earned runs the pitcher allows per 9 innings — lower is better, so a positive number means the home starter has been better.
Away starter's WHIP minus home starter's. WHIP = walks + hits allowed per inning — how much traffic the pitcher lets on base. Positive = home starter better.
Home team's average runs scored per game minus the away team's. Positive = home lineup scores more.
Away team's average runs allowed minus home's. Positive = the home team gives up fewer runs.
Reading it: the model currently trusts Offense edge most. Because features are standardized, weights are directly comparable — that is why the standardization step exists.
src/server/arena/mlb-ratings.ts (features()). Add a fifth number here and refit; the cloud state carries whatever the fit produces.starterRating / teamRate (3 starts & 5 IP; 10 games). Loosen them and early-season games get predicted — noisily.scripts/research/mlb-model.ts trains and backtests; arena:mlb-publish freezes the result to Neon. The cron never refits — every probability is traceable to a published brain.This site writes probabilities as cents per $1: a 60c price means the market thinks something is 60% likely, and a correct $0.60 bet pays $1. If your true chance is better than the price, the bet is good; if worse, it's bad. That one sentence is most of sports betting.
A bet on which team wins the game, nothing else. American odds like -150 mean 'bet $150 to win $100' (a favorite); +130 means 'bet $100 to win $130' (an underdog). Both convert to an implied probability.
A sportsbook's two sides always add to MORE than 100% — the extra is the vig, the book's built-in fee. De-vigging scales both sides down so they add to exactly 100%; the result is the book's honest opinion, called the fair or no-vig price.
A feature is one pre-game fact turned into a number (a rating gap, a yes/no flag). A weight is the knob saying how much that fact matters, learned from past games. The model is nothing but features × weights, summed, squashed. Adding a feature = adding a column; training finds its knob.
The simplest honest prediction machine: multiply each input by a learned weight, add them up, and squash the total through an S-shaped curve so the answer is always between 0 and 1 — a probability. The weights are learned from past games by nudging them until predictions fit outcomes.
The S-shaped curve that turns any sum into a probability. Sum 0 → 50%. Sum +2 → about 88%. It never quite reaches 0% or 100% — the model is structurally incapable of certainty, which is a feature.
The published aggregates cover 30 teams and 314 starters, of whom 230 clear the rating gates. Its current ten best-rated starters by ERA:
| starter | starts | ERA | WHIP |
|---|---|---|---|
| Mason Montgomery | 5 | 0.00 | 1.20 |
| Grant Taylor | 4 | 0.00 | 0.20 |
| Braydon Fisher | 7 | 1.13 | 1.25 |
| Bryan Hudson | 6 | 1.29 | 0.57 |
| Shohei Ohtani | 4 | 1.38 | 0.85 |
| Jacob Misiorowski | 19 | 1.57 | 0.77 |
| Ben Brown | 8 | 1.70 | 0.90 |
| Tyler Alexander | 4 | 1.80 | 0.60 |
| PJ Poulin | 11 | 1.80 | 1.00 |
| Zac Thornton | 4 | 1.93 | 0.86 |
A hypothetical tonight: CLE (with a league-median starter) at LAD (with Mason Montgomery — currently the best-rated starter in the brain). Computed with the exact functions the cron uses:
| feature | raw | z | × weight | = pushes |
|---|---|---|---|---|
| Starter ERA edge | 4.35 | 2.16 | +0.040 | +0.086 |
| Starter WHIP edge | 0.54 | 1.67 | +0.083 | +0.139 |
| Offense edge | 1.18 | 1.54 | +0.144 | +0.221 |
| Defense edge | 0.48 | 0.56 | +0.126 | +0.070 |
| home advantage (intercept) | +0.120 | |||
Sum of pushes = +0.636 in log-odds; the S-curve turns that into P(LAD wins) = 65.4%. Notice how modest that is despite the best starter and the best run differential in the league — single baseball games are mostly noise, and an honest model says so.
How the knobs get set: predict every past game, measure how wrong you were, nudge every weight a tiny step in the direction that reduces the wrongness, repeat thousands of times. No insight, no magic — just accumulated nudging. Everything interesting lives in the features you feed it.
Do the percentages mean what they say? Collect every game the model called ~65% and check that ~65% actually went that way. A model can be calibrated and still lose to the market — calibration is honesty, not edge.
Writing down what you expect BEFORE running the test. Try enough features and something will look great by pure luck; the registered prediction plus a fixed bar (|t| ≥ 2, stable weights) is the defense. This project's ledger: two nulls (MLB form, MLB head-to-head) and one partial pass (NFL QB availability).
Before the weights see an input, it is rescaled to 'how unusual is this, in standard deviations from average?'. That puts ERA (runs) and WHIP (runners) on the same footing so their weights are comparable.
The honesty meter for probability forecasts: the average of (prediction − outcome)², where the outcome is 1 or 0. Lower is better. Always guessing 50% scores 0.25 — a model has to beat that to know anything.
A 0.5-to-1 score for 'does this single number sort winners above losers?'. 0.5 = coin flip, 1.0 = perfect. A feature with AUC 0.66 alone is genuinely informative; 0.52 is barely a whisper.
How many standard errors an effect is away from zero. Roughly: |t| ≥ 2 means the effect is probably real; below that it is indistinguishable from luck. This site refuses to believe anything under t = 2, and distrusts single t ≥ 2 results too (see the arena page).
Did the price move toward your side after you bet? The closing price is the market's most informed opinion, so consistently beating it is the fastest reliable evidence of skill — much faster than waiting for wins and losses to pile up.
The stake-sizing formula that maximizes long-run growth: bet a fraction of bankroll proportional to your edge. Everyone who uses it bets a QUARTER of what it says, because overestimating your edge is easy and overbetting is ruinous.
Tested on games the model never saw while learning. A model graded on its own training data is a student grading their own exam — every honest number on this site is out-of-sample, and it's why our numbers look worse (and truer) than most.
| MIA |
| 104 |
| 4.41 |
| 4.33 |
| ARI | 104 | 4.51 | 4.49 |
| STL | 103 | 4.43 | 4.49 |
| CLE | 105 | 4.00 | 4.13 |
| BAL | 104 | 4.60 | 4.73 |
| PHI | 104 | 4.31 | 4.46 |
| SD | 104 | 4.13 | 4.40 |
| TEX | 103 | 4.19 | 4.48 |
| MIN | 105 | 4.74 | 5.05 |
| HOU | 105 | 4.59 | 4.98 |
| LAA | 104 | 4.31 | 4.88 |
| CIN | 102 | 4.19 | 4.78 |
| TOR | 104 | 3.93 | 4.54 |
| SF | 103 | 4.12 | 4.73 |
| NYM | 104 | 4.05 | 4.68 |
| KC | 106 | 4.20 | 5.11 |
| COL | 105 | 4.76 | 5.70 |
| ATH | 103 | 4.47 | 5.63 |