This model predicts the winner of NCAA Division I men's basketball games. The harder problem isn't the prediction — it's proving the prediction would have worked. Sports models are notoriously easy to fool: feed them a little information from the future and last season's results look uncannily predictable.
So the whole pipeline is built around one rule — every game is forecast using only what was known before tip-off— and the numbers on the overview page are the result: out-of-sample accuracy, calibration, and a head-to-head against the betting market across two complete seasons. This page explains how it's built and why the backtest can be trusted.
Each game is turned into 44 features describing the two teams: prior-season efficiency and ranking, running form within the current season (win rate, scoring margin, last-ten record, strength of schedule), eligibility-adjusted roster strength, home court, and travel distance and time-zone shift.
The critical detail is wheneach number is measured. Season-form features are computed by stepping through games in chronological order and locking each game's features beforeupdating the running totals — so a November game only ever sees October's games, never March's. Roster strength blends prior-season player ratings with a Bayesian update as the season unfolds. Nothing from the game being predicted, or any later game, can touch its features.
This is the guard against data leakage — the most common way a sports backtest quietly cheats. Get this wrong and the accuracy on this site would be fiction.
The features feed an XGBoost gradient-boosted tree model that outputs a single number: the probability the home team wins. Trees handle the non-linear interactions between ratings, form and context without hand-built rules.
One subtlety: a model can develop a small bias toward whichever side is labelled "team A." To cancel it, each game is predicted twice — once as listed and once with the teams swapped — and the two are averaged. This symmetric prediction removes positional bias and measurably improved the scores.
p(home) = ½ · [ p(home) + (1 − p_swap) ]p_swap = the model run with the two teams exchangedRather than train once and test on a held-out chunk, the backtest walks forward through the calendar. To predict a given day's games, the model is retrained on all prior seasons plus every game already played that season — and nothing after. The next day, that day's results join the training set and the model retrains again.
This mirrors exactly how the model would have been used in real time, and it's why each of the ~11,000 predictions is genuinely out-of-sample. The trade-off is cost — hundreds of model fits instead of one — but it's the only way to get an accuracy number you can believe.
The honest scoreboard: the model beats "always pick the home team" and a coin flip comfortably, and lands just behind the betting market's closing line on the games where odds exist. The market is a famously efficient baseline — running step-for-step with it out-of-sample is the real result here.
≋ Scale starts at 45%, not 0 · longer is better. Accuracy is the share of games where the higher-probability side won. The market and the model are measured on the same subset of games that carry odds.
The whole system is hand-built: the data warehouse, the feature engineering, the walk-forward backtest harness, the publish step and this dashboard — the same data-modelling, pipeline and visualisation work that sits behind any production analytics system.
Built by Isaac Kaczor. The model and this dashboard are independent hobby work — not affiliated with the NCAA or any betting operator. Forecasts are for interest and discussion only.