Repository navigation
slice-9: season-level backtest harness (gp backtest --season-level) - #22
Merged
Merged
Conversation
Fits at cutoffs 2/6/12 of each held-out season, chains the rest with slice-8's simulator, and scores constructor-share Spearman (next to a rolling legacy number at the same cutoff), title Brier for both championships and 80% interval coverage on final points. Writes a season_level block to backtest_summary.json with gate_valid / null verdict discipline. Tested end to end on the fixture with ADVI; the real backtest has not been run. Refs #19 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JyCVWAmeHjqML4h9ugdbGQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JyCVWAmeHjqML4h9ugdbGQ
Meerav29
pushed a commit
that referenced
this pull request
Oct 23, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F4qkQYA3HBDbujUy5MWTH6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tracking: #19 (autopilot window 2). Slice 9 in
docs/autopilot/queue.md.What this slice does
Adds the Phase 3 season-level backtest (spec §7 season-level metrics, §8 Phase 3, §4 legacy benchmark). It is built and tested on the fixture. It has not been run on real data.
gp backtest --season-level [--cutoffs 2,6,12]: for each held-out season and cutoff, it fits throughseason:cutoff(ADVI by default,--method nutsalso works) and chains the rest of that season with slice-8'scli.forecast_season. The chain only gets rows up to the cutoff. The real calendar is read off the season's own R/S rows through theremaining_calendarloader seam (backtest.backtest_schedule).backtest.score_season_cutoff):exp_shareagainst the actual sharebacktest.legacy_constructor_spearman) comes fromfeatures.build_features(early_rounds=cutoff)and the Ridge pipeline fromtrain.py, trained only on earlier seasons. The static 0.88 is reported next to it aslegacy_reference_spearman.backtest_summary.jsongets aseason_levelblock with per-cutoff means,failed_cutoffs,gate_valid,gate_invalid_reasonsandv2_beats_legacy_spearman. The verdict isnullin any of these cases: a cutoff failed (fit, chain, or no rounds left after it), a cutoff was never attempted, or a scored cutoff has no legacy number. If a race-level summary already exists in--out-dir, the block is merged into it and nothing is overwritten.season_backtest_metrics.csvand, for each cutoff,season_<Y>_r<c>_{drivers,constructors}_championship.csv.drivers_championship.csvandconstructors_championship.csv(includinggp season's output) gain two columns,p10_final_pointsandp90_final_points. The §6 columns are unchanged.Review pack
About 25 minutes, most of it waiting on the fits.
docs/autopilot/decisions.md(5 min). The one most likely to need your call is the legacy comparison. It trains on earlier seasons only, not LOSO, so its numbers will probably come out below 0.88.ls data/race_results.csv data/constructor_standings.csv. If they're missing, runPYTHONPATH=src python src/collect.py --start-year 2014 --end-year 2025first. Without them every cutoff has no legacy number and the verdict isnull, which is intended.--method advifor a quicker first look.outputs/season_bt/backtest_summary.json→season_level(5 min):gate_validshould be true andfailed_cutoffsempty.per_cutoff["2"].mean_constructor_spearman_v2againstmean_constructor_spearman_legacy.mean_coverage80_*should be near 0.8.v2_beats_legacy_spearmanis the verdict.season_2024_r6_drivers_championship.csvagainst what you remember of 2024 after round 6.Acceptance criteria
gp backtest --season-levelfits at cutoffs after rounds 2, 6 and 12 of each held-out season and chains the rest with slice-8's simulator.--cutoffsdefaults to2,6,12. The chain iscli.forecast_season, unchanged from slice-8. Evidence:test_cli_routes_season_level_to_the_season_harnessassertscutoffs == [2, 6, 12]and that the race-level path doesn't run.TestLoop::test_the_chain_sees_nothing_after_the_cutoff_but_the_real_calendarasserts the chain seesround.max() == 1at cutoff 1 and gets the real remaining calendar (Saudi Arabian Grand Prix).per_cutoff[c].mean_constructor_spearman_{v2,legacy},mean_brier_{drivers,constructors}_titleandmean_coverage80_{drivers,constructors}keys. Tests:test_scores_against_the_whole_season_not_the_cutoff,test_coverage_ignores_drivers_the_chain_did_not_simulate,test_championship_tables_carry_the_80pct_points_interval,test_legacy_trains_only_on_earlier_seasons.gate_valid/null-verdict discipline, so a partial run or a failed cutoff can't report a verdict. Tests:test_a_partial_run_cannot_report_a_verdict,test_a_failed_cutoff_cannot_report_a_verdict_and_is_named,test_a_missing_legacy_number_cannot_report_a_verdict,TestLoop::test_a_failed_fit_is_recorded_and_voids_the_verdict,test_a_cutoff_with_nothing_left_to_chain_is_a_failure_not_a_skip. A losing v2 reportsFalse, notNone(test_v2_losing_to_legacy_is_false_not_none).data/driver_rounds_fixture.csvwith ADVI and tiny draw counts. The real backtest was not run.test_end_to_end_on_the_fixture_writes_a_season_level_blockdoes a real ADVI fit (200 steps, 20 draws) through 2024:1, chains round 2 with 200 trials, scores it, and writes every file. It takes about 7 s once PyTensor has compiled. The fixture only has 2024 R1–R2, so this test uses cutoff 1. The default 2/6/12 cutoffs fail on it asno_rounds_after_cutoff, which the test above asserts.Suite, run locally (Python 3.11.17 venv, pymc 5.28.5, arviz 0.23.4, pandas 2.3.3),
PYTHONPATH=src pytest tests -q:CI (Python 3.12) is the authoritative gate. I did not run 3.12 locally.
Mutations, each applied alone to
tests/test_season_backtest.py(17 tests) and then reverted:driver_roundsinstead of the rows up to the cutoff (leak)!= season) instead of earlier seasonsp10/p90_final_pointsDecisions made without you
All three are recorded in
docs/autopilot/decisions.md(2026-10-23):train.pypipeline, allFEATURE_COLS) on seasons before the held-out one, using features built atearly_rounds=cutoff. The 0.88 is shown as a reference. Rejected: LOSO, the 0.88 method, which trains on future seasons. Also rejected: replaying LassoCV selection, weight tuning and the model pick for each fold.[p10, p90]on final points. Driver coverage counts only the cutoff line-up.--season-levelruns instead of the race-level backtest, not as well as it. Its block is merged into an existing summary in the same--out-dir. Rejected: coupling it to the 2.7 h race-level run.Deliberately not included
confidence.pyor the pkl. Both are untouched.How to revert
git revert <merge commit>onauto/queue. The change is additive:src/backtest.pycmd_backtestgets a new branch, and the old path is unchangedsim/season.pygains two output columnsNo data files or schemas change.
Generated by Claude Code