Files
foxhunt/bin/fxt/src/tui/state.rs
2026-03-04 13:11:49 +01:00

346 lines
8.7 KiB
Rust

//! Application state -- view-model structs consumed by every cockpit.
//!
//! These are intentionally decoupled from protobuf types so the TUI layer
//! has zero coupling to the gRPC schema.
use std::collections::VecDeque;
use std::time::Instant;
// ---------------------------------------------------------------------------
// Connection status
// ---------------------------------------------------------------------------
/// Connection state for the gRPC streaming layer.
#[derive(Debug, Clone)]
pub enum ConnectionStatus {
Connecting,
Connected,
Reconnecting { attempt: u32 },
Error(String),
}
impl Default for ConnectionStatus {
fn default() -> Self {
Self::Connecting
}
}
// ---------------------------------------------------------------------------
// Top-level state
// ---------------------------------------------------------------------------
/// Root state shared across all seven cockpit views.
pub struct AppState {
/// Active cockpit index (0..=6).
pub current_cockpit: usize,
/// Whether the help overlay is visible.
pub show_help: bool,
/// ML training sessions.
pub training_sessions: Vec<TrainingSessionData>,
/// GPU telemetry.
pub gpu: GpuData,
/// Service health statuses.
pub services: Vec<ServiceData>,
/// Open trading positions.
pub positions: Vec<PositionData>,
/// Recent trade executions (ring buffer).
pub executions: VecDeque<ExecutionData>,
/// Recent order events (ring buffer).
pub orders: VecDeque<OrderEventData>,
/// Account-level summary.
pub account: AccountData,
/// Aggregated risk metrics.
pub risk: RiskData,
/// Recent risk alerts (ring buffer).
pub risk_alerts: VecDeque<RiskAlertData>,
/// ML model statuses.
pub model_statuses: Vec<ModelStatusData>,
/// Host / cluster resource usage.
pub system_resources: SystemResources,
/// Live data-feed information.
pub data_feeds: Vec<DataFeedData>,
/// Data cache statistics.
pub data_cache: DataCacheData,
/// System alerts from monitoring (ring buffer).
pub alerts: VecDeque<AlertData>,
/// K8s pod groups (from SubscribeClusterPods).
pub pods: Vec<PodGroupData>,
/// Currently selected pod group index.
pub selected_pod_group: Option<usize>,
/// Whether the selected pod group is expanded.
pub pod_group_expanded: bool,
/// Timestamp of the last data refresh.
pub last_update: Instant,
/// gRPC connection status.
pub connection_status: ConnectionStatus,
}
impl Default for AppState {
fn default() -> Self {
Self {
current_cockpit: 0,
show_help: false,
training_sessions: Vec::new(),
gpu: GpuData::default(),
services: Vec::new(),
positions: Vec::new(),
executions: VecDeque::new(),
orders: VecDeque::new(),
account: AccountData::default(),
risk: RiskData::default(),
risk_alerts: VecDeque::new(),
model_statuses: Vec::new(),
system_resources: SystemResources::default(),
data_feeds: Vec::new(),
data_cache: DataCacheData::default(),
alerts: VecDeque::new(),
pods: Vec::new(),
selected_pod_group: None,
pod_group_expanded: false,
last_update: Instant::now(),
connection_status: ConnectionStatus::default(),
}
}
}
// ---------------------------------------------------------------------------
// Data sub-structs
// ---------------------------------------------------------------------------
/// A single ML training session.
pub struct TrainingSessionData {
pub model: String,
pub fold: u32,
pub epoch: u32,
pub max_epochs: u32,
pub loss: f64,
pub val_loss: f64,
pub batches_per_sec: f64,
pub sharpe: f64,
pub sortino: f64,
pub win_rate: f64,
pub max_dd: f64,
}
/// GPU telemetry snapshot.
pub struct GpuData {
pub name: String,
pub utilization: f64,
pub memory_used_gb: f64,
pub memory_total_gb: f64,
pub temperature_c: u32,
pub power_watts: u32,
pub power_limit_watts: u32,
}
impl Default for GpuData {
fn default() -> Self {
Self {
name: "N/A".into(),
utilization: 0.0,
memory_used_gb: 0.0,
memory_total_gb: 0.0,
temperature_c: 0,
power_watts: 0,
power_limit_watts: 0,
}
}
}
/// A backend microservice.
pub struct ServiceData {
pub name: String,
pub healthy: bool,
pub uptime: String,
pub version: String,
/// Health-check round-trip latency in milliseconds.
pub latency_ms: f64,
}
/// An open trading position.
pub struct PositionData {
pub symbol: String,
pub qty: i64,
pub entry_price: f64,
pub market_price: f64,
pub unrealized_pnl: f64,
}
/// A recent trade execution.
pub struct ExecutionData {
pub time: String,
pub symbol: String,
pub side: String,
pub qty: i64,
pub price: f64,
pub status: String,
}
/// Account-level financials.
pub struct AccountData {
pub equity: f64,
pub cash: f64,
pub margin_used: f64,
pub daily_pnl: f64,
pub total_pnl: f64,
}
impl Default for AccountData {
fn default() -> Self {
Self {
equity: 0.0,
cash: 0.0,
margin_used: 0.0,
daily_pnl: 0.0,
total_pnl: 0.0,
}
}
}
/// Kill-switch, drawdown, circuit-breaker, and position-limit data.
pub struct RiskData {
pub global_kill_switch: Option<bool>,
pub portfolio_kill_switch: Option<bool>,
pub strategy_kill_switch: Option<bool>,
pub instrument_kill_switch: Option<bool>,
pub current_drawdown_pct: f64,
pub max_drawdown_pct: f64,
pub high_water_mark: f64,
pub circuit_breakers: Vec<CircuitBreakerData>,
pub position_limits: Vec<PositionLimitData>,
}
impl Default for RiskData {
fn default() -> Self {
Self {
global_kill_switch: None,
portfolio_kill_switch: None,
strategy_kill_switch: None,
instrument_kill_switch: None,
current_drawdown_pct: 0.0,
max_drawdown_pct: 0.0,
high_water_mark: 0.0,
circuit_breakers: Vec::new(),
position_limits: Vec::new(),
}
}
}
/// A single circuit breaker.
pub struct CircuitBreakerData {
pub name: String,
pub threshold: Option<f64>,
pub current: Option<f64>,
pub tripped: bool,
}
/// Position-limit on a single instrument.
pub struct PositionLimitData {
pub symbol: String,
pub max_qty: i64,
pub current_qty: i64,
}
/// System resource metrics.
pub struct SystemResources {
pub cpu_usage_pct: f64,
pub ram_used_gb: f64,
pub ram_total_gb: f64,
pub gpu_cluster_utilization: f64,
}
impl Default for SystemResources {
fn default() -> Self {
Self {
cpu_usage_pct: 0.0,
ram_used_gb: 0.0,
ram_total_gb: 0.0,
gpu_cluster_utilization: 0.0,
}
}
}
/// A single live data feed.
pub struct DataFeedData {
pub symbol: String,
pub records_per_sec: u64,
pub latency_us: u64,
pub status: String,
}
/// Aggregate data cache statistics.
pub struct DataCacheData {
pub total_records: u64,
pub cache_hit_rate: f64,
pub disk_usage_gb: f64,
pub oldest_date: String,
pub newest_date: String,
}
impl Default for DataCacheData {
fn default() -> Self {
Self {
total_records: 0,
cache_hit_rate: 0.0,
disk_usage_gb: 0.0,
oldest_date: String::new(),
newest_date: String::new(),
}
}
}
/// An order lifecycle event.
pub struct OrderEventData {
pub time: String,
pub order_id: String,
pub symbol: String,
pub side: String,
pub status: String,
pub qty: f64,
pub filled_qty: f64,
}
/// A risk alert from the risk service.
pub struct RiskAlertData {
pub time: String,
pub severity: String,
pub alert_type: String,
pub message: String,
}
/// ML model operational status.
pub struct ModelStatusData {
pub model_name: String,
pub state: String,
pub health: String,
pub last_prediction: String,
}
/// A system-level alert from monitoring.
pub struct AlertData {
pub time: String,
pub severity: String,
pub service: String,
pub title: String,
}
/// A single K8s pod.
pub struct PodData {
pub name: String,
pub service: String,
pub status: String,
pub restarts: i32,
pub age: String,
pub node: String,
pub ready: bool,
pub version: String,
}
/// Pods grouped by service.
pub struct PodGroupData {
pub service: String,
pub pods: Vec<PodData>,
pub ready_count: usize,
pub total_count: usize,
}