# DrawdownMonitor Integration - Implementation Guide for Agent 25 ## Quick Start: Making the Tests Pass ### Overview **10 TDD tests** are waiting for you in `/home/jgrusewski/Work/foxhunt/ml/tests/risk_drawdown_integration_test.rs`. They currently **all FAIL** (intentional). Your job is to implement DQN trainer integration to make them **PASS**. **Current Status**: Phase 1 (RED - tests written, failing) **Your Task**: Phase 2 (GREEN - make tests pass) --- ## Test Summary (Quick Reference) | # | Test Name | What It Tests | Why It Fails | Fix Location | |---|-----------|---------------|-------------|--------------| | 1 | `test_drawdown_monitor_initialization` | Monitor creation with config | No monitor in trainer | `DQNTrainer::new()` | | 2 | `test_update_equity_each_step` | PnL updates during training | Trainer doesn't call monitor | Train loop (each step) | | 3 | `test_early_stop_on_15_percent_drawdown` | Early stop at 15% loss | No early stop check | Train loop (after update) | | 4 | `test_alert_at_10_percent_threshold` | Warning alert at 10% | Alert checking not tested | monitor.update_pnl() | | 5 | `test_alert_at_12_5_percent_threshold` | Critical alert at 12.5% | Alert checking not tested | monitor.update_pnl() | | 6 | `test_no_early_stop_below_threshold` | Continue below 15% | No threshold check | Train loop condition | | 7 | `test_drawdown_reset_between_epochs` | Reset monitor per epoch | No reset between epochs | Epoch loop (start) | | 8 | `test_async_alert_channel_receives_messages` | Alert subscription works | Channel may not deliver | Subscribe + check | | 9 | `test_current_drawdown_logged` | Log drawdown percentage | No logging | Train loop logging | | 10 | `test_checkpoint_saved_before_early_stop` | Save before stop | No checkpoint integration | Train loop (before break) | --- ## Implementation Phases (Estimated: 2-4 Hours) ### Phase 1: Monitor Initialization (30 min) **Goal**: Make test #1 PASS **What to do**: 1. Add `monitor: Arc` field to `DQNTrainer` struct 2. In `DQNTrainer::new()`, create monitor: ```rust let monitor = Arc::new(DrawdownMonitor::new()); let config = DrawdownAlertConfig { portfolio_id: Some(format!("dqn_epoch")), warning_threshold: 10.0, critical_threshold: 12.5, emergency_threshold: 15.0, enabled: true, }; monitor.configure_alerts(config).await?; ``` 3. Run test: `cargo test -p ml test_drawdown_monitor_initialization -- --exact --nocapture` 4. Expected: ✅ PASS --- ### Phase 2: Equity Updates (1 hour) **Goal**: Make tests #2, #4, #5 PASS **What to do**: 1. In training loop, compute current portfolio value after each step 2. Create `PnLMetrics` with current portfolio state 3. Call `monitor.update_pnl(&pnl_metrics).await?` 4. Track `high_water_mark` (max equity so far this epoch) **Code location**: In `DQNTrainer::train()`, main loop around line 900-1200 **Example**: ```rust // Inside training loop, after processing batch let current_equity = portfolio_tracker.get_total_value(); let pnl_metrics = PnLMetrics { portfolio_id: "dqn_training".to_string(), realized_pnl: Price::from_f64(realized)?, unrealized_pnl: Price::from_f64(unrealized)?, total_unrealized_pnl: Price::from_f64(unrealized)?, total_pnl: Price::from_f64(current_equity)?, daily_pnl: Price::from_f64(daily)?, inception_pnl: Price::from_f64(total)?, max_drawdown: Price::from_f64(max_dd)?, current_drawdown_pct: 0.0, // Will be computed by monitor high_water_mark: Price::from_f64(epoch_hwm)?, roi_pct: 0.0, timestamp: chrono::Utc::now().timestamp(), }; let _alerts = self.monitor.update_pnl(&pnl_metrics).await?; ``` 3. Run tests: `cargo test -p ml test_update_equity_each_step test_alert_at_10_percent -- --nocapture` 4. Expected: ✅ PASS (2-3 tests) --- ### Phase 3: Early Stopping (1.5 hours) **Goal**: Make tests #3, #6 PASS **What to do**: 1. After calling `monitor.update_pnl()`, get alerts 2. Check if any alert has `severity == RiskSeverity::Critical` 3. If yes: Save checkpoint, then break training loop 4. If no: Continue training **Code location**: Same training loop, right after `update_pnl()` **Example**: ```rust let alerts = self.monitor.update_pnl(&pnl_metrics).await?; // Check for emergency (15% drawdown) alert if alerts.iter().any(|a| a.severity == RiskSeverity::Critical) { warn!("Early stopping triggered: drawdown >= 15%"); // SAVE CHECKPOINT BEFORE STOPPING self.save_checkpoint(epoch)?; // Then break break; } ``` 3. Run tests: `cargo test -p ml test_early_stop_on_15_percent test_no_early_stop_below -- --nocapture` 4. Expected: ✅ PASS (2 tests) --- ### Phase 4: Epoch Reset (30 min) **Goal**: Make test #7 PASS **What to do**: 1. At start of each epoch, reset monitor (clear history, reset HWM) 2. OR create new monitor per epoch 3. Update high water mark to starting equity for epoch **Code location**: Epoch loop, right after `let epoch = ...` **Example**: ```rust for epoch in 0..self.hyperparams.epochs { // Create fresh monitor for this epoch let monitor = Arc::new(DrawdownMonitor::new()); monitor.configure_alerts(config).await?; // Or clear history: // self.monitor.reset_epoch(); // Continue with training... } ``` 3. Run test: `cargo test -p ml test_drawdown_reset_between_epochs -- --nocapture` 4. Expected: ✅ PASS (1 test) --- ### Phase 5: Logging (30 min) **Goal**: Make test #9 PASS **What to do**: 1. After `update_pnl()`, get drawdown stats: `let stats = monitor.get_drawdown_stats(...).await?` 2. Log at appropriate level based on drawdown %: - 0-10%: `info!()` - 10-15%: `warn!()` - 15%+: `error!()` **Code location**: Training loop, after update_pnl() **Example**: ```rust let stats = self.monitor.get_drawdown_stats("dqn_training").await?; match stats.current_drawdown_pct { dd if dd >= 15.0 => error!("Portfolio drawdown: {:.2}%", dd), dd if dd >= 10.0 => warn!("Portfolio drawdown: {:.2}%", dd), dd => info!("Portfolio drawdown: {:.2}%", dd), } ``` 3. Run test: `cargo test -p ml test_current_drawdown_logged -- --nocapture` 4. Expected: ✅ PASS (1 test) --- ### Phase 6: Checkpoint & Async (30 min) **Goal**: Make tests #8, #10 PASS **What to do**: 1. Ensure checkpoint is **saved BEFORE** breaking loop (already done in Phase 3) 2. For async alerts: Create subscription in trainer, spawn listener task **Code location**: Training setup + loop **Example**: ```rust // At trainer initialization let mut alert_rx = self.monitor.subscribe_alerts(); // Spawn listener (optional, for external monitoring) let alert_handle = tokio::spawn(async move { while let Ok(alert) = alert_rx.recv().await { warn!("Drawdown alert: {} - {}", alert.severity_level, alert.message); } }); // In loop (already done): self.save_checkpoint(epoch)?; // BEFORE break break; ``` 3. Run tests: `cargo test -p ml test_async_alert_channel test_checkpoint_saved -- --nocapture` 4. Expected: ✅ PASS (2 tests) --- ## Testing Strategy ### Run Individual Test ```bash cargo test -p ml test_drawdown_monitor_initialization -- --exact --nocapture ``` ### Run All DrawdownMonitor Tests ```bash cargo test -p ml risk_drawdown_integration_test -- --nocapture ``` ### Run Tests + Show Failures ```bash cargo test -p ml risk_drawdown_integration_test -- --nocapture --test-threads=1 ``` ### Run with Logging ```bash RUST_LOG=debug cargo test -p ml risk_drawdown_integration_test -- --nocapture ``` --- ## Key Files to Modify | File | Change | Lines | |------|--------|-------| | `/home/jgrusewski/Work/foxhunt/ml/src/trainers/dqn.rs` | Add monitor field, init, update, early stop | 900-1200 | | `/home/jgrusewski/Work/foxhunt/ml/src/dqn/mod.rs` | Export monitor module if needed | - | ## Files NOT to Touch - Test file: `/home/jgrusewski/Work/foxhunt/ml/tests/risk_drawdown_integration_test.rs` (read-only) - Risk crate: `/home/jgrusewski/Work/foxhunt/risk/` (already complete) --- ## Expected Test Results ### Before Implementation (Current) ``` test risk_drawdown_integration_test::test_drawdown_monitor_initialization ... FAILED test risk_drawdown_integration_test::test_update_equity_each_step ... FAILED test risk_drawdown_integration_test::test_early_stop_on_15_percent_drawdown ... FAILED test risk_drawdown_integration_test::test_alert_at_10_percent_threshold ... FAILED test risk_drawdown_integration_test::test_alert_at_12_5_percent_threshold ... FAILED test risk_drawdown_integration_test::test_no_early_stop_below_threshold ... FAILED test risk_drawdown_integration_test::test_drawdown_reset_between_epochs ... FAILED test risk_drawdown_integration_test::test_async_alert_channel_receives_messages ... FAILED test risk_drawdown_integration_test::test_current_drawdown_logged ... FAILED test risk_drawdown_integration_test::test_checkpoint_saved_before_early_stop ... FAILED failures: 10 ``` ### After Phase 1 Complete ``` test_drawdown_monitor_initialization ... PASSED test_update_equity_each_step ... FAILED ... (rest failing) failures: 9 ``` ### After All Phases Complete ``` test_drawdown_monitor_initialization ... PASSED test_update_equity_each_step ... PASSED test_early_stop_on_15_percent_drawdown ... PASSED test_alert_at_10_percent_threshold ... PASSED test_alert_at_12_5_percent_threshold ... PASSED test_no_early_stop_below_threshold ... PASSED test_drawdown_reset_between_epochs ... PASSED test_async_alert_channel_receives_messages ... PASSED test_current_drawdown_logged ... PASSED test_checkpoint_saved_before_early_stop ... PASSED failures: 0 ✅ ``` --- ## Common Issues & Solutions ### Issue: "Cannot find struct DrawdownMonitor" **Solution**: Ensure `use risk::drawdown_monitor::DrawdownMonitor;` is in imports ### Issue: "Expected async, got sync" **Solution**: Remember to `.await?` on async calls to monitor ### Issue: "Field not found in struct" **Solution**: Check DrawdownMonitor implementation in `/home/jgrusewski/Work/foxhunt/risk/src/drawdown_monitor.rs` for available methods ### Issue: "Alerts always empty" **Solution**: Ensure you're checking correct severity level (`RiskSeverity::Critical` for emergency) ### Issue: "Drawdown always 0%" **Solution**: Ensure `high_water_mark` is set correctly (should be max equity so far in epoch) --- ## Documentation ### Test Details - Full report: `/home/jgrusewski/Work/foxhunt/DRAWDOWN_TDD_REPORT.md` - Quick summary: `/home/jgrusewski/Work/foxhunt/DRAWDOWN_TDD_TEST_SUMMARY.txt` - Verification: `/home/jgrusewski/Work/foxhunt/DRAWDOWN_TDD_VERIFICATION.txt` ### Code References - DrawdownMonitor API: `/home/jgrusewski/Work/foxhunt/risk/src/drawdown_monitor.rs` (lines 67-263) - Risk types: `/home/jgrusewski/Work/foxhunt/risk/src/risk_types.rs` (lines 573-584) --- ## Success Criteria ✅ All 10 tests PASS ✅ Early stopping prevents >15% loss ✅ Checkpoints saved before stopping ✅ Alerts logged appropriately ✅ No compiler warnings ✅ Code follows existing style ✅ All async operations have `.await` --- ## Estimated Time - Phase 1 (Init): 30 min - Phase 2 (Updates): 1 hour - Phase 3 (Early Stop): 1.5 hours - Phase 4 (Reset): 30 min - Phase 5 (Logging): 30 min - Phase 6 (Async): 30 min - **Total**: 4.5 hours (can be 2-3 hours if experienced with codebase) --- ## Final Checklist - [ ] Phase 1: test_drawdown_monitor_initialization PASSES - [ ] Phase 2: test_update_equity_each_step PASSES - [ ] Phase 2: test_alert_at_10_percent_threshold PASSES - [ ] Phase 2: test_alert_at_12_5_percent_threshold PASSES - [ ] Phase 3: test_early_stop_on_15_percent_drawdown PASSES - [ ] Phase 3: test_no_early_stop_below_threshold PASSES - [ ] Phase 4: test_drawdown_reset_between_epochs PASSES - [ ] Phase 5: test_current_drawdown_logged PASSES - [ ] Phase 6: test_async_alert_channel_receives_messages PASSES - [ ] Phase 6: test_checkpoint_saved_before_early_stop PASSES - [ ] All 10/10 tests PASS - [ ] No new warnings introduced - [ ] Code compiles cleanly - [ ] Ready for production deployment --- Good luck! The tests are well-documented - each one tells you exactly what to implement.