feat(0076): deflate the sweep winner's metric for the number of trials

Adds optimize_deflated beside optimize in aura-registry: the in-sample
walk-forward selection now records, on each OOS winner's manifest, how much
the winner's metric is inflated by the size of the search it won — a deflated
score and (R arm) an overfit probability — without changing which member wins
(additive, C23; a regression test pins optimize_deflated's winner == optimize's).

- R arm (sqn_normalized/expectancy_r/...): a centred moving-block reality-check.
  Each member's per-trade R series is mean-subtracted to impose the no-edge null,
  resampled via the r_bootstrap kernel (extracted to a shared resample_block,
  byte-identical), and the best-of-K null-max gives
  overfit_probability = (count(null >= raw)+1)/(n+1) and
  deflated_score = raw - p95(null). Deterministic given the seed (C1).
- total_pips arm: a closed-form expected-max-of-K dispersion floor
  (inv_norm_cdf + expected_max_of_normals), no probability.

The record (FamilySelection on RunManifest) rides the proven
serde(default, skip_serializing_if) widening + a compat.rs mirror, so legacy
runs.jsonl/families.jsonl lines load unchanged and unstamped sweep/mc/standalone
manifests stay byte-identical (C14/C18). The SelectionMode enum reserves a
Plateau slot for #145. CLI: walk-forward stamps both arms; runs family <id> rank
surfaces a human-readable deflated/overfit line.

Quality review (re-dispatched after the implement-loop's quality gate exhausted)
caught a real defect: on the R arm with positive resamples but no member carrying
trade_rs (a family loaded from disk — trade_rs is serde-skipped), the null was
all-NEG_INFINITY, turning deflated_score into +inf. Fixed by collapsing "no usable
null" (zero resamples OR no trade_rs) into one degenerate floor (deflated == raw,
overfit == 1.0); pinned by optimize_deflated_no_trade_rs_floors_instead_of_infinity.

Verified: cargo test --workspace and cargo clippy --workspace --all-targets
-D warnings both green. Frictionless Stage-1 R; n_eff advisory and reload-time
recompute deferred (spec 0076 Out of scope).

closes #144
This commit is contained in:
2026-06-26 14:53:32 +02:00
parent 40e3f609c2
commit a2959050a4
19 changed files with 565 additions and 64 deletions
@@ -66,6 +66,7 @@ fn run_point(server: &Arc<DataServer>, point: &[Cell], from: Timestamp, to: Time
window: (from, to),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
selection: None,
},
metrics: summarize(&equity, &exposure),
}
@@ -76,6 +76,7 @@ fn run_point(
window: (from, to),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
selection: None,
},
metrics: summarize(&equity, &exposure),
};
@@ -399,6 +399,7 @@ pub fn report_from_trace(trace: &[BarTrace], from: Timestamp, to: Timestamp) ->
window: (from, to),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
selection: None,
},
metrics,
}
@@ -68,6 +68,7 @@ fn run_point(server: &Arc<DataServer>, point: &[Cell], from: Timestamp, to: Time
window: (from, to),
seed: 0,
broker: "sim-optimal(pip_size=1)".to_string(),
selection: None,
},
metrics: summarize(&equity, &exposure),
}
+1
View File
@@ -89,6 +89,7 @@ fn run_sample_over(prices: Vec<(Timestamp, Scalar)>) -> RunReport {
window,
seed: 0,
broker: "sim-optimal(pip_size=0.0001)".to_string(),
selection: None,
},
metrics,
}
@@ -89,6 +89,7 @@ fn run_sample(window: (Timestamp, Timestamp), source: Box<dyn Source>) -> RunRep
window,
seed: 0,
broker: "sim-optimal(pip_size=0.0001)".to_string(),
selection: None,
},
metrics: summarize(&equity, &exposure),
}