Aggregate cross-instrument generalization as a first-class validation step #146
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
An edge that appears on a single instrument can be a fit to that series' noise; an edge that survives unchanged across several independent instruments is far less likely to be spurious. This cross-instrument agreement is one of the strongest available defenses against false discovery, but in aura it is run only by hand:
aura sweep --strategy stage1-r --real <SYM>evaluates one symbol per invocation, and there is no built aggregator that scores a candidate by how consistently it holds across instruments.#139 (decision log, comment of 2026-06-26) explicitly defers cross-symbol pooling:
RealWindowGrammaraccepts one symbol today, and cross-symbol aggregation is marked "a separable expansion, deferred to a follow-on, not built this cycle". This issue is that follow-on, scoped as a validation step rather than as edge research.What to build
A World-layer aggregator that evaluates one candidate across a set of instruments and scores its generalization:
summarize_r,crates/aura-engine/src/report.rs:89).expectancy_r/sqn_normalizedacross instruments, not a single pooled average that one strong instrument could dominate.Relationship to the sweep-selection issues
This is the across-instrument axis of the same anti-false-discovery concern: the trials-deflation issue corrects for the number of parameter trials, the plateau issue corrects for peak-picking within one instrument, and this corrects for an edge that exists on one instrument but not others. Together they make the World's parallel sweep defensible rather than a false-discovery engine.
Design ratified (brainstorm → specify): #146 as a cross-instrument validator, not a third selector
Pivotal fork — aggregator/validator (A) vs cross-instrument selector (B) — decided A.
Basis: user decision, "mach es so" (@Brummel, 2026-06-26), ratifying the aggregator reading:
#146 grades a brought candidate's generalization across instruments and chains behind
deflation (#144) / plateau (#145); it does not select a winner across instruments.
Derived sub-decisions (orchestrator, with rationale):
expectancy_rdefault,sqn_normalizedoptional). Rationale: R is the only account/instrument-agnostic unit(C10);
total_pipsis meaningless across instruments (different pip scales / vol). Thescore is undefined for / refuses pip metrics.
score = min_i metric_iacross instruments,recorded alongside a sign-agreement count (
#{ i : metric_i > 0 }) and the fullper-instrument breakdown. Rationale: exact structural sibling of
PlateauMode::Worst(already min-over-neighbourhood); satisfies the issue's non-domination constraint by
construction (a min cannot be lifted by one strong instrument); "consistency of sign and
magnitude" is then fully carried by {min, sign-agreement, per-instrument vector}. Rejected
alternative: a cross-instrument t-stat
mean_i / std_i · sqrt(M)— reintroduces the pooledmeanthe issue warns against and adds knobs without clear benefit.instrument), a single full-window R run per instrument is already an out-of-sample test for
that instrument; no per-instrument walk-forward is needed for the first correct version
(walk-forward-per-instrument is a possible later knob).
FamilySelection. NewFamilyKind::CrossInstrument; the M per-instrument runs are the family members. Theaggregate (worst-case + sign-agreement) is a pure function of the members (recomputable,
like
McAggregate), printed live and derivable on reload — not stamped on theselector-annotation carrier (which has no meaning here: there is no within-family winner).
instrument: Option<String>toRunManifest(serdedefault/skip_serializing_if, same back-compat idiom asselection; C14/C18). Today a real-data run records the window/broker but not whichsymbol — a latent gap; this makes each member self-identifying and supplies the
"instrument set as lineage" the issue asks for.
optimize_deflated/optimize_plateau),using
resolve_metric/metric_value. Rationale: C9 — the validation/selection-read layeris registry-side, the engine stays simulation. Consistent with where #144/#145 landed.
aura generalizesubcommand reusing theStage1RGridflags as asingle-value candidate and letting
--realtake an instrument list(
--real EURUSD,GBPUSD,USDJPY). Maximal reuse of existing grid-parsing + per-instrument Revaluation; the new surface is just the instrument loop + the aggregate.
Next: handing the ratified design to
specify.Implemented & cycle-closed (cycle 0078)
Cross-instrument generalization landed as ratified (aggregator/validator, reading A).
The
aura generalizesubcommand runs a single-cell stage1-r candidate across a--real SYM1,SYM2,...list and scores its generalization as a worst-case R floor(min over instruments) + a sign-agreement count + the per-instrument breakdown
— R-only (C10), the cross-instrument sibling of plateau-worst, non-dominable by
construction (a strong instrument cannot lift a min).
New
FamilyKind::CrossInstrumentfamily + a first-classRunManifest.instrumentlineage field (serde-widened with
skip_serializing_if, so legacy lines load andevery non-cross-instrument path stays byte-identical — C14/C23). The reduction
(
generalization+check_r_metric) lives in aura-registry besideoptimize_deflated/optimize_plateau(C9); the score is a recomputable aggregateover the family members, NOT stamped on
FamilySelection(there is no within-familywinner here — that is the aggregator-vs-selector distinction from #144/#145).
All three milestone pieces — #144 trials-deflation, #145 plateau-over-peak, #146
cross-instrument — are now built; the inferential-validation half is structurally
complete.
cargo test --workspace+clippy -D warningsgreen. Local commits:spec (boss-signed) / plan / feat / audit-close; the ledger's inferential thread + the
C18 realization note are synced.
The Gitea milestone close (its end-to-end fieldtest) remains a deliberate manual step.
This issue auto-closes on push via the feat commit's
closes #146.