//! Trading ↔ Risk Management Integration Tests //! //! This module provides comprehensive integration testing between the Trading system //! and Risk Management components. Tests cover: //! //! ## Test Coverage Areas //! - Pre-trade risk checks (position limits, VaR) //! - Kelly sizing calculations for position sizing //! - Real-time risk monitoring during trades //! - Kill switch activation under stress conditions //! - Portfolio rebalancing triggers //! - VaR calculation accuracy across market regimes //! - Risk limit enforcement and escalation //! - Performance validation under HFT requirements //! //! ## Architecture Under Test //! ``` //! Trading Engine ←→ Risk Management ←→ Portfolio Monitor //! ↓ ↓ ↓ //! Order Validation Position Limits Risk Metrics //! ↓ ↓ ↓ //! Execution Logic VaR Models Kill Switches //! ``` #![allow(unused_crate_dependencies)] use std::collections::HashMap; use std::sync::Arc; use std::time::{Duration, Instant}; use tokio::sync::{RwLock, mpsc, Mutex}; use tokio::time::timeout; use uuid::Uuid; // Import core system types use trading_engine::timing::HardwareTimestamp; // use risk::prelude::*; // REMOVED - prelude does not exist /// Test result type for safe error handling type TestResult = Result>; /// Trading-Risk integration test configuration #[derive(Debug, Clone)] pub struct TradingRiskIntegrationConfig { /// Maximum risk check latency for HFT (microseconds) pub max_risk_check_latency_us: u64, /// Maximum position limit check latency pub max_position_check_latency_us: u64, /// Maximum VaR calculation latency pub max_var_calculation_latency_ms: u64, /// Test portfolio initial value pub test_portfolio_value: Decimal, /// Maximum position size as percentage of portfolio pub max_position_size_pct: f64, /// VaR confidence level pub var_confidence_level: f64, /// Kill switch activation threshold (percentage loss) pub kill_switch_threshold_pct: f64, /// Test symbols for risk validation pub test_symbols: Vec, /// Stress test scenarios pub stress_test_scenarios: Vec, } impl Default for TradingRiskIntegrationConfig { fn default() -> Self { Self { max_risk_check_latency_us: 5_000, // 5ms for HFT max_position_check_latency_us: 1_000, // 1ms position check max_var_calculation_latency_ms: 100, // 100ms VaR calc test_portfolio_value: Decimal::new(1_000_000_00, 2), // $1M portfolio max_position_size_pct: 0.05, // 5% max position var_confidence_level: 0.95, // 95% VaR confidence kill_switch_threshold_pct: 0.02, // 2% loss threshold test_symbols: vec!["EURUSD".to_string(), "GBPUSD".to_string(), "USDJPY".to_string()], stress_test_scenarios: vec![ StressTestScenario::MarketCrash { severity: 0.1 }, StressTestScenario::VolatilitySpike { multiplier: 3.0 }, StressTestScenario::LiquidityDrain { reduction: 0.8 }, ], } } } #[derive(Debug, Clone)] pub enum StressTestScenario { MarketCrash { severity: f64 }, VolatilitySpike { multiplier: f64 }, LiquidityDrain { reduction: f64 }, } /// Trading-Risk integration test suite pub struct TradingRiskIntegrationSuite { config: TradingRiskIntegrationConfig, risk_manager: Arc, portfolio_monitor: Arc, var_calculator: Arc, kelly_optimizer: Arc, position_limiter: Arc, kill_switch: Arc, performance_tracker: Arc, test_portfolio: Arc>, } impl TradingRiskIntegrationSuite { /// Create new Trading-Risk integration test suite pub async fn new(config: TradingRiskIntegrationConfig) -> TestResult { // Initialize risk management components let risk_manager = Arc::new( RiskManager::new(RiskConfig { max_portfolio_risk: config.max_position_size_pct, var_confidence: config.var_confidence_level, kill_switch_threshold: config.kill_switch_threshold_pct, max_position_concentration: 0.1, // 10% max single position ..Default::default() }).await .map_err(|e| format!("Failed to create risk manager: {}", e))? ); let portfolio_monitor = Arc::new( PortfolioMonitor::new(config.test_portfolio_value).await .map_err(|e| format!("Failed to create portfolio monitor: {}", e))? ); let var_calculator = Arc::new( VarCalculator::new(config.var_confidence_level).await .map_err(|e| format!("Failed to create VaR calculator: {}", e))? ); let kelly_optimizer = Arc::new( KellyOptimizer::new().await .map_err(|e| format!("Failed to create Kelly optimizer: {}", e))? ); let position_limiter = Arc::new( PositionLimiter::new(config.max_position_size_pct).await .map_err(|e| format!("Failed to create position limiter: {}", e))? ); let kill_switch = Arc::new( KillSwitch::new(config.kill_switch_threshold_pct).await .map_err(|e| format!("Failed to create kill switch: {}", e))? ); let performance_tracker = Arc::new(RiskPerformanceTracker::new()); // Initialize test portfolio let test_portfolio = Arc::new(RwLock::new( TestPortfolio::new(config.test_portfolio_value) )); Ok(Self { config, risk_manager, portfolio_monitor, var_calculator, kelly_optimizer, position_limiter, kill_switch, performance_tracker, test_portfolio, }) } /// Test pre-trade risk checks with position limits and VaR pub async fn test_pretrade_risk_checks(&self) -> TestResult<()> { let mut risk_check_latencies = Vec::new(); let mut approval_rate = 0.0; let mut total_orders = 0; let mut approved_orders = 0; for symbol in &self.config.test_symbols { // Test various order sizes let order_sizes = vec![ Decimal::new(10_000_00, 2), // $10K - should pass Decimal::new(50_000_00, 2), // $50K - might pass Decimal::new(100_000_00, 2), // $100K - risky Decimal::new(500_000_00, 2), // $500K - should reject ]; for order_size in order_sizes { let order = TestOrder { id: format!("TEST_ORDER_{}", Uuid::new_v4()), symbol: symbol.clone(), side: OrderSide::Buy, quantity: order_size / Decimal::new(110_00, 2), // Assume $1.10 price price: Decimal::new(110_00, 2), order_type: OrderType::Market, timestamp: HardwareTimestamp::now(), }; let start_time = HardwareTimestamp::now(); // Pre-trade risk check let risk_result = self.risk_manager .validate_order(&order) .await .map_err(|e| format!("Risk validation failed: {}", e))?; let risk_latency = HardwareTimestamp::now().latency_ns(&start_time); risk_check_latencies.push(risk_latency); // Validate risk check latency assert!( risk_latency < self.config.max_risk_check_latency_us * 1_000, "Risk check latency {}μs exceeds requirement {}μs for order {}", risk_latency / 1_000, self.config.max_risk_check_latency_us, order.id ); total_orders += 1; if risk_result.approved { approved_orders += 1; } // Validate risk assessment structure assert!( risk_result.risk_score >= 0.0 && risk_result.risk_score <= 1.0, "Risk score should be between 0 and 1" ); // Large orders should be rejected if order_size > Decimal::new(200_000_00, 2) { assert!( !risk_result.approved, "Large order of ${} should be rejected", order_size ); } self.performance_tracker .record_risk_check_latency(risk_latency / 1_000) .await; } } approval_rate = approved_orders as f64 / total_orders as f64; let avg_risk_latency = risk_check_latencies.iter().sum::() / risk_check_latencies.len() as u64; // Validate risk system behavior assert!( approval_rate >= 0.3 && approval_rate <= 0.8, "Risk approval rate {:.1}% should be between 30% and 80%", approval_rate * 100.0 ); assert!( avg_risk_latency < self.config.max_risk_check_latency_us * 1_000, "Average risk check latency {}μs exceeds requirement {}μs", avg_risk_latency / 1_000, self.config.max_risk_check_latency_us ); println!("✓ Pre-trade risk checks test passed:"); println!(" Orders tested: {}", total_orders); println!(" Approval rate: {:.1}%", approval_rate * 100.0); println!(" Average risk latency: {}μs", avg_risk_latency / 1_000); Ok(()) } /// Test Kelly sizing calculations for optimal position sizing pub async fn test_kelly_sizing_optimization(&self) -> TestResult<()> { let mut kelly_calculations = Vec::new(); for symbol in &self.config.test_symbols { // Simulate different market conditions for Kelly sizing let market_scenarios = vec![ MarketCondition { win_rate: 0.6, avg_win: 0.02, avg_loss: -0.01 }, // Favorable MarketCondition { win_rate: 0.5, avg_win: 0.015, avg_loss: -0.015 }, // Neutral MarketCondition { win_rate: 0.4, avg_win: 0.03, avg_loss: -0.02 }, // High risk/reward ]; for condition in market_scenarios { let start_time = HardwareTimestamp::now(); // Calculate Kelly optimal position size let kelly_result = self.kelly_optimizer .calculate_optimal_size( symbol, condition.win_rate, condition.avg_win, condition.avg_loss.abs(), self.config.test_portfolio_value, ) .await .map_err(|e| format!("Kelly calculation failed: {}", e))?; let kelly_latency = HardwareTimestamp::now().latency_ns(&start_time); // Validate Kelly calculation latency assert!( kelly_latency < 50_000_000, // 50ms max "Kelly calculation latency {}ms exceeds 50ms limit", kelly_latency / 1_000_000 ); // Validate Kelly sizing logic assert!( kelly_result.optimal_fraction >= 0.0 && kelly_result.optimal_fraction <= 1.0, "Kelly fraction should be between 0 and 1" ); // Favorable conditions should suggest larger positions if condition.win_rate > 0.55 && condition.avg_win > condition.avg_loss.abs() { assert!( kelly_result.optimal_fraction > 0.1, "Favorable conditions should suggest position > 10%" ); } // Poor conditions should suggest smaller positions if condition.win_rate < 0.45 { assert!( kelly_result.optimal_fraction < 0.1, "Poor conditions should suggest position < 10%" ); } kelly_calculations.push(kelly_result); self.performance_tracker .record_kelly_calculation(kelly_latency / 1_000) .await; } } let avg_kelly_fraction = kelly_calculations.iter() .map(|k| k.optimal_fraction) .sum::() / kelly_calculations.len() as f64; assert!( avg_kelly_fraction > 0.0 && avg_kelly_fraction < 0.5, "Average Kelly fraction {:.3} should be reasonable", avg_kelly_fraction ); println!("✓ Kelly sizing optimization test passed:"); println!(" Calculations performed: {}", kelly_calculations.len()); println!(" Average Kelly fraction: {:.3}", avg_kelly_fraction); println!(" Optimal sizing under various market conditions"); Ok(()) } /// Test real-time risk monitoring during active trades pub async fn test_realtime_risk_monitoring(&self) -> TestResult<()> { // Setup initial portfolio positions let mut portfolio = self.test_portfolio.write().await; // Add test positions for symbol in &self.config.test_symbols { portfolio.add_position(Position { symbol: symbol.clone(), quantity: Decimal::new(10_000, 0), average_price: Decimal::new(110_00, 2), current_price: Decimal::new(110_00, 2), unrealized_pnl: Decimal::ZERO, market_value: Decimal::new(1_100_000_00, 2), }); } drop(portfolio); let mut monitoring_cycles = 0; let mut risk_violations = 0; // Simulate 100 monitoring cycles for cycle in 0..100 { let start_time = HardwareTimestamp::now(); // Simulate price changes self.simulate_price_changes().await?; // Update portfolio with new prices self.update_portfolio_prices().await?; // Run risk monitoring cycle let risk_status = self.portfolio_monitor .assess_portfolio_risk() .await .map_err(|e| format!("Portfolio risk assessment failed: {}", e))?; let monitoring_latency = HardwareTimestamp::now().latency_ns(&start_time); // Validate monitoring latency assert!( monitoring_latency < 10_000_000, // 10ms max "Risk monitoring latency {}ms exceeds 10ms limit", monitoring_latency / 1_000_000 ); // Check risk metrics assert!( risk_status.total_var >= Decimal::ZERO, "VaR should be non-negative" ); assert!( risk_status.portfolio_beta.is_finite(), "Portfolio beta should be finite" ); if risk_status.risk_level > RiskLevel::Medium { risk_violations += 1; } monitoring_cycles += 1; self.performance_tracker .record_monitoring_cycle(monitoring_latency / 1_000) .await; // Small delay to simulate real-time monitoring tokio::time::sleep(Duration::from_millis(10)).await; } let violation_rate = risk_violations as f64 / monitoring_cycles as f64; // Validate monitoring system assert!( violation_rate < 0.3, // Less than 30% violations expected "Risk violation rate {:.1}% should be < 30%", violation_rate * 100.0 ); println!("✓ Real-time risk monitoring test passed:"); println!(" Monitoring cycles: {}", monitoring_cycles); println!(" Risk violations: {} ({:.1}%)", risk_violations, violation_rate * 100.0); println!(" Continuous portfolio risk assessment"); Ok(()) } /// Test kill switch activation under stress conditions pub async fn test_kill_switch_activation(&self) -> TestResult<()> { let mut kill_switch_activations = 0; for scenario in &self.config.stress_test_scenarios { println!("Testing kill switch under scenario: {:?}", scenario); // Reset kill switch state self.kill_switch.reset().await?; // Apply stress scenario self.apply_stress_scenario(scenario).await?; // Monitor for kill switch activation let mut monitoring_duration = 0; let max_monitoring_duration = 30; // 30 cycles max while monitoring_duration < max_monitoring_duration { let portfolio_state = self.portfolio_monitor .assess_portfolio_risk() .await?; // Check if kill switch should activate let kill_switch_status = self.kill_switch .evaluate_activation(&portfolio_state) .await?; if kill_switch_status.should_activate { kill_switch_activations += 1; // Validate kill switch response time assert!( kill_switch_status.response_time_ms < 100, "Kill switch response time {}ms should be < 100ms", kill_switch_status.response_time_ms ); // Test emergency position liquidation let liquidation_result = self.kill_switch .execute_emergency_liquidation() .await?; assert!( liquidation_result.positions_liquidated > 0, "Kill switch should liquidate positions" ); assert!( liquidation_result.liquidation_time_ms < 5000, // 5 seconds "Emergency liquidation should complete in < 5 seconds" ); break; } monitoring_duration += 1; tokio::time::sleep(Duration::from_millis(100)).await; } } assert!( kill_switch_activations > 0, "Kill switch should activate during stress scenarios" ); assert!( kill_switch_activations <= self.config.stress_test_scenarios.len(), "Kill switch should not activate multiple times per scenario" ); println!("✓ Kill switch activation test passed:"); println!(" Stress scenarios tested: {}", self.config.stress_test_scenarios.len()); println!(" Kill switch activations: {}", kill_switch_activations); println!(" Emergency procedures validated"); Ok(()) } /// Test VaR calculation accuracy across different market regimes pub async fn test_var_calculation_accuracy(&self) -> TestResult<()> { let market_regimes = vec![ MarketRegime::TrendingUp { strength: 0.8 }, MarketRegime::TrendingDown { strength: 0.8 }, MarketRegime::Sideways { volatility: 0.1 }, MarketRegime::HighVolatility { volatility: 0.3 }, ]; let mut var_calculations = Vec::new(); for regime in market_regimes { // Generate market data for the regime let market_data = self.generate_regime_market_data(®ime).await?; let start_time = HardwareTimestamp::now(); // Calculate VaR for the regime let var_result = self.var_calculator .calculate_portfolio_var(&market_data, self.config.var_confidence_level) .await .map_err(|e| format!("VaR calculation failed: {}", e))?; let var_latency = HardwareTimestamp::now().latency_ns(&start_time); // Validate VaR calculation latency assert!( var_latency < self.config.max_var_calculation_latency_ms * 1_000_000, "VaR calculation latency {}ms exceeds requirement {}ms", var_latency / 1_000_000, self.config.max_var_calculation_latency_ms ); // Validate VaR values assert!( var_result.daily_var > Decimal::ZERO, "Daily VaR should be positive" ); assert!( var_result.marginal_var.len() == self.config.test_symbols.len(), "Marginal VaR should be calculated for all positions" ); // High volatility regimes should produce higher VaR match regime { MarketRegime::HighVolatility { .. } => { assert!( var_result.daily_var > Decimal::new(10_000_00, 2), // $10K minimum "High volatility should produce higher VaR" ); } MarketRegime::Sideways { .. } => { assert!( var_result.daily_var < Decimal::new(50_000_00, 2), // $50K maximum "Low volatility should produce lower VaR" ); } _ => {} // Other regimes have intermediate expectations } var_calculations.push(var_result); self.performance_tracker .record_var_calculation(var_latency / 1_000) .await; } // Validate VaR calculation consistency let var_values: Vec = var_calculations.iter() .map(|v| v.daily_var.to_f64()) .collect(); let var_range = var_values.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap() - var_values.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap(); assert!( var_range > 1000.0, // VaR should vary across regimes "VaR should vary significantly across market regimes, range: ${:.2}", var_range ); println!("✓ VaR calculation accuracy test passed:"); println!(" Market regimes tested: {}", var_calculations.len()); println!(" VaR range across regimes: ${:.2}", var_range); println!(" Accurate risk measurement across conditions"); Ok(()) } /// Test portfolio rebalancing triggers and execution pub async fn test_portfolio_rebalancing(&self) -> TestResult<()> { // Setup imbalanced portfolio let mut portfolio = self.test_portfolio.write().await; portfolio.clear_positions(); // Create concentration in one position (should trigger rebalancing) portfolio.add_position(Position { symbol: "EURUSD".to_string(), quantity: Decimal::new(80_000, 0), average_price: Decimal::new(110_00, 2), current_price: Decimal::new(115_00, 2), // 5% gain unrealized_pnl: Decimal::new(40_000_00, 2), market_value: Decimal::new(920_000_00, 2), // 92% of portfolio }); // Small positions in other symbols portfolio.add_position(Position { symbol: "GBPUSD".to_string(), quantity: Decimal::new(3_000, 0), average_price: Decimal::new(130_00, 2), current_price: Decimal::new(128_00, 2), unrealized_pnl: Decimal::new(-6_000_00, 2), market_value: Decimal::new(38_400_00, 2), // 3.8% of portfolio }); drop(portfolio); let start_time = HardwareTimestamp::now(); // Check if rebalancing is needed let rebalance_analysis = self.portfolio_monitor .analyze_rebalancing_needs() .await?; let rebalance_latency = HardwareTimestamp::now().latency_ns(&start_time); // Validate rebalancing analysis assert!( rebalance_analysis.needs_rebalancing, "Concentrated portfolio should need rebalancing" ); assert!( rebalance_analysis.concentration_risk > 0.8, "Concentration risk should be high (>80%)" ); assert!( !rebalance_analysis.recommended_trades.is_empty(), "Rebalancing should recommend trades" ); // Execute rebalancing trades let rebalance_execution = self.portfolio_monitor .execute_rebalancing(&rebalance_analysis.recommended_trades) .await?; // Validate rebalancing execution assert!( rebalance_execution.trades_executed > 0, "Rebalancing should execute trades" ); assert!( rebalance_execution.execution_time_ms < 5000, // 5 seconds "Rebalancing should complete quickly" ); // Verify portfolio is more balanced after rebalancing let final_analysis = self.portfolio_monitor .analyze_rebalancing_needs() .await?; assert!( final_analysis.concentration_risk < 0.6, "Concentration risk should be reduced after rebalancing" ); println!("✓ Portfolio rebalancing test passed:"); println!(" Initial concentration risk: {:.1}%", rebalance_analysis.concentration_risk * 100.0); println!(" Final concentration risk: {:.1}%", final_analysis.concentration_risk * 100.0); println!(" Trades executed: {}", rebalance_execution.trades_executed); println!(" Rebalancing latency: {}ms", rebalance_latency / 1_000_000); Ok(()) } /// Helper methods for test implementation async fn simulate_price_changes(&self) -> TestResult<()> { // Simulate realistic price movements let price_changes = vec![ ("EURUSD", 0.001), // +0.1 pip ("GBPUSD", -0.002), // -0.2 pip ("USDJPY", 0.0005), // +0.05 pip ]; for (symbol, change) in price_changes { // This would update market data in a real system // For testing, we just simulate the change } Ok(()) } async fn update_portfolio_prices(&self) -> TestResult<()> { let mut portfolio = self.test_portfolio.write().await; // Update prices based on simulated changes for position in &mut portfolio.positions { let price_change = (rand::random::() - 0.5) * 0.002; // ±0.1% random change let new_price = position.current_price * (Decimal::ONE + Decimal::try_from(price_change).unwrap()); position.current_price = new_price; position.market_value = position.quantity * new_price; position.unrealized_pnl = position.market_value - (position.quantity * position.average_price); } Ok(()) } async fn apply_stress_scenario(&self, scenario: &StressTestScenario) -> TestResult<()> { let mut portfolio = self.test_portfolio.write().await; match scenario { StressTestScenario::MarketCrash { severity } => { // Apply severe price drops for position in &mut portfolio.positions { position.current_price = position.current_price * (Decimal::ONE - Decimal::try_from(*severity).unwrap()); position.market_value = position.quantity * position.current_price; position.unrealized_pnl = position.market_value - (position.quantity * position.average_price); } } StressTestScenario::VolatilitySpike { multiplier } => { // Increase price volatility for position in &mut portfolio.positions { let volatility = (rand::random::() - 0.5) * 0.1 * multiplier; // Enhanced volatility position.current_price = position.current_price * (Decimal::ONE + Decimal::try_from(volatility).unwrap()); position.market_value = position.quantity * position.current_price; position.unrealized_pnl = position.market_value - (position.quantity * position.average_price); } } StressTestScenario::LiquidityDrain { reduction: _ } => { // Simulate liquidity issues (for testing, just stress prices) for position in &mut portfolio.positions { position.current_price = position.current_price * Decimal::new(95, 2); // 5% haircut position.market_value = position.quantity * position.current_price; position.unrealized_pnl = position.market_value - (position.quantity * position.average_price); } } } Ok(()) } async fn generate_regime_market_data(&self, regime: &MarketRegime) -> TestResult> { let mut data_points = Vec::new(); let base_price = 1.1000; for i in 0..252 { // One year of daily data let price = match regime { MarketRegime::TrendingUp { strength } => { base_price + (i as f64 * 0.0001 * strength) } MarketRegime::TrendingDown { strength } => { base_price - (i as f64 * 0.0001 * strength) } MarketRegime::Sideways { volatility } => { base_price + (i as f64 / 50.0).sin() * volatility } MarketRegime::HighVolatility { volatility } => { base_price + (rand::random::() - 0.5) * volatility * 2.0 } }; data_points.push(MarketDataPoint { symbol: "EURUSD".to_string(), timestamp: HardwareTimestamp::now(), price: Decimal::try_from(price).unwrap(), volume: 1000000, volatility: match regime { MarketRegime::HighVolatility { volatility } => *volatility, MarketRegime::Sideways { volatility } => *volatility, _ => 0.1, }, }); } Ok(data_points) } /// Get comprehensive performance statistics pub async fn get_performance_stats(&self) -> RiskPerformanceStats { self.performance_tracker.get_stats().await } } // ============================================================================= // MOCK TYPES AND IMPLEMENTATIONS // ============================================================================= #[derive(Debug, Clone)] pub struct TestOrder { pub id: String, pub symbol: String, pub side: OrderSide, pub quantity: Decimal, pub price: Decimal, pub order_type: OrderType, pub timestamp: HardwareTimestamp, } #[derive(Debug, Clone)] pub struct TestPortfolio { pub total_value: Decimal, pub positions: Vec, } impl TestPortfolio { pub fn new(initial_value: Decimal) -> Self { Self { total_value: initial_value, positions: Vec::new(), } } pub fn add_position(&mut self, position: Position) { self.positions.push(position); } pub fn clear_positions(&mut self) { self.positions.clear(); } } #[derive(Debug, Clone)] pub struct Position { pub symbol: String, pub quantity: Decimal, pub average_price: Decimal, pub current_price: Decimal, pub unrealized_pnl: Decimal, pub market_value: Decimal, } #[derive(Debug, Clone)] pub struct MarketCondition { pub win_rate: f64, pub avg_win: f64, pub avg_loss: f64, } #[derive(Debug, Clone)] pub enum MarketRegime { TrendingUp { strength: f64 }, TrendingDown { strength: f64 }, Sideways { volatility: f64 }, HighVolatility { volatility: f64 }, } #[derive(Debug, Clone)] pub struct MarketDataPoint { pub symbol: String, pub timestamp: HardwareTimestamp, pub price: Decimal, pub volume: u64, pub volatility: f64, } #[derive(Debug, Clone)] pub enum RiskLevel { Low, Medium, High, Critical, } // Performance tracking #[derive(Debug)] pub struct RiskPerformanceTracker { risk_check_latencies: RwLock>, kelly_calculation_latencies: RwLock>, monitoring_latencies: RwLock>, var_calculation_latencies: RwLock>, } impl RiskPerformanceTracker { pub fn new() -> Self { Self { risk_check_latencies: RwLock::new(Vec::new()), kelly_calculation_latencies: RwLock::new(Vec::new()), monitoring_latencies: RwLock::new(Vec::new()), var_calculation_latencies: RwLock::new(Vec::new()), } } pub async fn record_risk_check_latency(&self, latency_us: u64) { self.risk_check_latencies.write().await.push(latency_us); } pub async fn record_kelly_calculation(&self, latency_us: u64) { self.kelly_calculation_latencies.write().await.push(latency_us); } pub async fn record_monitoring_cycle(&self, latency_us: u64) { self.monitoring_latencies.write().await.push(latency_us); } pub async fn record_var_calculation(&self, latency_us: u64) { self.var_calculation_latencies.write().await.push(latency_us); } pub async fn get_stats(&self) -> RiskPerformanceStats { let risk_lats = self.risk_check_latencies.read().await; let kelly_lats = self.kelly_calculation_latencies.read().await; let monitor_lats = self.monitoring_latencies.read().await; let var_lats = self.var_calculation_latencies.read().await; RiskPerformanceStats { avg_risk_check_latency_us: if !risk_lats.is_empty() { risk_lats.iter().sum::() / risk_lats.len() as u64 } else { 0 }, avg_kelly_calculation_latency_us: if !kelly_lats.is_empty() { kelly_lats.iter().sum::() / kelly_lats.len() as u64 } else { 0 }, avg_monitoring_latency_us: if !monitor_lats.is_empty() { monitor_lats.iter().sum::() / monitor_lats.len() as u64 } else { 0 }, avg_var_calculation_latency_us: if !var_lats.is_empty() { var_lats.iter().sum::() / var_lats.len() as u64 } else { 0 }, total_risk_checks: risk_lats.len(), total_kelly_calculations: kelly_lats.len(), total_monitoring_cycles: monitor_lats.len(), total_var_calculations: var_lats.len(), } } } #[derive(Debug, Clone)] pub struct RiskPerformanceStats { pub avg_risk_check_latency_us: u64, pub avg_kelly_calculation_latency_us: u64, pub avg_monitoring_latency_us: u64, pub avg_var_calculation_latency_us: u64, pub total_risk_checks: usize, pub total_kelly_calculations: usize, pub total_monitoring_cycles: usize, pub total_var_calculations: usize, } // Mock implementations for risk components pub struct RiskManager; pub struct PortfolioMonitor; pub struct VarCalculator; pub struct KellyOptimizer; pub struct PositionLimiter; pub struct KillSwitch; #[derive(Debug, Clone)] pub struct RiskConfig { pub max_portfolio_risk: f64, pub var_confidence: f64, pub kill_switch_threshold: f64, pub max_position_concentration: f64, } impl Default for RiskConfig { fn default() -> Self { Self { max_portfolio_risk: 0.05, var_confidence: 0.95, kill_switch_threshold: 0.02, max_position_concentration: 0.1, } } } #[derive(Debug, Clone)] pub struct RiskAssessment { pub approved: bool, pub risk_score: f64, pub reason: String, } #[derive(Debug, Clone)] pub struct KellyResult { pub optimal_fraction: f64, pub expected_return: f64, pub risk_metrics: HashMap, } #[derive(Debug, Clone)] pub struct PortfolioRiskStatus { pub total_var: Decimal, pub portfolio_beta: f64, pub risk_level: RiskLevel, pub concentration_metrics: HashMap, } #[derive(Debug, Clone)] pub struct KillSwitchStatus { pub should_activate: bool, pub response_time_ms: u64, pub trigger_reason: String, } #[derive(Debug, Clone)] pub struct LiquidationResult { pub positions_liquidated: usize, pub liquidation_time_ms: u64, pub total_value_liquidated: Decimal, } #[derive(Debug, Clone)] pub struct VarResult { pub daily_var: Decimal, pub marginal_var: HashMap, pub component_var: HashMap, } #[derive(Debug, Clone)] pub struct RebalanceAnalysis { pub needs_rebalancing: bool, pub concentration_risk: f64, pub recommended_trades: Vec, } #[derive(Debug, Clone)] pub struct RebalanceTrade { pub symbol: String, pub action: String, pub quantity: Decimal, } #[derive(Debug, Clone)] pub struct RebalanceExecution { pub trades_executed: usize, pub execution_time_ms: u64, pub total_volume: Decimal, } // Mock implementations impl RiskManager { pub async fn new(_config: RiskConfig) -> Result { Ok(Self) } pub async fn validate_order(&self, _order: &TestOrder) -> Result { tokio::time::sleep(Duration::from_micros(2000)).await; // 2ms simulation Ok(RiskAssessment { approved: true, risk_score: 0.3, reason: "Order approved by risk management".to_string(), }) } } impl PortfolioMonitor { pub async fn new(_initial_value: Decimal) -> Result { Ok(Self) } pub async fn assess_portfolio_risk(&self) -> Result { tokio::time::sleep(Duration::from_micros(5000)).await; // 5ms simulation Ok(PortfolioRiskStatus { total_var: Decimal::new(25000_00, 2), portfolio_beta: 1.2, risk_level: RiskLevel::Medium, concentration_metrics: HashMap::new(), }) } pub async fn analyze_rebalancing_needs(&self) -> Result { Ok(RebalanceAnalysis { needs_rebalancing: true, concentration_risk: 0.9, recommended_trades: vec![ RebalanceTrade { symbol: "EURUSD".to_string(), action: "SELL".to_string(), quantity: Decimal::new(30000, 0), }, ], }) } pub async fn execute_rebalancing(&self, _trades: &[RebalanceTrade]) -> Result { tokio::time::sleep(Duration::from_millis(100)).await; // 100ms simulation Ok(RebalanceExecution { trades_executed: 1, execution_time_ms: 100, total_volume: Decimal::new(30000, 0), }) } } impl VarCalculator { pub async fn new(_confidence: f64) -> Result { Ok(Self) } pub async fn calculate_portfolio_var(&self, _data: &[MarketDataPoint], _confidence: f64) -> Result { tokio::time::sleep(Duration::from_millis(50)).await; // 50ms simulation let mut marginal_var = HashMap::new(); marginal_var.insert("EURUSD".to_string(), Decimal::new(15000_00, 2)); marginal_var.insert("GBPUSD".to_string(), Decimal::new(8000_00, 2)); marginal_var.insert("USDJPY".to_string(), Decimal::new(12000_00, 2)); Ok(VarResult { daily_var: Decimal::new(25000_00, 2), marginal_var, component_var: HashMap::new(), }) } } impl KellyOptimizer { pub async fn new() -> Result { Ok(Self) } pub async fn calculate_optimal_size( &self, _symbol: &str, win_rate: f64, avg_win: f64, avg_loss: f64, _portfolio_value: Decimal, ) -> Result { tokio::time::sleep(Duration::from_millis(10)).await; // 10ms simulation // Kelly formula: f = (bp - q) / b // where b = odds, p = win rate, q = loss rate let odds = avg_win / avg_loss; let kelly_fraction = (odds * win_rate - (1.0 - win_rate)) / odds; let optimal_fraction = kelly_fraction.max(0.0).min(0.25); // Cap at 25% Ok(KellyResult { optimal_fraction, expected_return: win_rate * avg_win - (1.0 - win_rate) * avg_loss, risk_metrics: HashMap::new(), }) } } impl PositionLimiter { pub async fn new(_max_size: f64) -> Result { Ok(Self) } } impl KillSwitch { pub async fn new(_threshold: f64) -> Result { Ok(Self) } pub async fn reset(&self) -> Result<(), String> { Ok(()) } pub async fn evaluate_activation(&self, portfolio_state: &PortfolioRiskStatus) -> Result { let should_activate = matches!(portfolio_state.risk_level, RiskLevel::Critical); Ok(KillSwitchStatus { should_activate, response_time_ms: 50, trigger_reason: if should_activate { "Critical risk level detected".to_string() } else { "Normal operation".to_string() }, }) } pub async fn execute_emergency_liquidation(&self) -> Result { tokio::time::sleep(Duration::from_millis(1000)).await; // 1s simulation Ok(LiquidationResult { positions_liquidated: 3, liquidation_time_ms: 1000, total_value_liquidated: Decimal::new(950000_00, 2), }) } } // ============================================================================= // INTEGRATION TESTS // ============================================================================= #[tokio::test] async fn test_trading_risk_pretrade_checks() -> TestResult<()> { let config = TradingRiskIntegrationConfig::default(); let suite = TradingRiskIntegrationSuite::new(config).await?; suite.test_pretrade_risk_checks().await?; Ok(()) } #[tokio::test] async fn test_trading_risk_kelly_sizing() -> TestResult<()> { let config = TradingRiskIntegrationConfig::default(); let suite = TradingRiskIntegrationSuite::new(config).await?; suite.test_kelly_sizing_optimization().await?; Ok(()) } #[tokio::test] async fn test_trading_risk_monitoring() -> TestResult<()> { let config = TradingRiskIntegrationConfig::default(); let suite = TradingRiskIntegrationSuite::new(config).await?; suite.test_realtime_risk_monitoring().await?; Ok(()) } #[tokio::test] async fn test_trading_risk_kill_switch() -> TestResult<()> { let config = TradingRiskIntegrationConfig::default(); let suite = TradingRiskIntegrationSuite::new(config).await?; suite.test_kill_switch_activation().await?; Ok(()) } #[tokio::test] async fn test_trading_risk_var_calculation() -> TestResult<()> { let config = TradingRiskIntegrationConfig::default(); let suite = TradingRiskIntegrationSuite::new(config).await?; suite.test_var_calculation_accuracy().await?; Ok(()) } #[tokio::test] async fn test_trading_risk_rebalancing() -> TestResult<()> { let config = TradingRiskIntegrationConfig::default(); let suite = TradingRiskIntegrationSuite::new(config).await?; suite.test_portfolio_rebalancing().await?; Ok(()) } /// Comprehensive Trading-Risk integration test runner #[tokio::test] async fn run_comprehensive_trading_risk_integration_tests() -> TestResult<()> { println!("=== TRADING ↔ RISK MANAGEMENT INTEGRATION TEST SUITE ==="); let config = TradingRiskIntegrationConfig::default(); let suite = TradingRiskIntegrationSuite::new(config).await?; let test_timeout = Duration::from_secs(180); // 3 minutes per test // Run all risk integration tests with timeout protection timeout(test_timeout, suite.test_pretrade_risk_checks()).await??; timeout(test_timeout, suite.test_kelly_sizing_optimization()).await??; timeout(test_timeout, suite.test_realtime_risk_monitoring()).await??; timeout(test_timeout, suite.test_kill_switch_activation()).await??; timeout(test_timeout, suite.test_var_calculation_accuracy()).await??; timeout(test_timeout, suite.test_portfolio_rebalancing()).await??; // Display final performance statistics let stats = suite.get_performance_stats().await; println!("=== TRADING ↔ RISK MANAGEMENT TEST RESULTS ==="); println!("✓ Pre-trade risk checks (position limits, VaR)"); println!("✓ Kelly sizing calculations for position sizing"); println!("✓ Real-time risk monitoring during trades"); println!("✓ Kill switch activation under stress"); println!("✓ VaR calculation accuracy across market regimes"); println!("✓ Portfolio rebalancing triggers and execution"); println!(""); println!("Performance Summary:"); println!(" Average Risk Check Latency: {}μs ({})", stats.avg_risk_check_latency_us, stats.total_risk_checks); println!(" Average Kelly Calculation: {}μs ({})", stats.avg_kelly_calculation_latency_us, stats.total_kelly_calculations); println!(" Average Monitoring Cycle: {}μs ({})", stats.avg_monitoring_latency_us, stats.total_monitoring_cycles); println!(" Average VaR Calculation: {}μs ({})", stats.avg_var_calculation_latency_us, stats.total_var_calculations); println!(""); println!("✓ ALL TRADING ↔ RISK MANAGEMENT INTEGRATION TESTS PASSED"); Ok(()) }