diff --git a/crates/ml-backtesting/src/harness.rs b/crates/ml-backtesting/src/harness.rs index ec2e01c30..aafc3021f 100644 --- a/crates/ml-backtesting/src/harness.rs +++ b/crates/ml-backtesting/src/harness.rs @@ -301,9 +301,10 @@ impl BacktestHarness { if self.event_count >= next_progress_event { let elapsed = started.elapsed().as_secs_f32(); let rate = self.event_count as f32 / elapsed.max(1e-3); + let trades = self.sim.read_total_trade_count().unwrap_or(0); eprintln!( - "progress: events={} decisions={} elapsed={:.0}s rate={:.0}ev/s", - self.event_count, total_decisions, elapsed, rate, + "progress: events={} decisions={} trades={} elapsed={:.0}s rate={:.0}ev/s", + self.event_count, total_decisions, trades, elapsed, rate, ); next_progress_event += PROGRESS_EVERY; } diff --git a/crates/ml-backtesting/src/sim/mod.rs b/crates/ml-backtesting/src/sim/mod.rs index 7c46cc506..28db6b867 100644 --- a/crates/ml-backtesting/src/sim/mod.rs +++ b/crates/ml-backtesting/src/sim/mod.rs @@ -390,6 +390,17 @@ impl LobSimCuda { self.n_backtests } + /// Sum of per-backtest trade-log head counters. Cheap DtoH copy of + /// `n_backtests * 4` bytes — used by the harness progress line to + /// surface intra-run trade firing without reading the full ring. + /// Heads count writes (clamped to ring cap inside the kernel), so the + /// sum is a reliable monotone trade counter across the sweep. + pub fn read_total_trade_count(&self) -> Result { + let mut heads = vec![0u32; self.n_backtests]; + self.stream.memcpy_dtoh(&self.trade_log_head_d, heads.as_mut_slice())?; + Ok(heads.iter().map(|&h| h as u64).sum()) + } + /// Apply one MBP-10 snapshot's top-10 levels to every backtest's book. /// v1 inputs are single-snapshot broadcast (same market data to all /// parallel backtests — see spec §7 inference scope, policy sweeps only).