How Our NBA Model Works: Efficiency, Pace, Spreads and Totals
NBA markets are some of the most liquid and closely watched in sports betting β thousands of possessions a night, decades of box-score history, and books that adjust fast. That makes the NBA a good test of what a model can actually add once the obvious edges are gone. Here is exactly how ours is built: what it is trained on, which features move the needle, an honest story about a data outage that forced us to rebuild our own stats pipeline, and how a predicted score turns into a bet you can act on.
Three models and an ensemble
We don't run a single black-box predictor. The NBA system is three models plus an ensemble layer: an XGBoost win classifier that outputs a moneyline probability, and two point regressors β one predicting home points, one predicting away points. A logistic-regression layer blends the classifier's win probability with signal from the regressors to produce the final probabilities we bet on.
Skip the hand-calculation.
Get real value bets flagged for you β 7-day free trialThe two regressors do double duty. Their predicted margin (home points minus away points) and predicted total (home points plus away points) are the raw inputs for spreads and totals β but a single predicted number is not a probability. We combine each prediction with the model's empirical error distribution β a margin standard deviation of roughly 12.2 points and a total standard deviation of roughly 16.9 points, measured from actual prediction errors β to convert "we expect a 4-point home win" into "there is an X% chance the home team covers -3.5" or "there is a Y% chance the game goes over 224.5". That is what lets one predicted score feed three separate markets: moneyline, spreads, and totals (plus a small, deliberately cautious player-props trial that is not the core of the model).
Efficiency, pace and the features that matter
The core philosophy behind the NBA model is simple to state and easy to underestimate: efficiency beats counting stats. A team that scores 115 points on 90 possessions is a different team than one that scores 115 on 105 possessions, even though the box score looks identical. Raw points, rebounds, and assists per game blur that distinction. Per-possession and per-shot metrics don't.
The strongest features in the model are advanced stats: offensive rating and defensive rating (points scored/allowed per 100 possessions), pace (possessions per 48 minutes), effective field-goal percentage (which credits the extra value of a three-pointer), offensive-rebound rate, and free-throw rate. Alongside those sit rolling recent-scoring windows β points scored over the last 10 games β and team Elo ratings. In order of importance, the top features are:
- Recent points β points scored over the last 10 games, a fast-moving read on current form.
- Average pace β possessions per 48 minutes, the single biggest lever on how high a total can go.
- Offensive rating β points scored per 100 possessions, the cleanest read on scoring efficiency.
- Elo difference β the team-strength gap, updated leakage-free before every game.
Pace deserves a special mention because it matters twice: once for how good a team is per possession, and again directly for totals. Two fast-paced teams can inflate a game's total simply by playing more possessions β even if neither team is unusually efficient. Ignore pace and a totals model will misprice exactly the matchups where the number moves the most: uptempo-vs-uptempo and grind-it-out-vs-grind-it-out games.
Why we compute our own advanced stats
Here's a part of the story we don't usually tell: for a while, our NBA pipeline was quietly broken. The advanced-stats source we originally relied on, stats.nba.com, geo-blocks requests from European servers β and it blocked ours completely. Games and box scores kept flowing from elsewhere, but the advanced-stats enrichment silently stopped. Nobody was sending an error; the pipeline just stopped filling in offensive rating, defensive rating, and pace for new games.
The fix was to stop depending on a single proxy source. We rebuilt NBA sourcing on ESPN box scores, which are reachable from Europe, and instead of trying to find another site that publishes ready-made advanced stats, we now compute them ourselves directly from raw box-score data β possession estimates feeding into offensive/defensive ratings, pace, effective field-goal percentage, and the rest, using the same standard formulas the industry uses. The output lines up closely with the official numbers (our computed offensive rating averages roughly 109 versus the official ~111 β the same scale, the same shape).
Owning the computation instead of scraping someone else's pre-built number has a real advantage beyond avoiding the geo-block: we control exactly when a stat is calculated relative to a game, which matters for a reason that is easy to get wrong β a stat computed with hindsight (using information from after the game it's supposed to predict) leaks the answer into the input. Our nightly stats refresh, along with the underlying team Elo ratings, is built to be leakage-free: every rating used to predict a game reflects only what was known before that game tipped off. Season data refreshes and the model gets a fresh retrain every October as the new NBA season starts generating games.
From points to probabilities
Once the regressors output a predicted home score and away score, everything downstream is arithmetic on a distribution, not a single guess. Predicted margin plus the ~12.2-point margin standard deviation gives a cover probability for any spread line. Predicted total plus the ~16.9-point total standard deviation gives an over/under probability for any totals line β the same idea behind point spread betting, just built from our own predicted score rather than the market's.
One deliberate choice: for totals, we don't automatically bet whichever line produces the highest expected value. We bet the line closest to the model's own expected total. A line far from what the model actually expects can look tempting on EV alone, but it's usually a sign of thinner calibration out at the edges, not a bigger real edge. Betting near the model's center keeps us in the region where its probability estimates are best supported by the training data.
From probability to bet
A probability isn't a bet on its own β it has to be compared against a price. Our anchor is de-vigged Pinnacle: we strip the bookmaker margin out of Pinnacle's line to get the sharp market's best estimate of the true probability, and we measure our own model against that, not against the raw quoted odds of any one book. When a softer book prices a side above what de-vigged Pinnacle says it's worth, that gap is the opportunity we're looking for.
The full set of guardrails before a number becomes a bet:
- 5% minimum EV β below this, rounding error and small calibration slippage can eat the whole edge.
- 18% maximum EV β above this, it is almost always a data artifact rather than a real mispricing.
- 3.50 maximum odds β long-shot prices are the least reliably calibrated part of any model.
- 25% fractional Kelly, capped at 5% of bankroll β deliberately conservative sizing, not full Kelly.
That last point matters more than it looks. Even a well-calibrated model is wrong often enough that betting a full Kelly stake would be reckless β fractional Kelly trades some long-run growth for a much smoother bankroll curve.
How we know it works
The model is trained on roughly 13,400 games from 2015 to 2026. Its win-probability calibration scores a Brier score of 0.1877 β lower is better, and 0.25 is what you'd get from a coin flip, so this reflects real predictive skill, not noise. The point regressors carry a mean absolute error of roughly 8.2 points for home teams and 8.3 for away teams per game.
But a good Brier score on the moneyline doesn't automatically mean the model is profitable on every market, and we don't pretend otherwise. We validate different markets differently. For moneyline bets, closing line value β whether we consistently get a better price than the market settles on β is the metric that matters most long-run. For spreads and totals, we lean more on realized ROI and calibration checks, because we've measured cases across our models where a bet showed positive CLV but still lost money β handicap-style markets can move favorably in price and still miss on results often enough that CLV alone would mislead you. Grading each market on the metric that actually reflects its behavior, instead of one number for everything, is part of being honest about what the data shows.
None of this changes the basic reality of NBA markets: they are efficient, well-staffed, and fast-moving. We are not claiming to outsmart Pinnacle on a nightly basis β we are trying to match its accuracy and find the moments where a softer book hasn't caught up. That's a narrower, more honest goal than "beating the NBA," and it's the one the numbers above actually support.
Curious what the model outputs for tonight's slate? The NBA hub shows today's games, and our full method β including how spreads and totals are priced across every sport we cover β is on the model page. If you want to see how the numbers have actually performed over time rather than take our word for it, the track record is public and updated continuously.