diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index b6c28ce..f62d5e9 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -556,7 +556,7 @@ fn run_sample(trace: Option<&str>) -> RunReport { vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ], window, 0, @@ -593,7 +593,7 @@ fn run_sample_real(symbol: &str, from_ms: Option, to_ms: Option, trace vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ], window, 0, @@ -949,7 +949,7 @@ fn sample_blueprint_with_sinks(pip_size: f64) -> ( let (tx_ex, rx_ex) = mpsc::channel(); let mut g = GraphBuilder::new("sample"); let sig = g.add(signals("signals")); - let exposure = g.add(Bias::builder().named("exposure")); + let exposure = g.add(Bias::builder().named("bias")); let broker = g.add(SimBroker::builder(pip_size)); let eq = g.add(Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq)); let ex = g.add(Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex)); @@ -994,7 +994,7 @@ fn sweep_family(trace: Option<&str>, data: &DataSource) -> SweepFamily { .axis("signals.momentum.signal.length", [msig]) .axis("signals.blend.weights[0]", [1.0]) .axis("signals.blend.weights[1]", [1.0]) - .axis("exposure.scale", [0.5]); + .axis("bias.scale", [0.5]); let varying: HashSet = binder.varying_axes().into_iter().collect(); binder .sweep(|point| { @@ -1023,7 +1023,7 @@ fn sweep_family(trace: Option<&str>, data: &DataSource) -> SweepFamily { /// The signal is how far price sits above/below its own EMA (`price - ema`), bounded /// into a directional (unsized) bias by `Bias`, then passed through the long-only /// `LongOnly` gate. Three swept -/// knobs of three kinds — `ema.length` (i64), `exposure.scale` (f64), +/// knobs of three kinds — `ema.length` (i64), `bias.scale` (f64), /// `longonly.enabled` (bool) — with nothing in common with the SMA-cross demo: the /// generic-sweep proof. The exposure sink taps the FINAL (gated) exposure so the /// bool's effect is visible in the trace. @@ -1037,11 +1037,11 @@ fn momentum_blueprint_with_sinks(pip_size: f64) -> ( let (tx_ex, rx_ex) = mpsc::channel(); let mut g = GraphBuilder::new("momentum"); // Name the param-bearing nodes explicitly so the swept paths are guaranteed - // ema.length / exposure.scale / longonly.enabled regardless of default-name + // ema.length / bias.scale / longonly.enabled regardless of default-name // derivation (the member-key examples + the param-space test depend on these). let ema = g.add(Ema::builder().named("ema")); // ema.length let dist = g.add(Sub::builder()); // momentum = price - ema - let expo = g.add(Bias::builder().named("exposure")); // exposure.scale + let expo = g.add(Bias::builder().named("bias")); // bias.scale let gate = g.add(LongOnly::builder().named("longonly")); // longonly.enabled (the bool param) let broker = g.add(SimBroker::builder(pip_size)); let eq = g.add(Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq)); @@ -1059,7 +1059,7 @@ fn momentum_blueprint_with_sinks(pip_size: f64) -> ( } /// Run the momentum strategy over its built-in grid — `ema.length ∈ {5,10}` × -/// `exposure.scale ∈ {0.5,1.0}` × `longonly.enabled ∈ {true,false}` = 8 points, +/// `bias.scale ∈ {0.5,1.0}` × `longonly.enabled ∈ {true,false}` = 8 points, /// all three axes varying. Mirrors `sweep_family`: capture the binder's varying /// axes, key each member via the generic portable `member_key`. With `--trace`, /// persist each member under `runs/traces///`. @@ -1070,7 +1070,7 @@ fn momentum_sweep_family(trace: Option<&str>, data: &DataSource) -> SweepFamily let space = bp.param_space(); let binder = bp .axis("ema.length", [5, 10]) - .axis("exposure.scale", [0.5, 1.0]) + .axis("bias.scale", [0.5, 1.0]) .axis("longonly.enabled", [true, false]); let varying: HashSet = binder.varying_axes().into_iter().collect(); binder @@ -1315,7 +1315,7 @@ fn sweep_over(from: Timestamp, to: Timestamp, data: &DataSource) -> SweepFamily .axis("signals.momentum.signal.length", [msig]) .axis("signals.blend.weights[0]", [1.0]) .axis("signals.blend.weights[1]", [1.0]) - .axis("exposure.scale", [0.5]) + .axis("bias.scale", [0.5]) .sweep(|point| { let (bp, rx_eq, rx_ex) = sample_blueprint_with_sinks(pip); let mut h = bp @@ -1438,7 +1438,7 @@ fn mc_family(trace: Option<&str>) -> McFamily { vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ], window, seed, @@ -1600,7 +1600,7 @@ fn macd_strategy_blueprint( ) -> Composite { let mut g = GraphBuilder::new("macd_strategy"); let macd_node = g.add(macd("macd")); - let exposure = g.add(Bias::builder().named("exposure")); + let exposure = g.add(Bias::builder().named("bias")); let broker = g.add(SimBroker::builder(SYNTHETIC_PIP_SIZE)); let eq = g.add(Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq)); let ex = g.add(Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex)); @@ -1674,7 +1674,7 @@ fn run_macd(trace: Option<&str>) -> RunReport { ("ema_fast".to_string(), Scalar::i64(2)), ("ema_slow".to_string(), Scalar::i64(4)), ("ema_signal".to_string(), Scalar::i64(3)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ], window, 0, @@ -1737,7 +1737,7 @@ fn stage1_r_blueprint( let fast = g.add(Sma::builder().named("fast").bind("length", Scalar::i64(2))); let slow = g.add(Sma::builder().named("slow").bind("length", Scalar::i64(4))); let spread = g.add(Sub::builder()); - let exposure = g.add(Bias::builder().named("exposure").bind("scale", Scalar::f64(0.5))); + let exposure = g.add(Bias::builder().named("bias").bind("scale", Scalar::f64(0.5))); // pip branch (verbatim from sample_blueprint_with_sinks). let broker = g.add(SimBroker::builder(SYNTHETIC_PIP_SIZE)); let eq = g.add(Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq)); @@ -1824,7 +1824,7 @@ fn run_stage1_r(data: RunData, trace: Option<&str>) -> RunReport { vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ("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·σ) ], @@ -2394,7 +2394,7 @@ mod tests { vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ], window, seed, @@ -2441,7 +2441,7 @@ mod tests { .with("signals.momentum.signal.length", 3) .with("signals.blend.weights[0]", 1.0) .with("signals.blend.weights[1]", 1.0) - .with("exposure.scale", 0.5) + .with("bias.scale", 0.5) .bootstrap() .expect("sample blueprint compiles under a valid point"); h.run(vec![Box::new(VecSource::new(showcase_prices()))]); @@ -2460,10 +2460,10 @@ mod tests { for line in &lines { assert!(line.starts_with(r#"{"manifest":{"commit":""#), "not a RunReport: {line}"); } - assert!(lines[0].contains(r#""params":[["signals.trend.fast.length",{"I64":2}],["signals.trend.slow.length",{"I64":4}],["signals.momentum.fast.length",{"I64":2}],["signals.momentum.slow.length",{"I64":4}],["signals.momentum.signal.length",{"I64":3}],["signals.blend.weights[0]",{"F64":1.0}],["signals.blend.weights[1]",{"F64":1.0}],["exposure.scale",{"F64":0.5}]]"#), "line0: {}", lines[0]); - assert!(lines[1].contains(r#""params":[["signals.trend.fast.length",{"I64":2}],["signals.trend.slow.length",{"I64":5}],["signals.momentum.fast.length",{"I64":2}],["signals.momentum.slow.length",{"I64":4}],["signals.momentum.signal.length",{"I64":3}],["signals.blend.weights[0]",{"F64":1.0}],["signals.blend.weights[1]",{"F64":1.0}],["exposure.scale",{"F64":0.5}]]"#), "line1: {}", lines[1]); - assert!(lines[2].contains(r#""params":[["signals.trend.fast.length",{"I64":3}],["signals.trend.slow.length",{"I64":4}],["signals.momentum.fast.length",{"I64":2}],["signals.momentum.slow.length",{"I64":4}],["signals.momentum.signal.length",{"I64":3}],["signals.blend.weights[0]",{"F64":1.0}],["signals.blend.weights[1]",{"F64":1.0}],["exposure.scale",{"F64":0.5}]]"#), "line2: {}", lines[2]); - assert!(lines[3].contains(r#""params":[["signals.trend.fast.length",{"I64":3}],["signals.trend.slow.length",{"I64":5}],["signals.momentum.fast.length",{"I64":2}],["signals.momentum.slow.length",{"I64":4}],["signals.momentum.signal.length",{"I64":3}],["signals.blend.weights[0]",{"F64":1.0}],["signals.blend.weights[1]",{"F64":1.0}],["exposure.scale",{"F64":0.5}]]"#), "line3: {}", lines[3]); + assert!(lines[0].contains(r#""params":[["signals.trend.fast.length",{"I64":2}],["signals.trend.slow.length",{"I64":4}],["signals.momentum.fast.length",{"I64":2}],["signals.momentum.slow.length",{"I64":4}],["signals.momentum.signal.length",{"I64":3}],["signals.blend.weights[0]",{"F64":1.0}],["signals.blend.weights[1]",{"F64":1.0}],["bias.scale",{"F64":0.5}]]"#), "line0: {}", lines[0]); + assert!(lines[1].contains(r#""params":[["signals.trend.fast.length",{"I64":2}],["signals.trend.slow.length",{"I64":5}],["signals.momentum.fast.length",{"I64":2}],["signals.momentum.slow.length",{"I64":4}],["signals.momentum.signal.length",{"I64":3}],["signals.blend.weights[0]",{"F64":1.0}],["signals.blend.weights[1]",{"F64":1.0}],["bias.scale",{"F64":0.5}]]"#), "line1: {}", lines[1]); + assert!(lines[2].contains(r#""params":[["signals.trend.fast.length",{"I64":3}],["signals.trend.slow.length",{"I64":4}],["signals.momentum.fast.length",{"I64":2}],["signals.momentum.slow.length",{"I64":4}],["signals.momentum.signal.length",{"I64":3}],["signals.blend.weights[0]",{"F64":1.0}],["signals.blend.weights[1]",{"F64":1.0}],["bias.scale",{"F64":0.5}]]"#), "line2: {}", lines[2]); + assert!(lines[3].contains(r#""params":[["signals.trend.fast.length",{"I64":3}],["signals.trend.slow.length",{"I64":5}],["signals.momentum.fast.length",{"I64":2}],["signals.momentum.slow.length",{"I64":4}],["signals.momentum.signal.length",{"I64":3}],["signals.blend.weights[0]",{"F64":1.0}],["signals.blend.weights[1]",{"F64":1.0}],["bias.scale",{"F64":0.5}]]"#), "line3: {}", lines[3]); for line in &lines { assert!(line.contains(r#""total_pips":"#), "missing total_pips: {line}"); assert!(line.contains(r#""max_drawdown":"#), "missing max_drawdown: {line}"); @@ -2565,7 +2565,7 @@ mod tests { /// EMA `length` slots by node name to `macd.fast.length` / `macd.slow.length` / /// `macd.signal.length` — the named composite boundary visible end-to-end through /// `param_space()`, with the slot count and order unchanged (C23 — node names are - /// non-load-bearing: every interior slot stays sweepable, the `exposure.scale` + /// non-load-bearing: every interior slot stays sweepable, the `bias.scale` /// knob is unaffected). #[test] fn macd_param_space_surfaces_the_three_named_legs() { @@ -2579,7 +2579,7 @@ mod tests { "macd.fast.length".to_string(), "macd.slow.length".to_string(), "macd.signal.length".to_string(), - "exposure.scale".to_string(), + "bias.scale".to_string(), ], "MACD param surface must expose the three named EMA lengths + scale", ); @@ -2757,13 +2757,13 @@ mod tests { fn member_key_renders_varying_axes_portably() { let named = vec![ pair("ema.length", Scalar::i64(5)), - pair("exposure.scale", Scalar::f64(0.5)), + pair("bias.scale", Scalar::f64(0.5)), pair("longonly.enabled", Scalar::bool(true)), ]; let varying: std::collections::HashSet = named.iter().map(|(n, _)| n.clone()).collect(); let key = member_key(&named, &varying); - assert_eq!(key, "ema.length-5_exposure.scale-0.5_longonly.enabled-true"); + assert_eq!(key, "ema.length-5_bias.scale-0.5_longonly.enabled-true"); assert!(key.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-'))); } @@ -2771,7 +2771,7 @@ mod tests { fn member_key_omits_pinned_axes() { let named = vec![ pair("ema.length", Scalar::i64(5)), - pair("exposure.scale", Scalar::f64(0.5)), + pair("bias.scale", Scalar::f64(0.5)), pair("longonly.enabled", Scalar::bool(false)), ]; let mut varying = std::collections::HashSet::new(); @@ -2781,10 +2781,10 @@ mod tests { #[test] fn member_key_handles_negative_float_and_sanitises_names() { - let named = vec![pair("exposure.scale", Scalar::f64(-0.5))]; + let named = vec![pair("bias.scale", Scalar::f64(-0.5))]; let varying: std::collections::HashSet = - ["exposure.scale".to_string()].into_iter().collect(); - assert_eq!(member_key(&named, &varying), "exposure.scale--0.5"); + ["bias.scale".to_string()].into_iter().collect(); + assert_eq!(member_key(&named, &varying), "bias.scale--0.5"); let named2 = vec![pair("weird key!", Scalar::f64(1.0))]; let varying2: std::collections::HashSet = @@ -2794,7 +2794,7 @@ mod tests { #[test] fn member_key_is_m_when_no_axis_varies() { - let named = vec![pair("exposure.scale", Scalar::f64(0.5))]; + let named = vec![pair("bias.scale", Scalar::f64(0.5))]; let varying = std::collections::HashSet::new(); assert_eq!(member_key(&named, &varying), "m"); } @@ -2816,15 +2816,15 @@ mod tests { #[test] fn member_key_collision_free_over_a_non_trend_axis_set() { - // #105 regression: vary exposure.scale + a bool (NOT the old hardcoded + // #105 regression: vary bias.scale + a bool (NOT the old hardcoded // trend.fast/slow). Distinct points -> distinct keys; the old f…s… key // would have collapsed them all to one dir. let varying: std::collections::HashSet = - ["exposure.scale".to_string(), "longonly.enabled".to_string()].into_iter().collect(); + ["bias.scale".to_string(), "longonly.enabled".to_string()].into_iter().collect(); let p = |s: f64, b: bool| { vec![ pair("ema.length", Scalar::i64(5)), // pinned -> omitted from key - pair("exposure.scale", Scalar::f64(s)), + pair("bias.scale", Scalar::f64(s)), pair("longonly.enabled", Scalar::bool(b)), ] }; @@ -2846,7 +2846,7 @@ mod tests { names, vec![ "ema.length".to_string(), - "exposure.scale".to_string(), + "bias.scale".to_string(), "longonly.enabled".to_string(), ], ); diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index c155268..4b0fdb7 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -635,7 +635,7 @@ fn sweep_prints_four_family_json_lines_and_exits_zero() { // `broker` (the commit value is the volatile git HEAD, pinned by params below). assert!(first.starts_with("{\"family_id\":\"sweep-0\",\"report\":{\"manifest\":{"), "got: {stdout}"); assert!( - first.contains("\"params\":[[\"signals.trend.fast.length\",{\"I64\":2}],[\"signals.trend.slow.length\",{\"I64\":4}],[\"signals.momentum.fast.length\",{\"I64\":2}],[\"signals.momentum.slow.length\",{\"I64\":4}],[\"signals.momentum.signal.length\",{\"I64\":3}],[\"signals.blend.weights[0]\",{\"F64\":1.0}],[\"signals.blend.weights[1]\",{\"F64\":1.0}],[\"exposure.scale\",{\"F64\":0.5}]]"), + first.contains("\"params\":[[\"signals.trend.fast.length\",{\"I64\":2}],[\"signals.trend.slow.length\",{\"I64\":4}],[\"signals.momentum.fast.length\",{\"I64\":2}],[\"signals.momentum.slow.length\",{\"I64\":4}],[\"signals.momentum.signal.length\",{\"I64\":3}],[\"signals.blend.weights[0]\",{\"F64\":1.0}],[\"signals.blend.weights[1]\",{\"F64\":1.0}],[\"bias.scale\",{\"F64\":0.5}]]"), "got: {stdout}" ); let _ = std::fs::remove_dir_all(&cwd); @@ -1035,7 +1035,7 @@ fn sweep_strategy_momentum_prints_eight_typed_bool_member_lines() { /// Property (#105, the bool actually gates — observable on disk): the /// `longonly.enabled` param is genuinely WIRED into the momentum harness, not a /// no-op knob. For one grid point's two bool members (same `ema.length` + -/// `exposure.scale`, differing only in the bool), the `enabled=true` member +/// `bias.scale`, differing only in the bool), the `enabled=true` member /// records a long-only exposure stream — every recorded value `>= 0` — while the /// otherwise-identical `enabled=false` member records at least one NEGATIVE /// exposure over the same deterministic showcase stream. This pins the gate's @@ -1063,8 +1063,8 @@ fn momentum_longonly_true_clamps_exposure_nonnegative_false_keeps_negatives() { // One grid point, its two bool members (identical but for the bool). let base = cwd.join("runs/traces/gate"); - let enabled = base.join("ema.length-5_exposure.scale-1_longonly.enabled-true"); - let disabled = base.join("ema.length-5_exposure.scale-1_longonly.enabled-false"); + let enabled = base.join("ema.length-5_bias.scale-1_longonly.enabled-true"); + let disabled = base.join("ema.length-5_bias.scale-1_longonly.enabled-false"); let exposures = |dir: &std::path::Path| -> Vec { let body = std::fs::read_to_string(dir.join("exposure.json")).expect("read exposure.json"); diff --git a/crates/aura-engine/src/blueprint.rs b/crates/aura-engine/src/blueprint.rs index 790c028..c9eadcb 100644 --- a/crates/aura-engine/src/blueprint.rs +++ b/crates/aura-engine/src/blueprint.rs @@ -304,7 +304,7 @@ impl Composite { /// fluent alternative to a positional `bootstrap_with_params` vector). The /// bound name is the exact `param_space()` name — `.` at every /// level, e.g. `sma_cross.fast.length` for a composite-interior knob and - /// `exposure.scale` for a root-level knob. + /// `bias.scale` for a root-level knob. pub fn with(self, name: &str, v: impl Into) -> Binder { Binder { bp: self, bound: vec![(name.to_string(), v.into())] } } @@ -962,7 +962,7 @@ mod tests { .0 .range("sma_cross.fast.length", fast) .range("sma_cross.slow.length", slow) - .range("exposure.scale", scale) + .range("bias.scale", scale) .sweep(count, seed, run_point) .expect("named ranges resolve against param_space()"); @@ -972,7 +972,7 @@ mod tests { .0 .range("sma_cross.slow.length", slow) .range("sma_cross.fast.length", fast) - .range("exposure.scale", scale) + .range("bias.scale", scale) .sweep(count, seed, run_point) .expect("named ranges resolve regardless of call order"); @@ -1078,7 +1078,7 @@ mod tests { let result = bp .axis("sma_cross.fast.length", [2.0, 3.0]) // F64 values for the I64 slot .axis("sma_cross.slow.length", [4]) - .axis("exposure.scale", [0.5]) + .axis("bias.scale", [0.5]) .sweep(|_: &[Cell]| -> RunReport { panic!("axis pre-validation must reject before running") }); assert_eq!( result, @@ -1100,7 +1100,7 @@ mod tests { &[ ("sma_cross.fast.length".to_string(), vec![Scalar::i64(2), Scalar::i64(3)]), ("sma_cross.slow.length".to_string(), vec![Scalar::i64(4), Scalar::i64(5)]), - ("exposure.scale".to_string(), vec![Scalar::f64(0.5)]), + ("bias.scale".to_string(), vec![Scalar::f64(0.5)]), ], ) .expect("named axes resolve"); @@ -1209,7 +1209,7 @@ mod tests { let mut named = bp .with("sma_cross.fast.length", 2) .with("sma_cross.slow.length", 4) - .with("exposure.scale", 0.5) + .with("bias.scale", 0.5) .bootstrap() .expect("named binding resolves and bootstraps"); named.run(vec![Box::new(VecSource::new(synthetic_prices()))]); @@ -2104,7 +2104,7 @@ mod tests { // scale (F64); Sub/SimBroker/Recorder declare none assert_eq!( space.iter().map(|p| p.name.as_str()).collect::>(), - ["sma_cross.fast.length", "sma_cross.slow.length", "exposure.scale"], + ["sma_cross.fast.length", "sma_cross.slow.length", "bias.scale"], ); assert_eq!( space.iter().map(|p| p.kind).collect::>(), @@ -2563,7 +2563,7 @@ mod tests { "root", vec![ BlueprintNode::Composite(cross), - Bias::builder().named("exposure").into(), + Bias::builder().named("bias").into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(), ], vec![ @@ -2583,7 +2583,7 @@ mod tests { let mut h = bp .with("sma_cross.fast.length", 2) .with("sma_cross.slow.length", 4) - .with("exposure.scale", 0.5) + .with("bias.scale", 0.5) .bootstrap() .expect("named (injective) cross resolves by name and bootstraps"); h.run(vec![Box::new(VecSource::new(prices))]); diff --git a/crates/aura-engine/src/builder.rs b/crates/aura-engine/src/builder.rs index 2427a1d..0e1ea6d 100644 --- a/crates/aura-engine/src/builder.rs +++ b/crates/aura-engine/src/builder.rs @@ -236,7 +236,7 @@ mod tests { use aura_core::Scalar; // Shared param vector for the harness param-space (sma_cross.fast.length, - // sma_cross.slow.length, exposure.scale) — applied to both sides, so it + // sma_cross.slow.length, bias.scale) — applied to both sides, so it // cancels in the comparison. Params size buffers, not topology (C11), so // edges/sources are param-independent; a fixed vector just satisfies the // arity gate compile() (no-param) trips on this value-empty harness. diff --git a/crates/aura-engine/src/report.rs b/crates/aura-engine/src/report.rs index dc13443..302256e 100644 --- a/crates/aura-engine/src/report.rs +++ b/crates/aura-engine/src/report.rs @@ -637,7 +637,7 @@ mod tests { params: vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ], window: (Timestamp(1), Timestamp(5)), seed: 0, @@ -879,7 +879,7 @@ mod tests { params: vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(1.0)), + ("bias_scale".to_string(), Scalar::f64(1.0)), ], window: (Timestamp(1), Timestamp(6)), seed: 0, @@ -894,7 +894,7 @@ mod tests { }; assert_eq!( report.to_json(), - r#"{"manifest":{"commit":"abc123","params":[["sma_fast",{"I64":2}],["sma_slow",{"I64":4}],["exposure_scale",{"F64":1.0}]],"window":[1,6],"seed":0,"broker":"sim-optimal(pip_size=1.0)"},"metrics":{"total_pips":12.0,"max_drawdown":1.0,"bias_sign_flips":1}}"#, + r#"{"manifest":{"commit":"abc123","params":[["sma_fast",{"I64":2}],["sma_slow",{"I64":4}],["bias_scale",{"F64":1.0}]],"window":[1,6],"seed":0,"broker":"sim-optimal(pip_size=1.0)"},"metrics":{"total_pips":12.0,"max_drawdown":1.0,"bias_sign_flips":1}}"#, ); } @@ -907,7 +907,7 @@ mod tests { params: vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(1.0)), + ("bias_scale".to_string(), Scalar::f64(1.0)), ], window: (Timestamp(1), Timestamp(6)), seed: 0, @@ -927,7 +927,7 @@ mod tests { params: vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(1.0)), + ("bias_scale".to_string(), Scalar::f64(1.0)), ], window: (Timestamp(1), Timestamp(6)), seed: 0, diff --git a/crates/aura-engine/src/test_fixtures.rs b/crates/aura-engine/src/test_fixtures.rs index 8150330..d4929a5 100644 --- a/crates/aura-engine/src/test_fixtures.rs +++ b/crates/aura-engine/src/test_fixtures.rs @@ -62,7 +62,7 @@ pub(crate) fn composite_sma_cross_harness() -> ( "root", vec![ BlueprintNode::Composite(sma_cross()), - Bias::builder().named("exposure").into(), + Bias::builder().named("bias").into(), SimBroker::builder(0.0001).into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq).into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(), diff --git a/crates/aura-engine/src/walkforward.rs b/crates/aura-engine/src/walkforward.rs index 43e4f10..3628dc4 100644 --- a/crates/aura-engine/src/walkforward.rs +++ b/crates/aura-engine/src/walkforward.rs @@ -467,7 +467,7 @@ mod tests { let result = WalkForwardResult { space: vec![ ParamSpec { name: "sma.fast".to_string(), kind: ScalarKind::I64 }, - ParamSpec { name: "exposure.scale".to_string(), kind: ScalarKind::F64 }, + ParamSpec { name: "bias.scale".to_string(), kind: ScalarKind::F64 }, ], windows: vec![mk(8, 0.25), mk(13, 0.75)], stitched_oos_equity: vec![], @@ -476,14 +476,14 @@ mod tests { result.named_params(0), vec![ ("sma.fast".to_string(), Scalar::i64(8)), - ("exposure.scale".to_string(), Scalar::f64(0.25)), + ("bias.scale".to_string(), Scalar::f64(0.25)), ], ); assert_eq!( result.named_params(1), vec![ ("sma.fast".to_string(), Scalar::i64(13)), - ("exposure.scale".to_string(), Scalar::f64(0.75)), + ("bias.scale".to_string(), Scalar::f64(0.75)), ], ); } diff --git a/crates/aura-ingest/tests/real_bars.rs b/crates/aura-ingest/tests/real_bars.rs index 7b88dff..af651e0 100644 --- a/crates/aura-ingest/tests/real_bars.rs +++ b/crates/aura-ingest/tests/real_bars.rs @@ -84,7 +84,7 @@ fn run_sample_over(prices: Vec<(Timestamp, Scalar)>) -> RunReport { params: vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ], window, seed: 0, diff --git a/crates/aura-ingest/tests/streaming_seam.rs b/crates/aura-ingest/tests/streaming_seam.rs index ae8b2fc..f81cc38 100644 --- a/crates/aura-ingest/tests/streaming_seam.rs +++ b/crates/aura-ingest/tests/streaming_seam.rs @@ -84,7 +84,7 @@ fn run_sample(window: (Timestamp, Timestamp), source: Box) -> RunRep params: vec![ ("sma_fast".to_string(), Scalar::i64(2)), ("sma_slow".to_string(), Scalar::i64(4)), - ("exposure_scale".to_string(), Scalar::f64(0.5)), + ("bias_scale".to_string(), Scalar::f64(0.5)), ], window, seed: 0, diff --git a/docs/design/INDEX.md b/docs/design/INDEX.md index 8e9ef6d..3eba19a 100644 --- a/docs/design/INDEX.md +++ b/docs/design/INDEX.md @@ -606,9 +606,12 @@ runnable GER40 examples (already at the correct `1.0`) are routed through the sa lookup separately (#98). **Realization (cycle 0065 — Stage-1 R signal quality, #119/#126/#127/#128/#129).** The Stage-1 chain above is now built. `Exposure → Bias` renames the unsized strategy -output (the node + its output field); the persisted `exposure_sign_flips` metric key, -the `SimBroker` `exposure` input slot, and the `exposure_scale` manifest param keep -the old name as **serde-stable residue** — a deferred rename tail. A **stop-rule** +output (the node + its output field); the persisted `exposure_sign_flips` metric key +(kept as a serde alias) and the `SimBroker` `exposure` input slot + the on-disk +`exposure` **tap** label (a deliberate permanent keep, #117) retain the old name. The +strategy-output **param namespace** — the `Bias` instance, its `bias.scale` knob, and +the `bias_scale` manifest param — was the deferred rename tail, **completed in #134** +(the knob path is a runtime address, so no on-disk back-compat break). A **stop-rule** defines 1R: `FixedStop` (a triggered-constant primitive) and a `vol_stop(length, k)` **composition** `k·√EMA(Δ²)` (the originally-fused node was corrected to a composition of new `Mul`/`Sqrt` primitives — a node is a primitive only if not DAG-expressible