From f94febd95a3eeeb4d9202fd3b859ee5d2ceb12cc Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Fri, 27 Mar 2026 13:01:27 +0100 Subject: [PATCH] fix: MaxDD episode reset after done bar, not before The done bar's return (liquidation loss) must be counted in the current episode's DD before resetting. Previous code reset BEFORE compounding, which applied the liquidation return to fresh initial_capital. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/ml/src/trainers/dqn/financials.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/ml/src/trainers/dqn/financials.rs b/crates/ml/src/trainers/dqn/financials.rs index c203ce3b9..b6149cbec 100644 --- a/crates/ml/src/trainers/dqn/financials.rs +++ b/crates/ml/src/trainers/dqn/financials.rs @@ -125,17 +125,19 @@ pub(crate) fn compute_epoch_financials( let mut max_dd = 0.0_f64; for (idx, &ret) in returns[dd_start..].iter().enumerate() { let global_idx = dd_start + idx; - // Reset equity at episode boundaries (capital floor / episode end) + equity *= 1.0 + ret; + if equity > peak { + peak = equity; + } + // Reset equity AFTER processing the done bar's return. + // The done bar's loss is part of this episode's DD — the reset + // applies to the NEXT bar (start of new episode with fresh capital). if global_idx < trade_stats.done_flags.len() && trade_stats.done_flags[global_idx] > 0.5 { equity = initial_capital; peak = initial_capital; } - equity *= 1.0 + ret; - if equity > peak { - peak = equity; - } let dd = if peak > 1e-10 { (peak - equity) / peak } else {