//! Shared types for MarketSim - Financial World Model. //! //! This crate defines the IPC types for market simulation and scenario generation. use serde::{Deserialize, Serialize}; // ============================================================================ // Market State Types // ============================================================================ /// A financial asset/security. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Asset { /// Ticker symbol. pub symbol: String, /// Asset name. pub name: String, /// Asset type. pub asset_type: AssetType, /// Current price. pub current_price: f64, } /// Type of financial asset. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum AssetType { /// Equity/Stock. Equity, /// Index. Index, /// Bond. Bond, /// Commodity. Commodity, /// Currency. Currency, /// Cryptocurrency. Crypto, } /// Historical price data for an asset. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PriceHistory { /// Asset symbol. pub symbol: String, /// Dates (YYYY-MM-DD format). pub dates: Vec, /// Open prices. pub open: Vec, /// High prices. pub high: Vec, /// Low prices. pub low: Vec, /// Close prices. pub close: Vec, /// Volume. pub volume: Vec, } impl PriceHistory { /// Get the number of data points. #[must_use] pub fn len(&self) -> usize { self.close.len() } /// Check if empty. #[must_use] pub fn is_empty(&self) -> bool { self.close.is_empty() } /// Calculate returns from close prices. #[must_use] pub fn returns(&self) -> Vec { self.close .windows(2) .map(|w| (w[1] - w[0]) / w[0]) .collect() } } /// Current market state snapshot. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MarketState { /// Timestamp (ISO 8601). pub timestamp: String, /// Asset prices. pub prices: Vec, /// Market indicators. pub indicators: MarketIndicators, /// Economic factors. pub economic_factors: EconomicFactors, } /// Single asset price. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AssetPrice { /// Asset symbol. pub symbol: String, /// Price. pub price: f64, /// Daily change (%). pub change_pct: f64, } /// Market-wide indicators. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MarketIndicators { /// VIX volatility index. pub vix: f64, /// S&P 500 level. pub sp500: f64, /// 10-year Treasury yield. pub treasury_10y: f64, /// Credit spread (high yield - treasury). pub credit_spread: f64, /// Put/Call ratio. pub put_call_ratio: f64, } impl Default for MarketIndicators { fn default() -> Self { Self { vix: 15.0, sp500: 5000.0, treasury_10y: 4.0, credit_spread: 3.5, put_call_ratio: 0.8, } } } /// Economic factors. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EconomicFactors { /// GDP growth rate (%). pub gdp_growth: f64, /// Inflation rate (%). pub inflation: f64, /// Unemployment rate (%). pub unemployment: f64, /// Federal funds rate (%). pub fed_funds_rate: f64, /// Consumer sentiment index. pub consumer_sentiment: f64, } impl Default for EconomicFactors { fn default() -> Self { Self { gdp_growth: 2.5, inflation: 2.5, unemployment: 4.0, fed_funds_rate: 5.0, consumer_sentiment: 100.0, } } } // ============================================================================ // Scenario Types // ============================================================================ /// A market scenario description. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ScenarioDescription { /// Scenario name. pub name: String, /// Scenario type. pub scenario_type: ScenarioType, /// Text description. pub description: String, /// Severity (0-1, higher = more severe). pub severity: f64, /// Duration in trading days. pub duration_days: usize, } /// Type of market scenario. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum ScenarioType { /// Bear market/crash. Crisis, /// Bull market rally. Rally, /// High volatility regime. HighVolatility, /// Low volatility calm. LowVolatility, /// Sector rotation. SectorRotation, /// Interest rate shock. RateShock, /// Inflation spike. InflationSpike, /// Custom user-defined. Custom, } impl ScenarioType { /// Get a display name. #[must_use] pub fn display_name(&self) -> &'static str { match self { ScenarioType::Crisis => "Market Crisis", ScenarioType::Rally => "Bull Rally", ScenarioType::HighVolatility => "High Volatility", ScenarioType::LowVolatility => "Low Volatility", ScenarioType::SectorRotation => "Sector Rotation", ScenarioType::RateShock => "Rate Shock", ScenarioType::InflationSpike => "Inflation Spike", ScenarioType::Custom => "Custom Scenario", } } } /// Request to generate market scenarios. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ScenarioRequest { /// Assets to simulate. pub assets: Vec, /// Historical data for conditioning. pub historical_data: Vec, /// Current market state. pub initial_state: MarketState, /// Scenario description. pub scenario: ScenarioDescription, /// Number of paths to generate. pub num_paths: usize, /// Simulation horizon (trading days). pub horizon_days: usize, /// Random seed for reproducibility. pub seed: Option, } /// Generated scenario result. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ScenarioResult { /// Scenario used. pub scenario: ScenarioDescription, /// Generated price paths for each asset. pub price_paths: Vec, /// Path statistics. pub statistics: PathStatistics, /// Generation metadata. pub metadata: SimulationMetadata, } /// Price paths for a single asset. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AssetPricePaths { /// Asset symbol. pub symbol: String, /// Price paths (outer = path, inner = time steps). pub paths: Vec>, /// Return paths. pub return_paths: Vec>, } /// Statistics across all paths. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PathStatistics { /// Per-asset statistics. pub asset_stats: Vec, /// Correlation matrix at final time. pub final_correlation: Vec>, } /// Statistics for a single asset's paths. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AssetPathStats { /// Asset symbol. pub symbol: String, /// Mean final price. pub mean_final_price: f64, /// Median final price. pub median_final_price: f64, /// 5th percentile final price. pub pct_5_final_price: f64, /// 95th percentile final price. pub pct_95_final_price: f64, /// Mean return over horizon. pub mean_return: f64, /// Volatility (annualized). pub volatility: f64, /// Maximum drawdown (mean across paths). pub mean_max_drawdown: f64, } /// Simulation metadata. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SimulationMetadata { /// Computation time in milliseconds. pub computation_time_ms: u64, /// Model version. pub model_version: String, /// Seed used. pub seed_used: u64, } // ============================================================================ // Backtesting Types // ============================================================================ /// Trading strategy definition. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Strategy { /// Strategy name. pub name: String, /// Strategy type. pub strategy_type: StrategyType, /// Parameters. pub parameters: StrategyParameters, } /// Type of trading strategy. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum StrategyType { /// Buy and hold. BuyAndHold, /// Momentum following. Momentum, /// Mean reversion. MeanReversion, /// Risk parity. RiskParity, /// Trend following. TrendFollowing, /// Custom strategy. Custom, } /// Strategy parameters. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StrategyParameters { /// Lookback window (days). pub lookback: usize, /// Rebalance frequency (days). pub rebalance_frequency: usize, /// Max position size (fraction). pub max_position: f64, /// Stop loss percentage. pub stop_loss: Option, /// Take profit percentage. pub take_profit: Option, /// Custom parameters. pub custom: std::collections::HashMap, } impl Default for StrategyParameters { fn default() -> Self { Self { lookback: 20, rebalance_frequency: 5, max_position: 0.25, stop_loss: None, take_profit: None, custom: std::collections::HashMap::new(), } } } /// Backtest request. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BacktestRequest { /// Strategy to test. pub strategy: Strategy, /// Scenario results to test on. pub scenarios: Vec, /// Initial capital. pub initial_capital: f64, /// Transaction cost (basis points). pub transaction_cost_bps: f64, } /// Backtest result. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BacktestResult { /// Strategy tested. pub strategy: Strategy, /// Per-scenario results. pub scenario_results: Vec, /// Aggregate statistics. pub aggregate_stats: AggregateBacktestStats, } /// Backtest result for a single scenario. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ScenarioBacktestResult { /// Scenario name. pub scenario_name: String, /// Mean final portfolio value. pub mean_final_value: f64, /// Mean total return. pub mean_total_return: f64, /// Mean Sharpe ratio. pub mean_sharpe_ratio: f64, /// Mean max drawdown. pub mean_max_drawdown: f64, /// Probability of loss. pub probability_of_loss: f64, } /// Aggregate statistics across all scenarios. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AggregateBacktestStats { /// Overall mean return. pub overall_mean_return: f64, /// Overall Sharpe ratio. pub overall_sharpe_ratio: f64, /// Worst case return. pub worst_case_return: f64, /// Best case return. pub best_case_return: f64, /// Win rate (% of scenarios with positive return). pub win_rate: f64, } // ============================================================================ // Sample Data // ============================================================================ /// Get sample assets for demo. #[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, }, ] } /// 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, }, ], indicators: MarketIndicators::default(), economic_factors: EconomicFactors::default(), } } /// Get predefined crisis scenario. #[must_use] pub fn get_crisis_scenario() -> ScenarioDescription { 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, } } /// Get predefined rally scenario. #[must_use] pub fn get_rally_scenario() -> ScenarioDescription { 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, } } /// Get predefined high volatility scenario. #[must_use] pub fn get_high_vol_scenario() -> ScenarioDescription { ScenarioDescription { name: "Elevated Volatility Regime".to_string(), scenario_type: ScenarioType::HighVolatility, description: "Choppy market with large daily swings".to_string(), severity: 0.5, duration_days: 30, } } // ============================================================================ // Tests // ============================================================================ #[cfg(test)] mod tests { use super::*; #[test] fn test_asset_type() { assert_eq!( serde_json::to_string(&AssetType::Equity).unwrap(), "\"Equity\"" ); } #[test] fn test_sample_assets() { let assets = get_sample_assets(); assert_eq!(assets.len(), 4); assert_eq!(assets[0].symbol, "SPY"); } #[test] fn test_sample_market_state() { let state = get_sample_market_state(); assert_eq!(state.prices.len(), 4); assert!(state.indicators.vix > 0.0); } #[test] fn test_price_history_returns() { let history = PriceHistory { symbol: "TEST".to_string(), dates: vec![ "2024-01-01".to_string(), "2024-01-02".to_string(), "2024-01-03".to_string(), ], open: vec![100.0, 101.0, 102.0], high: vec![101.0, 102.0, 103.0], low: vec![99.0, 100.0, 101.0], close: vec![100.0, 102.0, 101.0], volume: vec![1000.0, 1100.0, 900.0], }; let returns = history.returns(); assert_eq!(returns.len(), 2); assert!((returns[0] - 0.02).abs() < 0.001); assert!((returns[1] - (-0.0098)).abs() < 0.001); } #[test] fn test_scenario_types() { assert_eq!(ScenarioType::Crisis.display_name(), "Market Crisis"); assert_eq!(ScenarioType::Rally.display_name(), "Bull Rally"); } #[test] fn test_strategy_parameters_default() { let params = StrategyParameters::default(); assert_eq!(params.lookback, 20); assert_eq!(params.rebalance_frequency, 5); } #[test] fn test_serialization() { let scenario = get_crisis_scenario(); let json = serde_json::to_string(&scenario).unwrap(); assert!(json.contains("2008-Style")); let parsed: ScenarioDescription = serde_json::from_str(&json).unwrap(); assert_eq!(parsed.name, scenario.name); } }