From b59e2f7847bc1bb56a81dc501e4e9aad6e4e28a6 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Sun, 29 Mar 2026 19:29:42 +0200 Subject: [PATCH] fix(hyperopt): TRIAL_SUMMARY best_epoch was epochs_completed, not actual best MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit best_epoch always showed 20 (total epochs) instead of the epoch where the best val_loss checkpoint was saved. Misleading — looked like the model never peaked. Now correctly reports actual_best_epoch from the trainer's checkpoint tracker. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/ml/src/hyperopt/adapters/dqn.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ml/src/hyperopt/adapters/dqn.rs b/crates/ml/src/hyperopt/adapters/dqn.rs index c1a2496aa..27f6cf628 100644 --- a/crates/ml/src/hyperopt/adapters/dqn.rs +++ b/crates/ml/src/hyperopt/adapters/dqn.rs @@ -3149,6 +3149,7 @@ impl HyperparameterOptimizable for DQNTrainer { None }; + let actual_best_epoch = internal_trainer.get_best_epoch(); let metrics = DQNMetrics { train_loss: training_metrics.loss, val_loss: internal_trainer.get_best_val_loss(), // Use best validation loss for hyperopt @@ -3302,7 +3303,7 @@ impl HyperparameterOptimizable for DQNTrainer { let best_sharpe = metrics.backtest_metrics.as_ref().map(|b| b.sharpe_ratio).unwrap_or(0.0); let best_win_rate = metrics.backtest_metrics.as_ref().map(|b| b.win_rate * 100.0).unwrap_or(0.0); let best_max_dd = metrics.backtest_metrics.as_ref().map(|b| b.max_drawdown_pct).unwrap_or(0.0); - let best_epoch = metrics.epochs_completed; + let best_epoch = actual_best_epoch; info!( "TRIAL_SUMMARY: trial={}, objective={:.4}, sharpe={:.4}, win_rate={:.2}%, \