diff --git a/bin/fxt/tests/cli_integration_test.rs b/bin/fxt/tests/cli_integration_test.rs index c77043731..3269e80b0 100644 --- a/bin/fxt/tests/cli_integration_test.rs +++ b/bin/fxt/tests/cli_integration_test.rs @@ -215,10 +215,11 @@ fn test_tune_status_invalid_uuid() { #[ignore = "Ignored because it tries to launch terminal UI"] fn test_dashboard_command() { let mut cmd = Command::cargo_bin("fxt").unwrap(); - cmd.arg("dashboard") + let _ = cmd.arg("dashboard") .timeout(std::time::Duration::from_secs(2)) .assert(); - // Will timeout or fail trying to connect, but that's expected + // Will timeout or fail trying to connect, but that's expected — assert result + // intentionally discarded; this test verifies arg parsing reaches launch. } /// Test version flag diff --git a/crates/ml-alpha/examples/alpha_rl_train.rs b/crates/ml-alpha/examples/alpha_rl_train.rs index f820f24cf..3a373eab8 100644 --- a/crates/ml-alpha/examples/alpha_rl_train.rs +++ b/crates/ml-alpha/examples/alpha_rl_train.rs @@ -110,6 +110,15 @@ use ml_alpha::rl::isv_slots::{ RL_KELLY_SAFETY_FRAC_INDEX, RL_KELLY_MIN_TRADES_FOR_RELEASE_INDEX, RL_TRAIL_TIGHTEN_FACTOR_INDEX, RL_TRAIL_LOOSEN_FACTOR_INDEX, RL_CUMULATIVE_DONES_INDEX, + // v9 defensive eval-boundary calibration (spec + // 2026-05-31-v9-defensive-eval-boundary-calibration.md). + RL_EVAL_WARMUP_REMAINING_INDEX, RL_EVAL_WARMUP_STEPS_CONFIG_INDEX, + RL_EVAL_WARMUP_DECAY_STEPS_CONFIG_INDEX, + RL_EVAL_KELLY_SAFETY_DEFENSIVE_INDEX, RL_EVAL_IQN_TAU_MIN_DEFENSIVE_INDEX, + RL_EVAL_ENTROPY_COEF_MIN_DEFENSIVE_INDEX, RL_EVAL_PPO_CLIP_EPS_MIN_DEFENSIVE_INDEX, + RL_EVAL_KELLY_SAFETY_NORMAL_INDEX, RL_EVAL_IQN_TAU_MIN_NORMAL_INDEX, + RL_EVAL_ENTROPY_COEF_MIN_NORMAL_INDEX, RL_EVAL_PPO_CLIP_EPS_MIN_NORMAL_INDEX, + RL_ENTROPY_COEF_MIN_INDEX, RL_PPO_CLIP_EPS_MIN_INDEX, }; use ml_alpha::trainer::diag_staging::DiagStaging; use ml_alpha::trainer::integrated::{ @@ -760,6 +769,22 @@ fn main() -> Result<()> { }) }; + // v9 eval_warmup pre-compute (json! macro can't take Rust blocks/if-else). + let eval_warmup_remaining = isv[RL_EVAL_WARMUP_REMAINING_INDEX]; + let eval_warmup_decay_steps = isv[RL_EVAL_WARMUP_DECAY_STEPS_CONFIG_INDEX]; + let eval_warmup_active = if eval_warmup_remaining > 0.0 { 1.0_f32 } else { 0.0_f32 }; + let eval_warmup_blend = if eval_warmup_remaining < 0.0 { + 0.0_f32 + } else if eval_warmup_remaining > eval_warmup_decay_steps { + 1.0_f32 + } else if eval_warmup_decay_steps > 0.0 { + eval_warmup_remaining / eval_warmup_decay_steps + } else if eval_warmup_remaining > 0.0 { + 1.0_f32 + } else { + 0.0_f32 + }; + let record = json!({ "step": step, "elapsed_s": t_start.elapsed().as_secs_f32(), @@ -886,6 +911,36 @@ fn main() -> Result<()> { "tighten": isv[RL_TRAIL_TIGHTEN_FACTOR_INDEX], "loosen": isv[RL_TRAIL_LOOSEN_FACTOR_INDEX], }, + // v9 — defensive eval-boundary calibration ([[pearl_adaptive_carryover_discipline]]). + // `remaining` is the per-step counter: ≥ decay_steps → full + // defensive blend=1.0; (0, decay_steps] → linear decay to 0; + // 0 → final boundary write of normal values; -1 → kernel + // no-op (post-warmup steady state). The four `floor_*` + // entries are the LIVE values (read AFTER the warmup + // kernel ran this step, so they reflect any override). + "eval_warmup": { + "remaining": eval_warmup_remaining, + "active": eval_warmup_active, + "blend": eval_warmup_blend, + "warmup_steps_config": isv[RL_EVAL_WARMUP_STEPS_CONFIG_INDEX], + "decay_steps_config": eval_warmup_decay_steps, + "floor_kelly_safety": isv[RL_KELLY_SAFETY_FRAC_INDEX], + "floor_iqn_tau_min": isv[RL_IQN_ACTION_TAU_MIN_INDEX], + "floor_entropy_min": isv[RL_ENTROPY_COEF_MIN_INDEX], + "floor_ppo_eps_min": isv[RL_PPO_CLIP_EPS_MIN_INDEX], + "target_defensive": { + "kelly_safety": isv[RL_EVAL_KELLY_SAFETY_DEFENSIVE_INDEX], + "iqn_tau_min": isv[RL_EVAL_IQN_TAU_MIN_DEFENSIVE_INDEX], + "entropy_min": isv[RL_EVAL_ENTROPY_COEF_MIN_DEFENSIVE_INDEX], + "ppo_eps_min": isv[RL_EVAL_PPO_CLIP_EPS_MIN_DEFENSIVE_INDEX], + }, + "target_normal": { + "kelly_safety": isv[RL_EVAL_KELLY_SAFETY_NORMAL_INDEX], + "iqn_tau_min": isv[RL_EVAL_IQN_TAU_MIN_NORMAL_INDEX], + "entropy_min": isv[RL_EVAL_ENTROPY_COEF_MIN_NORMAL_INDEX], + "ppo_eps_min": isv[RL_EVAL_PPO_CLIP_EPS_MIN_NORMAL_INDEX], + }, + }, // Calibration diagnostics — atom-span vs. Bellman dynamic // bound. Per docs/superpowers/specs/2026-05-31-c51-atom- // span-math-validation.md: the structural constraint is diff --git a/crates/ml/examples/alpha_baseline.rs b/crates/ml/examples/alpha_baseline.rs index a6006842c..cbcd3b587 100644 --- a/crates/ml/examples/alpha_baseline.rs +++ b/crates/ml/examples/alpha_baseline.rs @@ -907,7 +907,7 @@ fn main() -> Result<()> { .context("reset vol_obs min slot")?; } // Lockstep step loop. - for step in 0..cli.horizon { + for _ in 0..cli.horizon { // Gather current states (CPU; <100μs for N=500). let mut batched_states_host = vec![0.0_f32; n_par * STATE_DIM]; for i in 0..n_par { diff --git a/crates/ml/tests/behavioral/harness.rs b/crates/ml/tests/behavioral/harness.rs index 84c4cda7d..8a703d316 100644 --- a/crates/ml/tests/behavioral/harness.rs +++ b/crates/ml/tests/behavioral/harness.rs @@ -9,7 +9,7 @@ use ml::cuda_pipeline::lob_bar::LobBar; use std::collections::HashMap; #[derive(Debug, Default)] -pub struct BehavioralResult { +pub(crate) struct BehavioralResult { /// Action distribution keys: "hold", "flat", "long_quarter", /// "long_half", "long_full", "short_quarter", "short_half", "short_full". pub action_dist: HashMap<&'static str, f32>, @@ -27,7 +27,7 @@ pub struct BehavioralResult { /// each Phase 2B test rewrites the body to call the trainer methods it /// requires. The signature here is the load-bearing piece — keep it stable. #[allow(dead_code, unused_variables)] -pub fn evaluate_policy_on_market( +pub(crate) fn evaluate_policy_on_market( // Phase 2B swaps `&mut ()` for `&mut GpuDqnTrainer` once the per-test // trainer methods land. Until then the unit param keeps the signature // callable in tests that don't yet exercise the trainer path. diff --git a/crates/ml/tests/behavioral/oracle.rs b/crates/ml/tests/behavioral/oracle.rs index 9a61f777b..076d34292 100644 --- a/crates/ml/tests/behavioral/oracle.rs +++ b/crates/ml/tests/behavioral/oracle.rs @@ -5,10 +5,10 @@ use ml::cuda_pipeline::lob_bar::LobBar; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum MagBucket { Quarter, Half, Full } +pub(crate) enum MagBucket { Quarter, Half, Full } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum OracleAction { +pub(crate) enum OracleAction { Hold, Long(MagBucket), Short(MagBucket), @@ -16,12 +16,12 @@ pub enum OracleAction { } /// Oracle for flat markets: always Hold. -pub fn flat_market_oracle(bars: &[LobBar]) -> Vec { +pub(crate) fn flat_market_oracle(bars: &[LobBar]) -> Vec { vec![OracleAction::Hold; bars.len()] } /// Oracle for drift markets: take direction-of-trend, mag = Half. -pub fn drift_market_oracle(bars: &[LobBar]) -> Vec { +pub(crate) fn drift_market_oracle(bars: &[LobBar]) -> Vec { let mut actions = Vec::with_capacity(bars.len()); actions.push(OracleAction::Hold); // first bar: no prior for w in bars.windows(2) { @@ -38,7 +38,7 @@ pub fn drift_market_oracle(bars: &[LobBar]) -> Vec { } /// Oracle for OU markets: reverse at ±2σ extremes. -pub fn ou_market_oracle(bars: &[LobBar], mu: f32, sigma: f32) -> Vec { +pub(crate) fn ou_market_oracle(bars: &[LobBar], mu: f32, sigma: f32) -> Vec { bars.iter().map(|b| { let z = (b.price - mu) / sigma.max(1e-6); if z > 2.0 { OracleAction::Short(MagBucket::Half) } diff --git a/crates/ml/tests/behavioral/synthetic_markets.rs b/crates/ml/tests/behavioral/synthetic_markets.rs index 37d1ae85a..ac82f94fb 100644 --- a/crates/ml/tests/behavioral/synthetic_markets.rs +++ b/crates/ml/tests/behavioral/synthetic_markets.rs @@ -6,7 +6,7 @@ use rand::{Rng, SeedableRng}; use rand::rngs::StdRng; /// Flat market: constant base price + Gaussian noise. -pub fn flat_market(n: usize, noise_sigma: f32, mean_spread: f32, seed: u64) -> Vec { +pub(crate) fn flat_market(n: usize, noise_sigma: f32, mean_spread: f32, seed: u64) -> Vec { let mut rng = StdRng::seed_from_u64(seed); (0..n).map(|_| { let noise: f32 = rng.gen_range(-1.0..1.0) * noise_sigma; @@ -18,7 +18,7 @@ pub fn flat_market(n: usize, noise_sigma: f32, mean_spread: f32, seed: u64) -> V } /// Drift market: drift μ + noise σ. -pub fn drift_market(n: usize, mu: f32, sigma: f32, mean_spread: f32, seed: u64) -> Vec { +pub(crate) fn drift_market(n: usize, mu: f32, sigma: f32, mean_spread: f32, seed: u64) -> Vec { let mut rng = StdRng::seed_from_u64(seed); let mut price: f32 = 4500.0; (0..n).map(|_| { @@ -31,7 +31,7 @@ pub fn drift_market(n: usize, mu: f32, sigma: f32, mean_spread: f32, seed: u64) } /// OU process: mean-reverting around equilibrium. -pub fn ou_market(n: usize, theta: f32, sigma_eq: f32, mu: f32, +pub(crate) fn ou_market(n: usize, theta: f32, sigma_eq: f32, mu: f32, mean_spread: f32, seed: u64) -> Vec { let mut rng = StdRng::seed_from_u64(seed); let mut price: f32 = mu; @@ -45,7 +45,7 @@ pub fn ou_market(n: usize, theta: f32, sigma_eq: f32, mu: f32, } /// Regime-switch: Markov chain over `n_regimes`, each with (mu, sigma). -pub fn regime_switch_market( +pub(crate) fn regime_switch_market( n: usize, regime_params: &[(f32, f32)], transition_matrix: &[Vec], diff --git a/crates/ml/tests/sp5_producer_unit_tests.rs b/crates/ml/tests/sp5_producer_unit_tests.rs index b6c85e8a3..d39b1a2e1 100644 --- a/crates/ml/tests/sp5_producer_unit_tests.rs +++ b/crates/ml/tests/sp5_producer_unit_tests.rs @@ -1980,7 +1980,7 @@ fn pearl_1_ext_num_atoms_threshold_cascade() { let scratch_size: usize = 4; let base: i32 = 0; - let mut scratch = unsafe { MappedF32Buffer::new(scratch_size) }.expect("scratch alloc"); + let scratch = unsafe { MappedF32Buffer::new(scratch_size) }.expect("scratch alloc"); let isv_dev = isv_buf.dev_ptr; let scr_dev = scratch.dev_ptr; @@ -2048,7 +2048,7 @@ fn pearl_1_ext_num_atoms_exact_threshold_falls_to_next_branch() { let scratch_size: usize = 4; let base: i32 = 0; - let mut scratch = unsafe { MappedF32Buffer::new(scratch_size) }.expect("scratch alloc"); + let scratch = unsafe { MappedF32Buffer::new(scratch_size) }.expect("scratch alloc"); let isv_dev = isv_buf.dev_ptr; let scr_dev = scratch.dev_ptr;