//! Agent F4: Wave D Features 201-225 Validation Script //! //! This script validates all regime detection features using synthetic data //! and measures their latency performance. use chrono::Utc; use ml::ensemble::MarketRegime; use ml::features::extraction::OHLCVBar as ExtBar; use ml::features::regime_adaptive::RegimeAdaptiveFeatures; use ml::features::regime_adx::{OHLCVBar as AdxBar, RegimeADXFeatures}; use ml::features::regime_cusum::RegimeCUSUMFeatures; use std::time::Instant; fn main() { println!("=== Agent F4: Wave D Features 201-225 Validation ===\n"); // Validate CUSUM features (201-210) validate_cusum_features(); // Validate ADX features (211-215) validate_adx_features(); // Validate Adaptive features (221-224) validate_adaptive_features(); // Note: Transition features (216-220) are stubs, skip validation println!("\n=== Validation Complete ==="); } fn validate_cusum_features() { println!("## Validating CUSUM Features (201-210)"); let mut features = RegimeCUSUMFeatures::new(0.0, 1.0, 0.5, 5.0); let mut passing = 0; let mut total = 0; // Test 1: Initialization total += 1; let result = features.update(0.0); if result.len() == 10 && result.iter().all(|&x| x.is_finite()) { passing += 1; println!("✓ Test 1: Initialization - PASS"); } else { println!("✗ Test 1: Initialization - FAIL"); } // Test 2: Positive break detection total += 1; let mut detected_break = false; for _ in 0..10 { let result = features.update(3.0); if result[2] == 1.0 { // Feature 203: break indicator detected_break = true; break; } } if detected_break { passing += 1; println!("✓ Test 2: Positive break detection - PASS"); } else { println!("✗ Test 2: Positive break detection - FAIL"); } // Test 3: Normalization bounds total += 1; features = RegimeCUSUMFeatures::new(0.0, 1.0, 0.5, 5.0); for _ in 0..20 { features.update(5.0); } let result = features.update(5.0); if result[0] >= 0.0 && result[0] <= 1.5 && result[1] >= 0.0 && result[1] <= 1.5 { passing += 1; println!("✓ Test 3: Normalization bounds - PASS"); } else { println!( "✗ Test 3: Normalization bounds - FAIL (S+={}, S-={})", result[0], result[1] ); } // Test 4: Latency benchmark total += 1; let mut features = RegimeCUSUMFeatures::new(0.0, 1.0, 0.5, 5.0); let iterations = 10000; let start = Instant::now(); for i in 0..iterations { let value = ((i as f64) * 0.1).sin() * 2.0; features.update(value); } let elapsed = start.elapsed(); let avg_latency_ns = elapsed.as_nanos() / iterations as u128; let avg_latency_us = avg_latency_ns as f64 / 1000.0; if avg_latency_us < 50.0 { passing += 1; println!( "✓ Test 4: Latency - PASS ({:.2}μs < 50μs target)", avg_latency_us ); } else { println!( "✗ Test 4: Latency - FAIL ({:.2}μs >= 50μs target)", avg_latency_us ); } println!("CUSUM Results: {}/{} passing\n", passing, total); } fn validate_adx_features() { println!("## Validating ADX Features (211-215)"); let mut features = RegimeADXFeatures::new(14); let mut passing = 0; let mut total = 0; // Create test bars let bars = create_test_bars(50); // Test 1: Initialization total += 1; let result = features.update(&bars[0]); if result.len() == 5 && result == [0.0; 5] { passing += 1; println!("✓ Test 1: Initialization - PASS"); } else { println!("✗ Test 1: Initialization - FAIL"); } // Test 2: Valid range after warmup total += 1; for bar in &bars[1..30] { features.update(bar); } let result = features.update(&bars[30]); if result[0] >= 0.0 && result[0] <= 100.0 && // ADX result[1] >= 0.0 && result[1] <= 100.0 && // +DI result[2] >= 0.0 && result[2] <= 100.0 && // -DI result[3] >= 0.0 && result[3] <= 100.0 && // DX result[4] >= 0.0 { // ATR passing += 1; println!("✓ Test 2: Valid range after warmup - PASS"); } else { println!("✗ Test 2: Valid range after warmup - FAIL"); } // Test 3: Trend detection total += 1; let mut features = RegimeADXFeatures::new(14); let trend_bars = create_trend_bars(50); for bar in &trend_bars { features.update(bar); } let result = features.update(&trend_bars[49]); if result[0] > 15.0 && result[1] > result[2] { // ADX > 15 and +DI > -DI passing += 1; println!( "✓ Test 3: Trend detection - PASS (ADX={:.2}, +DI={:.2}, -DI={:.2})", result[0], result[1], result[2] ); } else { println!( "✗ Test 3: Trend detection - FAIL (ADX={:.2}, +DI={:.2}, -DI={:.2})", result[0], result[1], result[2] ); } // Test 4: Latency benchmark total += 1; let mut features = RegimeADXFeatures::new(14); let test_bars = create_test_bars(100); // Warmup for bar in &test_bars[0..30] { features.update(bar); } let iterations = 1000; let start = Instant::now(); for bar in test_bars.iter().cycle().take(iterations) { features.update(bar); } let elapsed = start.elapsed(); let avg_latency_us = elapsed.as_micros() as f64 / iterations as f64; if avg_latency_us < 50.0 { passing += 1; println!( "✓ Test 4: Latency - PASS ({:.2}μs < 50μs target)", avg_latency_us ); } else { println!( "✗ Test 4: Latency - FAIL ({:.2}μs >= 50μs target)", avg_latency_us ); } println!("ADX Results: {}/{} passing\n", passing, total); } fn validate_adaptive_features() { println!("## Validating Adaptive Features (221-224)"); let mut features = RegimeAdaptiveFeatures::new(20, 100_000.0, 14); let mut passing = 0; let mut total = 0; let bars = create_ext_bars(20); // Test 1: Position multiplier validation total += 1; let result = features.update(MarketRegime::Normal, 0.01, 50_000.0, &bars); if result[0] == 1.0 { // Normal regime = 1.0x passing += 1; println!("✓ Test 1: Position multiplier (Normal) - PASS"); } else { println!( "✗ Test 1: Position multiplier (Normal) - FAIL (expected 1.0, got {})", result[0] ); } // Test 2: All regimes total += 1; let regimes = vec![ (MarketRegime::Trending, 1.5), (MarketRegime::Sideways, 0.8), (MarketRegime::Bull, 1.2), (MarketRegime::Bear, 0.7), (MarketRegime::HighVolatility, 0.5), (MarketRegime::Crisis, 0.2), ]; let mut all_correct = true; for (regime, expected_mult) in regimes { let result = features.update(regime, 0.01, 50_000.0, &bars); if result[0] != expected_mult { all_correct = false; println!( " ✗ {:?}: expected {}, got {}", regime, expected_mult, result[0] ); } } if all_correct { passing += 1; println!("✓ Test 2: All regime multipliers - PASS"); } else { println!("✗ Test 2: All regime multipliers - FAIL"); } // Test 3: Risk budget bounds total += 1; let result = features.update(MarketRegime::Normal, 0.01, 50_000.0, &bars); if result[3] >= 0.0 && result[3] <= 1.0 { passing += 1; println!("✓ Test 3: Risk budget bounds - PASS"); } else { println!("✗ Test 3: Risk budget bounds - FAIL (got {})", result[3]); } // Test 4: Latency benchmark total += 1; let mut features = RegimeAdaptiveFeatures::new(20, 100_000.0, 14); let test_bars = create_ext_bars(30); let iterations = 10000; let start = Instant::now(); for i in 0..iterations { let regime = if i % 2 == 0 { MarketRegime::Normal } else { MarketRegime::Trending }; features.update(regime, 0.01, 50_000.0, &test_bars); } let elapsed = start.elapsed(); let avg_latency_us = elapsed.as_micros() as f64 / iterations as f64; if avg_latency_us < 50.0 { passing += 1; println!( "✓ Test 4: Latency - PASS ({:.2}μs < 50μs target)", avg_latency_us ); } else { println!( "✗ Test 4: Latency - FAIL ({:.2}μs >= 50μs target)", avg_latency_us ); } println!("Adaptive Results: {}/{} passing\n", passing, total); } // Helper functions fn create_test_bars(count: usize) -> Vec { let mut bars = Vec::new(); let mut price = 100.0; for i in 0..count { price += ((i as f64) * 0.3).sin() * 0.5; bars.push(AdxBar { timestamp: i as i64, open: price, high: price + 0.5, low: price - 0.5, close: price, volume: 1000.0, }); } bars } fn create_trend_bars(count: usize) -> Vec { let mut bars = Vec::new(); let mut price = 100.0; for i in 0..count { price += 1.0; // Strong uptrend bars.push(AdxBar { timestamp: i as i64, open: price - 0.3, high: price + 0.5, low: price - 0.6, close: price, volume: 1000.0, }); } bars } fn create_ext_bars(count: usize) -> Vec { let mut bars = Vec::new(); let base_time = Utc::now(); let mut price = 100.0; for i in 0..count { price += ((i as f64) * 0.3).sin() * 0.5; bars.push(ExtBar { timestamp: base_time + chrono::Duration::seconds(i as i64 * 60), open: price, high: price + 0.5, low: price - 0.5, close: price, volume: 1000.0, }); } bars }