diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index 54d1bbf..6879ea4 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -3637,6 +3637,64 @@ fn generalize_real_e2e_pins_the_exact_current_grade() { assert_eq!(per[1][1].as_f64(), Some(0.005795903617609842), "USDJPY expectancy R: {grade_line}"); } +/// Characterization pin (byte-identity anchor for the walkforward dissolution, +/// #210). The current `aura walkforward` runs its per-window IS-refit inline +/// (`walkforward_family` -> `select_winner` -> OOS run -> stitch/pool); the +/// dissolution reroutes the same rolling IS-sweep -> OOS-run -> deflation through +/// the campaign `std::walk_forward` stage, and must retain the OOS pip curve the +/// campaign runner currently discards (`oos_equity: vec![]`) to reproduce +/// `stitched_total_pips`, plus reconstruct pooled `oos_r`. The multi-point grid +/// (fast 3,5 x slow 12,20) makes the per-window winner selection non-degenerate -- +/// `param_stability` varies across windows -- so a winner-pick divergence between +/// the inline and campaign refit cannot ship green. This pins the EXACT current +/// grade of the identical invocation over a fixed 2025 GER40 window; after the +/// dissolution the same command must reproduce these bytes (the acceptance gate). +/// Gated on the shared GER40 archive; skips cleanly on a data refusal. +#[test] +fn walkforward_real_e2e_pins_the_exact_current_grade() { + const FROM_MS: &str = "1735689600000"; + const TO_MS: &str = "1767225599000"; + let cwd = temp_cwd("walkforward-exact-grade"); + let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) + .args([ + "walkforward", "--strategy", "r-sma", "--real", "GER40", + "--fast", "3,5", "--slow", "12,20", "--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 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("{\"walkforward\":")) + .unwrap_or_else(|| panic!("the walkforward summary line: {stdout}")); + let v: serde_json::Value = serde_json::from_str(grade_line).expect("summary line parses as JSON"); + let wf = &v["walkforward"]; + assert_eq!(wf["windows"].as_u64(), Some(9), "window count: {grade_line}"); + // EXACT floats -- the byte-identity anchor the dissolution must preserve. + // stitched_total_pips is retained only if the campaign runner keeps the OOS curve. + assert_eq!(wf["stitched_total_pips"].as_f64(), Some(-10398606.666650848), "stitched OOS pips: {grade_line}"); + let r = &wf["oos_r"]; + assert_eq!(r["n_trades"].as_u64(), Some(20681), "pooled OOS trade count: {grade_line}"); + assert_eq!(r["expectancy_r"].as_f64(), Some(-0.002397100685333715), "pooled OOS expectancy R: {grade_line}"); + // param_stability varies across windows -> the per-window IS-refit picks + // different winners; pinning the means locks the winner-selection equivalence. + let ps = wf["param_stability"].as_array().expect("param_stability is an array"); + assert_eq!(ps[0]["mean"].as_f64(), Some(3.888888888888889), "fast-MA refit mean: {grade_line}"); + assert_eq!(ps[1]["mean"].as_f64(), Some(17.333333333333332), "slow-MA refit mean: {grade_line}"); +} + /// Property: `aura generalize` is now thin sugar over the one campaign path — a /// successful run durably auto-registers exactly one generated process document, /// one generated campaign document (carrying the `--name` handle and the stop as