//! # Feature Engineering for Financial ML Models
//!
//! Comprehensive feature engineering pipeline for HFT trading systems that transforms
//! raw market data into meaningful features for machine learning models. This module
//! provides a complete toolkit for quantitative finance feature extraction.
//!
//! ## Core Components
//!
//! ### Technical Indicators
//! - **Moving Averages**: Simple (SMA) and Exponential (EMA) moving averages
//! - **Momentum**: RSI, MACD, momentum oscillators
//! - **Volatility**: Bollinger Bands, Average True Range
//! - **Volume**: Volume-weighted indicators and flow analysis
//!
//! ### Market Microstructure Features
//! - **Spreads**: Bid-ask spread analysis and Roll spread estimation
//! - **Imbalances**: Volume and order book imbalances
//! - **Price Impact**: Kyle's lambda, Amihud illiquidity ratio
//! - **Liquidity**: Market depth and liquidity scoring
//!
//! ### TLOB (Time-Limited Order Book) Features
//! - **Order Flow**: Order flow imbalance and directional analysis
//! - **Book Dynamics**: Order book shape and dynamics
//! - **Execution Quality**: Slippage and execution cost analysis
//!
//! ### Temporal Features
//! - **Cyclical**: Hour of day, day of week patterns
//! - **Market Sessions**: Pre-market, regular hours, after-market
//! - **Calendar Effects**: Month-end, quarter-end, holiday effects
//!
//! ### Portfolio & Risk Features
//! - **Performance**: P&L tracking, Sharpe ratio, Sortino ratio
//! - **Risk Metrics**: VaR, Expected Shortfall, Maximum Drawdown
//! - **Exposure**: Beta, correlation analysis
//!
//! ## Architecture
//!
//! ```text
//! ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
//! │   Raw Market    │────│  Feature        │────│   ML Model      │
//! │   Data          │    │  Engineering    │    │   Input         │
//! └─────────────────┘    └─────────────────┘    └─────────────────┘
//!          │                       │                       │
//!          ▼                       ▼                       ▼
//! ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
//! │   Tick Data     │    │   Technical     │    │   Feature       │
//! │   Order Books   │────│   Indicators    │────│   Vectors       │
//! │   Trade Flow    │    │   Microstructure│    │   (Normalized)  │
//! └─────────────────┘    └─────────────────┘    └─────────────────┘
//! ```
//!
//! ## Performance Considerations
//!
//! - **Streaming Updates**: Incremental calculations for real-time processing
//! - **Memory Efficiency**: Rolling windows with automatic cleanup
//! - **SIMD Optimization**: Vectorized computations where possible
//! - **Caching**: Intelligent caching of intermediate calculations
//!
//! ## Usage Examples
//!
//! ```rust
//! use data::features::{
//!     TechnicalIndicators, MicrostructureAnalyzer, TemporalFeatures,
//!     FeatureVector, PricePoint
//! };
//! use config::data_config::TechnicalIndicatorsConfig;
//! use chrono::Utc;
//!
//! // Initialize technical indicators
//! let config = TechnicalIndicatorsConfig {
//!     ma_periods: vec![10, 20, 50],
//!     rsi_periods: vec![14],
//!     bollinger_periods: vec![20],
//!     // ... other config
//! };
//!
//! let mut indicators = TechnicalIndicators::new(config);
//!
//! // Update with new price data
//! let price_point = PricePoint {
//!     timestamp: Utc::now(),
//!     open: 100.0,
//!     high: 101.5,
//!     low: 99.5,
//!     close: 101.0,
//! };
//!
//! indicators.update_price("AAPL", price_point);
//!
//! // Extract features
//! let tech_features = indicators.calculate_features("AAPL");
//! let temporal_features = TemporalFeatures::extract_features(Utc::now());
//!
//! // Combine into feature vector
//! let mut all_features = tech_features;
//! all_features.extend(temporal_features);
//!
//! let feature_vector = FeatureVector {
//!     timestamp: Utc::now(),
//!     symbol: "AAPL".to_string(),
//!     features: all_features,
//!     metadata: FeatureMetadata::default(),
//! };
//! ```
//!
//! ## Feature Categories
//!
//! Features are organized into categories for better model interpretation:
//!
//! - **Price**: OHLC-based features and price transformations
//! - **Volume**: Volume-based indicators and flow analysis
//! - **TechnicalIndicator**: Traditional TA indicators (RSI, MACD, etc.)
//! - **Microstructure**: Market microstructure and liquidity features
//! - **Temporal**: Time-based features and calendar effects
//! - **Regime**: Market regime and volatility state features
//! - **TLOB**: Time-Limited Order Book specific features
//! - **Portfolio**: Portfolio-level performance and risk metrics
//! - **Risk**: Risk management and exposure metrics

use chrono::{DateTime, Datelike, Timelike, Utc};
use config::data_config::{
    DataMicrostructureConfig as MicrostructureConfig, DataTLOBConfig as TLOBConfig,
    DataTechnicalIndicatorsConfig as TechnicalIndicatorsConfig,
};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap, VecDeque};

/// Feature vector for ML model training and inference.
///
/// Represents a complete set of features extracted from market data at a specific
/// point in time. This is the primary data structure used to feed machine learning
/// models in the trading system.
///
/// # Structure
///
/// - **Timestamp**: When these features were calculated
/// - **Symbol**: The financial instrument these features apply to
/// - **Features**: Key-value map of feature names to numerical values
/// - **Metadata**: Additional information about feature quality and categories
///
/// # Feature Organization
///
/// Features are stored as a flat HashMap for efficient access, but can be
/// categorized using the metadata for model interpretation and debugging.
///
/// # Normalization
///
/// Features should be normalized before training ML models. The feature
/// engineering pipeline can apply various normalization techniques:
/// - Z-score normalization (mean=0, std=1)
/// - Min-max scaling (0-1 range)
/// - Robust scaling (using percentiles)
///
/// # Examples
///
/// ```rust
/// use data::features::{FeatureVector, FeatureMetadata, FeatureCategory};
/// use std::collections::HashMap;
/// use chrono::Utc;
///
/// let mut features = HashMap::new();
/// features.insert("sma_20".to_string(), 150.25);
/// features.insert("rsi_14".to_string(), 65.8);
/// features.insert("volume_ratio".to_string(), 1.2);
///
/// let feature_vector = FeatureVector {
///     timestamp: Utc::now(),
///     symbol: "AAPL".to_string(),
///     features,
///     metadata: FeatureMetadata::default(),
/// };
///
/// // Access specific features
/// let sma_value = feature_vector.features.get("sma_20").unwrap();
/// assert_eq!(*sma_value, 150.25);
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeatureVector {
    /// Timestamp when these features were calculated
    ///
    /// UTC timestamp indicating the exact time these features represent.
    /// Critical for time-series analysis and ensuring proper temporal ordering.
    pub timestamp: DateTime<Utc>,

    /// Financial instrument symbol (ticker)
    ///
    /// The security identifier (e.g., "AAPL", "SPY", "EURUSD") that these
    /// features were calculated for. Used for symbol-specific model training.
    pub symbol: String,

    /// Feature name-value pairs
    ///
    /// Map of feature names to their calculated numerical values.
    /// Feature names should be descriptive and consistent across time
    /// (e.g., "sma_20", "rsi_14", "bid_ask_spread_bps").
    pub features: HashMap<String, f64>,

    /// Feature metadata and quality information
    ///
    /// Additional information about the features including descriptions,
    /// categories, and data quality indicators.
    pub metadata: FeatureMetadata,
}

/// Metadata and quality information for feature vectors.
///
/// Provides additional context about features including descriptions,
/// categorization, and data quality metrics. Essential for feature
/// interpretation, model debugging, and data quality monitoring.
///
/// # Quality Indicators
///
/// Quality indicators help identify potential data issues:
/// - **Completeness**: Percentage of non-null values (0.0-1.0)
/// - **Freshness**: How recent the underlying data is (0.0-1.0)
/// - **Reliability**: Confidence in data accuracy (0.0-1.0)
/// - **Stability**: Variance stability over time (0.0-1.0)
///
/// # Feature Categories
///
/// Categorization helps with:
/// - Feature selection and importance analysis
/// - Model interpretation and explainability
/// - Feature engineering pipeline organization
/// - Regulatory compliance and audit trails
///
/// # Examples
///
/// ```rust
/// use data::features::{FeatureMetadata, FeatureCategory};
/// use std::collections::HashMap;
///
/// let mut descriptions = HashMap::new();
/// descriptions.insert("sma_20".to_string(), "20-period Simple Moving Average".to_string());
/// descriptions.insert("rsi_14".to_string(), "14-period Relative Strength Index".to_string());
///
/// let mut categories = HashMap::new();
/// categories.insert("sma_20".to_string(), FeatureCategory::TechnicalIndicator);
/// categories.insert("rsi_14".to_string(), FeatureCategory::TechnicalIndicator);
///
/// let mut quality = HashMap::new();
/// quality.insert("sma_20".to_string(), 0.98); // 98% data completeness
/// quality.insert("rsi_14".to_string(), 0.95); // 95% data completeness
///
/// let metadata = FeatureMetadata {
///     feature_descriptions: descriptions,
///     feature_categories: categories,
///     quality_indicators: quality,
/// };
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeatureMetadata {
    /// Human-readable descriptions of each feature
    ///
    /// Maps feature names to their detailed descriptions explaining
    /// what the feature represents and how it's calculated.
    /// Essential for model documentation and interpretation.
    pub feature_descriptions: HashMap<String, String>,

    /// Categorical classification of features
    ///
    /// Maps feature names to their category types for organization
    /// and analysis. Helps with feature selection and model interpretation.
    pub feature_categories: HashMap<String, FeatureCategory>,

    /// Data quality metrics for each feature (0.0-1.0)
    ///
    /// Maps feature names to quality scores indicating reliability,
    /// completeness, and freshness of the underlying data.
    /// Used for automated quality monitoring and alerts.
    pub quality_indicators: HashMap<String, f64>,

    /// Symbol these features belong to (for tests)
    pub symbol: String,

    /// Timestamp when metadata was created (for tests)
    pub timestamp: DateTime<Utc>,

    /// Total count of features (for tests)
    pub feature_count: usize,

    /// List of categories present (for tests)
    pub categories: Vec<FeatureCategory>,
}

/// Categorical classification system for organizing features.
///
/// Provides a hierarchical way to organize features based on their
/// data source, calculation method, and business purpose. This
/// categorization is essential for:
///
/// - **Feature Selection**: Group-based importance analysis
/// - **Model Interpretation**: Understanding feature contributions by category
/// - **Data Lineage**: Tracking feature dependencies and data sources
/// - **Regulatory Compliance**: Documenting model inputs by data type
/// - **Performance Monitoring**: Category-specific quality metrics
///
/// # Category Descriptions
///
/// - **Price**: Features derived from OHLC price data
/// - **Volume**: Features based on trading volume and turnover
/// - **TechnicalIndicator**: Traditional technical analysis indicators
/// - **Microstructure**: Market microstructure and liquidity metrics
/// - **Temporal**: Time-based features and calendar effects
/// - **Regime**: Market regime and volatility state indicators
/// - **TLOB**: Time-Limited Order Book specific features
/// - **Portfolio**: Portfolio-level performance and allocation metrics
/// - **Risk**: Risk management and exposure indicators
///
/// # Usage in Feature Selection
///
/// ```rust
/// use data::features::{FeatureCategory, FeatureMetadata};
/// use std::collections::HashMap;
///
/// fn filter_technical_indicators(metadata: &FeatureMetadata) -> Vec<String> {
///     metadata.feature_categories
///         .iter()
///         .filter(|(_, category)| matches!(category, FeatureCategory::TechnicalIndicator))
///         .map(|(name, _)| name.clone())
///         .collect()
/// }
/// ```
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum FeatureCategory {
    /// Price-based features (OHLC, returns, price ratios)
    ///
    /// Features derived directly from price data including:
    /// - Open, High, Low, Close prices and their transformations
    /// - Price returns and log returns
    /// - Price ratios and relative price movements
    /// - Gap analysis and price range metrics
    Price,

    /// Volume-based features (volume, turnover, VWAP)
    ///
    /// Features calculated from trading volume data including:
    /// - Raw volume and volume ratios
    /// - Volume-weighted average price (VWAP)
    /// - Volume rate of change
    /// - Dollar volume and turnover metrics
    Volume,

    /// Traditional technical analysis indicators
    ///
    /// Classic technical indicators including:
    /// - Moving averages (SMA, EMA, WMA)
    /// - Momentum indicators (RSI, MACD, Stochastic)
    /// - Volatility indicators (Bollinger Bands, ATR)
    /// - Trend indicators (ADX, Parabolic SAR)
    TechnicalIndicator,

    /// Market microstructure and liquidity features
    ///
    /// Features related to market structure and liquidity including:
    /// - Bid-ask spreads and spread components
    /// - Order book imbalances and depth
    /// - Price impact measures (Kyle's lambda, Amihud ratio)
    /// - Trade classification and flow analysis
    Microstructure,

    /// Time-based and calendar features
    ///
    /// Features derived from timestamps and calendar patterns:
    /// - Hour of day, day of week effects
    /// - Market session indicators (pre-market, regular hours)
    /// - Calendar effects (month-end, quarter-end, holidays)
    /// - Seasonal and cyclical patterns
    Temporal,

    /// Market regime and volatility state features
    ///
    /// Features that capture market regime changes:
    /// - Volatility regime indicators
    /// - Trend vs. mean-reverting states
    /// - Correlation regime changes
    /// - Market stress indicators
    Regime,

    /// Time-Limited Order Book (TLOB) specific features
    ///
    /// Features derived from order book dynamics:
    /// - Order flow imbalances
    /// - Book shape and slope analysis
    /// - Order arrival and cancellation patterns
    /// - Liquidity provision patterns
    TLOB,

    /// Portfolio-level performance and allocation features
    ///
    /// Features calculated at the portfolio level:
    /// - Portfolio returns and risk metrics
    /// - Sector and style allocations
    /// - Concentration and diversification measures
    /// - Performance attribution factors
    Portfolio,

    /// Risk management and exposure features
    ///
    /// Features related to risk measurement and control:
    /// - Value at Risk (VaR) estimates
    /// - Maximum drawdown metrics
    /// - Exposure concentrations
    /// - Correlation and beta measurements
    Risk,
}

/// Technical indicators calculator for financial time series analysis.
///
/// Provides a comprehensive suite of technical analysis indicators commonly used
/// in quantitative trading and machine learning models. Supports streaming
/// calculations with automatic data management and efficient memory usage.
///
/// # Supported Indicators
///
/// - **Moving Averages**: Simple (SMA) and Exponential (EMA) moving averages
/// - **Momentum**: Relative Strength Index (RSI) with configurable periods
/// - **MACD**: Moving Average Convergence Divergence with signal line
/// - **Bollinger Bands**: Price bands with standard deviation channels
/// - **Volume Indicators**: Volume-based technical indicators
///
/// # Performance Features
///
/// - **Streaming Updates**: Incremental calculations for real-time processing
/// - **Memory Management**: Automatic cleanup of old data based on largest period
/// - **Multiple Timeframes**: Support for different periods simultaneously
/// - **State Persistence**: Maintains indicator state for continuous calculations
///
/// # Configuration
///
/// The calculator is configured via `TechnicalIndicatorsConfig` which specifies:
/// - Moving average periods (e.g., 10, 20, 50, 200)
/// - RSI calculation periods (e.g., 14, 21)
/// - Bollinger Bands periods and standard deviations
/// - MACD parameters (fast, slow, signal periods)
///
/// # Examples
///
/// ```rust
/// use data::features::{TechnicalIndicators, PricePoint};
/// use config::data_config::TechnicalIndicatorsConfig;
/// use chrono::Utc;
///
/// let config = TechnicalIndicatorsConfig {
///     ma_periods: vec![10, 20, 50],
///     rsi_periods: vec![14],
///     bollinger_periods: vec![20],
///     // ... other configuration
/// };
///
/// let mut indicators = TechnicalIndicators::new(config);
///
/// // Add price data
/// let price_point = PricePoint {
///     timestamp: Utc::now(),
///     open: 100.0,
///     high: 102.0,
///     low: 99.0,
///     close: 101.5,
/// };
///
/// indicators.update_price("AAPL", price_point);
///
/// // Calculate all features
/// let features = indicators.calculate_features("AAPL");
///
/// // Access specific indicators
/// if let Some(sma_20) = features.get("sma_20") {
///     println!("20-period SMA: {}", sma_20);
/// }
/// ```
///
/// # Data Requirements
///
/// Different indicators have different minimum data requirements:
/// - **SMA/EMA**: Requires at least 'period' data points
/// - **RSI**: Requires at least 'period + 1' data points
/// - **MACD**: Requires sufficient data for slow EMA calculation
/// - **Bollinger Bands**: Requires at least 'period' data points
///
/// Features will be omitted from output until sufficient data is available.
pub struct TechnicalIndicators {
    pub config: TechnicalIndicatorsConfig,
    pub price_data: BTreeMap<String, VecDeque<PricePoint>>,
    pub volume_data: BTreeMap<String, VecDeque<VolumePoint>>,
    pub indicators: BTreeMap<String, IndicatorState>,
}

/// OHLC price data point for technical indicator calculations.
///
/// Represents a single price observation with open, high, low, and close prices
/// along with a timestamp. This is the fundamental data unit for all technical
/// indicator calculations in the system.
///
/// # Data Integrity
///
/// Price points should satisfy basic integrity constraints:
/// - High >= max(Open, Close)
/// - Low <= min(Open, Close)
/// - All prices should be positive
/// - Timestamps should be in chronological order
///
/// # Usage in Indicators
///
/// Different indicators use different price components:
/// - **Moving Averages**: Typically use Close price
/// - **Bollinger Bands**: Use Close price for center line
/// - **ATR**: Uses High, Low, and previous Close
/// - **Price Channels**: Use High and Low prices
///
/// # Examples
///
/// ```rust
/// use data::features::PricePoint;
/// use chrono::Utc;
///
/// let price_point = PricePoint {
///     timestamp: Utc::now(),
///     open: 100.0,
///     high: 102.5,  // Must be >= max(open, close)
///     low: 99.0,    // Must be <= min(open, close)
///     close: 101.5,
/// };
///
/// // Validate price point integrity
/// assert!(price_point.high >= price_point.open.max(price_point.close));
/// assert!(price_point.low <= price_point.open.min(price_point.close));
/// ```
///
/// # Time Series Usage
///
/// When building time series for indicators:
/// - Maintain chronological order by timestamp
/// - Handle gaps in data appropriately
/// - Consider timezone consistency (use UTC)
/// - Validate price continuity and outliers
#[derive(Debug, Clone)]
pub struct PricePoint {
    /// Timestamp of this price observation
    ///
    /// UTC timestamp indicating when this price data represents.
    /// Should be consistent with the timeframe being analyzed.
    pub timestamp: DateTime<Utc>,

    /// Opening price for the period
    ///
    /// The first traded price during the time period.
    /// For continuous markets, this is the first price after the previous close.
    pub open: f64,

    /// Highest price during the period
    ///
    /// Must be greater than or equal to both open and close prices.
    /// Used in volatility and range-based indicators.
    pub high: f64,

    /// Lowest price during the period
    ///
    /// Must be less than or equal to both open and close prices.
    /// Used in volatility and support/resistance analysis.
    pub low: f64,

    /// Closing price for the period
    ///
    /// The last traded price during the time period.
    /// Most commonly used price in technical indicators.
    pub close: f64,
}

/// Volume data point for volume-based indicator calculations.
///
/// Represents trading volume information for a specific time period,
/// including both raw volume and volume-weighted average price (VWAP).
/// Essential for volume-based technical indicators and market analysis.
///
/// # Volume Metrics
///
/// - **Volume**: Total number of shares/contracts traded
/// - **VWAP**: Volume-weighted average price for the period
/// - **Dollar Volume**: Volume * Average Price (derived)
///
/// # Applications
///
/// Volume data is used in:
/// - Volume moving averages and oscillators
/// - On-Balance Volume (OBV) calculations
/// - Volume Rate of Change (VROC)
/// - Accumulation/Distribution indicators
/// - Money Flow Index (MFI)
///
/// # Data Quality
///
/// Volume data should be validated for:
/// - Non-negative volume values
/// - Reasonable VWAP relative to price range
/// - Consistency with price data timestamps
/// - Outlier detection for unusual volume spikes
///
/// # Examples
///
/// ```rust
/// use data::features::VolumePoint;
/// use chrono::Utc;
///
/// let volume_point = VolumePoint {
///     timestamp: Utc::now(),
///     volume: 1_000_000.0,        // 1M shares traded
///     volume_weighted_price: 150.25, // VWAP for the period
/// };
///
/// // Calculate dollar volume
/// let dollar_volume = volume_point.volume * volume_point.volume_weighted_price;
/// assert_eq!(dollar_volume, 150_250_000.0); // $150.25M traded
/// ```
#[derive(Debug, Clone)]
pub struct VolumePoint {
    /// Timestamp of this volume observation
    ///
    /// UTC timestamp corresponding to the same period as the associated price data.
    pub timestamp: DateTime<Utc>,

    /// Total volume traded during the period
    ///
    /// Number of shares, contracts, or units traded. Should be non-negative
    /// and represent the total trading activity for the time period.
    pub volume: f64,

    /// Volume-weighted average price (VWAP) for the period
    ///
    /// The average price weighted by trading volume, calculated as:
    /// VWAP = Σ(Price × Volume) / Σ(Volume)
    /// Provides a more representative average price than simple arithmetic mean.
    pub volume_weighted_price: f64,

    /// Buy-side volume for the period (for tests)
    pub buy_volume: f64,

    /// Sell-side volume for the period (for tests)
    pub sell_volume: f64,
}

/// Internal state for maintaining technical indicator calculations.
///
/// Stores the current state of all technical indicators for a specific symbol,
/// enabling efficient incremental updates without recalculating from scratch.
/// This state persistence is crucial for real-time trading systems where
/// performance is critical.
///
/// # State Components
///
/// - **SMA**: Simple moving average values for different periods
/// - **EMA**: Exponential moving average values and their smoothing states
/// - **RSI**: Relative Strength Index values with gain/loss tracking
/// - **MACD**: Complete MACD state including EMA components
/// - **Bollinger**: Bollinger Bands state with statistics
///
/// # Memory Management
///
/// The state automatically manages memory by:
/// - Storing only current indicator values, not full history
/// - Using efficient data structures for O(1) updates
/// - Automatically cleaning up unused indicators
///
/// # Thread Safety
///
/// This structure is designed for single-threaded use per symbol.
/// For multi-threaded access, wrap in appropriate synchronization primitives.
///
/// # Examples
///
/// ```rust
/// use data::features::IndicatorState;
/// use std::collections::HashMap;
///
/// // State is typically managed internally by TechnicalIndicators
/// let mut state = IndicatorState {
///     sma: HashMap::new(),
///     ema: HashMap::new(),
///     rsi: HashMap::new(),
///     macd: Default::default(),
///     bollinger: HashMap::new(),
/// };
///
/// // Access specific indicator values
/// if let Some(sma_20) = state.sma.get(&20) {
///     println!("20-period SMA: {}", sma_20);
/// }
/// ```
#[derive(Debug, Clone)]
pub struct IndicatorState {
    /// Simple Moving Average values by period
    ///
    /// Maps period lengths to their current SMA values.
    /// Updated with each new price point using rolling window calculation.
    pub sma: HashMap<u32, f64>,

    /// Exponential Moving Average values by period
    ///
    /// Maps period lengths to their current EMA values.
    /// Maintains smoothing state for efficient incremental updates.
    pub ema: HashMap<u32, f64>,

    /// Relative Strength Index values by period
    ///
    /// Maps RSI periods to their current RSI values (0-100 scale).
    /// Internally tracks average gains and losses for calculation.
    pub rsi: HashMap<u32, f64>,

    /// MACD (Moving Average Convergence Divergence) state
    ///
    /// Complete MACD calculation state including MACD line, signal line,
    /// histogram, and underlying EMA components.
    pub macd: MACDState,

    /// Bollinger Bands state by period
    ///
    /// Maps periods to complete Bollinger Bands state including
    /// upper/lower bands, bandwidth, and %B calculations.
    pub bollinger: HashMap<u32, BollingerBandsState>,
}

/// MACD (Moving Average Convergence Divergence) indicator state.
///
/// Maintains the complete state for MACD calculation including the main MACD line,
/// signal line, histogram, and underlying exponential moving averages. MACD is
/// a trend-following momentum indicator that shows relationships between two
/// moving averages of prices.
///
/// # MACD Components
///
/// - **MACD Line**: Fast EMA - Slow EMA (typically 12-day - 26-day)
/// - **Signal Line**: EMA of MACD line (typically 9-day)
/// - **Histogram**: MACD Line - Signal Line
///
/// # Signal Interpretation
///
/// - **Bullish Signal**: MACD line crosses above signal line
/// - **Bearish Signal**: MACD line crosses below signal line
/// - **Momentum**: Histogram increasing = strengthening trend
/// - **Divergence**: MACD vs price divergence = potential reversal
///
/// # State Persistence
///
/// The state maintains the underlying EMA calculations to enable
/// efficient incremental updates without recalculating the entire history.
///
/// # Examples
///
/// ```rust
/// use data::features::MACDState;
///
/// let macd_state = MACDState {
///     macd_line: 1.25,      // Fast EMA - Slow EMA
///     signal_line: 0.95,    // EMA of MACD line
///     histogram: 0.30,      // MACD line - Signal line
///     fast_ema: 150.25,     // Current fast EMA value
///     slow_ema: 149.00,     // Current slow EMA value
///     signal_ema: 0.95,     // Signal line EMA value
/// };
///
/// // Check for bullish crossover
/// let is_bullish = macd_state.macd_line > macd_state.signal_line &&
///                  macd_state.histogram > 0.0;
/// ```
#[derive(Debug, Clone)]
pub struct MACDState {
    /// MACD line value (Fast EMA - Slow EMA)
    ///
    /// The main MACD indicator calculated as the difference between
    /// fast and slow exponential moving averages. Positive values
    /// indicate upward momentum, negative values indicate downward momentum.
    pub macd_line: f64,

    /// Signal line value (EMA of MACD line)
    ///
    /// The signal line is an exponential moving average of the MACD line,
    /// used to generate buy/sell signals through crossovers with the MACD line.
    pub signal_line: f64,

    /// MACD histogram (MACD line - Signal line)
    ///
    /// The difference between MACD line and signal line, displayed as
    /// a histogram. Shows the convergence and divergence of the two lines.
    pub histogram: f64,

    /// Last update timestamp (for tests)
    pub last_update: DateTime<Utc>,

    /// Current fast EMA value
    ///
    /// The faster exponential moving average component (typically 12-period).
    /// Maintained for efficient incremental calculation updates.
    pub fast_ema: f64,

    /// Current slow EMA value
    ///
    /// The slower exponential moving average component (typically 26-period).
    /// Maintained for efficient incremental calculation updates.
    pub slow_ema: f64,

    /// Current signal line EMA value
    ///
    /// The EMA used for the signal line calculation (typically 9-period).
    /// Maintained for efficient incremental calculation updates.
    pub signal_ema: f64,
}

/// Bollinger Bands indicator state and calculations.
///
/// Bollinger Bands consist of a middle line (moving average) and two bands
/// (upper and lower) that are standard deviations away from the middle line.
/// They are used to identify overbought/oversold conditions and volatility.
///
/// # Band Components
///
/// - **Upper Band**: Middle line + (2 × Standard Deviation)
/// - **Middle Band**: Simple Moving Average (typically 20-period)
/// - **Lower Band**: Middle line - (2 × Standard Deviation)
///
/// # Derived Metrics
///
/// - **Bandwidth**: (Upper Band - Lower Band) / Middle Band
/// - **%B**: (Price - Lower Band) / (Upper Band - Lower Band)
///
/// # Trading Signals
///
/// - **Bollinger Squeeze**: Low bandwidth indicates low volatility
/// - **Band Walking**: Price staying near upper/lower band indicates strong trend
/// - **Mean Reversion**: Price touching bands often reverses toward middle
/// - **Breakouts**: Price breaking outside bands can signal trend continuation
///
/// # Examples
///
/// ```rust
/// use data::features::BollingerBandsState;
///
/// let bb_state = BollingerBandsState {
///     upper_band: 152.50,
///     middle_band: 150.00,  // 20-period SMA
///     lower_band: 147.50,
///     bandwidth: 0.033,     // 3.3% bandwidth
///     percent_b: 0.75,      // Price at 75% of band range
/// };
///
/// // Check for squeeze condition (low volatility)
/// let is_squeeze = bb_state.bandwidth < 0.05; // Less than 5%
///
/// // Check overbought/oversold conditions
/// let is_overbought = bb_state.percent_b > 0.8;
/// let is_oversold = bb_state.percent_b < 0.2;
/// ```
#[derive(Debug, Clone)]
pub struct BollingerBandsState {
    /// Upper Bollinger Band (Middle + 2 × Std Dev)
    ///
    /// The upper boundary of the Bollinger Bands, typically set at
    /// 2 standard deviations above the middle line. Prices touching
    /// or exceeding this level may indicate overbought conditions.
    pub upper_band: f64,

    /// Middle Bollinger Band (Simple Moving Average)
    ///
    /// The center line of Bollinger Bands, typically a 20-period
    /// simple moving average. Acts as dynamic support/resistance.
    pub middle_band: f64,

    /// Lower Bollinger Band (Middle - 2 × Std Dev)
    ///
    /// The lower boundary of the Bollinger Bands, typically set at
    /// 2 standard deviations below the middle line. Prices touching
    /// or falling below this level may indicate oversold conditions.
    pub lower_band: f64,

    /// Bandwidth ratio ((Upper - Lower) / Middle)
    ///
    /// Measures the width of the bands relative to the middle band.
    /// Low bandwidth indicates low volatility ("squeeze"),
    /// high bandwidth indicates high volatility.
    pub bandwidth: f64,

    /// %B indicator ((Price - Lower) / (Upper - Lower))
    ///
    /// Shows where the price is relative to the bands:
    /// - 1.0 = At upper band
    /// - 0.5 = At middle band
    /// - 0.0 = At lower band
    /// Values outside 0-1 indicate price outside the bands.
    pub percent_b: f64,

    /// Last update timestamp (for tests)
    pub last_update: DateTime<Utc>,
}

/// Market microstructure analyzer for liquidity and trading cost analysis.
///
/// Analyzes the detailed structure of financial markets including bid-ask spreads,
/// order book dynamics, price impact, and liquidity measures. Essential for
/// high-frequency trading strategies and execution cost analysis.
///
/// # Microstructure Features
///
/// - **Spreads**: Bid-ask spread, effective spread, Roll spread
/// - **Imbalances**: Volume imbalance, order book imbalance
/// - **Price Impact**: Kyle's lambda, Amihud illiquidity ratio
/// - **Liquidity**: Market depth, liquidity scores
/// - **Trade Classification**: Buy/sell classification, trade direction
///
/// # Data Sources
///
/// The analyzer processes multiple data streams:
/// - **Order Books**: Level 1 and Level 2 market data
/// - **Trades**: Time and sales data with trade classification
/// - **Quotes**: Bid/ask quotes with sizes
///
/// # Applications
///
/// - **Execution Analysis**: Measuring transaction costs and market impact
/// - **Market Making**: Optimal spread setting and inventory management
/// - **Regime Detection**: Identifying changes in market liquidity conditions
/// - **Risk Management**: Liquidity risk assessment and monitoring
///
/// # Performance Considerations
///
/// - **Real-time Processing**: Designed for sub-millisecond latency
/// - **Memory Efficiency**: Rolling windows for historical calculations
/// - **Configurable Features**: Enable only needed calculations for performance
///
/// # Examples
///
/// ```rust
/// use data::features::{MicrostructureAnalyzer, QuoteData, TradeData};
/// use config::data_config::MicrostructureConfig;
/// use chrono::Utc;
///
/// let config = MicrostructureConfig {
///     bid_ask_spread: true,
///     volume_imbalance: true,
///     price_impact: true,
///     kyle_lambda: false,    // Computationally expensive
///     amihud_ratio: true,
///     // ... other settings
/// };
///
/// let mut analyzer = MicrostructureAnalyzer::new(config);
///
/// // Update with market data
/// let quote = QuoteData {
///     timestamp: Utc::now(),
///     bid: 149.95,
///     ask: 150.05,
///     bid_size: 1000.0,
///     ask_size: 800.0,
/// };
/// analyzer.update_quote("AAPL", quote);
///
/// // Calculate microstructure features
/// let features = analyzer.calculate_features("AAPL");
///
/// if let Some(spread_bps) = features.get("bid_ask_spread_bps") {
///     println!("Bid-ask spread: {} bps", spread_bps);
/// }
/// ```
pub struct MicrostructureAnalyzer {
    pub config: MicrostructureConfig,
    pub order_books: HashMap<String, OrderBookState>,
    pub trade_data: BTreeMap<String, VecDeque<TradeData>>,
    pub quote_data: BTreeMap<String, VecDeque<QuoteData>>,
}

/// Order book state snapshot for microstructure analysis.
///
/// Represents a point-in-time view of the order book including bids, asks,
/// and derived metrics like spread and imbalance. Used for calculating
/// market microstructure features and liquidity analysis.
///
/// # Order Book Metrics
///
/// - **Mid Price**: (Best Bid + Best Ask) / 2
/// - **Spread**: Best Ask - Best Bid (absolute)
/// - **Imbalance**: (Bid Size - Ask Size) / (Bid Size + Ask Size)
/// - **Depth**: Total volume available at best levels
///
/// # Data Quality
///
/// Order book states should be validated for:
/// - Best bid < Best ask (no crossed market)
/// - Positive sizes for all price levels
/// - Proper price level ordering (ascending asks, descending bids)
/// - Timestamp consistency with market data
///
/// # Applications
///
/// - **Spread Analysis**: Transaction cost estimation
/// - **Liquidity Assessment**: Available depth and market impact
/// - **Imbalance Signals**: Short-term price direction prediction
/// - **Market Making**: Optimal quote placement strategies
///
/// # Examples
///
/// ```rust
/// use data::features::OrderBookState;
/// use common::PriceLevel;
/// use chrono::Utc;
///
/// let order_book = OrderBookState {
///     timestamp: Utc::now(),
///     bids: vec![
///         PriceLevel { price: 149.95.into(), size: 1000.into() },
///         PriceLevel { price: 149.90.into(), size: 1500.into() },
///     ],
///     asks: vec![
///         PriceLevel { price: 150.05.into(), size: 800.into() },
///         PriceLevel { price: 150.10.into(), size: 1200.into() },
///     ],
///     mid_price: 150.00,
///     spread: 0.10,
///     imbalance: 0.111,  // (1000-800)/(1000+800)
///     depth: 1800.0,     // Total at best levels
/// };
/// ```
#[derive(Debug, Clone)]
pub struct OrderBookState {
    pub timestamp: DateTime<Utc>,
    pub best_bid: f64,
    pub best_ask: f64,
    pub bid_size: f64,
    pub ask_size: f64,
    pub spread: f64,
    pub mid_price: f64,
}

// PriceLevel moved to canonical source in common::types
// Note: Original used f64 types - code may need conversion to Decimal

/// Individual trade data for microstructure analysis.
///
/// Represents a single trade execution with price, size, direction, and timing
/// information. Used for calculating trade-based microstructure features like
/// price impact, trade classification, and flow analysis.
///
/// # Trade Classification
///
/// Trade direction is classified using various methods:
/// - **Tick Rule**: Compare trade price to previous trade price
/// - **Quote Rule**: Compare trade price to midpoint of bid-ask
/// - **LR Algorithm**: Lee-Ready algorithm combining tick and quote rules
/// - **Bulk Volume Classification**: Statistical methods for block trades
///
/// # Applications
///
/// - **Order Flow Analysis**: Measuring buy vs sell pressure
/// - **Price Impact**: Analyzing effect of trades on subsequent prices
/// - **Trade Size Analysis**: Volume distribution and block trade detection
/// - **Execution Quality**: Measuring implementation shortfall and slippage
///
/// # Data Quality
///
/// Trade data should be validated for:
/// - Positive trade sizes
/// - Reasonable prices within market ranges
/// - Proper timestamp ordering
/// - Trade direction consistency with price movements
///
/// # Examples
///
/// ```rust
/// use data::features::{TradeData, TradeDirection};
/// use chrono::Utc;
///
/// let trade = TradeData {
///     timestamp: Utc::now(),
///     price: 150.05,
///     size: 1000.0,
///     direction: TradeDirection::Buy, // Classified as buyer-initiated
/// };
///
/// // Calculate trade value
/// let trade_value = trade.price * trade.size; // $150,050
///
/// // Check for block trade
/// let is_block_trade = trade.size > 10_000.0;
/// ```
#[derive(Debug, Clone)]
pub struct TradeData {
    pub timestamp: DateTime<Utc>,
    pub price: f64,
    pub size: f64,
    pub direction: TradeDirection,
    pub conditions: Vec<String>,
}

/// Quote data (bid/ask prices and sizes) for microstructure analysis.
///
/// Represents the best bid and offer (BBO) at a specific point in time,
/// including both prices and sizes. Essential for calculating spreads,
/// imbalances, and other microstructure features.
///
/// # Quote Components
///
/// - **Bid**: Highest price buyers are willing to pay
/// - **Ask**: Lowest price sellers are willing to accept
/// - **Bid Size**: Quantity available at the bid price
/// - **Ask Size**: Quantity available at the ask price
///
/// # Derived Metrics
///
/// From quote data, we can calculate:
/// - **Spread**: Ask - Bid
/// - **Mid Price**: (Ask + Bid) / 2
/// - **Imbalance**: (Bid Size - Ask Size) / (Bid Size + Ask Size)
/// - **Depth**: Bid Size + Ask Size
///
/// # Data Integrity
///
/// Quote data should satisfy:
/// - Ask price >= Bid price (no crossed market in normal conditions)
/// - Positive sizes for both bid and ask
/// - Reasonable spread relative to price level
/// - Timestamps in chronological order
///
/// # Examples
///
/// ```rust
/// use data::features::QuoteData;
/// use chrono::Utc;
///
/// let quote = QuoteData {
///     timestamp: Utc::now(),
///     bid: 149.95,
///     ask: 150.05,
///     bid_size: 1000.0,
///     ask_size: 800.0,
/// };
///
/// // Calculate derived metrics
/// let spread = quote.ask - quote.bid;                    // 0.10
/// let mid_price = (quote.ask + quote.bid) / 2.0;        // 150.00
/// let imbalance = (quote.bid_size - quote.ask_size) /    // 0.111
///                 (quote.bid_size + quote.ask_size);
/// ```
#[derive(Debug, Clone)]
pub struct QuoteData {
    pub timestamp: DateTime<Utc>,
    pub bid_price: f64,
    pub ask_price: f64,
    pub bid_size: f64,
    pub ask_size: f64,
    pub exchange: String,
}

/// Classification of trade direction for order flow analysis.
///
/// Determines whether a trade was initiated by a buyer (market buy order)
/// or seller (market sell order), which is crucial for understanding
/// order flow and price pressure in the market.
///
/// # Classification Methods
///
/// - **Tick Rule**: Compare to previous trade price
///   - Uptick (higher price) = Buy
///   - Downtick (lower price) = Sell
///   - Zero tick = Use previous classification
///
/// - **Quote Rule**: Compare to bid-ask midpoint
///   - Above midpoint = Buy
///   - Below midpoint = Sell
///   - At midpoint = Unknown
///
/// - **Lee-Ready Algorithm**: Combination of tick and quote rules
///   - Use quote rule first
///   - If at midpoint, use tick rule
///
/// # Applications
///
/// - **Order Flow Imbalance**: Net buying vs selling pressure
/// - **Price Impact**: Effect of buyer vs seller initiated trades
/// - **Market Microstructure**: Understanding trade dynamics
/// - **Execution Analysis**: Assessing market impact of strategies
///
/// # Examples
///
/// ```rust
/// use data::features::TradeDirection;
///
/// // Classify trades based on price movement
/// fn classify_by_tick_rule(current_price: f64, previous_price: f64) -> TradeDirection {
///     if current_price > previous_price {
///         TradeDirection::Buy
///     } else if current_price < previous_price {
///         TradeDirection::Sell
///     } else {
///         TradeDirection::Unknown
///     }
/// }
/// ```
#[derive(Debug, Clone)]
pub enum TradeDirection {
    /// Buyer-initiated trade (market buy order)
    ///
    /// Trade was initiated by an aggressive buyer using a market order
    /// to purchase at the best available ask price. Indicates positive
    /// price pressure and buying interest.
    Buy,

    /// Seller-initiated trade (market sell order)
    ///
    /// Trade was initiated by an aggressive seller using a market order
    /// to sell at the best available bid price. Indicates negative
    /// price pressure and selling interest.
    Sell,

    /// Unknown or indeterminate trade direction
    ///
    /// Trade direction could not be determined reliably, either due to:
    /// - Trade at exact midpoint with no prior price reference
    /// - Insufficient data for classification algorithms
    /// - Special trade conditions (e.g., block trades, opening auctions)
    Unknown,
}

/// TLOB (Time-Limited Order Book) analyzer
pub struct TLOBAnalyzer {
    pub config: TLOBConfig,
    pub snapshots: BTreeMap<String, VecDeque<TLOBSnapshot>>,
    pub order_flow: BTreeMap<String, VecDeque<OrderFlowEvent>>,
}

/// TLOB snapshot for analysis
#[derive(Debug, Clone)]
pub struct TLOBSnapshot {
    pub timestamp: DateTime<Utc>,
    pub bid_levels: Vec<(f64, f64)>,
    pub ask_levels: Vec<(f64, f64)>,
    pub mid_price: f64,
    pub weighted_mid: f64,
    pub imbalance: f64,
}

/// Order flow event for TLOB analysis
#[derive(Debug, Clone)]
pub struct OrderFlowEvent {
    pub timestamp: DateTime<Utc>,
    pub event_type: OrderFlowEventType,
    pub price: f64,
    pub size: f64,
    pub side: String,
}

/// Order flow event types
#[derive(Debug, Clone)]
pub enum OrderFlowEventType {
    NewOrder,
    OrderCancel,
    OrderModify,
    Trade,
    MarketDataUpdate,
}

/// Temporal feature extractor for time-based market patterns.
///
/// Extracts features related to time patterns, market sessions, and calendar
/// effects that can be predictive in financial markets. These features help
/// capture cyclical patterns and regime changes based on time of day,
/// day of week, and other temporal factors.
///
/// # Extracted Features
///
/// ### Time of Day
/// - Hour and minute components
/// - Cyclical encoding using sine/cosine transforms
/// - Market session indicators (pre-market, regular hours, after-hours)
///
/// ### Calendar Effects
/// - Day of week (Monday effect, Friday effect)
/// - Weekend indicators
/// - Month and day of month
/// - Month-end and quarter-end effects
///
/// ### Market Sessions (US Market)
/// - **Pre-market**: 4:00 AM - 9:30 AM ET
/// - **Regular Hours**: 9:30 AM - 4:00 PM ET
/// - **After-hours**: 4:00 PM - 8:00 PM ET
///
/// # Cyclical Encoding
///
/// Time components are encoded using sine and cosine functions to capture
/// cyclical nature and ensure smooth transitions (e.g., 23:59 to 00:00):
///
/// ```text
/// hour_sin = sin(hour * 2π / 24)
/// hour_cos = cos(hour * 2π / 24)
/// ```
///
/// # Applications
///
/// - **Intraday Patterns**: Volume and volatility changes throughout the day
/// - **Weekly Patterns**: Monday gaps, Friday positioning effects
/// - **Calendar Effects**: Month-end rebalancing, quarterly window dressing
/// - **Regime Detection**: Identifying different market regimes by time
///
/// # Examples
///
/// ```rust
/// use data::features::TemporalFeatures;
/// use chrono::{Utc, Timelike, Datelike};
///
/// let timestamp = Utc::now();
/// let features = TemporalFeatures::extract_features(timestamp);
///
/// // Check for specific time-based conditions
/// let is_market_open = features.get("is_regular_hours").unwrap_or(&0.0) > 0.5;
/// let is_friday = features.get("weekday").unwrap_or(&0.0) == &4.0;
/// let is_month_end = features.get("is_month_end").unwrap_or(&0.0) > 0.5;
///
/// if is_market_open && is_friday {
///     println!("Friday regular trading hours");
/// }
/// ```
///
/// # Implementation Note
///
/// This is a utility struct with static methods. All functionality is
/// provided through the `extract_features` associated function.
pub struct TemporalFeatures;

/// Configuration for regime detector
#[derive(Debug, Clone)]
pub struct RegimeDetectorConfig {
    pub lookback_periods: usize,
    pub volatility_threshold: f64,
    pub trend_threshold: f64,
    pub correlation_threshold: f64,
    pub rebalance_frequency: usize,
}

/// Regime detection analyzer
pub struct RegimeDetector {
    pub config: RegimeDetectorConfig,
    pub volatility_history: BTreeMap<String, VecDeque<f64>>,
    pub volume_history: BTreeMap<String, VecDeque<f64>>,
    pub price_history: BTreeMap<String, VecDeque<f64>>,
    pub correlation_matrix: HashMap<String, HashMap<String, f64>>,
}

/// Configuration for portfolio analyzer
#[derive(Debug, Clone)]
pub struct PortfolioAnalyzerConfig {
    pub risk_free_rate: f64,
    pub target_return: f64,
    pub rebalance_threshold: f64,
    pub max_position_size: f64,
    pub diversification_target: usize,
}

/// Portfolio performance analyzer
pub struct PortfolioAnalyzer {
    pub config: PortfolioAnalyzerConfig,
    pub positions: HashMap<String, Position>,
    pub pnl_history: VecDeque<PnLPoint>,
    pub risk_metrics: RiskMetrics,
}

/// Position information
#[derive(Debug, Clone)]
pub struct Position {
    pub symbol: String,
    pub quantity: f64,
    pub entry_price: f64,
    pub current_price: f64,
    pub entry_time: DateTime<Utc>,
    pub last_update: DateTime<Utc>,
}

/// P&L tracking point
#[derive(Debug, Clone)]
pub struct PnLPoint {
    pub timestamp: DateTime<Utc>,
    pub realized_pnl: f64,
    pub unrealized_pnl: f64,
    pub total_pnl: f64,
    pub cumulative_pnl: f64,
}

/// Risk metrics
#[derive(Debug, Clone)]
pub struct RiskMetrics {
    pub var_95: f64,
    pub var_99: f64,
    pub expected_shortfall: f64,
    pub sharpe_ratio: f64,
    pub sortino_ratio: f64,
    pub max_drawdown: f64,
    pub volatility: f64,
}

impl TechnicalIndicators {
    /// Create new technical indicators calculator
    pub fn new(config: TechnicalIndicatorsConfig) -> Self {
        Self {
            config,
            price_data: BTreeMap::new(),
            volume_data: BTreeMap::new(),
            indicators: BTreeMap::new(),
        }
    }

    /// Update with new price data
    pub fn update_price(&mut self, symbol: &str, price_point: PricePoint) {
        let data = self
            .price_data
            .entry(symbol.to_string())
            .or_insert_with(VecDeque::new);
        data.push_back(price_point.clone());

        // Keep only required data based on largest period
        let max_period = self.config.ma_periods.iter().max().unwrap_or(&200);
        while data.len() > *max_period as usize {
            data.pop_front();
        }

        // Update indicators
        self.update_indicators(symbol);
    }

    /// Calculate features for a symbol
    pub fn calculate_features(&self, symbol: &str) -> HashMap<String, f64> {
        let mut features = HashMap::new();

        if let Some(indicators) = self.indicators.get(symbol) {
            // Simple Moving Averages
            for &period in &self.config.ma_periods {
                if let Some(&sma) = indicators.sma.get(&(period as u32)) {
                    features.insert(format!("sma_{}", period), sma);
                }
            }

            // Exponential Moving Averages
            for &period in &self.config.ma_periods {
                if let Some(&ema) = indicators.ema.get(&(period as u32)) {
                    features.insert(format!("ema_{}", period), ema);
                }
            }

            // RSI
            for &period in &self.config.rsi_periods {
                if let Some(&rsi) = indicators.rsi.get(&(period as u32)) {
                    features.insert(format!("rsi_{}", period), rsi);
                }
            }

            // MACD
            features.insert("macd_line".to_string(), indicators.macd.macd_line);
            features.insert("macd_signal".to_string(), indicators.macd.signal_line);
            features.insert("macd_histogram".to_string(), indicators.macd.histogram);

            // Bollinger Bands
            for &period in &self.config.bollinger_periods {
                if let Some(bb) = indicators.bollinger.get(&(period as u32)) {
                    features.insert(format!("bb_upper_{}", period), bb.upper_band);
                    features.insert(format!("bb_middle_{}", period), bb.middle_band);
                    features.insert(format!("bb_lower_{}", period), bb.lower_band);
                    features.insert(format!("bb_bandwidth_{}", period), bb.bandwidth);
                    features.insert(format!("bb_percent_b_{}", period), bb.percent_b);
                }
            }
        }

        features
    }

    /// Update all indicators for a symbol
    fn update_indicators(&mut self, symbol: &str) {
        // Get price data first to avoid borrowing conflicts
        let price_data = match self.price_data.get(symbol) {
            Some(data) => data.clone(),
            None => return,
        };

        // Get configuration periods
        let ma_periods = self.config.ma_periods.clone();
        let rsi_periods = self.config.rsi_periods.clone();
        let bollinger_periods = self.config.bollinger_periods.clone();

        // Get or create indicator state
        let indicator_state = self
            .indicators
            .entry(symbol.to_string())
            .or_insert_with(|| IndicatorState {
                sma: HashMap::new(),
                ema: HashMap::new(),
                rsi: HashMap::new(),
                macd: MACDState {
                    macd_line: 0.0,
                    signal_line: 0.0,
                    histogram: 0.0,
                    fast_ema: 0.0,
                    slow_ema: 0.0,
                    signal_ema: 0.0,
                    last_update: Utc::now(),
                },
                bollinger: HashMap::new(),
            });

        // Store current EMA values and MACD state for calculations
        let current_ema_values: HashMap<u32, f64> = indicator_state.ema.clone();
        let current_macd_state = indicator_state.macd.clone();

        // Now we can perform calculations without borrowing conflicts

        // Update SMA
        for &period in &ma_periods {
            if let Some(sma) = Self::calculate_sma_static(&price_data, period as u32) {
                indicator_state.sma.insert(period as u32, sma);
            }
        }

        // Update EMA
        for &period in &ma_periods {
            if let Some(ema) = Self::calculate_ema_static(
                &price_data,
                period as u32,
                current_ema_values.get(&(period as u32)).copied(),
            ) {
                indicator_state.ema.insert(period as u32, ema);
            }
        }

        // Update RSI
        for &period in &rsi_periods {
            if let Some(rsi) = Self::calculate_rsi_static(&price_data, period as u32) {
                indicator_state.rsi.insert(period as u32, rsi);
            }
        }

        // Update MACD
        indicator_state.macd = Self::calculate_macd_static(&price_data, &current_macd_state);

        // Update Bollinger Bands
        for &period in &bollinger_periods {
            if let Some(bb) = Self::calculate_bollinger_bands_static(&price_data, period as u32) {
                indicator_state.bollinger.insert(period as u32, bb);
            }
        }
    }

    /// Calculate Simple Moving Average
    fn calculate_sma(&self, data: &VecDeque<PricePoint>, period: u32) -> Option<f64> {
        if data.len() < period as usize {
            return None;
        }

        let sum: f64 = data
            .iter()
            .rev()
            .take(period as usize)
            .map(|p| p.close)
            .sum();
        Some(sum / period as f64)
    }

    /// Calculate Exponential Moving Average
    fn calculate_ema(
        &self,
        data: &VecDeque<PricePoint>,
        period: u32,
        prev_ema: Option<f64>,
    ) -> Option<f64> {
        if data.is_empty() {
            return None;
        }

        let current_price = data.back()?.close;
        let alpha = 2.0 / (period as f64 + 1.0);

        match prev_ema {
            Some(prev) => Some(alpha * current_price + (1.0 - alpha) * prev),
            None => Some(current_price), // First EMA value is the first price
        }
    }

    /// Calculate Relative Strength Index
    fn calculate_rsi(&self, data: &VecDeque<PricePoint>, period: u32) -> Option<f64> {
        if data.len() < (period + 1) as usize {
            return None;
        }

        let mut gains = Vec::new();
        let mut losses = Vec::new();

        for window in data
            .iter()
            .rev()
            .take(period as usize + 1)
            .collect::<Vec<_>>()
            .windows(2)
        {
            let change = window[0].close - window[1].close;
            if change > 0.0 {
                gains.push(change);
                losses.push(0.0);
            } else {
                gains.push(0.0);
                losses.push(-change);
            }
        }

        let avg_gain: f64 = gains.iter().sum::<f64>() / period as f64;
        let avg_loss: f64 = losses.iter().sum::<f64>() / period as f64;

        if avg_loss == 0.0 {
            return Some(100.0);
        }

        let rs = avg_gain / avg_loss;
        Some(100.0 - (100.0 / (1.0 + rs)))
    }

    /// Calculate MACD
    fn calculate_macd(&self, data: &VecDeque<PricePoint>, prev_state: &MACDState) -> MACDState {
        if data.is_empty() {
            return prev_state.clone();
        }

        let current_price = match data.back() {
            Some(price_point) => price_point.close,
            None => return prev_state.clone(), // Return previous state if data is unexpectedly empty
        };
        let fast_alpha = 2.0 / (self.config.macd.fast_period as f64 + 1.0);
        let slow_alpha = 2.0 / (self.config.macd.slow_period as f64 + 1.0);
        let signal_alpha = 2.0 / (self.config.macd.signal_period as f64 + 1.0);

        let fast_ema = if prev_state.fast_ema == 0.0 {
            current_price
        } else {
            fast_alpha * current_price + (1.0 - fast_alpha) * prev_state.fast_ema
        };

        let slow_ema = if prev_state.slow_ema == 0.0 {
            current_price
        } else {
            slow_alpha * current_price + (1.0 - slow_alpha) * prev_state.slow_ema
        };

        let macd_line = fast_ema - slow_ema;

        let signal_line = if prev_state.signal_ema == 0.0 {
            macd_line
        } else {
            signal_alpha * macd_line + (1.0 - signal_alpha) * prev_state.signal_line
        };

        let histogram = macd_line - signal_line;

        MACDState {
            macd_line,
            signal_line,
            histogram,
            fast_ema,
            slow_ema,
            signal_ema: signal_line,
            last_update: Utc::now(),
        }
    }

    /// Calculate Bollinger Bands
    fn calculate_bollinger_bands(
        &self,
        data: &VecDeque<PricePoint>,
        period: u32,
    ) -> Option<BollingerBandsState> {
        if data.len() < period as usize {
            return None;
        }

        let prices: Vec<f64> = data
            .iter()
            .rev()
            .take(period as usize)
            .map(|p| p.close)
            .collect();
        let mean = prices.iter().sum::<f64>() / period as f64;

        let variance = prices.iter().map(|&p| (p - mean).powi(2)).sum::<f64>() / period as f64;
        let std_dev = variance.sqrt();

        let upper_band = mean + 2.0 * std_dev;
        let lower_band = mean - 2.0 * std_dev;
        let bandwidth = (upper_band - lower_band) / mean;

        let current_price = data.back()?.close;
        let percent_b = if upper_band != lower_band {
            (current_price - lower_band) / (upper_band - lower_band)
        } else {
            0.5
        };

        Some(BollingerBandsState {
            upper_band,
            middle_band: mean,
            lower_band,
            bandwidth,
            percent_b,
            last_update: Utc::now(),
        })
    }

    // Static methods to avoid borrowing conflicts

    /// Calculate Simple Moving Average (static version)
    fn calculate_sma_static(data: &VecDeque<PricePoint>, period: u32) -> Option<f64> {
        if data.len() < period as usize {
            return None;
        }

        let sum: f64 = data
            .iter()
            .rev()
            .take(period as usize)
            .map(|p| p.close)
            .sum();
        Some(sum / period as f64)
    }

    /// Calculate Exponential Moving Average (static version)
    fn calculate_ema_static(
        data: &VecDeque<PricePoint>,
        period: u32,
        prev_ema: Option<f64>,
    ) -> Option<f64> {
        if data.is_empty() {
            return None;
        }

        let current_price = data.back()?.close;
        let alpha = 2.0 / (period as f64 + 1.0);

        match prev_ema {
            Some(prev) => Some(alpha * current_price + (1.0 - alpha) * prev),
            None => Some(current_price), // First EMA value is the current price
        }
    }

    /// Calculate RSI (static version)
    fn calculate_rsi_static(data: &VecDeque<PricePoint>, period: u32) -> Option<f64> {
        if data.len() < (period + 1) as usize {
            return None;
        }

        let mut gains = 0.0;
        let mut losses = 0.0;

        // SAFETY: Loop bound ensures idx and prev_idx are valid indices.
        // We check data.len() >= period + 1 above, so:
        // - idx = data.len() - 1 - i where i < period, so idx >= 0
        // - prev_idx = data.len() - 2 - i where i < period, so prev_idx >= 0
        #[allow(clippy::indexing_slicing)]
        for i in 0..period {
            let idx = data.len() - 1 - i as usize;
            let prev_idx = data.len() - 2 - i as usize;
            let change = data[idx].close - data[prev_idx].close;

            if change > 0.0 {
                gains += change;
            } else {
                losses += -change;
            }
        }

        let avg_gain = gains / period as f64;
        let avg_loss = losses / period as f64;

        if avg_loss == 0.0 {
            return Some(100.0);
        }

        let rs = avg_gain / avg_loss;
        Some(100.0 - (100.0 / (1.0 + rs)))
    }

    /// Calculate MACD (static version)
    fn calculate_macd_static(data: &VecDeque<PricePoint>, prev_state: &MACDState) -> MACDState {
        if data.is_empty() {
            return prev_state.clone();
        }

        let current_price = match data.back() {
            Some(point) => point.close,
            None => return prev_state.clone(),
        };

        // Use fixed MACD parameters
        let fast_alpha = 2.0 / 13.0; // 12-day EMA
        let slow_alpha = 2.0 / 27.0; // 26-day EMA
        let signal_alpha = 2.0 / 10.0; // 9-day EMA

        let fast_ema = if prev_state.fast_ema == 0.0 {
            current_price
        } else {
            fast_alpha * current_price + (1.0 - fast_alpha) * prev_state.fast_ema
        };

        let slow_ema = if prev_state.slow_ema == 0.0 {
            current_price
        } else {
            slow_alpha * current_price + (1.0 - slow_alpha) * prev_state.slow_ema
        };

        let macd_line = fast_ema - slow_ema;

        let signal_line = if prev_state.signal_line == 0.0 {
            macd_line
        } else {
            signal_alpha * macd_line + (1.0 - signal_alpha) * prev_state.signal_line
        };

        let histogram = macd_line - signal_line;

        MACDState {
            macd_line,
            signal_line,
            histogram,
            fast_ema,
            slow_ema,
            signal_ema: signal_line,
            last_update: Utc::now(),
        }
    }

    /// Calculate Bollinger Bands (static version)
    fn calculate_bollinger_bands_static(
        data: &VecDeque<PricePoint>,
        period: u32,
    ) -> Option<BollingerBandsState> {
        if data.len() < period as usize {
            return None;
        }

        let prices: Vec<f64> = data
            .iter()
            .rev()
            .take(period as usize)
            .map(|p| p.close)
            .collect();
        let mean = prices.iter().sum::<f64>() / period as f64;

        let variance = prices.iter().map(|&p| (p - mean).powi(2)).sum::<f64>() / period as f64;
        let std_dev = variance.sqrt();

        let upper_band = mean + 2.0 * std_dev;
        let lower_band = mean - 2.0 * std_dev;
        let bandwidth = (upper_band - lower_band) / mean;

        let current_price = data.back()?.close;
        let percent_b = if upper_band != lower_band {
            (current_price - lower_band) / (upper_band - lower_band)
        } else {
            0.5
        };

        Some(BollingerBandsState {
            upper_band,
            middle_band: mean,
            lower_band,
            bandwidth,
            percent_b,
            last_update: Utc::now(),
        })
    }
}

impl MicrostructureAnalyzer {
    /// Create new microstructure analyzer
    pub fn new(config: MicrostructureConfig) -> Self {
        Self {
            config,
            order_books: HashMap::new(),
            trade_data: BTreeMap::new(),
            quote_data: BTreeMap::new(),
        }
    }

    /// Update with new quote data
    pub fn update_quote(&mut self, symbol: &str, quote: QuoteData) {
        let data = self
            .quote_data
            .entry(symbol.to_string())
            .or_insert_with(VecDeque::new);
        data.push_back(quote);

        // Keep only recent data
        while data.len() > 1000 {
            data.pop_front();
        }
    }

    /// Update with new trade data
    pub fn update_trade(&mut self, symbol: &str, trade: TradeData) {
        let data = self
            .trade_data
            .entry(symbol.to_string())
            .or_insert_with(VecDeque::new);
        data.push_back(trade);

        // Keep only recent data
        while data.len() > 1000 {
            data.pop_front();
        }
    }

    /// Calculate microstructure features
    pub fn calculate_features(&self, symbol: &str) -> HashMap<String, f64> {
        let mut features = HashMap::new();

        // Bid-ask spread features
        if self.config.bid_ask_spread {
            if let Some(spread) = self.calculate_bid_ask_spread(symbol) {
                features.insert("bid_ask_spread".to_string(), spread.bid_ask_spread);
                features.insert("relative_spread".to_string(), spread.relative_spread);
                features.insert("effective_spread".to_string(), spread.effective_spread);
            }
        }

        // Volume imbalance
        if self.config.volume_imbalance {
            if let Some(imbalance) = self.calculate_volume_imbalance(symbol) {
                features.insert("volume_imbalance".to_string(), imbalance);
            }
        }

        // Price impact
        if self.config.price_impact {
            if let Some(impact) = self.calculate_price_impact(symbol) {
                features.insert("price_impact".to_string(), impact);
            }
        }

        // Kyle's lambda
        if self.config.kyle_lambda {
            if let Some(lambda) = self.calculate_kyle_lambda(symbol) {
                features.insert("kyle_lambda".to_string(), lambda);
            }
        }

        // Amihud illiquidity ratio
        if self.config.amihud_ratio {
            if let Some(ratio) = self.calculate_amihud_ratio(symbol) {
                features.insert("amihud_ratio".to_string(), ratio);
            }
        }

        // Roll spread (using bid_ask_spread field)
        if self.config.bid_ask_spread {
            if let Some(roll) = self.calculate_roll_spread(symbol) {
                features.insert("roll_spread".to_string(), roll);
            }
        }

        features
    }

    /// Calculate bid-ask spread metrics
    fn calculate_bid_ask_spread(&self, symbol: &str) -> Option<SpreadMetrics> {
        let quote_data = self.quote_data.get(symbol)?;
        let latest_quote = quote_data.back()?;

        let bid_ask_spread = latest_quote.ask_price - latest_quote.bid_price;
        let mid_price = (latest_quote.ask_price + latest_quote.bid_price) / 2.0;
        let relative_spread = bid_ask_spread / mid_price;

        Some(SpreadMetrics {
            bid_ask_spread,
            relative_spread,
            effective_spread: bid_ask_spread * 0.5,
            realized_spread: bid_ask_spread * 0.3,
            price_impact: bid_ask_spread * 0.2,
        })
    }

    /// Calculate volume imbalance
    fn calculate_volume_imbalance(&self, symbol: &str) -> Option<f64> {
        let quote_data = self.quote_data.get(symbol)?;
        let latest_quote = quote_data.back()?;

        let total_volume = latest_quote.bid_size + latest_quote.ask_size;
        if total_volume == 0.0 {
            return Some(0.0);
        }

        Some((latest_quote.bid_size - latest_quote.ask_size) / total_volume)
    }

    /// Calculate price impact
    fn calculate_price_impact(&self, symbol: &str) -> Option<f64> {
        let trade_data = self.trade_data.get(symbol)?;
        if trade_data.len() < 2 {
            return None;
        }

        let recent_trades: Vec<&TradeData> = trade_data.iter().rev().take(10).collect();
        let price_changes: Vec<f64> = recent_trades
            .windows(2)
            .map(|w| w[0].price - w[1].price)
            .collect();

        if price_changes.is_empty() {
            return None;
        }

        let avg_price_change = price_changes.iter().sum::<f64>() / price_changes.len() as f64;
        Some(avg_price_change)
    }

    /// Calculate Kyle's lambda (price impact parameter).
    ///
    /// Kyle's lambda is the regression coefficient of price change on signed order flow:
    ///   lambda = cov(delta_price, signed_volume) / var(signed_volume)
    ///
    /// Uses a rolling window of recent trades. Signed volume is determined by the tick rule:
    ///   sign = sign(price_change) applied to volume.
    fn calculate_kyle_lambda(&self, symbol: &str) -> Option<f64> {
        let trade_data = self.trade_data.get(symbol)?;
        let quote_data = self.quote_data.get(symbol)?;

        if trade_data.len() < 20 || quote_data.len() < 10 {
            return None;
        }

        // Use the most recent 100 trades (or all available if fewer)
        let window_size = 100.min(trade_data.len());
        let recent_trades: Vec<&TradeData> = trade_data.iter().rev().take(window_size).collect();

        // Need at least 20 consecutive trades for meaningful regression
        if recent_trades.len() < 20 {
            return None;
        }

        // Compute price changes and signed volumes using tick rule
        let mut price_changes = Vec::with_capacity(recent_trades.len() - 1);
        let mut signed_volumes = Vec::with_capacity(recent_trades.len() - 1);

        for window in recent_trades.windows(2) {
            let current = window[0];
            let previous = window[1];

            let delta_price = current.price - previous.price;
            // Tick rule: sign volume by the direction of the price change
            let sign = if delta_price > 0.0 {
                1.0
            } else if delta_price < 0.0 {
                -1.0
            } else {
                // No price change: use the trade's classified direction if available
                match current.direction {
                    TradeDirection::Buy => 1.0,
                    TradeDirection::Sell => -1.0,
                    TradeDirection::Unknown => 0.0,
                }
            };
            let signed_vol = current.size * sign;

            price_changes.push(delta_price);
            signed_volumes.push(signed_vol);
        }

        let n = price_changes.len() as f64;
        if n < 2.0 {
            return None;
        }

        // Compute means
        let mean_dp = price_changes.iter().sum::<f64>() / n;
        let mean_sv = signed_volumes.iter().sum::<f64>() / n;

        // Compute covariance and variance
        let mut cov = 0.0;
        let mut var_sv = 0.0;
        for i in 0..price_changes.len() {
            let dp_diff = price_changes[i] - mean_dp;
            let sv_diff = signed_volumes[i] - mean_sv;
            cov += dp_diff * sv_diff;
            var_sv += sv_diff * sv_diff;
        }
        cov /= n;
        var_sv /= n;

        // Avoid division by zero (no variance in signed volume)
        if var_sv.abs() < 1e-18 {
            return None;
        }

        let lambda = cov / var_sv;

        // Kyle's lambda should be non-negative (higher order flow -> higher price impact)
        // A negative value indicates the model is not well-specified for this data window
        if lambda < 0.0 {
            None
        } else {
            Some(lambda)
        }
    }

    /// Calculate Amihud illiquidity ratio
    fn calculate_amihud_ratio(&self, symbol: &str) -> Option<f64> {
        let trade_data = self.trade_data.get(symbol)?;
        if trade_data.len() < 2 {
            return None;
        }

        let recent_trades: Vec<&TradeData> = trade_data.iter().rev().take(20).collect();
        let mut total_ratio = 0.0;
        let mut count = 0;

        for window in recent_trades.windows(2) {
            let price_change = (window[0].price - window[1].price).abs();
            let volume = window[0].size;

            if volume > 0.0 {
                total_ratio += price_change / volume;
                count += 1;
            }
        }

        if count > 0 {
            Some(total_ratio / count as f64)
        } else {
            None
        }
    }

    /// Calculate Roll spread estimator
    fn calculate_roll_spread(&self, symbol: &str) -> Option<f64> {
        let trade_data = self.trade_data.get(symbol)?;
        if trade_data.len() < 3 {
            return None;
        }

        let recent_trades: Vec<&TradeData> = trade_data.iter().rev().take(50).collect();
        let price_changes: Vec<f64> = recent_trades
            .windows(2)
            .map(|w| w[0].price - w[1].price)
            .collect();

        if price_changes.len() < 2 {
            return None;
        }

        // Calculate serial covariance
        let mean_change = price_changes.iter().sum::<f64>() / price_changes.len() as f64;
        let covariance: f64 = price_changes
            .windows(2)
            .map(|w| (w[0] - mean_change) * (w[1] - mean_change))
            .sum::<f64>()
            / (price_changes.len() - 1) as f64;

        Some(2.0 * (-covariance).max(0.0).sqrt())
    }
}

/// Comprehensive bid-ask spread metrics for transaction cost analysis.
///
/// Provides multiple representations of the bid-ask spread to support
/// different analysis requirements and to normalize spreads across
/// different price levels and market conditions.
///
/// # Spread Representations
///
/// - **Absolute**: Raw price difference (Ask - Bid)
/// - **Percentage**: Relative to mid-price ((Ask - Bid) / Mid-price)
/// - **Basis Points**: Percentage × 10,000 for easier interpretation
///
/// # Applications
///
/// - **Transaction Cost Analysis**: Estimating cost of immediate execution
/// - **Liquidity Assessment**: Lower spreads indicate higher liquidity
/// - **Market Comparison**: Normalized spreads enable cross-asset comparison
/// - **Regime Detection**: Spread changes indicate market stress or calm
///
/// # Interpretation Guidelines
///
/// - **Tight Spreads** (< 5 bps): Highly liquid, low transaction costs
/// - **Normal Spreads** (5-20 bps): Standard liquidity for most assets
/// - **Wide Spreads** (> 20 bps): Lower liquidity, higher transaction costs
/// - **Very Wide Spreads** (> 100 bps): Illiquid or stressed market conditions
///
/// # Examples
///
/// ```rust
/// use data::features::SpreadMetrics;
///
/// let spread = SpreadMetrics {
///     absolute: 0.05,        // 5 cent spread
///     percentage: 0.0003,    // 0.03% of mid-price
///     basis_points: 3.0,     // 3 basis points
/// };
///
/// // Classify liquidity based on spread
/// let liquidity_tier = match spread.basis_points {
///     bp if bp < 5.0 => "Highly Liquid",
///     bp if bp < 20.0 => "Liquid",
///     bp if bp < 50.0 => "Moderately Liquid",
///     _ => "Illiquid",
/// };
/// ```
#[derive(Debug, Clone)]
pub struct SpreadMetrics {
    pub bid_ask_spread: f64,
    pub relative_spread: f64,
    pub effective_spread: f64,
    pub realized_spread: f64,
    pub price_impact: f64,
}

impl TemporalFeatures {
    /// Extract temporal features from timestamp
    pub fn extract_features(timestamp: DateTime<Utc>) -> HashMap<String, f64> {
        let mut features = HashMap::new();

        // Convert UTC to EST (UTC-5) for market hour calculations
        // Note: This doesn't account for daylight saving time, assumes EST year-round
        use chrono::FixedOffset;
        // SAFETY: 5*3600 = 18000 seconds is always a valid UTC offset
        #[allow(clippy::unwrap_used)]
        let est_offset = FixedOffset::west_opt(5 * 3600).unwrap();
        let est_time = timestamp.with_timezone(&est_offset);

        // Time of day features (using EST for market hours)
        let hour = est_time.hour() as f64;
        let minute = est_time.minute() as f64;

        features.insert("hour".to_string(), hour);
        features.insert("minute".to_string(), minute);
        features.insert(
            "hour_sin".to_string(),
            (hour * 2.0 * std::f64::consts::PI / 24.0).sin(),
        );
        features.insert(
            "hour_cos".to_string(),
            (hour * 2.0 * std::f64::consts::PI / 24.0).cos(),
        );

        // Day of week features
        let weekday = timestamp.weekday().num_days_from_monday() as f64;
        features.insert("weekday".to_string(), weekday);
        features.insert(
            "is_weekend".to_string(),
            if weekday >= 5.0 { 1.0 } else { 0.0 },
        );

        // Market session features (assuming US market hours)
        let is_premarket = hour < 9.0 || (hour == 9.0 && minute < 30.0);
        let is_regular_hours = (hour > 9.0 || (hour == 9.0 && minute >= 30.0)) && hour < 16.0;
        let is_aftermarket = hour >= 16.0 && hour < 20.0;

        features.insert(
            "is_premarket".to_string(),
            if is_premarket { 1.0 } else { 0.0 },
        );
        features.insert(
            "is_regular_hours".to_string(),
            if is_regular_hours { 1.0 } else { 0.0 },
        );
        features.insert(
            "is_aftermarket".to_string(),
            if is_aftermarket { 1.0 } else { 0.0 },
        );

        // Month and day features
        let month = timestamp.month() as f64;
        let day = timestamp.day() as f64;

        features.insert("month".to_string(), month);
        features.insert("day".to_string(), day);
        features.insert(
            "is_month_end".to_string(),
            if day >= 28.0 { 1.0 } else { 0.0 },
        );
        features.insert(
            "is_quarter_end".to_string(),
            if month % 3.0 == 0.0 && day >= 28.0 {
                1.0
            } else {
                0.0
            },
        );

        features
    }
}

impl TLOBAnalyzer {
    pub fn new(config: TLOBConfig) -> Self {
        Self {
            config,
            snapshots: BTreeMap::new(),
            order_flow: BTreeMap::new(),
        }
    }
}

impl RegimeDetector {
    pub fn new(config: RegimeDetectorConfig) -> Self {
        Self {
            config,
            volatility_history: BTreeMap::new(),
            volume_history: BTreeMap::new(),
            price_history: BTreeMap::new(),
            correlation_matrix: HashMap::new(),
        }
    }
}

impl PortfolioAnalyzer {
    pub fn new(config: PortfolioAnalyzerConfig) -> Self {
        Self {
            config,
            positions: HashMap::new(),
            pnl_history: VecDeque::new(),
            risk_metrics: RiskMetrics {
                var_95: 0.0,
                var_99: 0.0,
                expected_shortfall: 0.0,
                sharpe_ratio: 0.0,
                sortino_ratio: 0.0,
                max_drawdown: 0.0,
                volatility: 0.0,
            },
        }
    }
}

#[cfg(test)]
#[allow(clippy::get_unwrap)]
mod tests {
    use super::*;

    #[test]
    fn test_technical_indicators_creation() {
        let config = TechnicalIndicatorsConfig {
            enable_moving_averages: true,
            enable_momentum: true,
            enable_volatility: true,
            window_sizes: vec![10, 20],
            ma_periods: vec![10, 20],
            rsi_periods: vec![14],
            bollinger_periods: vec![20],
            macd: config::data_config::DataMACDConfig {
                fast_period: 12,
                slow_period: 26,
                signal_period: 9,
                enabled: true,
            },
        };

        let indicators = TechnicalIndicators::new(config);
        assert!(indicators.price_data.is_empty());
        assert!(indicators.indicators.is_empty());
    }

    #[test]
    fn test_temporal_features() {
        let timestamp = Utc::now();
        let features = TemporalFeatures::extract_features(timestamp);

        assert!(features.contains_key("hour"));
        assert!(features.contains_key("weekday"));
        assert!(features.contains_key("is_regular_hours"));
    }

    #[test]
    fn test_microstructure_analyzer() {
        let config = MicrostructureConfig {
            enable_bid_ask_spread: true,
            enable_order_flow: true,
            tick_size: 0.01,
            lot_size: 100.0,
            bid_ask_spread: true,
            volume_imbalance: true,
            price_impact: true,
            kyle_lambda: false,
            amihud_ratio: false,
        };

        let analyzer = MicrostructureAnalyzer::new(config);
        assert!(analyzer.order_books.is_empty());
        assert!(analyzer.trade_data.is_empty());
    }

    #[test]
    fn test_feature_vector_creation() {
        let mut features = HashMap::new();
        features.insert("price".to_string(), 100.0);
        features.insert("volume".to_string(), 1000.0);

        let mut feature_categories = HashMap::new();
        feature_categories.insert("price".to_string(), FeatureCategory::Price);
        feature_categories.insert("volume".to_string(), FeatureCategory::Volume);

        let metadata = FeatureMetadata {
            symbol: "AAPL".to_string(),
            timestamp: Utc::now(),
            feature_count: 2,
            categories: vec![FeatureCategory::Price, FeatureCategory::Volume],
            feature_descriptions: HashMap::new(),
            feature_categories,
            quality_indicators: HashMap::new(),
        };

        let vector = FeatureVector {
            timestamp: Utc::now(),
            symbol: "AAPL".to_string(),
            features,
            metadata: metadata.clone(),
        };
        assert_eq!(vector.features.len(), 2);
        assert_eq!(vector.metadata.feature_count, 2);
        assert_eq!(vector.metadata.symbol, "AAPL");
    }

    #[test]
    fn test_technical_indicators_update() {
        let config = TechnicalIndicatorsConfig {
            enable_moving_averages: true,
            enable_momentum: true,
            enable_volatility: true,
            window_sizes: vec![5],
            ma_periods: vec![5],
            rsi_periods: vec![14],
            bollinger_periods: vec![20],
            macd: config::data_config::DataMACDConfig {
                fast_period: 12,
                slow_period: 26,
                signal_period: 9,
                enabled: true,
            },
        };

        let mut indicators = TechnicalIndicators::new(config);

        // Add some price points
        for i in 0..10 {
            let price = PricePoint {
                timestamp: Utc::now(),
                open: 100.0 + i as f64,
                high: 102.0 + i as f64,
                low: 99.0 + i as f64,
                close: 101.0 + i as f64,
            };
            indicators.update_price("AAPL", price);
        }

        // price_data is a HashMap<String, VecDeque<PricePoint>>
        // We expect 1 symbol ("AAPL")
        // Data is trimmed to max_period (5 in this test's config)
        assert_eq!(indicators.price_data.len(), 1);
        assert_eq!(indicators.price_data.get("AAPL").unwrap().len(), 5);
    }

    #[test]
    fn test_volume_point_validation() {
        let volume = VolumePoint {
            timestamp: Utc::now(),
            volume: 1000.0,
            volume_weighted_price: 100.5,
            buy_volume: 600.0,
            sell_volume: 400.0,
        };

        assert_eq!(volume.volume, 1000.0);
        assert_eq!(volume.buy_volume, 600.0);
        assert!(volume.buy_volume + volume.sell_volume == 1000.0);
    }

    #[test]
    fn test_macd_state() {
        let state = MACDState {
            macd_line: 2.5,
            signal_line: 2.0,
            histogram: 0.5,
            fast_ema: 102.0,
            slow_ema: 99.5,
            signal_ema: 2.0,
            last_update: Utc::now(),
        };

        assert_eq!(state.macd_line - state.signal_line, state.histogram);
    }

    #[test]
    fn test_bollinger_bands_state() {
        let state = BollingerBandsState {
            upper_band: 105.0,
            middle_band: 100.0,
            lower_band: 95.0,
            bandwidth: 10.0,
            percent_b: 0.5,
            last_update: Utc::now(),
        };

        assert!(state.upper_band > state.middle_band);
        assert!(state.middle_band > state.lower_band);
        assert_eq!(state.bandwidth, 10.0);
    }

    #[test]
    fn test_order_book_state() {
        let state = OrderBookState {
            timestamp: Utc::now(),
            best_bid: 99.5,
            best_ask: 100.5,
            bid_size: 1000.0,
            ask_size: 800.0,
            spread: 1.0,
            mid_price: 100.0,
        };

        assert_eq!(state.best_ask - state.best_bid, state.spread);
        assert_eq!((state.best_bid + state.best_ask) / 2.0, state.mid_price);
    }

    #[test]
    fn test_trade_data() {
        let trade = TradeData {
            timestamp: Utc::now(),
            price: 100.0,
            size: 100.0,
            direction: TradeDirection::Buy,
            conditions: vec!["RegularSale".to_string()],
        };

        assert_eq!(trade.price, 100.0);
        assert_eq!(trade.size, 100.0);
        assert!(matches!(trade.direction, TradeDirection::Buy));
    }

    #[test]
    fn test_quote_data() {
        let quote = QuoteData {
            timestamp: Utc::now(),
            bid_price: 99.5,
            ask_price: 100.5,
            bid_size: 1000.0,
            ask_size: 800.0,
            exchange: "NYSE".to_string(),
        };

        assert!(quote.ask_price > quote.bid_price);
        assert_eq!(quote.exchange, "NYSE");
    }

    #[test]
    fn test_tlob_analyzer_creation() {
        let config = TLOBConfig {
            depth_levels: 10,
            enable_imbalance: true,
            enable_pressure: true,
            window_size: 100,
        };

        let analyzer = TLOBAnalyzer::new(config);
        assert!(analyzer.snapshots.is_empty());
    }

    #[test]
    fn test_tlob_snapshot() {
        let snapshot = TLOBSnapshot {
            timestamp: Utc::now(),
            bid_levels: vec![(99.5, 1000.0), (99.0, 1500.0)],
            ask_levels: vec![(100.5, 800.0), (101.0, 1200.0)],
            mid_price: 100.0,
            weighted_mid: 99.9,
            imbalance: 0.2,
        };

        assert_eq!(snapshot.bid_levels.len(), 2);
        assert_eq!(snapshot.ask_levels.len(), 2);
        assert_eq!(snapshot.mid_price, 100.0);
    }

    #[test]
    fn test_order_flow_event() {
        let event = OrderFlowEvent {
            timestamp: Utc::now(),
            event_type: OrderFlowEventType::NewOrder,
            price: 100.0,
            size: 100.0,
            side: "buy".to_string(),
        };

        assert!(matches!(event.event_type, OrderFlowEventType::NewOrder));
        assert_eq!(event.side, "buy");
    }

    #[test]
    fn test_temporal_features_market_hours() {
        use chrono::TimeZone;

        // Test regular hours (10:00 AM EST = 15:00 UTC)
        let regular_hours = Utc.with_ymd_and_hms(2024, 1, 15, 15, 0, 0).unwrap();
        let features = TemporalFeatures::extract_features(regular_hours);

        assert_eq!(features.get("is_regular_hours"), Some(&1.0));
        assert_eq!(features.get("is_premarket"), Some(&0.0));
        assert_eq!(features.get("is_aftermarket"), Some(&0.0));
    }

    #[test]
    fn test_temporal_features_premarket() {
        use chrono::TimeZone;

        // Test premarket (8:00 AM EST = 13:00 UTC)
        let premarket = Utc.with_ymd_and_hms(2024, 1, 15, 13, 0, 0).unwrap();
        let features = TemporalFeatures::extract_features(premarket);

        assert_eq!(features.get("is_premarket"), Some(&1.0));
        assert_eq!(features.get("is_regular_hours"), Some(&0.0));
    }

    #[test]
    fn test_temporal_features_quarter_end() {
        use chrono::TimeZone;

        // Test quarter end (March 31)
        let quarter_end = Utc.with_ymd_and_hms(2024, 3, 31, 12, 0, 0).unwrap();
        let features = TemporalFeatures::extract_features(quarter_end);

        assert_eq!(features.get("is_quarter_end"), Some(&1.0));
        assert_eq!(features.get("is_month_end"), Some(&1.0));
    }

    #[test]
    fn test_regime_detector_creation() {
        let config = RegimeDetectorConfig {
            lookback_periods: 50,
            volatility_threshold: 0.02,
            trend_threshold: 0.01,
            correlation_threshold: 0.7,
            rebalance_frequency: 100,
        };

        let detector = RegimeDetector::new(config);
        assert!(detector.price_history.is_empty());
    }

    #[test]
    fn test_portfolio_analyzer_creation() {
        let config = PortfolioAnalyzerConfig {
            risk_free_rate: 0.03,
            target_return: 0.10,
            rebalance_threshold: 0.05,
            max_position_size: 0.20,
            diversification_target: 10,
        };

        let analyzer = PortfolioAnalyzer::new(config);
        assert!(analyzer.positions.is_empty());
    }

    #[test]
    fn test_position_creation() {
        let position = Position {
            symbol: "AAPL".to_string(),
            quantity: 100.0,
            entry_price: 150.0,
            current_price: 155.0,
            entry_time: Utc::now(),
            last_update: Utc::now(),
        };

        let pnl = (position.current_price - position.entry_price) * position.quantity;
        assert_eq!(pnl, 500.0); // (155 - 150) * 100
    }

    #[test]
    fn test_pnl_point() {
        let pnl = PnLPoint {
            timestamp: Utc::now(),
            realized_pnl: 1000.0,
            unrealized_pnl: 500.0,
            total_pnl: 1500.0,
            cumulative_pnl: 5000.0,
        };

        assert_eq!(pnl.realized_pnl + pnl.unrealized_pnl, pnl.total_pnl);
    }

    #[test]
    fn test_risk_metrics() {
        let metrics = RiskMetrics {
            var_95: 10000.0,
            var_99: 15000.0,
            expected_shortfall: 18000.0,
            sharpe_ratio: 1.5,
            sortino_ratio: 2.0,
            max_drawdown: 0.15,
            volatility: 0.02,
        };

        assert!(metrics.var_99 > metrics.var_95);
        assert!(metrics.expected_shortfall > metrics.var_99);
        assert!(metrics.sortino_ratio > metrics.sharpe_ratio);
    }

    #[test]
    fn test_spread_metrics() {
        let metrics = SpreadMetrics {
            bid_ask_spread: 0.01,
            relative_spread: 0.0001,
            effective_spread: 0.005,
            realized_spread: 0.003,
            price_impact: 0.002,
        };

        assert!(metrics.bid_ask_spread > metrics.effective_spread);
        assert!(metrics.effective_spread > metrics.realized_spread);
    }

    #[test]
    fn test_feature_category_ordering() {
        assert!(FeatureCategory::Price < FeatureCategory::Volume);
        assert!(FeatureCategory::TechnicalIndicator < FeatureCategory::Microstructure);
    }
}
