New to these reports? Start here
  • Dotted-underlined words have a plain-English definition — hover or tap them. Every term is also on the glossary page.
  • "Null" means we found nothing, not that something broke. Most reports here are negative results, on purpose — knowing an idea doesn't work is the point.
  • Two questions get asked separately. First, is the effect real? Second, is it already priced into the betting odds? An effect can be completely real and still useless to bet on.
  • A "calibration" row is a self-check. It runs the same method on something already known to be true. If that fails, the whole report is unreliable — so it's reported alongside the findings.
  • If a confidence interval includes zero, the real effect might be nothing at all, so no claim gets made.

Did the Weather + FTN Features Actually Earn Their Keep?

Measurement of commit 7ba65e1, which wired real Open-Meteo weather and trailing FTN charting features into the fantasy projection engine and the betting feature builder.

Verdict: modest real gain on fantasy (concentrated exactly where weather should bite, with a clear biasbiasWhether the misses lean consistently one way. A projection can have a good average error size but still be biased if it is almost always too high. Bias is often the more fixable problem. correction). No confirmed betting edge.

TL;DR

Side Measure Result Verdict
Fantasy passing-yards MAEmean absolute errorAverage size of the miss, ignoring direction. If a projection is off by 3 one week and -5 the next, the MAE is 4. Lower is better. 62.73 → 62.04 (−1.10%) ✅ real, modest
Fantasy passing-yards bias +6.98 → +4.06 (−42%) ✅ the clearer win
Fantasy all 12 stat categories 5 better, 6 worse (worse ones ≤ +0.23%) mixed, net positive
Betting wind → closing total survives holdoutholdoutData deliberately set aside and never looked at while developing an idea, then used once at the end as a fair test. Peeking at it first would defeat the purpose., but tail fails ❌ not exploitable
Betting wind ≥15mph unders (holdout) 50.0% hit rate (need 52.4%) ❌ no edge
Betting precipitation / temp → market nullnull resultA test that found nothing. "Null" is the starting assumption that there is no real effect; a "null result" means the data gave us no reason to abandon that assumption. It does not mean the data was missing or the test failed to run. ❌ priced
Betting FTN → market errormarket errorHow far the actual result landed from what the betting market predicted. If this is unrelated to some factor, the market already accounted for that factor. nothing clears the floor ❌ priced

Fantasy side — measured on the real walk-forward harness

scripts/evaluate_fantasy_mae.py, season 2025, weeks 5–18, strict walk-forwardwalk-forwardEvaluating week by week using only what was knowable before each week, mimicking how the model would actually have been used at the time., A/B on the weather_adjustments config flag. 3,328 projected player-weeks (2,654 with a stat line).

Unlike the injury multipliers, weather is not gated behind _is_future_week — the archive has real observations for completed games — so the standard harness measures it directly.

MAE by category

Stat n weather OFF weather ON Δ %
passing_yards 313 62.7271 62.0363 −0.6908 −1.10%
passing_tds 313 0.9755 0.9709 −0.0046 −0.47%
receiving_tds 2341 0.2795 0.2783 −0.0012 −0.43%
receiving_yards 2341 18.2230 18.1512 −0.0718 −0.39%
rushing_yards 1034 20.3959 20.3926 −0.0033 −0.02%
receptions 2341 1.3915 1.3919 +0.0004 +0.03%
interceptions 313 0.6351 0.6353 +0.0002 +0.03%
targets 2341 1.7666 1.7675 +0.0009 +0.05%
passing_attempts 313 7.3105 7.3189 +0.0084 +0.11%
rushing_tds 1034 0.3905 0.3910 +0.0005 +0.13%
rushing_attempts 1034 3.3524 3.3602 +0.0078 +0.23%
fumbles_lost 2654 0.0448 0.0448 0.0000 tie

The pattern is the point, not the headline number. Every improvement is in an efficiency/production stat where weather physically acts (passing yards, passing TDs, receiving yards, receiving TDs). Every regression is in a volume stat (attempts, targets, receptions) and is ≤ +0.23%. That is what a correctly-located signal looks like; a spurious one would scatter.

Bias — the clearer result

Stat bias OFF bias ON
passing_yards +6.9751 +4.0618 −42%
passing_tds +0.0405 +0.0201 −50%
rushing_yards −1.0934 −0.9274 −15%
receiving_yards +0.1874 −0.0837 −55%

All four major categories improved. The engine was systematically over-projecting passing, and real weather removes a meaningful share of it. For a projection used to set lineups, removing a persistent 7-yard-per-QB-game over-projection matters more than a 1% MAE move.


Betting side — the two-stage frame

Stage AStage AThe first question: is the effect real at all? Measured against what actually happened, ignoring betting markets.: is there a real effect on the outcome? Stage BStage BThe second question: is the effect already reflected in the betting odds? An effect can be completely real and still worthless to bet, because the price already includes it.: is it already in the closing lineclosing lineThe final odds right before a game starts. It reflects everything the betting market knows, which makes it the hardest benchmark to beat. (formal null E[market_error | feature] = 0)? Books have had forecasts and charting for years, so "priced" was the prior.

A unit error in the first pass nearly produced a false positive, and is worth recording. Slopes were initially reported per 1.0 unit of feature and compared against a points-denominated tradeable floortradeable floorThe minimum edge needed to overcome the bookmaker's cut. Anything smaller is real but unprofitable.. But wx_pass_mult spans only ~0.85–1.05, so "per 1.0 unit" is ~5× its entire observed range — which made several features look wildly "TRADEABLE". Re-expressed per +1 SD of the feature, most of them collapsed. Same class of error as the earlier starter-misidentification artifact: the statistics were fine, the units were not.

Weather vs the market (per +1 SD, develop 2015–2021)

Feature 1 SD Effect on total error 95% CI95% confidence intervalThe range the true value is plausibly in. If this range includes zero, we cannot rule out that the real effect is nothing at all. vs 0.79 floor
wind 4.23 mph −1.480 [−2.341, −0.849] above
wx pass multiplier 0.060 +1.537 [+0.856, +2.108] above (collinear with wind)
precipitation 0.065 in −0.707 [−1.606, +0.170] null
temperature 16.5 °F +0.317 [−0.211, +0.886] null

Spread error: wind and precipitation both null.

The pre-registered holdout test — and why it still fails

Wind was the one survivor, so it got exactly one holdout test:

Split Effect per +1 SD 95% CI
Develop 2015–2021 −1.480 [−2.341, −0.849]
Holdout 2022–2025 −1.109 [−2.292, −0.351]

The continuous slope does survive. But three things kill it as an edge:

1. The actual bet doesn't clear the vigvigThe bookmaker's built-in cut. It is why a coin-flip bet at -110 needs you to win about 52.4% of the time just to break even.. In the holdout, games at wind ≥15 mph: n=44, mean total error −0.35, under hit rate 50.0% against the 52.4% needed. Betting it would break even at best.

2. The linear fit is not describing the tail. Mean total error by wind bin:

Wind (mph) All Develop Holdout n (all)
0–4 +1.35 +1.30 +1.44 353
4–8 +0.79 +0.57 +1.18 832
8–12 −0.54 −0.87 +0.11 561
12–16 −2.34 −1.30 −4.18 225
16–20 −0.85 −1.92 +1.20 81
20+ −8.77 −11.88 −0.50 22

The 20+ bin rests on 22 games total, 16 of them in develop, and its −11.88 develop mean is a huge leverage point on the linear slope. The holdout tail reverses (16–20 is +1.20). The slope predicts ~−2.2 points at 15 mph; the observed windy-game mean is −0.35. The estimate and the reality disagree by 6×.

3. It explains almost nothing. corr(wind, total error) = −0.104, so wind accounts for 1.08% of the variance in market error. Overall mean total error is +0.022 — the market is essentially unbiased in aggregate.

The one genuinely interesting pattern is a gradient rather than a level bias: calm games trend over (+1.35 at 0–4 mph) and mid-windy games trend under. That is directionally consistent across both splits and is worth watching forward, but it is not a bet at these magnitudes.

FTN vs the market (per +1 SD, develop 2022–2023, n=447)

Feature Effect 95% CI Verdict
sum catchable rate → total error +0.190 [−0.671, +1.131] null
sum drop rate → total error −0.814 [−1.520, +0.187] null
sum pressure proxy → total error +0.814 [+0.134, +1.452] marginal — CI floor 0.134, far below 0.79
diff catchable → spread error −0.269 [−0.347, −0.195] real but below floor
diff blitz → spread error −0.294 [−0.443, −0.149] real but below floor

FTN features move the outcome decisively (Stage A: sum-catchable +20.7 pts on actual totals, sum-drop −34.1) — they clearly measure something real about offensive quality. But essentially none of it survives against the closing line. The two spread effects are statistically solid and economically too small; the pressure effect's confidence intervalconfidence intervalA range of values the true answer is plausibly in. If it includes zero, the effect might genuinely be zero, so we cannot claim it is real. reaches nearly to zero.

The holdout for FTN was not opened, since nothing cleared the floor in-samplein-sampleMeasured on the same data used to build or tune the idea. Nearly always looks better than reality..


Caveats

  • FTN covers only 2022–2025, so its develop split is 447 games. UnderpoweredunderpoweredNot enough data to detect an effect even if it is really there. An underpowered null means "we could not tell", which is very different from "there is nothing there". for anything but a large effect.
  • The fantasy A/B is one season (2025). The direction is consistent and the improvements are correctly located, but a single season of walk-forward is not a strong test of a ~1% MAE move.
  • Weather Stage B tests the closing line only; nothing here can detect a stale-opening-line or line-movement edge, since nflverse carries no openers.
  • Wind and wx_pass_mult are collinear by construction (the multiplier is a function of wind), so they are one finding, not two.

Bottom line

Keep the weather features on the fantasy side. They cut a systematic passing over-projection by ~42% and improve exactly the categories where weather physically acts, at negligible cost elsewhere.

Do not build a weather or FTN betting model expecting edge. The market prices both. Wind shows a real, holdout-surviving gradient against the closing total, but it explains 1% of variance, its magnitude is inflated by ~22 extreme games, and the tradeable version of it hits exactly 50% out of sample. The features remain worth having in the betting feature set for calibrationcalibrationA deliberate sanity check on the method itself: run it on something already known to be true. If it fails to detect the known thing, the method is broken and its other results mean nothing., not for edge — and any future claim of edge from them needs a forward test, not another backtest.

Reproduce:

python3 scripts/evaluate_fantasy_mae.py --season 2025 --weeks 5-18 \
    --variant current --set weather_adjustments=0 --tag wx_off_2025
python3 scripts/evaluate_fantasy_mae.py --season 2025 --weeks 5-18 \
    --variant current --set weather_adjustments=1 --tag wx_on_2025
python3 backtests/weather_ftn_value/measure_betting_value.py

On this page

Terms in this report

Source

backtests/weather_ftn_value/FINDINGS.md
updated 2026-08-23 04:11