//! VALIDATION 1/8: Test that SharedMLStrategy extracts 225 features //! //! This test validates that the Wave D implementation correctly extracts //! all 225 features (201 Wave C + 24 Wave D regime detection features). use chrono::Utc; use common::ml_strategy::SharedMLStrategy; use ml::features::ProductionFeatureExtractorAdapter; #[tokio::test] async fn test_sharedml_extracts_225_features() { // Create SharedMLStrategy with production feature extractor (225 features) let extractor = Box::new(ProductionFeatureExtractorAdapter::new()); let strategy = SharedMLStrategy::new_with_production_extractor(extractor, 0.5); // Warm up the feature extractor with some historical data // (needed to properly compute indicators like EMAs, RSI, etc.) for i in 0..100 { let price = 100.0 + (i as f64 * 0.1); let volume = 1000.0 + (i as f64 * 10.0); let _ = strategy .get_ensemble_prediction(price, volume, Utc::now()) .await; } // Extract features from the strategy let predictions = strategy .get_ensemble_prediction(100.0, 1000.0, Utc::now()) .await .expect("Should extract features successfully"); // Get features from the first prediction (all models use same features) assert!( !predictions.is_empty(), "Should have at least one prediction" ); let features = &predictions[0].features; // VALIDATION 1: Verify feature count is exactly 225 assert_eq!( features.len(), 225, "SharedMLStrategy must extract exactly 225 features (201 Wave C + 24 Wave D), but got {}", features.len() ); // VALIDATION 2: Verify no NaN values for (i, f) in features.iter().enumerate() { assert!( !f.is_nan(), "Feature at index {} is NaN (value: {})", i, f ); } // VALIDATION 3: Verify no Inf values for (i, f) in features.iter().enumerate() { assert!( f.is_finite(), "Feature at index {} is not finite (value: {}). All features must be finite numbers.", i, f ); } println!("✅ VALIDATION 1/8 PASSED"); println!(" - Feature count: {} (expected 225)", features.len()); println!(" - All features are finite"); println!(" - No NaN or Inf values detected"); } #[tokio::test] async fn test_feature_extraction_wave_d_breakdown() { // Create SharedMLStrategy with production feature extractor (225 features) let extractor = Box::new(ProductionFeatureExtractorAdapter::new()); let strategy = SharedMLStrategy::new_with_production_extractor(extractor, 0.5); // Warm up the feature extractor for i in 0..100 { let price = 100.0 + (i as f64 * 0.1); let volume = 1000.0 + (i as f64 * 10.0); let _ = strategy .get_ensemble_prediction(price, volume, Utc::now()) .await; } // Extract features let predictions = strategy .get_ensemble_prediction(100.0, 1000.0, Utc::now()) .await .expect("Should extract features successfully"); let features = &predictions[0].features; // Verify feature breakdown (expected from Wave D documentation): // - Wave A: 26 features (indices 0-25) // - Wave B: 10 features (indices 26-35) [alternative bar sampling] // - Wave C: 165 features (indices 36-200) [advanced feature engineering] // - Wave D: 24 features (indices 201-224) [regime detection] // Total: 225 features assert_eq!( features.len(), 225, "Expected 225 total features (26 Wave A + 10 Wave B + 165 Wave C + 24 Wave D)" ); // Verify Wave D features (indices 201-224) are present for i in 201..225 { let feature_value = features.get(i); assert!( feature_value.is_some(), "Wave D feature at index {} is missing", i ); let value = feature_value.unwrap(); assert!( value.is_finite(), "Wave D feature at index {} is not finite: {}", i, value ); } println!("✅ Wave D feature breakdown validated"); println!(" - Wave A features (0-25): present"); println!(" - Wave B features (26-35): present"); println!(" - Wave C features (36-200): present"); println!(" - Wave D features (201-224): present"); }