feat(ml): wire Prometheus eval metrics into evaluate_baseline
Emit set_eval_metrics (win_rate, sharpe, profit_factor, return) per fold for DQN/PPO evaluation. Start metrics server on :9094. Feeds training cockpit Grafana dashboard eval panels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use common::metrics::{server as metrics_server, training_metrics as tm};
|
||||
use ml::dqn::{DQNConfig, DQN};
|
||||
|
||||
#[allow(unreachable_pub)]
|
||||
@@ -645,6 +646,10 @@ fn main() -> Result<()> {
|
||||
eprintln!("Observability init failed (non-fatal): {e}");
|
||||
}
|
||||
|
||||
tm::init();
|
||||
metrics_server::start_metrics_server(9094);
|
||||
tm::set_active_workers(1.0);
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
let eval_dqn = args.model == "dqn" || args.model == "both";
|
||||
@@ -667,7 +672,15 @@ fn main() -> Result<()> {
|
||||
|
||||
// 1. Load all OHLCV bars from DBN files
|
||||
info!("Step 1/5: Loading OHLCV bars from DBN files...");
|
||||
let data_load_start = std::time::Instant::now();
|
||||
let bars = load_all_bars(&args.data_dir, &args.symbol)?;
|
||||
let data_load_secs = data_load_start.elapsed().as_secs_f64();
|
||||
if eval_dqn {
|
||||
tm::record_data_load("dqn", data_load_secs);
|
||||
}
|
||||
if eval_ppo {
|
||||
tm::record_data_load("ppo", data_load_secs);
|
||||
}
|
||||
if bars.is_empty() {
|
||||
anyhow::bail!("No bars loaded from {}", args.data_dir.display());
|
||||
}
|
||||
@@ -781,16 +794,26 @@ fn main() -> Result<()> {
|
||||
&hp,
|
||||
) {
|
||||
Ok((returns, action_counts)) => {
|
||||
let metrics = compute_metrics(&returns);
|
||||
let fold_metrics = compute_metrics(&returns);
|
||||
let fold_str = window.fold.to_string();
|
||||
tm::set_epoch("dqn", &fold_str, window.fold as f64);
|
||||
tm::set_eval_metrics(
|
||||
"dqn",
|
||||
&fold_str,
|
||||
fold_metrics.win_rate_pct / 100.0,
|
||||
fold_metrics.sharpe_ratio,
|
||||
fold_metrics.profit_factor,
|
||||
fold_metrics.total_return_pct / 100.0,
|
||||
);
|
||||
info!(
|
||||
" [DQN] Fold {} - Sharpe={:.4} MaxDD={:.2}% WinRate={:.1}% PF={:.2} Return={:.4}% Trades={}",
|
||||
window.fold,
|
||||
metrics.sharpe_ratio,
|
||||
metrics.max_drawdown_pct,
|
||||
metrics.win_rate_pct,
|
||||
metrics.profit_factor,
|
||||
metrics.total_return_pct,
|
||||
metrics.num_trades,
|
||||
fold_metrics.sharpe_ratio,
|
||||
fold_metrics.max_drawdown_pct,
|
||||
fold_metrics.win_rate_pct,
|
||||
fold_metrics.profit_factor,
|
||||
fold_metrics.total_return_pct,
|
||||
fold_metrics.num_trades,
|
||||
);
|
||||
info!(
|
||||
" [DQN] Actions - BUY={} SELL={} HOLD={}",
|
||||
@@ -801,12 +824,12 @@ fn main() -> Result<()> {
|
||||
all_fold_metrics.push(FoldMetrics {
|
||||
fold: window.fold,
|
||||
model: "dqn".to_owned(),
|
||||
sharpe_ratio: metrics.sharpe_ratio,
|
||||
max_drawdown_pct: metrics.max_drawdown_pct,
|
||||
win_rate_pct: metrics.win_rate_pct,
|
||||
profit_factor: metrics.profit_factor,
|
||||
total_return_pct: metrics.total_return_pct,
|
||||
num_trades: metrics.num_trades,
|
||||
sharpe_ratio: fold_metrics.sharpe_ratio,
|
||||
max_drawdown_pct: fold_metrics.max_drawdown_pct,
|
||||
win_rate_pct: fold_metrics.win_rate_pct,
|
||||
profit_factor: fold_metrics.profit_factor,
|
||||
total_return_pct: fold_metrics.total_return_pct,
|
||||
num_trades: fold_metrics.num_trades,
|
||||
test_start: test_start.clone(),
|
||||
test_end: test_end.clone(),
|
||||
});
|
||||
@@ -830,16 +853,26 @@ fn main() -> Result<()> {
|
||||
&hp,
|
||||
) {
|
||||
Ok((returns, action_counts)) => {
|
||||
let metrics = compute_metrics(&returns);
|
||||
let fold_metrics = compute_metrics(&returns);
|
||||
let fold_str = window.fold.to_string();
|
||||
tm::set_epoch("ppo", &fold_str, window.fold as f64);
|
||||
tm::set_eval_metrics(
|
||||
"ppo",
|
||||
&fold_str,
|
||||
fold_metrics.win_rate_pct / 100.0,
|
||||
fold_metrics.sharpe_ratio,
|
||||
fold_metrics.profit_factor,
|
||||
fold_metrics.total_return_pct / 100.0,
|
||||
);
|
||||
info!(
|
||||
" [PPO] Fold {} - Sharpe={:.4} MaxDD={:.2}% WinRate={:.1}% PF={:.2} Return={:.4}% Trades={}",
|
||||
window.fold,
|
||||
metrics.sharpe_ratio,
|
||||
metrics.max_drawdown_pct,
|
||||
metrics.win_rate_pct,
|
||||
metrics.profit_factor,
|
||||
metrics.total_return_pct,
|
||||
metrics.num_trades,
|
||||
fold_metrics.sharpe_ratio,
|
||||
fold_metrics.max_drawdown_pct,
|
||||
fold_metrics.win_rate_pct,
|
||||
fold_metrics.profit_factor,
|
||||
fold_metrics.total_return_pct,
|
||||
fold_metrics.num_trades,
|
||||
);
|
||||
info!(
|
||||
" [PPO] Actions - BUY={} SELL={} HOLD={}",
|
||||
@@ -850,12 +883,12 @@ fn main() -> Result<()> {
|
||||
all_fold_metrics.push(FoldMetrics {
|
||||
fold: window.fold,
|
||||
model: "ppo".to_owned(),
|
||||
sharpe_ratio: metrics.sharpe_ratio,
|
||||
max_drawdown_pct: metrics.max_drawdown_pct,
|
||||
win_rate_pct: metrics.win_rate_pct,
|
||||
profit_factor: metrics.profit_factor,
|
||||
total_return_pct: metrics.total_return_pct,
|
||||
num_trades: metrics.num_trades,
|
||||
sharpe_ratio: fold_metrics.sharpe_ratio,
|
||||
max_drawdown_pct: fold_metrics.max_drawdown_pct,
|
||||
win_rate_pct: fold_metrics.win_rate_pct,
|
||||
profit_factor: fold_metrics.profit_factor,
|
||||
total_return_pct: fold_metrics.total_return_pct,
|
||||
num_trades: fold_metrics.num_trades,
|
||||
test_start: test_start.clone(),
|
||||
test_end: test_end.clone(),
|
||||
});
|
||||
@@ -917,5 +950,7 @@ fn main() -> Result<()> {
|
||||
info!(" Report saved to: {}", args.output.display());
|
||||
info!(" Total fold evaluations: {}", report.folds.len());
|
||||
|
||||
tm::set_active_workers(0.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user