//! Test Data Utilities and Generators for Foxhunt HFT Trading System
//!
//! This module provides utilities for generating test data, including
//! market data, time series, random data generation, and test data persistence.
//!
//! ## Usage
//!
//! ```rust
//! use tests::fixtures::test_data::*;
//!
//! // Generate market data
//! let market_data = MarketDataGenerator::new()
//!     .with_symbol(TEST_EQUITY_1)
//!     .generate_price_series(1000);
//!
//! // Generate time series data
//! let time_series = TimeSeriesGenerator::new()
//!     .with_frequency(Duration::from_secs(1))
//!     .generate_ohlcv(24 * 60 * 60); // 1 day of second data
//!
//! // Generate random portfolios
//! let random_portfolio = RandomDataGenerator::new()
//!     .generate_random_portfolio(10); // 10 positions
//! ```

use ::rust_decimal::Decimal;
use chrono::{DateTime, Duration as ChronoDuration, Timelike, Utc};
use rand::distributions::Distribution;
use rand::{rngs::StdRng, Rng, SeedableRng};
use rand_distr::Normal;
use serde_json::json;
use std::collections::HashMap;
use uuid::Uuid;

// Import types from risk crate
use super::builders::*;
use super::*;
use crate::fixtures::helpers::ToDecimal;
use risk::risk_types::{Position, StressScenario};

/// Legacy constant for backward compatibility
pub const SAMPLE_PRICE: f64 = 100.0;

// =============================================================================
// MARKET DATA GENERATOR
// =============================================================================

/// Generator for realistic market data with configurable parameters
#[derive(Debug, Clone)]
pub struct MarketDataGenerator {
    pub symbol: String,
    pub initial_price: Decimal,
    pub volatility: f64,         // Daily volatility (e.g., 0.02 = 2%)
    pub drift: f64,              // Daily drift (e.g., 0.0001 = 0.01%)
    pub tick_size: Decimal,      // Minimum price increment
    pub bid_ask_spread: Decimal, // Spread in price units
    pub seed: Option<u64>,       // Random seed for reproducible data
}

impl Default for MarketDataGenerator {
    fn default() -> Self {
        Self::new()
    }
}

impl MarketDataGenerator {
    pub fn new() -> Self {
        Self {
            symbol: TEST_EQUITY_1.to_string(),
            initial_price: Decimal::from(100),
            volatility: 0.02,                   // 2% daily volatility
            drift: 0.0001,                      // 0.01% daily drift
            tick_size: Decimal::new(1, 2),      // $0.01
            bid_ask_spread: Decimal::new(2, 2), // $0.02
            seed: Some(42),                     // Deterministic by default for testing
        }
    }

    pub fn with_symbol(mut self, symbol: impl Into<String>) -> Self {
        self.symbol = symbol.into();
        self
    }

    pub fn with_initial_price(mut self, price: Decimal) -> Self {
        self.initial_price = price;
        self
    }

    pub fn with_volatility(mut self, volatility: f64) -> Self {
        self.volatility = volatility;
        self
    }

    pub fn with_drift(mut self, drift: f64) -> Self {
        self.drift = drift;
        self
    }

    pub fn with_tick_size(mut self, tick_size: Decimal) -> Self {
        self.tick_size = tick_size;
        self
    }

    pub fn with_seed(mut self, seed: u64) -> Self {
        self.seed = Some(seed);
        self
    }

    pub fn random(mut self) -> Self {
        self.seed = None;
        self
    }

    /// Generate a price series using geometric Brownian motion
    pub fn generate_price_series(&self, count: usize) -> Vec<PricePoint> {
        let mut rng = match self.seed {
            Some(seed) => StdRng::seed_from_u64(seed),
            None => StdRng::from_entropy(),
        };

        let normal = Normal::new(0.0, 1.0).unwrap();
        let mut prices = Vec::with_capacity(count);
        let mut current_price = self.initial_price;
        let start_time = Utc::now();

        // Convert daily parameters to per-tick parameters
        let dt = 1.0 / (252.0 * 24.0 * 60.0 * 60.0); // Assume 1-second intervals
        let drift_per_tick = self.drift * dt;
        let vol_per_tick = self.volatility * dt.sqrt();

        for i in 0..count {
            let timestamp = start_time + ChronoDuration::seconds(i as i64);

            // Geometric Brownian Motion: dS = μS dt + σS dW
            let random_shock = normal.sample(&mut rng);
            let price_change_pct = drift_per_tick + vol_per_tick * random_shock;
            let new_price = current_price
                * (Decimal::ONE + Decimal::try_from(price_change_pct).unwrap_or(Decimal::ZERO));

            // Round to tick size
            let rounded_price = self.round_to_tick_size(new_price);
            current_price = rounded_price;

            prices.push(PricePoint {
                symbol: self.symbol.clone(),
                timestamp,
                price: current_price,
                bid: current_price - (self.bid_ask_spread / Decimal::from(2)),
                ask: current_price + (self.bid_ask_spread / Decimal::from(2)),
                volume: Decimal::from(rng.gen_range(100..=10000)),
                sequence: i as u64,
            });
        }

        prices
    }

    /// Generate OHLCV bars from price series
    pub fn generate_ohlcv_bars(&self, count: usize, bar_duration: ChronoDuration) -> Vec<OHLCVBar> {
        let price_points = self.generate_price_series(count * 60); // 60 ticks per bar
        let mut bars = Vec::new();
        let mut current_bar: Option<OHLCVBar> = None;

        for point in price_points {
            let bar_start = self.get_bar_start_time(point.timestamp, bar_duration);

            match &mut current_bar {
                Some(bar) if bar.timestamp == bar_start => {
                    // Update existing bar
                    bar.high = bar.high.max(point.price);
                    bar.low = bar.low.min(point.price);
                    bar.close = point.price;
                    bar.volume += point.volume;
                },
                _ => {
                    // Start new bar
                    if let Some(completed_bar) = current_bar.take() {
                        bars.push(completed_bar);
                    }

                    current_bar = Some(OHLCVBar {
                        symbol: self.symbol.clone(),
                        timestamp: bar_start,
                        open: point.price,
                        high: point.price,
                        low: point.price,
                        close: point.price,
                        volume: point.volume,
                    });
                },
            }

            if bars.len() >= count {
                break;
            }
        }

        if let Some(final_bar) = current_bar {
            bars.push(final_bar);
        }

        bars.truncate(count);
        bars
    }

    /// Generate market depth (order book) data
    pub fn generate_market_depth(&self, levels: usize) -> MarketDepth {
        let mut rng = match self.seed {
            Some(seed) => StdRng::seed_from_u64(seed),
            None => StdRng::from_entropy(),
        };

        let mid_price = self.initial_price;
        let mut bids = Vec::with_capacity(levels);
        let mut asks = Vec::with_capacity(levels);

        for i in 0..levels {
            let level_offset = Decimal::from(i + 1) * self.tick_size;
            let bid_price = mid_price - level_offset;
            let ask_price = mid_price + level_offset;

            let bid_size = Decimal::from(rng.gen_range(100..=5000));
            let ask_size = Decimal::from(rng.gen_range(100..=5000));

            bids.push(DepthLevel {
                price: bid_price,
                size: bid_size,
                order_count: rng.gen_range(1..=10),
            });

            asks.push(DepthLevel {
                price: ask_price,
                size: ask_size,
                order_count: rng.gen_range(1..=10),
            });
        }

        MarketDepth {
            symbol: self.symbol.clone(),
            timestamp: Utc::now(),
            bids,
            asks,
        }
    }

    fn round_to_tick_size(&self, price: Decimal) -> Decimal {
        if self.tick_size == Decimal::ZERO {
            return price;
        }

        let ticks = price / self.tick_size;
        let rounded_ticks = ticks.round();
        rounded_ticks * self.tick_size
    }

    fn get_bar_start_time(
        &self,
        timestamp: DateTime<Utc>,
        duration: ChronoDuration,
    ) -> DateTime<Utc> {
        let seconds_since_epoch = timestamp.timestamp();
        let duration_seconds = duration.num_seconds();
        let bar_start_seconds = (seconds_since_epoch / duration_seconds) * duration_seconds;
        DateTime::from_timestamp(bar_start_seconds, 0).unwrap_or(timestamp)
    }
}

// =============================================================================
// TIME SERIES GENERATOR
// =============================================================================

/// Generator for time series data with various patterns
#[derive(Debug, Clone)]
pub struct TimeSeriesGenerator {
    pub frequency: ChronoDuration,
    pub start_time: DateTime<Utc>,
    pub trend: f64,       // Linear trend component
    pub seasonality: f64, // Seasonal amplitude
    pub noise_level: f64, // Random noise level
    pub seed: Option<u64>,
}

impl Default for TimeSeriesGenerator {
    fn default() -> Self {
        Self::new()
    }
}

impl TimeSeriesGenerator {
    pub fn new() -> Self {
        Self {
            frequency: ChronoDuration::minutes(1),
            start_time: Utc::now() - ChronoDuration::hours(24),
            trend: 0.0,
            seasonality: 0.1,
            noise_level: 0.05,
            seed: Some(42),
        }
    }

    pub fn with_frequency(mut self, frequency: ChronoDuration) -> Self {
        self.frequency = frequency;
        self
    }

    pub fn with_start_time(mut self, start_time: DateTime<Utc>) -> Self {
        self.start_time = start_time;
        self
    }

    pub fn with_trend(mut self, trend: f64) -> Self {
        self.trend = trend;
        self
    }

    pub fn with_seasonality(mut self, seasonality: f64) -> Self {
        self.seasonality = seasonality;
        self
    }

    pub fn with_noise_level(mut self, noise_level: f64) -> Self {
        self.noise_level = noise_level;
        self
    }

    /// Generate time series with trend, seasonality, and noise
    pub fn generate_series(&self, count: usize, base_value: f64) -> Vec<TimeSeriesPoint> {
        let mut rng = match self.seed {
            Some(seed) => StdRng::seed_from_u64(seed),
            None => StdRng::from_entropy(),
        };

        let normal = Normal::new(0.0, self.noise_level).unwrap();
        let mut series = Vec::with_capacity(count);

        for i in 0..count {
            let timestamp = self.start_time + (self.frequency * i as i32);

            // Trend component
            let trend_value = self.trend * i as f64;

            // Seasonal component (daily pattern)
            let hour_of_day = timestamp.hour() as f64;
            let seasonal_value =
                self.seasonality * (2.0 * std::f64::consts::PI * hour_of_day / 24.0).sin();

            // Noise component
            let noise_value = normal.sample(&mut rng);

            // Combine components
            let value = base_value + trend_value + seasonal_value + noise_value;

            series.push(TimeSeriesPoint {
                timestamp,
                value: Decimal::try_from(value).unwrap_or(Decimal::from(0)),
                metadata: json!({
                    "trend": trend_value,
                    "seasonal": seasonal_value,
                    "noise": noise_value
                }),
            });
        }

        series
    }

    /// Generate OHLCV data from time series
    pub fn generate_ohlcv(&self, count: usize) -> Vec<OHLCVBar> {
        let base_price = 100.0;
        let series = self.generate_series(count, base_price);
        let mut ohlcv = Vec::with_capacity(count);

        for point in series {
            let price = point.value;
            let volume = Decimal::from(1000 + (point.timestamp.timestamp() % 9000));

            ohlcv.push(OHLCVBar {
                symbol: TEST_EQUITY_1.to_string(),
                timestamp: point.timestamp,
                open: price,
                high: price * Decimal::new(1001, 3), // +0.1%
                low: price * Decimal::new(999, 3),   // -0.1%
                close: price,
                volume,
            });
        }

        ohlcv
    }
}

// =============================================================================
// RANDOM DATA GENERATOR
// =============================================================================

/// Generator for random test data with realistic distributions
#[derive(Debug)]
pub struct RandomDataGenerator {
    rng: StdRng,
}

impl RandomDataGenerator {
    pub fn new() -> Self {
        Self {
            rng: StdRng::seed_from_u64(42),
        }
    }

    pub fn with_seed(seed: u64) -> Self {
        Self {
            rng: StdRng::seed_from_u64(seed),
        }
    }

    /// Generate a random portfolio with specified number of positions
    pub fn generate_random_portfolio(
        &mut self,
        position_count: usize,
    ) -> (Portfolio, Vec<Instrument>, Vec<Position>) {
        let portfolio_id = format!("RANDOM_PORTFOLIO_{}", self.rng.gen::<u32>());

        let portfolio = PortfolioBuilder::new()
            .with_id(&portfolio_id)
            .with_name("Random Test Portfolio")
            .with_base_currency("USD")
            .strategy_portfolio()
            .build();

        let mut instruments = Vec::with_capacity(position_count);
        let mut positions = Vec::with_capacity(position_count);

        let asset_classes = [
            AssetClass::Equities,
            AssetClass::Currencies,
            AssetClass::FixedIncome,
            AssetClass::Derivatives,
            AssetClass::Commodities,
            AssetClass::Alternatives,
        ];

        for i in 0..position_count {
            let asset_class = asset_classes[self.rng.gen_range(0..asset_classes.len())];
            let symbol = generate_test_symbol(asset_class);

            // Generate random instrument
            let mut instrument_builder = InstrumentBuilder::new()
                .with_symbol(&symbol)
                .with_name(format!("Random Instrument {}", i + 1));

            instrument_builder = match asset_class {
                AssetClass::Equities => instrument_builder.equity(),
                AssetClass::Currencies => instrument_builder.currency(),
                AssetClass::FixedIncome => instrument_builder.bond(),
                AssetClass::Derivatives => instrument_builder.future(),
                AssetClass::Commodities => instrument_builder.commodity(),
                AssetClass::Alternatives => instrument_builder.crypto(),
                AssetClass::Cash => instrument_builder.equity(),
            };

            instruments.push(instrument_builder.build());

            // Generate random position
            let quantity = Decimal::from(self.rng.gen_range(100..=10000));
            let price = self.rng.gen_range(10.0..=1000.0).to_decimal();
            let price_change_pct = self.rng.gen_range(-0.1..=0.1); // ±10%
            let market_price = price
                * (Decimal::ONE + Decimal::try_from(price_change_pct).unwrap_or(Decimal::ZERO));

            let position = PositionBuilder::new()
                .with_portfolio_id(&portfolio_id)
                .with_symbol(&symbol)
                .with_quantity(quantity)
                .with_average_price(price)
                .with_market_price(market_price)
                .with_beta(Decimal::try_from(self.rng.gen_range(0.5..=2.0)).unwrap())
                .build();

            positions.push(position);
        }

        (portfolio, instruments, positions)
    }

    /// Generate random market events
    pub fn generate_random_events(&mut self, count: usize) -> Vec<MarketEvent> {
        let event_types = ["trade", "quote", "news", "halt", "resume"];
        let symbols = ALL_TEST_SYMBOLS;
        let mut events = Vec::with_capacity(count);

        for i in 0..count {
            let event_type = event_types[self.rng.gen_range(0..event_types.len())];
            let symbol = symbols[self.rng.gen_range(0..symbols.len())];
            let timestamp = Utc::now() + ChronoDuration::seconds(i as i64);

            events.push(MarketEvent {
                id: Uuid::new_v4(),
                event_type: event_type.to_string(),
                symbol: symbol.to_string(),
                timestamp,
                data: json!({
                    "random_data": true,
                    "sequence": i,
                    "value": self.rng.gen_range(1.0..=1000.0)
                }),
            });
        }

        events
    }

    /// Generate random stress test scenarios
    pub fn generate_random_stress_scenarios(&mut self, count: usize) -> Vec<StressScenario> {
        let scenario_types = ["Historical", "Hypothetical", "Monte Carlo"];
        let mut scenarios = Vec::with_capacity(count);

        for i in 0..count {
            let _scenario_type = scenario_types[self.rng.gen_range(0..scenario_types.len())];

            let shock_factors = json!({
                "equity_shock": self.rng.gen_range(-0.5..=0.2),
                "bond_shock": self.rng.gen_range(-0.2..=0.3),
                "fx_shock": self.rng.gen_range(-0.3..=0.3),
                "commodity_shock": self.rng.gen_range(-0.4..=0.4),
                "volatility_multiplier": self.rng.gen_range(1.0..=5.0)
            });

            let equity_shock = shock_factors
                .get("equity_shock")
                .and_then(|v| v.as_f64())
                .unwrap_or(0.0);
            let mut price_shocks = HashMap::new();
            price_shocks.insert("EQUITY".to_string(), equity_shock);

            scenarios.push(StressScenario {
                id: Uuid::new_v4().to_string(),
                name: format!("Random Stress Scenario {}", i + 1),
                price_shocks: price_shocks.clone(),
                market_shocks: price_shocks,
                volatility_multiplier: shock_factors
                    .get("volatility_multiplier")
                    .and_then(|v| v.as_f64())
                    .unwrap_or(1.0),
                volatility_multipliers: HashMap::new(),
                correlation_changes: HashMap::new(),
                correlation_adjustments: HashMap::new(),
                liquidity_haircuts: HashMap::new(),
            });
        }

        scenarios
    }
}

impl Default for RandomDataGenerator {
    fn default() -> Self {
        Self::new()
    }
}

// =============================================================================
// DATA STRUCTURES
// =============================================================================

/// Price point for market data
#[derive(Debug, Clone)]
pub struct PricePoint {
    pub symbol: String,
    pub timestamp: DateTime<Utc>,
    pub price: Decimal,
    pub bid: Decimal,
    pub ask: Decimal,
    pub volume: Decimal,
    pub sequence: u64,
}

/// OHLCV bar for candlestick data
#[derive(Debug, Clone)]
pub struct OHLCVBar {
    pub symbol: String,
    pub timestamp: DateTime<Utc>,
    pub open: Decimal,
    pub high: Decimal,
    pub low: Decimal,
    pub close: Decimal,
    pub volume: Decimal,
}

/// Market depth level
#[derive(Debug, Clone)]
pub struct DepthLevel {
    pub price: Decimal,
    pub size: Decimal,
    pub order_count: u32,
}

/// Market depth (order book)
#[derive(Debug, Clone)]
pub struct MarketDepth {
    pub symbol: String,
    pub timestamp: DateTime<Utc>,
    pub bids: Vec<DepthLevel>,
    pub asks: Vec<DepthLevel>,
}

/// Time series data point
#[derive(Debug, Clone)]
pub struct TimeSeriesPoint {
    pub timestamp: DateTime<Utc>,
    pub value: Decimal,
    pub metadata: serde_json::Value,
}

/// Market event for event-driven testing
#[derive(Debug, Clone)]
pub struct MarketEvent {
    pub id: Uuid,
    pub event_type: String,
    pub symbol: String,
    pub timestamp: DateTime<Utc>,
    pub data: serde_json::Value,
}

// =============================================================================
// UTILITY FUNCTIONS
// =============================================================================

/// Create realistic test prices for different asset classes
pub fn create_realistic_test_prices() -> HashMap<String, Decimal> {
    let mut prices = HashMap::new();

    // Equity prices
    for symbol in ALL_TEST_EQUITIES {
        prices.insert(
            symbol.to_string(),
            Decimal::from(100 + (symbol.len() as i64 * 10)),
        );
    }

    // FX prices (rates)
    prices.insert(TEST_FOREX_1.to_string(), Decimal::new(12345, 5)); // 1.2345
    prices.insert(TEST_FOREX_2.to_string(), Decimal::new(13456, 5)); // 1.3456
    prices.insert(TEST_FOREX_3.to_string(), Decimal::from(150)); // 150.00
    prices.insert(TEST_FOREX_EXOTIC.to_string(), Decimal::new(2850, 2)); // 28.50 (USDTRY)

    // Futures prices
    for symbol in ALL_TEST_FUTURES {
        prices.insert(
            symbol.to_string(),
            Decimal::from(4000 + (symbol.len() as i64 * 100)),
        );
    }

    // Bond prices (yield-like)
    for symbol in ALL_TEST_BONDS {
        prices.insert(
            symbol.to_string(),
            Decimal::new(250 + (symbol.len() as i64 * 10), 2),
        );
    }

    // Commodity prices
    prices.insert(TEST_COMMODITY_1.to_string(), Decimal::from(2000)); // Gold
    prices.insert(TEST_COMMODITY_2.to_string(), Decimal::from(25)); // Silver
    prices.insert(TEST_COMMODITY_OIL.to_string(), Decimal::from(80)); // Oil
    prices.insert(TEST_COMMODITY_GAS.to_string(), Decimal::from(4)); // Natural Gas

    // Crypto prices
    prices.insert(TEST_CRYPTO_1.to_string(), Decimal::from(50000)); // BTC-like
    prices.insert(TEST_CRYPTO_2.to_string(), Decimal::from(3000)); // ETH-like
    prices.insert(TEST_CRYPTO_ALT.to_string(), Decimal::from(1)); // Altcoin

    // Option prices
    prices.insert(TEST_OPTION_CALL.to_string(), Decimal::from(5)); // Call option premium
    prices.insert(TEST_OPTION_PUT.to_string(), Decimal::from(3)); // Put option premium

    prices
}

/// Create test volatility estimates for different asset classes
pub fn create_test_volatilities() -> HashMap<String, f64> {
    let mut volatilities = HashMap::new();

    // Equity volatilities (annualized)
    for symbol in ALL_TEST_EQUITIES {
        volatilities.insert(symbol.to_string(), 0.20); // 20% annual vol
    }

    // FX volatilities
    volatilities.insert(TEST_FOREX_1.to_string(), 0.10); // 10% annual vol
    volatilities.insert(TEST_FOREX_2.to_string(), 0.12);
    volatilities.insert(TEST_FOREX_3.to_string(), 0.08);
    volatilities.insert(TEST_FOREX_EXOTIC.to_string(), 0.18); // Higher vol for exotic pair

    // Futures volatilities
    for symbol in ALL_TEST_FUTURES {
        volatilities.insert(symbol.to_string(), 0.25); // 25% annual vol
    }

    // Bond volatilities
    for symbol in ALL_TEST_BONDS {
        volatilities.insert(symbol.to_string(), 0.05); // 5% annual vol
    }

    // Commodity volatilities
    volatilities.insert(TEST_COMMODITY_1.to_string(), 0.15); // Gold
    volatilities.insert(TEST_COMMODITY_2.to_string(), 0.20); // Silver
    volatilities.insert(TEST_COMMODITY_OIL.to_string(), 0.35); // Oil
    volatilities.insert(TEST_COMMODITY_GAS.to_string(), 0.50); // Natural Gas

    // Crypto volatilities
    volatilities.insert(TEST_CRYPTO_1.to_string(), 0.60); // BTC-like
    volatilities.insert(TEST_CRYPTO_2.to_string(), 0.70); // ETH-like
    volatilities.insert(TEST_CRYPTO_ALT.to_string(), 0.80); // Altcoin

    volatilities
}

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

    #[test]
    fn test_market_data_generator() {
        let generator = MarketDataGenerator::new();
        let prices = generator.generate_price_series(100);

        assert_eq!(prices.len(), 100);
        assert!(prices[0].price > Decimal::ZERO);

        // Check timestamps are sequential
        for window in prices.windows(2) {
            assert!(window[1].timestamp >= window[0].timestamp);
        }
    }

    #[test]
    fn test_ohlcv_generation() {
        let generator = MarketDataGenerator::new();
        let bars = generator.generate_ohlcv_bars(50, ChronoDuration::minutes(1));

        assert_eq!(bars.len(), 50);

        for bar in &bars {
            assert!(bar.high >= bar.low);
            assert!(bar.high >= bar.open);
            assert!(bar.high >= bar.close);
            assert!(bar.low <= bar.open);
            assert!(bar.low <= bar.close);
        }
    }

    #[test]
    fn test_time_series_generator() {
        let generator = TimeSeriesGenerator::new()
            .with_trend(0.1)
            .with_seasonality(0.2);

        let series = generator.generate_series(100, 100.0);
        assert_eq!(series.len(), 100);

        // Check that trend is applied (last value should be higher due to positive trend)
        assert!(series.last().unwrap().value > series.first().unwrap().value);
    }

    #[test]
    fn test_random_data_generator() {
        let mut generator = RandomDataGenerator::new();
        let (portfolio, instruments, positions) = generator.generate_random_portfolio(5);

        assert_eq!(instruments.len(), 5);
        assert_eq!(positions.len(), 5);
        assert!(!portfolio.id.is_empty());

        // Check that positions are created (portfolio_id not stored in Position anymore)
        assert!(!positions.is_empty());
    }

    #[test]
    fn test_market_depth_generation() {
        let generator = MarketDataGenerator::new();
        let depth = generator.generate_market_depth(5);

        assert_eq!(depth.bids.len(), 5);
        assert_eq!(depth.asks.len(), 5);

        // Check that bid prices are decreasing and ask prices are increasing
        for window in depth.bids.windows(2) {
            assert!(window[0].price > window[1].price);
        }

        for window in depth.asks.windows(2) {
            assert!(window[0].price < window[1].price);
        }
    }

    #[test]
    fn test_realistic_test_prices() {
        let prices = create_realistic_test_prices();
        assert!(!prices.is_empty());

        // Check that all test symbols have prices
        for &symbol in ALL_TEST_SYMBOLS {
            assert!(prices.contains_key(symbol));
            assert!(prices[symbol] > Decimal::ZERO);
        }
    }

    #[test]
    fn test_volatility_estimates() {
        let volatilities = create_test_volatilities();
        assert!(!volatilities.is_empty());

        // Check that volatilities are reasonable (between 0 and 100%)
        for (symbol, &vol) in &volatilities {
            assert!(vol > 0.0, "Volatility for {} should be > 0", symbol);
            assert!(vol < 1.0, "Volatility for {} should be < 1.0", symbol); // Less than 100% for most assets
        }
    }
}
