diff --git a/CLAUDE.md b/CLAUDE.md index a7c5054..244b8a2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -84,31 +84,30 @@ design decision, not a refactor, and belongs in the ledger. makes a live external call mid-replay. (See `~/.claude/CLAUDE.md` for the IONOS consent rule: external LLM calls happen at the recording/live-source edge, with explicit per-session consent, never inside a backtest.) -7. **Strategy output is a directional bias stream; risk-based execution and - realistic brokers are decoupled downstream layers; signal quality is measured - in R.** The DAG expresses one state at t (C8: ≤1 record per `eval`), so a - strategy's primary, backtestable output is a signed, bounded **bias** `f64 ∈ - [-1,+1]` per cycle — direction (sign) + conviction (magnitude, optional), - **unsized** (not an equity curve, not a position, not a size). Sizing leaves the - strategy: a decoupled **risk-based execution** layer `bias → stop-rule → Sizer → - Veto → position-management` (the **RiskExecutor** composite, per symbol, nested - in a Broker/Account composite) turns bias + a protective **stop** into a sized - intent. The **stop defines the risk unit R** (1R = the loss if stopped). - **Signal quality is measured in R** (R-multiples / expectancy), the account- and - instrument-agnostic yardstick — **Stage 1**: flat-1R sizing, feed-forward, no - equity feedback, the primary research loop. **Currency P&L is Stage 2** (deploy - viability): fixed-fractional sizing reads equity (compounding) → realistic - brokers apply real frictions → currency equity, entered only after `E[R] > 0`. - The single feedback (equity → Sizer) is cut by a `z⁻¹` register on the **fill - edge** (mark-to-market stays a same-cycle price read), encapsulated in the - executor composite; flat-1R vs compounding is a **structural axis** (C11). The - broker-independent **position-event table** (`event_ts, action[buy/sell/close], - position_id, instrument_id, volume`) remains the **decoupled, derived** Stage-2 - audit layer — the first difference of the *book* (`deal = target − book − - in_flight`), a computed table (not a per-`eval` output, since one decision - instant may yield >1 event) — feeding realistic brokers. Brokers/executors are - ordinary downstream nodes, never part of the strategy; account mode - (netting/hedging) is a composition constraint. +7. **Strategy output is a directional bias stream; risk-based execution is a + decoupled downstream layer; signal quality is measured in R.** The DAG + expresses one state at t (C8: ≤1 record per `eval`), so a strategy's primary, + backtestable output is a signed, bounded **bias** `f64 ∈ [-1,+1]` per cycle — + direction (sign) + conviction (magnitude, optional), **unsized** (not an + equity curve, not a position, not a size). Sizing leaves the strategy: a + decoupled **risk-based execution** layer `bias → stop-rule → + position-management` (the **RiskExecutor** composite, per symbol; the **Veto** + an optional documented pre-trade-gate seam) turns bias + a protective **stop** + into a managed position, in R. The **stop defines the risk unit R** (1R = the + loss if stopped). **Signal quality is measured in R** (R-multiples / + expectancy), the account- and instrument-agnostic yardstick — the research + loop is **pure feed-forward**: flat-1R, gross R → net R via the composable + cost-model graph (C10), no Sizer, no equity feedback, no `z⁻¹` register. + **Money is a live/deploy-edge concern**: currency P&L, fixed-fractional + sizing, and compounding are post-hoc money-management transforms of the net-R + sequence at the deploy/account layer, and the live broker is an **I/O + adapter** at the recording/deploy edge (C11/C13), never part of the strategy. + The broker-independent **position-event table** (`event_ts, + action[buy/sell/close], position_id, instrument_id, volume`) survives as the + **decoupled, derived** deploy/reconciliation audit artifact — the first + difference of the *book* (`deal = target − book − in_flight`), a computed + table (not a per-`eval` output, since one decision instant may yield >1 + event). 8. **Deploy artifacts are frozen.** Hot-reload (cdylib) is an authoring-loop tool only. The live bot is a statically-linked, versioned, frozen artifact — never hot-swapped (audit trail: this bot = this commit). diff --git a/crates/aura-analysis/src/lib.rs b/crates/aura-analysis/src/lib.rs index 5023892..a78107c 100644 --- a/crates/aura-analysis/src/lib.rs +++ b/crates/aura-analysis/src/lib.rs @@ -71,7 +71,7 @@ pub struct RunMetrics { /// `runs.jsonl` lines still deserialise (C14/C18 back-compat). #[serde(alias = "exposure_sign_flips")] pub bias_sign_flips: u64, - /// Optional Stage-1 R metrics block. `None` for a pip-only run (and for legacy + /// Optional R metrics block. `None` for a pip-only run (and for legacy /// `runs.jsonl` written before this field existed — `serde(default)`); omitted from /// the JSON entirely when absent (`skip_serializing_if`), so the pip-only on-disk /// shape stays byte-unchanged (C14/C18 back-compat). @@ -79,7 +79,7 @@ pub struct RunMetrics { pub r: Option, } -/// R-based signal-quality metrics (Stage-1), reduced from a `PositionManagement` dense +/// R-based signal-quality metrics, reduced from a `PositionManagement` dense /// record stream by [`summarize_r`]. Account- and instrument-agnostic (pure R). Carries /// the enriched dispersion/churn fields (SQN, conviction terciles, net-of-cost). #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] @@ -129,7 +129,7 @@ impl PartialEq for RMetrics { // Dense `PositionManagement` record column indices — the lockstep contract with // aura-std's FIELD_NAMES/RECORD_KINDS. The record crosses the crate boundary as a // type-erased `Scalar` vector (C4 SoA), so it is read positionally, not by importing -// the producer's types; the layout is shared by convention and `stage1_r_e2e.rs` +// the producer's types; the layout is shared by convention and `r_sma_e2e.rs` // guards that it matches. mod r_col { pub const CLOSED: usize = 0; @@ -324,7 +324,7 @@ pub fn summarize_r( /// `summarize_r_includes_open_trade_and_matches_r_metrics_from_rs` — touch one copy /// without the other and that test is the sole tripwire. The /// fields a flat R series cannot carry are set honestly: `n_open_at_end = 0`, -/// `net_expectancy_r = expectancy_r` (exact under the Stage-1 cost = 0 invariant), +/// `net_expectancy_r = expectancy_r` (exact under the cost = 0 invariant), /// `conviction_terciles_r = [0,0,0]` (per-trade conviction is not pooled). Empty /// input -> a well-defined all-zero `RMetrics`. pub fn r_metrics_from_rs(rs: &[f64]) -> RMetrics { @@ -375,7 +375,7 @@ pub fn r_metrics_from_rs(rs: &[f64]) -> RMetrics { n_open_at_end: 0, sqn, sqn_normalized, - net_expectancy_r: mean, // cost = 0 -> net == gross (Stage-1 frictionless) + net_expectancy_r: mean, // cost = 0 -> net == gross (frictionless) conviction_terciles_r: [0.0; 3], trade_rs: Vec::new(), } @@ -1108,7 +1108,7 @@ mod tests { #[test] fn r_metrics_from_rs_folds_a_flat_series() { // pooled across-window R series [2.0, -1.0, 1.0]: expectancy = 2/3, 2 wins of 3, - // profit_factor = (2+1)/1 = 3. At cost 0 (frictionless Stage-1) net == gross. + // profit_factor = (2+1)/1 = 3. At cost 0 (frictionless) net == gross. // conviction terciles are not pooled -> [0,0,0]; n_open_at_end is not a pooled // concept -> 0. let m = r_metrics_from_rs(&[2.0, -1.0, 1.0]); diff --git a/crates/aura-cli/Cargo.toml b/crates/aura-cli/Cargo.toml index 3b33f9c..932eefe 100644 --- a/crates/aura-cli/Cargo.toml +++ b/crates/aura-cli/Cargo.toml @@ -12,8 +12,8 @@ path = "src/main.rs" [dependencies] aura-core = { path = "../aura-core" } aura-engine = { path = "../aura-engine" } -# aura-composites: the Stage-1 RiskExecutor / vol_stop composite-builders the -# stage1-r harness wires (kept out of aura-engine so the engine stays domain-free). +# aura-composites: the RiskExecutor / vol_stop composite-builders the +# r-sma harness wires (kept out of aura-engine so the engine stays domain-free). aura-composites = { path = "../aura-composites" } aura-registry = { path = "../aura-registry" } aura-std = { path = "../aura-std" } diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index 4d87fbf..b01b777 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -707,7 +707,7 @@ fn probe_window( /// Single home of the real-source construction the single-run handlers share — the /// sidecar-pip lookup, the `DataServer` `has_symbol` refusal, the probe-window pass, and /// the run-source `open` (each refusal an stderr + exit 1). Pre-data refusals keep the -/// pip honest by construction. Shared by `run_sample_real` and `run_stage1_r`. +/// pip honest by construction. Shared by `run_sample_real` and `run_r_sma`. fn open_real_source( symbol: &str, from_ms: Option, @@ -1043,14 +1043,14 @@ fn momentum_sweep_family(trace: Option<&str>, data: &DataSource) -> SweepFamily .expect("the momentum named grid matches the momentum param-space") } -/// The honest broker label for the dual-tap stage1-r harness: it runs a RiskExecutor +/// The honest broker label for the dual-tap r-sma harness: it runs a RiskExecutor /// branch alongside the SimBroker, so the plain "sim-optimal" label would under-report /// it (#132). Shared by the single run and the sweep so the two cannot drift. -fn stage1_r_broker_label(pip_size: f64) -> String { +fn r_sma_broker_label(pip_size: f64) -> String { format!("sim-optimal+risk-executor(pip_size={pip_size})") } -/// `aura sweep --strategy stage1-r`: sweep the stage1-r harness over a fast×slow +/// `aura sweep --strategy r-sma`: sweep the r-sma harness over a fast×slow /// SIGNAL grid (the stop + sizing stay fixed — see the cycle-0066 / #133 decision /// log: risk_budget is R-invariant and bias.scale is sign-only under flat-1R, both /// degenerate axes; the stop defines the R unit, so varying it would break @@ -1060,13 +1060,13 @@ fn stage1_r_broker_label(pip_size: f64) -> String { /// member's equity / exposure / r_equity streams are persisted under /// `runs/traces///` via `persist_traces_r` (mirroring /// `momentum_sweep_family`), so a swept member is chartable. -/// The four griddable knobs of the stage1-r sweep as value lists (#137): the SMA fast +/// The four griddable knobs of the r-sma sweep as value lists (#137): the SMA fast /// / slow lengths and the vol-stop length / `k`-multiplier. The family is the cartesian /// product of the four lists; absent flags fall back to the historical defaults (fast /// `{2,3}`, slow `{6,12}`, stop_length `{3}`, stop_k `{2.0}`) so a no-flags sweep is /// byte-identical to the pre-#137 family. #[derive(Clone, Debug, PartialEq)] -struct Stage1RGrid { +struct RGrid { fast: Vec, slow: Vec, stop_length: Vec, @@ -1076,13 +1076,13 @@ struct Stage1RGrid { band_k: Vec, } -impl Default for Stage1RGrid { +impl Default for RGrid { fn default() -> Self { Self { fast: vec![2, 3], slow: vec![6, 12], - stop_length: vec![STAGE1_R_STOP_LENGTH], - stop_k: vec![STAGE1_R_STOP_K], + stop_length: vec![R_SMA_STOP_LENGTH], + stop_k: vec![R_SMA_STOP_K], channel: vec![1920], window: vec![1920], band_k: vec![2.0], @@ -1101,11 +1101,11 @@ const STOP_LENGTH_SUFFIX: &str = ".vol_stop.stop_length.length"; const STOP_K_SUFFIX: &str = ".vol_stop.stop_k.weights[0]"; /// The path-qualified `param_space()` suffixes of the two open SMA-cross signal knobs. -/// Since cycle 0092 the signal leg is a nested `stage1_signal` composite, so its knobs -/// land in `param_space` under the composite prefix (`stage1_signal.fast.length` / -/// `stage1_signal.slow.length`); like the stop knobs they are resolved by suffix so the +/// Since cycle 0092 the signal leg is a nested `sma_signal` composite, so its knobs +/// land in `param_space` under the composite prefix (`sma_signal.fast.length` / +/// `sma_signal.slow.length`); like the stop knobs they are resolved by suffix so the /// prefix is never hand-synced, and rendered back to the bare `fast.length` / `slow.length` -/// manifest names by `stage1_r_friendly_name` (the pre-0092 family-record names — C18). +/// manifest names by `r_sma_friendly_name` (the pre-0092 family-record names — C18). const FAST_LENGTH_SUFFIX: &str = ".fast.length"; const SLOW_LENGTH_SUFFIX: &str = ".slow.length"; @@ -1114,7 +1114,7 @@ const SLOW_LENGTH_SUFFIX: &str = ".slow.length"; /// pre-#137 manual manifest names), every other slot unchanged. Decoupling the manifest /// name from the deep param-space path keeps the family record auditable (C18) while the /// values flow through the real `.axis(..)` grid. -fn stage1_r_friendly_name(space_name: &str) -> String { +fn r_sma_friendly_name(space_name: &str) -> String { if space_name.ends_with(STOP_LENGTH_SUFFIX) { "stop_length".to_string() } else if space_name.ends_with(STOP_K_SUFFIX) { @@ -1128,7 +1128,7 @@ fn stage1_r_friendly_name(space_name: &str) -> String { } } -fn stage1_r_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RGrid) -> SweepFamily { +fn r_sma_sweep_family(trace: Option<&str>, data: &DataSource, grid: &RGrid) -> SweepFamily { let pip = data.pip_size(); let window = data.full_window(); // a single throwaway floated build, only to resolve param_space (borrow) then @@ -1141,7 +1141,7 @@ fn stage1_r_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RG let (tx_ex, _) = mpsc::channel(); let (tx_r, _) = mpsc::channel(); let (tx_req, _) = mpsc::channel(); - let bp = stage1_r_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, true, None); + let bp = r_sma_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, true, None); let space = bp.param_space(); // resolve the open stop slots' exact path-qualified names from the live param-space // (the composite prefix is never hand-synced — match by the stable suffix). @@ -1149,22 +1149,22 @@ fn stage1_r_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RG .iter() .map(|p| p.name.clone()) .find(|n| n.ends_with(STOP_LENGTH_SUFFIX)) - .expect("open stage1-r vol-stop exposes a stop_length axis"); + .expect("open r-sma vol-stop exposes a stop_length axis"); let stop_k_axis = space .iter() .map(|p| p.name.clone()) .find(|n| n.ends_with(STOP_K_SUFFIX)) - .expect("open stage1-r vol-stop exposes a stop_k axis"); + .expect("open r-sma vol-stop exposes a stop_k axis"); let fast_axis = space .iter() .map(|p| p.name.clone()) .find(|n| n.ends_with(FAST_LENGTH_SUFFIX)) - .expect("open stage1-r signal exposes a fast.length axis"); + .expect("open r-sma signal exposes a fast.length axis"); let slow_axis = space .iter() .map(|p| p.name.clone()) .find(|n| n.ends_with(SLOW_LENGTH_SUFFIX)) - .expect("open stage1-r signal exposes a slow.length axis"); + .expect("open r-sma signal exposes a slow.length axis"); let binder = bp .axis(&fast_axis, grid.fast.clone()) .axis(&slow_axis, grid.slow.clone()) @@ -1176,7 +1176,7 @@ fn stage1_r_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RG let varying: HashSet = binder .varying_axes() .into_iter() - .map(|n| stage1_r_friendly_name(&n)) + .map(|n| r_sma_friendly_name(&n)) .collect(); binder .sweep(|point| { @@ -1185,9 +1185,9 @@ fn stage1_r_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RG let (tx_r, rx_r) = mpsc::channel(); let (tx_req, rx_req) = mpsc::channel(); let reduce = trace.is_none(); - let mut h = stage1_r_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, reduce, None) + let mut h = r_sma_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, reduce, None) .bootstrap_with_cells(point) - .expect("stage1-r grid points are kind-checked against param_space"); + .expect("r-sma grid points are kind-checked against param_space"); h.run(data.run_sources()); // record the swept knobs PLUS the fixed R-defining bias scale, matching the // single run: a family member must be reproducible from its own manifest @@ -1197,12 +1197,12 @@ fn stage1_r_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RG // auditable from the family record by the same names as before. let mut named: Vec<(String, Scalar)> = zip_params(&space, point) .into_iter() - .map(|(n, v)| (stage1_r_friendly_name(&n), v)) + .map(|(n, v)| (r_sma_friendly_name(&n), v)) .collect(); named.push(("bias_scale".to_string(), Scalar::f64(0.5))); let key = member_key(&named, &varying); let mut manifest = sim_optimal_manifest(named, window, 0, pip); - manifest.broker = stage1_r_broker_label(pip); + manifest.broker = r_sma_broker_label(pip); let metrics = if reduce { // folded: GatedRecorder emits O(trades) R rows; each SeriesReducer // emits one [last, max_drawdown, sign_flips] summary row. @@ -1232,53 +1232,53 @@ fn stage1_r_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RG }; RunReport { manifest, metrics } }) - .expect("the stage1-r named grid matches the stage1-r param-space") + .expect("the r-sma named grid matches the r-sma param-space") } -/// The param-space of the OPEN stage1-r blueprint (all four knobs free) — the kinds +/// The param-space of the OPEN r-sma blueprint (all four knobs free) — the kinds /// the per-window `WindowRun::chosen_params` are read against (C7). Mirrors the -/// throwaway-build param_space resolution inside `stage1_r_sweep_family`. -fn stage1_r_space() -> Vec { +/// throwaway-build param_space resolution inside `r_sma_sweep_family`. +fn r_sma_space() -> Vec { let (tx_eq, _) = mpsc::channel(); let (tx_ex, _) = mpsc::channel(); let (tx_r, _) = mpsc::channel(); let (tx_req, _) = mpsc::channel(); - stage1_r_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, true, None).param_space() + r_sma_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, true, None).param_space() } -/// Windowed reduce-mode stage1-r sweep over `[from, to]` — the in-sample leg of the -/// stage1-r walk-forward. Identical grid/axis/fold logic to `stage1_r_sweep_family`, +/// Windowed reduce-mode r-sma sweep over `[from, to]` — the in-sample leg of the +/// r-sma walk-forward. Identical grid/axis/fold logic to `r_sma_sweep_family`, /// but windowed (`windowed_sources`) and always folded (O(trades)/member): each /// member's RunReport carries `metrics.r = Some(summarize_r(..))`, so the family is /// rankable by an R metric. -fn stage1_r_sweep_over(from: Timestamp, to: Timestamp, data: &DataSource, grid: &Stage1RGrid) -> (SweepFamily, Option>) { +fn r_sma_sweep_over(from: Timestamp, to: Timestamp, data: &DataSource, grid: &RGrid) -> (SweepFamily, Option>) { let pip = data.pip_size(); let (tx_eq, _) = mpsc::channel(); let (tx_ex, _) = mpsc::channel(); let (tx_r, _) = mpsc::channel(); let (tx_req, _) = mpsc::channel(); - let bp = stage1_r_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, true, None); + let bp = r_sma_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, true, None); let space = bp.param_space(); let stop_length_axis = space .iter() .map(|p| p.name.clone()) .find(|n| n.ends_with(STOP_LENGTH_SUFFIX)) - .expect("open stage1-r vol-stop exposes a stop_length axis"); + .expect("open r-sma vol-stop exposes a stop_length axis"); let stop_k_axis = space .iter() .map(|p| p.name.clone()) .find(|n| n.ends_with(STOP_K_SUFFIX)) - .expect("open stage1-r vol-stop exposes a stop_k axis"); + .expect("open r-sma vol-stop exposes a stop_k axis"); let fast_axis = space .iter() .map(|p| p.name.clone()) .find(|n| n.ends_with(FAST_LENGTH_SUFFIX)) - .expect("open stage1-r signal exposes a fast.length axis"); + .expect("open r-sma signal exposes a fast.length axis"); let slow_axis = space .iter() .map(|p| p.name.clone()) .find(|n| n.ends_with(SLOW_LENGTH_SUFFIX)) - .expect("open stage1-r signal exposes a slow.length axis"); + .expect("open r-sma signal exposes a slow.length axis"); bp.axis(&fast_axis, grid.fast.clone()) .axis(&slow_axis, grid.slow.clone()) .axis(&stop_length_axis, grid.stop_length.clone()) @@ -1288,17 +1288,17 @@ fn stage1_r_sweep_over(from: Timestamp, to: Timestamp, data: &DataSource, grid: let (tx_ex, rx_ex) = mpsc::channel(); let (tx_r, rx_r) = mpsc::channel(); let (tx_req, _rx_req) = mpsc::channel(); - let mut h = stage1_r_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, true, None) + let mut h = r_sma_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, true, None) .bootstrap_with_cells(point) - .expect("stage1-r grid points are kind-checked against param_space"); + .expect("r-sma grid points are kind-checked against param_space"); let sources = data.windowed_sources(from, to); let window = window_of(&sources).expect("non-empty in-sample window"); h.run(sources); let mut named: Vec<(String, Scalar)> = zip_params(&space, point).into_iter() - .map(|(n, v)| (stage1_r_friendly_name(&n), v)).collect(); + .map(|(n, v)| (r_sma_friendly_name(&n), v)).collect(); named.push(("bias_scale".to_string(), Scalar::f64(0.5))); let mut manifest = sim_optimal_manifest(named, window, 0, pip); - manifest.broker = stage1_r_broker_label(pip); + manifest.broker = r_sma_broker_label(pip); let r_rows: Vec<(Timestamp, Vec)> = rx_r.try_iter().collect(); let (total_pips, max_drawdown) = rx_eq.try_iter().next() .map(|(_, row)| (row[0].as_f64(), row[1].as_f64())).unwrap_or((0.0, 0.0)); @@ -1308,10 +1308,10 @@ fn stage1_r_sweep_over(from: Timestamp, to: Timestamp, data: &DataSource, grid: RunReport { manifest, metrics: m } }) .map(|(fam, lat)| (fam, Some(lat))) - .expect("the stage1-r named grid matches the stage1-r param-space") + .expect("the r-sma named grid matches the r-sma param-space") } -/// Run the chosen stage1-r params over an OOS window; return the recorded pip-equity +/// Run the chosen r-sma params over an OOS window; return the recorded pip-equity /// segment (for stitching) and the OOS RunReport whose `metrics.r` carries both the /// R metrics and the per-trade `trade_rs`. Non-reduce (raw recorders): one window's /// curve is bounded, and `stitch` needs the full pip-equity series. @@ -1323,7 +1323,7 @@ fn run_oos_r( let (tx_ex, rx_ex) = mpsc::channel(); let (tx_r, rx_r) = mpsc::channel(); let (tx_req, rx_req) = mpsc::channel(); - let bp = stage1_r_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, false, None); + let bp = r_sma_graph(tx_eq, tx_ex, tx_r, tx_req, None, None, true, false, None); let space = bp.param_space(); let mut h = bp.bootstrap_with_cells(params) .expect("chosen params pre-validated by the in-sample GridSpace::new"); @@ -1335,10 +1335,10 @@ fn run_oos_r( let r_rows: Vec<(Timestamp, Vec)> = rx_r.try_iter().collect(); let req_rows: Vec<(Timestamp, Vec)> = rx_req.try_iter().collect(); let mut named: Vec<(String, Scalar)> = zip_params(&space, params).into_iter() - .map(|(n, v)| (stage1_r_friendly_name(&n), v)).collect(); + .map(|(n, v)| (r_sma_friendly_name(&n), v)).collect(); named.push(("bias_scale".to_string(), Scalar::f64(0.5))); let mut manifest = sim_optimal_manifest(named, window, 0, pip); - manifest.broker = stage1_r_broker_label(pip); + manifest.broker = r_sma_broker_label(pip); if let Some(name) = trace { persist_traces_r(&format!("{name}/oos{}", from.0), &manifest, &eq_rows, &ex_rows, &req_rows, &[]); } @@ -1349,14 +1349,14 @@ fn run_oos_r( (equity, RunReport { manifest, metrics }) } -/// `aura sweep --strategy stage1-breakout`: sweep the breakout harness over a channel × +/// `aura sweep --strategy r-breakout`: sweep the breakout harness over a channel × /// stop grid. One `channel` length drives BOTH rolling nodes (parameter-ganging, #61), /// so the family iterates the cartesian product MANUALLY with a fully-bound graph per -/// point (compile_with_params(&[]) + Harness::bootstrap, like run_stage1_r) rather than +/// point (compile_with_params(&[]) + Harness::bootstrap, like run_r_sma) rather than /// the open .axis/bootstrap_with_cells path. Each member folds the dense R-record via /// summarize_r, so the family is rankable by sqn / expectancy_r / ... (parity with -/// stage1-r). With --trace, raw recorders persist the per-cycle streams. -fn stage1_breakout_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RGrid) -> SweepFamily { +/// r-sma). With --trace, raw recorders persist the per-cycle streams. +fn r_breakout_sweep_family(trace: Option<&str>, data: &DataSource, grid: &RGrid) -> SweepFamily { let pip = data.pip_size(); let window = data.full_window(); let mut varying: HashSet = HashSet::new(); @@ -1378,10 +1378,10 @@ fn stage1_breakout_sweep_family(trace: Option<&str>, data: &DataSource, grid: &S let (tx_r, rx_r) = mpsc::channel(); let (tx_req, rx_req) = mpsc::channel(); let reduce = trace.is_none(); - let flat = stage1_breakout_graph(tx_eq, tx_ex, tx_r, tx_req, Some(c), sl, sk, reduce) + let flat = r_breakout_graph(tx_eq, tx_ex, tx_r, tx_req, Some(c), sl, sk, reduce) .compile_with_params(&[]) - .expect("valid stage1-breakout blueprint"); - let mut h = Harness::bootstrap(flat).expect("valid stage1-breakout harness"); + .expect("valid r-breakout blueprint"); + let mut h = Harness::bootstrap(flat).expect("valid r-breakout harness"); h.run(data.run_sources()); let named: Vec<(String, Scalar)> = vec![ ("channel".to_string(), Scalar::i64(c)), @@ -1390,7 +1390,7 @@ fn stage1_breakout_sweep_family(trace: Option<&str>, data: &DataSource, grid: &S ]; let key = member_key(&named, &varying); let mut manifest = sim_optimal_manifest(named, window, 0, pip); - manifest.broker = stage1_r_broker_label(pip); + manifest.broker = r_sma_broker_label(pip); let metrics = if reduce { let r_rows: Vec<(Timestamp, Vec)> = rx_r.try_iter().collect(); let (total_pips, max_drawdown) = rx_eq @@ -1422,7 +1422,7 @@ fn stage1_breakout_sweep_family(trace: Option<&str>, data: &DataSource, grid: &S SweepFamily { space: vec![], points } } -fn stage1_meanrev_sweep_family(trace: Option<&str>, data: &DataSource, grid: &Stage1RGrid) -> SweepFamily { +fn r_meanrev_sweep_family(trace: Option<&str>, data: &DataSource, grid: &RGrid) -> SweepFamily { let pip = data.pip_size(); let window = data.full_window(); let mut varying: HashSet = HashSet::new(); @@ -1449,10 +1449,10 @@ fn stage1_meanrev_sweep_family(trace: Option<&str>, data: &DataSource, grid: &St let (tx_req, rx_req) = mpsc::channel(); let reduce = trace.is_none(); let flat = - stage1_meanrev_graph(tx_eq, tx_ex, tx_r, tx_req, Some(n), bk, sl, sk, reduce) + r_meanrev_graph(tx_eq, tx_ex, tx_r, tx_req, Some(n), bk, sl, sk, reduce) .compile_with_params(&[]) - .expect("valid stage1-meanrev blueprint"); - let mut h = Harness::bootstrap(flat).expect("valid stage1-meanrev harness"); + .expect("valid r-meanrev blueprint"); + let mut h = Harness::bootstrap(flat).expect("valid r-meanrev harness"); h.run(data.run_sources()); let named: Vec<(String, Scalar)> = vec![ ("window".to_string(), Scalar::i64(n)), @@ -1462,7 +1462,7 @@ fn stage1_meanrev_sweep_family(trace: Option<&str>, data: &DataSource, grid: &St ]; let key = member_key(&named, &varying); let mut manifest = sim_optimal_manifest(named, window, 0, pip); - manifest.broker = stage1_r_broker_label(pip); + manifest.broker = r_sma_broker_label(pip); let metrics = if reduce { let r_rows: Vec<(Timestamp, Vec)> = rx_r.try_iter().collect(); let (total_pips, max_drawdown) = rx_eq @@ -1520,9 +1520,9 @@ fn default_registry() -> Registry { enum Strategy { SmaCross, Momentum, - Stage1R, - Stage1Breakout, - Stage1MeanRev, + RSma, + RBreakout, + RMeanRev, } /// In-sample winner-selection objective for walk-forward (cycle 0077). `Argmax` is @@ -1553,15 +1553,15 @@ impl Strategy { match self { Strategy::SmaCross => "sma", Strategy::Momentum => "momentum", - Strategy::Stage1R => "stage1-r", - Strategy::Stage1Breakout => "stage1-breakout", - Strategy::Stage1MeanRev => "stage1-meanrev", + Strategy::RSma => "r-sma", + Strategy::RBreakout => "r-breakout", + Strategy::RMeanRev => "r-meanrev", } } } /// Parse a comma-separated list of `T` (each item parsed via `FromStr`), rejecting -/// any item that fails to parse — the shared validator for the stage1-r grid flags +/// any item that fails to parse — the shared validator for the r-sma grid flags /// (#137). The caller folds the `Err` into the subcommand `usage()` so a malformed /// `--fast 2,x` is the strict usage path, not a downstream panic. `str::split(',')` /// always yields at least one item, and an empty item (`""`, or a trailing comma) @@ -1601,14 +1601,14 @@ fn mc_member_line(id: &str, seed: u64, report: &RunReport) -> String { ) } -/// `aura sweep [--strategy ] [--name |--trace ]`: run the +/// `aura sweep [--strategy ] [--name |--trace ]`: run the /// selected built-in sweep, persist it as a *family* (related records sharing one /// `family_id`, C18/C21) via `append_family`, and print each point's record line /// carrying the assigned id. With `--trace`, every strategy -/// (`sma`/`momentum`/`stage1-r`) persists each member's streams under -/// `runs/traces///` (opt-in); the `stage1-r` member also carries +/// (`sma`/`momentum`/`r-sma`) persists each member's streams under +/// `runs/traces///` (opt-in); the `r-sma` member also carries /// the `r_equity` tap (via `persist_traces_r`). -fn run_sweep(strategy: Strategy, name: &str, persist: bool, data: DataSource, grid: &Stage1RGrid) { +fn run_sweep(strategy: Strategy, name: &str, persist: bool, data: DataSource, grid: &RGrid) { if persist && let Err(e) = TraceStore::open("runs").ensure_name_free(name, WriteKind::Family) { @@ -1619,9 +1619,9 @@ fn run_sweep(strategy: Strategy, name: &str, persist: bool, data: DataSource, gr let family = match strategy { Strategy::SmaCross => sweep_family(persist.then_some(name), &data), Strategy::Momentum => momentum_sweep_family(persist.then_some(name), &data), - Strategy::Stage1R => stage1_r_sweep_family(persist.then_some(name), &data, grid), - Strategy::Stage1Breakout => stage1_breakout_sweep_family(persist.then_some(name), &data, grid), - Strategy::Stage1MeanRev => stage1_meanrev_sweep_family(persist.then_some(name), &data, grid), + Strategy::RSma => r_sma_sweep_family(persist.then_some(name), &data, grid), + Strategy::RBreakout => r_breakout_sweep_family(persist.then_some(name), &data, grid), + Strategy::RMeanRev => r_meanrev_sweep_family(persist.then_some(name), &data, grid), }; let id = match reg.append_family(name, FamilyKind::Sweep, &sweep_member_reports(&family)) { Ok(id) => id, @@ -1636,7 +1636,7 @@ fn run_sweep(strategy: Strategy, name: &str, persist: bool, data: DataSource, gr } /// `aura generalize --real --fast --slow --stop-length -/// --stop-k `: grade one stage1-r candidate (a single-cell grid) across an +/// --stop-k `: grade one r-sma candidate (a single-cell grid) across an /// instrument list. Pre-checks the R-metric data-free (refuse a non-R / unknown /// metric before any run), runs the candidate per instrument (stamping each report's /// manifest `instrument`), reduces to the worst-case floor + sign-agreement + @@ -1645,7 +1645,7 @@ fn run_sweep(strategy: Strategy, name: &str, persist: bool, data: DataSource, gr fn run_generalize( name: &str, symbols: &[String], - grid: &Stage1RGrid, + grid: &RGrid, metric: &str, from_ms: Option, to_ms: Option, @@ -1659,7 +1659,7 @@ fn run_generalize( for symbol in symbols { let choice = DataChoice::Real { symbol: symbol.clone(), from_ms, to_ms }; let data = DataSource::from_choice(choice); // per-instrument pip_or_refuse + has_symbol - let family = stage1_r_sweep_family(None, &data, grid); // single-cell grid -> 1 member + let family = r_sma_sweep_family(None, &data, grid); // single-cell grid -> 1 member let mut report = family.points[0].report.clone(); report.manifest.instrument = Some(symbol.clone()); members.push(report); @@ -1685,7 +1685,7 @@ fn run_generalize( /// print each carrying the assigned id, then the stitched summary line. With /// `--trace`, also persist each OOS member's streams under /// `runs/traces//oos/` (opt-in). Deterministic (C1). -fn run_walkforward(strategy: Strategy, name: &str, persist: bool, data: DataSource, grid: &Stage1RGrid, select: Selection) { +fn run_walkforward(strategy: Strategy, name: &str, persist: bool, data: DataSource, grid: &RGrid, select: Selection) { if persist && let Err(e) = TraceStore::open("runs").ensure_name_free(name, WriteKind::Family) { @@ -1739,10 +1739,10 @@ fn select_winner( /// windows. Each window sweeps a grid in-sample, optimizes by a metric, and runs /// the chosen params out-of-sample — both strategy-dispatched: the `SmaCross` arm /// sweeps the SMA sample grid and optimizes by `total_pips` (axis 2); the -/// `Stage1R` arm sweeps the stage1-r grid and optimizes by `sqn_normalized`. +/// `RSma` arm sweeps the r-sma grid and optimizes by `sqn_normalized`. /// Other strategies have no walk-forward form yet (exit 2). fn walkforward_family( - strategy: Strategy, trace: Option<&str>, data: &DataSource, grid: &Stage1RGrid, + strategy: Strategy, trace: Option<&str>, data: &DataSource, grid: &RGrid, select: Selection, ) -> WalkForwardResult { let span = data.wf_full_span(); @@ -1774,10 +1774,10 @@ fn walkforward_family( } }) } - Strategy::Stage1R => { - let space = stage1_r_space(); + Strategy::RSma => { + let space = r_sma_space(); walk_forward(roller, space, |w: WindowBounds| { - let (is_family, lattice) = stage1_r_sweep_over(w.is.0, w.is.1, data, grid); + let (is_family, lattice) = r_sma_sweep_over(w.is.0, w.is.1, data, grid); let (best, selection) = match select_winner(&is_family, "sqn_normalized", select, lattice.as_deref()) { Ok(v) => v, Err(msg) => { eprintln!("aura: {msg}"); std::process::exit(2); } @@ -1941,7 +1941,7 @@ fn walkforward_window_source(from: Timestamp, to: Timestamp) -> VecSource { /// helper (mirrors `sweep_report`). #[cfg(test)] fn walkforward_report() -> String { - let result = walkforward_family(Strategy::SmaCross, None, &DataSource::Synthetic, &Stage1RGrid::default(), Selection::Argmax); + let result = walkforward_family(Strategy::SmaCross, None, &DataSource::Synthetic, &RGrid::default(), Selection::Argmax); let mut out = String::new(); for w in &result.windows { out.push_str(&w.run.oos_report.to_json()); @@ -2028,7 +2028,7 @@ fn run_mc(name: &str, persist: bool) { /// Parsed form of the `mc` tail: either the synthetic seed-resweep (today's path, /// preserved byte-for-byte) or the real-candidate R-bootstrap. The R path accepts -/// ONLY `--strategy stage1-r` (the sole R-reporting walk-forward strategy); a bare +/// ONLY `--strategy r-sma` (the sole R-reporting walk-forward strategy); a bare /// `--real` without it is a usage error (the synthetic seed-resweep is undefined /// over real bars). // Parsed once at the dispatch boundary and immediately destructured into the run @@ -2037,25 +2037,25 @@ fn run_mc(name: &str, persist: bool) { #[derive(Clone, Debug, PartialEq)] enum McArgs { Synthetic { name: String, persist: bool }, - RealR { choice: DataChoice, grid: Stage1RGrid, block_len: usize, n_resamples: usize, seed: u64 }, + RealR { choice: DataChoice, grid: RGrid, block_len: usize, n_resamples: usize, seed: u64 }, } -/// `aura mc --strategy stage1-r [--real ]`: run the stage1-r walk-forward, pool +/// `aura mc --strategy r-sma [--real ]`: run the r-sma walk-forward, pool /// every OOS window's per-trade R series in roll order, and print one moving-block /// bootstrap `mc_r_bootstrap` line (`E[R]` distribution + P(`E[R]` <= 0)). Frictionless -/// Stage-1 (no costs); deterministic given `seed` (C1). -fn run_mc_r_bootstrap(data: DataSource, grid: &Stage1RGrid, block_len: usize, n_resamples: usize, seed: u64) { +/// gross R (no costs); deterministic given `seed` (C1). +fn run_mc_r_bootstrap(data: DataSource, grid: &RGrid, block_len: usize, n_resamples: usize, seed: u64) { println!("{}", mc_r_bootstrap_report(&data, grid, block_len, n_resamples, seed)); } -/// Assemble the `mc` R-bootstrap line: run the stage1-r walk-forward, pool the OOS +/// Assemble the `mc` R-bootstrap line: run the r-sma walk-forward, pool the OOS /// per-trade R series in roll order, bootstrap `E[R]`, and render the `mc_r_bootstrap` /// line. The body of `run_mc_r_bootstrap` minus the `println!`, so the full real-R /// wiring (walk-forward -> non-empty pooling -> bootstrap -> render) is reachable /// over synthetic data in a `#[cfg(test)]` unit, mirroring `mc_report` / /// `walkforward_report` / `sweep_report`. -fn mc_r_bootstrap_report(data: &DataSource, grid: &Stage1RGrid, block_len: usize, n_resamples: usize, seed: u64) -> String { - let result = walkforward_family(Strategy::Stage1R, None, data, grid, Selection::Argmax); +fn mc_r_bootstrap_report(data: &DataSource, grid: &RGrid, block_len: usize, n_resamples: usize, seed: u64) -> String { + let result = walkforward_family(Strategy::RSma, None, data, grid, Selection::Argmax); let pooled = pooled_oos_trade_rs(&result); let boot = r_bootstrap(&pooled, n_resamples, block_len, seed); mc_r_bootstrap_json(&boot) @@ -2235,8 +2235,8 @@ fn reproduce_family_in(reg: &Registry, id: &str, data: &DataSource) -> Reproduce std::process::exit(1); }) }; - // The param_space of the WRAPPED signal — its knobs carry the `stage1_r` - // wrapper's `stage1_signal.` node-path prefix, exactly the names the manifest + // The param_space of the WRAPPED signal — its knobs carry the `r_sma` + // wrapper's `sma_signal.` node-path prefix, exactly the names the manifest // recorded at write time. Mirrors `blueprint_sweep_family`'s probe so the // reproduce-side space name-matches the stored params (raw `signal.param_space()` // would drop the prefix and `point_from_params` could not find the knobs). @@ -2245,7 +2245,7 @@ fn reproduce_family_in(reg: &Registry, id: &str, data: &DataSource) -> Reproduce let (tx_r, _) = mpsc::channel(); let (tx_req, _) = mpsc::channel(); let space = - wrap_stage1r(reload(), tx_eq, tx_ex, tx_r, tx_req, false, true, None).param_space(); + wrap_r(reload(), tx_eq, tx_ex, tx_r, tx_req, false, true, None).param_space(); let point = point_from_params(&space, &stored.manifest.params); // A MonteCarlo member carries no tuning params (the params-join is empty), so its // reproduce line would print a BLANK member label; the seed IS its realization @@ -2442,32 +2442,32 @@ fn run_macd(trace: Option<&str>) -> RunReport { RunReport { manifest, metrics } } -// --- Stage-1 R harness (the SMA-cross signal scored in R) -------------------- +// --- r-sma harness (the SMA-cross signal scored in R) -------------------- -/// The stage1-r vol-stop EWMA length (cycles). Single source for the `StopRule::Vol` +/// The r-sma vol-stop EWMA length (cycles). Single source for the `StopRule::Vol` /// the blueprint embeds and the `stop` param the manifest records — kept honest by one /// constant instead of a hand-synced literal across the function boundary. -const STAGE1_R_STOP_LENGTH: i64 = 3; +const R_SMA_STOP_LENGTH: i64 = 3; -/// The stage1-r vol-stop multiplier (1R = `k`·σ). Single source for the embedded -/// `StopRule::Vol` and its manifest record, like [`STAGE1_R_STOP_LENGTH`]. -const STAGE1_R_STOP_K: f64 = 2.0; +/// The r-sma vol-stop multiplier (1R = `k`·σ). Single source for the embedded +/// `StopRule::Vol` and its manifest record, like [`R_SMA_STOP_LENGTH`]. +const R_SMA_STOP_K: f64 = 2.0; -/// The stage1-r demo signal SMA lengths (fast/slow), bound at single-run build time. -/// Single source for the graph that runs (`stage1_r_graph`), the signal that is hashed -/// (`stage1_signal` → `topology_hash`), and the recorded manifest params — so the +/// The r-sma demo signal SMA lengths (fast/slow), bound at single-run build time. +/// Single source for the graph that runs (`r_sma_graph`), the signal that is hashed +/// (`sma_signal` → `topology_hash`), and the recorded manifest params — so the /// hashed topology cannot drift from the executed one across the function boundary. -const STAGE1_R_SMA_FAST: i64 = 2; -const STAGE1_R_SMA_SLOW: i64 = 4; +const R_SMA_FAST: i64 = 2; +const R_SMA_SLOW: i64 = 4; -/// The stage1-r demo `Bias` scale (conviction magnitude). Single source for the -/// `Bias` node bound in `stage1_signal` (the hashed + executed signal) and the +/// The r-sma demo `Bias` scale (conviction magnitude). Single source for the +/// `Bias` node bound in `sma_signal` (the hashed + executed signal) and the /// recorded manifest param, so the hashed topology cannot drift from the recorded /// `bias_scale` across the function boundary — the same guard as the SMA lengths. -const STAGE1_R_BIAS_SCALE: f64 = 0.5; +const R_SMA_BIAS_SCALE: f64 = 0.5; /// Short-horizon realized-range window for vol-scaled slippage. Deliberately -/// distinct from `STAGE1_R_STOP_LENGTH` (3): scaling slippage by the stop's own +/// distinct from `R_SMA_STOP_LENGTH` (3): scaling slippage by the stop's own /// vol would collapse cost-in-R to a constant (spec 0082). Short enough to warm /// within the synthetic smoke fixture so the run path exercises non-zero slippage. const SLIP_VOL_LENGTH: i64 = 5; @@ -2488,10 +2488,10 @@ enum RunData { Real { symbol: String, from: Option, to: Option }, } -/// A rise-fall-rise synthetic stream for the stage1-r smoke run: long enough to warm +/// A rise-fall-rise synthetic stream for the r-sma smoke run: long enough to warm /// the `vol_stop(length=3)` and flip the SMA(2)/SMA(4) cross at least once, so the /// RiskExecutor opens and closes at least one trade. Deterministic (C1). -fn stage1_r_prices() -> Vec<(Timestamp, Scalar)> { +fn r_sma_prices() -> Vec<(Timestamp, Scalar)> { [ 1.0000_f64, 1.0008, 1.0021, 1.0039, 1.0062, 1.0090, 1.0083, 1.0061, 1.0034, 1.0012, 0.9998, 1.0006, 1.0024, 1.0047, 1.0069, 1.0086, 1.0097, 1.0092, @@ -2504,18 +2504,18 @@ fn stage1_r_prices() -> Vec<(Timestamp, Scalar)> { /// Interned `col[i]` recorder-port names, built once. The `GraphBuilder::input` API /// wants `&'static str`; interning here (instead of `format!(...).leak()` per field) -/// means reusing the stage1-r harness in a sweep / Monte-Carlo loop reuses these +/// means reusing the r-sma harness in a sweep / Monte-Carlo loop reuses these /// strings rather than leaking 14 fresh ones per build (#132). static COL_PORTS: LazyLock> = LazyLock::new(|| (0..PM_FIELD_NAMES.len()).map(|i| format!("col[{i}]")).collect()); -/// The Stage-1 signal leg as a standalone, serializable Composite: SMA-cross → +/// The r-sma signal leg as a standalone, serializable Composite: SMA-cross → /// Bias. Exposes a `price` input-role and a single `bias` output — the boundary /// shape a serialized blueprint round-trips (the `blueprint_serde_e2e.rs` /// template). The two SMA knobs are bound when `Some` (byte-identical to the old /// build-time bind) and left open as `fast.length` / `slow.length` when `None`. -fn stage1_signal(fast_len: Option, slow_len: Option) -> Composite { - let mut g = GraphBuilder::new("stage1_signal"); +fn sma_signal(fast_len: Option, slow_len: Option) -> Composite { + let mut g = GraphBuilder::new("sma_signal"); let mut fast_b = Sma::builder().named("fast"); if let Some(l) = fast_len { fast_b = fast_b.bind("length", Scalar::i64(l)); @@ -2530,7 +2530,7 @@ fn stage1_signal(fast_len: Option, slow_len: Option) -> Composite { let exposure = g.add( Bias::builder() .named("bias") - .bind("scale", Scalar::f64(STAGE1_R_BIAS_SCALE)), + .bind("scale", Scalar::f64(R_SMA_BIAS_SCALE)), ); let price = g.source_role("price", ScalarKind::F64); g.feed(price, vec![fast.input("series"), slow.input("series")]); @@ -2538,7 +2538,7 @@ fn stage1_signal(fast_len: Option, slow_len: Option) -> Composite { g.connect(slow.output("value"), spread.input("rhs")); g.connect(spread.output("value"), exposure.input("signal")); g.expose(exposure.output("bias"), "bias"); - g.build().expect("stage1 signal wiring resolves") + g.build().expect("sma_signal wiring resolves") } /// SHA256 (hex) of a canonical (#164) blueprint JSON string — the content id (#158). @@ -2558,12 +2558,12 @@ fn topology_hash(signal: &Composite) -> String { content_id(&blueprint_to_json(signal).expect("a buildable signal serializes")) } -/// The Stage-1 R harness topology, shared by the single run and the sweep. The two +/// The r-sma harness topology, shared by the single run and the sweep. The two /// signal knobs are bound when `Some` (single run — identical to the old /// build-time bind, output byte-unchanged) and left free when `None` (sweep — they -/// now nest in the `stage1_signal` composite, so they land in `param_space` as -/// `stage1_signal.fast.length` / `stage1_signal.slow.length`, rendered back to the -/// bare `fast.length` / `slow.length` manifest names by `stage1_r_friendly_name`). +/// now nest in the `sma_signal` composite, so they land in `param_space` as +/// `sma_signal.fast.length` / `sma_signal.slow.length`, rendered back to the +/// bare `fast.length` / `slow.length` manifest names by `r_sma_friendly_name`). /// The vol-stop knobs are /// bound to the pinned constants when `stop_open` is `false` (single run + the /// default sweep, output byte-unchanged) and left free when `true` (a gridded sweep @@ -2580,7 +2580,7 @@ fn topology_hash(signal: &Composite) -> String { /// an edge, trading the lint for indirection; the `too_many_arguments` allow is /// preferred over that churn. #[allow(clippy::type_complexity, clippy::too_many_arguments)] -fn stage1_r_graph( +fn r_sma_graph( tx_eq: mpsc::Sender<(Timestamp, Vec)>, tx_ex: mpsc::Sender<(Timestamp, Vec)>, tx_r: mpsc::Sender<(Timestamp, Vec)>, @@ -2591,8 +2591,8 @@ fn stage1_r_graph( reduce: bool, cost: Option<(CostConfig, mpsc::Sender<(Timestamp, Vec)>, mpsc::Sender<(Timestamp, Vec)>)>, ) -> Composite { - wrap_stage1r( - stage1_signal(fast_len, slow_len), + wrap_r( + sma_signal(fast_len, slow_len), tx_eq, tx_ex, tx_r, @@ -2603,14 +2603,14 @@ fn stage1_r_graph( ) } -/// Wrap a Stage-1 `signal` composite (a `price`→`bias` leg) in the Stage-1 R run +/// Wrap a `signal` composite (a `price`→`bias` leg) in the R run /// scaffolding: pip broker, the per-tap recorders, the vol-stop RiskExecutor, the /// r_equity / cost legs. The signal is nested and its `price`/`bias` boundary is -/// wired across; everything else is verbatim from the old `stage1_r_graph` body, so +/// wired across; everything else is verbatim from the old `r_sma_graph` body, so /// a serialized signal loaded via `blueprint_from_json` runs through exactly the /// scaffolding the Rust-built signal does. #[allow(clippy::type_complexity, clippy::too_many_arguments)] -fn wrap_stage1r( +fn wrap_r( signal: Composite, tx_eq: mpsc::Sender<(Timestamp, Vec)>, tx_ex: mpsc::Sender<(Timestamp, Vec)>, @@ -2624,7 +2624,7 @@ fn wrap_stage1r( mpsc::Sender<(Timestamp, Vec)>, )>, ) -> Composite { - let mut g = GraphBuilder::new("stage1_r"); + let mut g = GraphBuilder::new("r_sma"); // SMA-cross signal → Bias, nested as a serializable `price`→`bias` leg. let sig = g.add(BlueprintNode::Composite(signal)); // pip branch (verbatim from sample_blueprint_with_sinks). @@ -2652,7 +2652,7 @@ fn wrap_stage1r( let exec = g.add(if stop_open { risk_executor_vol_open(1.0) } else { - risk_executor(StopRule::Vol { length: STAGE1_R_STOP_LENGTH, k: STAGE1_R_STOP_K }, 1.0) + risk_executor(StopRule::Vol { length: R_SMA_STOP_LENGTH, k: R_SMA_STOP_K }, 1.0) }); let rrec = if reduce { g.add(GatedRecorder::builder(PM_RECORD_KINDS.to_vec(), gate_col, Firing::Any, tx_r)) @@ -2756,13 +2756,13 @@ fn wrap_stage1r( g.connect(cg.output("open_cost_in_r"), cost_rec.input("col[2]")); } } - g.build().expect("stage1_r wiring resolves") + g.build().expect("r_sma wiring resolves") } /// Resolve a `RunData` selector to the `(sources, window, pip_size)` triple the -/// stage1-r run paths feed to the harness: the built-in synthetic Stage-1-R stream, +/// r-sma run paths feed to the harness: the built-in synthetic R stream, /// or a lazily-streamed real M1 close source (with its sidecar pip + probed window). -/// One definition shared by `run_stage1_r` and `run_signal_stage1r` so the +/// One definition shared by `run_r_sma` and `run_signal_r` so the /// source/window/pip wiring cannot drift between the two paths. #[allow(clippy::type_complexity)] fn resolve_run_data( @@ -2775,7 +2775,7 @@ fn resolve_run_data( match data { RunData::Synthetic => { let sources: Vec> = - vec![Box::new(VecSource::new(stage1_r_prices()))]; + vec![Box::new(VecSource::new(r_sma_prices()))]; let window = window_of(&sources).expect("non-empty synthetic stream"); (sources, window, SYNTHETIC_PIP_SIZE) } @@ -2786,12 +2786,12 @@ fn resolve_run_data( } } -/// Run a signal blueprint through the Stage-1-R scaffolding: hash the signal, +/// Run a signal blueprint through the R scaffolding: hash the signal, /// wrap it (broker + equity/exposure/R sinks), compile with `params`, bootstrap, /// run over `data`, and build the RunReport (manifest carries topology_hash). /// The single construction+run path shared by the `aura run ` CLI /// arm and its bit-identical test. -fn run_signal_stage1r(signal: Composite, params: &[Scalar], data: RunData, seed: u64) -> RunReport { +fn run_signal_r(signal: Composite, params: &[Scalar], data: RunData, seed: u64) -> RunReport { let topo = topology_hash(&signal); // before signal is consumed let names: Vec = signal .param_space() @@ -2804,11 +2804,11 @@ fn run_signal_stage1r(signal: Composite, params: &[Scalar], data: RunData, seed: // The req tap (r_equity recorder) is wired but not persisted on this path; keep the // receiver alive so the sink's sends do not fail, but do not drain it. let (tx_req, _rx_req) = mpsc::channel(); - let wrapped = wrap_stage1r(signal, tx_eq, tx_ex, tx_r, tx_req, false, false, None); + let wrapped = wrap_r(signal, tx_eq, tx_ex, tx_r, tx_req, false, false, None); let flat = wrapped .compile_with_params(params) .expect("signal binds + wraps to a valid harness"); - let mut h = Harness::bootstrap(flat).expect("valid stage1-r harness"); + let mut h = Harness::bootstrap(flat).expect("valid r-sma harness"); let (sources, window, pip_size) = resolve_run_data(&data); h.run(sources); let eq_rows: Vec<(Timestamp, Vec)> = rx_eq.try_iter().collect(); @@ -2817,7 +2817,7 @@ fn run_signal_stage1r(signal: Composite, params: &[Scalar], data: RunData, seed: let named_params: Vec<(String, Scalar)> = names.into_iter().zip(params.iter().copied()).collect(); let mut manifest = sim_optimal_manifest(named_params, window, seed, pip_size); - manifest.broker = stage1_r_broker_label(pip_size); + manifest.broker = r_sma_broker_label(pip_size); manifest.topology_hash = Some(topo); let mut metrics = summarize(&f64_field(&eq_rows, 0), &f64_field(&ex_rows, 0)); metrics.r = Some(summarize_r(&r_rows, &[])); @@ -2845,13 +2845,13 @@ fn run_blueprint_member( let (tx_ex, rx_ex) = mpsc::channel(); let (tx_r, rx_r) = mpsc::channel(); let (tx_req, _rx_req) = mpsc::channel(); - let mut h = wrap_stage1r(signal, tx_eq, tx_ex, tx_r, tx_req, false, true, None) + let mut h = wrap_r(signal, tx_eq, tx_ex, tx_r, tx_req, false, true, None) .bootstrap_with_cells(point) .expect("member bootstraps (point kind-checked against param_space)"); h.run(sources); let named = zip_params(space, point); // by-name params for the manifest record let mut manifest = sim_optimal_manifest(named, window, seed, pip); - manifest.broker = stage1_r_broker_label(pip); + manifest.broker = r_sma_broker_label(pip); manifest.topology_hash = Some(topo.to_string()); let r_rows: Vec<(Timestamp, Vec)> = rx_r.try_iter().collect(); let (total_pips, max_drawdown) = rx_eq @@ -2867,7 +2867,7 @@ fn run_blueprint_member( } /// The exact wrapped probe the loaded-blueprint sweep resolves its axes -/// against: the loaded signal wrapped in the stage1-r scaffolding (stop bound, +/// against: the loaded signal wrapped in the r-sma scaffolding (stop bound, /// reduce, no cost), taps discarded. `param_space()` on it is the axis /// namespace `--axis` binds; `.axis()` consumes it to seed a sweep. Single /// source for the sweep terminal, the MC closed-check, AND `--list-axes`, so @@ -2884,7 +2884,7 @@ fn blueprint_axis_probe(doc: &str) -> Composite { let (tx_ex, _) = mpsc::channel(); let (tx_r, _) = mpsc::channel(); let (tx_req, _) = mpsc::channel(); - wrap_stage1r(signal, tx_eq, tx_ex, tx_r, tx_req, false, true, None) + wrap_r(signal, tx_eq, tx_ex, tx_r, tx_req, false, true, None) } /// `aura sweep --list-axes`: one `:` line per open @@ -2897,11 +2897,11 @@ fn list_blueprint_axes(doc: &str) { } /// Sweep a serialized signal `doc` over user-named param-space axes — the structural -/// twin of [`stage1_r_sweep_family`], with three deviations. (1) The signal source is -/// `wrap_stage1r(blueprint_from_json(doc))` — a loaded blueprint, not the Rust-built -/// stage1-r graph. (2) The signal is RE-loaded from `doc` per member (a `Composite` is +/// twin of [`r_sma_sweep_family`], with three deviations. (1) The signal source is +/// `wrap_r(blueprint_from_json(doc))` — a loaded blueprint, not the Rust-built +/// r-sma graph. (2) The signal is RE-loaded from `doc` per member (a `Composite` is /// `!Clone`, so the throwaway param-space probe and each grid point each reload). (3) -/// The axes are taken verbatim BY NAME (not the four suffix-resolved stage1-r knobs): +/// The axes are taken verbatim BY NAME (not the four suffix-resolved r-sma knobs): /// each `(name, vals)` is fed straight to the `SweepBinder`, so an unknown name or a /// kind mismatch surfaces as the sweep terminal's [`BindError`], rendered to a message /// string — a named error, never a panic. A FULLY BOUND (closed) blueprint has an empty @@ -2964,7 +2964,7 @@ fn blueprint_sweep_family( } /// Sweep the LOADED blueprint over the user `--axis` grid on an in-sample window -/// `[from,to]` — the windowed, lattice-carrying twin of `stage1_r_sweep_over` and +/// `[from,to]` — the windowed, lattice-carrying twin of `r_sma_sweep_over` and /// `blueprint_sweep_family`. `sweep_with_lattice` gives the grid lattice `--select /// plateau` needs. An unknown/kind-mismatched axis surfaces as `BindError` at the /// sweep terminal (no panic, no hidden exit) for the caller to render. @@ -3011,7 +3011,7 @@ fn run_oos_blueprint( /// The loaded-blueprint IS-refit walk-forward: per IS window, re-optimize the /// blueprint over the user `--axis` grid, select by `sqn_normalized`, run the -/// winner OOS. Structural twin of `walkforward_family`'s `Stage1R` arm — the +/// winner OOS. Structural twin of `walkforward_family`'s `RSma` arm — the /// generic `walk_forward` driver + `select_winner` reused; only the per-window /// sweep/OOS source the loaded blueprint. In-closure errors (a bad `--axis`) /// `exit(2)` with the sweep terminal's message, as the hard-wired arm does. @@ -3064,7 +3064,7 @@ fn blueprint_walkforward_family( /// pattern (a distinct realization per seed). A FIXED `SyntheticSpec` shared by the /// `aura mc ` persist path AND the reproduce MonteCarlo branch, so the /// seed->walk reconstruction is bit-exact (C1). Length 60 comfortably warms the loaded -/// stage1-r graph (SMA slow=4 + the len-3 vol stop) so draws produce differing trades. +/// r-sma graph (SMA slow=4 + the len-3 vol stop) so draws produce differing trades. fn synthetic_walk_sources(seed: u64) -> Vec> { let spec = SyntheticSpec { start: 1.0, len: 60, step: 1 }; vec![Box::new(spec.source(seed))] @@ -3258,14 +3258,14 @@ fn run_blueprint_mc(doc: &str, n_seeds: u64, name: &str, data: DataSource) { println!("{}", mc_aggregate_json(&family.aggregate)); } -/// The stage1-r harness with its signal leg swapped for a Donchian channel breakout: +/// The r-sma harness with its signal leg swapped for a Donchian channel breakout: /// `close -> Delay(1) -> {RollingMax,RollingMin}(channel) -> {Gt,Gt} -> {Latch,Latch} /// -> Sub = bias in {-1,0,+1}`. The one Delay(1) on close feeds both rolling nodes, so /// each channel covers `close[t-N..t-1]` (the C2 guard: the current bar is excluded). /// `channel = None` leaves the lengths open (unused today; the family binds them); the -/// stop is bound (defines R), identical downstream to stage1_r_graph. +/// stop is bound (defines R), identical downstream to r_sma_graph. #[allow(clippy::type_complexity, clippy::too_many_arguments)] -fn stage1_breakout_graph( +fn r_breakout_graph( tx_eq: mpsc::Sender<(Timestamp, Vec)>, tx_ex: mpsc::Sender<(Timestamp, Vec)>, tx_r: mpsc::Sender<(Timestamp, Vec)>, @@ -3275,7 +3275,7 @@ fn stage1_breakout_graph( stop_k: f64, reduce: bool, ) -> Composite { - let mut g = GraphBuilder::new("stage1_breakout"); + let mut g = GraphBuilder::new("r_breakout"); // Donchian breakout signal leg. let delay = g.add(Delay::builder().bind("lag", Scalar::i64(1))); let mut mx_b = RollingMax::builder().named("channel_hi"); @@ -3291,7 +3291,7 @@ fn stage1_breakout_graph( let up_latch = g.add(Latch::builder()); let down_latch = g.add(Latch::builder()); let exposure = g.add(Sub::builder()); // up_latch - down_latch -> bias in {-1,0,+1} - // pip branch (verbatim from stage1_r_graph). + // pip branch (verbatim from r_sma_graph). let broker = g.add(SimBroker::builder(SYNTHETIC_PIP_SIZE)); let gate_col = PM_FIELD_NAMES .iter() @@ -3352,20 +3352,20 @@ fn stage1_breakout_graph( g.connect(exec.output("unrealized_r"), r_equity.input("term[1]")); g.connect(r_equity.output("value"), req.input("col[0]")); } - g.build().expect("stage1_breakout wiring resolves") + g.build().expect("r_breakout wiring resolves") } -/// The Stage-1 EWMA Bollinger-band mean-reversion candidate, mirroring -/// `stage1_breakout_graph` but swapping the signal leg: fade deviation from a +/// The EWMA Bollinger-band mean-reversion candidate, mirroring +/// `r_breakout_graph` but swapping the signal leg: fade deviation from a /// rolling mean (price above `mean + k*sigma` -> short; below `mean - k*sigma` -> /// long), latched +-1. sigma = `Sqrt(Ema((price-mean)^2))` (deviation squared /// then smoothed, the vol_stop shape — no catastrophic cancellation, no NaN). /// No Delay: the current bar legitimately belongs to its own band (causal, C2). /// `window` gangs the mean Ema and the variance Ema (one Bollinger window); /// `band_k` is the band half-width in sigma. Everything below the signal leg is -/// byte-identical to `stage1_breakout_graph`. +/// byte-identical to `r_breakout_graph`. #[allow(clippy::type_complexity, clippy::too_many_arguments)] -fn stage1_meanrev_graph( +fn r_meanrev_graph( tx_eq: mpsc::Sender<(Timestamp, Vec)>, tx_ex: mpsc::Sender<(Timestamp, Vec)>, tx_r: mpsc::Sender<(Timestamp, Vec)>, @@ -3376,7 +3376,7 @@ fn stage1_meanrev_graph( stop_k: f64, reduce: bool, ) -> Composite { - let mut g = GraphBuilder::new("stage1_meanrev"); + let mut g = GraphBuilder::new("r_meanrev"); // EWMA Bollinger-band mean-reversion signal leg (the ONLY change vs breakout). let (mut mean_b, mut var_b) = (Ema::builder().named("mean_window"), Ema::builder().named("var_window")); @@ -3397,7 +3397,7 @@ fn stage1_meanrev_graph( let short_latch = g.add(Latch::builder()); let long_latch = g.add(Latch::builder()); let exposure = g.add(Sub::builder()); // long_latch - short_latch -> bias in {-1,0,+1} - // pip branch (VERBATIM from stage1_breakout_graph). + // pip branch (VERBATIM from r_breakout_graph). let broker = g.add(SimBroker::builder(SYNTHETIC_PIP_SIZE)); let gate_col = PM_FIELD_NAMES .iter() @@ -3467,17 +3467,17 @@ fn stage1_meanrev_graph( g.connect(exec.output("unrealized_r"), r_equity.input("term[1]")); g.connect(r_equity.output("value"), req.input("col[0]")); } - g.build().expect("stage1_meanrev wiring resolves") + g.build().expect("r_meanrev wiring resolves") } -/// `aura run --harness stage1-r [--real [--from][--to]] [--trace ]`: build the -/// dual-tap stage1-r harness, run it on synthetic or real M1 data, fold the pip taps via +/// `aura run --harness r-sma [--real [--from][--to]] [--trace ]`: build the +/// dual-tap r-sma harness, run it on synthetic or real M1 data, fold the pip taps via /// `summarize` and the dense R-record via `summarize_r`, and attach the R block as /// `RunMetrics.r = Some(..)`. Pure/deterministic (C1). `cost` is the optional flat /// round-trip cost per trade (price units): `Some(c)` wires the `ConstantCost` node and /// the `net_r_equity` tap and folds the cost stream into `net_expectancy_r`; `None` is the /// frictionless gross-R baseline (net == gross, no extra tap, byte-unchanged). -fn run_stage1_r( +fn run_r_sma( data: RunData, trace: Option<&str>, const_cost: Option, @@ -3510,20 +3510,20 @@ fn run_stage1_r( } else { None }; - let flat = stage1_r_graph( + let flat = r_sma_graph( tx_eq, tx_ex, tx_r, tx_req, - Some(STAGE1_R_SMA_FAST), - Some(STAGE1_R_SMA_SLOW), + Some(R_SMA_FAST), + Some(R_SMA_SLOW), false, false, cost_bundle, ) .compile_with_params(&[]) - .expect("valid stage1-r blueprint"); - let mut h = Harness::bootstrap(flat).expect("valid stage1-r harness"); + .expect("valid r-sma blueprint"); + let mut h = Harness::bootstrap(flat).expect("valid r-sma harness"); let (sources, window, pip_size) = resolve_run_data(&data); h.run(sources); @@ -3540,20 +3540,20 @@ fn run_stage1_r( let mut manifest = sim_optimal_manifest( vec![ - ("sma_fast".to_string(), Scalar::i64(STAGE1_R_SMA_FAST)), - ("sma_slow".to_string(), Scalar::i64(STAGE1_R_SMA_SLOW)), - ("bias_scale".to_string(), Scalar::f64(STAGE1_R_BIAS_SCALE)), - ("stop_length".to_string(), Scalar::i64(STAGE1_R_STOP_LENGTH)), // vol_stop EWMA length - ("stop_k".to_string(), Scalar::f64(STAGE1_R_STOP_K)), // vol_stop multiplier (1R = k·σ) + ("sma_fast".to_string(), Scalar::i64(R_SMA_FAST)), + ("sma_slow".to_string(), Scalar::i64(R_SMA_SLOW)), + ("bias_scale".to_string(), Scalar::f64(R_SMA_BIAS_SCALE)), + ("stop_length".to_string(), Scalar::i64(R_SMA_STOP_LENGTH)), // vol_stop EWMA length + ("stop_k".to_string(), Scalar::f64(R_SMA_STOP_K)), // vol_stop multiplier (1R = k·σ) ], window, 0, pip_size, ); - manifest.broker = stage1_r_broker_label(pip_size); - manifest.topology_hash = Some(topology_hash(&stage1_signal( - Some(STAGE1_R_SMA_FAST), - Some(STAGE1_R_SMA_SLOW), + manifest.broker = r_sma_broker_label(pip_size); + manifest.topology_hash = Some(topology_hash(&sma_signal( + Some(R_SMA_FAST), + Some(R_SMA_SLOW), ))); if let Some(name) = trace { persist_traces_r(name, &manifest, &eq_rows, &ex_rows, &req_rows, &net_rows); @@ -3563,7 +3563,7 @@ fn run_stage1_r( RunReport { manifest, metrics } } -/// Persist a stage1-r run's taps: equity (off the SimBroker), exposure (off the Bias), +/// Persist a r-sma run's taps: equity (off the SimBroker), exposure (off the Bias), /// r_equity = cum_realized_r + unrealized_r (off the RiskExecutor), and — only on a cost /// run — net_r_equity (gross r_equity minus the cost-in-R taps). Separate from the two-tap /// `persist_traces` so the pip handlers stay byte-unchanged on disk; the `net_r_equity` tap @@ -3598,11 +3598,11 @@ fn persist_traces_r( enum HarnessKind { Sma, Macd, - Stage1R, + RSma, } /// The parsed `aura run` invocation: a harness, a data source, an optional trace name, and -/// an optional flat round-trip cost per trade (`--cost-per-trade`, stage1-r only this cycle). +/// an optional flat round-trip cost per trade (`--cost-per-trade`, r-sma only this cycle). #[derive(Debug)] struct RunArgs { harness: HarnessKind, @@ -3643,7 +3643,7 @@ fn run_dispatch(args: RunArgs) -> Result { Ok(match (args.harness, args.data) { (HarnessKind::Sma, RunData::Synthetic) => run_sample(trace), (HarnessKind::Macd, RunData::Synthetic) => run_macd(trace), - (HarnessKind::Stage1R, data) => run_stage1_r(data, trace, args.cost, args.slip_vol_mult, args.carry_per_cycle), + (HarnessKind::RSma, data) => run_r_sma(data, trace, args.cost, args.slip_vol_mult, args.carry_per_cycle), (HarnessKind::Sma, RunData::Real { symbol, from, to }) => { run_sample_real(&symbol, from, to, trace) } @@ -3657,7 +3657,7 @@ fn run_dispatch(args: RunArgs) -> Result { // The declarative argument grammar. clap owns argv tokenizing, scoped `--help`, // `--version`, `--flag=value`, `--`, and long-option abbreviation; the `dispatch_*` // handlers below convert each `*Cmd` into the argument shapes the existing execution -// fns accept, reusing the value helpers (`Strategy`, `Stage1RGrid`, `Selection`, +// fns accept, reusing the value helpers (`Strategy`, `RGrid`, `Selection`, // `DataSource::from_choice`, `parse_scalar_csv`, `parse_csv_list`, `parse_select`, // `parse_param_cells`). The four dual-grammar subcommands carry an optional // `[blueprint]` positional; a first-positional that names an existing `.json` file @@ -3739,7 +3739,7 @@ struct GraphIntrospectCmd { #[derive(Args)] struct GeneralizeCmd { - /// The candidate strategy; must be stage1-r (the candidate must produce R). + /// The candidate strategy; must be r-sma (the candidate must produce R). #[arg(long)] strategy: Option, /// Comma-separated instrument list (>=2 distinct, required). @@ -3802,7 +3802,7 @@ struct RunCmd { /// A serialized signal blueprint (.json). An existing file selects the /// loaded-blueprint grammar; otherwise the built-in harness grammar. blueprint: Option, - /// Built-in harness: sma | macd | stage1-r (default sma). + /// Built-in harness: sma | macd | r-sma (default sma). #[arg(long)] harness: Option, /// Blueprint params (JSON scalar-cell array; .json mode). @@ -3827,11 +3827,11 @@ struct RunCmd { // the flag value so the non-negativity guard (`run_args_from`) can surface the // named `must be non-negative` refusal, rather than clap rejecting `-0.5` as an // unknown option before the value is parsed. - #[arg(long, allow_hyphen_values = true, long_help = "per-trade cost in price units. stage1-r only; charged in R as cost/|entry-stop|.")] + #[arg(long, allow_hyphen_values = true, long_help = "per-trade cost in price units. r-sma only; charged in R as cost/|entry-stop|.")] cost_per_trade: Option, - #[arg(long, allow_hyphen_values = true, long_help = "slippage multiplier on realized vol. stage1-r only.")] + #[arg(long, allow_hyphen_values = true, long_help = "slippage multiplier on realized vol. r-sma only.")] slip_vol_mult: Option, - #[arg(long, allow_hyphen_values = true, long_help = "carry in price units per ENGINE cycle (not per day, not an overnight swap). stage1-r only.")] + #[arg(long, allow_hyphen_values = true, long_help = "carry in price units per ENGINE cycle (not per day, not an overnight swap). r-sma only.")] carry_per_cycle: Option, } @@ -3839,7 +3839,7 @@ struct RunCmd { struct SweepCmd { /// A loaded blueprint (.json); omit for the built-in --strategy grammar. blueprint: Option, - /// Built-in strategy: sma | momentum | stage1-r | stage1-breakout | stage1-meanrev (default sma). + /// Built-in strategy: sma | momentum | r-sma | r-breakout | r-meanrev (default sma). #[arg(long)] strategy: Option, /// Real instrument symbol to sweep over (recorded data); omit for the synthetic stream. @@ -3869,13 +3869,13 @@ struct SweepCmd { /// Stop-k grid axis (comma-separated values). #[arg(long)] stop_k: Option, - /// Breakout-channel grid axis (comma-separated values; stage1-breakout). + /// Breakout-channel grid axis (comma-separated values; r-breakout). #[arg(long)] channel: Option, - /// Mean-reversion window grid axis (comma-separated values; stage1-meanrev). + /// Mean-reversion window grid axis (comma-separated values; r-meanrev). #[arg(long)] window: Option, - /// Mean-reversion band-k grid axis (comma-separated values; stage1-meanrev). + /// Mean-reversion band-k grid axis (comma-separated values; r-meanrev). #[arg(long)] band_k: Option, /// Blueprint sweep axis `=` (repeatable; .json mode). @@ -3890,7 +3890,7 @@ struct SweepCmd { struct WalkforwardCmd { /// A loaded blueprint (.json); omit for the built-in --strategy grammar. blueprint: Option, - /// Built-in strategy: sma | momentum | stage1-r | stage1-breakout | stage1-meanrev (default sma). + /// Built-in strategy: sma | momentum | r-sma | r-breakout | r-meanrev (default sma). #[arg(long)] strategy: Option, /// Real instrument symbol to validate over (recorded data); omit for the synthetic stream. @@ -3932,10 +3932,10 @@ struct WalkforwardCmd { struct McCmd { /// A loaded blueprint (.json); omit for the built-in grammar. blueprint: Option, - /// Built-in strategy: stage1-r selects the R-bootstrap path (else the synthetic seed-resweep). + /// Built-in strategy: r-sma selects the R-bootstrap path (else the synthetic seed-resweep). #[arg(long)] strategy: Option, - /// Real instrument symbol for the R-bootstrap (recorded data); requires --strategy stage1-r. + /// Real instrument symbol for the R-bootstrap (recorded data); requires --strategy r-sma. #[arg(long)] real: Option, /// Window start (Unix ms, inclusive); requires --real. @@ -4009,7 +4009,7 @@ fn run_data_from(real: Option<&str>, from: Option, to: Option) -> RunD /// is an unexpected token; the harness-enum map, the cost-flags-require-R-harness /// guard, and the non-negative-rate checks reuse the existing message strings. fn run_args_from(a: &RunCmd) -> Result { - let usage = || "Usage: aura run [--harness ] [--real [--from ] [--to ]] [--trace ] [--cost-per-trade ] [--slip-vol-mult ] [--carry-per-cycle ]".to_string(); + let usage = || "Usage: aura run [--harness ] [--real [--from ] [--to ]] [--trace ] [--cost-per-trade ] [--slip-vol-mult ] [--carry-per-cycle ]".to_string(); // A positional that is not an existing `.json` blueprint is an unexpected token // (the built-in run grammar takes only flags) — the #16 strict reading. if a.blueprint.is_some() { @@ -4018,7 +4018,7 @@ fn run_args_from(a: &RunCmd) -> Result { let harness = match a.harness.as_deref() { None | Some("sma") => HarnessKind::Sma, Some("macd") => HarnessKind::Macd, - Some("stage1-r") => HarnessKind::Stage1R, + Some("r-sma") => HarnessKind::RSma, Some(_) => return Err(usage()), }; // A parsed-but-negative rate is a named refusal (a sign typo distinguished from a @@ -4033,11 +4033,11 @@ fn run_args_from(a: &RunCmd) -> Result { let cost = nonneg("--cost-per-trade", a.cost_per_trade)?; let slip_vol_mult = nonneg("--slip-vol-mult", a.slip_vol_mult)?; let carry_per_cycle = nonneg("--carry-per-cycle", a.carry_per_cycle)?; - if !matches!(harness, HarnessKind::Stage1R) + if !matches!(harness, HarnessKind::RSma) && (cost.is_some() || slip_vol_mult.is_some() || carry_per_cycle.is_some()) { return Err( - "cost flags require an R-evaluator harness (stage1-r); \ + "cost flags require an R-evaluator harness (r-sma); \ --harness sma/macd produces no R to charge against" .to_string(), ); @@ -4060,11 +4060,11 @@ fn run_args_from(a: &RunCmd) -> Result { #[allow(clippy::type_complexity)] fn generalize_args_from( a: &GeneralizeCmd, -) -> Result<(String, Vec, Stage1RGrid, String, Option, Option), String> { +) -> Result<(String, Vec, RGrid, String, Option, Option), String> { if let Some(s) = a.strategy.as_deref() - && s != "stage1-r" + && s != "r-sma" { - return Err("generalize requires --strategy stage1-r (the candidate must produce R)".to_string()); + return Err("generalize requires --strategy r-sma (the candidate must produce R)".to_string()); } let symbols: Vec = match a.real.as_deref() { None => return Err("generalize requires --real — a comma list of two or more instruments".to_string()), @@ -4087,12 +4087,12 @@ fn generalize_args_from( return Err("generalize: each instrument may appear once; --real has a duplicate symbol".to_string()); } let knobs = || "generalize requires all four candidate knobs: --fast --slow --stop-length --stop-k, each a single value".to_string(); - let grid = Stage1RGrid { + let grid = RGrid { fast: vec![a.fast.ok_or_else(knobs)?], slow: vec![a.slow.ok_or_else(knobs)?], stop_length: vec![a.stop_length.ok_or_else(knobs)?], stop_k: vec![a.stop_k.ok_or_else(knobs)?], - ..Stage1RGrid::default() + ..RGrid::default() }; let metric = a.metric.clone().unwrap_or_else(|| "expectancy_r".to_string()); let name = a.name.clone().unwrap_or_else(|| "generalize".to_string()); @@ -4123,9 +4123,9 @@ fn strategy_from(s: Option<&str>, usage: &impl Fn() -> String) -> Result Strategy::SmaCross, Some("momentum") => Strategy::Momentum, - Some("stage1-r") => Strategy::Stage1R, - Some("stage1-breakout") => Strategy::Stage1Breakout, - Some("stage1-meanrev") => Strategy::Stage1MeanRev, + Some("r-sma") => Strategy::RSma, + Some("r-breakout") => Strategy::RBreakout, + Some("r-meanrev") => Strategy::RMeanRev, Some(_) => return Err(usage()), }) } @@ -4215,7 +4215,7 @@ fn dispatch_run(a: RunCmd) { None => Vec::new(), }; let data = run_data_from(a.real.as_deref(), a.from, a.to); - let report = run_signal_stage1r(signal, ¶ms, data, a.seed.unwrap_or(0)); + let report = run_signal_r(signal, ¶ms, data, a.seed.unwrap_or(0)); println!("{}", report.to_json()); } None => { @@ -4343,7 +4343,7 @@ fn dispatch_sweep(a: SweepCmd) { run_blueprint_sweep(&doc, &axes, &name, persist, DataSource::from_choice(data)); } None => { - let usage = || "sweep [--strategy ] [--real [--from ] [--to ]] [--name | --trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--channel ] [--window ] [--band-k ]".to_string(); + let usage = || "sweep [--strategy ] [--real [--from ] [--to ]] [--name | --trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--channel ] [--window ] [--band-k ]".to_string(); if a.blueprint.is_some() || !a.axis.is_empty() || a.list_axes { eprintln!("aura: {}", usage()); std::process::exit(2); @@ -4352,7 +4352,7 @@ fn dispatch_sweep(a: SweepCmd) { eprintln!("aura: {m}"); std::process::exit(2); }); - let mut grid = Stage1RGrid::default(); + let mut grid = RGrid::default(); let bad = |m: String| -> ! { eprintln!("aura: {m}"); std::process::exit(2) @@ -4421,7 +4421,7 @@ fn dispatch_walkforward(a: WalkforwardCmd) { run_blueprint_walkforward(&doc, &axes, &name, DataSource::Synthetic, select); } None => { - let usage = || "walkforward [--strategy ] [--real [--from ] [--to ]] [--name | --trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--select ]".to_string(); + let usage = || "walkforward [--strategy ] [--real [--from ] [--to ]] [--name | --trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--select ]".to_string(); if a.blueprint.is_some() || !a.axis.is_empty() { eprintln!("aura: {}", usage()); std::process::exit(2); @@ -4430,7 +4430,7 @@ fn dispatch_walkforward(a: WalkforwardCmd) { eprintln!("aura: {m}"); std::process::exit(2); }); - let mut grid = Stage1RGrid::default(); + let mut grid = RGrid::default(); let bad = |m: String| -> ! { eprintln!("aura: {m}"); std::process::exit(2) @@ -4458,7 +4458,7 @@ fn dispatch_walkforward(a: WalkforwardCmd) { } /// `aura mc`: loaded-blueprint Monte-Carlo (an existing `.json` first-positional), or -/// the built-in synthetic seed-resweep / stage1-r R-bootstrap split. +/// the built-in synthetic seed-resweep / r-sma R-bootstrap split. fn dispatch_mc(a: McCmd) { match is_blueprint_file(&a.blueprint) { Some(path) => { @@ -4500,12 +4500,12 @@ fn dispatch_mc(a: McCmd) { run_blueprint_mc(&doc, n_seeds, &name, DataSource::Synthetic); } None => { - let usage = || "usage: mc [--name |--trace ] | mc --strategy stage1-r [--real [--from ] [--to ]] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--block-len ] [--resamples ] [--seed ]".to_string(); + let usage = || "usage: mc [--name |--trace ] | mc --strategy r-sma [--real [--from ] [--to ]] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--block-len ] [--resamples ] [--seed ]".to_string(); if a.blueprint.is_some() || a.seeds.is_some() { eprintln!("aura: {}", usage()); std::process::exit(2); } - // The R-bootstrap path is selected by any stage1-r knob; otherwise the + // The R-bootstrap path is selected by any r-sma knob; otherwise the // synthetic seed-resweep family. Name flags are invalid on the R path. let r_path = a.strategy.is_some() || a.real.is_some() @@ -4519,11 +4519,11 @@ fn dispatch_mc(a: McCmd) { || a.resamples.is_some() || a.seed.is_some(); let mc_args = if r_path { - if a.strategy.as_deref() != Some("stage1-r") || a.name.is_some() || a.trace.is_some() { + if a.strategy.as_deref() != Some("r-sma") || a.name.is_some() || a.trace.is_some() { eprintln!("aura: {}", usage()); std::process::exit(2); } - let mut grid = Stage1RGrid::default(); + let mut grid = RGrid::default(); let bad = |m: String| -> ! { eprintln!("aura: {m}"); std::process::exit(2) @@ -5095,7 +5095,7 @@ mod tests { .append_family( "walkforward", FamilyKind::WalkForward, - &walkforward_member_reports(&walkforward_family(Strategy::SmaCross, None, &DataSource::Synthetic, &Stage1RGrid::default(), Selection::Argmax)), + &walkforward_member_reports(&walkforward_family(Strategy::SmaCross, None, &DataSource::Synthetic, &RGrid::default(), Selection::Argmax)), ) .expect("walkforward family"); assert_eq!((sid.as_str(), mid.as_str(), wid.as_str()), ("sweep-0", "mc-0", "walkforward-0")); @@ -5390,11 +5390,11 @@ mod tests { assert_eq!(a, b, "C1: the momentum family is a pure function of the build"); } - /// Property: the *shipped* `stage1_meanrev_graph` (the CLI compile unit, not - /// the hand-rebuilt subgraph in `stage1_meanrev_e2e.rs`) FADES against the + /// Property: the *shipped* `r_meanrev_graph` (the CLI compile unit, not + /// the hand-rebuilt subgraph in `r_meanrev_e2e.rs`) FADES against the /// move — its exposure tap reads short (-1) above the band and long (+1) /// below, the sign-inverted-vs-breakout polarity that defines mean-reversion. - /// A latch-polarity copy-paste from `stage1_breakout_graph` (swapping the + /// A latch-polarity copy-paste from `r_breakout_graph` (swapping the /// `set`/`reset` legs) would leave the fold-vs-raw and window-grid CLI tests /// green yet silently invert the signal; only an observable read of this /// function's bias catches it. `k = 0` collapses the band to the lagging EWMA @@ -5402,15 +5402,15 @@ mod tests { /// (alpha = 0.5) lags the level clearly. The exposure Recorder (`tx_ex`, /// `reduce = false`) carries the bias in col[0]. #[test] - fn stage1_meanrev_graph_fades_short_above_the_band_and_long_below() { + fn r_meanrev_graph_fades_short_above_the_band_and_long_below() { let (tx_eq, _rx_eq) = mpsc::channel(); let (tx_ex, rx_ex) = mpsc::channel(); let (tx_r, _rx_r) = mpsc::channel(); let (tx_req, _rx_req) = mpsc::channel(); - let flat = stage1_meanrev_graph(tx_eq, tx_ex, tx_r, tx_req, Some(3), 0.0, 3, 2.0, false) + let flat = r_meanrev_graph(tx_eq, tx_ex, tx_r, tx_req, Some(3), 0.0, 3, 2.0, false) .compile_with_params(&[]) - .expect("stage1-meanrev blueprint compiles"); - let mut h = Harness::bootstrap(flat).expect("stage1-meanrev harness bootstraps"); + .expect("r-meanrev blueprint compiles"); + let mut h = Harness::bootstrap(flat).expect("r-meanrev harness bootstraps"); // calm (price == lagging mean -> no fade) | sustained UP (price > mean -> // fade SHORT) | sustained DOWN (price < mean -> fade LONG). let closes = [ @@ -5432,7 +5432,7 @@ mod tests { /// Property: a `blueprint_sweep_family` member built from a serialized signal is /// the SAME trading result as the cycle-1 single run of that signal at the same - /// params — the loaded-blueprint sweep reuses the identical `wrap_stage1r` run path + /// params — the loaded-blueprint sweep reuses the identical `wrap_r` run path /// (the keystone). Every member of one family carries the SAME `topology_hash` (the /// loaded signal's, the deviation from the Rust-built mirror), and distinct grid /// points key to distinct `member_key`s (members are distinguishable). @@ -5440,13 +5440,13 @@ mod tests { fn blueprint_sweep_member_equals_single_run_and_shares_topology_hash() { // An OPEN signal (both SMA knobs free) so the sweep can bind them by name; the // serialized doc round-trips to the topology the single run hashes. - let open = stage1_signal(None, None); + let open = sma_signal(None, None); let doc = blueprint_to_json(&open).expect("serializes"); let data = DataSource::Synthetic; // fast pinned at 2, slow varied over {4, 6}: a 2x1 grid, slow the varying axis. let axes = vec![ - ("stage1_signal.fast.length".to_string(), vec![Scalar::i64(2)]), - ("stage1_signal.slow.length".to_string(), vec![Scalar::i64(4), Scalar::i64(6)]), + ("sma_signal.fast.length".to_string(), vec![Scalar::i64(2)]), + ("sma_signal.slow.length".to_string(), vec![Scalar::i64(4), Scalar::i64(6)]), ]; let family = blueprint_sweep_family(&doc, &axes, &data).expect("named axes resolve"); assert_eq!(family.points.len(), 2, "2x1 grid -> 2 members"); @@ -5461,8 +5461,8 @@ mod tests { // (a) the slow=4 member reproduces the cycle-1 single run at fast=2, slow=4 — // same equity/exposure stream (total_pips/max_drawdown/bias_sign_flips) and the // same topology_hash, proving the loaded blueprint runs through the identical path. - let single = run_signal_stage1r( - stage1_signal(None, None), + let single = run_signal_r( + sma_signal(None, None), &[Scalar::i64(2), Scalar::i64(4)], RunData::Synthetic, 0, @@ -5473,7 +5473,7 @@ mod tests { // (c) the two members' keys differ (member_key over the varying slow.length axis). let varying: HashSet = - ["stage1_signal.slow.length".to_string()].into_iter().collect(); + ["sma_signal.slow.length".to_string()].into_iter().collect(); let k4 = member_key(&family.points[0].report.manifest.params, &varying); let k6 = member_key(&family.points[1].report.manifest.params, &varying); assert_ne!(k4, k6, "distinct grid points key distinctly"); @@ -5482,26 +5482,26 @@ mod tests { #[test] fn blueprint_axis_probe_lists_prefixed_open_knobs() { // The open fixture's two SMA lengths are the sweepable knobs; the probe - // wraps the signal (name "stage1_signal") so the names are prefixed — + // wraps the signal (name "sma_signal") so the names are prefixed — // exactly what `--axis` binds. - let open = include_str!("../tests/fixtures/stage1_signal_open.json"); + let open = include_str!("../tests/fixtures/sma_signal_open.json"); let space = blueprint_axis_probe(open).param_space(); let names: Vec<&str> = space.iter().map(|p| p.name.as_str()).collect(); - assert_eq!(names, ["stage1_signal.fast.length", "stage1_signal.slow.length"]); + assert_eq!(names, ["sma_signal.fast.length", "sma_signal.slow.length"]); assert!(space.iter().all(|p| matches!(p.kind, ScalarKind::I64))); // A closed blueprint (both lengths bound) has no open axes. - let closed = include_str!("../tests/fixtures/stage1_signal.json"); + let closed = include_str!("../tests/fixtures/sma_signal.json"); assert!(blueprint_axis_probe(closed).param_space().is_empty()); } #[test] fn blueprint_walkforward_family_refits_each_window() { // The open fixture's two SMA lengths are re-fit per IS window over a 2x2 grid. - let doc = include_str!("../tests/fixtures/stage1_signal_open.json"); + let doc = include_str!("../tests/fixtures/sma_signal_open.json"); let axes = vec![ - ("stage1_signal.fast.length".to_string(), vec![Scalar::i64(2), Scalar::i64(3)]), - ("stage1_signal.slow.length".to_string(), vec![Scalar::i64(4), Scalar::i64(6)]), + ("sma_signal.fast.length".to_string(), vec![Scalar::i64(2), Scalar::i64(3)]), + ("sma_signal.slow.length".to_string(), vec![Scalar::i64(4), Scalar::i64(6)]), ]; let result = blueprint_walkforward_family(doc, &axes, &DataSource::Synthetic, Selection::Argmax); // 24/12/12 over the 60-bar synthetic span -> 3 rolling windows. @@ -5520,7 +5520,7 @@ mod tests { // topology_hash, and DIFFERING metrics — the seed reaches the DATA (a distinct // synthetic walk per draw), not just the manifest label. The anti-degenerate guard: // a regression to seed-as-label-only would make the three draws identical. - let closed = stage1_signal(Some(2), Some(4)); + let closed = sma_signal(Some(2), Some(4)); let doc = blueprint_to_json(&closed).expect("serializes"); let family = blueprint_mc_family(&doc, 3, &DataSource::Synthetic).expect("closed blueprint"); @@ -5544,7 +5544,7 @@ mod tests { // distribution. A CLOSED deep-lookback signal whose slow SMA length (60) equals the // fixed 60-bar synthetic walk never warms, so every seed yields zero trades and thus // identical metrics; that is a wrong result with no error (C10 refuse-don't-guess). - let deep = stage1_signal(Some(2), Some(60)); // slow len == walk len -> never warms + let deep = sma_signal(Some(2), Some(60)); // slow len == walk len -> never warms let doc = blueprint_to_json(&deep).expect("serializes"); let err = blueprint_mc_family(&doc, 3, &DataSource::Synthetic) .expect_err("a vacuous (all-identical) Monte-Carlo is rejected, not returned"); @@ -5561,7 +5561,7 @@ mod tests { // is unit-testable (the IO wrapper renders it to stderr + exit 2, mirroring the sibling // blueprint_sweep_family). MC binds no axis, so a free knob would have no binder; the // rejection pre-empts the downstream compile_with_params arity panic. - let open = stage1_signal(None, None); // both SMA knobs free -> non-empty param_space + let open = sma_signal(None, None); // both SMA knobs free -> non-empty param_space let doc = blueprint_to_json(&open).expect("serializes"); let err = blueprint_mc_family(&doc, 4, &DataSource::Synthetic) .expect_err("an open blueprint is rejected, not run"); @@ -5576,13 +5576,13 @@ mod tests { std::fs::create_dir_all(&dir).expect("temp dir"); let reg = Registry::open(dir.join("runs.jsonl")); - let open = stage1_signal(None, None); + let open = sma_signal(None, None); let doc = blueprint_to_json(&open).expect("serializes"); let data = DataSource::Synthetic; // 2x grid over slow.length {4,6} at fast=2 — slow=4 is the open-at-end member. let axes = vec![ - ("stage1_signal.fast.length".to_string(), vec![Scalar::i64(2)]), - ("stage1_signal.slow.length".to_string(), vec![Scalar::i64(4), Scalar::i64(6)]), + ("sma_signal.fast.length".to_string(), vec![Scalar::i64(2)]), + ("sma_signal.slow.length".to_string(), vec![Scalar::i64(4), Scalar::i64(6)]), ]; let family = blueprint_sweep_family(&doc, &axes, &data).expect("axes resolve"); @@ -5615,7 +5615,7 @@ mod tests { let reg = Registry::open(dir.join("runs.jsonl")); // a CLOSED signal (both SMA knobs bound) — MC binds no axis. - let closed = stage1_signal(Some(2), Some(4)); + let closed = sma_signal(Some(2), Some(4)); let doc = blueprint_to_json(&closed).expect("serializes"); let data = DataSource::Synthetic; let family = blueprint_mc_family(&doc, 3, &data).expect("closed blueprint"); @@ -5650,11 +5650,11 @@ mod tests { /// the RED phase compiles whether the builder returns a `BindError` or a `String`. #[test] fn blueprint_sweep_family_rejects_a_fully_bound_blueprint() { - let closed = stage1_signal(Some(2), Some(4)); // both SMA knobs bound -> empty param_space + let closed = sma_signal(Some(2), Some(4)); // both SMA knobs bound -> empty param_space let doc = blueprint_to_json(&closed).expect("serializes"); // an axis naming a bound-out knob: the pre-fix path returns UnknownKnob for it. let axes = vec![( - "stage1_signal.slow.length".to_string(), + "sma_signal.slow.length".to_string(), vec![Scalar::i64(4), Scalar::i64(6)], )]; let err = match blueprint_sweep_family(&doc, &axes, &DataSource::Synthetic) { @@ -5682,7 +5682,7 @@ mod tests { std::fs::create_dir_all(&dir).expect("temp dir"); let reg = Registry::open(dir.join("runs.jsonl")); - let closed = stage1_signal(Some(2), Some(4)); // MC binds no axis -> closed blueprint + let closed = sma_signal(Some(2), Some(4)); // MC binds no axis -> closed blueprint let doc = blueprint_to_json(&closed).expect("serializes"); let data = DataSource::Synthetic; let family = blueprint_mc_family(&doc, 3, &data).expect("closed blueprint"); @@ -5747,11 +5747,11 @@ mod tests { } #[test] - fn run_stage1_r_synthetic_folds_an_r_block() { - // the stage1-r harness scores the SMA-cross signal in R: one shell-callable run + fn run_r_sma_synthetic_folds_an_r_block() { + // the r-sma harness scores the SMA-cross signal in R: one shell-callable run // yields a RunReport whose metrics.r is Some with a finite SQN and >= 1 trade. - let report = run_stage1_r(RunData::Synthetic, None, None, None, None); - let r = report.metrics.r.as_ref().expect("stage1-r run must populate metrics.r"); + let report = run_r_sma(RunData::Synthetic, None, None, None, None); + let r = report.metrics.r.as_ref().expect("r-sma run must populate metrics.r"); assert!(r.n_trades >= 1, "expected >= 1 trade, got {}", r.n_trades); assert!(r.sqn.is_finite(), "SQN must be finite, got {}", r.sqn); assert!(r.expectancy_r.is_finite(), "E[R] must be finite, got {}", r.expectancy_r); @@ -5759,7 +5759,7 @@ mod tests { // SimBroker, so its manifest carries a dedicated broker label. assert!( report.manifest.broker.contains("risk-executor"), - "stage1-r manifest should carry a dedicated broker label, got: {}", + "r-sma manifest should carry a dedicated broker label, got: {}", report.manifest.broker ); } @@ -5781,18 +5781,18 @@ mod tests { #[test] #[ignore = "regenerates the committed demo signal blueprint fixture"] fn emit_demo_signal_fixture() { - let json = blueprint_to_json(&stage1_signal(Some(2), Some(4))).expect("serializes"); - std::fs::write("tests/fixtures/stage1_signal.json", json).expect("write fixture"); + let json = blueprint_to_json(&sma_signal(Some(2), Some(4))).expect("serializes"); + std::fs::write("tests/fixtures/sma_signal.json", json).expect("write fixture"); } /// Regenerates the committed OPEN demo signal blueprint the `aura sweep /// ` E2E sweeps — fast/slow left unbound, so its param_space exposes - /// `stage1_signal.fast.length` / `.slow.length` axes to grid. Ignored by default. + /// `sma_signal.fast.length` / `.slow.length` axes to grid. Ignored by default. #[test] #[ignore = "regenerates the committed open demo signal blueprint fixture"] fn emit_demo_signal_open_fixture() { - let json = blueprint_to_json(&stage1_signal(None, None)).expect("serializes"); - std::fs::write("tests/fixtures/stage1_signal_open.json", json).expect("write fixture"); + let json = blueprint_to_json(&sma_signal(None, None)).expect("serializes"); + std::fs::write("tests/fixtures/sma_signal_open.json", json).expect("write fixture"); } /// The cycle-1 keystone (C1): a signal serialized → loaded back through the @@ -5800,10 +5800,10 @@ mod tests { /// twin — same metrics, same traces, same topology_hash. #[test] fn loaded_signal_runs_bit_identical_to_rust_built() { - let json = blueprint_to_json(&stage1_signal(Some(2), Some(4))).expect("serializes"); + let json = blueprint_to_json(&sma_signal(Some(2), Some(4))).expect("serializes"); let loaded = blueprint_from_json(&json, &|t| std_vocabulary(t)).expect("loads"); - let a = run_signal_stage1r(stage1_signal(Some(2), Some(4)), &[], RunData::Synthetic, 0); - let b = run_signal_stage1r(loaded, &[], RunData::Synthetic, 0); + let a = run_signal_r(sma_signal(Some(2), Some(4)), &[], RunData::Synthetic, 0); + let b = run_signal_r(loaded, &[], RunData::Synthetic, 0); assert_eq!(a.to_json(), b.to_json(), "loaded run is bit-identical incl. topology_hash"); assert_eq!(a.manifest.topology_hash.as_deref().map(str::len), Some(64), "64-hex sha256 present"); } @@ -5812,9 +5812,9 @@ mod tests { /// the #158 reproducibility-anchor property. #[test] fn topology_hash_is_stable_and_distinguishes() { - let h = topology_hash(&stage1_signal(Some(2), Some(4))); - assert_eq!(h, topology_hash(&stage1_signal(Some(2), Some(4))), "same signal -> same hash"); - assert_ne!(h, topology_hash(&stage1_signal(Some(3), Some(4))), "different topology -> different hash"); + let h = topology_hash(&sma_signal(Some(2), Some(4))); + assert_eq!(h, topology_hash(&sma_signal(Some(2), Some(4))), "same signal -> same hash"); + assert_ne!(h, topology_hash(&sma_signal(Some(3), Some(4))), "different topology -> different hash"); } /// #158 acc 1 (content-id stability across the store round-trip): a blueprint's content @@ -5823,7 +5823,7 @@ mod tests { /// topology_hash). `content_id` is the shared primitive `topology_hash` uses. #[test] fn content_id_is_stable_across_the_store_round_trip() { - let json = blueprint_to_json(&stage1_signal(Some(2), Some(4))).expect("serializes"); + let json = blueprint_to_json(&sma_signal(Some(2), Some(4))).expect("serializes"); let id = content_id(&json); assert_eq!(id.len(), 64, "a 64-hex sha256"); let reloaded = blueprint_from_json(&json, &|t| std_vocabulary(t)).expect("reloads"); @@ -5840,7 +5840,7 @@ mod tests { /// bytes and thus the same content id. #[test] fn content_id_is_stable_across_a_tolerated_tier1_field() { - let base = blueprint_to_json(&stage1_signal(Some(2), Some(4))).expect("serializes"); + let base = blueprint_to_json(&sma_signal(Some(2), Some(4))).expect("serializes"); // a future Tier-1 optional field injected at the top level (the blueprint does not use it). let with_extra = base.replacen("{\"format_version\":1,", "{\"format_version\":1,\"future_optional\":123,", 1); @@ -5860,7 +5860,7 @@ mod tests { /// primitive. Pins that the two command paths cannot silently drift apart. #[test] fn topology_hash_is_the_content_id_of_the_canonical_form() { - let sig = stage1_signal(Some(2), Some(4)); + let sig = sma_signal(Some(2), Some(4)); assert_eq!(topology_hash(&sig), content_id(&blueprint_to_json(&sig).expect("serializes"))); } @@ -5886,17 +5886,17 @@ mod tests { #[test] fn mc_r_bootstrap_report_pools_a_non_empty_oos_r_series_over_synthetic() { - // Property: the full real-R assembly path — walkforward_family(Stage1R) -> + // Property: the full real-R assembly path — walkforward_family(RSma) -> // pooled_oos_trade_rs -> r_bootstrap -> mc_r_bootstrap_json — wires up and - // reduces a NON-EMPTY pooled OOS R series (the synthetic stage1-r walk-forward + // reduces a NON-EMPTY pooled OOS R series (the synthetic r-sma walk-forward // closes >= 1 trade across its windows). Guards the wiring + the non-empty // pooling branch the parser/primitive unit tests cannot reach; mirrors // `mc_report` / `walkforward_report`. Deterministic (C1). - let result = walkforward_family(Strategy::Stage1R, None, &DataSource::Synthetic, &Stage1RGrid::default(), Selection::Argmax); + let result = walkforward_family(Strategy::RSma, None, &DataSource::Synthetic, &RGrid::default(), Selection::Argmax); let pooled = pooled_oos_trade_rs(&result); - assert!(!pooled.is_empty(), "synthetic stage1-r walk-forward must pool >= 1 OOS trade R"); + assert!(!pooled.is_empty(), "synthetic r-sma walk-forward must pool >= 1 OOS trade R"); - let line = mc_r_bootstrap_report(&DataSource::Synthetic, &Stage1RGrid::default(), 1, 256, 1); + let line = mc_r_bootstrap_report(&DataSource::Synthetic, &RGrid::default(), 1, 256, 1); let v: serde_json::Value = serde_json::from_str(&line).expect("canonical json line"); let obj = &v["mc_r_bootstrap"]; // n_trades is the pooled-series length the bootstrap actually saw — non-zero @@ -5909,7 +5909,7 @@ mod tests { // C1 at the CLI edge: the assembled real-R line is byte-identical on re-run. assert_eq!( line, - mc_r_bootstrap_report(&DataSource::Synthetic, &Stage1RGrid::default(), 1, 256, 1), + mc_r_bootstrap_report(&DataSource::Synthetic, &RGrid::default(), 1, 256, 1), ); } } diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index d019d6f..8cd6711 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -322,7 +322,7 @@ fn run_real_nonvetted_symbol_resolves_pip_from_sidecar() { #[test] fn aura_run_loads_and_runs_a_blueprint_file() { let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) - .args(["run", "tests/fixtures/stage1_signal.json"]) + .args(["run", "tests/fixtures/sma_signal.json"]) .output() .expect("spawn aura run blueprint"); assert_eq!( @@ -365,7 +365,7 @@ fn run_blueprint_rejects_builtin_only_flags_exit_two() { &["--slip-vol-mult", "1"][..], &["--carry-per-cycle", "1"][..], ] { - let mut args = vec!["run", "tests/fixtures/stage1_signal.json"]; + let mut args = vec!["run", "tests/fixtures/sma_signal.json"]; args.extend_from_slice(extra); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(&args) @@ -722,7 +722,7 @@ fn per_subcommand_help_is_scoped_stdout_exit_zero() { /// Property (#153): the cost-flag units/constraints are discoverable from /// `aura run --help` — the appended note states the in-R charge, the per-ENGINE- -/// cycle carry semantics, the `>= 0` constraint, and that cost flags are stage1-r +/// cycle carry semantics, the `>= 0` constraint, and that cost flags are r-sma /// only. Pre-#153 the usage listed the flags with no unit or scale guidance. #[test] fn run_help_surfaces_cost_flag_units_note() { @@ -734,7 +734,7 @@ fn run_help_surfaces_cost_flag_units_note() { // multi-word "per ENGINE cycle". assert!(stdout.contains("ENGINE"), "help must explain carry units: {stdout}"); assert!(stdout.contains("cycle"), "help must explain the per-cycle carry scale: {stdout}"); - assert!(stdout.contains("stage1-r"), "help must state the R-harness requirement: {stdout}"); + assert!(stdout.contains("r-sma"), "help must state the R-harness requirement: {stdout}"); } /// Property (#153/#175): a malformed cost value (`--cost-per-trade x`, not a f64) is @@ -746,7 +746,7 @@ fn run_help_surfaces_cost_flag_units_note() { #[test] fn run_malformed_cost_value_usage_error_carries_note_on_stderr() { let out = Command::new(BIN) - .args(["run", "--harness", "stage1-r", "--cost-per-trade", "x"]) + .args(["run", "--harness", "r-sma", "--cost-per-trade", "x"]) .output() .expect("spawn aura run --cost-per-trade x"); assert_eq!(out.status.code(), Some(2), "a malformed cost value is a usage error (exit 2); stderr: {}", @@ -1391,8 +1391,8 @@ fn sweep_real_is_byte_deterministic_across_runs() { /// The synthetic seed-resweep `mc` is undefined over real bars (one realization -> /// identical members), so a bare `aura mc --real EURUSD` — with no R candidate — is /// REJECTED (usage on stderr, exit 2) at the binary boundary. The real path is -/// reachable ONLY via `--strategy stage1-r` (the R-bootstrap over the pooled OOS R -/// series, `mc_strategy_stage1_r_prints_a_bootstrap_line_deterministically`); a +/// reachable ONLY via `--strategy r-sma` (the R-bootstrap over the pooled OOS R +/// series, `mc_strategy_r_sma_prints_a_bootstrap_line_deterministically`); a /// `--real` without it stays a usage error. NOT gated — the refusal precedes any /// data access, so it is CI-safe on every machine. #[test] @@ -1652,22 +1652,22 @@ fn chart_family_serves_member_count_and_spanning_window_in_meta() { // --- `aura run --harness ` selector (iter-3 Task 3) -------------------- -/// Property: `aura run --harness stage1-r` runs the R-scored harness — its stdout +/// Property: `aura run --harness r-sma` runs the R-scored harness — its stdout /// `RunReport` carries the nested `r` block (the R yardstick), and that block carries -/// the `sqn` field. Pins the selector wiring stage1-r → `run_stage1_r` at the +/// the `sqn` field. Pins the selector wiring r-sma → `run_r_sma` at the /// built-binary boundary; a miswiring to the pip-only SMA arm would drop the `r` key. #[test] -fn run_harness_stage1_r_prints_an_r_block() { - let out = std::process::Command::new(BIN).args(["run", "--harness", "stage1-r"]).output().unwrap(); +fn run_harness_r_sma_prints_an_r_block() { + let out = std::process::Command::new(BIN).args(["run", "--harness", "r-sma"]).output().unwrap(); assert!(out.status.success()); let s = String::from_utf8(out.stdout).unwrap(); - assert!(s.contains("\"r\":{"), "stage1-r run must carry an r block: {s}"); + assert!(s.contains("\"r\":{"), "r-sma run must carry an r block: {s}"); assert!(s.contains("\"sqn\""), "the r block must carry sqn: {s}"); } /// Property: `aura run --harness sma` is the pip-only default — its `RunReport` /// OMITS the `r` key entirely (the `skip_serializing_if` keeps a pip run byte-clean). -/// The companion of the stage1-r test: it proves the selector switches the surface, +/// The companion of the r-sma test: it proves the selector switches the surface, /// not that every run sprouts an `r` block. #[test] fn run_harness_sma_is_pip_only_no_r_block() { @@ -1728,18 +1728,18 @@ fn run_unknown_harness_exits_two() { assert_eq!(out.status.code(), Some(2)); } -/// Property: `--trace` on the stage1-r harness persists the THIRD `r_equity` tap +/// Property: `--trace` on the r-sma harness persists the THIRD `r_equity` tap /// beside equity/exposure, and that tap round-trips through `aura chart --tap /// r_equity` into a rendered series. Pins the full author→persist→chart loop for the /// R-equity series end to end (the pip harnesses write only the two taps). #[test] -fn stage1_r_trace_persists_r_equity_and_charts_it() { +fn r_sma_trace_persists_r_equity_and_charts_it() { // `temp_cwd` (cli_run.rs:13) gives a unique CWD with no external tempfile dep — the // pattern the existing trace tests use; it returns a `PathBuf`, so `.join` directly. - let dir = temp_cwd("stage1-r-trace"); + let dir = temp_cwd("r-sma-trace"); let run = Command::new(BIN) .current_dir(&dir) - .args(["run", "--harness", "stage1-r", "--trace", "q1"]) + .args(["run", "--harness", "r-sma", "--trace", "q1"]) .output() .unwrap(); assert!(run.status.success()); @@ -1757,7 +1757,7 @@ fn stage1_r_trace_persists_r_equity_and_charts_it() { let _ = std::fs::remove_dir_all(&dir); } -/// Property (Task 3, the run-path cost seam): `aura run --harness stage1-r +/// Property (Task 3, the run-path cost seam): `aura run --harness r-sma /// --cost-per-trade ` charges a flat round-trip cost in R end to end — the run /// persists a fourth `net_r_equity` tap beside r_equity and the report's /// `net_expectancy_r` is STRICTLY below the gross `expectancy_r`. Pins the whole @@ -1765,11 +1765,11 @@ fn stage1_r_trace_persists_r_equity_and_charts_it() { /// leaves net == gross, the held golden); a regression that dropped the cost node or /// left the net fold reading an empty stream would collapse net back onto gross here. #[test] -fn stage1_r_cost_run_persists_net_r_equity_and_charges_cost() { - let dir = temp_cwd("stage1-r-cost"); +fn r_sma_cost_run_persists_net_r_equity_and_charges_cost() { + let dir = temp_cwd("r-sma-cost"); let run = Command::new(BIN) .current_dir(&dir) - .args(["run", "--harness", "stage1-r", "--cost-per-trade", "2", "--trace", "c1"]) + .args(["run", "--harness", "r-sma", "--cost-per-trade", "2", "--trace", "c1"]) .output() .unwrap(); assert!(run.status.success(), "exit: {:?}; stderr: {}", run.status, @@ -1788,10 +1788,10 @@ fn stage1_r_cost_run_persists_net_r_equity_and_charges_cost() { /// net-R curve. The combined net is strictly below the flat-cost-only net (the /// vol-slippage node bites on top), and the `net_r_equity` trace persists. #[test] -fn stage1_r_both_costs_compose_net_below_each_alone() { - let dir = temp_cwd("stage1-r-compose"); +fn r_sma_both_costs_compose_net_below_each_alone() { + let dir = temp_cwd("r-sma-compose"); let run_net = |args: &[&str], trace: &str| -> f64 { - let mut full = vec!["run", "--harness", "stage1-r"]; + let mut full = vec!["run", "--harness", "r-sma"]; full.extend_from_slice(args); full.extend_from_slice(&["--trace", trace]); let run = Command::new(BIN).current_dir(&dir).args(&full).output().unwrap(); @@ -1809,20 +1809,20 @@ fn stage1_r_both_costs_compose_net_below_each_alone() { } /// Golden characterization (cycle 0083, the CostNode-trait migration): pins the -/// EXACT `net_expectancy_r` of `aura run --harness stage1-r --cost-per-trade 2`, so +/// EXACT `net_expectancy_r` of `aura run --harness r-sma --cost-per-trade 2`, so /// the ConstantCost-through-CostRunner migration (Task 2) is VERIFIED byte-preserving /// at the observable boundary, not assumed. The sibling -/// `stage1_r_cost_run_persists_net_r_equity_and_charges_cost` only asserts the +/// `r_sma_cost_run_persists_net_r_equity_and_charges_cost` only asserts the /// RELATION `net < gross`; a regression that drifted the cost numerator or the /// `cost_per_trade / |entry - stop|` R-normalization (the token form the cycle /// promises to keep verbatim) would keep `net < gross` true and pass that test /// silently while shifting this value. Pins only the cost-affected float — gross -/// `expectancy_r` is already pinned by `stage1_r_single_run_output_golden` and is +/// `expectancy_r` is already pinned by `r_sma_single_run_output_golden` and is /// unchanged by cost. Deterministic over the fixed synthetic stream (C1). #[test] -fn stage1_r_flat_cost_net_expectancy_r_golden() { +fn r_sma_flat_cost_net_expectancy_r_golden() { let out = Command::new(BIN) - .args(["run", "--harness", "stage1-r", "--cost-per-trade", "2"]) + .args(["run", "--harness", "r-sma", "--cost-per-trade", "2"]) .output() .unwrap(); assert!(out.status.success(), "exit: {:?}; stderr: {}", out.status, @@ -1838,19 +1838,19 @@ fn stage1_r_flat_cost_net_expectancy_r_golden() { } /// Golden characterization (cycle 0083, the composed cost path): pins the EXACT -/// `net_expectancy_r` of `aura run --harness stage1-r --cost-per-trade 2 +/// `net_expectancy_r` of `aura run --harness r-sma --cost-per-trade 2 /// --slip-vol-mult 0.5`, so the VolSlippageCost-through-CostRunner migration (Task 3) /// AND the CostSum per-field aggregation reading the shared `COST_FIELD_NAMES` /// contract (Task 4) are VERIFIED byte-preserving end to end. The sibling -/// `stage1_r_both_costs_compose_net_below_each_alone` only asserts the RELATION +/// `r_sma_both_costs_compose_net_below_each_alone` only asserts the RELATION /// `net_both < net_flat`; a drift in the vol-scaled numerator, the CostSum field /// order, or the `cost_graph` composite's per-node `cost[k].` wiring would keep /// that relation true and pass silently while shifting this value. Deterministic /// over the fixed synthetic stream (C1). #[test] -fn stage1_r_composed_cost_net_expectancy_r_golden() { +fn r_sma_composed_cost_net_expectancy_r_golden() { let out = Command::new(BIN) - .args(["run", "--harness", "stage1-r", "--cost-per-trade", "2", "--slip-vol-mult", "0.5"]) + .args(["run", "--harness", "r-sma", "--cost-per-trade", "2", "--slip-vol-mult", "0.5"]) .output() .unwrap(); assert!(out.status.success(), "exit: {:?}; stderr: {}", out.status, @@ -1866,13 +1866,13 @@ fn stage1_r_composed_cost_net_expectancy_r_golden() { } /// Golden characterization (cycle 0085, the per-held-cycle accrual path): pins the EXACT -/// `net_expectancy_r` of `aura run --harness stage1-r --carry-per-cycle 0.5`, so the +/// `net_expectancy_r` of `aura run --harness r-sma --carry-per-cycle 0.5`, so the /// CarryCost accrual (open_cost grows over the hold, dumps into cum at close) is VERIFIED /// at the observable boundary. Deterministic over the fixed synthetic stream (C1). #[test] -fn stage1_r_carry_cost_net_expectancy_r_golden() { +fn r_sma_carry_cost_net_expectancy_r_golden() { let out = Command::new(BIN) - .args(["run", "--harness", "stage1-r", "--carry-per-cycle", "0.5"]) + .args(["run", "--harness", "r-sma", "--carry-per-cycle", "0.5"]) .output() .unwrap(); assert!(out.status.success(), "exit: {:?}; stderr: {}", out.status, @@ -1887,13 +1887,13 @@ fn stage1_r_carry_cost_net_expectancy_r_golden() { } /// Golden characterization (cycle 0085, composed at-close + accrual): pins the EXACT -/// `net_expectancy_r` of `aura run --harness stage1-r --cost-per-trade 2 --carry-per-cycle +/// `net_expectancy_r` of `aura run --harness r-sma --cost-per-trade 2 --carry-per-cycle /// 0.5` — the per-trade ConstantCost and the per-held-cycle CarryCost sum through /// cost_graph/CostSum into one net-R curve. Deterministic (C1). #[test] -fn stage1_r_cost_and_carry_composed_net_expectancy_r_golden() { +fn r_sma_cost_and_carry_composed_net_expectancy_r_golden() { let out = Command::new(BIN) - .args(["run", "--harness", "stage1-r", "--cost-per-trade", "2", "--carry-per-cycle", "0.5"]) + .args(["run", "--harness", "r-sma", "--cost-per-trade", "2", "--carry-per-cycle", "0.5"]) .output() .unwrap(); assert!(out.status.success(), "exit: {:?}; stderr: {}", out.status, @@ -1915,11 +1915,11 @@ fn stage1_r_cost_and_carry_composed_net_expectancy_r_golden() { /// end, never closing), where cum_cost_in_r is constant so the growth is the open-side /// accrual alone. #[test] -fn stage1_r_carry_net_r_equity_bleeds_over_the_hold() { - let dir = temp_cwd("stage1-r-carry-bleed"); +fn r_sma_carry_net_r_equity_bleeds_over_the_hold() { + let dir = temp_cwd("r-sma-carry-bleed"); let run = Command::new(BIN) .current_dir(&dir) - .args(["run", "--harness", "stage1-r", "--carry-per-cycle", "0.5", "--trace", "bleed"]) + .args(["run", "--harness", "r-sma", "--carry-per-cycle", "0.5", "--trace", "bleed"]) .output() .unwrap(); assert!(run.status.success(), "exit: {:?}; stderr: {}", run.status, @@ -1959,11 +1959,11 @@ fn stage1_r_carry_net_r_equity_bleeds_over_the_hold() { /// `CarryCost::new` and PANICs, and a negative price-unit carry would CREDIT R — /// inverting the accrual sign. Deterministic; the refusal precedes any data access. #[test] -fn stage1_r_negative_carry_per_cycle_refused_non_negative_exit_2() { - let dir = temp_cwd("stage1-r-carry-neg"); +fn r_sma_negative_carry_per_cycle_refused_non_negative_exit_2() { + let dir = temp_cwd("r-sma-carry-neg"); let out = Command::new(BIN) .current_dir(&dir) - .args(["run", "--harness", "stage1-r", "--carry-per-cycle", "-1"]) + .args(["run", "--harness", "r-sma", "--carry-per-cycle", "-1"]) .output() .expect("spawn aura run --carry-per-cycle -1"); assert_eq!(out.status.code(), Some(2), @@ -1984,7 +1984,7 @@ fn run_negative_cost_per_trade_refused_non_negative_exit_2() { let dir = temp_cwd("cost-per-trade-neg"); let out = Command::new(BIN) .current_dir(&dir) - .args(["run", "--harness", "stage1-r", "--cost-per-trade", "-0.5"]) + .args(["run", "--harness", "r-sma", "--cost-per-trade", "-0.5"]) .output() .expect("spawn aura run --cost-per-trade -0.5"); assert_eq!(out.status.code(), Some(2), "negative cost must exit 2; stderr: {}", @@ -2005,10 +2005,10 @@ fn run_negative_cost_per_trade_refused_non_negative_exit_2() { /// other pair of numbers and slip past a golden; this relation survives any future /// fixture re-baseline. Deterministic over the fixed synthetic stream (C1). #[test] -fn stage1_r_larger_carry_per_cycle_lowers_net_expectancy_r() { +fn r_sma_larger_carry_per_cycle_lowers_net_expectancy_r() { let net = |rate: &str| -> f64 { let out = Command::new(BIN) - .args(["run", "--harness", "stage1-r", "--carry-per-cycle", rate]) + .args(["run", "--harness", "r-sma", "--carry-per-cycle", rate]) .output() .unwrap(); assert!(out.status.success(), "exit: {:?}; stderr: {}", out.status, @@ -2030,9 +2030,9 @@ fn stage1_r_larger_carry_per_cycle_lowers_net_expectancy_r() { /// goldens stayed green. Pins the zero ACCRUAL is zero COST invariant relationally, so it /// survives any future re-baseline of the gross value. Deterministic (C1). #[test] -fn stage1_r_zero_carry_per_cycle_charges_nothing() { +fn r_sma_zero_carry_per_cycle_charges_nothing() { let out = Command::new(BIN) - .args(["run", "--harness", "stage1-r", "--carry-per-cycle", "0"]) + .args(["run", "--harness", "r-sma", "--carry-per-cycle", "0"]) .output() .unwrap(); assert!(out.status.success(), "exit: {:?}; stderr: {}", out.status, @@ -2045,7 +2045,7 @@ fn stage1_r_zero_carry_per_cycle_charges_nothing() { assert_eq!(net, gross, "zero carry must be exactly free: net {net} == gross {gross}; {s}"); } -/// Property (the spec's dual-yardstick headline): `aura run --harness stage1-r` scores +/// Property (the spec's dual-yardstick headline): `aura run --harness r-sma` scores /// ONE signal in BOTH yardsticks at once — the single emitted `RunReport` carries the pip /// `metrics.total_pips` (off the SimBroker branch) AND the nested `r` block (off the /// RiskExecutor branch), because one bias is fanned into both. The shipped selector test @@ -2055,52 +2055,52 @@ fn stage1_r_zero_carry_per_cycle_charges_nothing() { /// pip yardstick; this fails it. Asserts on the structural co-presence of both keys, never /// the volatile metric floats, so it stays deterministic. #[test] -fn run_harness_stage1_r_carries_both_pip_and_r_yardsticks() { +fn run_harness_r_sma_carries_both_pip_and_r_yardsticks() { let out = std::process::Command::new(BIN) - .args(["run", "--harness", "stage1-r"]) + .args(["run", "--harness", "r-sma"]) .output() - .expect("spawn aura run --harness stage1-r"); + .expect("spawn aura run --harness r-sma"); assert!(out.status.success(), "exit: {:?}", out.status); let s = String::from_utf8(out.stdout).expect("utf-8 stdout"); // exactly one report line. assert_eq!(s.lines().count(), 1, "stdout was: {s:?}"); // the pip yardstick (SimBroker branch) is still present in the same report... - assert!(s.contains("\"total_pips\":"), "stage1-r must keep the pip yardstick: {s}"); + assert!(s.contains("\"total_pips\":"), "r-sma must keep the pip yardstick: {s}"); // ...and the R yardstick (RiskExecutor branch) is folded in alongside it. - assert!(s.contains("\"r\":{"), "stage1-r must carry the R yardstick too: {s}"); + assert!(s.contains("\"r\":{"), "r-sma must carry the R yardstick too: {s}"); } /// Property (C1, the foundational determinism invariant at the new harness boundary): -/// `aura run --harness stage1-r` is byte-deterministic — running it twice over the +/// `aura run --harness r-sma` is byte-deterministic — running it twice over the /// fixed synthetic stream yields BYTE-IDENTICAL stdout (modulo the build-constant git /// commit, which is equal within one build). The whole engine rests on a backtest being /// reproducible (C1); the sibling sweep/walkforward paths each have a determinism E2E, -/// but the brand-new dual-tap stage1-r run-loop (the SMA→Bias fan into SimBroker + the +/// but the brand-new dual-tap r-sma run-loop (the SMA→Bias fan into SimBroker + the /// RiskExecutor + the summarize_r fold) had none. A non-determinism in the new R branch /// (e.g. an unstable channel drain order or an allocator-ordered fold) would surface as a /// drift here. Compares the full stdout body, so it pins the metric floats too. #[test] -fn run_harness_stage1_r_is_byte_deterministic_across_runs() { +fn run_harness_r_sma_is_byte_deterministic_across_runs() { let run = || { let out = std::process::Command::new(BIN) - .args(["run", "--harness", "stage1-r"]) + .args(["run", "--harness", "r-sma"]) .output() - .expect("spawn aura run --harness stage1-r"); + .expect("spawn aura run --harness r-sma"); assert!(out.status.success(), "exit: {:?}", out.status); String::from_utf8(out.stdout).expect("utf-8 stdout") }; - assert_eq!(run(), run(), "the stage1-r run must be byte-identical across runs (C1)"); + assert_eq!(run(), run(), "the r-sma run must be byte-identical across runs (C1)"); } /// Golden characterization (cycle 0066): pins the EXACT metric output of the -/// stage1-r single run, so the `stage1_r_graph` helper extraction (Task 2) is +/// r-sma single run, so the `r_sma_graph` helper extraction (Task 2) is /// VERIFIED byte-preserving, not assumed. Captured against HEAD before the /// refactor; the refactor must keep it identical (a drift is a behaviour change → /// debug). Commit-agnostic: pins only the `metrics` object, since the manifest /// carries the volatile build commit. #[test] -fn stage1_r_single_run_output_golden() { - let out = Command::new(BIN).args(["run", "--harness", "stage1-r"]).output().unwrap(); +fn r_sma_single_run_output_golden() { + let out = Command::new(BIN).args(["run", "--harness", "r-sma"]).output().unwrap(); assert!(out.status.success(), "exit: {:?}", out.status); let s = String::from_utf8(out.stdout).expect("utf-8 stdout"); let key = "\"metrics\":"; @@ -2109,30 +2109,30 @@ fn stage1_r_single_run_output_golden() { assert_eq!( metrics, r#""metrics":{"total_pips":0.34185000000002036,"max_drawdown":0.11139999999998655,"bias_sign_flips":2,"r":{"expectancy_r":1.2710005136982836,"n_trades":3,"win_rate":1.0,"avg_win_r":1.2710005136982836,"avg_loss_r":0.0,"profit_factor":0.0,"max_r_drawdown":0.0,"n_open_at_end":1,"sqn":3.141496526818299,"sqn_normalized":3.141496526818299,"net_expectancy_r":1.2710005136982836,"conviction_terciles_r":[0.9285858482198718,2.0771328641652427,0.8072828287097363]}}}"#, - "stage1-r single-run metric output drifted from the golden: {s}" + "r-sma single-run metric output drifted from the golden: {s}" ); } -/// Property (#133): `aura sweep --strategy stage1-r` runs the stage1-r harness over +/// Property (#133): `aura sweep --strategy r-sma` runs the r-sma harness over /// the fast×slow signal grid (4 members), each carrying an R block, persisted as a /// rankable family; `runs family rank sqn` orders them highest-SQN first. #[test] -fn sweep_strategy_stage1_r_ranks_a_family_by_sqn() { - let cwd = temp_cwd("sweep-stage1-r"); +fn sweep_strategy_r_sma_ranks_a_family_by_sqn() { + let cwd = temp_cwd("sweep-r-sma"); let out = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-r"]) + .args(["sweep", "--strategy", "r-sma"]) .current_dir(&cwd) .output() - .expect("spawn aura sweep --strategy stage1-r"); + .expect("spawn aura sweep --strategy r-sma"); assert!( out.status.success(), - "stage1-r sweep exit: {:?}; stderr: {}", + "r-sma sweep exit: {:?}; stderr: {}", out.status, String::from_utf8_lossy(&out.stderr) ); let stdout = String::from_utf8(out.stdout).expect("utf-8"); let lines: Vec<&str> = stdout.lines().collect(); - assert_eq!(lines.len(), 4, "stage1-r sweep must print 4 member lines: {stdout:?}"); + assert_eq!(lines.len(), 4, "r-sma sweep must print 4 member lines: {stdout:?}"); for line in &lines { assert!(line.contains("\"r\":{"), "member must carry an r block: {line}"); assert!(line.contains("\"sqn\""), "member r block must carry sqn: {line}"); @@ -2175,7 +2175,7 @@ fn sweep_strategy_stage1_r_ranks_a_family_by_sqn() { } /// Property (#130): `sqn_normalized` (SQN100) is a working rank key *through the -/// binary*, not just an in-memory `metric_cmp` arm. A persisted stage1-r family +/// binary*, not just an in-memory `metric_cmp` arm. A persisted r-sma family /// must carry the new `sqn_normalized` field in each member's on-disk `r` block, /// and `aura runs family rank sqn_normalized` must parse the new key (it was /// an `UnknownMetric` exit-2 error before #130), read the field back from @@ -2186,16 +2186,16 @@ fn sweep_strategy_stage1_r_ranks_a_family_by_sqn() { /// `sqn_normalized == sqn` per member and the SQN100 ranking matches the raw-SQN /// ranking — asserted as a cross-check. #[test] -fn sweep_strategy_stage1_r_ranks_a_family_by_sqn_normalized() { - let cwd = temp_cwd("sweep-stage1-r-sqn100"); +fn sweep_strategy_r_sma_ranks_a_family_by_sqn_normalized() { + let cwd = temp_cwd("sweep-r-sma-sqn100"); let out = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-r"]) + .args(["sweep", "--strategy", "r-sma"]) .current_dir(&cwd) .output() - .expect("spawn aura sweep --strategy stage1-r"); + .expect("spawn aura sweep --strategy r-sma"); assert!( out.status.success(), - "stage1-r sweep exit: {:?}; stderr: {}", + "r-sma sweep exit: {:?}; stderr: {}", out.status, String::from_utf8_lossy(&out.stderr) ); @@ -2257,23 +2257,23 @@ fn sweep_strategy_stage1_r_ranks_a_family_by_sqn_normalized() { let _ = std::fs::remove_dir_all(&cwd); } -/// Property (#135): `aura sweep --strategy stage1-r --trace ` now persists one +/// Property (#135): `aura sweep --strategy r-sma --trace ` now persists one /// member dir per grid point (it was refused with exit 2 before). The 2×2 fast×slow -/// grid yields 4 members; each carries the THIRD `r_equity` tap (the stage1-r member +/// grid yields 4 members; each carries the THIRD `r_equity` tap (the r-sma member /// has the R curve, unlike the two-tap sma/momentum members), and a member is /// chartable via `chart / --tap r_equity`. `--name` (record the rankable /// family, no per-member streams) still works. #[test] -fn sweep_strategy_stage1_r_trace_persists_member_dirs_with_r_equity() { - let cwd = temp_cwd("sweep-stage1-r-trace"); +fn sweep_strategy_r_sma_trace_persists_member_dirs_with_r_equity() { + let cwd = temp_cwd("sweep-r-sma-trace"); let traced = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-r", "--trace", "t1"]) + .args(["sweep", "--strategy", "r-sma", "--trace", "t1"]) .current_dir(&cwd) .output() - .expect("spawn aura sweep --strategy stage1-r --trace"); + .expect("spawn aura sweep --strategy r-sma --trace"); assert!( traced.status.success(), - "stage1-r --trace must persist members (exit 0): {:?}; stderr: {}", + "r-sma --trace must persist members (exit 0): {:?}; stderr: {}", traced.status, String::from_utf8_lossy(&traced.stderr) ); @@ -2282,7 +2282,7 @@ fn sweep_strategy_stage1_r_trace_persists_member_dirs_with_r_equity() { .expect("read t1 trace dir") .map(|e| e.expect("dir entry").file_name().to_string_lossy().into_owned()) .collect(); - assert_eq!(members.len(), 4, "2x2 fast×slow stage1-r grid = 4 member dirs; got {members:?}"); + assert_eq!(members.len(), 4, "2x2 fast×slow r-sma grid = 4 member dirs; got {members:?}"); for m in &members { assert!( m.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-')), @@ -2307,22 +2307,22 @@ fn sweep_strategy_stage1_r_trace_persists_member_dirs_with_r_equity() { ); // --name records the rankable family (no per-member streams) — still succeeds. let named = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-r", "--name", "n1"]) + .args(["sweep", "--strategy", "r-sma", "--name", "n1"]) .current_dir(&cwd) .output() - .expect("spawn aura sweep --strategy stage1-r --name"); + .expect("spawn aura sweep --strategy r-sma --name"); assert!( named.status.success(), - "stage1-r --name must succeed: {:?}; stderr: {}", + "r-sma --name must succeed: {:?}; stderr: {}", named.status, String::from_utf8_lossy(&named.stderr) ); let _ = std::fs::remove_dir_all(&cwd); } -/// Property (#137 — headline): `aura sweep --strategy stage1-r` accepts the four +/// Property (#137 — headline): `aura sweep --strategy r-sma` accepts the four /// optional comma-separated grid flags `--fast`, `--slow`, `--stop-length`, -/// `--stop-k`, which become the sweep's grid axes over the four stage1_r_graph +/// `--stop-k`, which become the sweep's grid axes over the four r_sma_graph /// knobs (sma fast length, sma slow length, vol-stop length, vol-stop k). The /// family is the cartesian product of the supplied lists; a single-value list per /// axis is a 1-member family. This is the enabling change that lets the stop @@ -2330,7 +2330,7 @@ fn sweep_strategy_stage1_r_trace_persists_member_dirs_with_r_equity() { /// fast for a 240/960-bar momentum cross) — so a slow time-series-momentum edge can /// be screened across timeframes, stop included. /// -/// Mirrors `sweep_strategy_stage1_r_ranks_a_family_by_sqn`: drive the built binary, +/// Mirrors `sweep_strategy_r_sma_ranks_a_family_by_sqn`: drive the built binary, /// read each stdout member line (the assigned `family_id` + the nested RunReport), /// and inspect the manifest `params` array — whose on-disk shape is /// `["fast.length",{"I64":N}]` / `["stop_k",{"F64":K}]` (verified against the live @@ -2339,13 +2339,13 @@ fn sweep_strategy_stage1_r_trace_persists_member_dirs_with_r_equity() { /// token rejected with exit 2 (the grid only honours the hardcoded fast×slow), so /// this is RED for the right reason: the flags + gridding are absent. #[test] -fn sweep_strategy_stage1_r_grids_all_four_knobs_from_csv_flags() { - let cwd = temp_cwd("sweep-stage1-r-grid-flags"); +fn sweep_strategy_r_sma_grids_all_four_knobs_from_csv_flags() { + let cwd = temp_cwd("sweep-r-sma-grid-flags"); let out = Command::new(BIN) .args([ "sweep", "--strategy", - "stage1-r", + "r-sma", "--fast", "240", "--slow", @@ -2359,10 +2359,10 @@ fn sweep_strategy_stage1_r_grids_all_four_knobs_from_csv_flags() { ]) .current_dir(&cwd) .output() - .expect("spawn aura sweep --strategy stage1-r with grid flags"); + .expect("spawn aura sweep --strategy r-sma with grid flags"); assert!( out.status.success(), - "gridded stage1-r sweep must succeed (exit 0): {:?}; stderr: {}", + "gridded r-sma sweep must succeed (exit 0): {:?}; stderr: {}", out.status, String::from_utf8_lossy(&out.stderr) ); @@ -2394,23 +2394,23 @@ fn sweep_strategy_stage1_r_grids_all_four_knobs_from_csv_flags() { } /// Property (#137 — default preservation, the hard constraint): when the grid flags -/// are ABSENT, `aura sweep --strategy stage1-r` must produce *exactly today's* +/// are ABSENT, `aura sweep --strategy r-sma` must produce *exactly today's* /// family — the 2×2 fast×slow grid {2,3}×{6,12} (4 members) with the stop pinned at /// stop_length=3, stop_k=2.0. This guards the byte-greenness of every existing /// golden/sweep test once the four flags land: a no-flags invocation falls back to /// the historical defaults, not to an empty/changed grid. It pins the contract that /// the flag wiring must not move the no-flags baseline. #[test] -fn sweep_strategy_stage1_r_no_flags_keeps_the_default_grid() { - let cwd = temp_cwd("sweep-stage1-r-default-grid"); +fn sweep_strategy_r_sma_no_flags_keeps_the_default_grid() { + let cwd = temp_cwd("sweep-r-sma-default-grid"); let out = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-r"]) + .args(["sweep", "--strategy", "r-sma"]) .current_dir(&cwd) .output() - .expect("spawn aura sweep --strategy stage1-r"); + .expect("spawn aura sweep --strategy r-sma"); assert!( out.status.success(), - "no-flags stage1-r sweep exit: {:?}; stderr: {}", + "no-flags r-sma sweep exit: {:?}; stderr: {}", out.status, String::from_utf8_lossy(&out.stderr) ); @@ -2476,7 +2476,7 @@ fn metrics_object(line: &str) -> &str { } /// Property (#138, task 8 — the streaming-reduction wiring invariant): a no-trace -/// `aura sweep --strategy stage1-r` (the *folded* path — `SeriesReducer` folds the +/// `aura sweep --strategy r-sma` (the *folded* path — `SeriesReducer` folds the /// eq/ex series to one summary row each, `GatedRecorder` retains only the gated R /// rows, so memory is O(trades) not O(cycles)) reports **byte-for-byte the same /// `metrics` object per member** as the `--trace` path (the *raw-recorder* @@ -2490,15 +2490,15 @@ fn metrics_object(line: &str) -> &str { /// matched in stdout order (both paths iterate the same deterministic grid, C1), /// and the `metrics` block excludes the family_id, which is allowed to differ. #[test] -fn sweep_strategy_stage1_r_folded_no_trace_metrics_equal_raw_trace_metrics() { - let cwd = temp_cwd("sweep-stage1-r-fold-vs-raw"); +fn sweep_strategy_r_sma_folded_no_trace_metrics_equal_raw_trace_metrics() { + let cwd = temp_cwd("sweep-r-sma-fold-vs-raw"); // folded path: no --trace -> reduce = true (SeriesReducer + GatedRecorder). let folded = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-r"]) + .args(["sweep", "--strategy", "r-sma"]) .current_dir(&cwd) .output() - .expect("spawn folded (no-trace) stage1-r sweep"); + .expect("spawn folded (no-trace) r-sma sweep"); assert!( folded.status.success(), "folded no-trace sweep exit: {:?}; stderr: {}", @@ -2509,10 +2509,10 @@ fn sweep_strategy_stage1_r_folded_no_trace_metrics_equal_raw_trace_metrics() { // raw path: --trace -> reduce = false (the verbatim Recorder reference). let raw = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-r", "--trace", "t1"]) + .args(["sweep", "--strategy", "r-sma", "--trace", "t1"]) .current_dir(&cwd) .output() - .expect("spawn raw (--trace) stage1-r sweep"); + .expect("spawn raw (--trace) r-sma sweep"); assert!( raw.status.success(), "raw --trace sweep exit: {:?}; stderr: {}", @@ -2547,19 +2547,19 @@ fn sweep_strategy_stage1_r_folded_no_trace_metrics_equal_raw_trace_metrics() { let _ = std::fs::remove_dir_all(&cwd); } -/// The breakout strategy reaches the CLI seam: `aura sweep --strategy stage1-breakout` +/// The breakout strategy reaches the CLI seam: `aura sweep --strategy r-breakout` /// emits an R-bearing member, and the folded no-trace path equals the raw --trace path -/// byte-for-byte (parity with the stage1-r fold-vs-raw guard). channel 3 warms up on +/// byte-for-byte (parity with the r-sma fold-vs-raw guard). channel 3 warms up on /// the synthetic stream; one channel value × the single default stop = one member. #[test] -fn sweep_strategy_stage1_breakout_folded_no_trace_metrics_equal_raw_trace_metrics() { - let cwd = temp_cwd("sweep-stage1-breakout-fold-vs-raw"); +fn sweep_strategy_r_breakout_folded_no_trace_metrics_equal_raw_trace_metrics() { + let cwd = temp_cwd("sweep-r-breakout-fold-vs-raw"); let folded = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-breakout", "--channel", "3"]) + .args(["sweep", "--strategy", "r-breakout", "--channel", "3"]) .current_dir(&cwd) .output() - .expect("spawn folded (no-trace) stage1-breakout sweep"); + .expect("spawn folded (no-trace) r-breakout sweep"); assert!( folded.status.success(), "folded no-trace breakout sweep exit: {:?}; stderr: {}", @@ -2569,10 +2569,10 @@ fn sweep_strategy_stage1_breakout_folded_no_trace_metrics_equal_raw_trace_metric let folded_out = String::from_utf8(folded.stdout).expect("utf-8"); let raw = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-breakout", "--channel", "3", "--trace", "b1"]) + .args(["sweep", "--strategy", "r-breakout", "--channel", "3", "--trace", "b1"]) .current_dir(&cwd) .output() - .expect("spawn raw (--trace) stage1-breakout sweep"); + .expect("spawn raw (--trace) r-breakout sweep"); assert!( raw.status.success(), "raw --trace breakout sweep exit: {:?}; stderr: {}", @@ -2607,18 +2607,18 @@ fn sweep_strategy_stage1_breakout_folded_no_trace_metrics_equal_raw_trace_metric /// Property: the breakout family is the cartesian product of its grid axes — a /// multi-value `--channel` list yields exactly one member per channel value, each /// member carrying its own bound channel length in the manifest params. Guards the -/// manual cartesian-product loop in `stage1_breakout_sweep_family`: a regression that +/// manual cartesian-product loop in `r_breakout_sweep_family`: a regression that /// collapsed the loop (sweeping only the last value, or ganging the two channels) /// would silently drop members here. Channels 2 and 3 both warm up on the 18-bar /// synthetic stream, so two members must appear, in grid order, distinctly. #[test] -fn sweep_strategy_stage1_breakout_grids_one_member_per_channel() { - let cwd = temp_cwd("sweep-stage1-breakout-channel-grid"); +fn sweep_strategy_r_breakout_grids_one_member_per_channel() { + let cwd = temp_cwd("sweep-r-breakout-channel-grid"); let out = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-breakout", "--channel", "2,3"]) + .args(["sweep", "--strategy", "r-breakout", "--channel", "2,3"]) .current_dir(&cwd) .output() - .expect("spawn 2-channel stage1-breakout sweep"); + .expect("spawn 2-channel r-breakout sweep"); assert!( out.status.success(), "channel-grid breakout sweep exit: {:?}; stderr: {}", @@ -2643,19 +2643,19 @@ fn sweep_strategy_stage1_breakout_grids_one_member_per_channel() { } /// The mean-reversion strategy reaches the CLI seam: `aura sweep --strategy -/// stage1-meanrev` emits an R-bearing member, and the folded no-trace path equals -/// the raw --trace path byte-for-byte (parity with the stage1-r / breakout +/// r-meanrev` emits an R-bearing member, and the folded no-trace path equals +/// the raw --trace path byte-for-byte (parity with the r-sma / breakout /// fold-vs-raw guard). window 3 warms up on the synthetic stream; one window × /// one band_k × the single default stop = one member. #[test] -fn sweep_strategy_stage1_meanrev_folded_no_trace_metrics_equal_raw_trace_metrics() { - let cwd = temp_cwd("sweep-stage1-meanrev-fold-vs-raw"); +fn sweep_strategy_r_meanrev_folded_no_trace_metrics_equal_raw_trace_metrics() { + let cwd = temp_cwd("sweep-r-meanrev-fold-vs-raw"); let folded = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-meanrev", "--window", "3"]) + .args(["sweep", "--strategy", "r-meanrev", "--window", "3"]) .current_dir(&cwd) .output() - .expect("spawn folded (no-trace) stage1-meanrev sweep"); + .expect("spawn folded (no-trace) r-meanrev sweep"); assert!( folded.status.success(), "folded no-trace meanrev sweep exit: {:?}; stderr: {}", @@ -2665,10 +2665,10 @@ fn sweep_strategy_stage1_meanrev_folded_no_trace_metrics_equal_raw_trace_metrics let folded_out = String::from_utf8(folded.stdout).expect("utf-8"); let raw = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-meanrev", "--window", "3", "--trace", "m1"]) + .args(["sweep", "--strategy", "r-meanrev", "--window", "3", "--trace", "m1"]) .current_dir(&cwd) .output() - .expect("spawn raw (--trace) stage1-meanrev sweep"); + .expect("spawn raw (--trace) r-meanrev sweep"); assert!( raw.status.success(), "raw --trace meanrev sweep exit: {:?}; stderr: {}", @@ -2703,16 +2703,16 @@ fn sweep_strategy_stage1_meanrev_folded_no_trace_metrics_equal_raw_trace_metrics /// Property: the meanrev family is the cartesian product of its grid axes — a /// multi-value `--window` list yields exactly one member per window value, each /// carrying its own bound window length in the manifest params. Guards the manual -/// cartesian loop in `stage1_meanrev_sweep_family`. windows 3 and 4 both warm up +/// cartesian loop in `r_meanrev_sweep_family`. windows 3 and 4 both warm up /// on the 18-bar synthetic stream, so two members must appear, in grid order. #[test] -fn sweep_strategy_stage1_meanrev_grids_one_member_per_window() { - let cwd = temp_cwd("sweep-stage1-meanrev-window-grid"); +fn sweep_strategy_r_meanrev_grids_one_member_per_window() { + let cwd = temp_cwd("sweep-r-meanrev-window-grid"); let out = Command::new(BIN) - .args(["sweep", "--strategy", "stage1-meanrev", "--window", "3,4"]) + .args(["sweep", "--strategy", "r-meanrev", "--window", "3,4"]) .current_dir(&cwd) .output() - .expect("spawn 2-window stage1-meanrev sweep"); + .expect("spawn 2-window r-meanrev sweep"); assert!( out.status.success(), "window-grid meanrev sweep exit: {:?}; stderr: {}", @@ -2735,24 +2735,24 @@ fn sweep_strategy_stage1_meanrev_grids_one_member_per_window() { let _ = std::fs::remove_dir_all(&cwd); } -/// Property: `aura walkforward --strategy stage1-r` on synthetic data reports R +/// Property: `aura walkforward --strategy r-sma` on synthetic data reports R /// quality per window AND pooled across windows — every per-window member line /// carries a `metrics.r` block (the windowed reduce-mode IS sweep folds the dense /// R-record), and the summary line carries a pooled `oos_r` block (the across-window /// reduction of the OOS per-trade R series). Deterministic: a second run is /// byte-identical (C1). #[test] -fn walkforward_strategy_stage1_r_reports_oos_r() { +fn walkforward_strategy_r_sma_reports_oos_r() { let run = || { - let cwd = temp_cwd("wf-stage1r"); + let cwd = temp_cwd("wf-r-sma"); let out = Command::new(BIN) - .args(["walkforward", "--strategy", "stage1-r"]) + .args(["walkforward", "--strategy", "r-sma"]) .current_dir(&cwd) .output() - .expect("spawn aura walkforward --strategy stage1-r"); + .expect("spawn aura walkforward --strategy r-sma"); assert!( out.status.success(), - "walkforward --strategy stage1-r exit: {:?}; stderr: {}", + "walkforward --strategy r-sma exit: {:?}; stderr: {}", out.status, String::from_utf8_lossy(&out.stderr) ); @@ -2771,10 +2771,10 @@ fn walkforward_strategy_stage1_r_reports_oos_r() { assert!(v["report"]["metrics"]["r"].is_object(), "per-window r block present"); } let out2 = run(); - assert_eq!(out, out2, "stage1-r walkforward is deterministic"); + assert_eq!(out, out2, "r-sma walkforward is deterministic"); } -/// Property: a stage1-r walk-forward stamps each OOS winner's manifest with the +/// Property: a r-sma walk-forward stamps each OOS winner's manifest with the /// trials-deflation provenance (#144), and `runs family rank` surfaces it as a /// human-readable `deflated=… P(overfit)=…` line beside each member's JSON. This is /// the end-to-end witness that the selection record reaches disk (C18) and the @@ -2783,10 +2783,10 @@ fn walkforward_strategy_stage1_r_reports_oos_r() { fn runs_family_rank_shows_deflated_line() { let cwd = temp_cwd("runs-deflated"); let wf = Command::new(BIN) - .args(["walkforward", "--strategy", "stage1-r", "--name", "wf-defl"]) + .args(["walkforward", "--strategy", "r-sma", "--name", "wf-defl"]) .current_dir(&cwd) .output() - .expect("spawn walkforward --strategy stage1-r --name wf-defl"); + .expect("spawn walkforward --strategy r-sma --name wf-defl"); assert!( wf.status.success(), "walkforward exit: {:?}; stderr: {}", @@ -2812,7 +2812,7 @@ fn runs_family_rank_shows_deflated_line() { ); } -/// Property: a stage1-r walk-forward run with `--select plateau:mean` selects the +/// Property: a r-sma walk-forward run with `--select plateau:mean` selects the /// neighbourhood-smoothed winner and stamps each OOS manifest with the plateau /// provenance (`mode = PlateauMean`, `neighbourhood_score`, `n_neighbours`); `runs /// family … rank` renders the `plateau(mean)=… over N cells` line, and the @@ -2823,7 +2823,7 @@ fn walkforward_plateau_select_stamps_plateau_provenance() { let cwd = temp_cwd("runs-plateau"); let wf = Command::new(BIN) .args([ - "walkforward", "--strategy", "stage1-r", "--select", "plateau:mean", + "walkforward", "--strategy", "r-sma", "--select", "plateau:mean", "--fast", "50,100", "--slow", "200,400", "--stop-length", "14,21", "--stop-k", "2.0,3.0", "--name", "wf-plat", @@ -2854,7 +2854,7 @@ fn walkforward_plateau_select_stamps_plateau_provenance() { } /// Property (C23, the opt-in feature's headline guarantee): `--select argmax` is a -/// no-op against the default. A stage1-r walk-forward run with an EXPLICIT +/// no-op against the default. A r-sma walk-forward run with an EXPLICIT /// `--select argmax` produces stdout byte-for-byte identical to the same run with /// no `--select` flag at all, AND keeps the trials-deflation provenance /// (`mode == Argmax`, the deflated annotation) on each OOS manifest. Pins both @@ -2882,8 +2882,8 @@ fn walkforward_select_argmax_is_byte_identical_to_default() { ); String::from_utf8(out.stdout).expect("utf-8") }; - let default = run(&["--strategy", "stage1-r"]); - let explicit = run(&["--strategy", "stage1-r", "--select", "argmax"]); + let default = run(&["--strategy", "r-sma"]); + let explicit = run(&["--strategy", "r-sma", "--select", "argmax"]); assert_eq!(default, explicit, "explicit --select argmax must be a no-op vs the default"); // argmax stayed the deflation path (not a bare argmax): the per-window member // JSON carries the Argmax mode and its deflation annotation. @@ -2905,7 +2905,7 @@ fn walkforward_plateau_worst_stamps_worst_variant_and_label() { let cwd = temp_cwd("runs-plateau-worst"); let wf = Command::new(BIN) .args([ - "walkforward", "--strategy", "stage1-r", "--select", "plateau:worst", + "walkforward", "--strategy", "r-sma", "--select", "plateau:worst", "--fast", "50,100", "--slow", "200,400", "--stop-length", "14,21", "--stop-k", "2.0,3.0", "--name", "wf-worst", @@ -2938,7 +2938,7 @@ fn walkforward_plateau_worst_stamps_worst_variant_and_label() { /// Property: the bare `aura walkforward` (default SMA-cross) summary is preserved /// byte-for-byte by the new `--strategy`/grid plumbing — it still carries exactly -/// `windows`, `stitched_total_pips`, `param_stability` and NOT the stage1-r-only +/// `windows`, `stitched_total_pips`, `param_stability` and NOT the r-sma-only /// `oos_r` block. The spec promised the bare path's golden is unchanged; the /// signature widening defaults to SmaCross and the summary gates `oos_r` on /// `metrics.r.is_some()`, so a regression that leaked R-reporting (or any other @@ -2961,7 +2961,7 @@ fn walkforward_bare_sma_summary_has_no_oos_r() { assert!(wf["windows"].is_number(), "bare SMA summary keeps windows: {wf}"); assert!(wf["stitched_total_pips"].is_number(), "bare SMA summary keeps stitched_total_pips: {wf}"); assert!(wf["param_stability"].is_array(), "bare SMA summary keeps param_stability: {wf}"); - // no per-window member line carries a metrics.r block (R-reporting is stage1-r-only) + // no per-window member line carries a metrics.r block (R-reporting is r-sma-only) for l in &lines[..lines.len() - 1] { let v: serde_json::Value = serde_json::from_str(l).unwrap(); assert!(v["report"]["metrics"]["r"].is_null(), "bare SMA per-window line must NOT carry metrics.r: {l}"); @@ -2970,44 +2970,44 @@ fn walkforward_bare_sma_summary_has_no_oos_r() { } /// Property: a strategy that parses but has no walk-forward form yet (e.g. -/// `stage1-breakout`) is rejected with exit 2 and a stderr message that names the +/// `r-breakout`) is rejected with exit 2 and a stderr message that names the /// OFFENDING strategy by its CLI token — not silently run as SMA, not crashed. /// The dispatch's catch-all arm routes through `Strategy::cli_token()`, so the -/// diagnostic must echo the token the user typed (`stage1-breakout`), proving the +/// diagnostic must echo the token the user typed (`r-breakout`), proving the /// inverse map is wired. Distinct from a bad `--strategy bogus` (a usage parse /// error): here the token is a valid strategy with no walk-forward arm. #[test] fn walkforward_unsupported_strategy_exits_2_naming_the_token() { let cwd = temp_cwd("wf-unsupported"); let out = Command::new(BIN) - .args(["walkforward", "--strategy", "stage1-breakout"]) + .args(["walkforward", "--strategy", "r-breakout"]) .current_dir(&cwd) .output() - .expect("spawn aura walkforward --strategy stage1-breakout"); + .expect("spawn aura walkforward --strategy r-breakout"); assert_eq!(out.status.code(), Some(2), "unsupported walkforward strategy must exit 2"); let stderr = String::from_utf8(out.stderr).expect("utf-8 stderr"); assert!( - stderr.contains("stage1-breakout"), + stderr.contains("r-breakout"), "stderr must name the offending strategy token: {stderr:?}" ); assert!(out.stdout.is_empty(), "no family summary on the rejected path: {:?}", out.stdout); let _ = std::fs::remove_dir_all(&cwd); } -/// Property: `aura mc --strategy stage1-r` runs the stage1-r walk-forward on +/// Property: `aura mc --strategy r-sma` runs the r-sma walk-forward on /// synthetic data, bootstraps the pooled OOS per-trade R series, and prints exactly /// one canonical `mc_r_bootstrap` line carrying an `e_r` distribution block and /// `prob_le_zero`. Deterministic: a second run with the same (default) seed is -/// byte-identical (C1). Frictionless Stage-1 — no costs, no `runs/` family write. +/// byte-identical (C1). Frictionless gross R — no costs, no `runs/` family write. #[test] -fn mc_strategy_stage1_r_prints_a_bootstrap_line_deterministically() { - let dir = temp_cwd("mc_stage1r_boot"); +fn mc_strategy_r_sma_prints_a_bootstrap_line_deterministically() { + let dir = temp_cwd("mc_r_sma_boot"); let run = || { Command::new(BIN) - .args(["mc", "--strategy", "stage1-r", "--resamples", "200"]) + .args(["mc", "--strategy", "r-sma", "--resamples", "200"]) .current_dir(&dir) .output() - .expect("spawn aura mc --strategy stage1-r") + .expect("spawn aura mc --strategy r-sma") }; let out = run(); assert!(out.status.success(), "exit: {:?} stderr: {}", out.status, String::from_utf8_lossy(&out.stderr)); @@ -3033,14 +3033,14 @@ fn mc_strategy_stage1_r_prints_a_bootstrap_line_deterministically() { /// always) would still pass the happy-path E2E. Same-seed determinism (C1) pins /// that the difference is the block length, not RNG noise. Not gated — synthetic. #[test] -fn mc_stage1_r_block_len_is_observable_and_changes_the_distribution() { - let dir = temp_cwd("mc_stage1r_block"); +fn mc_r_sma_block_len_is_observable_and_changes_the_distribution() { + let dir = temp_cwd("mc_r_sma_block"); let run = |block_len: &str| { let out = Command::new(BIN) - .args(["mc", "--strategy", "stage1-r", "--resamples", "256", "--seed", "1", "--block-len", block_len]) + .args(["mc", "--strategy", "r-sma", "--resamples", "256", "--seed", "1", "--block-len", block_len]) .current_dir(&dir) .output() - .expect("spawn aura mc --strategy stage1-r --block-len"); + .expect("spawn aura mc --strategy r-sma --block-len"); assert!(out.status.success(), "exit: {:?} stderr: {}", out.status, String::from_utf8_lossy(&out.stderr)); String::from_utf8(out.stdout).expect("utf-8 stdout") }; @@ -3069,22 +3069,22 @@ fn mc_stage1_r_block_len_is_observable_and_changes_the_distribution() { /// equal). The emitted `block_len` reports the clamped value, not the raw flag. /// This pins the clamp the engine primitive guards in-crate, now at the observable /// CLI edge where an off-by-one in the index arithmetic would otherwise surface as -/// a process crash. Not gated — synthetic stage1-r pools a fixed, non-empty series. +/// a process crash. Not gated — synthetic r-sma pools a fixed, non-empty series. #[test] -fn mc_stage1_r_oversized_block_len_clamps_and_collapses() { - let dir = temp_cwd("mc_stage1r_clamp"); +fn mc_r_sma_oversized_block_len_clamps_and_collapses() { + let dir = temp_cwd("mc_r_sma_clamp"); let out = Command::new(BIN) - .args(["mc", "--strategy", "stage1-r", "--resamples", "200", "--seed", "1", "--block-len", "9999"]) + .args(["mc", "--strategy", "r-sma", "--resamples", "200", "--seed", "1", "--block-len", "9999"]) .current_dir(&dir) .output() - .expect("spawn aura mc --strategy stage1-r --block-len 9999"); + .expect("spawn aura mc --strategy r-sma --block-len 9999"); assert!(out.status.success(), "must not crash on oversized block-len: {:?} stderr: {}", out.status, String::from_utf8_lossy(&out.stderr)); let stdout = String::from_utf8(out.stdout).expect("utf-8 stdout"); let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("canonical JSON"); let obj = &v["mc_r_bootstrap"]; let n_trades = obj["n_trades"].as_u64().expect("n_trades is an integer"); - assert!(n_trades >= 1, "synthetic stage1-r must pool a non-empty R series: {stdout}"); + assert!(n_trades >= 1, "synthetic r-sma must pool a non-empty R series: {stdout}"); // block_len is reported clamped to the pooled-series length, never the raw 9999. assert_eq!(obj["block_len"], serde_json::json!(n_trades), "oversized block-len must clamp to n_trades: {stdout}"); @@ -3096,7 +3096,7 @@ fn mc_stage1_r_oversized_block_len_clamps_and_collapses() { let _ = std::fs::remove_dir_all(&dir); } -/// Property: `aura generalize` grades a single stage1-r candidate across two +/// Property: `aura generalize` grades a single r-sma candidate across two /// instruments — its stdout carries the per-instrument breakdown + the worst-case /// floor + the sign-agreement, then the persisted CrossInstrument family handle. /// Gated on local GER40/USDJPY data (the shared Sept-2024 window), skips cleanly @@ -3108,7 +3108,7 @@ fn generalize_grades_a_candidate_across_two_instruments() { let cwd = temp_cwd("generalize-two"); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args([ - "generalize", "--strategy", "stage1-r", "--real", "GER40,USDJPY", + "generalize", "--strategy", "r-sma", "--real", "GER40,USDJPY", "--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) @@ -3140,7 +3140,7 @@ fn generalize_grades_a_candidate_across_two_instruments() { fn generalize_refuses_a_single_instrument() { let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args([ - "generalize", "--strategy", "stage1-r", "--real", "GER40", + "generalize", "--strategy", "r-sma", "--real", "GER40", "--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0", ]) .output() @@ -3154,7 +3154,7 @@ fn generalize_refuses_a_single_instrument() { fn generalize_refuses_a_non_r_metric() { let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args([ - "generalize", "--strategy", "stage1-r", "--real", "GER40,USDJPY", + "generalize", "--strategy", "r-sma", "--real", "GER40,USDJPY", "--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0", "--metric", "total_pips", ]) @@ -3177,7 +3177,7 @@ fn generalize_refuses_a_non_r_metric() { fn generalize_refuses_a_duplicate_instrument() { let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args([ - "generalize", "--strategy", "stage1-r", "--real", "GER40,GER40", + "generalize", "--strategy", "r-sma", "--real", "GER40,GER40", "--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0", ]) .output() @@ -3192,14 +3192,14 @@ fn generalize_refuses_a_duplicate_instrument() { assert!(stderr.contains("generalize"), "must surface the generalize usage, got: {stderr}"); } -/// Property: a non-`stage1-r` strategy is refused at the binary boundary (exit 2, +/// Property: a non-`r-sma` strategy is refused at the binary boundary (exit 2, /// no run, data-free). The candidate must produce R (C10) — the cross-instrument -/// reduction is R-only — so only the stage1-r grid is an admissible candidate. The +/// reduction is R-only — so only the r-sma grid is an admissible candidate. The /// parser unit test pins the grammar refusal; this pins that `--strategy sma` /// reaches `exit(2)` through the dispatch arm rather than silently defaulting to a /// running candidate. Asserts on any machine (the strategy check precedes any run). #[test] -fn generalize_refuses_a_non_stage1r_strategy() { +fn generalize_refuses_a_non_r_sma_strategy() { let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args([ "generalize", "--strategy", "sma", "--real", "GER40,USDJPY", @@ -3207,7 +3207,7 @@ fn generalize_refuses_a_non_stage1r_strategy() { ]) .output() .expect("spawn aura"); - assert_eq!(out.status.code(), Some(2), "a non-stage1-r strategy must exit 2"); + assert_eq!(out.status.code(), Some(2), "a non-r-sma strategy must exit 2"); assert!( out.stdout.is_empty(), "the refusal must not leak an aggregate to stdout, got: {:?}", @@ -3225,7 +3225,7 @@ fn generalize_refuses_a_non_stage1r_strategy() { fn generalize_refuses_a_multi_value_candidate_flag() { let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args([ - "generalize", "--strategy", "stage1-r", "--real", "GER40,USDJPY", + "generalize", "--strategy", "r-sma", "--real", "GER40,USDJPY", "--fast", "2,3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0", ]) .output() @@ -3255,7 +3255,7 @@ fn generalize_persists_a_discoverable_cross_instrument_family() { let cwd = temp_cwd("generalize-family"); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args([ - "generalize", "--strategy", "stage1-r", "--real", "GER40,USDJPY", + "generalize", "--strategy", "r-sma", "--real", "GER40,USDJPY", "--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) @@ -3296,12 +3296,12 @@ fn generalize_persists_a_discoverable_cross_instrument_family() { #[test] fn aura_sweep_loads_a_blueprint_and_persists_a_sweep_family() { let cwd = temp_cwd("blueprint-sweep"); - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = Command::new(BIN) .args([ "sweep", &fixture, - "--axis", "stage1_signal.fast.length=2,4", - "--axis", "stage1_signal.slow.length=8,16", + "--axis", "sma_signal.fast.length=2,4", + "--axis", "sma_signal.slow.length=8,16", "--trace", "f", ]) .current_dir(&cwd) @@ -3327,12 +3327,12 @@ fn aura_sweep_persists_the_canonical_blueprint_keyed_by_topology_hash() { use sha2::{Digest, Sha256}; let cwd = temp_cwd("blueprint-sweep-store"); - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = Command::new(BIN) .args([ "sweep", &fixture, - "--axis", "stage1_signal.fast.length=2,4", - "--axis", "stage1_signal.slow.length=8,16", + "--axis", "sma_signal.fast.length=2,4", + "--axis", "sma_signal.slow.length=8,16", "--trace", "f", ]) .current_dir(&cwd) @@ -3383,7 +3383,7 @@ fn aura_sweep_persists_the_canonical_blueprint_keyed_by_topology_hash() { /// from a fully-bound blueprint, which is refused earlier as "nothing to sweep". #[test] fn aura_sweep_rejects_an_unknown_axis() { - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = Command::new(BIN) .args(["sweep", &fixture, "--axis", "nope=1,2", "--name", "f"]) .output() @@ -3404,12 +3404,12 @@ fn aura_sweep_rejects_an_unknown_axis() { #[test] fn aura_reproduce_re_derives_a_persisted_sweep_family() { let cwd = temp_cwd("blueprint-reproduce"); - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let sweep = Command::new(BIN) .args([ "sweep", &fixture, - "--axis", "stage1_signal.fast.length=2,4", - "--axis", "stage1_signal.slow.length=8,16", + "--axis", "sma_signal.fast.length=2,4", + "--axis", "sma_signal.slow.length=8,16", "--trace", "f", ]) .current_dir(&cwd) @@ -3432,7 +3432,7 @@ fn aura_reproduce_re_derives_a_persisted_sweep_family() { 4, "every member re-derives bit-identically: {stdout}" ); - assert!(stdout.contains("stage1_signal.fast.length=2"), "label renders values portably: {stdout}"); + assert!(stdout.contains("sma_signal.fast.length=2"), "label renders values portably: {stdout}"); assert!(!stdout.contains("I64("), "label must not leak the Scalar Debug form: {stdout}"); assert!(stdout.contains("reproduced 4/4 members bit-identically"), "summary line: {stdout}"); let _ = std::fs::remove_dir_all(&cwd); @@ -3445,12 +3445,12 @@ fn aura_reproduce_re_derives_a_persisted_sweep_family() { #[test] fn aura_reproduce_reports_a_diverged_member_and_exits_one() { let cwd = temp_cwd("blueprint-reproduce-diverge"); - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let sweep = Command::new(BIN) .args([ "sweep", &fixture, - "--axis", "stage1_signal.fast.length=2,4", - "--axis", "stage1_signal.slow.length=8,16", + "--axis", "sma_signal.fast.length=2,4", + "--axis", "sma_signal.slow.length=8,16", "--trace", "f", ]) .current_dir(&cwd) @@ -3484,16 +3484,16 @@ fn aura_reproduce_reports_a_diverged_member_and_exits_one() { #[test] fn aura_reproduce_re_derives_a_persisted_sweep_bit_identically() { let cwd = temp_cwd("reproduce_roundtrip"); - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let sweep = std::process::Command::new(BIN) .args([ "sweep", &fixture, "--axis", - "stage1_signal.fast.length=2", + "sma_signal.fast.length=2", "--axis", - "stage1_signal.slow.length=4,6", + "sma_signal.slow.length=4,6", "--name", "smacross", ]) @@ -3523,7 +3523,7 @@ fn aura_reproduce_re_derives_a_persisted_sweep_bit_identically() { #[test] fn aura_mc_over_a_blueprint_reproduces_bit_identically() { let cwd = temp_cwd("mc-blueprint-reproduce"); - let fixture = format!("{}/tests/fixtures/stage1_signal.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal.json", env!("CARGO_MANIFEST_DIR")); let mc = Command::new(BIN) .args(["mc", &fixture, "--seeds", "4", "--name", "mcx"]) @@ -3568,10 +3568,10 @@ fn aura_mc_over_a_blueprint_reproduces_bit_identically() { #[test] fn aura_walkforward_over_a_blueprint_reproduces_bit_identically() { let cwd = temp_cwd("blueprint-walkforward-reproduce"); - let bp = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let run = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) - .args(["walkforward", &bp, "--axis", "stage1_signal.fast.length=2,3", - "--axis", "stage1_signal.slow.length=4,6", "--name", "wfr"]) + .args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3", + "--axis", "sma_signal.slow.length=4,6", "--name", "wfr"]) .current_dir(&cwd) .output() .expect("spawn aura walkforward"); @@ -3593,10 +3593,10 @@ fn aura_walkforward_over_a_blueprint_reproduces_bit_identically() { #[test] fn aura_walkforward_over_a_blueprint_persists_a_family() { let cwd = temp_cwd("blueprint-walkforward-persist"); - let bp = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) - .args(["walkforward", &bp, "--axis", "stage1_signal.fast.length=2,3", - "--axis", "stage1_signal.slow.length=4,6", "--name", "wfx"]) + .args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3", + "--axis", "sma_signal.slow.length=4,6", "--name", "wfx"]) .current_dir(&cwd) .output() .expect("spawn aura walkforward"); @@ -3618,7 +3618,7 @@ fn aura_walkforward_over_a_blueprint_persists_a_family() { /// error (exit 2), naming that >= 1 --axis is required. #[test] fn aura_walkforward_over_a_blueprint_rejects_no_axis() { - let bp = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["walkforward", &bp]) .output() @@ -3634,7 +3634,7 @@ fn aura_walkforward_over_a_blueprint_rejects_no_axis() { fn aura_walkforward_over_a_blueprint_rejects_malformed() { let bp = format!("{}/tests/fixtures/unknown_node.json", env!("CARGO_MANIFEST_DIR")); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) - .args(["walkforward", &bp, "--axis", "stage1_signal.fast.length=2,3"]) + .args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3"]) .output() .expect("spawn aura walkforward (malformed)"); assert_eq!(out.status.code(), Some(2), "malformed must be rejected, not panic"); @@ -3653,7 +3653,7 @@ fn aura_walkforward_over_a_blueprint_rejects_malformed() { /// while leaving the malformed test green. #[test] fn aura_walkforward_over_a_blueprint_rejects_an_unknown_axis() { - let bp = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["walkforward", &bp, "--axis", "nope=1,2"]) .output() @@ -3685,7 +3685,7 @@ fn aura_walkforward_over_a_blueprint_rejects_an_unknown_axis() { /// fan-out) makes every run deterministically single. #[test] fn aura_walkforward_emits_an_unknown_axis_rejection_once() { - let bp = format!("{}/tests/fixtures/stage1_signal.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal.json", env!("CARGO_MANIFEST_DIR")); for attempt in 1..=20 { let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["walkforward", &bp, "--axis", "graph.fast.length=2,3"]) @@ -3707,7 +3707,7 @@ fn aura_walkforward_emits_an_unknown_axis_rejection_once() { #[test] fn aura_mc_rejects_an_open_blueprint() { let cwd = temp_cwd("mc-blueprint-open-reject"); - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = Command::new(BIN) .args(["mc", &fixture, "--seeds", "4"]) .current_dir(&cwd) @@ -3727,7 +3727,7 @@ fn aura_mc_rejects_an_open_blueprint() { #[test] fn aura_run_rejects_an_open_blueprint_without_panicking() { let cwd = temp_cwd("run-blueprint-open-reject"); - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = Command::new(BIN) .args(["run", &fixture]) .current_dir(&cwd) @@ -3801,15 +3801,15 @@ fn aura_reproduce_rejects_a_non_generated_family() { #[test] fn aura_reproduce_rejects_a_missing_stored_blueprint() { let cwd = temp_cwd("reproduce_missing_store"); - let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let fixture = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let sweep = std::process::Command::new(BIN) .args([ "sweep", &fixture, "--axis", - "stage1_signal.fast.length=2", + "sma_signal.fast.length=2", "--axis", - "stage1_signal.slow.length=4", + "sma_signal.slow.length=4", "--name", "bp", ]) @@ -3834,25 +3834,25 @@ fn aura_reproduce_rejects_a_missing_stored_blueprint() { /// E2E (#169): `aura sweep --list-axes` prints one /// `:` line per open sweepable knob, in `param_space()` order, and -/// exits 0. The names are prefixed by the wrapping (stage1_signal.*) — exactly +/// exits 0. The names are prefixed by the wrapping (sma_signal.*) — exactly /// the strings `--axis` binds — so a user can discover then sweep without guessing. #[test] fn aura_sweep_list_axes_prints_prefixed_names() { - let bp = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["sweep", &bp, "--list-axes"]) .output() .expect("spawn aura sweep --list-axes"); assert_eq!(out.status.code(), Some(0)); let stdout = String::from_utf8(out.stdout).expect("utf-8"); - assert_eq!(stdout, "stage1_signal.fast.length:I64\nstage1_signal.slow.length:I64\n"); + assert_eq!(stdout, "sma_signal.fast.length:I64\nsma_signal.slow.length:I64\n"); } /// E2E (#169): `--list-axes` on a CLOSED blueprint (all knobs bound) prints /// nothing and exits 0 — an empty axis set is the honest answer, not an error. #[test] fn aura_sweep_list_axes_closed_is_empty() { - let bp = format!("{}/tests/fixtures/stage1_signal.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal.json", env!("CARGO_MANIFEST_DIR")); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["sweep", &bp, "--list-axes"]) .output() @@ -3866,9 +3866,9 @@ fn aura_sweep_list_axes_closed_is_empty() { /// and takes no other flags. A query cannot also seed a sweep. #[test] fn aura_sweep_list_axes_rejects_other_flags() { - let bp = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) - .args(["sweep", &bp, "--list-axes", "--axis", "stage1_signal.fast.length=2,3"]) + .args(["sweep", &bp, "--list-axes", "--axis", "sma_signal.fast.length=2,3"]) .output() .expect("spawn aura sweep --list-axes + --axis"); assert_eq!(out.status.code(), Some(2)); @@ -3904,7 +3904,7 @@ fn aura_sweep_list_axes_rejects_malformed_blueprint() { #[test] fn aura_sweep_list_axes_names_round_trip_into_a_working_sweep() { let cwd = temp_cwd("blueprint-list-axes-roundtrip"); - let bp = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR")); + let bp = format!("{}/tests/fixtures/sma_signal_open.json", env!("CARGO_MANIFEST_DIR")); // 1) discover the axis names (do NOT hardcode them) — this is what a user reads. let list = Command::new(BIN) diff --git a/crates/aura-cli/tests/fixtures/sma_signal.json b/crates/aura-cli/tests/fixtures/sma_signal.json new file mode 100644 index 0000000..cbdc8c7 --- /dev/null +++ b/crates/aura-cli/tests/fixtures/sma_signal.json @@ -0,0 +1 @@ +{"format_version":1,"blueprint":{"name":"sma_signal","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/crates/aura-cli/tests/fixtures/sma_signal_open.json b/crates/aura-cli/tests/fixtures/sma_signal_open.json new file mode 100644 index 0000000..52ec036 --- /dev/null +++ b/crates/aura-cli/tests/fixtures/sma_signal_open.json @@ -0,0 +1 @@ +{"format_version":1,"blueprint":{"name":"sma_signal","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/crates/aura-cli/tests/fixtures/stage1_signal.json b/crates/aura-cli/tests/fixtures/stage1_signal.json deleted file mode 100644 index b5bb17c..0000000 --- a/crates/aura-cli/tests/fixtures/stage1_signal.json +++ /dev/null @@ -1 +0,0 @@ -{"format_version":1,"blueprint":{"name":"stage1_signal","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/crates/aura-cli/tests/fixtures/stage1_signal_open.json b/crates/aura-cli/tests/fixtures/stage1_signal_open.json deleted file mode 100644 index a7cb961..0000000 --- a/crates/aura-cli/tests/fixtures/stage1_signal_open.json +++ /dev/null @@ -1 +0,0 @@ -{"format_version":1,"blueprint":{"name":"stage1_signal","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/crates/aura-cli/tests/fixtures/unknown_node.json b/crates/aura-cli/tests/fixtures/unknown_node.json index 7516354..04352fe 100644 --- a/crates/aura-cli/tests/fixtures/unknown_node.json +++ b/crates/aura-cli/tests/fixtures/unknown_node.json @@ -1 +1 @@ -{"format_version":1,"blueprint":{"name":"stage1_signal","nodes":[{"primitive":{"type":"Nope","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} \ No newline at end of file +{"format_version":1,"blueprint":{"name":"sma_signal","nodes":[{"primitive":{"type":"Nope","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/crates/aura-cli/tests/graph_construct.rs b/crates/aura-cli/tests/graph_construct.rs index 8c123fc..46c198a 100644 --- a/crates/aura-cli/tests/graph_construct.rs +++ b/crates/aura-cli/tests/graph_construct.rs @@ -234,7 +234,7 @@ fn graph_build_renders_role_kind_mismatch_at_finalize_by_identifier() { /// runs and distinct for a structurally different topology. It is the same hash `aura run` /// stamps as `topology_hash` (both via the one `content_id` primitive over the canonical /// `blueprint_to_json` bytes), so a data-authored topology has a stable, comparable content -/// id. (Note: an op-script and the Rust `stage1_signal` builder produce *different* +/// id. (Note: an op-script and the Rust `sma_signal` builder produce *different* /// canonical forms — composite debug-name, an unbound `Bias.scale` — so they are different /// topologies by the byte definition; content-addressing keys on the canonical form.) #[test] diff --git a/crates/aura-composites/src/lib.rs b/crates/aura-composites/src/lib.rs index 2de8ac4..0ab7eac 100644 --- a/crates/aura-composites/src/lib.rs +++ b/crates/aura-composites/src/lib.rs @@ -1,4 +1,4 @@ -//! `aura-composites` — reusable Stage-1 composite-builders: the volatility stop and +//! `aura-composites` — reusable composite-builders for the feed-forward R loop: the volatility stop and //! the per-symbol RiskExecutor. Both are `GraphBuilder` compositions (from //! `aura-engine`) of `aura-std` primitives, so this crate sits *above* both and is //! the single place where the engine's builder and the standard nodes are wired @@ -35,7 +35,7 @@ pub fn vol_stop(length: i64, k: f64) -> Composite { /// are `named` `stop_length` / `stop_k` so their slots surface in `param_space` under /// the `.stop_length.length` / `.stop_k.weights[0]` suffixes). One body, so the /// topology — every node, edge, and exposed role — cannot drift between the two arms; -/// only the two knobs differ (the `Option`-knob idiom `stage1_r_graph` uses). +/// only the two knobs differ (the `Option`-knob idiom `r_sma_graph` uses). fn vol_stop_inner(knobs: Option<(i64, f64)>) -> Composite { let mut g = GraphBuilder::new("vol_stop"); let price = g.input_role("price"); diff --git a/crates/aura-composites/tests/risk_executor.rs b/crates/aura-composites/tests/risk_executor.rs index 0425c22..7120ffe 100644 --- a/crates/aura-composites/tests/risk_executor.rs +++ b/crates/aura-composites/tests/risk_executor.rs @@ -1,8 +1,8 @@ -//! The `RiskExecutor` composite (Stage-1, #128): a per-symbol risk-based executor over a +//! The `RiskExecutor` composite (#128): a per-symbol risk-based executor over a //! bias stream — `stop-rule -> Sizer -> position-management`, exposing the dense R-record. //! Proves the composite bootstraps, runs end-to-end, and folds to the SAME R-outcomes as //! the hand-wired iter-1 chain, and that R is invariant under the Sizer's `risk_budget` -//! (Stage-1 feed-forward). The Veto is a DOCUMENTED SEAM, not a runtime node (a +//! (feed-forward). The Veto is a DOCUMENTED SEAM, not a runtime node (a //! pass-through identity is exactly what C19/C23 DCE deletes), so it appears nowhere here. use aura_core::{ Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar, @@ -14,7 +14,7 @@ use aura_std::{Recorder, PM_FIELD_NAMES, PM_RECORD_KINDS}; use std::sync::mpsc::channel; // The dense-record columns this fixture reads, named in lockstep with the sibling -// `stage1_r_e2e.rs` (which names `REALIZED_R = 1` the same way) so the cross-crate +// `r_sma_e2e.rs` (which names `REALIZED_R = 1` the same way) so the cross-crate // `PM_FIELD_NAMES` layout is never referenced by a bare literal. const REALIZED_R: usize = 1; const SIZE: usize = 10; @@ -100,7 +100,7 @@ fn risk_executor_bootstraps_and_folds_to_expected_rmetric() { assert!((m.expectancy_r - 0.5).abs() < 1e-9, "window-end R = (105-100)/10; got {}", m.expectancy_r); } -/// Property: R is invariant under the Sizer's `risk_budget` (Stage-1 feed-forward). The +/// Property: R is invariant under the Sizer's `risk_budget` (feed-forward). The /// same price path at two budgets yields a bit-identical realised-R ledger, while the /// `size` column scales with the budget — proving size flows through the Sizer into PM yet /// never touches R. @@ -127,7 +127,7 @@ fn risk_executor_r_invariant_under_risk_budget() { } /// Property: **a real folded `RMetrics` survives the `RunMetrics.r` serde round-trip -/// byte-for-byte (the Stage-1 on-disk back-compat contract), driven from an actual run — +/// byte-for-byte (the on-disk back-compat contract), driven from an actual run — /// not a hand-built literal.** A bootstrapped RiskExecutor run is folded by `summarize_r` /// and the result is attached as `RunMetrics.r = Some(..)`; serializing then /// deserializing must reproduce an equal value, and the `r` key must be present. The @@ -148,7 +148,7 @@ fn folded_rmetrics_survives_runmetrics_serde_round_trip() { } /// Property: the RiskExecutor embeds the `vol_stop` composite (the new `StopRule::Vol` -/// arm) and folds to a finite RMetric — the volatility-defined default the `stage1-r` +/// arm) and folds to a finite RMetric — the volatility-defined default the `r-sma` /// harness uses. A short k·σ stop over a rising-then-falling path opens a trade and /// (stop or window-end) closes at least one, so the fold is non-empty and sane. #[test] @@ -246,7 +246,7 @@ fn risk_executor_vol_open_exposes_the_two_stop_knobs_as_free_axes() { /// Property (#137): the OPEN vol-stop arm, bootstrapped at a given `(length, k)`, folds to /// the SAME R-outcome as the BOUND arm at the same values — the byte-equivalence the -/// no-flags stage1-r sweep relies on to stay golden (the gridding variant only frees the +/// no-flags r-sma sweep relies on to stay golden (the gridding variant only frees the /// two knobs; topology and every other constant are unchanged). The open arm's positional /// point is `[length, k]` in `param_space` slot order (length first, then k). #[test] diff --git a/crates/aura-engine/Cargo.toml b/crates/aura-engine/Cargo.toml index c98fb61..c59bb46 100644 --- a/crates/aura-engine/Cargo.toml +++ b/crates/aura-engine/Cargo.toml @@ -22,7 +22,7 @@ serde_json = { workspace = true } [dev-dependencies] # aura-std is a TEST-ONLY dependency: the engine's own integration tests -# (stage1_r_e2e, random_sweep_e2e, ger40_breakout) drive real standard nodes through +# (r_sma_e2e, random_sweep_e2e, ger40_breakout) drive real standard nodes through # a bootstrapped Harness. The engine's library code never names a concrete node — it # routes type-erased Scalar records (summarize_r reads the PositionManagement record # positionally, by index), so aura-std stays out of [dependencies] and the engine is diff --git a/crates/aura-engine/tests/stage1_breakout_e2e.rs b/crates/aura-engine/tests/r_breakout_e2e.rs similarity index 100% rename from crates/aura-engine/tests/stage1_breakout_e2e.rs rename to crates/aura-engine/tests/r_breakout_e2e.rs diff --git a/crates/aura-engine/tests/stage1_meanrev_e2e.rs b/crates/aura-engine/tests/r_meanrev_e2e.rs similarity index 98% rename from crates/aura-engine/tests/stage1_meanrev_e2e.rs rename to crates/aura-engine/tests/r_meanrev_e2e.rs index ee1c8ce..046d8cc 100644 --- a/crates/aura-engine/tests/stage1_meanrev_e2e.rs +++ b/crates/aura-engine/tests/r_meanrev_e2e.rs @@ -3,7 +3,7 @@ //! {Gt, Gt} -> {Latch, Latch} -> Sub = bias in {-1,0,+1}. Tests the FADE //! direction (price above mean+k*sigma -> short -1; below mean-k*sigma -> long //! +1), the latched hold, and that no fade fires on a flat series. Built -//! straight from aura-std nodes (no CLI dependency); mirrors stage1_breakout_e2e. +//! straight from aura-std nodes (no CLI dependency); mirrors r_breakout_e2e. use aura_core::{Scalar, Timestamp}; use aura_engine::{GraphBuilder, Harness, Source, VecSource}; diff --git a/crates/aura-engine/tests/stage1_r_e2e.rs b/crates/aura-engine/tests/r_sma_e2e.rs similarity index 99% rename from crates/aura-engine/tests/stage1_r_e2e.rs rename to crates/aura-engine/tests/r_sma_e2e.rs index bc0fc4a..3f4b7a5 100644 --- a/crates/aura-engine/tests/stage1_r_e2e.rs +++ b/crates/aura-engine/tests/r_sma_e2e.rs @@ -1,4 +1,4 @@ -//! Stage-1 R E2E: a synthetic bias+price chain through stop-rule + position-management, +//! R E2E: a synthetic bias+price chain through stop-rule + position-management, //! recorded and folded by `summarize_r`. Also guards the dense-record layout contract //! (aura-std's `PM_FIELD_NAMES`/`PM_RECORD_KINDS` vs the column indices `summarize_r` //! reads). @@ -94,7 +94,7 @@ fn synthetic_long_then_stop_produces_a_sane_rmetric() { /// Drive the real cross-crate chain `stop -> PositionManagement` over a `(bias, price)` /// path, collecting the producer's dense records (decoded by the producer's own -/// `PM_RECORD_KINDS`, the wire contract) into a ledger — the recorded stream a Stage-1 +/// `PM_RECORD_KINDS`, the wire contract) into a ledger — the recorded stream an R-yardstick /// run would persist. Per-cycle bias (not constant) so a fixture can drop bias to 0 to /// forbid the immediate re-entry after a stop. The fold is left to the caller so the /// same recorded ledger can be summarized at several `round_trip_cost` values. @@ -124,7 +124,7 @@ fn run_chain_ledger(stop: &mut dyn Node, path: &[(f64, f64)]) -> Vec<(Timestamp, /// Drive the real cross-crate chain `stop -> PositionManagement -> summarize_r` /// over a `(bias, price)` path. This is the actual producer->consumer seam, -/// node-by-node; the resulting `RMetrics` is what a recorded Stage-1 run would report. +/// node-by-node; the resulting `RMetrics` is what a recorded R-yardstick run would report. fn run_chain(stop: &mut dyn Node, path: &[(f64, f64)]) -> RMetrics { summarize_r(&run_chain_ledger(stop, path), &[]) } diff --git a/crates/aura-engine/tests/streaming_reduction_equivalence.rs b/crates/aura-engine/tests/streaming_reduction_equivalence.rs index 47657fa..22f20d6 100644 --- a/crates/aura-engine/tests/streaming_reduction_equivalence.rs +++ b/crates/aura-engine/tests/streaming_reduction_equivalence.rs @@ -1,5 +1,5 @@ //! Folded sink reductions are byte-for-byte equal to the raw-recorder path. -//! Drives the folding sinks node-by-node (the `stage1_r_e2e.rs` direct-node +//! Drives the folding sinks node-by-node (the `r_sma_e2e.rs` direct-node //! style) over a synthetic dense R-record + equity/exposure series, and asserts //! `summarize_r` over `GatedRecorder`'s emitted rows equals `summarize_r` over //! the full record, and a `SeriesReducer` summary equals `summarize`'s fields. diff --git a/crates/aura-std/src/position_management.rs b/crates/aura-std/src/position_management.rs index 206a542..b8802c7 100644 --- a/crates/aura-std/src/position_management.rs +++ b/crates/aura-std/src/position_management.rs @@ -1,4 +1,4 @@ -//! `PositionManagement` — the stateful heart of Stage-1 risk-based execution. Turns a +//! `PositionManagement` — the stateful heart of feed-forward risk-based execution. Turns a //! bias + a protective-stop distance into a stream of realised per-trade R-outcomes. //! Emits ONE dense record per cycle (C8, like `SimBroker`; multi-field output on the //! `Resample` precedent): the trade ledger is the rows where `closed_this_cycle`, the @@ -146,7 +146,7 @@ impl Node for PositionManagement { } else { price.max(p.stop_level) }; - // size-invariant: R is a pure stop-distance ratio (Stage-1 feed-forward). + // size-invariant: R is a pure stop-distance ratio (feed-forward). realized = p.dir as f64 * (fill - p.entry) / p.latched_dist; was_stopped = true; reason = ExitReason::Stop as i64; @@ -157,7 +157,7 @@ impl Node for PositionManagement { closed = true; self.pos = None; } else if bias_exit { - // size-invariant: R is a pure stop-distance ratio (Stage-1 feed-forward). + // size-invariant: R is a pure stop-distance ratio (feed-forward). realized = p.dir as f64 * (price - p.entry) / p.latched_dist; reason = if sign0(bias) != 0.0 { ExitReason::ReversalLeg as i64 @@ -275,7 +275,7 @@ mod tests { } // (3) R-invariance under size: the Sizer's `size` flows into col 10 but never into R. - // Scaling size leaves every realized_r identical (the property that keeps Stage 1 + // Scaling size leaves every realized_r identical (the property that keeps the research loop // feed-forward); col 10 reflects the size so we know it actually flowed end-to-end. #[test] fn realized_r_is_invariant_under_size() { diff --git a/crates/aura-std/src/sizer.rs b/crates/aura-std/src/sizer.rs index d36fd1b..cbdef69 100644 --- a/crates/aura-std/src/sizer.rs +++ b/crates/aura-std/src/sizer.rs @@ -1,11 +1,11 @@ -//! `Sizer` — the flat-1R sizing seam (C10 Stage-1). Turns a protective-stop distance +//! `Sizer` — the flat-1R sizing seam (C10). Turns a protective-stop distance //! into a position `size = risk_budget / stop_distance`: with `risk_budget = 1.0` this is //! true flat-1R (one risk unit per trade) and `size` is inversely proportional to the //! stop — NOT a constant. The `bias` input gates firing (a size is only meaningful when a -//! strategy is taking a position) and is the Stage-2 seam: fixed-fractional sizing swaps -//! `risk_budget` for `risk_fraction · equity` (an added equity input), same node shape. -//! R is computed SIZE-INVARIANTLY downstream, so the Sizer never contaminates signal -//! quality — it only scales the (Stage-2) currency exposure. +//! strategy is taking a position) and is the live/deploy-edge seam: fixed-fractional +//! sizing swaps `risk_budget` for `risk_fraction · equity` (an added equity input), same +//! node shape. R is computed SIZE-INVARIANTLY downstream, so the Sizer never contaminates +//! signal quality — it only scales the (deploy-edge) currency exposure. use aura_core::{ Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder, ScalarKind, diff --git a/docs/design/INDEX.md b/docs/design/INDEX.md index de6be30..da629c7 100644 --- a/docs/design/INDEX.md +++ b/docs/design/INDEX.md @@ -614,7 +614,7 @@ the ground truth**. The cost model *approximates*; it **never claims realism**. `SimBroker` (the pip-equity, unsized-exposure node) is the **pre-reframe pip ancestor** — with the net-R cost model it is **redundant as a quality measure** (R supersedes pips). It is retained as a **legacy / simple optional pip yardstick** -(still wired in the `stage1-r` harness for an honest dual readout), **not part of +(still wired in the `r-sma` harness for an honest dual readout), **not part of the new model and not to be expanded**. **Forbids.** Putting **sizing or the stop in the strategy** (bias is unsized; the @@ -737,7 +737,7 @@ co-temporal cost stream (positional 1:1 join — `cost[i]` is `record[i]`'s cycl cost = 0 baseline on an empty stream. The headline sink is **`net_r_equity`** = `LinComb(4)[cum_realized_r, unrealized_r, −cum_cost_in_r, −open_cost_in_r]` → Recorder (C8/C18), a sibling of `r_equity`, emitted only when a cost is authored. Wired on the -**run path** via `--cost-per-trade` (`stage1_r_graph`); sweep / walk-forward / mc pass +**run path** via `--cost-per-trade` (`r_sma_graph`); sweep / walk-forward / mc pass `None` (cost on the reduce-mode sweep path and cost in OOS pooling deferred — the positional join holds only while one cost node fires in lockstep with PM). Deferred to later milestone cycles: the general `CostNode` trait + multi-node cost-graph @@ -928,8 +928,8 @@ composite-builder (`risk_executor(StopRule, risk_budget)`) with a `StopRule{Fixed,Vol}` **structural axis** (C20); per the 2026-06-28 reframe its Sizer interior and `risk_budget` arg are dropped / vestigial, the **Veto** stays a documented seam, not a runtime node. The layer is operable from the CLI: `aura run ---harness ` — a compile-time selector over Rust-authored -harnesses (C9/C17) — folds `summarize_r` into `RunMetrics.r`, the `stage1-r` +--harness ` — a compile-time selector over Rust-authored +harnesses (C9/C17) — folds `summarize_r` into `RunMetrics.r`, the `r-sma` harness fanning one bias into both `SimBroker` (legacy pip) and the RiskExecutor (R); an `r_equity` tap charts the by-trade R-equity. Composites live in the dedicated `aura-composites` crate, so `aura-engine`'s runtime dependency stays @@ -937,10 +937,10 @@ dedicated `aura-composites` crate, so `aura-engine`'s runtime dependency stays stays acyclic). **Realization (cycle 0066).** SQN is the operational single-number objective for -ranking a Stage-1 sweep family by signal quality — C12 **axis-2 (argmax-metric)** +ranking an r-sma sweep family by signal quality — C12 **axis-2 (argmax-metric)** over the C18 family store. `metric_cmp` (`aura-registry`) learns the higher-is-better R metrics `sqn`, `expectancy_r`, `net_expectancy_r`; a member -with no `r` block sorts last (`NEG_INFINITY`). `aura sweep --strategy stage1-r` +with no `r` block sorts last (`NEG_INFINITY`). `aura sweep --strategy r-sma` produces the rankable R family, each member folding `summarize_r` into `RunMetrics.r`. The default grid varies **only the signal** (`fast` / `slow` SMA lengths), holding the stop and sizing fixed: `risk_budget` is R-invariant and @@ -954,8 +954,8 @@ beside the floated knobs (reproducible from its own manifest, C18). (`SQN_CAP = 100`), turnover-robust where raw `sqn` rewards trade count. It is an **opt-in** rank key (`Metric::SqnNormalized`); raw `sqn` and the default ranker stay byte-unchanged, and the field carries `#[serde(default)]` (C18). Below the cap -(`n ≤ 100`) it equals raw `sqn` exactly. **#135 (stage1-r `--trace`):** -`stage1_r_sweep_family` persists each member's equity / exposure / r_equity under +(`n ≤ 100`) it equals raw `sqn` exactly. **#135 (r-sma `--trace`):** +`r_sma_sweep_family` persists each member's equity / exposure / r_equity under `runs/traces///` via the same `persist_traces_r` the single run uses; per-member `--trace` is symmetric across all three sweep strategies. @@ -1327,7 +1327,7 @@ one `content_id` primitive `topology_hash` also uses (acc 1); a Tier-1 optional blueprint does not use leaves the id byte-stable (acc 3, composing #156/#164). `serde_json/float_roundtrip` is enabled so stored f64 metrics round-trip exactly through `families.jsonl` — the precondition for a bit-identical compare (C1). **Signal-only this -cycle**: the id covers the signal blueprint; the fixed Stage-1-R scaffolding stays +cycle**: the id covers the signal blueprint; the fixed r-sma scaffolding stays commit-identified (it is not yet blueprint-data, C24). Whole-harness / structural-axis content-addressing, and whether the id should exclude the composite **debug-name** (a non-load-bearing symbol, invariant 11 — an op-script and the Rust builder produce @@ -1887,7 +1887,7 @@ Tier-2 section to validate it; recorded on #156). **Realization (2026-06-30, cycle 0092 — runs are built FROM blueprint-data, #165).** `aura run ` now loads a serialized **signal** blueprint (`blueprint_from_json` → the closed `std_vocabulary`; an unknown type fails clean as -`UnknownNodeType`, the data-plane face of invariant 9), wraps it in the Stage-1-R run +`UnknownNodeType`, the data-plane face of invariant 9), wraps it in the r-sma run scaffolding (sinks / broker / data supplied **at run**, not serialized — C24's deferred set), runs it, and emits a `RunReport` **bit-identical** (C1) to its Rust-built twin — proven by `loaded_signal_runs_bit_identical_to_rust_built`. The `RunManifest` now carries @@ -1895,8 +1895,8 @@ a **`topology_hash`**: SHA256 of the canonical (#164) `blueprint_to_json`, the # reproducibility anchor, a Tier-1 optional field (#156). The hash + helper live research-side (`aura-cli` + `sha2`), off the frozen engine (invariant 8). This is the **keystone of the World/C21 milestone**: topology-as-data is now *runnable*, not only -serializable. The harness wrap was made shareable — `stage1_r_graph()` = -`wrap_stage1r(stage1_signal(...))`, so the Rust path and the data path are the same seam +serializable. The harness wrap was made shareable — `r_sma_graph()` = +`wrap_r(sma_signal(...))`, so the Rust path and the data path are the same seam over the same signal (a behaviour-preserving C19/C23 restructure). Scope note: the hash covers the **signal** only (the fixed scaffolding is identified by `commit`); a content-id distinguishing harness-structural variants is a #158/#166 concern once scaffolding varies. @@ -1904,7 +1904,7 @@ distinguishing harness-structural variants is a #158/#166 concern once scaffoldi **Realization (2026-07-01, cycle 0093 — the World orchestrates FAMILIES from blueprint-data, #166).** `aura sweep --axis =` loads an **open** signal blueprint, grids the by-name axes against its `param_space()`, builds each member through cycle-1's -`wrap_stage1r` seam, and aggregates to a `FamilyKind::Sweep` family — byte-identical in shape +`wrap_r` seam, and aggregates to a `FamilyKind::Sweep` family — byte-identical in shape to the hard-wired sweep (`append_family` / `sweep_member_reports` reused verbatim). This is the C21 step beyond a single run: the World now constructs and orchestrates **families** of harnesses from topology-data, not just one. Each member manifest carries the **shared** @@ -1928,7 +1928,7 @@ the loaded blueprint's params over the user `--axis` grid (the #169 prefixed nam `select_winner` reused verbatim), runs it out-of-sample, and aggregates to a `FamilyKind::WalkForward` family — persisted + content-addressed + `aura reproduce` bit-identical (the read-side WalkForward branch rebuilds each OOS window from `manifest.window`). One -substitution deep from the hard-wired stage1-r WF arm (the loaded blueprint via +substitution deep from the hard-wired r-sma WF arm (the loaded blueprint via `blueprint_axis_probe` replaces the built-in strategy); a bad `--axis` is a clean in-closure exit 2, never a panic. This is Arm A (the settled direction): the loaded IS-optimizing form honestly carries the `walkforward` name. Reduce-mode members are R-measured (`oos_r` the @@ -1942,11 +1942,11 @@ prints are exactly what `--axis` binds. Every listed name is **mandatory** on a `walkforward`: the blueprint must be fully bound before it runs, so a subset grid is refused with the missing knob named (`BindError::MissingKnob`) and there is no default — pin a knob you do not want to vary with a single-value axis (`--axis =`). A single `blueprint_axis_probe` helper now single-sources -the wrapped probe (`wrap_stage1r(loaded_signal).param_space()`) for the sweep terminal, the MC +the wrapped probe (`wrap_r(loaded_signal).param_space()`) for the sweep terminal, the MC closed-check, AND the listing (three former inline copies → one), so **listed == swept by construction** (and stays so across #159's harness retirement — the listing tracks whatever the sweep actually resolves, never a second source of truth). The names are prefixed by the current -stage1-r wrapping (`stage1_signal.fast.length`, the nested-composite prefix), not the raw +r-sma wrapping (`sma_signal.fast.length`, the nested-composite prefix), not the raw `param_space` — which is why the discovery lives on the sweep verb (it owns the wrapping), not `graph introspect`. Per-member trace-writing on `--trace` (#168) remains tracked debt. @@ -1956,7 +1956,7 @@ content-addressed blueprint store (`aura reproduce`) shipped cycle 0094 (see C18 Realization). What **remains** is a content-id that covers **structural-axis / whole-harness variants** (the scaffolding is not yet blueprint-data) and the debug-name-in-id question (#171/#170). Still deferred: retiring the pre-C24 -hard-wired `aura-cli` harnesses (`HarnessKind`, `run_stage1_r`, `*_sweep_family`) once +hard-wired `aura-cli` harnesses (`HarnessKind`, `run_r_sma`, `*_sweep_family`) once the project-as-crate layer lands (#159, paired with #157's data-authoring surface). **Out of the first cut's round-trippable set** (deliberate; fails clean as `UnknownNodeType`, never a silent wrong graph): recording sinks (capture an `mpsc::Sender` — runtime identity, not param-generic data, C19) and diff --git a/docs/glossary.md b/docs/glossary.md index 744482e..6c9cd7e 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -93,7 +93,7 @@ The set of harness instances produced by varying the structural axes (strategy ### exposure stream **Avoid:** — (superseded) -**Superseded by `bias` (2026-06 R-reframe).** The pre-reframe primary output: a signed, bounded `f64 ∈ [-1,+1]` per cycle read as the desired fractional *position* (conflating direction + conviction + size). Reframed to `bias` (unsized direction + conviction), sizing moved downstream to the `Sizer`/`RiskExecutor`; the term and the `Exposure` node persist in pre-reframe code until the Stage-1 rename. +**Superseded by `bias` (2026-06 R-reframe).** The pre-reframe primary output: a signed, bounded `f64 ∈ [-1,+1]` per cycle read as the desired fractional *position* (conflating direction + conviction + size). Reframed to `bias` (unsized direction + conviction), sizing moved downstream to the `Sizer`/`RiskExecutor`; the term and the `Exposure` node persisted in pre-reframe code until the r-family rename (#174). ### firing policy **Avoid:** — @@ -249,7 +249,7 @@ System Quality Number — `√n · mean_R / stdev_R`: the dispersion- and trade- ### Stage 1 / Stage 2 **Avoid:** — (superseded) -**Superseded (2026-06-28 C10 reframe): there is no Stage-2 currency/compounding gate.** Research is one pure feed-forward R loop — gross R → net R via the `cost model` — with money, compounding, and a real broker living only at a separate later **live / deploy edge** (compounding is a post-hoc money-management transform of the net-R sequence, not an in-loop axis). The pre-reframe cleave gated a Stage-2 currency / fixed-fractional / realistic-broker layer behind `E[R] > 0`; **"Stage-1"** now survives only as a historical cycle / identifier name for the shipped feed-forward R chain, not one half of a two-stage gate. +**Superseded (2026-06-28 C10 reframe): there is no Stage-2 currency/compounding gate.** Research is one pure feed-forward R loop — gross R → net R via the `cost model` — with money, compounding, and a real broker living only at a separate later **live / deploy edge** (compounding is a post-hoc money-management transform of the net-R sequence, not an in-loop axis). The pre-reframe cleave gated a Stage-2 currency / fixed-fractional / realistic-broker layer behind `E[R] > 0`; **"Stage-1"** now survives only as a historical cycle name — the shipped feed-forward R chain's identifier family was renamed to the r-family (`r-sma` / `r-breakout` / `r-meanrev`, #174) — not one half of a two-stage gate. ### strategy **Avoid:** —