//! QuantumPort - Higher-Order Portfolio Optimization Demo. //! //! This demo showcases portfolio optimization using higher-order moments //! (skewness, kurtosis) beyond traditional mean-variance optimization. pub mod comparison; pub mod frontier; pub mod moments; pub mod optimizer; pub mod sample_data; use quantumport_shared::{ EfficientFrontier, OptimizationRequest, OptimizedPortfolio, PortfolioComparison, PortfolioConstraints, }; use thiserror::Error; use comparison::PortfolioComparer; use frontier::FrontierGenerator; use optimizer::PortfolioOptimizer; /// Errors that can occur during portfolio optimization. #[derive(Debug, Error)] pub enum QuantumPortError { /// No assets provided. #[error("No assets provided for optimization")] NoAssets, /// No returns data provided. #[error("No returns data provided")] NoReturnsData, /// Mismatched assets and returns. #[error("Asset count ({0}) doesn't match returns count ({1})")] MismatchedData(usize, usize), /// Optimization failed. #[error("Optimization failed: {0}")] OptimizationFailed(String), /// Invalid constraints. #[error("Invalid constraints: {0}")] InvalidConstraints(String), /// Numerical error. #[error("Numerical error: {0}")] NumericalError(String), } /// Main QuantumPort system for higher-order portfolio optimization. #[derive(Debug)] pub struct QuantumPort { optimizer: PortfolioOptimizer, frontier_generator: FrontierGenerator, comparer: PortfolioComparer, } impl Default for QuantumPort { fn default() -> Self { Self::new() } } impl QuantumPort { /// Create a new QuantumPort system. #[must_use] pub fn new() -> Self { Self { optimizer: PortfolioOptimizer::new(), frontier_generator: FrontierGenerator::new(), comparer: PortfolioComparer::new(), } } /// Optimize a portfolio based on the request. /// /// # Errors /// /// Returns error if optimization fails or input data is invalid. pub fn optimize( &self, request: &OptimizationRequest, ) -> Result { // Validate input self.validate_request(request)?; // Run optimization self.optimizer.optimize(request) } /// Generate efficient frontier. /// /// # Errors /// /// Returns error if frontier generation fails. pub fn generate_frontier( &self, request: &OptimizationRequest, num_points: usize, ) -> Result { self.validate_request(request)?; self.frontier_generator.generate(request, num_points) } /// Compare mean-variance vs higher-order optimization. /// /// # Errors /// /// Returns error if comparison fails. pub fn compare_methods( &self, request: &OptimizationRequest, ) -> Result { self.validate_request(request)?; self.comparer.compare(request) } /// Validate optimization request. fn validate_request(&self, request: &OptimizationRequest) -> Result<(), QuantumPortError> { if request.assets.is_empty() { return Err(QuantumPortError::NoAssets); } if request.returns.is_empty() { return Err(QuantumPortError::NoReturnsData); } if request.assets.len() != request.returns.len() { return Err(QuantumPortError::MismatchedData( request.assets.len(), request.returns.len(), )); } self.validate_constraints(&request.constraints)?; Ok(()) } /// Validate portfolio constraints. fn validate_constraints( &self, constraints: &PortfolioConstraints, ) -> Result<(), QuantumPortError> { if constraints.budget <= 0.0 { return Err(QuantumPortError::InvalidConstraints( "Budget must be positive".to_string(), )); } if constraints.max_weight < constraints.min_weight { return Err(QuantumPortError::InvalidConstraints( "max_weight must be >= min_weight".to_string(), )); } if !constraints.allow_short && constraints.min_weight < 0.0 { return Err(QuantumPortError::InvalidConstraints( "min_weight must be >= 0 when short selling is not allowed".to_string(), )); } Ok(()) } } /// Run a complete demo with sample data. /// /// # Errors /// /// Returns error if demo fails. pub fn run_demo() -> Result { let request = quantumport_shared::get_sample_request(); let qp = QuantumPort::new(); qp.compare_methods(&request) } #[cfg(test)] mod tests { use super::*; use quantumport_shared::{ Asset, AssetClass, AssetReturns, OptimizationMethod, OptimizationObjectives, }; fn create_test_request() -> OptimizationRequest { OptimizationRequest { assets: vec![ Asset { symbol: "A".to_string(), name: "Asset A".to_string(), asset_class: AssetClass::Equity, sector: None, currency: "USD".to_string(), }, Asset { symbol: "B".to_string(), name: "Asset B".to_string(), asset_class: AssetClass::FixedIncome, sector: None, currency: "USD".to_string(), }, ], returns: vec![ AssetReturns { symbol: "A".to_string(), returns: vec![0.01, -0.02, 0.015, 0.005, -0.01], start_date: "2024-01-01".to_string(), end_date: "2024-01-05".to_string(), }, AssetReturns { symbol: "B".to_string(), returns: vec![0.002, 0.001, 0.003, -0.001, 0.002], start_date: "2024-01-01".to_string(), end_date: "2024-01-05".to_string(), }, ], objectives: OptimizationObjectives::default(), constraints: PortfolioConstraints::default(), method: OptimizationMethod::HigherOrder, } } #[test] fn test_quantumport_creation() { let qp = QuantumPort::new(); assert!(std::mem::size_of_val(&qp) > 0); } #[test] fn test_optimize() { let request = create_test_request(); let qp = QuantumPort::new(); let result = qp.optimize(&request); assert!(result.is_ok()); let portfolio = result.unwrap(); assert!(!portfolio.weights.is_empty()); } #[test] fn test_no_assets_error() { let mut request = create_test_request(); request.assets.clear(); request.returns.clear(); let qp = QuantumPort::new(); let result = qp.optimize(&request); assert!(matches!(result.unwrap_err(), QuantumPortError::NoAssets)); } #[test] fn test_mismatched_data_error() { let mut request = create_test_request(); request.returns.pop(); let qp = QuantumPort::new(); let result = qp.optimize(&request); assert!(matches!( result.unwrap_err(), QuantumPortError::MismatchedData(_, _) )); } #[test] fn test_generate_frontier() { let request = create_test_request(); let qp = QuantumPort::new(); let result = qp.generate_frontier(&request, 10); assert!(result.is_ok()); let frontier = result.unwrap(); assert!(!frontier.points.is_empty()); } #[test] fn test_compare_methods() { let request = create_test_request(); let qp = QuantumPort::new(); let result = qp.compare_methods(&request); assert!(result.is_ok()); let comparison = result.unwrap(); assert!(!comparison.mean_variance.weights.is_empty()); assert!(!comparison.higher_order.weights.is_empty()); } #[test] fn test_run_demo() { let result = run_demo(); assert!(result.is_ok()); } #[test] fn test_invalid_constraints() { let mut request = create_test_request(); request.constraints.max_weight = -1.0; let qp = QuantumPort::new(); let result = qp.optimize(&request); assert!(matches!( result.unwrap_err(), QuantumPortError::InvalidConstraints(_) )); } }