Files
rustytorch/demos/marketsim-shared/src/lib.rs
T
2026-03-04 00:08:42 +00:00

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);
}
}