Add an n-normalized SQN (SQN100) as an optional sweep objective #130

Closed
opened 2026-06-24 09:54:00 +02:00 by Brummel · 1 comment
Owner

Motivation

summarize_r's sqn (crates/aura-engine/src/report.rs, cycle 0065 iter-2, spec
0065) is the raw Van-Tharp form √n · mean(R) / stdev(R). Because it scales with
the raw trade count, a strategy that simply trades more earns a higher SQN partly
for the turnover alone — not ideal when SQN is the single-number objective used to
rank a sweep family whose members differ in trade count.

Idea

Offer an n-normalized variant alongside the raw sqn — Van Tharp's "SQN score"
caps the trade count, conventionally at 100:

sqn_normalized = (mean(R) / stdev(R)) · √(min(n, CAP))   # CAP conventionally 100

This makes the objective comparable across sweep members with different trade
counts: it still rewards edge quality (mean/stdev) and consistency, but stops
rewarding sheer turnover past the cap.

Notes / open choices

  • Additive: keep the raw sqn field; add a sqn_normalized field (or make the cap
    a summarize_r parameter), so existing reporting stays byte-unchanged.
  • Spec 0065 deliberately set the raw form; this is a follow-up refinement, not a
    correction.
  • Decide: fixed cap at 100 vs. a parameter; and whether the sweep ranker should
    default to raw or normalized SQN.

Context: Stage-1 "R-based signal quality" cycle 0065 / decision log #117.

## Motivation `summarize_r`'s `sqn` (`crates/aura-engine/src/report.rs`, cycle 0065 iter-2, spec 0065) is the raw Van-Tharp form `√n · mean(R) / stdev(R)`. Because it scales with the raw trade count, a strategy that simply trades more earns a higher SQN partly for the turnover alone — not ideal when SQN is the single-number objective used to rank a sweep family whose members differ in trade count. ## Idea Offer an n-normalized variant alongside the raw `sqn` — Van Tharp's "SQN score" caps the trade count, conventionally at 100: ``` sqn_normalized = (mean(R) / stdev(R)) · √(min(n, CAP)) # CAP conventionally 100 ``` This makes the objective comparable across sweep members with different trade counts: it still rewards edge quality (mean/stdev) and consistency, but stops rewarding sheer turnover past the cap. ## Notes / open choices - Additive: keep the raw `sqn` field; add a `sqn_normalized` field (or make the cap a `summarize_r` parameter), so existing reporting stays byte-unchanged. - Spec 0065 deliberately set the raw form; this is a follow-up refinement, not a correction. - Decide: fixed cap at 100 vs. a parameter; and whether the sweep ranker should default to raw or normalized SQN. Context: Stage-1 "R-based signal quality" cycle 0065 / decision log #117.
Brummel added the idea label 2026-06-24 09:54:00 +02:00
Author
Owner

Design reconciliation (specify) — cycle 0067

Spec: 0067-r-sweep-followups. This issue's open choices are resolved below
(derived autonomously under /boss; reply to veto).

  • Fork: cap fixed vs. a summarize_r parameter → fixed named constant
    SQN_CAP = 100, not a parameter.
    Basis: derived — the cap is Van Tharp's convention, not a per-experiment
    tuning knob; a parameter forces every call site to answer "what cap?" with
    no principled value but 100 (YAGNI). A future variable-cap need is a
    separate change.
  • Fork: ranker default raw vs. normalized → unchanged; raw sqn keeps the
    meaning of "sqn", and sqn_normalized is a NEW opt-in metric selected
    explicitly (aura runs family <id> rank sqn_normalized).
    Basis: derived — non-breaking, preserves the 0066 byte-compat / golden,
    consistent with how 0066 added the R metrics as opt-in ranking vocabulary.
  • Fork: serialization back-compat → the new RMetrics.sqn_normalized field
    carries #[serde(default)], so a pre-0067 r: block (without the field)
    still deserializes (→ 0.0).
    Basis: derived — C18 back-compat (the registry reads runs.jsonl); mirrors
    the metrics.r field's own serde(default).

Note: additive — the 0066 single-run golden is re-baselined (the r: block
gains one field). That is the correct response to an intended output change,
not drift.

## Design reconciliation (specify) — cycle 0067 Spec: `0067-r-sweep-followups`. This issue's open choices are resolved below (derived autonomously under `/boss`; reply to veto). - **Fork: cap fixed vs. a `summarize_r` parameter** → fixed named constant `SQN_CAP = 100`, not a parameter. Basis: derived — the cap is Van Tharp's convention, not a per-experiment tuning knob; a parameter forces every call site to answer "what cap?" with no principled value but 100 (YAGNI). A future variable-cap need is a separate change. - **Fork: ranker default raw vs. normalized** → unchanged; raw `sqn` keeps the meaning of `"sqn"`, and `sqn_normalized` is a NEW opt-in metric selected explicitly (`aura runs family <id> rank sqn_normalized`). Basis: derived — non-breaking, preserves the 0066 byte-compat / golden, consistent with how 0066 added the R metrics as opt-in ranking vocabulary. - **Fork: serialization back-compat** → the new `RMetrics.sqn_normalized` field carries `#[serde(default)]`, so a pre-0067 `r:` block (without the field) still deserializes (→ 0.0). Basis: derived — C18 back-compat (the registry reads `runs.jsonl`); mirrors the `metrics.r` field's own `serde(default)`. Note: additive — the 0066 single-run golden is re-baselined (the `r:` block gains one field). That is the correct response to an intended output change, not drift.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#130