refactor(stage1-r): dedicated broker label + intern col-port names (no per-build leak)
Two cosmetic smells the cycle-0065 close audit flagged (#132), neither a correctness issue: 1. Broker label. run_stage1_r reused sim_optimal_manifest, recording broker: "sim-optimal(pip_size=...)" for a harness that also runs a RiskExecutor branch. It now overrides the label to "sim-optimal+risk-executor(pip_size=...)" so the dual-tap harness reads honestly. A new assertion in run_stage1_r_synthetic_folds_an_r_block pins it. 2. col[i] leak. stage1_r_blueprint did format!("col[{i}]").leak() per PositionManagement field (14 strings) to satisfy the &'static str port-name API -- harmless for a CLI one-shot, but a leak-per-build smell once the harness is reused in a sweep / Monte-Carlo loop (the #133 direction). Replaced with a process-wide LazyLock<Vec<String>> (COL_PORTS), interned once: any number of builds now reuse the same 14 strings. (#132 deferred this to #133, but the intern is robust to any reuse pattern, so it lands here cleanly.) Behaviour-preserving (the port names and the recorded R-record are unchanged); the broker label is the only observable change, and it is pinned. Verified: clippy --all-targets -D warnings clean; full suite 514 passed / 0 failed; live `aura run --harness stage1-r` reports "broker":"sim-optimal+risk-executor(pip_size=0.0001)". closes #132
This commit is contained in:
@@ -31,6 +31,7 @@ use aura_std::{
|
||||
Bias, Ema, LinComb, LongOnly, Recorder, SimBroker, Sma, Sub, PM_FIELD_NAMES, PM_RECORD_KINDS,
|
||||
};
|
||||
use std::sync::mpsc::{self, Receiver};
|
||||
use std::sync::LazyLock;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// The pip size the built-in *synthetic* harnesses run at: a 5-decimal FX major
|
||||
@@ -1719,6 +1720,13 @@ fn stage1_r_prices() -> Vec<(Timestamp, Scalar)> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// strings rather than leaking 14 fresh ones per build (#132).
|
||||
static COL_PORTS: LazyLock<Vec<String>> =
|
||||
LazyLock::new(|| (0..PM_FIELD_NAMES.len()).map(|i| format!("col[{i}]")).collect());
|
||||
|
||||
/// The Stage-1 R harness: the SMA-cross → `Bias` signal fanned into BOTH a `SimBroker`
|
||||
/// (pip equity, the existing pip yardstick) AND the `risk_executor` (the dense R-record,
|
||||
/// the new R yardstick), so one run scores the same signal in pips and in R. Four taps:
|
||||
@@ -1770,8 +1778,7 @@ fn stage1_r_blueprint(
|
||||
g.connect(broker.output("equity"), eq.input("col[0]"));
|
||||
// tap the dense R-record (all PM fields) for summarize_r.
|
||||
for (i, field) in PM_FIELD_NAMES.iter().enumerate() {
|
||||
let col: &'static str = format!("col[{i}]").leak();
|
||||
g.connect(exec.output(field), rrec.input(col));
|
||||
g.connect(exec.output(field), rrec.input(COL_PORTS[i].as_str()));
|
||||
}
|
||||
// r_equity sum + tap.
|
||||
g.connect(exec.output("cum_realized_r"), r_equity.input("term[0]"));
|
||||
@@ -1820,7 +1827,7 @@ fn run_stage1_r(data: RunData, trace: Option<&str>) -> RunReport {
|
||||
let r_rows: Vec<(Timestamp, Vec<Scalar>)> = rx_r.try_iter().collect();
|
||||
let req_rows: Vec<(Timestamp, Vec<Scalar>)> = rx_req.try_iter().collect();
|
||||
|
||||
let manifest = sim_optimal_manifest(
|
||||
let mut manifest = sim_optimal_manifest(
|
||||
vec![
|
||||
("sma_fast".to_string(), Scalar::i64(2)),
|
||||
("sma_slow".to_string(), Scalar::i64(4)),
|
||||
@@ -1832,6 +1839,9 @@ fn run_stage1_r(data: RunData, trace: Option<&str>) -> RunReport {
|
||||
0,
|
||||
pip_size,
|
||||
);
|
||||
// #132: the dual-tap harness runs a RiskExecutor branch alongside the SimBroker, so
|
||||
// the plain "sim-optimal" label would under-report it — record both honestly.
|
||||
manifest.broker = format!("sim-optimal+risk-executor(pip_size={pip_size})");
|
||||
if let Some(name) = trace {
|
||||
persist_traces_r(name, &manifest, &eq_rows, &ex_rows, &req_rows);
|
||||
}
|
||||
@@ -2943,6 +2953,13 @@ mod tests {
|
||||
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);
|
||||
// #132: the dual-tap harness runs a RiskExecutor branch alongside the
|
||||
// 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: {}",
|
||||
report.manifest.broker
|
||||
);
|
||||
}
|
||||
|
||||
/// Property: a bare `aura run` parses to the default harness/data — `--harness sma`
|
||||
|
||||
Reference in New Issue
Block a user