Deflate the sweep winner's metric for the number of trials #144
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Concern
optimize(family, metric)selects the single best sweep member by a bare argmax —crates/aura-registry/src/lib.rs:186, the reduce at:191keeps a member only when it is strictly better bymetric_cmp(:128).rank_by(:171) orders the same way. Neither applies any penalty for the number of configurations evaluated.The World (C12) makes massively parallel sweeps aura's differentiator:
GridSpace/RandomSpaceenumerate K parameters, optionally across M instruments. The more members a family contains, the higher the best member's metric climbs by chance alone — a sweep over a large grid selects the best cell on noise. The current selection path has no defense against this: the winner'sexpectancy_r/sqn_normalizedis reported and ranked at face value regardless of how many members competed for the top slot.What to build
A multiple-comparisons-aware selection step that adjusts the selected member's score for the size of the family it won against, so the reported edge reflects the search effort:
Relationship to #139
Orthogonal. #139 validates a given candidate out-of-sample (walk-forward IS to OOS, plus a block-bootstrap confidence interval on its OOS per-trade R). That tests whether one candidate's OOS edge is separable from zero; it does not correct for the candidate having been chosen as the best of many. #139's own per-window step still picks the best-of-grid by an R metric with no trials-deflation. This issue addresses the selection-across-the-family bias that #139 leaves unguarded.
Notes
summarize_r,crates/aura-engine/src/report.rs:89); the deflation operates on the R-family metrics already ranked inrank_by.Design reconciliation (specify)
Spec:
0076-deflate-sweep-winner-trials(first cycle of the milestone).The forks below were settled this session; this records their resolution
before the spec is written.
User decisions (ratified 2026-06-26 via a structured prompt):
total_pipsarm asymmetry → the R /sqn_normalizedarm gets thefull empirical overfit probability; the
total_pipsSmaCross arm gets only thedispersion-floor deflation. Basis: user decision ("accept the asymmetry"); R is
the C7 yardstick and is risk-normalised, pips are not;
selection_metricself-documents which path ran.
the in-sample family stays transient as today. Basis: user decision; the recorded
(seed, n_resamples, block_len)make the number reproducible-by-re-run without aC18 wire-shape expansion.
r_bootstrapkernel), not sign/label permutation. Basis: user decision; onedeterminism-tested mechanism (C1).
RunManifest.selectioncarrier + theSelectionModeenum with a reservedPlateauslot), then #146 (the orthogonal across-instrument axis). Basis: userdecision.
Derived orchestrator decisions (rationale, not preference):
primary with resampling as the fallback; this is inverted. The resampled
best-of-K is the trunk; the closed-form effective-trials (
N_eff) is demoted toan optional advisory and deferred out of this cycle. Basis: derived —
SweepFamilycarries onlyspace+ odometer points and no axis cardinalities(
sweep.rs:291), soN_eff's per-axis-cardinality inference silently mis-shapesoff a full cartesian grid and is undefined on
RandomSpace; the resamplingstatistic rests on aura's own determinism-tested kernel and needs no grid topology.
FamilySelectionlives inaura-engine(besideRunManifest/RMetrics),constructed by
aura-registry. Basis: derived —RunManifestis anaura-enginetype and
aura-registrydepends onaura-engine; defining the carrier in theregistry would invert the dependency (C9).
trade_rsismean-subtracted (imposing the no-edge null) before the moving-block resample;
overfit_probabilityis the best-of-K max-statistic p-value in the(count + 1) / (n_resamples + 1)form. Basis: derived — resampling each member'sown uncentred trades reproduces its own edge, so the best-of-K ≈ the winner and
the statistic degenerates to ≈0.5; centring to the null is what makes the
block-resample a false-positive test.
total_pipsdispersion floor uses the exact expected-max-of-K-normalscoefficient (via a small pure inverse-normal-CDF helper), not the
sqrt(2*ln K)asymptote. Basis: derived — the SmaCross grid is tiny (K≈4), wheresqrt(2*ln K)overstates the hurdle by ~60%; the exact order-statisticcoefficient is accurate at small K and reusable for the milestone.
Out of scope (deferred): the
N_effeffective-trials advisory; any reload-timerecompute of the resampling statistic (
trade_rsisserde(skip), so the variantruns only live inside
walkforward_familypre-persist — a reload recompute needs awire-shape change). Frictionless Stage-1 R; existing SMA/
total_pipswalk-forwardand synthetic-MC goldens preserved (add, don't break).
Derived decision (planner, /boss): SplitMix64 cross-crate visibility
While decomposing 0076, plan-recon surfaced that
optimize_deflated(aura-registry) must drive the moving-block resample deterministically, but
SplitMix64ispub(crate)inaura-engine/src/harness.rs:143and is notre-exported — unreachable from aura-registry.
Decision → make
SplitMix64puband re-export it fromaura-engine(beside the already-public
r_bootstrap); aura-registry creates the per-resampleRNG (
SplitMix64::new(seed ^ i)) and calls the extractedresample_blockdirectly. Basis: derived — this keeps the "which metric" selection logic in
aura-registry (where
Metric/metric_cmp/optimizelive) and the determinismkernel + metric-from-series (
resample_block,r_metrics_from_rs) in aura-engine,rather than pushing metric selection into the engine via a duplicated metric enum
or a closure across the crate boundary. C1 is preserved (the RNG is the project's
canonical seeded primitive; exposing it changes no behaviour); the kernel stays
byte-identical (the extraction is pinned by
r_bootstrap's determinism golden).Two mechanical sub-points (not forks, recorded for completeness): the two
walkforward_familycall sites rebindoos_reportasmutto stamp theselection;
runs_familygains an additive human-readabledeflated=… P(overfit)=…line (only its own display golden is extended — sweep/mc/standalone manifests stay
byte-identical via
skip_serializing_if = "Option::is_none").