How it works
AdjEM follows the KenPom approach. For every game we count possessions (field-goal attempts minus offensive rebounds plus turnovers plus 0.44 × free-throw attempts) and points per 100 possessions for each side. Then every team's offense and defense, plus home-court advantage, are solved together with ridge regression. Because they're solved at once, a team that piled up points against bad defenses gets marked down, and one that held up against good offenses gets credit. Tempo (possessions per 40 minutes) is solved the same way.
Recent games count more: a game's weight halves every 120 days. Last season's games count as a prior that fades as this season's games come in, so early-season ratings lean on last year. Ratings are pulled gently toward average until there's enough evidence, which stops a team that's played three games from looking like a champion.
Predictions combine the two teams' offense, defense and tempo into an expected score. The win probability comes from how big the model's misses have historically been: games swing about ±13.8 points around the prediction. The season simulator plays out the remaining schedule and the finals 10,000 times. In each run every team's rating is also nudged up or down by a random amount, because the ratings are only estimates. The size of that nudge was set by replaying past seasons, and it shrinks as the season goes on.
League right now
| Average points per 100 possessions | 115.2 |
| Average possessions per 40 minutes | 80.3 |
| Home-court advantage | +1.4 points per game |
| Typical prediction error (1 standard deviation) | 13.8 points |
Backtest
Walk-forward test from 2016-10-06 to 2026-10-11: every week the model was refit using only earlier games, then predicted the next week. That's 1,480 games the model had never seen.
Calibration
When the model says a team is a 65% favourite, does it win about 65% of the time?
| Favourite's chance | Games | Predicted | Actual |
|---|---|---|---|
| 50-60% | 715 | 55.0% | 57.6% |
| 60-70% | 564 | 64.5% | 65.1% |
| 70-80% | 173 | 73.8% | 69.9% |
| 80-90% | 27 | 83.5% | 85.2% |
| 90-100% | 1 | 92.2% | 100.0% |
By season
| Season | Games | Winner % | Log loss |
|---|---|---|---|
| 2016-17 | 115 | 60.9% | 0.699 |
| 2017-18 | 118 | 61.9% | 0.665 |
| 2018-19 | 120 | 59.2% | 0.669 |
| 2019-20 | 134 | 67.2% | 0.615 |
| 2020-21 | 171 | 61.4% | 0.657 |
| 2021-22 | 148 | 65.5% | 0.643 |
| 2022-23 | 153 | 60.8% | 0.665 |
| 2023-24 | 154 | 58.4% | 0.673 |
| 2024-25 | 159 | 64.8% | 0.660 |
| 2025-26 | 179 | 66.5% | 0.609 |
| 2026-27 | 29 | 44.8% | 0.721 |
Log loss rewards confident correct calls and punishes confident misses; lower is better. A coin flip scores 0.693.
Prediction ledger
The public ledger has started: 136 predictions are logged and will be graded as the games are played.
This season so far (predictions rebuilt using only earlier games): 29 games, winner 44.8%, log loss 0.731, margin error 10.8.
Data
Box scores and schedules come from the NBL's official stats feed (FIBA LiveStats), collected daily by Jason Zivkovic's open-source nblR project. Some splits (points in the paint, fast-break points) aren't in this season's feed, so they're not shown.