refactor(stage1-r): rename the strategy-output param namespace exposure -> bias
Complete the deferred rename tail from cycle 0065's #126 (which renamed only the typed metric, keeping the param namespace uniformly "exposure" so the single-run rename would not smuggle in a ~30-site sweep-pinned change). Now renamed together as one consistent unit, all driven by the Bias node's instance name: - Bias::builder().named("exposure") -> .named("bias") (the harness builders + the engine fixtures in test_fixtures.rs / blueprint.rs); - the derived knob path exposure.scale -> bias.scale (Composite::param_space derives the knob from the node name) -- the sweep .axis/.range bindings and .with() points (blueprint.rs, main.rs), the walkforward ParamSpecs (walkforward.rs), the member_key dir names, and the JSON byte-pins in cli_run.rs / report.rs / main.rs tests; - the exposure_scale manifest param label -> bias_scale (the single-run manifest builders + the streaming_seam / real_bars fixtures). The rename is surgical: the three targets (named("exposure"), exposure.scale, exposure_scale) are syntactically distinct from the deliberately-kept "exposure" forms, which are untouched -- SimBroker's pre-reframe exposure input slot + prev_exposure + the exposure-integral; the LongOnly / gate / latch ports named "exposure"; and the on-disk "exposure" tap label (the recorded bias series that feeds `chart --tap exposure`, decoupled from the node name via ColumnarTrace::from_rows("exposure", ...)). No on-disk back-compat break: the knob path is a runtime param address, and recorded params in old runs.jsonl / families.jsonl are inert data strings (a pre-existing recorded "exposure.scale" still loads, it is just data). INDEX.md cycle-0065 realization note amended: the param namespace is recorded as the now-completed tail; the SimBroker slot + the on-disk tap label remain the deliberate permanent keeps (#117), and exposure_sign_flips stays a serde alias. Verified: cargo build --workspace --all-targets clean; clippy --all-targets -D warnings clean; full suite 514 passed / 0 failed (source + byte-pins renamed together); live `aura run` emits "bias_scale", `aura sweep` emits "bias.scale". closes #134 refs #126 #117
This commit is contained in:
+34
-34
@@ -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<i64>, to_ms: Option<i64>, 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<String> = 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/<name>/<member_key>/`.
|
||||
@@ -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<String> = 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<String> =
|
||||
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<String> =
|
||||
["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<String> =
|
||||
@@ -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<String> =
|
||||
["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(),
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user