diff --git a/bin/fxt/proto/monitoring.proto b/bin/fxt/proto/monitoring.proto index d7233faf6..157a89453 100644 --- a/bin/fxt/proto/monitoring.proto +++ b/bin/fxt/proto/monitoring.proto @@ -9,6 +9,10 @@ service MonitoringService { // Server-streaming: pushes updates every N seconds rpc StreamTrainingMetrics(StreamTrainingMetricsRequest) returns (stream GetLiveTrainingMetricsResponse); + + // Epoch history for a specific session (ring buffer, max 50 epochs) + rpc GetEpochHistory(GetEpochHistoryRequest) + returns (GetEpochHistoryResponse); } message GetLiveTrainingMetricsRequest { @@ -81,6 +85,20 @@ message TrainingSession { uint32 hyperopt_trial_epoch = 33; float hyperopt_trial_best_loss = 34; float hyperopt_elapsed_seconds = 35; + + // Epoch-level financial metrics + float epoch_sharpe = 36; + float epoch_sortino = 37; + float epoch_win_rate = 38; + float epoch_max_drawdown = 39; + float epoch_profit_factor = 40; + float epoch_total_return = 41; + float epoch_avg_return = 42; + uint32 epoch_total_trades = 43; + // Action distribution + float action_buy_pct = 44; + float action_sell_pct = 45; + float action_hold_pct = 46; } message GpuSnapshot { @@ -90,3 +108,33 @@ message GpuSnapshot { float temperature_celsius = 4; float power_watts = 5; } + +message GetEpochHistoryRequest { + string model = 1; + string fold = 2; + uint32 max_epochs = 3; // 0 = all (up to 50) +} + +message EpochFinancialSnapshot { + uint32 epoch = 1; + float sharpe = 2; + float sortino = 3; + float win_rate = 4; + float max_drawdown = 5; + float profit_factor = 6; + float total_return = 7; + float avg_return = 8; + uint32 total_trades = 9; + float loss = 10; + float val_loss = 11; + float learning_rate = 12; + float action_buy_pct = 13; + float action_sell_pct = 14; + float action_hold_pct = 15; +} + +message GetEpochHistoryResponse { + string model = 1; + string fold = 2; + repeated EpochFinancialSnapshot epochs = 3; +} diff --git a/services/monitoring_service/proto/monitoring.proto b/services/monitoring_service/proto/monitoring.proto index d7233faf6..157a89453 100644 --- a/services/monitoring_service/proto/monitoring.proto +++ b/services/monitoring_service/proto/monitoring.proto @@ -9,6 +9,10 @@ service MonitoringService { // Server-streaming: pushes updates every N seconds rpc StreamTrainingMetrics(StreamTrainingMetricsRequest) returns (stream GetLiveTrainingMetricsResponse); + + // Epoch history for a specific session (ring buffer, max 50 epochs) + rpc GetEpochHistory(GetEpochHistoryRequest) + returns (GetEpochHistoryResponse); } message GetLiveTrainingMetricsRequest { @@ -81,6 +85,20 @@ message TrainingSession { uint32 hyperopt_trial_epoch = 33; float hyperopt_trial_best_loss = 34; float hyperopt_elapsed_seconds = 35; + + // Epoch-level financial metrics + float epoch_sharpe = 36; + float epoch_sortino = 37; + float epoch_win_rate = 38; + float epoch_max_drawdown = 39; + float epoch_profit_factor = 40; + float epoch_total_return = 41; + float epoch_avg_return = 42; + uint32 epoch_total_trades = 43; + // Action distribution + float action_buy_pct = 44; + float action_sell_pct = 45; + float action_hold_pct = 46; } message GpuSnapshot { @@ -90,3 +108,33 @@ message GpuSnapshot { float temperature_celsius = 4; float power_watts = 5; } + +message GetEpochHistoryRequest { + string model = 1; + string fold = 2; + uint32 max_epochs = 3; // 0 = all (up to 50) +} + +message EpochFinancialSnapshot { + uint32 epoch = 1; + float sharpe = 2; + float sortino = 3; + float win_rate = 4; + float max_drawdown = 5; + float profit_factor = 6; + float total_return = 7; + float avg_return = 8; + uint32 total_trades = 9; + float loss = 10; + float val_loss = 11; + float learning_rate = 12; + float action_buy_pct = 13; + float action_sell_pct = 14; + float action_hold_pct = 15; +} + +message GetEpochHistoryResponse { + string model = 1; + string fold = 2; + repeated EpochFinancialSnapshot epochs = 3; +} diff --git a/services/monitoring_service/src/service.rs b/services/monitoring_service/src/service.rs index 85404dd49..656d22e43 100644 --- a/services/monitoring_service/src/service.rs +++ b/services/monitoring_service/src/service.rs @@ -8,8 +8,9 @@ use tonic::{Request, Response, Status}; use tracing::error; use crate::monitoring::{ - monitoring_service_server::MonitoringService, GetLiveTrainingMetricsRequest, - GetLiveTrainingMetricsResponse, GpuSnapshot, StreamTrainingMetricsRequest, TrainingSession, + monitoring_service_server::MonitoringService, GetEpochHistoryRequest, + GetEpochHistoryResponse, GetLiveTrainingMetricsRequest, GetLiveTrainingMetricsResponse, + GpuSnapshot, StreamTrainingMetricsRequest, TrainingSession, }; use crate::prometheus_client::{MetricSample, PrometheusClient}; @@ -92,6 +93,16 @@ impl MonitoringService for MonitoringServiceImpl { Ok(Response::new(Box::pin(stream))) } + + async fn get_epoch_history( + &self, + _request: Request, + ) -> Result, Status> { + // TODO(task-7): wire to epoch ring buffer storage + Err(Status::unimplemented( + "GetEpochHistory not yet wired — see task 7", + )) + } } /// Group flat metric samples into TrainingSession structs keyed by (model, fold) @@ -178,6 +189,29 @@ fn group_into_sessions(samples: &[MetricSample], model_filter: &str) -> Vec { session.hyperopt_elapsed_seconds = s.value as f32; } + // Epoch-level financial metrics + "foxhunt_training_epoch_sharpe" => session.epoch_sharpe = s.value as f32, + "foxhunt_training_epoch_sortino" => session.epoch_sortino = s.value as f32, + "foxhunt_training_epoch_win_rate" => session.epoch_win_rate = s.value as f32, + "foxhunt_training_epoch_max_drawdown" => { + session.epoch_max_drawdown = s.value as f32; + } + "foxhunt_training_epoch_profit_factor" => { + session.epoch_profit_factor = s.value as f32; + } + "foxhunt_training_epoch_total_return" => { + session.epoch_total_return = s.value as f32; + } + "foxhunt_training_epoch_avg_return" => { + session.epoch_avg_return = s.value as f32; + } + "foxhunt_training_epoch_total_trades" => { + session.epoch_total_trades = s.value as u32; + } + // Action distribution + "foxhunt_training_action_buy_pct" => session.action_buy_pct = s.value as f32, + "foxhunt_training_action_sell_pct" => session.action_sell_pct = s.value as f32, + "foxhunt_training_action_hold_pct" => session.action_hold_pct = s.value as f32, _ => {} } }