Files
foxhunt/tli/src/dashboard/mod.rs
jgrusewski 6bd5b18465 🔧 Wave 33: Test Compilation Improvements - 57 errors remaining
**Progress: 1,178 → 57 test errors (95% reduction)**

## Status Summary
-  Production code: Compiles cleanly (0 errors)
- ⚠️  Test code: 57 errors remain (massive improvement)
- ⚙️  All services build successfully
- 📊 Warning count: 253 (target: <20) - AGENTS WILL FIX

## Remaining Test Errors (57 total)
### Primary Issues:
1. 23× E0308 mismatched types
2. 17× E0433 undeclared Decimal
3. 15× E0433 compliance module not found
4. 6× E0624 private method access
5. Various import and type issues

## Next Phase: Wave 33-2
Launch 10+ parallel agents to:
- Fix remaining 57 test compilation errors
- Reduce 253 warnings to <20
- Achieve 95% test coverage
- Ensure all tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-01 21:24:28 +02:00

323 lines
11 KiB
Rust

//! Dashboard Framework for TLI Terminal Interface
//!
//! This module provides a comprehensive dashboard system for the Foxhunt HFT trading system.
//! It implements a multi-dashboard architecture with real-time data streaming and interactive
//! controls using Ratatui for terminal-based visualization.
//!
//! ## Architecture
//! - **`DashboardManager`**: Central coordinator for all dashboards
//! - **Dashboard Trait**: Common interface for all dashboard implementations
//! - **Real-time Updates**: Event-driven data streaming from gRPC services
//! - **Navigation**: Keyboard shortcuts for dashboard switching
//! - **Layout Management**: Consistent UI layout across all dashboards
use anyhow::Result;
use crossterm::event::KeyEvent;
use ratatui::prelude::*;
use std::collections::HashMap;
use tokio::sync::mpsc;
// Import from events module
use crate::dashboard::events::DashboardEvent;
use crate::dashboard::layout::LayoutManager;
pub mod backtesting;
pub mod config;
pub mod events;
pub mod layout;
pub mod ml;
pub mod performance;
pub mod risk;
pub mod trading;
pub mod vault_status;
// Import dashboard implementations after module declarations
use backtesting::BacktestingDashboard;
use config::create_config_dashboard;
use ml::MLDashboard;
use performance::PerformanceDashboard;
use risk::RiskDashboard;
use trading::TradingDashboard;
use vault_status::VaultStatusWidget;
// NO RE-EXPORTS: Import directly from submodules
// Use tli::dashboard::events::DashboardEvent instead
// Use tli::dashboard::layout::LayoutManager instead
/// Main dashboard manager that coordinates all dashboards
pub struct DashboardManager {
pub active_dashboard: DashboardType,
pub dashboards: HashMap<DashboardType, Box<dyn Dashboard>>,
pub layout_manager: LayoutManager,
pub event_receiver: mpsc::Receiver<DashboardEvent>,
pub _event_sender: mpsc::Sender<DashboardEvent>,
// Vault service removed - TLI is pure client, uses shared config crate
}
/// Available dashboard types
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DashboardType {
Trading, // Live positions, orders, executions, market data
Risk, // VaR, drawdown, position limits, safety controls
ML, // Model predictions, signal strength, confidence
Performance, // PnL, Sharpe ratios, strategy performance
Config, // System configuration management
Backtesting, // Strategy testing, historical analysis, results
Vault, // Vault status, credentials, service discovery
}
impl DashboardType {
pub fn all() -> Vec<DashboardType> {
vec![
DashboardType::Trading,
DashboardType::Risk,
DashboardType::ML,
DashboardType::Performance,
DashboardType::Config,
DashboardType::Backtesting,
DashboardType::Vault,
]
}
pub const fn shortcut_key(&self) -> char {
match self {
DashboardType::Trading => 't',
DashboardType::Risk => 'r',
DashboardType::ML => 'm',
DashboardType::Performance => 'p',
DashboardType::Config => 'c',
DashboardType::Backtesting => 'b',
DashboardType::Vault => 'v',
}
}
pub const fn title(&self) -> &'static str {
match self {
DashboardType::Trading => "Trading",
DashboardType::Risk => "Risk",
DashboardType::ML => "ML",
DashboardType::Performance => "Performance",
DashboardType::Config => "Configuration",
DashboardType::Backtesting => "Backtesting",
DashboardType::Vault => "Vault Status",
}
}
}
/// Common interface for all dashboard implementations
pub trait Dashboard: Send + Sync {
/// Render the dashboard to the given frame area
fn render(&mut self, frame: &mut Frame, area: Rect) -> Result<()>;
/// Handle keyboard input and return optional dashboard events
fn handle_input(&mut self, key: KeyEvent) -> Result<Option<DashboardEvent>>;
/// Update dashboard with new data/events
fn update(&mut self, event: DashboardEvent) -> Result<()>;
/// Get dashboard title for display
fn title(&self) -> &str;
/// Get keyboard shortcut for this dashboard
fn shortcut_key(&self) -> char;
/// Check if dashboard needs redraw
fn needs_redraw(&self) -> bool;
/// Mark dashboard as drawn
fn mark_drawn(&mut self);
}
impl DashboardManager {
pub fn new() -> (Self, mpsc::Sender<DashboardEvent>) {
let (_event_sender, event_receiver) = mpsc::channel(1000);
let mut dashboards: HashMap<DashboardType, Box<dyn Dashboard>> = HashMap::new();
// Initialize all dashboards
dashboards.insert(
DashboardType::Trading,
Box::new(TradingDashboard::new(_event_sender.clone())),
);
dashboards.insert(
DashboardType::Risk,
Box::new(RiskDashboard::new(_event_sender.clone())),
);
dashboards.insert(
DashboardType::ML,
Box::new(MLDashboard::new(_event_sender.clone())),
);
dashboards.insert(
DashboardType::Performance,
Box::new(PerformanceDashboard::new(_event_sender.clone())),
);
dashboards.insert(
DashboardType::Config,
create_config_dashboard(_event_sender.clone()),
);
dashboards.insert(
DashboardType::Backtesting,
Box::new(BacktestingDashboard::new(_event_sender.clone())),
);
dashboards.insert(
DashboardType::Vault,
Box::new(VaultStatusWidget::new(_event_sender.clone())),
);
let manager = Self {
active_dashboard: DashboardType::Trading,
dashboards,
layout_manager: LayoutManager::new(),
event_receiver,
_event_sender: _event_sender.clone(),
};
(manager, _event_sender)
}
pub fn render(&mut self, frame: &mut Frame) -> Result<()> {
let area = frame.area();
// Create main layout
let (header_area, content_area, sidebar_area, footer_area) =
self.layout_manager.create_layout(area);
// Render header with navigation tabs
self.render_header(frame, header_area)?;
// Render active dashboard
if let Some(dashboard) = self.dashboards.get_mut(&self.active_dashboard) {
dashboard.render(frame, content_area)?;
}
// Render sidebar with quick stats
self.render_sidebar(frame, sidebar_area)?;
// Render footer with help and status
self.render_footer(frame, footer_area)?;
Ok(())
}
pub fn handle_input(&mut self, key: KeyEvent) -> Result<Option<DashboardEvent>> {
// Check for dashboard switching shortcuts first
for dashboard_type in DashboardType::all() {
if key.code == crossterm::event::KeyCode::Char(dashboard_type.shortcut_key()) {
self.active_dashboard = dashboard_type;
return Ok(Some(DashboardEvent::SwitchDashboard(dashboard_type)));
}
}
// Handle ESC for exit
if key.code == crossterm::event::KeyCode::Esc {
return Ok(Some(DashboardEvent::Exit));
}
// Pass input to active dashboard
if let Some(dashboard) = self.dashboards.get_mut(&self.active_dashboard) {
dashboard.handle_input(key)
} else {
Ok(None)
}
}
pub async fn handle_event(&mut self, event: DashboardEvent) -> Result<bool> {
match event {
DashboardEvent::SwitchDashboard(dashboard_type) => {
self.active_dashboard = dashboard_type;
Ok(false)
},
DashboardEvent::Exit => {
Ok(true) // Signal to exit
},
_ => {
// Forward event to all dashboards that might be interested
for dashboard in self.dashboards.values_mut() {
let _ = dashboard.update(event.clone());
}
Ok(false)
},
}
}
// Vault service functionality removed - TLI is pure client, uses shared config crate
/// All vault-related methods removed - TLI uses shared config crate instead
// Vault service functionality completely removed from TLI
// TLI is a pure client - no vault service management
fn render_header(&self, frame: &mut Frame, area: Rect) -> Result<()> {
let titles: Vec<String> = DashboardType::all()
.iter()
.map(|dt| {
let _prefix = if *dt == self.active_dashboard {
"\u{25cf}"
} else {
"\u{25cb}"
};
format!("[{}]{}", dt.shortcut_key().to_uppercase(), dt.title())
})
.collect();
let tabs = ratatui::widgets::Tabs::new(titles)
.block(
ratatui::widgets::Block::default()
.borders(ratatui::widgets::Borders::ALL)
.title("Foxhunt HFT Trading System - TLI Terminal"),
)
.style(Style::default().fg(Color::White))
.highlight_style(
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
)
.select(self.active_dashboard as usize);
frame.render_widget(tabs, area);
Ok(())
}
fn render_sidebar(&self, frame: &mut Frame, area: Rect) -> Result<()> {
let block = ratatui::widgets::Block::default()
.borders(ratatui::widgets::Borders::ALL)
.title("Quick Stats");
// Get actual Vault status (placeholder - TLI uses shared config crate)
let vault_status = "\u{25cb}"; // Empty circle for not available - use config crate integration
let content = ratatui::widgets::Paragraph::new(
format!(
"Connection: \u{25cf}\u{25cf}\u{25cf}\nVault: {}\nLatency: 12ms\nOrders: 15\nPositions: 5\nPnL: +$2,500",
vault_status
),
)
.block(block)
.wrap(ratatui::widgets::Wrap { trim: true });
frame.render_widget(content, area);
Ok(())
}
fn render_footer(&self, frame: &mut Frame, area: Rect) -> Result<()> {
let help_text = format!(
"[{}] Dashboards | [ESC] Exit | Status: Connected",
DashboardType::all()
.iter()
.map(|dt| format!(
"[{}]{}",
dt.shortcut_key().to_uppercase(),
dt.title().chars().next().unwrap()
))
.collect::<Vec<_>>()
.join(" ")
);
let footer = ratatui::widgets::Paragraph::new(help_text)
.block(ratatui::widgets::Block::default().borders(ratatui::widgets::Borders::ALL))
.style(Style::default().fg(Color::Gray));
frame.render_widget(footer, area);
Ok(())
}
}