The Closing Line: can a simple model beat Vegas?
Loading the summary numbers…
Where the data comes from and how every number is computed
What one row is
The core data set is a panel with one row per team per game: every game appears twice, once from each team's side, with the date, season, sport, team, opponent, home/away/neutral, points (or runs or goals) for and against, the result, and, where a betting market existed, the line, the total, the moneyline or 1X2 odds and the outcome against them. The time column is the date and season; the group columns are sport and team. There is one file per sport (data/nfl.csv, data/nba.csv, data/mlb.csv, data/nhl.csv, data/epl.csv) with the same 24 columns. Player data lives in separate files: per-game rows for recent seasons (data/<sport>_players.csv) and season totals for the full span (data/<sport>_player_seasons.csv).
Data sources
- NFL games 1990 to 1998, spreads and totals: the “NFL scores and betting data” archive (spreadspoke, originally on Kaggle), from the GitHub mirror github.com/RussellHerzog/nfl_game_outcomes.
- NFL games 1999 onward, spreads, totals, moneylines, and weekly player stats: nflverse, github.com/nflverse/nflverse-data/releases (schedules and stats_player). Licensed CC-BY-4.0.
- NBA games 1990-91 to 2000-01: FiveThirtyEight NBA Elo data, github.com/fivethirtyeight/data/tree/master/nba-elo. Licensed CC-BY-4.0.
- NBA games, team and player box scores 2001-02 onward, and closing lines 2022-23 onward: hoopR NBA data (ESPN), SportsDataverse, github.com/sportsdataverse/hoopR-nba-data. Licensed CC-BY-4.0.
- NBA, NHL and MLB closing odds (seasons up to 2021-22, NHL partly 2022-23): SportsbookReviewsOnline archive files, via github.com/DillonKoch/Sports_Betting (NBA), the Internet Archive copy of sportsbookreviewsonline.com (NHL) and sportsbookreviewsonline.com (MLB).
- MLB games 1990 to 2025, starting pitchers and player-game stats: Retrosheet game logs and box-score files, www.retrosheet.org. The information used here was obtained free of charge from and is copyrighted by Retrosheet. Interested parties may contact Retrosheet at www.retrosheet.org.
- MLB 2026 games and box scores: MLB Stats API, statsapi.mlb.com; player id crosswalk from the Chadwick Bureau register, github.com/chadwickbureau/register.
- NHL games, skater and goalie game logs: NHL Stats API, api.nhle.com/stats/rest/en/game.
- EPL results, 1X2, Asian handicap and over/under odds: Football-Data.co.uk, www.football-data.co.uk/englandm.php.
- EPL player goals and assists: Fantasy Premier League history compiled by vaastav, github.com/vaastav/Fantasy-Premier-League (FPL scoring rules, not official Opta records).
- Live scores and fallback odds: ESPN's public scoreboard feed (
site.api.espn.com/apis/site/v2/sports/…/scoreboard); live odds, when a key is configured, from The Odds API. Both are read with HTTP GET only. - Live player prop lines: the full-game over/under player lines in ESPN's public odds feed (
sports.core.api.espn.com/…/odds/<provider>/propBets), from the sportsbook ESPN lists, currently DraftKings. The dashboard shows them next to the model's player projections. For small count lines, such as 0.5 hits or 1.5 strikeouts, the model's chance of going over is P(X ≥ line rounded up), with X Poisson-distributed around the projection. For other stats it shows the projection minus the line. No free archive of historical prop lines exists, so these comparisons are not backtested and carry no edge flag. - 3D game replays (dashboard): ESPN's public game summary,
site.api.espn.com/apis/site/v2/sports/<sport>/<league>/summary?event=<id>. Each drawn marker sits where ESPN recorded the play: NBA shot coordinates, with both teams normalized to one basket by ESPN, so the site shows home at the right basket. It also uses NHL rink coordinates in feet, MLB Gameday spray-chart and pitch-location coordinates, NFL yard lines and EPL shot start and end positions. Plays without a recorded location go on a fixed rule spot when the rules fix it: free throws, strikeouts and walks at home plate, penalties, and kickoffs. Otherwise they go on a time-order rail along the near side, which is not a field position. Older positions in (0, 0) or an unverified 0–1 scale are treated as missing. These are reconstructions from play-by-play locations, not video or player-tracking data. - Every game since 1990 with the model's pick:
data/predictions_<sport>.csvholds the exported model's pre-game prediction for every game in the data, built with only information available before that game (Elo before the game, rest, the last ten games, starters' or goalies' earlier games, EPL attack and defense ratings). Each row is labelled: burn-in for the first two seasons, which warm up the Elo ratings and are not used for training; train for in-sample seasons, where the model's coefficients were fit, so a correct pick there is not a true test; holdout for the two out-of-sample backtest seasons; and current for the season in progress. The holdout rows in the file reproduce the backtest log loss exactly. The Games & 3D page matches each game to ESPN's public scoreboard by date and final score. Where ESPN has the play-by-play, the 3D replay is built from it; otherwise the 3D view shows only the final score on the correct surface, plus the inning-by-inning line score for MLB from Retrosheet game-log fields 20–21. - Stadiums in the 3D replays: the venue of each game comes from ESPN, and its attributes come from
data/venues.json. Capacity, playing surface, roof type, MLB outfield fence distances and EPL pitch dimensions are read from each venue's Wikipedia infobox, pinned to one revision, and MLB wall heights come from a quoted sentence per park. Values a source does not state are left empty. Fences, wall heights and pitch sizes are drawn to scale. Stands are schematic, sized to the published capacity. As a check across 120 recent MLB games, 99.2% of home runs land at or beyond the drawn wall.
Rows dropped and why
How every rate, ratio and average is computed
The report and the dashboard use exactly the same definitions:
- Win % = rows with result W ÷ all rows (ties and draws count as non-wins).
- Cover % = cover ÷ (cover + miss), with pushes and games without a line left out. A team covers when
margin + line > 0, wherelineis the team's own handicap (negative = favored). For EPL Asian handicaps, a quarter line that would pay a half-win is counted as a cover. - Over % = over ÷ (over + under), with pushes left out. EPL's total line is 2.5 goals, set only for matches where the source has over/under 2.5 odds.
- Favorite = rows with
favorite = 1: the team with the negative line, or, when there is no line, the team with the higher no-vig implied win probability. NBA seasons from 2022-23 have spreads but no moneylines, so the favorite is defined by the spread alone there. - Home win % = win % over rows with
home_away = H; neutral-site games (Super Bowls, international games, the 2020 NBA bubble) are excluded. - Average total = mean of
total, the two teams' combined score, over one row per game. The scoring index divides each season's average by the average over the sport's first ten seasons and multiplies by 100. - Days of rest = days between a team's game and its previous game in the same regular season.
- No-vig implied probability. For American odds
A, the raw probability is−A ÷ (−A + 100)when A < 0 and100 ÷ (A + 100)when A > 0. The two sides are divided by their sum so they add to 1. For EPL 1X2 decimal oddsd, the raw probability is1 ÷ d, normalised over home, draw and away. - Fair odds from a model probability p. American:
−100·p ÷ (1 − p)when p ≥ 0.5, otherwise100·(1 − p) ÷ p. Decimal (EPL):1 ÷ p. - Log loss = −mean(y·ln p + (1 − y)·ln(1 − p)) and Brier score = mean((p − y)²), where y = 1 if the team won. Lower is better for both.
- Return at −110 = (wins × 100 − losses × 110) ÷ (bets × 110), pushes returned; this is the standard price of a spread bet and needs a 52.4% win rate to break even.
Honest limits
- Betting lines before 2007-08 (NBA, NHL) and 2010 (MLB) do not exist in any free archive, so those seasons show “n/a” for line fields. MLB lines stop after 2021 and NHL lines after part of 2022-23 because no free archive covers later seasons.
- Only EPL from 2019-20 has odds explicitly recorded at kick-off. NFL lines are the sources' pre-game reference lines. The NBA, NHL and MLB archives label their lines as closing lines, but that cannot be independently verified. EPL odds before 2019-20 are pre-match snapshots.
- Player-game stats start in 1999 (NFL), 2001-02 (NBA) and 2016-17 (EPL). MLB and NHL player data cover the full span.
- The NHL treats Utah (2024-25 onward) as a new franchise, so Arizona (ARI) and Utah (UTA) appear as separate teams.
- NHL scores are as published: a shootout win counts as a one-goal win.