The threshold-tuning smoke at 81decf40f produced n_trades=0 despite
74.6% of decisions having max_conv ≥ 0.30 — the linear-weighted-mean
aggregator in decision_policy_default is structurally dilution-bound
at cold-start (per spec §1).
Q1 stopgap: when sim_variants[i].use_cold_start_stopgap = true, the
harness uploads a max-confidence Strategy bytecode program for that
backtest, routing decisions through decision_policy_program with
OP_AGG_MAX_CONFIDENCE. Existing kernel; zero CUDA changes.
Field additions (atomically across BatchedSimConfig + UniformSimParams
+ ResolvedSimVariant + SweepBase.SimVariant) — every UniformSimParams
literal migrated to include use_cold_start_stopgap: false (default).
The sweep YAML's sim_variants entry sets it to true only for the
validation run; production deployability uses Q2's kernel fix instead.
Sweep YAML (config/ml/sweep_smoke.yaml) flipped to use_cold_start_stopgap=true
at threshold=0.0, cost=0.125 — same anchor as the threshold-tuning
smoke that produced n_trades=0, for direct comparison.
This is a VALIDATION step. Cluster smoke at this commit MUST produce
n_trades > 100 + finite metrics. Q2's kernel CBSW immediately follows
and deletes this entire stopgap atomically (field, harness branch,
YAML setting, every literal).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55 lines
2.1 KiB
Rust
55 lines
2.1 KiB
Rust
//! P1 regression test: per-backtest sim parameter arrays. With uniform
|
|
//! config across N backtests, all N must produce the same market_target
|
|
//! decision (proves the per-backtest indexing reduces correctly to the
|
|
//! pre-refactor scalar-broadcast semantics).
|
|
//!
|
|
//! Pair: the independence test (proving DIFFERENT per-backtest configs
|
|
//! produce different outputs) lands alongside the P2 inflight-limits
|
|
//! kernel where the read_first_inflight_arrival_ts helper exists.
|
|
|
|
use anyhow::Result;
|
|
use ml_backtesting::sim::{BatchedSimConfig, LobSimCuda, UniformSimParams};
|
|
use ml_core::device::MlDevice;
|
|
|
|
#[test]
|
|
#[ignore = "requires CUDA"]
|
|
fn parallel_sim_equivalence_with_uniform_config() -> Result<()> {
|
|
let dev = match MlDevice::cuda(0) {
|
|
Ok(d) => d,
|
|
Err(e) => {
|
|
eprintln!("skipping: cuda device unavailable ({e})");
|
|
return Ok(());
|
|
}
|
|
};
|
|
let mut sim = LobSimCuda::new(8, &dev)?;
|
|
sim.broadcast_alpha(&[0.8, 0.8, 0.8, 0.8, 0.8])?;
|
|
let cfg = BatchedSimConfig::from_uniform(
|
|
8,
|
|
&UniformSimParams {
|
|
target_annual_vol_units: 50.0,
|
|
annualisation_factor: 825.0,
|
|
max_lots: 5,
|
|
latency_ns: 0,
|
|
kelly_frac_floor: 0.20,
|
|
sharpe_weight_floor: 0.10,
|
|
threshold: 0.0,
|
|
cost_per_lot_per_side: 0.0,
|
|
use_cold_start_stopgap: false,
|
|
},
|
|
);
|
|
sim.step_decision_with_latency(0, &cfg)?;
|
|
let first = sim.read_market_target(0)?;
|
|
for b in 1..8 {
|
|
let got = sim.read_market_target(b)?;
|
|
assert_eq!(
|
|
got, first,
|
|
"backtest {b} differs from backtest 0 under uniform config: {got:?} vs {first:?}"
|
|
);
|
|
}
|
|
// And the uniform-config result must equal the result we'd get from
|
|
// a single-backtest sim (preserves pre-refactor behaviour).
|
|
assert_eq!(first.0, 0, "uniform config with p_h=0.8 should produce a buy; got side={}", first.0);
|
|
assert!(first.1 >= 1, "uniform-config size {} < 1", first.1);
|
|
Ok(())
|
|
}
|