diff --git a/bin/fxt/src/commands/train/monitor.rs b/bin/fxt/src/commands/train/monitor.rs index b8a13e00f..56c32d899 100644 --- a/bin/fxt/src/commands/train/monitor.rs +++ b/bin/fxt/src/commands/train/monitor.rs @@ -101,6 +101,7 @@ fn render_snapshot(resp: &GetLiveTrainingMetricsResponse) { print_rl_diagnostics(&resp.sessions); print_hyperopt_summary(&resp.sessions); print_health_summary(&resp.sessions); + print_financial_metrics(&resp.sessions); } fn render_tui(resp: &GetLiveTrainingMetricsResponse) { @@ -232,3 +233,42 @@ fn print_health_summary(sessions: &[TrainingSession]) { total_nan, total_grad, total_ckpt ); } + +fn print_financial_metrics(sessions: &[TrainingSession]) { + let financial: Vec<_> = sessions + .iter() + .filter(|s| s.epoch_sharpe != 0.0 || s.epoch_win_rate > 0.0) + .collect(); + if financial.is_empty() { + return; + } + println!(); + println!("{}", "Epoch Financial Metrics:".bright_cyan()); + for s in &financial { + let sharpe_colored = if s.epoch_sharpe >= 2.0 { + format!("{:.2}", s.epoch_sharpe).green() + } else if s.epoch_sharpe >= 1.0 { + format!("{:.2}", s.epoch_sharpe).yellow() + } else { + format!("{:.2}", s.epoch_sharpe).red() + }; + println!( + " {}: Sharpe={} WinRate={:.1}% MaxDD={:.1}% PF={:.2} Return={:+.2}% Trades={}", + s.model.bright_white(), + sharpe_colored, + s.epoch_win_rate * 100.0, + s.epoch_max_drawdown * 100.0, + s.epoch_profit_factor, + s.epoch_total_return * 100.0, + s.epoch_total_trades, + ); + if s.action_buy_pct > 0.0 || s.action_sell_pct > 0.0 { + println!( + " Actions: BUY {:.0}% | SELL {:.0}% | HOLD {:.0}%", + s.action_buy_pct * 100.0, + s.action_sell_pct * 100.0, + s.action_hold_pct * 100.0, + ); + } + } +}