//! Sample data utilities for the QuantumPort demo. use quantumport_shared::{ Asset, AssetClass, AssetReturns, OptimizationMethod, OptimizationObjectives, OptimizationRequest, PortfolioConstraints, }; /// Get a comprehensive sample portfolio request. #[must_use] pub fn get_comprehensive_request() -> OptimizationRequest { OptimizationRequest { assets: get_diversified_portfolio(), returns: get_simulated_returns(), objectives: OptimizationObjectives { target_return: None, minimize_variance: true, maximize_skewness: true, minimize_kurtosis: true, risk_aversion: 2.0, skewness_preference: 1.0, kurtosis_aversion: 0.5, }, constraints: PortfolioConstraints { budget: 1.0, allow_short: false, max_weight: 0.30, min_weight: 0.05, max_assets: Some(8), sector_constraints: vec![], max_turnover: None, }, method: OptimizationMethod::HigherOrder, } } /// Get a diversified portfolio of assets. #[must_use] pub fn get_diversified_portfolio() -> Vec { vec![ // US Large Cap Asset { symbol: "SPY".to_string(), name: "S&P 500 ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("US Large Cap".to_string()), currency: "USD".to_string(), }, // US Tech Asset { symbol: "QQQ".to_string(), name: "NASDAQ 100 ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("US Technology".to_string()), currency: "USD".to_string(), }, // US Small Cap Asset { symbol: "IWM".to_string(), name: "Russell 2000 ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("US Small Cap".to_string()), currency: "USD".to_string(), }, // International Developed Asset { symbol: "EFA".to_string(), name: "EAFE ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("International Developed".to_string()), currency: "USD".to_string(), }, // Emerging Markets Asset { symbol: "EEM".to_string(), name: "Emerging Markets ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("Emerging Markets".to_string()), currency: "USD".to_string(), }, // US Bonds Asset { symbol: "AGG".to_string(), name: "US Aggregate Bond ETF".to_string(), asset_class: AssetClass::FixedIncome, sector: None, currency: "USD".to_string(), }, // Long-term Treasury Asset { symbol: "TLT".to_string(), name: "20+ Year Treasury Bond ETF".to_string(), asset_class: AssetClass::FixedIncome, sector: None, currency: "USD".to_string(), }, // High Yield Asset { symbol: "HYG".to_string(), name: "High Yield Corporate Bond ETF".to_string(), asset_class: AssetClass::FixedIncome, sector: None, currency: "USD".to_string(), }, // Gold Asset { symbol: "GLD".to_string(), name: "Gold ETF".to_string(), asset_class: AssetClass::Commodity, sector: None, currency: "USD".to_string(), }, // Real Estate Asset { symbol: "VNQ".to_string(), name: "Real Estate ETF".to_string(), asset_class: AssetClass::RealEstate, sector: None, currency: "USD".to_string(), }, ] } /// Get simulated historical returns. #[must_use] pub fn get_simulated_returns() -> Vec { let num_days = 252; // One year of trading days vec![ // SPY - moderate return, moderate vol simulate_asset_returns("SPY", num_days, 0.0004, 0.011, -0.15, 3.2), // QQQ - higher return, higher vol simulate_asset_returns("QQQ", num_days, 0.0005, 0.014, -0.25, 3.8), // IWM - moderate return, high vol simulate_asset_returns("IWM", num_days, 0.0003, 0.015, -0.30, 4.0), // EFA - lower return, moderate vol simulate_asset_returns("EFA", num_days, 0.0002, 0.012, -0.10, 3.1), // EEM - volatile emerging markets simulate_asset_returns("EEM", num_days, 0.0003, 0.018, -0.35, 4.5), // AGG - low return, low vol, positive skew simulate_asset_returns("AGG", num_days, 0.0001, 0.004, 0.10, 2.8), // TLT - negative correlation to equities simulate_asset_returns("TLT", num_days, 0.0001, 0.012, 0.20, 3.0), // HYG - bond-like but higher vol simulate_asset_returns("HYG", num_days, 0.0002, 0.007, -0.20, 3.5), // GLD - safe haven, positive skew simulate_asset_returns("GLD", num_days, 0.0002, 0.009, 0.25, 3.2), // VNQ - real estate, equity-like simulate_asset_returns("VNQ", num_days, 0.0003, 0.015, -0.25, 4.0), ] } /// Simulate returns for a single asset. fn simulate_asset_returns( symbol: &str, num_days: usize, mean: f64, std: f64, _skew: f64, _kurt: f64, ) -> AssetReturns { let mut returns = Vec::with_capacity(num_days); let mut seed = symbol .bytes() .fold(0u64, |acc, b| acc.wrapping_add(b as u64)) * 12345; for _ in 0..num_days { // LCG pseudo-random - use modulo to ensure [0, 1) range seed = seed.wrapping_mul(1103515245).wrapping_add(12345); let u1 = ((seed >> 16) % 32768) as f64 / 32768.0 + 0.0001; // Avoid 0 seed = seed.wrapping_mul(1103515245).wrapping_add(12345); let u2 = ((seed >> 16) % 32768) as f64 / 32768.0; // Box-Muller transform for normal distribution let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos(); // Clamp to reasonable daily return range (-15% to +15%) let ret = (mean + std * z).clamp(-0.15, 0.15); returns.push(ret); } AssetReturns { symbol: symbol.to_string(), returns, start_date: "2023-01-01".to_string(), end_date: "2023-12-31".to_string(), } } /// Create a request for aggressive growth portfolio. #[must_use] pub fn get_aggressive_growth_request() -> OptimizationRequest { let assets = vec![ Asset { symbol: "QQQ".to_string(), name: "NASDAQ 100 ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("US Technology".to_string()), currency: "USD".to_string(), }, Asset { symbol: "ARKK".to_string(), name: "ARK Innovation ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("Innovation".to_string()), currency: "USD".to_string(), }, Asset { symbol: "SOXX".to_string(), name: "Semiconductor ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("Semiconductors".to_string()), currency: "USD".to_string(), }, Asset { symbol: "XBI".to_string(), name: "Biotech ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("Biotech".to_string()), currency: "USD".to_string(), }, ]; let returns = vec![ simulate_asset_returns("QQQ", 252, 0.0006, 0.016, -0.20, 3.5), simulate_asset_returns("ARKK", 252, 0.0008, 0.028, -0.40, 5.0), simulate_asset_returns("SOXX", 252, 0.0007, 0.022, -0.30, 4.2), simulate_asset_returns("XBI", 252, 0.0005, 0.025, -0.35, 4.8), ]; OptimizationRequest { assets, returns, objectives: OptimizationObjectives { target_return: Some(0.20), minimize_variance: true, maximize_skewness: true, minimize_kurtosis: true, risk_aversion: 0.5, skewness_preference: 0.3, kurtosis_aversion: 0.2, }, constraints: PortfolioConstraints::default(), method: OptimizationMethod::HigherOrder, } } /// Create a request for conservative income portfolio. #[must_use] pub fn get_conservative_income_request() -> OptimizationRequest { let assets = vec![ Asset { symbol: "AGG".to_string(), name: "US Aggregate Bond ETF".to_string(), asset_class: AssetClass::FixedIncome, sector: None, currency: "USD".to_string(), }, Asset { symbol: "BND".to_string(), name: "Total Bond Market ETF".to_string(), asset_class: AssetClass::FixedIncome, sector: None, currency: "USD".to_string(), }, Asset { symbol: "VCIT".to_string(), name: "Intermediate Corporate Bond ETF".to_string(), asset_class: AssetClass::FixedIncome, sector: None, currency: "USD".to_string(), }, Asset { symbol: "SCHD".to_string(), name: "Dividend ETF".to_string(), asset_class: AssetClass::Equity, sector: Some("Dividend".to_string()), currency: "USD".to_string(), }, ]; let returns = vec![ simulate_asset_returns("AGG", 252, 0.0001, 0.004, 0.05, 2.8), simulate_asset_returns("BND", 252, 0.0001, 0.004, 0.05, 2.7), simulate_asset_returns("VCIT", 252, 0.00012, 0.005, 0.02, 2.9), simulate_asset_returns("SCHD", 252, 0.0003, 0.008, -0.10, 3.0), ]; OptimizationRequest { assets, returns, objectives: OptimizationObjectives { target_return: Some(0.05), minimize_variance: true, maximize_skewness: true, minimize_kurtosis: true, risk_aversion: 3.0, skewness_preference: 0.8, kurtosis_aversion: 0.8, }, constraints: PortfolioConstraints::default(), method: OptimizationMethod::MinimumVariance, } } #[cfg(test)] mod tests { use super::*; #[test] fn test_comprehensive_request() { let request = get_comprehensive_request(); assert_eq!(request.assets.len(), 10); assert_eq!(request.returns.len(), 10); } #[test] fn test_diversified_portfolio() { let assets = get_diversified_portfolio(); assert_eq!(assets.len(), 10); // Check for diversity of asset classes let equity_count = assets .iter() .filter(|a| a.asset_class == AssetClass::Equity) .count(); let fixed_income_count = assets .iter() .filter(|a| a.asset_class == AssetClass::FixedIncome) .count(); assert!(equity_count >= 3); assert!(fixed_income_count >= 2); } #[test] fn test_simulated_returns() { let returns = get_simulated_returns(); assert_eq!(returns.len(), 10); for ret in &returns { assert_eq!(ret.returns.len(), 252); } } #[test] fn test_aggressive_growth() { let request = get_aggressive_growth_request(); assert_eq!(request.assets.len(), 4); assert!(request.objectives.risk_aversion < 1.0); // Low risk aversion = aggressive } #[test] fn test_conservative_income() { let request = get_conservative_income_request(); assert_eq!(request.assets.len(), 4); assert!(request.objectives.risk_aversion > 2.0); // High risk aversion = conservative } #[test] fn test_returns_reasonable_range() { let returns = get_simulated_returns(); for asset_returns in &returns { for &r in &asset_returns.returns { // Daily returns should typically be within -10% to +10% assert!(r > -0.2 && r < 0.2); } } } }