[v0.26] Validation hardening — purged chronological splits + the leakage-vs-regime diagnosis - #30
[v0.26] Validation hardening — purged chronological splits + the leakage-vs-regime diagnosis#30ieatyoursushi wants to merge 3 commits into
Conversation
…taSplit facade Validation hardening (GPT-thread Phase A adoption; see DataMemo/temp/direct_indexing_concept_architecture_plan_contextualized.md section 5). Y_Soft_BT at time t is a function of prices over (t, t+30], so adjacent rows share future context and the v0.1-v0.25 stratified RANDOM splits can leak it across train/test boundaries. - New Splits/TemporalSplit.cs: chronological TrainTest with an embargo gap (train t <= T* - E ensures every training label window ends strictly before the test period), and PurgedFolds — contiguous k-fold with both-side embargo (Lopez de Prado-style purged k-fold). - New Splits/SplitPolicy.cs: process-wide mode set once from the CLI (--split=temporal, --embargo=N, --testfrac=F; testfrac=0.5 = the decade walk-forward). Default remains stratified-random: existing commands reproduce v0.25 numbers exactly. - New Splits/DataSplit.cs facade; the 14 trainer/pipeline call sites (5 trainers x2, PCA, tax regression, GridSearchCV, inline linreg CV) renamed mechanically with identical signatures. - Artifacts write to data/artifacts-mlnet-temporal/ under the flag so random-split baselines are never clobbered. - Program.cs: warn when --oracle/--ctrade are passed to non-simulate modes (review finding: silently-ignored flag was a footgun for the two-arm workflow). - 5 new TemporalSplitTests (boundary/embargo honesty, purge accounting, both-side fold embargo + partition, determinism, facade dispatch). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…op is the aging prevalence crash DataMemo/ValidationHardening_v026.md records the random-vs-temporal-purged comparison and the ROC-AUC-vs-PR-AUC diagnostic that disentangles leakage from regime shift. Result (canonical scalarized arm, 80/20 temporal, embargo=30d): - ROC-AUC HELD/rose (soft GBT 0.9935->0.9970; oracle 0.9986->1.0000) => ranking skill transfers across time, no material ranking leakage. - PR-AUC collapsed (soft GBT 0.8126->0.4585) because the temporal test period is the cost-basis-aged tail: soft+ prevalence 3.93% (train era) -> 0.22% (2022-26). PR-AUC's no-skill floor IS the positive rate, so identical ranking on an 18x-rarer class yields lower AUPRC. In lift over no-skill the temporal model is BETTER (~25x -> ~210x). Gate: PASS with a mandate. The champions survive honest validation on the skill metric; the prevalence crash it exposes is the exact v0.3 P0 (contributions/rebalancing/trim restore harvestable supply -> recover prevalence -> recover PR-AUC). Standing change: report ROC-AUC + PR-AUC + test-prevalence together, and prefer --split=temporal for forward claims. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…drift isolated Decade split (train ~2006-2016, test ~2016-2026, 924k test rows): - oracle GBT ROC=1.0000 (flat across random/80-20/decade) => the deterministic target is the leakage control; its perfect invariance rules out leakage. - soft GBT ROC=0.9609 (vs ~0.997 under 80/20) => genuine regime-generalization decay over a full unseen decade, NOT leakage; still strong ranking. The decade split distinguishes mild honest drift from leakage the 80/20 could not. - PR-AUC 0.4173 (test soft+ 0.33%) => same prevalence-crash mechanism. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decade walk-forward — confirmed, with a sharper conclusionThe
The oracle target is the clean leakage control — it's a deterministic function of current-timestep features, so leakage would show up as its ROC-AUC being inflated under random and dropping under temporal. Instead it's 0.9986 → 1.0000 → 1.0000, flat and perfect. Leakage is ruled out. The soft ROC-AUC does dip to 0.9609 under the full-decade shift (from ~0.997 under the milder 80/20) — but that's genuine regime-generalization decay, not leakage: a model that never saw 2016–2026 ranks its harvest propensity slightly less sharply. 0.96 is still strong. The decade split's value is precisely that it distinguishes this mild honest drift from the leakage the 80/20 alone couldn't isolate. Net: leakage ruled out, mild decade-horizon drift quantified, and the PR-AUC collapse remains the prevalence crash (decade test soft+ = 0.33%). Memo and PR body updated; the gate result stands — pass with the v0.3 mandate. |
v0.26 — prep for v0.3/v0.4 (GPT-thread Phase A adoption)
First milestone after v0.25. Builds honest temporal validation and uses it to answer a question the random-split numbers couldn't: was the v0.1–v0.25 supervised skill real, or partly look-ahead leakage from overlapping 30-day forward labels?
Infrastructure
Splits/TemporalSplit.cs— chronologicalTrainTestwith a purge/embargo gap (traint ≤ T*−E⇒ label window ends strictly before the test period) andPurgedFolds(contiguous k-fold, both-side embargo — López de Prado purged k-fold).Splits/SplitPolicy.cs— process-wide mode from the CLI (--split=temporal,--embargo=Ndefault 30,--testfrac=F). Default stays stratified-random, so existing commands reproduce v0.25 bit-for-bit. Temporal artifacts write todata/artifacts-mlnet-temporal/.Splits/DataSplit.cs— facade dispatching on the policy; the 14 trainer/pipeline call sites renamed mechanically, so partition semantics live in one place.TemporalSplitTests;Program.csalso warns when--oracle/--ctradeare passed to non-simulate modes (review finding — silently-ignored flag was a two-arm-workflow footgun).The finding (the point of the milestone)
Canonical scalarized arm, 80/20 temporal, embargo = 30d:
ROC-AUC held (rose); PR-AUC collapsed. ROC-AUC is prevalence-insensitive (pure ranking) — its holding means the champions transfer across time with no material ranking leakage. PR-AUC's no-skill floor is the positive rate, and the temporal test period is the cost-basis-aged tail where soft+ prevalence falls 3.93% → 0.22% (~18×). Identical ranking on an 18×-rarer class yields lower AUPRC by construction — in lift over no-skill the temporal model is better (~25× → ~210×).
So the old random-split PR-AUC was not leakage-inflated in its ranking content, but it was a regime-averaged number dominated by the harvest-rich early years. Full derivation, CV table, and prevalence breakdown in
DataMemo/ValidationHardening_v026.md.Gate: pass, with a mandate
Champions survive honest validation on the skill metric. The prevalence crash it exposes is exactly the v0.3 P0 — contributions/rebalancing/the sell-winner trim restore harvestable supply → recover prevalence → recover PR-AUC without touching ranking. Standing methodology change: report ROC-AUC + PR-AUC + test-prevalence together; prefer
--split=temporalfor any forward-deployment claim.Decade walk-forward (
--testfrac=0.5, test ~2016–2026, oracle+ 0.018%) is running as confirmation and will be appended.🤖 Generated with Claude Code