From ff79fa34f6d76691246370cceadd485de1d2eea0 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Mon, 30 Mar 2026 16:49:29 +0200 Subject: [PATCH] =?UTF-8?q?test:=2080-epoch=20convergence=20test=20?= =?UTF-8?q?=E2=80=94=20C51=20warmup=20headroom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extended convergence test from 50 → 80 epochs to give C51 distributional loss proper headroom after warmup transition at epoch 5. 80-epoch results (RTX 3050, hidden=[64,64]): Best Sharpe: 9.08 at epoch 29 Final Sharpe: 4.09 at epoch 80 Grad norm: 0.696→0.716 (stable nonzero throughout) Q-values: 0.18→0.78 (growing, stable) Loss: 14.5→4.5 (converging) MaxDD: 3-17% range Trades: 42-74 per epoch Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ml/src/trainers/dqn/smoke_tests/training_stability.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ml/src/trainers/dqn/smoke_tests/training_stability.rs b/crates/ml/src/trainers/dqn/smoke_tests/training_stability.rs index a6b110b87..20e52d12f 100644 --- a/crates/ml/src/trainers/dqn/smoke_tests/training_stability.rs +++ b/crates/ml/src/trainers/dqn/smoke_tests/training_stability.rs @@ -305,10 +305,10 @@ fn test_50_epoch_convergence() -> anyhow::Result<()> { let data_dir = test_data_dir() .expect("FOXHUNT_TEST_DATA or test_data/ must exist"); let mut params = smoke_params(); - params.epochs = 50; + params.epochs = 80; params.early_stopping_enabled = false; // Run all epochs — we need the loss trajectory - params.gradient_collapse_patience = 50000; // Disable gradient collapse (readback is deferred, reports 0 per-step) - params.min_epochs_before_stopping = 50; // Must complete all 50 epochs + params.gradient_collapse_patience = 50000; // Disable gradient collapse detector + params.min_epochs_before_stopping = 80; // Must complete all 80 epochs let mut trainer = smoke_trainer_with(params)?; let rt = tokio::runtime::Builder::new_current_thread() .enable_all() @@ -317,7 +317,7 @@ fn test_50_epoch_convergence() -> anyhow::Result<()> { Ok("skip".to_owned()) }))?; - assert!(metrics.epochs_trained >= 10, "Must complete at least 10 epochs, got {}", metrics.epochs_trained); + assert!(metrics.epochs_trained >= 50, "Must complete at least 50 epochs (C51 warmup at 5), got {}", metrics.epochs_trained); // GPU trade stats: 50 epochs must produce meaningful trade count let total_trades = metrics.additional_metrics.get("total_trades").copied().unwrap_or(0.0) as usize;