—
— —
| MATCHUP away @ HOME | FINAL | LINE | VS LINE | MODEL'S SIDE | BET |
|---|
LINE is the number the game was graded against — the consensus at pick time, shown for the home team. VS LINE is the home team's final margin minus that line: positive means the home side covered, and its size is by how much. Every W and L in the two right-hand columns follows from that one number, so you can check any of them yourself.
| MATCHUP away @ HOME | MARKET LINE | MODEL'S LINE | GAP pts | MODEL'S SIDE | BET | SIGNALS |
|---|
Both lines are for the home team — listed second, in bold — written the way
a sportsbook would: SEA −3.5 means Seattle is favoured
by 3.5, SEA +2.5 means Seattle is getting 2.5.
MODEL'S LINE is where the model thinks the line should be, so
SEA −3.5 against a model line of
SEA −5.7 means the model rates Seattle 2.2 points
higher than the market — a GAP of 2.2, on Seattle. The model bets only
when that gap clears the week's threshold.
Gap colour: ≤3 ·
3–5 ·
5+. A green gap is a big disagreement, not
automatically a bet — the threshold is 5.0 in week 1 but 6.0 from week 5, and
a bet is marked by the highlighted row.
A sportsbook is trying to be right. This is trying to be different.
Vegas prices in everything — quarterbacks, injuries, weather, travel, and what the public is going to bet. This model's rating deliberately ignores all of it. It knows only who won, by how much, and against whom, going back to 1999. Nothing else.
One exception, added in 2026 week 3: who is actually playing quarterback, and whether a team's top quarterback or receiver is ruled out. A rating built from scores cannot see a missing starter, so on those games its disagreement with the market was the blind spot talking, not a mispricing. Those corrections are sized by what the market itself charges, never by what won — see Who is playing below.
Then it compares its number to theirs. On most games they are close and it does not bet. A few times a year they are five or six points apart — and that gap is the whole product. It means the line moved for a reason that is not football.
What it does differently, in plain terms
- Only results count. No player stats, no weather, no narrative. Who beat whom, by how much.
- Except the quarterback. The number is corrected for the passer actually starting, and for a ruled-out starting QB or WR1 — by amounts measured against the market, not picked by hand. A team whose starting QB is out is never bet.
- It changes its mind slowly. One game can only move a team so far, so a single blowout does not fool it.
- Blowouts count more, but not proportionally. Winning by 30 says more than winning by 3 — and beating a bad team by 30 says less than beating a good one by 30.
- Home field is a flat 2.6 points for every team. No stadium-by-stadium guessing.
- Every team resets a third of the way toward average each offseason. Last year matters, but not fully.
- It only bets a 5–6+ point disagreement — it passes on most of the slate.
- It refuses to bet weeks 2–4 entirely, because after one or two games the ratings are unreliable.
- Every bet is the same size. It never chases a loss or presses a hot week.
Injuries: two quarterback-shaped exceptions, and playing time for the rest
A sportsbook converts an injury into points — "that quarterback is worth seven". This model refuses to pick those numbers. Assigning a hand-picked value to a missing player invents a figure nobody can validate, and that is how a model quietly becomes an opinion with arithmetic attached.
It does take points off for exactly two absences — a ruled-out starting quarterback (2 points) and a ruled-out top receiver (1.5) — because those two are measured: they are what the market demonstrably charges, read off the gap between the model's line and the market's. That is a bias being removed, not an opinion being added. Every other position was tested the same way and none cleared the bar (below).
For everything else it measures how much of a team's actual playing time is missing or compromised. Every player on the injury report is weighted by the share of snaps he had already been taking, so losing an every-down starter counts for far more than losing a rotational player — and a fourth-stringer on the report counts for almost nothing.
| Status | Counts as |
|---|---|
| Out or Doubtful | 100% of his snaps |
| Questionable, did not practise | 40% |
| Questionable, limited | 20% |
| Questionable, practised fully | 5% |
It reads practice participation, which is published before kickoff — never whether the player actually took a snap, which is only knowable afterwards. A model built on that column would backtest beautifully and be impossible to run on a Sunday morning.
And it still does not bet it. Backing the healthier side is the strongest thing found here — 55.49%, z=+2.41 over 483 games — and it is recorded on every card and staked on nothing. The injury data only begins in 2023, about 850 games. That is a hint, not a finding, and the difference matters.
Byes: the opposite of what everyone assumes
The common belief is that a rested team coming off a bye has an edge. It does not. Across 718 games, backing the team coming off a bye returns 50.56% — a coin flip, and well below the 52.38% needed to break even. That angle is fully priced and probably always was.
The signal is on the other side of the bye. Backing the team playing the week before its bye returns 53.40% over 854 games — teams appear to empty the tank when a week off is waiting.
| Era | Before the bye | n |
|---|---|---|
| 1999–2007 | 54.98% | 291 |
| 2008–2016 | 49.12% | 283 |
| 2017–2026 | 56.07% | 280 |
Which is why it is recorded and not bet. A belief that works, dies for nine years, then works again is not one to stake money on. It shows on the card as pre-bye:TEAM so it keeps earning or losing the argument in public.
The part that actually matters
Anyone can build a rating. Everything this one believes is written down, and every pick is timestamped three hours before kickoff. 176 tests have been run against 7,000 games — weather, quarterback angles, pass rush, prime time, takeaways, body clock, all thrown out as things to bet. A few survived and none is bet on its own.
One change was made mid-season, and it is dated: the quarterback correction shipped during 2026 week 3. Weeks 2–4 place no bets and the week-1 pick was withdrawn, so the model's bet record has no picks from before it — every graded bet on this site was made by the corrected model.
The record on this site fills itself in whether it flatters the model or not.
The thing it is not
The predecessor was a 14-tab spreadsheet whose "ELO" column was a transform of Super Bowl futures prices — the betting market's own opinion, fed into a model whose entire purpose is to disagree with the betting market. It cannot systematically disagree with the thing it is derived from. It went 122–115 (51.48%) against a 52.38% break-even.
But 237 picks carries a 95% interval of roughly 45%–58%, which contains both "no edge" and "very profitable". A season of hand-tracked results cannot tell you which you have. That is why this model tests against 6,967 games before betting, and why this site refuses to print a verdict on its own record until 150 graded bets.
The signal — Elo
What goes into it — the whole list
The rating reads five fields per game, and nothing else exists to it:
season home_team away_team home_score away_score
Which is three things: who played, the final score, and which season (so it knows when to regress toward the mean). There is no player data of any kind — no quarterback, no injuries, no yards, no efficiency, no weather, no rest, no travel, no personnel. The rating cannot tell you who took a single snap. A team is a number that rises when it wins and rises further when it wins big against someone good.
That list describes the rating. The spread published on the card is the rating plus the quarterback correction described under Who is playing — the one place player data enters, and only to stop the model disagreeing with the market about a roster it cannot see.
That blindness is the strategy, not a limitation. The market has already priced the quarterback, the injuries, the weather and the travel — all of it is inside spread_line before the number is posted. A model looking at the same inputs would land on the same number and have no edge by construction. That is exactly how the predecessor failed: its "ELO" was derived from Super Bowl futures prices, so it was the market's own opinion in disguise. This one looks at nothing but results, and a bet is placed only when that deliberately ignorant number lands far from the fully-informed one. The disagreement is the product.
Ratings are built by walking every game in order, rating each team before the game is played, so the rating is point-in-time by construction rather than by discipline. The model's spread is the rating gap converted to points, then corrected for who is playing (next section), in this order:
elo_spread = ((elo_home + 65) − elo_away) / 25
model_spread = elo_spread
− missing starters (QB1 out: 2.0, WR1 out: 1.5, per team)
+ passer value (home − away, 12 pts per EPA/dropback)
edge = model_spread − market_spread
The slate is priced off ratings that have absorbed every finished game. Until 27 September it was priced one result stale — each team's rating from before its latest game — which moved 11 of 16 week-3 spreads by half a point or more. Weeks 2–4 place no bets, so it cost no picks. The backtest still rates every game strictly before it is played.
A note on signs. Internally both spreads are from the
home team's view, where a positive number means the home side is
favoured — nflverse's convention, since home covers when
result > spread_line. Every betting screen writes that same
favourite with a minus. The slate tables convert to the sportsbook form on
display only; the model's own numbers, and picks.csv, keep the
internal sign.
| Parameter | Value | Meaning |
|---|---|---|
| K | 20 | How far a rating moves per result |
| HFA_ELO | 65 | Home advantage, ≈2.6 points |
| ELO_PER_POINT | 25 | Rating points per point of spread |
| MEAN | 1505 | Where every team starts |
| CARRYOVER | 2/3 | Regress ⅓ toward the mean each offseason |
Margin of victory is dampened, and discounted when the favourite wins, using the standard autocorrelation-correcting form:
mov_multiplier = (|margin| + 3)^0.8 / (7.5 + 0.006 × elo_diff)
These are not tuned. They are the conventional public
values. Fitting them to maximise cover rate on the same games the model is then
tested against would guarantee a good-looking number and a worthless one. A
tune() function exists in elo.py and is deliberately
never called.
Who is playing — the quarterback correction
The Elo rates a jersey. When the man under centre changes, the rating does not, and the model publishes a spread for a roster that is not taking the field. 2026 week 3 had three at once, pointing different ways: Miami rated as Tua-era Miami with Malik Willis starting, Atlanta rated without the Tua who had just arrived, Seattle rated on Drew Lock's two weeks with Sam Darnold back. That is not a mispricing to bet. It is the model being wrong about who is playing.
How much the market charges for a missing starter
Measured as the gap between the model's line and the market's, 2024–2026, for a team whose snap-share leader at a position is ruled Out, Doubtful or on reserve:
| Position | n | Model − market | z | Applied |
|---|---|---|---|---|
| Nobody missing | 988 | +0.00 | — | baseline |
| QB | 78 | +4.10 | +8.8 | yes |
| WR | 62 | +1.85 | +3.6 | yes |
| LB | 51 | +1.52 | +2.4 | no |
| G / C | 24 | +1.71 | +1.7 | no |
| TE | 71 | +0.94 | +1.8 | no |
| Tackle | 30 | +0.77 | +0.9 | no |
| RB / CB / S | — | +0.3 to +0.6 | ≤1.3 | no |
| Edge rusher | 13 | — | — | cannot measure |
The baseline of exactly zero is the important row. In ordinary games the Elo is unbiased against the market, so the four points on a missing quarterback is the quarterback, not a general lean. Ten groups were tested, so the bar is not two sigma but about 2.8 — QB and WR clear it, nothing else does. Left tackle and the star pass rusher are not applied because the data does not support them; a pass rusher plays through injuries and is almost never ruled out.
Calibration, not a fitted edge. These constants come from what the market prices, never from who covered. Fitting a number to the 22 bets the model made on such games would be exactly the fudge factor this page refuses everywhere else. Measuring the mean gap across 78 games against the market removes a bias that is demonstrably there.
Pricing the passer
A flat constant cannot fix a quarterback change — "a different quarterback" has no sign; the new one may be better. EPA per dropback does have a sign. Regressing the model-minus-market gap on the starter's EPA per dropback, minus that of the passers who earned the rating:
| Era | n | Pts per EPA/db | z |
|---|---|---|---|
| Fit, 2016–2021 | 2,908 | −13.79 | −20.7 |
| Test, 2022–2026 | 2,240 | −13.66 | −18.1 |
Near-identical on a test half the fit never saw. It ships at 12 points per EPA/dropback, rounded toward zero, capped at 6 points either way as a guard rail. A passer with under 150 dropbacks is treated as unknown, not bad, and priced at −0.13, the fit-era median for such passers.
Passer pricing halved the missing-starter numbers rather than replacing them: re-measured with it applied, a ruled-out QB1 still costs +1.94 and a WR1 +1.26. So the flat amounts shipped at 2 points for the QB and 1.5 for the WR, not 4 and 2. Passer value knows what each quarterback is worth; it does not know one of them is ruled out on Sunday. Both are needed.
Picking the expected starter was most of the work. It is the quarterback with the most snaps for this team over last season and this, among those on the current active roster and not ruled out. Each simpler rule shipped and was wrong on a real board: this season's snaps alone named Drew Lock on the day Darnold returned; career snaps named a little-used backup Miami's starter; no roster check named Tua the starter for a team he had left.
And no bet on a team whose quarterback is out
If the side the model would bet has its starting quarterback ruled out, it does not bet — the card reads no-bet:qb1-out:TEAM. On 2023–2025 picks of 6+ points, 22 of 88 backed a team whose QB1 was out, going 9–13 against 29–36 elsewhere. That is too few games to size anything, so the rule refuses rather than adjusts: refusing needs no fitted number. The opponent's quarterback being out does not block a bet — there the market has moved toward the model.
Backing the healthy side is not an edge in its own right: 52.86% (37–33–3, z=+0.48). The market prices a missing starter about right, which is exactly why the model should not pretend to know better.
"Out" means ruled Out or Doubtful on the injury report, or on reserve. A player on IR drops off the weekly practice report, so the report alone misses the season-ending injuries — 51% of QB1 absences in 2024. Gameday inactives are ignored: they publish about 90 minutes before kickoff, after the pick has frozen.
Every passer adjustment of half a point or more shows on the card as qbv:TEAM±n, and a missing starter as qb1-out:TEAM or wr1-out:TEAM, so no line moves without a trace. If passer pricing fails on a run, every row says NO-QB-PRICING rather than quietly publishing unadjusted numbers.
What this has not been tested against. Every ladder, robustness and bucket figure further down this page was measured on the unadjusted Elo spread. Nothing has been re-swept on the corrected one, and with injury data from 2023 and reserve lists from 2024 it cannot yet be. The correction removes a bias against the market; whether it makes the bets win more is a question only the live record can answer.
Tried and not shipped: a quarterback-aware Elo
The obvious fix is to rate the team and the quarterback separately, so a rating earned by one starter does not transfer whole to the next. Starters are known for every game since 1999, so this one could be held to a proper standard: fitted on 1999–2012, tested once on 2013–2026.
| Plain | QB-aware | |
|---|---|---|
| Bias when the starter changed (n=831) | +2.07 | +1.69 |
| Weeks 5+, edge ≥ 5 | 51.87% | 53.30% |
| Weeks 5+, edge ≥ 6 — the bet threshold | 52.77% | 50.18% |
| Weeks 5+, edge ≥ 7 | 53.76% | 47.33% |
It removed only 18% of the bias it was built for and is worse at the threshold the model actually bets. It wins only at 5 — and choosing 5 because that is where the new version looks good is the multiple-testing mistake this page warns about. It is kept as a negative result, and a test guarantees nothing uses it.
When it bets — the week ladder
A flat "no bets before week 5" rule was replaced once the threshold was swept separately per band. Cover rate by week band and disagreement size:
| Week | edge ≥3 | ≥4 | ≥5 | ≥6 |
|---|---|---|---|---|
| Week 1 | 55.40% | 54.32% | 62.75% | 76.92% |
| Weeks 2–4 | 51.02% | 51.75% | 47.06% | 47.42% |
| Weeks 5+ | 51.14% | 52.36% | 52.20% | 52.62% |
n = 139/81/51/26, 441/286/170/97, 1891/1251/751/420 respectively. Seasons 2001+ only: 1999–2000 are spent warming the ratings and are excluded, the same basis elo.py uses for its own reporting.
| Band | Threshold | Stake | Why |
|---|---|---|---|
| Week 1 | ≥ 5.0 pts | 0.5u | 62.75% over n=51 |
| Weeks 2–4 | no bets | — | 47.06% at ≥5 — Elo is overreacting |
| Weeks 5+ | ≥ 6.0 pts | 1.0u | 52.62% at ≥6 over n=420 |
Why week 1, then nothing until week 5. In week 1 every rating is pure prior — last season regressed toward the mean, with no current-season contamination. By week 2 the model has swallowed one result at K=20 and is overreacting to it, which is worse than either the clean prior or a real sample. Hence the U: good, bad, good. That is a mechanism, not a curve fit, which is the only reason weeks 2–4 are dark rather than merely unprofitable.
Week 1 rides on n=51 with an interval of roughly ±13.7 whose lower bound sits at break-even, out of a ledger past 140 tests — so it stakes half a unit. Nothing scales with recent results: an 8-game hot streak is noise.
When the model's view is final
Three hours before kickoff, and the same for every game. Until then a row refreshes on every run, so it holds the model's latest reading rather than whatever it thought the first time the game appeared in the feed. At the three-hour mark it freezes permanently — side, number, price and stake — and is graded at exactly those terms.
The deadline is per-game rather than a clock time because a fixed daily run meant something different depending on the slot: an 8am reading is five hours old for a 1pm kickoff and twelve for an 8:20pm one. Three hours out is also the honest deadline for a bettor — that number was still available to take.
Nothing can be changed afterwards. A pick cannot be added, removed or resized once a row is frozen, and an unreadable kickoff timestamp counts as frozen: if it cannot be proved that a game has not reached its deadline, the row is left alone. That property is the only reason this record is worth reading.
The human override
An override says only "the model is blind here", and the response to a known blind spot is to not bet. It is deliberately not a point adjustment: nudging a rating by a hand-picked number invents a figure nobody can validate, and fudge factors are how a model quietly becomes an opinion with arithmetic attached.
No overrides in weeks 1–6, ever. The early-season edge exists because the market overprices offseason change — a new quarterback, a new coach. An override for exactly that change would delete the mispricing week 1 is built to harvest. Week 1 already demands a 5-point gap and stakes half a unit; that is the discount for not knowing, and taking it twice is just not betting. Overrides are allowed from week 7.
Currently active: none.
How it was backtested
6,967 regular-season games, 1999–2026. Elo ratings are graded from 2001; 1999–2000 are spent warming them up, because every team starts at 1505 and the numbers mean nothing until results have moved them. Ratings are built by walking the seasons in order and rating each game before it is played, so the backtest is point-in-time by construction rather than by care.
Look-ahead is the failure this is built around. On the
first night of testing, "teams starting a backup QB" appeared to hand their
opponent a 56.27% cover rate at z=+5.86 — a monstrous edge. It was entirely
an artifact: "usual QB" had been computed across the whole season, so a week-3 game
was labelled using week-17 information. Point-in-time, the same test gives 52.52%
at z=+1.23 — not actionable. The result looked spectacular and the bug was
invisible in the output, so the defence cannot be "remember to be careful": tests
receive history through a prior_only() boundary that hands them only
the games which had finished before kickoff. Reaching into the full table is not
something a test can do by accident.
Multiple testing is the second one. Run twenty tests and one clears two sigma by luck. Testing thirty home stadiums produced exactly one team past two sigma when chance predicts about 1.5 — less than noise, though "GB covers 56.34% at Lambeau" reads like a finding. So every run appends to a ledger and the output prints the running count: a z-score is always read against how many chances it had. 176 tests are on that ledger, which is why a handful of 2-sigma hits is unremarkable — and eleven is less than it sounds, because six of them are slices of one body-clock idea rather than six separate findings.
| Verdict | Tests |
|---|---|
| No edge | 114 |
| Beats 52.38%, under 2σ | 41 |
| Beats 52.38% by 2σ+ | 11 |
| 2σ in the wrong direction | 4 |
| Too little data to say | 6 |
Under 30 games a test reports its numbers and is explicitly refused a verdict. Significance and profitability are also scored separately: beating 52.38% is a claim about money, clearing two sigma is a claim about noise, and a belief has to do both before it is interesting.
Nothing is fitted. K, home advantage and the points-per-rating scale are
the conventional public values, never tuned against the games they are then tested
on — a tune() function exists in elo.py and is
deliberately never called. Where a candidate survived, it was then cut by era
rather than trusted whole: week 1's band holds at 69.6% / 60.0% / 63.2% across
1999–2007, 2008–2016 and 2017–2025 — three eras all above break-even, none
carrying it alone.
Conventions, verified against all 6,967 rows rather than assumed: result = home_score − away_score; spread_line is the home spread and home covers when result exceeds it; a push is excluded from n and never counted as a loss.
What a backtest cannot do. It cannot produce evidence about picks chosen before they were observed — every result above was found by looking at games that had already been played. That is precisely what the live record on this site is for, and why it accumulates slowly and refuses to render a verdict under 150 graded bets. The backtest decided what to test; only the forward record can decide whether it works.
The parameters, and how each was arrived at
| Parameter | Value | How it was chosen |
|---|---|---|
| K | 20 | Not tested — conventional |
| HFA_ELO | 65 | Not tested — conventional |
| ELO_PER_POINT | 25 | Not tested — conventional |
| MEAN | 1505 | Not tested — conventional |
| CARRYOVER | 2/3 | Not tested — conventional |
| edge ≥ 5.0 | week 1 | Swept; 62.75% over n=51 |
| edge ≥ 6.0 | weeks 5+ | Swept; 52.62% over n=420 |
| BURDEN_GAP | 0.5 | Swept; 55.49%, z=+2.41, n=483 |
| QB_OUT_POINTS | 2.0 | Measured against the market: +1.94 residual once passer value is applied |
| WR1_OUT_POINTS | 1.5 | Measured against the market: +1.26 residual |
| SLOPE | −12 | Regression vs market: −13.79 fit, −13.66 test, rounded toward zero |
| REPLACEMENT_EPA | −0.13 | Fit-era median for passers under 150 dropbacks |
| MAX_ADJUST | 6.0 | Guard rail, not fitted |
| LOCK_LEAD | 3h | Operational choice, not fitted |
| BREAK_EVEN | 52.38% | Arithmetic of −110 juice |
The five Elo constants were never tested, and that is deliberate. They are the standard public values. Fitting them to maximise cover rate on the same games they are then evaluated against would manufacture an edge that does not survive contact — so what has been measured is the output of those constants, never the constants themselves.
Does the result depend on them?
Varying each constant one at a time — not to choose better ones, which would be fitting on the test set, but to see whether the edge is a property of the model or of the numbers picked by convention:
| Setting | Weeks 5+, ≥6 | Week 1, ≥5 |
|---|---|---|
| As shipped | 52.62% n=420 | 62.75% n=51 |
| K = 10 / 32 | 53.31% / 49.10% | 59.72% / 61.70% |
| HFA = 40 / 80 | 53.74% / 52.64% | 64.44% / 58.06% |
| pts/point = 20 / 30 | 51.00% / 53.85% | 51.79% / 64.81% |
| carryover = 0.5 / 1.0 | 53.22% / 54.43% | 58.06% / 57.69% |
The weeks-5+ band is thin. As shipped it clears the 52.38% break-even by 0.24 points. Change K to 32, or the points-per-rating scale to 20, and it falls clearly below. That edge is not a robust property of the model — it is partly a property of constants chosen by convention rather than by evidence. Week 1 holds up better across most variations, but rests on 51 games.
Caveat: changing a constant also changes which games clear the threshold, so n moves and these are not controlled comparisons. It indicates robustness; it does not measure it.
A correction
These figures were originally computed over 1999–2026. But elo.py discards 1999–2000 from its own reporting, because every team starts at 1505 and the ratings mean nothing until results have moved them — so the ladder was partly justified on games rated by an un-warmed model. Recomputed on the basis the model itself uses, every figure is weaker:
| Claim | As stated | Corrected |
|---|---|---|
| Week 1, ≥5 | 64.52% n=62, z=+2.29 | 62.75% n=51, z=+1.82 |
| Week 1, ≥6 | 75.76% n=33, z=+2.96 | 76.92% n=26, z=+2.75 |
| Weeks 5+, ≥6 | 53.54% n=452 | 52.62% n=420 |
Week 1 at ≥5 no longer clears two sigma on the corrected basis. It is still the band the model bets, at half a unit, because 62.75% over 51 games with the ≥6 subset at 76.92% is the best evidence available — but it is one belief weaker than this page previously claimed, and the interval's lower bound now sits at break-even rather than above it.
What gets weighed, and what was thrown out
176 tests have been run and logged. 114 showed no edge. 41 beat break-even without clearing two sigma. 6 had too little data to say. Eleven were logged as clearing break-even by more than two sigma; grouped into the ideas they actually test, and with one that no longer clears once the warm-up seasons are excluded:
| Belief | Cover | z | n |
|---|---|---|---|
| Model disagrees by ≥6, week 1 | 76.92% | +2.75 | 26 |
| Model disagrees by ≥5, week 1 — no longer 2σ | 62.75% | +1.82 | 51 |
| Injury burden gap ≥0.5 — back the healthier team | 55.49% | +2.41 | 483 |
| Before a bye, 2017–2026 | 56.07% | +2.03 | 280 |
| Before a bye, 2013–2026 | 55.04% | +2.03 | 407 |
| Prime time — back the team whose body clock is earlier | 56.71% | +2.04 | 231 |
| Prime time — back the Pacific team | 60.23% | +2.71 | 176 |
Body clock, tested in September and not added. A West Coast team playing a 1pm Eastern kickoff is playing at 10am by its own clock. That angle was real in 2000–2008 (60.44%) and has since been priced out — 50.53%, then 45.45% — the kind of steady decay that suggests teams solved it. The prime-time half is the first thing on the ledger to hold up in all three eras (57.1 / 57.7 / 55.9%), and it is still not bet: its 95% interval bottoms out at 50.26%, below break-even, and at about 14 qualifying games a season the live record would take decades to confirm it. The other four body-clock rows past 2σ are era and venue slices of the same belief — two of them the early-kickoff window back in 2000–2008.
The main Elo signal itself sits at 52.85% (z=+1.33, n=543) for disagreements of 6 or more across all weeks — above the 52.38% break-even, but by less than half a point.
A useful confirmation: backing the more injured team returns 44.51% at z=−2.41 over the same 483 games. A real signal has a mirror image; noise does not.
Why the stake never scales with the gap
The sweep table above is cumulative, which makes the edge look like it grows with the size of the disagreement. Split into separate buckets, weeks 5+, it does not:
| Gap | n | Cover |
|---|---|---|
| 3–4 | 640 | 48.75% |
| 4–5 | 500 | 52.60% |
| 5–6 | 331 | 51.66% |
| 6–8 | 279 | 52.69% |
| 8+ | 141 | 52.48% |
Flat above 4. The apparent rise in the cumulative table is the 3–4 bucket being bad and dragging the low thresholds down, not big gaps being good. Sizing up on a larger disagreement would stake more money at an identical hit rate — more variance, no more expectation.
The same split is worth knowing about week 1: 3–4 is 56.9% (n=58), 4–5 is 40.0% (n=30), 5–6 is 48.0% (n=25) and 6+ is 76.9% (n=26). The headline 64.5% at ≥5 is one 26-game bucket carrying the band, which is why week 1 stakes half a unit and not more.
Injury burden — recorded, not bet
Burden is the sum of each unavailable or compromised player's weight times his prior-weeks snap share, so losing an every-down player costs more than losing a rotational one.
| Status | Weight |
|---|---|
| Out / Doubtful | 1.00 |
| Questionable, did not practise | 0.40 |
| Questionable, limited | 0.20 |
| Questionable, full practice | 0.05 |
It is computed from practice_status, which is published before
kickoff — never from whether the player actually took a snap, which is only
knowable afterwards. Building on the snap column would produce a model that
backtests beautifully and cannot be run on a Sunday morning.
Stated plainly: the injuries dataset starts in 2023 — roughly 850 games, not 7,000. Burden appears on the card as a signal (burden:TEAM+n) and is logged beside every pick, but is never bet on its own. Recording it live is how it earns or loses the argument.
Where the data comes from
| Source | Used for | Cost |
|---|---|---|
| nflverse/nfldata games.csv |
7,548 games 1999–2026: scores, consensus spreads, totals, moneylines, weather, QBs, coaches, rest days | Free |
| nflverse-data releases |
snap_counts, injuries, pbp, weekly_rosters, player_stats. Injuries begin 2023. | Free |
| The Odds API api.the-odds-api.com/v4 |
Live prices for line shopping — spreads,totals across us,us2 | 4 credits/call 500/month free |
The publisher runs every 30 minutes, but only buys a board when a game is within 40 minutes of its three-hour deadline. One pull covers the whole slate, so a wave of 1pm kickoffs costs a single call — around seven a week, near 120 credits a month against a 500 allowance. Every other run refreshes the free feeds and costs nothing, which is what makes a 30-minute staleness check affordable.
This replaced a fixed Tuesday/Thursday/Sunday cadence, under which a Monday-night game's price was last refreshed on Sunday morning — about 36 hours before kickoff. The recorded price is meant to be one you could have taken.
Prices are shopped only across funded accounts — DraftKings, FanDuel, BetMGM and Caesars. A best line at a book you cannot bet is not a line.
Deliberately absent: intraweek line-movement data. It is not free, and optimising when to bet a signal that does not beat the closing line is optimising when to lose.
Signal and execution are kept apart
The edge is always measured against nflverse's consensus spread, because that is what the 6.0-point threshold was calibrated on across 28 seasons. Feeding the best shopped line into that threshold instead would silently change what the number means — a 6.0 measured against a shopped line is not the 6.0 that was tested. Shopping is applied afterwards, at execution.
And shopping matters more than any belief here. The Elo signal's 52.85% against a 52.38% break-even is an edge of 0.47% — thin enough that one point of juice erases it. The same picks at −105 move break-even to 51.22% and turn that 0.47% into 1.63%. About one NFL game in ten lands exactly on 3, so moving from +3 to +3.5 is worth roughly 1.5–2% on its own — larger than every belief that survived testing.
Ranked by number, then price, then book. A half-point outranks a nickel of juice; reversing those two would reliably take the worse side of the trade. Ties on both fall to DraftKings, then FanDuel, then Caesars, then MGM — a preference, not an edge: at identical terms the bet is identical, and the order exists so the same slate names the same book on every run.
A bet is graded at the number it was taken at, not at whatever the feed says on grading day — the shopped number first, the consensus at pick time second. Grading against a different line than the one that was paid out at would make the record and the money disagree about the same game.
The rules that keep the record honest
- Point-in-time by construction. Tests receive history through a
prior_only()boundary rather than reaching into the full table. Night one produced a 56.27% edge at z=+5.86 on backup quarterbacks; it was entirely look-ahead, and point-in-time the same test gives 52.52%. - Every test is logged. Run twenty tests and one clears two sigma by luck. The ledger prints the running count so a z-score is always read against how many chances it had.
- Flat units, always. A unit is what is risked, never what is won. Nothing sizes up on a good week.
- A pick cannot be added, removed or resized once frozen. Rows refresh until three hours before kickoff and freeze permanently at that moment.
- Every game gets an opinion; few get a bet. Both are recorded, and they are never added together.
- Pushes are excluded from n, never counted as losses.
- No hit rate is treated as a verdict below 150 graded bets.
Source: elo.py, weekly.py, injury_burden.py, qb_value.py, elo_qb.py, odds.py, harness.py and data/ledger.jsonl in the model repository. Figures here are maintained by hand and were last checked against the source on 7 October 2026. Personal bets live in data/bets.csv and are kept entirely separate: the model's record reads the same whether a pick was bet three times or never.