//! Comprehensive tests for core/src/features/unified_extractor.rs
//!
//! This test suite provides complete coverage of all 6 ML model feature extractors
//! (TLOB, MAMBA, DQN, PPO, Liquid, TFT) with 80+ test functions covering every
//! feature extraction method and integration point.

use chrono::{DateTime, Utc, Duration, Datelike, Timelike};
use std::collections::HashMap;
use std::time::Instant;
use tokio_test;
use proptest::prelude::*;

use trading_engine::features::unified_extractor::{
    UnifiedFeatureExtractor, UnifiedConfig, FeatureError,
    DatabentoBuFeatures, BenzingaNewsFeatures, BaseMarketFeatures,
    TLOBFeatures, MAMBAFeatures, DQNFeatures, PPOFeatures,
    LiquidFeatures, TFTFeatures,
    DatabentoBuData, BenzingaNewsData, NewsArticle, SentimentScore,
    AnalystRating, UnusualOptionsActivity,
};
use common::QuoteEvent;

// Test fixtures and mock data generators

fn create_test_config() -> UnifiedConfig {
    UnifiedConfig::default()
}

fn create_custom_config() -> UnifiedConfig {
    UnifiedConfig {
        min_data_points: 50,
        max_missing_ratio: 0.2,
        outlier_threshold: 2.5,
        short_window: Duration::from_secs(180),
        medium_window: Duration::from_secs(1800),
        long_window: Duration::from_secs(7200),
        tlob_sequence_length: 25,
        mamba_sequence_length: 100,
        dqn_history_length: 5,
        ppo_history_length: 5,
        tft_encoder_length: 96,
        tft_decoder_length: 12,
        enable_simd: false, // For testing without SIMD
        parallel_processing: false,
        cache_intermediate_results: true,
    }
}

fn create_test_market_ticks(count: usize) -> Vec<MarketTick> {
    let mut ticks = Vec::new();
    let base_time = Utc::now() - Duration::minutes(count as i64);
    
    for i in 0..count {
        let timestamp = base_time + Duration::minutes(i as i64);
        let price = Price::from_f64(100.0 + i as f64 * 0.01 + (i as f64 * 0.1).sin()).unwrap();
        let size = Volume::from_u64(1000 + i as u64 * 10).unwrap();
        
        ticks.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size,
            side: if i % 2 == 0 { TradeSide::Buy } else { TradeSide::Sell },
        });
    }
    
    ticks
}

fn create_test_databento_data() -> DatabentoBuData {
    DatabentoBuData {
        order_book: vec![
            OrderBookLevel {
                price: Price::from_f64(99.95).unwrap(),
                size: Volume::from_u64(1000).unwrap(),
                side: BookSide::Bid,
            },
            OrderBookLevel {
                price: Price::from_f64(100.05).unwrap(),
                size: Volume::from_u64(1500).unwrap(),
                side: BookSide::Ask,
            },
        ],
        trades: vec![
            Trade {
                timestamp: Utc::now(),
                price: Price::from_f64(100.0).unwrap(),
                size: Volume::from_u64(200).unwrap(),
                side: TradeSide::Buy,
            },
        ],
        quotes: vec![
            QuoteEvent {
                timestamp: Utc::now(),
                symbol: "AAPL".to_string(),
                bid: Some(Decimal::try_from(99.95).unwrap()),
                ask: Some(Decimal::try_from(100.05).unwrap()),
                bid_size: Some(Decimal::from_u64(1000).unwrap()),
                    ask_size: Some(Decimal::from_u64(1500).unwrap()),
                    exchange: Some("NASDAQ".to_string()),
                },
        ],
        timestamp: Utc::now(),
    }
}

fn create_test_benzinga_data(sentiment_count: usize) -> BenzingaNewsData {
    let mut articles = Vec::new();
    let mut sentiment_scores = Vec::new();
    
    for i in 0..sentiment_count {
        let sentiment = (i as f64 / sentiment_count as f64) * 2.0 - 1.0; // Range -1 to 1
        
        articles.push(NewsArticle {
            title: format!("Test Article {}", i),
            content: format!("Test content for article {}", i),
            source: "TestSource".to_string(),
            timestamp: Utc::now() - Duration::minutes(i as i64 * 10),
            symbols: vec![Symbol::new("AAPL").unwrap()],
            category: "Earnings".to_string(),
            importance: 0.7 + (i as f64 / sentiment_count as f64) * 0.3,
        });
        
        sentiment_scores.push(SentimentScore {
            symbol: Symbol::new("AAPL").unwrap(),
            score: sentiment,
            confidence: 0.8,
            timestamp: Utc::now() - Duration::minutes(i as i64 * 10),
        });
    }
    
    BenzingaNewsData {
        articles,
        sentiment_scores,
        analyst_ratings: vec![
            AnalystRating {
                symbol: Symbol::new("AAPL").unwrap(),
                rating: "BUY".to_string(),
                price_target: Some(120.0),
                analyst: "John Doe".to_string(),
                firm: "Test Firm".to_string(),
                timestamp: Utc::now() - Duration::hours(1),
            },
        ],
        unusual_options: vec![
            UnusualOptionsActivity {
                symbol: Symbol::new("AAPL").unwrap(),
                option_type: "CALL".to_string(),
                strike: 105.0,
                expiration: Utc::now() + Duration::days(30),
                volume: 10000,
                unusual_score: 0.8,
                timestamp: Utc::now() - Duration::minutes(30),
            },
        ],
        timestamp: Utc::now(),
    }
}

// 1. Configuration and Setup Tests (10 tests)

#[test]
fn test_unified_config_default() {
    let config = UnifiedConfig::default();
    
    assert_eq!(config.min_data_points, 100);
    assert_eq!(config.max_missing_ratio, 0.1);
    assert_eq!(config.outlier_threshold, 3.0);
    assert_eq!(config.tlob_sequence_length, 50);
    assert_eq!(config.mamba_sequence_length, 200);
    assert_eq!(config.dqn_history_length, 10);
    assert_eq!(config.ppo_history_length, 10);
    assert_eq!(config.tft_encoder_length, 192);
    assert_eq!(config.tft_decoder_length, 24);
    assert!(config.enable_simd);
    assert!(config.parallel_processing);
    assert!(config.cache_intermediate_results);
}

#[test]
fn test_unified_config_custom() {
    let config = create_custom_config();
    
    assert_eq!(config.min_data_points, 50);
    assert_eq!(config.tlob_sequence_length, 25);
    assert_eq!(config.mamba_sequence_length, 100);
    assert!(!config.enable_simd);
    assert!(!config.parallel_processing);
}

#[test]
fn test_extractor_creation_default_config() {
    let config = create_test_config();
    let extractor = UnifiedFeatureExtractor::new(config);
    
    // Should create successfully
    assert!(extractor.feature_cache.is_empty());
}

#[test]
fn test_extractor_creation_custom_config() {
    let config = create_custom_config();
    let extractor = UnifiedFeatureExtractor::new(config.clone());
    
    assert_eq!(extractor.config.min_data_points, 50);
    assert!(!extractor.config.enable_simd);
}

#[test]
fn test_extractor_simd_initialization() {
    let config = UnifiedConfig {
        enable_simd: true,
        ..create_test_config()
    };
    
    let extractor = UnifiedFeatureExtractor::new(config);
    
    // Should handle SIMD availability gracefully
    match extractor.simd_processor {
        Some(_) => (), // SIMD available
        None => (),    // SIMD not available, should still work
    }
}

#[test]
fn test_extractor_without_simd() {
    let config = UnifiedConfig {
        enable_simd: false,
        ..create_test_config()
    };
    
    let extractor = UnifiedFeatureExtractor::new(config);
    assert!(extractor.simd_processor.is_none());
}

#[test]
fn test_config_time_windows() {
    let config = create_test_config();
    
    assert_eq!(config.short_window, Duration::from_secs(300));
    assert_eq!(config.medium_window, Duration::from_secs(3600));
    assert_eq!(config.long_window, Duration::from_secs(14400));
    assert!(config.short_window < config.medium_window);
    assert!(config.medium_window < config.long_window);
}

#[test]
fn test_model_specific_config_parameters() {
    let config = create_test_config();
    
    // TLOB parameters
    assert_eq!(config.tlob_sequence_length, 50);
    
    // MAMBA parameters
    assert_eq!(config.mamba_sequence_length, 200);
    assert!(config.mamba_sequence_length > config.tlob_sequence_length);
    
    // DQN/PPO parameters
    assert_eq!(config.dqn_history_length, 10);
    assert_eq!(config.ppo_history_length, 10);
    
    // TFT parameters
    assert_eq!(config.tft_encoder_length, 192);
    assert_eq!(config.tft_decoder_length, 24);
    assert!(config.tft_encoder_length > config.tft_decoder_length);
}

#[test]
fn test_feature_error_types() {
    let insufficient_data = FeatureError::InsufficientData {
        feature: "test_feature".to_string(),
        required: 100,
        available: 50,
    };
    
    match insufficient_data {
        FeatureError::InsufficientData { required, available, .. } => {
            assert_eq!(required, 100);
            assert_eq!(available, 50);
        }
        _ => panic!("Wrong error type"),
    }
    
    let invalid_params = FeatureError::InvalidParameters {
        feature: "test_feature".to_string(),
        reason: "test reason".to_string(),
    };
    
    assert!(format!("{}", invalid_params).contains("test reason"));
}

#[test]
fn test_databento_data_structure() {
    let data = create_test_databento_data();
    
    assert!(!data.order_book.is_empty());
    assert!(!data.trades.is_empty());
    assert!(!data.quotes.is_empty());
    assert!(data.timestamp <= Utc::now());
    
    // Verify order book structure
    let bid_levels: Vec<_> = data.order_book.iter().filter(|l| matches!(l.side, BookSide::Bid)).collect();
    let ask_levels: Vec<_> = data.order_book.iter().filter(|l| matches!(l.side, BookSide::Ask)).collect();
    
    assert!(!bid_levels.is_empty());
    assert!(!ask_levels.is_empty());
}

// 2. TLOB Feature Extraction Tests (15 tests)

#[tokio::test]
async fn test_tlob_features_basic_extraction() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(150);
    
    let result = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await;
    
    assert!(result.is_ok());
    let features = result.unwrap();
    
    // Verify structure
    assert_eq!(features.order_book_sequence.len(), extractor.config.tlob_sequence_length);
    assert_eq!(features.trade_flow_sequence.len(), extractor.config.tlob_sequence_length);
    assert_eq!(features.spread_sequence.len(), extractor.config.tlob_sequence_length);
    assert_eq!(features.depth_sequence.len(), extractor.config.tlob_sequence_length);
}

#[tokio::test]
async fn test_tlob_base_features() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(200);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Verify base features
    assert!(features.base.price.to_f64() > 0.0);
    assert!(features.base.volume.to_u64() > 0);
    assert!(features.base.time_of_day >= 0.0 && features.base.time_of_day <= 1.0);
    assert!(features.base.day_of_week >= 1 && features.base.day_of_week <= 7);
    assert!(features.base.market_session >= 1 && features.base.market_session <= 3);
}

#[tokio::test]
async fn test_tlob_databento_features() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Verify Databento features
    assert!(features.databento.bid_ask_spread_bps >= 0.0);
    assert!(features.databento.order_book_imbalance >= -1.0);
    assert!(features.databento.order_book_imbalance <= 1.0);
    assert!(features.databento.effective_spread_bps >= 0.0);
    assert!(features.databento.price_impact_bps >= 0.0);
    assert!(features.databento.l3_order_intensity >= 0.0);
    assert!(features.databento.l3_cancellation_ratio >= 0.0);
    assert!(features.databento.l3_cancellation_ratio <= 1.0);
}

#[tokio::test]
async fn test_tlob_benzinga_features() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(10);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Verify Benzinga features
    assert!(features.benzinga.sentiment_score >= -1.0);
    assert!(features.benzinga.sentiment_score <= 1.0);
    assert!(features.benzinga.sentiment_confidence >= 0.0);
    assert!(features.benzinga.sentiment_confidence <= 1.0);
    assert!(features.benzinga.news_velocity >= 0.0);
    assert!(features.benzinga.source_count > 0);
    assert!(features.benzinga.mention_volume > 0);
}

#[tokio::test]
async fn test_tlob_order_book_sequences() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Verify sequences are properly sized
    assert_eq!(features.order_book_sequence.len(), 50);
    assert_eq!(features.trade_flow_sequence.len(), 50);
    assert_eq!(features.spread_sequence.len(), 50);
    assert_eq!(features.depth_sequence.len(), 50);
    
    // Sequences should contain valid values
    for &val in &features.order_book_sequence {
        assert!(val.is_finite());
    }
}

#[tokio::test]
async fn test_tlob_trade_classification() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Trade sign should be -1, 0, or 1
    assert!(features.databento.trade_sign >= -1);
    assert!(features.databento.trade_sign <= 1);
    
    // Trade size category should be 1-4
    assert!(features.databento.trade_size_category >= 1);
    assert!(features.databento.trade_size_category <= 4);
    
    // Trade urgency should be normalized
    assert!(features.databento.trade_urgency >= 0.0);
    assert!(features.databento.trade_urgency <= 1.0);
}

#[tokio::test]
async fn test_tlob_microstructure_noise() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Microstructure features should be reasonable
    assert!(features.databento.realized_spread_bps >= 0.0);
    assert!(features.databento.information_share >= 0.0);
    assert!(features.databento.information_share <= 1.0);
    assert!(features.databento.microstructure_noise >= 0.0);
}

#[tokio::test]
async fn test_tlob_high_frequency_patterns() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // High-frequency pattern features
    assert!(features.databento.tick_direction_streak >= -10); // Reasonable range
    assert!(features.databento.tick_direction_streak <= 10);
    assert!(features.databento.quote_update_frequency >= 0.0);
    assert!(features.databento.order_arrival_intensity >= 0.0);
}

#[tokio::test]
async fn test_tlob_sentiment_analysis() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(15);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Weighted sentiment features
    assert!(features.benzinga.weighted_sentiment_1h >= -1.0);
    assert!(features.benzinga.weighted_sentiment_1h <= 1.0);
    assert!(features.benzinga.weighted_sentiment_4h >= -1.0);
    assert!(features.benzinga.weighted_sentiment_4h <= 1.0);
    assert!(features.benzinga.weighted_sentiment_24h >= -1.0);
    assert!(features.benzinga.weighted_sentiment_24h <= 1.0);
}

#[tokio::test]
async fn test_tlob_news_categorization() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // News categorization features
    // These are boolean/optional fields, so just check they exist
    assert!(features.benzinga.analyst_rating.is_none() || features.benzinga.analyst_rating.is_some());
    assert!(features.benzinga.sec_filing_type.is_none() || features.benzinga.sec_filing_type.is_some());
}

#[tokio::test]
async fn test_tlob_sentiment_momentum() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(10);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Sentiment momentum features should be finite
    assert!(features.benzinga.sentiment_acceleration.is_finite());
    assert!(features.benzinga.sentiment_divergence.is_finite());
    assert!(features.benzinga.contrarian_signal.is_finite());
    assert!(features.benzinga.social_amplification >= 0.0);
}

#[tokio::test]
async fn test_tlob_insufficient_data() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(10); // Insufficient data
    
    let result = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await;
    
    // Should return an error for insufficient data
    match result {
        Err(FeatureError::InsufficientData { .. }) => (),
        _ => panic!("Expected InsufficientData error"),
    }
}

#[tokio::test]
async fn test_tlob_performance_timing() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(200);
    
    let start_time = Instant::now();
    
    let result = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await;
    
    let elapsed = start_time.elapsed();
    
    assert!(result.is_ok());
    // Should complete in reasonable time (adjust as needed)
    assert!(elapsed.as_millis() < 1000);
}

#[tokio::test]
async fn test_tlob_serialization() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_tlob_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Test serialization
    let serialized = serde_json::to_string(&features);
    assert!(serialized.is_ok());
    
    // Test deserialization
    let deserialized: Result<TLOBFeatures, _> = serde_json::from_str(&serialized.unwrap());
    assert!(deserialized.is_ok());
}

// 3. MAMBA Feature Extraction Tests (12 tests)

#[tokio::test]
async fn test_mamba_features_basic_extraction() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(250);
    
    let result = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await;
    
    assert!(result.is_ok());
    let features = result.unwrap();
    
    // Verify MAMBA-specific sequences
    assert_eq!(features.price_sequence.len(), extractor.config.mamba_sequence_length);
    assert_eq!(features.volume_sequence.len(), extractor.config.mamba_sequence_length);
    assert_eq!(features.sentiment_sequence.len(), extractor.config.mamba_sequence_length);
    
    // Verify regime indicators
    assert!(features.volatility_regime.is_finite());
    assert!(features.trend_persistence.is_finite());
}

#[tokio::test]
async fn test_mamba_price_sequence_generation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(250);
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Price sequence should contain valid prices
    assert_eq!(features.price_sequence.len(), 200);
    
    for &price in &features.price_sequence[..historical_data.len().min(200)] {
        assert!(price > 0.0);
        assert!(price.is_finite());
    }
    
    // Remaining should be zero-padded
    if historical_data.len() < 200 {
        for &price in &features.price_sequence[historical_data.len()..] {
            assert_eq!(price, 0.0);
        }
    }
}

#[tokio::test]
async fn test_mamba_volume_sequence_generation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(250);
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Volume sequence should contain valid volumes
    assert_eq!(features.volume_sequence.len(), 200);
    
    for &volume in &features.volume_sequence[..historical_data.len().min(200)] {
        assert!(volume > 0.0);
        assert!(volume.is_finite());
    }
}

#[tokio::test]
async fn test_mamba_sentiment_sequence_generation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(10);
    let historical_data = create_test_market_ticks(250);
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Sentiment sequence should be properly sized
    assert_eq!(features.sentiment_sequence.len(), 200);
    
    // All values should be finite (may be zero if no sentiment data)
    for &sentiment in &features.sentiment_sequence {
        assert!(sentiment.is_finite());
    }
}

#[tokio::test]
async fn test_mamba_volatility_regime_calculation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    
    // Create data with varying volatility patterns
    let mut historical_data = Vec::new();
    let base_time = Utc::now() - Duration::minutes(250);
    
    for i in 0..250 {
        let timestamp = base_time + Duration::minutes(i as i64);
        let volatility_factor = if i < 125 { 0.01 } else { 0.1 }; // Higher volatility in second half
        let noise = (i as f64 * 0.1).sin() * volatility_factor;
        let price = Price::from_f64(100.0 + noise).unwrap();
        let size = Volume::from_u64(1000).unwrap();
        
        historical_data.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size,
            side: if i % 2 == 0 { TradeSide::Buy } else { TradeSide::Sell },
        });
    }
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Volatility regime should be calculated (currently returns 0.0 as placeholder)
    assert!(features.volatility_regime.is_finite());
}

#[tokio::test]
async fn test_mamba_trend_persistence_calculation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    
    // Create trending price data
    let mut historical_data = Vec::new();
    let base_time = Utc::now() - Duration::minutes(250);
    
    for i in 0..250 {
        let timestamp = base_time + Duration::minutes(i as i64);
        let trend = i as f64 * 0.01; // Consistent upward trend
        let price = Price::from_f64(100.0 + trend).unwrap();
        let size = Volume::from_u64(1000).unwrap();
        
        historical_data.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size,
            side: if i % 2 == 0 { TradeSide::Buy } else { TradeSide::Sell },
        });
    }
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Trend persistence should be calculated
    assert!(features.trend_persistence.is_finite());
}

#[tokio::test]
async fn test_mamba_sequence_length_configuration() {
    let mut config = create_test_config();
    config.mamba_sequence_length = 50; // Custom sequence length
    
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Sequences should respect custom configuration
    assert_eq!(features.price_sequence.len(), 50);
    assert_eq!(features.volume_sequence.len(), 50);
    assert_eq!(features.sentiment_sequence.len(), 50);
}

#[tokio::test]
async fn test_mamba_state_space_modeling_features() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(20);
    let historical_data = create_test_market_ticks(250);
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // State space modeling should generate proper sequences
    let non_zero_prices = features.price_sequence.iter().filter(|&&x| x != 0.0).count();
    let non_zero_volumes = features.volume_sequence.iter().filter(|&&x| x != 0.0).count();
    
    assert!(non_zero_prices > 0);
    assert!(non_zero_volumes > 0);
    
    // Should have same number of non-zero prices and volumes (up to data length)
    let data_len = historical_data.len().min(200);
    assert_eq!(non_zero_prices, data_len);
    assert_eq!(non_zero_volumes, data_len);
}

#[tokio::test]
async fn test_mamba_long_sequence_handling() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(500); // More data than sequence length
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Should still generate sequences of correct length
    assert_eq!(features.price_sequence.len(), 200);
    assert_eq!(features.volume_sequence.len(), 200);
    
    // All sequence values should be filled (no zeros from truncation)
    let zero_count = features.price_sequence.iter().filter(|&&x| x == 0.0).count();
    assert_eq!(zero_count, 0);
}

#[tokio::test]
async fn test_mamba_sequence_temporal_consistency() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    
    // Create temporally ordered data
    let mut historical_data = Vec::new();
    let base_time = Utc::now() - Duration::minutes(200);
    
    for i in 0..200 {
        let timestamp = base_time + Duration::minutes(i as i64);
        let price = Price::from_f64(100.0 + i as f64 * 0.01).unwrap(); // Increasing prices
        
        historical_data.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size: Volume::from_u64(1000).unwrap(),
            side: TradeSide::Buy,
        });
    }
    
    let features = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Price sequence should maintain temporal order
    for i in 1..features.price_sequence.len() {
        if features.price_sequence[i] != 0.0 && features.price_sequence[i-1] != 0.0 {
            assert!(features.price_sequence[i] >= features.price_sequence[i-1]);
        }
    }
}

#[tokio::test]
async fn test_mamba_performance_large_sequences() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(10);
    let historical_data = create_test_market_ticks(1000); // Large dataset
    
    let start_time = Instant::now();
    
    let result = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await;
    
    let elapsed = start_time.elapsed();
    
    assert!(result.is_ok());
    // Should handle large sequences efficiently
    assert!(elapsed.as_millis() < 2000);
}

#[tokio::test]
async fn test_mamba_empty_data_handling() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = Vec::new(); // Empty data
    
    let result = extractor.extract_mamba_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await;
    
    // Should handle empty data gracefully (return error for insufficient data)
    match result {
        Err(FeatureError::InsufficientData { .. }) => (),
        _ => panic!("Expected InsufficientData error for empty data"),
    }
}

// 4. DQN Feature Extraction Tests (12 tests)

#[tokio::test]
async fn test_dqn_features_basic_extraction() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(150);
    let current_position = 100.0;
    let unrealized_pnl = 250.0;
    
    let result = extractor.extract_dqn_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        current_position,
        unrealized_pnl,
    ).await;
    
    assert!(result.is_ok());
    let features = result.unwrap();
    
    // Verify DQN-specific features
    assert_eq!(features.position, current_position);
    assert_eq!(features.unrealized_pnl, unrealized_pnl);
    assert!(features.time_in_position.is_finite());
    assert!(features.market_impact_estimate.is_finite());
    assert!(features.opportunity_cost.is_finite());
    assert!(features.available_liquidity >= 0.0);
    assert!(features.transaction_cost_estimate >= 0.0);
    assert!(features.risk_budget_remaining.is_finite());
}

#[tokio::test]
async fn test_dqn_position_states() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    let positions = vec![0.0, 100.0, -50.0, 500.0, -1000.0];
    let pnl = 100.0;
    
    for position in positions {
        let features = extractor.extract_dqn_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            position,
            pnl,
        ).await.unwrap();
        
        assert_eq!(features.position, position);
        assert!(features.time_in_position >= 0.0);
        assert!(features.market_impact_estimate >= 0.0);
        
        // Risk budget should be affected by position size
        assert!(features.risk_budget_remaining.is_finite());
    }
}

#[tokio::test]
async fn test_dqn_pnl_states() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let position = 100.0;
    
    let pnl_values = vec![-500.0, -100.0, 0.0, 150.0, 1000.0];
    
    for pnl in pnl_values {
        let features = extractor.extract_dqn_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            position,
            pnl,
        ).await.unwrap();
        
        assert_eq!(features.unrealized_pnl, pnl);
        
        // Risk budget should be affected by PnL
        assert!(features.risk_budget_remaining.is_finite());
    }
}

#[tokio::test]
async fn test_dqn_market_impact_estimation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    // Test with different position sizes
    let position_sizes = vec![10.0, 100.0, 1000.0, 10000.0];
    let pnl = 0.0;
    
    for position in position_sizes {
        let features = extractor.extract_dqn_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            position,
            pnl,
        ).await.unwrap();
        
        // Market impact should generally increase with position size
        assert!(features.market_impact_estimate >= 0.0);
        assert!(features.market_impact_estimate.is_finite());
    }
}

#[tokio::test]
async fn test_dqn_liquidity_assessment() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let position = 100.0;
    let pnl = 50.0;
    
    let features = extractor.extract_dqn_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        position,
        pnl,
    ).await.unwrap();
    
    // Available liquidity should be positive and finite
    assert!(features.available_liquidity > 0.0);
    assert!(features.available_liquidity.is_finite());
    
    // Transaction costs should be reasonable
    assert!(features.transaction_cost_estimate >= 0.0);
    assert!(features.transaction_cost_estimate.is_finite());
}

#[tokio::test]
async fn test_dqn_opportunity_cost_calculation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let position = 100.0;
    let pnl = 50.0;
    
    let features = extractor.extract_dqn_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        position,
        pnl,
    ).await.unwrap();
    
    // Opportunity cost should be calculated
    assert!(features.opportunity_cost.is_finite());
}

#[tokio::test]
async fn test_dqn_risk_budget_calculation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    // Test risk budget with different positions and PnL
    let scenarios = vec![
        (0.0, 0.0),      // No position, no PnL
        (100.0, 50.0),   // Long position, positive PnL
        (-100.0, -50.0), // Short position, negative PnL
        (500.0, -200.0), // Large position, negative PnL
    ];
    
    for (position, pnl) in scenarios {
        let features = extractor.extract_dqn_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            position,
            pnl,
        ).await.unwrap();
        
        assert!(features.risk_budget_remaining.is_finite());
    }
}

#[tokio::test]
async fn test_dqn_state_representation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(150);
    let position = 200.0;
    let pnl = 75.0;
    
    let features = extractor.extract_dqn_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        position,
        pnl,
    ).await.unwrap();
    
    // State representation should include all RL components
    assert!(features.base.price.to_f64() > 0.0);
    assert!(features.base.volume.to_u64() > 0);
    assert!(features.databento.bid_ask_spread_bps >= 0.0);
    assert!(features.benzinga.sentiment_confidence >= 0.0);
    
    // RL-specific state components
    assert_eq!(features.position, position);
    assert_eq!(features.unrealized_pnl, pnl);
    assert!(features.time_in_position >= 0.0);
}

#[tokio::test]
async fn test_dqn_action_space_context() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let position = 100.0;
    let pnl = 50.0;
    
    let features = extractor.extract_dqn_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        position,
        pnl,
    ).await.unwrap();
    
    // Action space context features
    assert!(features.available_liquidity > 0.0);
    assert!(features.transaction_cost_estimate >= 0.0);
    assert!(features.risk_budget_remaining.is_finite());
}

#[tokio::test]
async fn test_dqn_extreme_positions() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    // Test extreme position sizes
    let extreme_positions = vec![
        (f64::MAX / 1e6, 1000.0),    // Very large long position
        (f64::MIN / 1e6, -1000.0),   // Very large short position
        (0.0, f64::MAX / 1e6),       // No position, very large PnL
        (100.0, f64::MIN / 1e6),     // Small position, very negative PnL
    ];
    
    for (position, pnl) in extreme_positions {
        let result = extractor.extract_dqn_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            position,
            pnl,
        ).await;
        
        // Should handle extreme values gracefully
        match result {
            Ok(features) => {
                assert!(features.position.is_finite());
                assert!(features.unrealized_pnl.is_finite());
            }
            Err(_) => (), // Acceptable to reject extreme values
        }
    }
}

#[tokio::test]
async fn test_dqn_time_in_position_calculation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    // Test different position scenarios
    let positions = vec![0.0, 100.0, -50.0];
    let pnl = 25.0;
    
    for position in positions {
        let features = extractor.extract_dqn_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            position,
            pnl,
        ).await.unwrap();
        
        // Time in position should be >= 0
        assert!(features.time_in_position >= 0.0);
        assert!(features.time_in_position.is_finite());
        
        // For zero position, time in position should be 0
        if position == 0.0 {
            assert_eq!(features.time_in_position, 0.0);
        }
    }
}

#[tokio::test]
async fn test_dqn_feature_consistency() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let position = 150.0;
    let pnl = 85.0;
    
    // Extract features multiple times with same inputs
    let features1 = extractor.extract_dqn_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        position,
        pnl,
    ).await.unwrap();
    
    let features2 = extractor.extract_dqn_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        position,
        pnl,
    ).await.unwrap();
    
    // Results should be consistent
    assert_eq!(features1.position, features2.position);
    assert_eq!(features1.unrealized_pnl, features2.unrealized_pnl);
    assert_eq!(features1.time_in_position, features2.time_in_position);
    assert_eq!(features1.market_impact_estimate, features2.market_impact_estimate);
}

// 5. PPO Feature Extraction Tests (10 tests)

#[tokio::test]
async fn test_ppo_features_basic_extraction() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(150);
    let action_history = vec![0.5, 0.3, -0.2, 0.8, -0.1, 0.4, -0.6, 0.9, -0.3, 0.1];
    let reward_history = vec![1.5, -0.8, 2.1, 0.3, -1.2, 0.9, -0.4, 1.8, 0.6, -0.5];
    
    let result = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        &action_history,
        &reward_history,
    ).await;
    
    assert!(result.is_ok());
    let features = result.unwrap();
    
    // Verify PPO-specific features
    assert_eq!(features.action_history, action_history);
    assert_eq!(features.reward_history, reward_history);
    assert!(features.advantage_estimate.is_finite());
    assert!(features.value_estimate.is_finite());
    assert!(features.policy_entropy >= 0.0);
    assert!(features.exploration_bonus >= 0.0);
    assert!(features.uncertainty_estimate >= 0.0);
}

#[tokio::test]
async fn test_ppo_gae_advantage_calculation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let action_history = vec![0.1; 10];
    
    // Test with different reward patterns
    let reward_patterns = vec![
        vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], // Consistent positive
        vec![-1.0; 10],                                            // Consistent negative
        vec![1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0], // Alternating
        (0..10).map(|i| i as f64).collect::<Vec<_>>(),             // Increasing
    ];
    
    for reward_history in reward_patterns {
        let features = extractor.extract_ppo_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            &action_history,
            &reward_history,
        ).await.unwrap();
        
        // GAE advantage should be calculated
        assert!(features.advantage_estimate.is_finite());
    }
}

#[tokio::test]
async fn test_ppo_state_value_estimation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let action_history = vec![0.2; 10];
    let reward_history = vec![0.5; 10];
    
    let features = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        &action_history,
        &reward_history,
    ).await.unwrap();
    
    // State value estimate should be reasonable
    assert!(features.value_estimate.is_finite());
}

#[tokio::test]
async fn test_ppo_policy_entropy_calculation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let reward_history = vec![0.1; 10];
    
    // Test with different action patterns
    let action_patterns = vec![
        vec![0.5; 10],                                             // Consistent actions (low entropy)
        vec![0.1, 0.9, 0.2, 0.8, 0.3, 0.7, 0.4, 0.6, 0.5, 0.5], // Varied actions (high entropy)
        vec![1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0], // Extreme actions
    ];
    
    for action_history in action_patterns {
        let features = extractor.extract_ppo_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            &action_history,
            &reward_history,
        ).await.unwrap();
        
        // Policy entropy should be non-negative
        assert!(features.policy_entropy >= 0.0);
        assert!(features.policy_entropy.is_finite());
    }
}

#[tokio::test]
async fn test_ppo_exploration_bonus() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let reward_history = vec![0.2; 10];
    
    // Test exploration bonus for different action diversity levels
    let conservative_actions = vec![0.1; 10];  // Low diversity
    let diverse_actions = (0..10).map(|i| (i as f64) / 10.0 - 0.5).collect::<Vec<_>>(); // High diversity
    
    let conservative_features = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        &conservative_actions,
        &reward_history,
    ).await.unwrap();
    
    let diverse_features = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        &diverse_actions,
        &reward_history,
    ).await.unwrap();
    
    // Both should have non-negative exploration bonuses
    assert!(conservative_features.exploration_bonus >= 0.0);
    assert!(diverse_features.exploration_bonus >= 0.0);
    
    // More diverse actions might get higher exploration bonus (implementation dependent)
    assert!(conservative_features.exploration_bonus.is_finite());
    assert!(diverse_features.exploration_bonus.is_finite());
}

#[tokio::test]
async fn test_ppo_model_uncertainty_estimation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let action_history = vec![0.3; 10];
    let reward_history = vec![0.4; 10];
    
    // Test with different market conditions (varying volatility)
    let volatile_data = {
        let mut data = Vec::new();
        let base_time = Utc::now() - Duration::minutes(150);
        
        for i in 0..150 {
            let timestamp = base_time + Duration::minutes(i as i64);
            let volatility = (i as f64 * 0.1).sin() * 2.0; // High volatility
            let price = Price::from_f64(100.0 + volatility).unwrap();
            
            data.push(MarketTick {
                timestamp,
                symbol: Symbol::new("AAPL").unwrap(),
                price,
                size: Volume::from_u64(1000).unwrap(),
                side: TradeSide::Buy,
            });
        }
        data
    };
    
    let features = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &volatile_data,
        &action_history,
        &reward_history,
    ).await.unwrap();
    
    // Uncertainty estimate should be calculated
    assert!(features.uncertainty_estimate >= 0.0);
    assert!(features.uncertainty_estimate.is_finite());
}

#[tokio::test]
async fn test_ppo_history_length_configuration() {
    let mut config = create_test_config();
    config.ppo_history_length = 5; // Custom history length
    
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let action_history = vec![0.1, 0.2, 0.3, 0.4, 0.5]; // Match configured length
    let reward_history = vec![0.1, 0.2, 0.3, 0.4, 0.5];
    
    let features = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        &action_history,
        &reward_history,
    ).await.unwrap();
    
    // Should accept the configured history length
    assert_eq!(features.action_history.len(), 5);
    assert_eq!(features.reward_history.len(), 5);
}

#[tokio::test]
async fn test_ppo_empty_history_handling() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let action_history = Vec::new(); // Empty history
    let reward_history = Vec::new();
    
    let result = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        &action_history,
        &reward_history,
    ).await;
    
    // Should handle empty history gracefully
    assert!(result.is_ok());
    let features = result.unwrap();
    
    assert!(features.action_history.is_empty());
    assert!(features.reward_history.is_empty());
    assert!(features.advantage_estimate.is_finite());
    assert!(features.policy_entropy >= 0.0);
}

#[tokio::test]
async fn test_ppo_mismatched_history_lengths() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let action_history = vec![0.1, 0.2, 0.3]; // 3 elements
    let reward_history = vec![0.1, 0.2, 0.3, 0.4, 0.5]; // 5 elements
    
    let result = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        &action_history,
        &reward_history,
    ).await;
    
    // Should handle mismatched lengths (implementation dependent)
    match result {
        Ok(features) => {
            assert_eq!(features.action_history.len(), 3);
            assert_eq!(features.reward_history.len(), 5);
        }
        Err(_) => (), // Acceptable to reject mismatched lengths
    }
}

#[tokio::test]
async fn test_ppo_performance_with_long_history() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    
    // Create long action/reward histories
    let action_history: Vec<f64> = (0..100).map(|i| (i as f64) / 100.0 - 0.5).collect();
    let reward_history: Vec<f64> = (0..100).map(|i| (i as f64 % 10) as f64 / 10.0).collect();
    
    let start_time = Instant::now();
    
    let result = extractor.extract_ppo_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        &action_history,
        &reward_history,
    ).await;
    
    let elapsed = start_time.elapsed();
    
    assert!(result.is_ok());
    // Should handle long histories efficiently
    assert!(elapsed.as_millis() < 1000);
}

// 6. Liquid Networks Feature Tests (8 tests)

#[tokio::test]
async fn test_liquid_features_basic_extraction() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(150);
    
    let result = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await;
    
    assert!(result.is_ok());
    let features = result.unwrap();
    
    // Verify Liquid Networks-specific features
    assert!(features.fast_adaptation_signal.is_finite());
    assert!(features.slow_adaptation_signal.is_finite());
    assert!(features.regime_change_signal.is_finite());
    assert!(features.adaptation_rate >= 0.0);
    assert!(features.causal_strength.is_finite());
    assert!(features.information_flow.is_finite());
    assert!(features.network_centrality.is_finite());
}

#[tokio::test]
async fn test_liquid_adaptive_time_constants() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    
    // Create data with different adaptation patterns
    let mut historical_data = Vec::new();
    let base_time = Utc::now() - Duration::minutes(150);
    
    for i in 0..150 {
        let timestamp = base_time + Duration::minutes(i as i64);
        // Simulate regime changes at different frequencies
        let fast_signal = (i as f64 * 0.5).sin(); // High frequency
        let slow_signal = (i as f64 * 0.05).sin(); // Low frequency
        let price = Price::from_f64(100.0 + fast_signal + slow_signal).unwrap();
        
        historical_data.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size: Volume::from_u64(1000).unwrap(),
            side: TradeSide::Buy,
        });
    }
    
    let features = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Fast and slow adaptation signals should be different
    assert!(features.fast_adaptation_signal.is_finite());
    assert!(features.slow_adaptation_signal.is_finite());
    
    // Adaptation rate should be positive
    assert!(features.adaptation_rate >= 0.0);
}

#[tokio::test]
async fn test_liquid_regime_change_detection() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    
    // Create data with clear regime change
    let mut historical_data = Vec::new();
    let base_time = Utc::now() - Duration::minutes(150);
    
    for i in 0..150 {
        let timestamp = base_time + Duration::minutes(i as i64);
        let regime = if i < 75 { 0.01 } else { 0.1 }; // Volatility regime change
        let noise = (i as f64 * 0.1).sin() * regime;
        let price = Price::from_f64(100.0 + noise).unwrap();
        
        historical_data.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size: Volume::from_u64(1000).unwrap(),
            side: TradeSide::Buy,
        });
    }
    
    let features = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Regime change signal should be calculated
    assert!(features.regime_change_signal.is_finite());
}

#[tokio::test]
async fn test_liquid_causal_discovery() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(10); // Multiple news events
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Causal discovery features should be calculated
    assert!(features.causal_strength.is_finite());
    assert!(features.information_flow.is_finite());
    
    // Causal strength should be in reasonable range
    assert!(features.causal_strength >= -1.0);
    assert!(features.causal_strength <= 1.0);
}

#[tokio::test]
async fn test_liquid_information_flow() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    
    // Create data with clear directional information flow
    let mut historical_data = Vec::new();
    let base_time = Utc::now() - Duration::minutes(150);
    
    for i in 0..150 {
        let timestamp = base_time + Duration::minutes(i as i64);
        // Information flows from past to future
        let information = (i as f64).ln() * 0.01; // Logarithmic growth pattern
        let price = Price::from_f64(100.0 + information).unwrap();
        
        historical_data.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size: Volume::from_u64(1000).unwrap(),
            side: TradeSide::Buy,
        });
    }
    
    let features = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Information flow should be calculated
    assert!(features.information_flow.is_finite());
}

#[tokio::test]
async fn test_liquid_network_centrality() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
    ).await.unwrap();
    
    // Network centrality should be calculated
    assert!(features.network_centrality.is_finite());
    assert!(features.network_centrality >= 0.0); // Centrality should be non-negative
}

#[tokio::test]
async fn test_liquid_adaptive_learning_rates() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    
    // Create data with different stability patterns
    let stable_data = create_test_market_ticks(150); // Stable pattern
    
    let mut volatile_data = Vec::new();
    let base_time = Utc::now() - Duration::minutes(150);
    
    for i in 0..150 {
        let timestamp = base_time + Duration::minutes(i as i64);
        let volatility = (i as f64 * 0.3).sin() * 5.0; // High volatility
        let price = Price::from_f64(100.0 + volatility).unwrap();
        
        volatile_data.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size: Volume::from_u64(1000).unwrap(),
            side: TradeSide::Buy,
        });
    }
    
    let stable_features = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &stable_data,
    ).await.unwrap();
    
    let volatile_features = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &volatile_data,
    ).await.unwrap();
    
    // Both should have valid adaptation rates
    assert!(stable_features.adaptation_rate >= 0.0);
    assert!(volatile_features.adaptation_rate >= 0.0);
    
    // Adaptation rates might be different based on data characteristics
    assert!(stable_features.adaptation_rate.is_finite());
    assert!(volatile_features.adaptation_rate.is_finite());
}

#[tokio::test]
async fn test_liquid_cross_modal_causal_analysis() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    
    // Create news data with strong sentiment
    let mut strong_news_data = create_test_benzinga_data(5);
    for sentiment_score in &mut strong_news_data.sentiment_scores {
        sentiment_score.score = 0.9; // Strong positive sentiment
    }
    
    let historical_data = create_test_market_ticks(150);
    
    let features = extractor.extract_liquid_features(
        &symbol,
        &databento_data,
        &strong_news_data,
        &historical_data,
    ).await.unwrap();
    
    // Cross-modal causal analysis should detect relationships
    assert!(features.causal_strength.is_finite());
    
    // With strong news sentiment, causal strength might be higher
    // (implementation dependent)
    assert!(features.causal_strength >= -1.0);
    assert!(features.causal_strength <= 1.0);
}

// 7. TFT Feature Extraction Tests (10 tests)

#[tokio::test]
async fn test_tft_features_basic_extraction() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(200);
    let forecast_horizon = 24;
    
    let result = extractor.extract_tft_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        forecast_horizon,
    ).await;
    
    assert!(result.is_ok());
    let features = result.unwrap();
    
    // Verify TFT-specific sequences
    assert_eq!(features.observed_sequence.len(), extractor.config.tft_encoder_length);
    assert_eq!(features.known_future.len(), forecast_horizon);
    assert_eq!(features.static_metadata.len(), 10); // Default metadata vector size
    assert_eq!(features.temporal_patterns.len(), 24); // Hourly patterns
    assert_eq!(features.seasonal_components.len(), 12); // Monthly seasonality
    assert_eq!(features.forecast_horizon, forecast_horizon);
}

#[tokio::test]
async fn test_tft_observed_sequence_generation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(250);
    let forecast_horizon = 12;
    
    let features = extractor.extract_tft_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        forecast_horizon,
    ).await.unwrap();
    
    // Observed sequence should contain historical observations
    assert_eq!(features.observed_sequence.len(), 192); // Default encoder length
    
    // Should contain valid price values from historical data
    let valid_observations = features.observed_sequence.iter()
        .take(historical_data.len().min(192))
        .filter(|&&x| x > 0.0)
        .count();
    
    assert!(valid_observations > 0);
}

#[tokio::test]
async fn test_tft_known_future_sequence() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(200);
    
    let forecast_horizons = vec![6, 12, 24, 48];
    
    for horizon in forecast_horizons {
        let features = extractor.extract_tft_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            horizon,
        ).await.unwrap();
        
        // Known future should match forecast horizon
        assert_eq!(features.known_future.len(), horizon);
        assert_eq!(features.forecast_horizon, horizon);
        
        // All values should be finite
        for &val in &features.known_future {
            assert!(val.is_finite());
        }
    }
}

#[tokio::test]
async fn test_tft_static_metadata_generation() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(200);
    let forecast_horizon = 24;
    
    let features = extractor.extract_tft_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        forecast_horizon,
    ).await.unwrap();
    
    // Static metadata should be symbol-specific
    assert_eq!(features.static_metadata.len(), 10);
    
    // All metadata values should be finite
    for &val in &features.static_metadata {
        assert!(val.is_finite());
    }
}

#[tokio::test]
async fn test_tft_temporal_patterns() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    
    // Create data with clear temporal patterns
    let mut historical_data = Vec::new();
    let base_time = Utc::now() - Duration::hours(24);
    
    for i in 0..24*6 { // 6 data points per hour for 24 hours
        let timestamp = base_time + Duration::minutes(i as i64 * 10);
        let hour = timestamp.hour() as f64;
        let hourly_pattern = (hour * 2.0 * std::f64::consts::PI / 24.0).sin(); // Daily pattern
        let price = Price::from_f64(100.0 + hourly_pattern).unwrap();
        
        historical_data.push(MarketTick {
            timestamp,
            symbol: Symbol::new("AAPL").unwrap(),
            price,
            size: Volume::from_u64(1000).unwrap(),
            side: TradeSide::Buy,
        });
    }
    
    let features = extractor.extract_tft_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        24,
    ).await.unwrap();
    
    // Temporal patterns should capture hourly cycles
    assert_eq!(features.temporal_patterns.len(), 24);
    
    // All pattern values should be finite
    for &val in &features.temporal_patterns {
        assert!(val.is_finite());
    }
}

#[tokio::test]
async fn test_tft_seasonal_decomposition() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(200);
    let forecast_horizon = 12;
    
    let features = extractor.extract_tft_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        forecast_horizon,
    ).await.unwrap();
    
    // Seasonal components should represent monthly patterns
    assert_eq!(features.seasonal_components.len(), 12);
    
    // All seasonal values should be finite
    for &val in &features.seasonal_components {
        assert!(val.is_finite());
    }
}

#[tokio::test]
async fn test_tft_encoder_decoder_configuration() {
    let mut config = create_test_config();
    config.tft_encoder_length = 96; // Custom encoder length
    config.tft_decoder_length = 12; // Custom decoder length
    
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(150);
    let forecast_horizon = 6; // Shorter than decoder length
    
    let features = extractor.extract_tft_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        forecast_horizon,
    ).await.unwrap();
    
    // Should use custom encoder length
    assert_eq!(features.observed_sequence.len(), 96);
    
    // Forecast horizon should match input
    assert_eq!(features.known_future.len(), 6);
    assert_eq!(features.forecast_horizon, 6);
}

#[tokio::test]
async fn test_tft_attention_mechanism_inputs() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(200);
    let forecast_horizon = 24;
    
    let features = extractor.extract_tft_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        forecast_horizon,
    ).await.unwrap();
    
    // Attention mechanism should have proper input dimensions
    assert!(!features.temporal_patterns.is_empty());
    assert!(!features.seasonal_components.is_empty());
    assert!(!features.observed_sequence.is_empty());
    assert!(!features.static_metadata.is_empty());
}

#[tokio::test]
async fn test_tft_multi_horizon_forecasting() {
    let config = create_test_config();
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(3);
    let historical_data = create_test_market_ticks(200);
    
    // Test different forecast horizons
    let horizons = vec![1, 6, 12, 24, 48];
    
    for horizon in horizons {
        let features = extractor.extract_tft_features(
            &symbol,
            &databento_data,
            &benzinga_data,
            &historical_data,
            horizon,
        ).await.unwrap();
        
        // Each horizon should generate appropriate future sequence
        assert_eq!(features.forecast_horizon, horizon);
        assert_eq!(features.known_future.len(), horizon);
        
        // Observed sequence should remain constant
        assert_eq!(features.observed_sequence.len(), 192);
    }
}

#[tokio::test]
async fn test_tft_large_sequence_performance() {
    let mut config = create_test_config();
    config.tft_encoder_length = 500; // Large encoder
    
    let mut extractor = UnifiedFeatureExtractor::new(config);
    
    let symbol = Symbol::new("AAPL").unwrap();
    let databento_data = create_test_databento_data();
    let benzinga_data = create_test_benzinga_data(5);
    let historical_data = create_test_market_ticks(600); // More data than encoder length
    let forecast_horizon = 50;
    
    let start_time = Instant::now();
    
    let result = extractor.extract_tft_features(
        &symbol,
        &databento_data,
        &benzinga_data,
        &historical_data,
        forecast_horizon,
    ).await;
    
    let elapsed = start_time.elapsed();
    
    assert!(result.is_ok());
    
    let features = result.unwrap();
    assert_eq!(features.observed_sequence.len(), 500);
    
    // Should handle large sequences efficiently
    assert!(elapsed.as_millis() < 1000);
}

// Property-based tests
proptest! {
    #[test]
    fn test_unified_config_property_invariants(
        min_data_points in 1usize..1000usize,
        max_missing_ratio in 0.0f64..1.0f64,
        tlob_length in 10usize..200usize,
        mamba_length in 50usize..500usize
    ) {
        let config = UnifiedConfig {
            min_data_points,
            max_missing_ratio,
            outlier_threshold: 3.0,
            short_window: Duration::from_secs(300),
            medium_window: Duration::from_secs(3600),
            long_window: Duration::from_secs(14400),
            tlob_sequence_length: tlob_length,
            mamba_sequence_length: mamba_length,
            dqn_history_length: 10,
            ppo_history_length: 10,
            tft_encoder_length: 192,
            tft_decoder_length: 24,
            enable_simd: true,
            parallel_processing: true,
            cache_intermediate_results: true,
        };
        
        prop_assert!(config.min_data_points > 0);
        prop_assert!(config.max_missing_ratio >= 0.0 && config.max_missing_ratio <= 1.0);
        prop_assert!(config.tlob_sequence_length >= 10);
        prop_assert!(config.mamba_sequence_length >= 50);
        prop_assert!(config.mamba_sequence_length >= config.tlob_sequence_length);
    }
    
    #[test] 
    fn test_market_tick_invariants(
        price in 0.01f64..10000.0f64,
        size in 1u64..1000000u64
    ) {
        let symbol = Symbol::new("TEST").unwrap();
        let price_obj = Price::from_f64(price).unwrap();
        let volume_obj = Volume::from_u64(size).unwrap();
        
        let tick = MarketTick {
            timestamp: Utc::now(),
            symbol: symbol.clone(),
            price: price_obj,
            size: volume_obj,
            side: TradeSide::Buy,
        };
        
        prop_assert_eq!(tick.symbol, symbol);
        prop_assert!(tick.price.to_f64() > 0.0);
        prop_assert!(tick.size.to_u64() > 0);
        prop_assert!(tick.timestamp <= Utc::now());
    }
}