//! Shared IPC types for QuantumPort higher-order portfolio optimization demo. //! //! This crate provides data structures for communication between //! the Tauri frontend and Rust backend for portfolio optimization. use serde::{Deserialize, Serialize}; // ============================================================================ // Asset Types // ============================================================================ /// Individual asset in the portfolio universe. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Asset { /// Asset ticker symbol pub symbol: String, /// Asset name pub name: String, /// Asset class pub asset_class: AssetClass, /// Sector (for equities) pub sector: Option, /// Currency pub currency: String, } /// Asset class. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum AssetClass { /// Equity / Stock Equity, /// Fixed income / Bond FixedIncome, /// Commodity Commodity, /// Real estate RealEstate, /// Cryptocurrency Crypto, /// Cash equivalent Cash, /// Alternative investment Alternative, } /// Historical returns data for an asset. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AssetReturns { /// Asset symbol pub symbol: String, /// Daily returns (chronological order) pub returns: Vec, /// Start date (ISO 8601) pub start_date: String, /// End date (ISO 8601) pub end_date: String, } // ============================================================================ // Statistical Moments // ============================================================================ /// First four statistical moments for portfolio analysis. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MomentStatistics { /// Expected return (1st moment) pub mean: f64, /// Variance (2nd moment) pub variance: f64, /// Skewness (3rd moment) - asymmetry of distribution pub skewness: f64, /// Kurtosis (4th moment) - tail heaviness pub kurtosis: f64, } /// Covariance matrix for portfolio assets. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CovarianceMatrix { /// Asset symbols in order pub symbols: Vec, /// Flattened covariance matrix (row-major) pub data: Vec, /// Matrix dimension (n x n) pub dimension: usize, } impl CovarianceMatrix { /// Get covariance between two assets by index. pub fn get(&self, i: usize, j: usize) -> Option { if i < self.dimension && j < self.dimension { Some(self.data[i * self.dimension + j]) } else { None } } } /// Co-skewness tensor (3rd order tensor). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CoskewnessTensor { /// Asset symbols pub symbols: Vec, /// Flattened tensor data pub data: Vec, /// Tensor dimension (n x n x n) pub dimension: usize, } /// Co-kurtosis tensor (4th order tensor). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CokurtosisTensor { /// Asset symbols pub symbols: Vec, /// Flattened tensor data pub data: Vec, /// Tensor dimension (n x n x n x n) pub dimension: usize, } // ============================================================================ // Optimization Types // ============================================================================ /// Portfolio optimization request. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OptimizationRequest { /// Available assets pub assets: Vec, /// Historical returns for each asset pub returns: Vec, /// Optimization objectives pub objectives: OptimizationObjectives, /// Portfolio constraints pub constraints: PortfolioConstraints, /// Optimization method pub method: OptimizationMethod, } /// Optimization objectives (what to optimize for). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OptimizationObjectives { /// Target return (optional) pub target_return: Option, /// Minimize variance pub minimize_variance: bool, /// Maximize skewness (prefer positive skew) pub maximize_skewness: bool, /// Minimize kurtosis (avoid fat tails) pub minimize_kurtosis: bool, /// Risk aversion coefficient (higher = more risk averse) pub risk_aversion: f64, /// Skewness preference coefficient pub skewness_preference: f64, /// Kurtosis aversion coefficient pub kurtosis_aversion: f64, } impl Default for OptimizationObjectives { fn default() -> Self { Self { target_return: None, minimize_variance: true, maximize_skewness: true, minimize_kurtosis: true, risk_aversion: 1.0, skewness_preference: 0.5, kurtosis_aversion: 0.5, } } } /// Portfolio constraints. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PortfolioConstraints { /// Budget constraint (weights sum to this, typically 1.0) pub budget: f64, /// Allow short selling pub allow_short: bool, /// Maximum weight per asset pub max_weight: f64, /// Minimum weight per asset (0 for long-only) pub min_weight: f64, /// Maximum number of assets to hold (cardinality) pub max_assets: Option, /// Sector constraints (max weight per sector) pub sector_constraints: Vec, /// Turnover constraint (max change from current portfolio) pub max_turnover: Option, } impl Default for PortfolioConstraints { fn default() -> Self { Self { budget: 1.0, allow_short: false, max_weight: 1.0, min_weight: 0.0, max_assets: None, sector_constraints: vec![], max_turnover: None, } } } /// Sector weight constraint. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SectorConstraint { /// Sector name pub sector: String, /// Maximum weight in sector pub max_weight: f64, /// Minimum weight in sector (optional) pub min_weight: Option, } /// Optimization method. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum OptimizationMethod { /// Classical mean-variance (Markowitz) MeanVariance, /// Mean-variance with skewness MeanVarianceSkewness, /// Full higher-order optimization (mean, variance, skewness, kurtosis) HigherOrder, /// QAOA-inspired classical optimization QAOAInspired, /// Minimum variance portfolio MinimumVariance, /// Maximum Sharpe ratio MaximumSharpe, /// Risk parity RiskParity, } // ============================================================================ // Portfolio Results // ============================================================================ /// Optimized portfolio result. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OptimizedPortfolio { /// Asset weights pub weights: Vec, /// Portfolio statistics pub statistics: PortfolioStatistics, /// Risk metrics pub risk_metrics: RiskMetrics, /// Optimization metadata pub metadata: OptimizationMetadata, } /// Individual asset weight in portfolio. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PortfolioWeight { /// Asset symbol pub symbol: String, /// Weight (fraction of portfolio) pub weight: f64, /// Value (if portfolio value provided) pub value: Option, } /// Portfolio-level statistics. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PortfolioStatistics { /// Expected annual return pub expected_return: f64, /// Annual volatility (std dev) pub volatility: f64, /// Skewness of portfolio returns pub skewness: f64, /// Excess kurtosis of portfolio returns pub kurtosis: f64, /// Sharpe ratio pub sharpe_ratio: f64, /// Sortino ratio pub sortino_ratio: f64, /// Maximum drawdown pub max_drawdown: f64, } /// Portfolio risk metrics. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RiskMetrics { /// Value at Risk (95%) pub var_95: f64, /// Value at Risk (99%) pub var_99: f64, /// Conditional VaR / Expected Shortfall (95%) pub cvar_95: f64, /// Conditional VaR / Expected Shortfall (99%) pub cvar_99: f64, /// Beta to market pub beta: f64, /// Tracking error (if benchmark provided) pub tracking_error: Option, /// Information ratio (if benchmark provided) pub information_ratio: Option, } /// Optimization metadata. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OptimizationMetadata { /// Method used pub method: OptimizationMethod, /// Number of iterations pub iterations: usize, /// Convergence achieved pub converged: bool, /// Final objective value pub objective_value: f64, /// Optimization time (seconds) pub time_seconds: f64, /// Constraints satisfied pub constraints_satisfied: bool, } // ============================================================================ // Efficient Frontier // ============================================================================ /// Efficient frontier response. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EfficientFrontier { /// Points on the frontier pub points: Vec, /// Minimum variance portfolio pub min_variance_portfolio: OptimizedPortfolio, /// Maximum Sharpe portfolio (tangency) pub max_sharpe_portfolio: OptimizedPortfolio, /// Frontier type pub frontier_type: FrontierType, } /// Single point on efficient frontier. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FrontierPoint { /// Expected return at this point pub expected_return: f64, /// Volatility at this point pub volatility: f64, /// Skewness at this point (for 3D frontier) pub skewness: Option, /// Kurtosis at this point pub kurtosis: Option, /// Sharpe ratio at this point pub sharpe_ratio: f64, /// Portfolio weights at this point pub weights: Vec, } /// Type of efficient frontier. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum FrontierType { /// Classical mean-variance frontier MeanVariance, /// Mean-variance-skewness frontier (3D) MeanVarianceSkewness, /// Higher-order frontier HigherOrder, } // ============================================================================ // Comparison Types // ============================================================================ /// Portfolio comparison result. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PortfolioComparison { /// Mean-variance optimized portfolio pub mean_variance: OptimizedPortfolio, /// Higher-order optimized portfolio pub higher_order: OptimizedPortfolio, /// Comparison metrics pub comparison: ComparisonMetrics, } /// Metrics comparing two portfolio approaches. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComparisonMetrics { /// Return difference (higher-order - mean-variance) pub return_difference: f64, /// Volatility difference pub volatility_difference: f64, /// Skewness improvement pub skewness_improvement: f64, /// Kurtosis reduction pub kurtosis_reduction: f64, /// Sharpe ratio difference pub sharpe_difference: f64, /// CVaR improvement (reduction in tail risk) pub cvar_improvement: f64, } // ============================================================================ // Sample Data // ============================================================================ /// Get sample optimization request for demo. pub fn get_sample_request() -> OptimizationRequest { OptimizationRequest { assets: get_sample_assets(), returns: get_sample_returns(), objectives: OptimizationObjectives::default(), constraints: PortfolioConstraints::default(), method: OptimizationMethod::HigherOrder, } } /// Get sample assets. pub fn get_sample_assets() -> Vec { vec![ Asset { symbol: "SPY".to_string(), name: "S&P 500 ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("Broad Market".to_string()), currency: "USD".to_string(), }, Asset { symbol: "QQQ".to_string(), name: "NASDAQ 100 ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("Technology".to_string()), currency: "USD".to_string(), }, Asset { symbol: "IWM".to_string(), name: "Russell 2000 ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("Small Cap".to_string()), currency: "USD".to_string(), }, Asset { symbol: "TLT".to_string(), name: "20+ Year Treasury Bond ETF".to_string(), asset_class: AssetClass::FixedIncome, sector: None, currency: "USD".to_string(), }, Asset { symbol: "GLD".to_string(), name: "Gold ETF".to_string(), asset_class: AssetClass::Commodity, sector: None, currency: "USD".to_string(), }, Asset { symbol: "VNQ".to_string(), name: "Real Estate ETF".to_string(), asset_class: AssetClass::RealEstate, sector: None, currency: "USD".to_string(), }, ] } /// Get sample returns data (simulated). pub fn get_sample_returns() -> Vec { // Simulate 252 trading days of returns vec![ AssetReturns { symbol: "SPY".to_string(), returns: simulate_returns(252, 0.0004, 0.012, -0.1, 3.5), start_date: "2023-01-01".to_string(), end_date: "2023-12-31".to_string(), }, AssetReturns { symbol: "QQQ".to_string(), returns: simulate_returns(252, 0.0005, 0.015, -0.2, 4.0), start_date: "2023-01-01".to_string(), end_date: "2023-12-31".to_string(), }, AssetReturns { symbol: "IWM".to_string(), returns: simulate_returns(252, 0.0003, 0.016, -0.3, 4.2), start_date: "2023-01-01".to_string(), end_date: "2023-12-31".to_string(), }, AssetReturns { symbol: "TLT".to_string(), returns: simulate_returns(252, 0.0001, 0.010, 0.1, 3.0), start_date: "2023-01-01".to_string(), end_date: "2023-12-31".to_string(), }, AssetReturns { symbol: "GLD".to_string(), returns: simulate_returns(252, 0.0002, 0.011, 0.3, 3.8), start_date: "2023-01-01".to_string(), end_date: "2023-12-31".to_string(), }, AssetReturns { symbol: "VNQ".to_string(), returns: simulate_returns(252, 0.0003, 0.014, -0.4, 4.5), start_date: "2023-01-01".to_string(), end_date: "2023-12-31".to_string(), }, ] } /// Simulate returns with given characteristics. fn simulate_returns(n: usize, mean: f64, std: f64, _skew: f64, _kurt: f64) -> Vec { // Simple pseudo-random simulation (deterministic for demo) let mut returns = Vec::with_capacity(n); let mut seed = 12345u64; for _ in 0..n { // LCG pseudo-random seed = seed.wrapping_mul(1103515245).wrapping_add(12345); let u1 = (seed >> 16) as f64 / 32768.0; seed = seed.wrapping_mul(1103515245).wrapping_add(12345); let u2 = (seed >> 16) as f64 / 32768.0; // Box-Muller transform let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos(); let ret = mean + std * z; returns.push(ret); } returns } // ============================================================================ // Tests // ============================================================================ #[cfg(test)] mod tests { use super::*; #[test] fn test_asset_class() { let asset = Asset { symbol: "AAPL".to_string(), name: "Apple Inc.".to_string(), asset_class: AssetClass::Equity, sector: Some("Technology".to_string()), currency: "USD".to_string(), }; assert_eq!(asset.asset_class, AssetClass::Equity); } #[test] fn test_optimization_objectives_default() { let obj = OptimizationObjectives::default(); assert!(obj.minimize_variance); assert!(obj.maximize_skewness); assert!(obj.minimize_kurtosis); } #[test] fn test_portfolio_constraints_default() { let constraints = PortfolioConstraints::default(); assert!((constraints.budget - 1.0).abs() < 1e-10); assert!(!constraints.allow_short); } #[test] fn test_covariance_matrix_get() { let cov = CovarianceMatrix { symbols: vec!["A".to_string(), "B".to_string()], data: vec![0.04, 0.02, 0.02, 0.09], dimension: 2, }; assert!((cov.get(0, 0).unwrap() - 0.04).abs() < 1e-10); assert!((cov.get(0, 1).unwrap() - 0.02).abs() < 1e-10); assert!((cov.get(1, 1).unwrap() - 0.09).abs() < 1e-10); } #[test] fn test_sample_assets() { let assets = get_sample_assets(); assert_eq!(assets.len(), 6); assert_eq!(assets[0].symbol, "SPY"); } #[test] fn test_sample_returns() { let returns = get_sample_returns(); assert_eq!(returns.len(), 6); assert_eq!(returns[0].returns.len(), 252); } #[test] fn test_sample_request() { let request = get_sample_request(); assert!(!request.assets.is_empty()); assert!(!request.returns.is_empty()); assert_eq!(request.method, OptimizationMethod::HigherOrder); } #[test] fn test_serialization() { let request = get_sample_request(); let json = serde_json::to_string(&request).unwrap(); assert!(json.contains("SPY")); } #[test] fn test_portfolio_weight() { let weight = PortfolioWeight { symbol: "SPY".to_string(), weight: 0.25, value: Some(25000.0), }; assert!((weight.weight - 0.25).abs() < 1e-10); } #[test] fn test_moment_statistics() { let stats = MomentStatistics { mean: 0.10, variance: 0.04, skewness: -0.2, kurtosis: 3.5, }; assert!((stats.mean - 0.10).abs() < 1e-10); assert!((stats.skewness - (-0.2)).abs() < 1e-10); } }