feat(proto): add epoch financial metrics + GetEpochHistory to monitoring.proto

Add 11 financial fields (sharpe, sortino, win_rate, max_drawdown,
profit_factor, total_return, avg_return, total_trades, action
distribution) to TrainingSession (fields 36-46), a new
GetEpochHistory RPC with request/response messages, and wire the
Prometheus metric mapping in the monitoring service with a
stub RPC handler for task 7.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-03-03 15:57:27 +01:00
parent 26c7c3b5b8
commit a471c913d7
3 changed files with 132 additions and 2 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<GetEpochHistoryRequest>,
) -> Result<Response<GetEpochHistoryResponse>, 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<Trai
"foxhunt_hyperopt_elapsed_seconds" => {
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,
_ => {}
}
}