test(cli): pin the exact generalize grade — byte-identity anchor (#210)
The current `aura generalize` binds its protective stop as a grid axis (`r_sma_sweep_family` with `stop_open=true`); the pending generalize dissolution rebinds the same stop through the risk-regime seam (`RiskRegime::Vol -> StopRule::Vol`). The existing generalize e2e (`generalize_grades_a_candidate_across_two_instruments`) asserts shape only — `worst_case` and the per-instrument R floats are never checked — so a divergence between the two stop bindings would ship green. This adds `generalize_real_e2e_pins_the_exact_current_grade`, capturing the exact current R grade of the identical invocation (GER40 0.0105637…, USDJPY 0.0057959…, worst_case 0.0057959…, sign_agreement 2). Green today against the inline path; after the dissolution the same command must reproduce these bytes — the acceptance gate that catches a stop-mechanism divergence loudly instead of silently. Resolves the generalize-dissolution grounding block (the unpinned stop-mechanism R equivalence) by the risk-regime characterization-pin pattern. refs #210
This commit is contained in:
@@ -3582,6 +3582,61 @@ fn generalize_grades_a_candidate_across_two_instruments() {
|
||||
assert!(stdout.contains("\"family_id\":\"generalize-0\""), "family handle: {stdout}");
|
||||
}
|
||||
|
||||
/// Characterization pin (byte-identity anchor for the generalize dissolution,
|
||||
/// #210). The current `aura generalize` binds its protective stop as a grid axis
|
||||
/// (`r_sma_sweep_family` with `stop_open=true`); the dissolution rebinds the same
|
||||
/// stop through the risk-regime seam (`RiskRegime::Vol -> StopRule::Vol`). The
|
||||
/// sibling `generalize_grades_a_candidate_across_two_instruments` asserts shape
|
||||
/// only — `worst_case` and the per-instrument R floats are never checked — so a
|
||||
/// stop-mechanism divergence between the two bindings would ship green. This pins
|
||||
/// the EXACT current R grade of the identical invocation; after the dissolution
|
||||
/// the same command must reproduce these bytes (the acceptance gate), and any
|
||||
/// drift fails here loudly. Gated on the shared GER40/USDJPY Sept-2024 archive;
|
||||
/// skips cleanly on a data refusal.
|
||||
#[test]
|
||||
fn generalize_real_e2e_pins_the_exact_current_grade() {
|
||||
const FROM_MS: &str = "1725148800000";
|
||||
const TO_MS: &str = "1727740799999";
|
||||
let cwd = temp_cwd("generalize-exact-grade");
|
||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args([
|
||||
"generalize", "--strategy", "r-sma", "--real", "GER40,USDJPY",
|
||||
"--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0",
|
||||
"--from", FROM_MS, "--to", TO_MS,
|
||||
])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.expect("spawn aura");
|
||||
if out.status.code() == Some(1) {
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(
|
||||
stderr.contains("no local data") || stderr.contains("no recorded geometry"),
|
||||
"exit 1 must be a data refusal, got: {stderr}"
|
||||
);
|
||||
eprintln!("skip: no local GER40/USDJPY data");
|
||||
return;
|
||||
}
|
||||
assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status);
|
||||
let stdout = String::from_utf8(out.stdout).expect("utf-8 stdout");
|
||||
let grade_line = stdout
|
||||
.lines()
|
||||
.find(|l| l.starts_with("{\"generalize\":"))
|
||||
.unwrap_or_else(|| panic!("the generalize grade line: {stdout}"));
|
||||
let v: serde_json::Value = serde_json::from_str(grade_line).expect("grade line parses as JSON");
|
||||
let g = &v["generalize"];
|
||||
assert_eq!(g["metric"].as_str(), Some("expectancy_r"), "metric: {grade_line}");
|
||||
assert_eq!(g["n_instruments"].as_u64(), Some(2), "two instruments: {grade_line}");
|
||||
assert_eq!(g["sign_agreement"].as_u64(), Some(2), "both agree in sign: {grade_line}");
|
||||
// EXACT R floats — the byte-identity anchor the dissolution must preserve.
|
||||
assert_eq!(g["worst_case"].as_f64(), Some(0.005795903617609842), "worst-case R floor: {grade_line}");
|
||||
let per = g["per_instrument"].as_array().expect("per_instrument is an array");
|
||||
assert_eq!(per.len(), 2, "two per-instrument grades: {grade_line}");
|
||||
assert_eq!(per[0][0].as_str(), Some("GER40"), "first instrument: {grade_line}");
|
||||
assert_eq!(per[0][1].as_f64(), Some(0.01056371324510624), "GER40 expectancy R: {grade_line}");
|
||||
assert_eq!(per[1][0].as_str(), Some("USDJPY"), "second instrument: {grade_line}");
|
||||
assert_eq!(per[1][1].as_f64(), Some(0.005795903617609842), "USDJPY expectancy R: {grade_line}");
|
||||
}
|
||||
|
||||
/// Property: a single-instrument generalize is refused at parse time (exit 2),
|
||||
/// before any data access — so it asserts the arity refusal on any machine.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user