//! Trading Algorithm Correctness Test Suite //! TARGET: 95% coverage for trading algorithm components //! //! Tests all critical trading algorithm functionality including: //! - TWAP/VWAP execution accuracy and timing //! - Iceberg order stealth validation //! - Strategy orchestrator conflict resolution //! - Execution algorithm performance //! - Order slicing and market impact use proptest::prelude::*; use std::sync::Arc; use std::time::{Duration, Instant, SystemTime}; use std::collections::VecDeque; #[cfg(test)] mod twap_vwap_tests { use super::*; /// Test `TWAP` execution accuracy #[test] fn test_twap_execution_accuracy() { let mut twap_algo = create_test_twap_algorithm(); let total_quantity = 100_000; let execution_period = Duration::from_secs(300); // 5 minutes let expected_slice_size = total_quantity / 20; // 20 slices twap_algo.initialize_order(TwapOrder { symbol: "EURUSD".to_string(), total_quantity, side: OrderSide::Buy, execution_period, start_time: SystemTime::now(), max_participation_rate: 0.20, // 20% max market participation }); let mut executed_slices = Vec::new(); let mut total_executed = 0; let start = Instant::now(); // Simulate execution over time while total_executed < total_quantity && start.elapsed() < execution_period { if let Some(slice) = twap_algo.get_next_slice() { executed_slices.push(slice.clone()); total_executed += slice.quantity; // Verify slice timing accuracy let expected_interval = execution_period / 20; let actual_interval = slice.execution_time.duration_since( executed_slices.first().unwrap().execution_time ).unwrap_or(Duration::ZERO); let timing_error = if actual_interval > expected_interval { actual_interval - expected_interval } else { expected_interval - actual_interval }; // Timing should be accurate within 100ms assert!(timing_error < Duration::from_millis(100), "TWAP timing error {} exceeds 100ms threshold", timing_error.as_millis()); // Slice size should be approximately equal let size_deviation = (slice.quantity as f64 - expected_slice_size as f64).abs() / expected_slice_size as f64; assert!(size_deviation < 0.1, "TWAP slice size deviation {:.2}% exceeds 10% threshold", size_deviation * 100.0); } std::thread::sleep(Duration::from_millis(50)); // Simulate time passage } // Verify total execution assert_eq!(total_executed, total_quantity, "TWAP should execute exact quantity"); // Verify execution distribution let execution_times: Vec = executed_slices.iter() .map(|slice| slice.execution_time.duration_since(SystemTime::UNIX_EPOCH).unwrap()) .collect(); // Check for even distribution for i in 1..execution_times.len() { let interval = execution_times[i] - execution_times[i-1]; let expected = execution_period / executed_slices.len() as u32; let deviation = if interval > expected { interval - expected } else { expected - interval }; assert!(deviation < Duration::from_millis(200), "TWAP execution intervals should be evenly distributed"); } } /// Test VWAP execution with `volume` profile matching #[test] fn test_vwap_execution_accuracy() { let mut vwap_algo = create_test_vwap_algorithm(); let historical_volume_profile = create_test_volume_profile(); vwap_algo.initialize_order(VwapOrder { symbol: "GBPUSD".to_string(), total_quantity: 50_000, side: OrderSide::Sell, execution_period: Duration::from_secs(600), // 10 minutes volume_profile: historical_volume_profile.clone(), max_participation_rate: 0.15, }); let mut executed_volume_by_period = Vec::new(); let mut total_executed = 0; for period in 0..10 { if let Some(slice) = vwap_algo.get_next_slice() { executed_volume_by_period.push(slice.quantity); total_executed += slice.quantity; // Verify volume profile matching let expected_proportion = historical_volume_profile[period] / historical_volume_profile.iter().sum::(); let actual_proportion = slice.quantity as f64 / 50_000.0; let profile_deviation = (actual_proportion - expected_proportion).abs(); assert!(profile_deviation < 0.05, "VWAP volume profile deviation {:.3} exceeds 5% threshold at period {}", profile_deviation, period); // Verify market participation limits let market_volume = get_market_volume_for_period(period); let participation_rate = slice.quantity as f64 / market_volume; assert!(participation_rate <= 0.16, // Allow small buffer over 15% "VWAP participation rate {:.2}% exceeds 15% limit", participation_rate * 100.0); } } assert_eq!(total_executed, 50_000, "VWAP should execute exact quantity"); } /// Test iceberg order stealth validation #[test] fn test_iceberg_order_stealth() { let mut iceberg_algo = create_test_iceberg_algorithm(); iceberg_algo.initialize_order(IcebergOrder { symbol: "USDJPY".to_string(), total_quantity: 1_000_000, side: OrderSide::Buy, displayed_quantity: 10_000, // Only show 1% of total price: Some(150.25), randomization_factor: 0.1, // 10% randomization }); let mut visible_quantities = Vec::new(); let mut total_displayed = 0; // Track displayed quantities over time for _ in 0..100 { if let Some(display_slice) = iceberg_algo.get_current_display() { visible_quantities.push(display_slice.displayed_quantity); total_displayed += display_slice.displayed_quantity; // Verify displayed quantity is always within bounds assert!(display_slice.displayed_quantity <= 12_000, // Allow for randomization "Iceberg displayed quantity {} exceeds randomized upper bound", display_slice.displayed_quantity); assert!(display_slice.displayed_quantity >= 8_000, // Allow for randomization "Iceberg displayed quantity {} below randomized lower bound", display_slice.displayed_quantity); // Verify stealth - no pattern should be detectable if visible_quantities.len() >= 10 { let recent_avg = visible_quantities[visible_quantities.len()-10..].iter().sum::() as f64 / 10.0; let overall_avg = visible_quantities.iter().sum::() as f64 / visible_quantities.len() as f64; // Randomization should prevent pattern detection let avg_deviation = (recent_avg - overall_avg).abs() / overall_avg; assert!(avg_deviation < 0.3, "Iceberg showing detectable pattern: deviation {:.2}%", avg_deviation * 100.0); } } // Simulate partial fills iceberg_algo.report_fill(1000); std::thread::sleep(Duration::from_millis(10)); } // Verify stealth characteristics let quantities_variance = calculate_variance(&visible_quantities); assert!(quantities_variance > 500_000.0, "Iceberg should show sufficient randomization variance"); } /// Property-based test for order slicing algorithms proptest! { #[test] fn test_order_slicing_properties( total_quantity in 1_000u32..1_000_000u32, num_slices in 5usize..50usize, randomization in 0.0f64..0.3f64 ) { let slicer = create_test_order_slicer(); let slices = slicer.slice_order(SlicingRequest { total_quantity, num_slices, randomization_factor: randomization, min_slice_size: 100, max_slice_size: total_quantity / 2, }); // Verify slice count prop_assert_eq!(slices.len(), num_slices, "Should generate exact number of slices"); // Verify total quantity conservation let total_sliced: u32 = slices.iter().sum(); prop_assert_eq!(total_sliced, total_quantity, "Total sliced quantity must equal original"); // Verify slice size bounds for slice in &slices { prop_assert!(*slice >= 100, "Slice size must meet minimum"); prop_assert!(*slice <= total_quantity / 2, "Slice size must not exceed maximum"); } // Verify randomization effect if randomization > 0.0 { let expected_size = total_quantity / num_slices as u32; let variance = calculate_variance(&slices); let expected_variance = (expected_size as f64 * randomization).powi(2); prop_assert!(variance >= expected_variance * 0.5, "Randomization should create sufficient variance"); } } } /// Test strategy orchestrator conflict resolution #[test] fn test_strategy_orchestrator_conflicts() { let mut orchestrator = create_test_strategy_orchestrator(); // Set up conflicting strategies orchestrator.add_strategy("momentum", Box::new(MomentumStrategy::new())); orchestrator.add_strategy("mean_reversion", Box::new(MeanReversionStrategy::new())); orchestrator.add_strategy("arbitrage", Box::new(ArbitrageStrategy::new())); // Create conflicting signals let market_state = create_conflicting_market_state(); let signals = orchestrator.generate_signals(&market_state); // Should detect conflicts let conflicts = orchestrator.detect_conflicts(&signals); assert!(!conflicts.is_empty(), "Should detect conflicts between momentum and mean reversion"); // Test conflict resolution let resolved_signals = orchestrator.resolve_conflicts(signals, &conflicts); // Verify resolution quality assert!(resolved_signals.len() <= signals.len(), "Resolution should reduce or maintain signal count"); // Check for opposing signals elimination let buy_signals = resolved_signals.iter().filter(|s| s.direction == SignalDirection::Buy).count(); let sell_signals = resolved_signals.iter().filter(|s| s.direction == SignalDirection::Sell).count(); // Shouldn't have strong opposing signals for same symbol if buy_signals > 0 && sell_signals > 0 { let net_signal_strength = resolved_signals.iter() .map(|s| match s.direction { SignalDirection::Buy => s.strength, SignalDirection::Sell => -s.strength, }) .sum::() .abs(); assert!(net_signal_strength > 0.1, "Net signal should have clear direction after resolution"); } // Test priority-based resolution orchestrator.set_strategy_priority("arbitrage", 10); // Highest priority orchestrator.set_strategy_priority("momentum", 5); orchestrator.set_strategy_priority("mean_reversion", 3); let priority_resolved = orchestrator.resolve_conflicts_by_priority(signals); // Arbitrage signals should be preserved let arb_signals = priority_resolved.iter().filter(|s| s.strategy == "arbitrage").count(); let original_arb = signals.iter().filter(|s| s.strategy == "arbitrage").count(); assert_eq!(arb_signals, original_arb, "High priority arbitrage signals should be preserved"); } /// Test execution algorithm performance metrics #[test] fn test_execution_algorithm_performance() { let mut execution_engine = create_test_execution_engine(); // Test large order execution let large_order = ExecutionOrder { symbol: "EURUSD".to_string(), quantity: 500_000, side: OrderSide::Buy, algorithm: ExecutionAlgorithm::SmartOrder, urgency: ExecutionUrgency::Medium, max_participation: 0.25, price_limit: Some(1.1050), }; let start_time = Instant::now(); let execution_result = execution_engine.execute_order(large_order); let execution_duration = start_time.elapsed(); // Verify execution quality assert!(execution_result.is_ok(), "Execution should succeed: {:?}", execution_result.err()); let result = execution_result.unwrap(); assert_eq!(result.total_executed, 500_000, "Should execute full quantity"); // Check execution cost (slippage + impact) let execution_cost = result.average_price - result.arrival_price; let cost_basis_points = (execution_cost / result.arrival_price * 10_000.0).abs(); assert!(cost_basis_points < 2.0, "Execution cost {:.1} bps exceeds 2 bps threshold", cost_basis_points); // Verify market impact minimization assert!(result.market_impact < 0.5, "Market impact {:.1} bps exceeds 0.5 bps threshold", result.market_impact); // Check execution time efficiency assert!(execution_duration < Duration::from_secs(60), "Execution time {:?} exceeds 60 second threshold", execution_duration); // Verify participation rate compliance assert!(result.max_participation_achieved <= 0.26, // Small buffer "Maximum participation rate {:.1}% exceeded limit", result.max_participation_achieved * 100.0); } /// Test algorithm adaptability to market conditions #[test] fn test_algorithm_market_adaptability() { let mut adaptive_algo = create_test_adaptive_algorithm(); // Test in different market conditions let market_conditions = vec![ MarketCondition::HighVolatility, MarketCondition::LowLiquidity, MarketCondition::TrendingMarket, MarketCondition::RangeMarket, ]; for condition in market_conditions { adaptive_algo.set_market_condition(condition.clone()); let execution_params = adaptive_algo.get_execution_parameters(); match condition { MarketCondition::HighVolatility => { assert!(execution_params.slice_size_factor < 1.0, "Should use smaller slices in high volatility"); assert!(execution_params.delay_between_slices > Duration::from_millis(500), "Should increase delays in high volatility"); } MarketCondition::LowLiquidity => { assert!(execution_params.max_participation_rate < 0.15, "Should reduce participation in low liquidity"); assert!(execution_params.patience_factor > 1.2, "Should be more patient in low liquidity"); } MarketCondition::TrendingMarket => { assert!(execution_params.urgency_multiplier > 1.0, "Should increase urgency in trending markets"); } MarketCondition::RangeMarket => { assert!(execution_params.opportunistic_factor > 1.0, "Should be more opportunistic in range markets"); } } } } // Helper functions and test data structures fn create_test_twap_algorithm() -> TestTwapAlgorithm { TestTwapAlgorithm::new() } fn create_test_vwap_algorithm() -> TestVwapAlgorithm { TestVwapAlgorithm::new() } fn create_test_iceberg_algorithm() -> TestIcebergAlgorithm { TestIcebergAlgorithm::new() } fn create_test_order_slicer() -> TestOrderSlicer { TestOrderSlicer::new() } fn create_test_strategy_orchestrator() -> TestStrategyOrchestrator { TestStrategyOrchestrator::new() } fn create_test_execution_engine() -> TestExecutionEngine { TestExecutionEngine::new() } fn create_test_adaptive_algorithm() -> TestAdaptiveAlgorithm { TestAdaptiveAlgorithm::new() } fn create_test_volume_profile() -> Vec { vec![0.05, 0.08, 0.12, 0.15, 0.18, 0.15, 0.12, 0.08, 0.05, 0.02] // 10 periods } fn create_conflicting_market_state() -> MarketState { MarketState { price: 1.1025, volume: 50000.0, volatility: 0.015, momentum_signal: 0.7, // Strong buy signal mean_reversion_signal: -0.6, // Strong sell signal arbitrage_opportunities: vec!["EUR/USD vs EUR/GBP + GBP/USD".to_string()], } } fn get_market_volume_for_period(_period: usize) -> f64 { 75000.0 // Simulated market volume } fn calculate_variance(values: &[u32]) -> f64 { let mean = values.iter().sum::() as f64 / values.len() as f64; let variance = values.iter() .map(|&x| (x as f64 - mean).powi(2)) .sum::() / values.len() as f64; variance } } // Test data structures and implementations #[derive(Debug)] struct TwapOrder { symbol: String, total_quantity: u32, side: OrderSide, execution_period: Duration, start_time: SystemTime, max_participation_rate: f64, } #[derive(Debug)] struct VwapOrder { symbol: String, total_quantity: u32, side: OrderSide, execution_period: Duration, volume_profile: Vec, max_participation_rate: f64, } #[derive(Debug)] struct IcebergOrder { symbol: String, total_quantity: u32, side: OrderSide, displayed_quantity: u32, price: Option, randomization_factor: f64, } #[derive(Debug, Clone)] // OrderSide now imported from canonical source use common::OrderSide; #[derive(Debug)] struct OrderSlice { quantity: u32, execution_time: SystemTime, price_limit: Option, } #[derive(Debug)] struct DisplaySlice { displayed_quantity: u32, hidden_quantity: u32, } #[derive(Debug)] struct SlicingRequest { total_quantity: u32, num_slices: usize, randomization_factor: f64, min_slice_size: u32, max_slice_size: u32, } #[derive(Debug)] struct MarketState { price: f64, volume: f64, volatility: f64, momentum_signal: f64, mean_reversion_signal: f64, arbitrage_opportunities: Vec, } #[derive(Debug)] struct TradingSignal { strategy: String, symbol: String, direction: SignalDirection, strength: f64, confidence: f64, } #[derive(Debug, Clone)] enum SignalDirection { Buy, Sell, Hold, } #[derive(Debug)] struct ExecutionOrder { symbol: String, quantity: u32, side: OrderSide, algorithm: ExecutionAlgorithm, urgency: ExecutionUrgency, max_participation: f64, price_limit: Option, } #[derive(Debug)] enum ExecutionAlgorithm { SmartOrder, Twap, Vwap, Iceberg, } #[derive(Debug)] enum ExecutionUrgency { Low, Medium, High, } #[derive(Debug)] struct ExecutionResult { total_executed: u32, average_price: f64, arrival_price: f64, market_impact: f64, max_participation_achieved: f64, execution_cost: f64, } #[derive(Debug, Clone)] enum MarketCondition { HighVolatility, LowLiquidity, TrendingMarket, RangeMarket, } #[derive(Debug)] struct ExecutionParameters { slice_size_factor: f64, delay_between_slices: Duration, max_participation_rate: f64, patience_factor: f64, urgency_multiplier: f64, opportunistic_factor: f64, } // Test implementations #[derive(Debug)] struct TestTwapAlgorithm { current_order: Option, slices_executed: u32, } impl TestTwapAlgorithm { fn new() -> Self { Self { current_order: None, slices_executed: 0, } } fn initialize_order(&mut self, order: TwapOrder) { self.current_order = Some(order); self.slices_executed = 0; } fn get_next_slice(&mut self) -> Option { if let Some(ref order) = self.current_order { if self.slices_executed < 20 { self.slices_executed += 1; let slice_size = order.total_quantity / 20; let execution_time = order.start_time + Duration::from_secs(15 * self.slices_executed as u64); // 15 second intervals Some(OrderSlice { quantity: slice_size, execution_time, price_limit: None, }) } else { None } } else { None } } } #[derive(Debug)] struct TestVwapAlgorithm { current_order: Option, period_executed: usize, } impl TestVwapAlgorithm { fn new() -> Self { Self { current_order: None, period_executed: 0, } } fn initialize_order(&mut self, order: VwapOrder) { self.current_order = Some(order); self.period_executed = 0; } fn get_next_slice(&mut self) -> Option { if let Some(ref order) = self.current_order { if self.period_executed < order.volume_profile.len() { let volume_proportion = order.volume_profile[self.period_executed]; let slice_quantity = (order.total_quantity as f64 * volume_proportion) as u32; self.period_executed += 1; Some(OrderSlice { quantity: slice_quantity, execution_time: SystemTime::now(), price_limit: None, }) } else { None } } else { None } } } #[derive(Debug)] struct TestIcebergAlgorithm { current_order: Option, displayed_so_far: u32, } impl TestIcebergAlgorithm { fn new() -> Self { Self { current_order: None, displayed_so_far: 0, } } fn initialize_order(&mut self, order: IcebergOrder) { self.current_order = Some(order); self.displayed_so_far = 0; } fn get_current_display(&self) -> Option { if let Some(ref order) = self.current_order { // Add randomization to displayed quantity let randomization = (rand::random::() - 0.5) * 2.0 * order.randomization_factor; let randomized_display = ((order.displayed_quantity as f64) * (1.0 + randomization)) as u32; Some(DisplaySlice { displayed_quantity: randomized_display, hidden_quantity: order.total_quantity - self.displayed_so_far, }) } else { None } } fn report_fill(&mut self, filled_quantity: u32) { self.displayed_so_far += filled_quantity; } } #[derive(Debug)] struct TestOrderSlicer; impl TestOrderSlicer { fn new() -> Self { Self } fn slice_order(&self, request: SlicingRequest) -> Vec { let base_size = request.total_quantity / request.num_slices as u32; let mut slices = Vec::new(); let mut remaining = request.total_quantity; for i in 0..request.num_slices { let randomization = if request.randomization_factor > 0.0 { (rand::random::() - 0.5) * 2.0 * request.randomization_factor } else { 0.0 }; let slice_size = if i == request.num_slices - 1 { remaining // Last slice gets remainder } else { let randomized_size = ((base_size as f64) * (1.0 + randomization)) as u32; randomized_size.max(request.min_slice_size).min(request.max_slice_size) }; slices.push(slice_size); remaining = remaining.saturating_sub(slice_size); } // Adjust if total doesn't match due to rounding let total_sliced: u32 = slices.iter().sum(); if total_sliced != request.total_quantity { let diff = request.total_quantity as i64 - total_sliced as i64; if let Some(last_slice) = slices.last_mut() { *last_slice = (*last_slice as i64 + diff) as u32; } } slices } } // Mock strategy implementations #[derive(Debug)] struct MomentumStrategy; #[derive(Debug)] struct MeanReversionStrategy; #[derive(Debug)] struct ArbitrageStrategy; impl MomentumStrategy { fn new() -> Self { Self } } impl MeanReversionStrategy { fn new() -> Self { Self } } impl ArbitrageStrategy { fn new() -> Self { Self } } trait TestStrategy: std::fmt::Debug { fn generate_signal(&self, market_state: &MarketState) -> Option; fn name(&self) -> &str; } impl TestStrategy for MomentumStrategy { fn generate_signal(&self, market_state: &MarketState) -> Option { if market_state.momentum_signal > 0.5 { Some(TradingSignal { strategy: "momentum".to_string(), symbol: "EURUSD".to_string(), direction: SignalDirection::Buy, strength: market_state.momentum_signal, confidence: 0.8, }) } else { None } } fn name(&self) -> &str { "momentum" } } impl TestStrategy for MeanReversionStrategy { fn generate_signal(&self, market_state: &MarketState) -> Option { if market_state.mean_reversion_signal < -0.5 { Some(TradingSignal { strategy: "mean_reversion".to_string(), symbol: "EURUSD".to_string(), direction: SignalDirection::Sell, strength: market_state.mean_reversion_signal.abs(), confidence: 0.7, }) } else { None } } fn name(&self) -> &str { "mean_reversion" } } impl TestStrategy for ArbitrageStrategy { fn generate_signal(&self, market_state: &MarketState) -> Option { if !market_state.arbitrage_opportunities.is_empty() { Some(TradingSignal { strategy: "arbitrage".to_string(), symbol: "EURUSD".to_string(), direction: SignalDirection::Buy, strength: 0.9, confidence: 0.95, }) } else { None } } fn name(&self) -> &str { "arbitrage" } } #[derive(Debug)] struct TestStrategyOrchestrator { strategies: Vec>, priorities: std::collections::HashMap, } impl TestStrategyOrchestrator { fn new() -> Self { Self { strategies: Vec::new(), priorities: std::collections::HashMap::new(), } } fn add_strategy(&mut self, _name: &str, strategy: Box) { self.strategies.push(strategy); } fn generate_signals(&self, market_state: &MarketState) -> Vec { self.strategies.iter() .filter_map(|strategy| strategy.generate_signal(market_state)) .collect() } fn detect_conflicts(&self, signals: &[TradingSignal]) -> Vec { let mut conflicts = Vec::new(); for i in 0..signals.len() { for j in i+1..signals.len() { if signals[i].symbol == signals[j].symbol && !std::matches!((signals[i].direction.clone(), signals[j].direction.clone()), (SignalDirection::Buy, SignalDirection::Buy) | (SignalDirection::Sell, SignalDirection::Sell) | (SignalDirection::Hold, _) | (_, SignalDirection::Hold)) { conflicts.push(SignalConflict { conflict_type: ConflictType::OpposingSignals, involved_signals: vec![signals[i].clone(), signals[j].clone()], }); } } } conflicts } fn resolve_conflicts(&self, signals: Vec, _conflicts: &[SignalConflict]) -> Vec { // Simple resolution: prefer higher confidence signals let mut resolved = Vec::new(); let mut symbols_seen = std::collections::HashSet::new(); let mut sorted_signals = signals; sorted_signals.sort_by(|a, b| b.confidence.partial_cmp(&a.confidence).unwrap()); for signal in sorted_signals { if !symbols_seen.contains(&signal.symbol) { resolved.push(signal.clone()); symbols_seen.insert(signal.symbol); } } resolved } fn set_strategy_priority(&mut self, strategy_name: &str, priority: u8) { self.priorities.insert(strategy_name.to_string(), priority); } fn resolve_conflicts_by_priority(&self, signals: Vec) -> Vec { let mut resolved = signals; resolved.sort_by(|a, b| { let priority_a = self.priorities.get(&a.strategy).unwrap_or(&0); let priority_b = self.priorities.get(&b.strategy).unwrap_or(&0); priority_b.cmp(priority_a) }); resolved } } #[derive(Debug)] enum ConflictType { OpposingSignals, DuplicateSignals, } #[derive(Debug)] struct SignalConflict { conflict_type: ConflictType, involved_signals: Vec, } #[derive(Debug)] struct TestExecutionEngine; impl TestExecutionEngine { fn new() -> Self { Self } fn execute_order(&self, order: ExecutionOrder) -> Result { // Simulate execution with realistic metrics let arrival_price = 1.1025; let avg_price = arrival_price + 0.0002; // 2 pip slippage let market_impact = 0.3; // 0.3 basis points Ok(ExecutionResult { total_executed: order.quantity, average_price: avg_price, arrival_price, market_impact, max_participation_achieved: order.max_participation * 0.95, // Slightly under limit execution_cost: avg_price - arrival_price, }) } } #[derive(Debug)] struct TestAdaptiveAlgorithm { current_condition: Option, } impl TestAdaptiveAlgorithm { fn new() -> Self { Self { current_condition: None, } } fn set_market_condition(&mut self, condition: MarketCondition) { self.current_condition = Some(condition); } fn get_execution_parameters(&self) -> ExecutionParameters { match &self.current_condition { Some(MarketCondition::HighVolatility) => ExecutionParameters { slice_size_factor: 0.7, delay_between_slices: Duration::from_millis(800), max_participation_rate: 0.20, patience_factor: 1.0, urgency_multiplier: 1.0, opportunistic_factor: 1.0, }, Some(MarketCondition::LowLiquidity) => ExecutionParameters { slice_size_factor: 1.0, delay_between_slices: Duration::from_millis(300), max_participation_rate: 0.10, patience_factor: 1.5, urgency_multiplier: 1.0, opportunistic_factor: 1.0, }, Some(MarketCondition::TrendingMarket) => ExecutionParameters { slice_size_factor: 1.0, delay_between_slices: Duration::from_millis(300), max_participation_rate: 0.25, patience_factor: 1.0, urgency_multiplier: 1.3, opportunistic_factor: 1.0, }, Some(MarketCondition::RangeMarket) => ExecutionParameters { slice_size_factor: 1.0, delay_between_slices: Duration::from_millis(300), max_participation_rate: 0.20, patience_factor: 1.0, urgency_multiplier: 1.0, opportunistic_factor: 1.4, }, None => ExecutionParameters { slice_size_factor: 1.0, delay_between_slices: Duration::from_millis(300), max_participation_rate: 0.20, patience_factor: 1.0, urgency_multiplier: 1.0, opportunistic_factor: 1.0, }, } } }