Methodology, data & limitations
What is forecast
For every regular-season and playoff game: expected home and away points (including overtime), the expected margin (home minus away), the expected total, win/loss/tie probabilities, and 80% and 95% ranges for the margin and total. Scores are means; a projected 23.7 is not a possible final score and does not imply tenth-of-a-point precision. Regular-season games carry a small explicit tie probability (the smoothed tie rate since the 2017 overtime change, about 0.35%); playoff games cannot tie.
Forecasts are released as immutable, timestamped files. The information cutoff is the release time. A game is scored against the last version released before kickoff (and, separately, the last version at least 72 hours before kickoff), never against a later model run.
Three model families
Market only (benchmark)
The point spread s (negative when home is favoured) and total t become M₀ = −s, T₀ = t, home = (T₀+M₀)/2, away = (T₀−M₀)/2. Only these two numbers are taken from the market; nothing else.
Football only ("stats-only")
Ridge regression on each team's offence against the opponent's defence: EPA per play, passing and rushing efficiency, success and explosive-play rates, sacks, turnovers, pace, points per drive, red-zone and field position, opponent-adjusted ratings, the expected starting QB's rating and experience, change versus the QBs behind the team's recent stats, rest, byes, venue and roof. It is also the fallback when no market line exists.
Combined (selected)
Starts from the market line and learns a regularised correction: M̂ = M₀ + g(X), T̂ = T₀ + h(X). Strong regularisation (chosen on earlier seasons) keeps it close to the line unless football information has shown value. A direct formulation and a gradient-boosting variant were tested and not selected.
Preventing look-ahead
- A completed game only counts once its data would have been available (kickoff + 12 hours) before the forecast cutoff. This applies to team form, the league averages used for shrinkage, and opponent-adjusted ratings, which are re-fitted for every information state.
- Validation is walk-forward: each season is predicted by models trained on earlier seasons only. Settings were chosen on 2019–21, the models were developed on 2022–24, and 2025 was held back and evaluated once after all choices were frozen.
- Probability calibration and interval widths are learned from earlier seasons' out-of-fold predictions only.
- Neutral-situation filters use the pre-play score and quarter, not a market-informed win probability. Spread-derived win-probability columns are removed on ingestion.
- Automated tests check leakage (adding a future game cannot change a frozen feature), spread sign conventions, M = H − A and T = H + A, non-negative scores, probabilities summing to one, zero tie probability in playoffs, and interval ordering.
Quarterback availability (live forecasts)
Every relevant quarterback is checked: the depth-chart QB1, QB2 and QB3 (from the latest daily depth-chart snapshot at or before the cutoff) and the team's most recent actual starter. Each gets a status with its evidence and observation time: listed on the team's published injury report, not on a published report, no report published yet, stale report, or a documented manual override. Missing or stale information is never treated as confirmed availability.
Freshness is checked explicitly. For injury reports, depth charts and rosters, two times are tracked: when the provider last updated the file, and when we last confirmed it. Either being more than 36 hours old (8 days for rosters) marks the source stale, and a stale source is not used as confirmation. The provider time is its most recent upload seen by our checks before the forecast, even when the content is unchanged (a Saturday report re-published on Monday is current); until 28 September 2026 only the first upload of unchanged content was used, which wrongly marked current reports as stale. Every game page shows these times and any problems.
Every link in the replacement chain is validated. Each QB in line is checked against the current roster (a QB on injured reserve, released, retired, on the practice squad or declared inactive cannot start), his injury designation, and any override. If every listed QB has some chance of being out, the remaining probability goes to the last usable QB and is flagged as an exhausted chain.
Probabilities are start probabilities, estimated from history and never assumed, never equal to the chance of playing. An audit of the 193 Questionable and 31 Doubtful depth-chart QB1s (2016–2024) found 112 Questionable QB1s started, 6 played without starting, 53 were declared inactive and 22 dressed but did not play; no Doubtful QB1 started or played. The final practice level matters: Questionable with full practice started 36/41, limited 68/131, no practice 8/21. Rates use a Jeffreys prior, cells by practice level are shrunk toward the status rate (strength chosen on earlier seasons only), every rate carries a 90% interval, and the estimator was selected by testing each season on earlier seasons. Daily depth charts (2025) are used where a category has at least 30 cases; otherwise weekly charts (2016–2024).
Before designations are published (early in the week), a listed QB's practice line is not the same measurement as the final report and there is no intra-week history, so a pooled rate for QB1s on final reports is used and labelled "designation pending", with the final-report rate for the same practice level shown as the low end of the plausible range. Intra-week report versions are now archived so this can be calibrated later.
When the starter is uncertain, the forecast is a probability-weighted mix of QB scenarios, and "lineup uncertain" is shown separately from the win probability. Documented overrides need a public source, its publication time and an expiry time; they apply only between the two.
Historical approximations in the backtest
- Actual-starter proxy. For the final-pregame horizon, historical backtests use the QB who actually started as the "expected" starter. Starters are usually known from inactive lists about 90 minutes before kickoff, but this is an approximation that knows the answer in the rare late switch. Live forecasts use the evidence-based availability model above instead, so live accuracy may be slightly worse. The early (72-hour) horizon uses the weekly depth chart or the previous starter.
- Untimed closing lines. Historical spreads and totals are a single untimed line per game, roughly the closing line. Market-based models are therefore validated only at the final-pregame horizon, and they may benefit from information that arrived close to kickoff. Live forecasts use the line observed at release time.
- Non-QB injuries are display-only. A non-QB availability feature was built and tested; it added nothing once the market line was included, so no current model uses it. Game pages from week 3 on show, for context only, the injury report for both teams as carried by the data feed (practice participation, and game status once it reaches the feed) as of the newest retrieval before kickoff, with the time the data last changed and a link to the official report on NFL.com, which can be a day or more ahead of the feed.
- Not modelled: weather (archived operational forecasts exist only from about April 2026) and coaching or play-caller changes (no free source with dates).
Releases, versions and publication
- Weekly publication. Each week's forecasts and model picks are first published on the Tuesday at 18:00 UK time before its first game, not as soon as the previous week ends; the model pick locks at that first publication. The first publication also waits, for at most 24 hours, until every finished game of the previous week (including Monday night) is in the team data, so a pick never locks without it. If the pipeline is not running at 18:00, the week is published at its next run before kickoff; nothing is back-dated. History: weeks 1–4 published when the previous week ended; from 5 October 2026 the time was Thursday 09:00 UK; on 6 October 2026 it moved to Wednesday 18:00 UK because lines had moved a lot by Thursday, and week 5 was published at once (Tuesday 6 October, evening); on 13 October 2026 it moved to Tuesday 18:00 UK, from week 6.
- The pipeline checks every 30 minutes. A new release is made when any game's inputs change (market spread or total, quarterback availability, the injury report, team form from newly completed games, or the model version), in the hour before each kickoff, and at least daily. Every version is kept; no game is updated after kickoff.
- Each forecast is validated before it can be shown or scored: non-negative scores, margin = home − away, total = home + away, probabilities in [0, 1] summing to 1, no playoff ties, ordered and nested intervals containing the forecast. If the combined forecast fails, a validated football-only forecast is used and labelled; if nothing passes, the previous valid version stays current.
- States: latest pregame (may still update), locked at kickoff, and scored (the locked version against the result).
- Three different times: the information cutoff and generation time (recorded by the pipeline) and the publication time, which is taken only from independent evidence: GitHub's own record of when the file was first pushed. A forecast counts as publicly verifiable pregame only if that evidence predates kickoff. Errors in earlier claims are recorded in an append-only corrections log; original release files are never edited. For example, the first Falcons–Packers forecasts were generated before kickoff but first made public 16 minutes after it.
- Durable archive outside GitHub Actions. Every forecast file gets a write-once manifest (SHA-256 hash, information cutoff, generation time, model version, input snapshots) and an append-only evidence log: an RFC 3161 trusted timestamp from FreeTSA over the hash (proves the exact file existed; only the hash is sent), a copy of GitHub's push record (preserved because Actions records expire), and an Internet Archive capture of the public, commit-pinned file whose bytes are checked against the hash. Evidence is recorded when obtained and never back-dated. An integrity check runs before every cycle and stops publishing if any earlier forecast file has changed.
Candidate features: collection and evaluation
- Weather: a forecast snapshot for the kickoff hour of every upcoming game is archived with the time it was observed (Open-Meteo, city-level venue coordinates, roof exposure). Evaluated separately on a retrospective 2019–2024 history (stitched short-lead forecasts, not what was knowable days ahead): small, statistically unclear total improvements; not promoted. Shown on game pages for context only.
- Non-QB injuries: every injury-report version is now archived with first-observed times, building the as-of history that did not exist before. Evaluated separately at the final horizon (final weekly reports): no gain for the combined model; not promoted; display only.
- A group is promoted only if, fixed in advance, the combined model improves in the development seasons with a 95% interval below zero and also improves in the tuning seasons. Coaching adjustments are deferred.
Model picks and weekly results
Picks are labels derived from one forecast version and the market line recorded in that same version; the model is unchanged. Model pick (e.g. PIT +3.5): the side of the market line on which the model's unrounded projected margin falls. It is locked 24 hours before kickoff, at the latest forecast with a market line at that moment, and graded against that line; until then it is shown as provisional and follows the latest forecast and line. Once locked, if the line or the forecast moves, the pick does not change (for example, the line is BUF −7.5 with BUF projected to win by 6.4: the pick is the opponent +7.5, and it stays +7.5 even if the line later moves to BUF −6.0). The game card shows the line and the model's projected margin at the moment the pick was locked and, once either has changed, the latest line and model ("Then" and "Now"), so the pick can always be checked against the numbers it was made from. Model line: the combined model's latest projected margin written as a line; it starts from the market line and adjusts it using the football data, so by design it stays close to the market. Stats-only line: the football-only model, which uses no market input. Projected winner: the team with the higher win probability in the latest forecast (shown as a forecast, not tracked as a record); the margin error uses the last valid version generated before kickoff, as scoring does. The win probability belongs to the projected winner only; it is not a probability of finishing on one side of the line. The size of the difference (in points; under 0.5 labelled tiny) is on each game's Details page; a difference that rounds to 0.00 points (below 0.005) means "No pick". For analysis and education only. Only results against the market line are tracked as a record; results exactly level with the line, no-pick and no-line games are counted separately. Games whose forecast was generated before but first published after kickoff are reported separately. Forecasts made before pick labels were added (25 September 2026) have their labels derived afterwards by the same fixed rule and are marked as retrospectively derived. Weeks show "Week to date" until every game is final.
Rule change, 19 October 2026 (decided after week 6, before any week 7 forecast): the pick locks 24 hours before each game's kickoff instead of at the first forecast with a market line, for games kicking off from 21 October 2026 (week 7 onward). Forecasts are still first published on Tuesday at 18:00 UK; the later lock lets injury news and the final injury reports into the pick (in weeks 4–6 the line moved on average 0.77 points between Tuesday evening and kickoff, and 0.21 points after Saturday evening). It does not change the model. Weeks 4–6 keep the rule in force when they were played (locked at the first forecast with a market line) and are not regraded. If no forecast with a market line exists 24 hours before kickoff (for example, if publication was delayed), the pick locks at the first forecast with a market line after that.
Rule change, 1 October 2026 (decided before any week 4 game): the locked pick applies to games kicking off from 1 October 2026 (week 4 onward). Earlier games keep the rule in force when they were played: the model pick from the last valid forecast before kickoff, graded against that forecast's line. Finished weeks are not regraded; week 3 stays 8–6 with 1 level result among publicly verifiable forecasts (it would have been 9–6 under the new rule).
Display additions, 6 October 2026 (no forecast, pick or grading change): the pick box shows the size of the edge (tiny under 0.5 points, about a coin flip; small 0.5–1.5; moderate 1.5–3; large 3 or more) and the stats-onlyline, which is the football-only model with no market input, at the lock and now. Big gap marks a game where the stats-only margin was at least 3 points from the line when the pick was locked. In the backtests the result finished on the stats-only side of the line in 52.2% (2019–21), 57.5% (2022–24) and 51.5% (2025) of such games: not a reliable edge, and the 2026 sample is small. It is a prompt to check why the market disagrees, not a recommendation. From 6 October 2026 releases also store how each group of inputs adds up to the stats-only margin (Details page); earlier releases did not, so older games do not show it.
Stats-only tracking record, 9 October 2026 (display only; not the official model pick and not part of the model record): for each publicly verifiable locked pick, the side of the locked line and total on which that same version's stats-only margin and total fell, graded against that line and total; "Big gaps" counts only games where the stats-only margin was at least 3 points from the line. It starts with week 3, the first week with locked picks.
Display changes, 18 October 2026 (no forecast, pick or grading change): the site now tracks results against the market line only. The projected-winner record and winner-accuracy columns were removed, as was the stats-only totals record; the home page shows the model pick record for the season and for the week on screen. Biggest gap marks a game where the stats-only margin was at least 4 points from the locked line (about 1 game in 10 in the backtests), with its own tracking record. Larger gaps were not more accurate in the backtests: the result finished on the stats-only side in 49.1% (2019–21), 53.7% (2022–24) and 47.1% (2025) of such games. Like Big gap, it is a prompt to check why the market disagrees, not a recommendation.
Wording, 9 and 13 October 2026: betting terms were replaced with neutral forecasting terms, since the site is a forecasting and analytics project. The model line is built from the market line, so it is not an independent opinion; the stats-only line is the independent one. The numbers and rules did not change.
Data sources and what is missing
- nflverse (CC-BY 4.0): play-by-play with EPA/CPOE, schedules, scores, depth charts, injury reports, snap counts, rosters. Advanced FTN charting (CC-BY-SA 4.0, "FTN Data via nflverse") exists from 2022 but is not used yet.
- No free timestamped history of market lines. Historical lines are one untimed value per game (about the closing line), so market-based models are validated only at the final-pregame horizon. From September 2026 this project archives its own line snapshots, which will allow early-horizon evaluation over time.
- Live market lines (feed change 25 September 2026): from release rel_20260925T132351Z onward, the spread and total come from The Odds API (free plan, US market lines): the median point spread for the home team and the median total across sources whose lines were updated before kickoff, rounded to the half point, cross-checked against the nflverse line. Only point values are kept; all prices are discarded before saving. When no valid API line newer than 36 hours exists before the forecast cutoff, the nflverse schedule line is used and the game page says so. Earlier forecasts keep the nflverse lines they were made with. The model's weights, features and calibration did not change. It was trained on nflverse lines; in the first comparison (29 games, 25 September 2026) the API consensus spread and total were each within 1 point of the nflverse line (mean differences 0.17 and 0.29 points).
- Injury reports: only the final weekly report survives historically, so early-week injury information cannot be backtested.
- Weather: archived operational forecasts exist only from about April 2026, so weather is not in the historical model.
- Coordinators and play-callers: not available in any free feed; not modelled.
- nflverse republishes EPA with model updates, so historical backtests are close, but not exact, reconstructions of what was knowable at the time.
Limitations
- An NFL game is noisy: even the best forecasts miss the margin by about 10 points on average, and the 80% range spans about 32 points.
- Seven seasons of backtests is limited evidence; paired comparisons use week-block bootstrap intervals and many are indistinguishable from zero.
- The early-horizon combined forecast applies a model validated at the final horizon to the line observed at release time; its accuracy is being measured live.
- Model "contributions" describe the fitted model, not causes on the field.