598 lines
16 KiB
Rust
598 lines
16 KiB
Rust
//! 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<String>,
|
|
/// Open prices.
|
|
pub open: Vec<f64>,
|
|
/// High prices.
|
|
pub high: Vec<f64>,
|
|
/// Low prices.
|
|
pub low: Vec<f64>,
|
|
/// Close prices.
|
|
pub close: Vec<f64>,
|
|
/// Volume.
|
|
pub volume: Vec<f64>,
|
|
}
|
|
|
|
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<f64> {
|
|
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<AssetPrice>,
|
|
/// 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<Asset>,
|
|
/// Historical data for conditioning.
|
|
pub historical_data: Vec<PriceHistory>,
|
|
/// 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<u64>,
|
|
}
|
|
|
|
/// 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<AssetPricePaths>,
|
|
/// 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<Vec<f64>>,
|
|
/// Return paths.
|
|
pub return_paths: Vec<Vec<f64>>,
|
|
}
|
|
|
|
/// Statistics across all paths.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PathStatistics {
|
|
/// Per-asset statistics.
|
|
pub asset_stats: Vec<AssetPathStats>,
|
|
/// Correlation matrix at final time.
|
|
pub final_correlation: Vec<Vec<f64>>,
|
|
}
|
|
|
|
/// 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<f64>,
|
|
/// Take profit percentage.
|
|
pub take_profit: Option<f64>,
|
|
/// Custom parameters.
|
|
pub custom: std::collections::HashMap<String, f64>,
|
|
}
|
|
|
|
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<ScenarioResult>,
|
|
/// 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<ScenarioBacktestResult>,
|
|
/// 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<Asset> {
|
|
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);
|
|
}
|
|
}
|