//! Sample data for MarketSim demo. use marketsim_shared::{ Asset, AssetPrice, AssetType, BacktestRequest, EconomicFactors, MarketIndicators, MarketState, PriceHistory, ScenarioDescription, ScenarioRequest, ScenarioResult, ScenarioType, Strategy, StrategyParameters, StrategyType, }; /// Create a sample crisis simulation request. #[must_use] pub fn create_crisis_simulation_request() -> ScenarioRequest { ScenarioRequest { assets: get_sample_assets(), historical_data: get_sample_historical_data(), initial_state: get_sample_market_state(), scenario: ScenarioDescription { name: "2008-Style Financial Crisis".to_string(), scenario_type: ScenarioType::Crisis, description: "Sharp market decline with elevated volatility and credit stress" .to_string(), severity: 0.8, duration_days: 60, }, num_paths: 100, horizon_days: 60, seed: Some(42), } } /// Create a sample rally simulation request. #[must_use] pub fn create_rally_simulation_request() -> ScenarioRequest { ScenarioRequest { assets: get_sample_assets(), historical_data: get_sample_historical_data(), initial_state: get_sample_market_state(), scenario: ScenarioDescription { name: "Post-Crisis Recovery Rally".to_string(), scenario_type: ScenarioType::Rally, description: "Strong market recovery with risk-on sentiment".to_string(), severity: 0.6, duration_days: 90, }, num_paths: 100, horizon_days: 90, seed: Some(123), } } /// Create a backtest request for the given scenarios. #[must_use] pub fn create_backtest_request(scenarios: Vec) -> BacktestRequest { BacktestRequest { strategy: Strategy { name: "Momentum Strategy".to_string(), strategy_type: StrategyType::Momentum, parameters: StrategyParameters { lookback: 20, rebalance_frequency: 5, max_position: 0.4, stop_loss: Some(0.10), take_profit: Some(0.20), custom: std::collections::HashMap::new(), }, }, scenarios, initial_capital: 1_000_000.0, transaction_cost_bps: 10.0, } } /// Get sample assets. #[must_use] pub fn get_sample_assets() -> Vec { vec![ Asset { symbol: "SPY".to_string(), name: "S&P 500 ETF".to_string(), asset_type: AssetType::Index, current_price: 500.0, }, Asset { symbol: "QQQ".to_string(), name: "NASDAQ 100 ETF".to_string(), asset_type: AssetType::Index, current_price: 400.0, }, Asset { symbol: "TLT".to_string(), name: "20+ Year Treasury ETF".to_string(), asset_type: AssetType::Bond, current_price: 90.0, }, Asset { symbol: "GLD".to_string(), name: "Gold ETF".to_string(), asset_type: AssetType::Commodity, current_price: 180.0, }, Asset { symbol: "VNQ".to_string(), name: "Real Estate ETF".to_string(), asset_type: AssetType::Equity, current_price: 85.0, }, ] } /// Get sample market state. #[must_use] pub fn get_sample_market_state() -> MarketState { MarketState { timestamp: "2024-01-15T16:00:00Z".to_string(), prices: vec![ AssetPrice { symbol: "SPY".to_string(), price: 500.0, change_pct: 0.5, }, AssetPrice { symbol: "QQQ".to_string(), price: 400.0, change_pct: 0.8, }, AssetPrice { symbol: "TLT".to_string(), price: 90.0, change_pct: -0.2, }, AssetPrice { symbol: "GLD".to_string(), price: 180.0, change_pct: 0.1, }, AssetPrice { symbol: "VNQ".to_string(), price: 85.0, change_pct: 0.3, }, ], indicators: MarketIndicators { vix: 15.0, sp500: 5000.0, treasury_10y: 4.2, credit_spread: 3.5, put_call_ratio: 0.85, }, economic_factors: EconomicFactors { gdp_growth: 2.5, inflation: 3.0, unemployment: 3.8, fed_funds_rate: 5.25, consumer_sentiment: 102.0, }, } } /// Get sample historical data. #[must_use] pub fn get_sample_historical_data() -> Vec { let num_days = 252; vec![ generate_price_history("SPY", num_days, 500.0, 0.0003, 0.012), generate_price_history("QQQ", num_days, 400.0, 0.0004, 0.015), generate_price_history("TLT", num_days, 90.0, 0.0001, 0.010), generate_price_history("GLD", num_days, 180.0, 0.0002, 0.008), generate_price_history("VNQ", num_days, 85.0, 0.0002, 0.014), ] } /// Generate sample price history. fn generate_price_history( symbol: &str, num_days: usize, initial_price: f64, daily_drift: f64, daily_vol: f64, ) -> PriceHistory { let mut seed = symbol .bytes() .fold(0u64, |acc, b| acc.wrapping_add(b as u64)) * 54321; let mut dates = Vec::with_capacity(num_days); let mut open = Vec::with_capacity(num_days); let mut high = Vec::with_capacity(num_days); let mut low = Vec::with_capacity(num_days); let mut close = Vec::with_capacity(num_days); let mut volume = Vec::with_capacity(num_days); let mut current_price = initial_price; for day in 0..num_days { // Generate date let date = format!("2023-{:02}-{:02}", (day / 30 % 12) + 1, (day % 28) + 1); dates.push(date); // Generate OHLC seed = seed.wrapping_mul(1103515245).wrapping_add(12345); let u1 = ((seed >> 16) % 32768) as f64 / 32768.0 + 0.0001; seed = seed.wrapping_mul(1103515245).wrapping_add(12345); let u2 = ((seed >> 16) % 32768) as f64 / 32768.0; let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos(); let daily_return = (daily_drift + daily_vol * z).clamp(-0.05, 0.05); let day_open = current_price; let day_close = current_price * (1.0 + daily_return); // High and low based on volatility seed = seed.wrapping_mul(1103515245).wrapping_add(12345); let range_mult = 1.0 + ((seed >> 16) % 32768) as f64 / 32768.0 * daily_vol; let day_high = day_open.max(day_close) * range_mult; let day_low = day_open.min(day_close) / range_mult; open.push(day_open); high.push(day_high); low.push(day_low); close.push(day_close); // Volume seed = seed.wrapping_mul(1103515245).wrapping_add(12345); let vol = 1_000_000.0 * (0.5 + ((seed >> 16) % 32768) as f64 / 32768.0); volume.push(vol); current_price = day_close; } PriceHistory { symbol: symbol.to_string(), dates, open, high, low, close, volume, } } /// Get all predefined scenarios. #[must_use] pub fn get_predefined_scenarios() -> Vec { vec![ ScenarioDescription { name: "2008-Style Financial Crisis".to_string(), scenario_type: ScenarioType::Crisis, description: "Sharp market decline with elevated volatility and credit stress" .to_string(), severity: 0.8, duration_days: 60, }, ScenarioDescription { name: "COVID-19 Crash".to_string(), scenario_type: ScenarioType::Crisis, description: "Rapid 30% decline followed by V-shaped recovery".to_string(), severity: 0.7, duration_days: 30, }, ScenarioDescription { name: "Tech Bubble Burst".to_string(), scenario_type: ScenarioType::Crisis, description: "Extended decline focused on tech sector".to_string(), severity: 0.6, duration_days: 120, }, ScenarioDescription { name: "Bull Market Rally".to_string(), scenario_type: ScenarioType::Rally, description: "Strong upward momentum with low volatility".to_string(), severity: 0.6, duration_days: 90, }, ScenarioDescription { name: "Post-Crisis Recovery".to_string(), scenario_type: ScenarioType::Rally, description: "Rapid recovery following market stress".to_string(), severity: 0.7, duration_days: 60, }, ScenarioDescription { name: "VIX Spike".to_string(), scenario_type: ScenarioType::HighVolatility, description: "Sudden increase in market volatility".to_string(), severity: 0.5, duration_days: 20, }, ScenarioDescription { name: "Fed Rate Hike".to_string(), scenario_type: ScenarioType::RateShock, description: "Aggressive monetary tightening".to_string(), severity: 0.6, duration_days: 45, }, ScenarioDescription { name: "Stagflation".to_string(), scenario_type: ScenarioType::InflationSpike, description: "Rising inflation with slowing growth".to_string(), severity: 0.7, duration_days: 90, }, ] } #[cfg(test)] mod tests { use super::*; #[test] fn test_sample_assets() { let assets = get_sample_assets(); assert_eq!(assets.len(), 5); assert_eq!(assets[0].symbol, "SPY"); } #[test] fn test_sample_market_state() { let state = get_sample_market_state(); assert_eq!(state.prices.len(), 5); assert!(state.indicators.vix > 0.0); } #[test] fn test_sample_historical_data() { let data = get_sample_historical_data(); assert_eq!(data.len(), 5); assert_eq!(data[0].close.len(), 252); } #[test] fn test_crisis_request() { let request = create_crisis_simulation_request(); assert_eq!(request.assets.len(), 5); assert_eq!(request.num_paths, 100); assert_eq!(request.scenario.scenario_type, ScenarioType::Crisis); } #[test] fn test_rally_request() { let request = create_rally_simulation_request(); assert_eq!(request.scenario.scenario_type, ScenarioType::Rally); } #[test] fn test_predefined_scenarios() { let scenarios = get_predefined_scenarios(); assert!(scenarios.len() >= 5); } #[test] fn test_price_history_reasonable() { let data = get_sample_historical_data(); for history in &data { for &price in &history.close { assert!(price > 0.0); assert!(price < 10000.0); } } } }