Files
rustytorch/demos/rtx-quantumport-demo/src/comparison.rs
T
2026-03-04 00:08:42 +00:00

291 lines
9.6 KiB
Rust

//! Portfolio comparison between mean-variance and higher-order optimization.
use crate::QuantumPortError;
use crate::optimizer::PortfolioOptimizer;
use quantumport_shared::{
ComparisonMetrics, OptimizationMethod, OptimizationRequest, PortfolioComparison,
};
/// Portfolio method comparer.
#[derive(Debug)]
pub struct PortfolioComparer {
/// Optimizer
optimizer: PortfolioOptimizer,
}
impl Default for PortfolioComparer {
fn default() -> Self {
Self::new()
}
}
impl PortfolioComparer {
/// Create a new comparer.
#[must_use]
pub fn new() -> Self {
Self {
optimizer: PortfolioOptimizer::new(),
}
}
/// Compare mean-variance and higher-order optimization.
pub fn compare(
&self,
request: &OptimizationRequest,
) -> Result<PortfolioComparison, QuantumPortError> {
// Optimize with mean-variance
let mut mv_request = request.clone();
mv_request.method = OptimizationMethod::MeanVariance;
let mean_variance = self.optimizer.optimize(&mv_request)?;
// Optimize with higher-order
let mut ho_request = request.clone();
ho_request.method = OptimizationMethod::HigherOrder;
let higher_order = self.optimizer.optimize(&ho_request)?;
// Calculate comparison metrics
let comparison = ComparisonMetrics {
return_difference: higher_order.statistics.expected_return
- mean_variance.statistics.expected_return,
volatility_difference: higher_order.statistics.volatility
- mean_variance.statistics.volatility,
skewness_improvement: higher_order.statistics.skewness
- mean_variance.statistics.skewness,
kurtosis_reduction: mean_variance.statistics.kurtosis
- higher_order.statistics.kurtosis,
sharpe_difference: higher_order.statistics.sharpe_ratio
- mean_variance.statistics.sharpe_ratio,
cvar_improvement: mean_variance.risk_metrics.cvar_95
- higher_order.risk_metrics.cvar_95,
};
Ok(PortfolioComparison {
mean_variance,
higher_order,
comparison,
})
}
/// Compare all available methods.
pub fn compare_all_methods(
&self,
request: &OptimizationRequest,
) -> Result<Vec<(OptimizationMethod, quantumport_shared::OptimizedPortfolio)>, QuantumPortError>
{
let methods = [
OptimizationMethod::MeanVariance,
OptimizationMethod::MinimumVariance,
OptimizationMethod::HigherOrder,
OptimizationMethod::QAOAInspired,
OptimizationMethod::RiskParity,
];
let mut results = Vec::new();
for method in methods {
let mut method_request = request.clone();
method_request.method = method;
let portfolio = self.optimizer.optimize(&method_request)?;
results.push((method, portfolio));
}
Ok(results)
}
/// Generate comparison report as text.
#[must_use]
pub fn generate_report(comparison: &PortfolioComparison) -> String {
let mut report = String::new();
report.push_str("PORTFOLIO OPTIMIZATION COMPARISON\n");
report.push_str("=================================\n\n");
report.push_str("MEAN-VARIANCE PORTFOLIO:\n");
report.push_str(&format!(
" Expected Return: {:.2}%\n",
comparison.mean_variance.statistics.expected_return * 100.0
));
report.push_str(&format!(
" Volatility: {:.2}%\n",
comparison.mean_variance.statistics.volatility * 100.0
));
report.push_str(&format!(
" Sharpe Ratio: {:.3}\n",
comparison.mean_variance.statistics.sharpe_ratio
));
report.push_str(&format!(
" Skewness: {:.3}\n",
comparison.mean_variance.statistics.skewness
));
report.push_str(&format!(
" Kurtosis: {:.3}\n",
comparison.mean_variance.statistics.kurtosis
));
report.push_str(&format!(
" CVaR (95%): {:.2}%\n\n",
comparison.mean_variance.risk_metrics.cvar_95 * 100.0
));
report.push_str("HIGHER-ORDER PORTFOLIO:\n");
report.push_str(&format!(
" Expected Return: {:.2}%\n",
comparison.higher_order.statistics.expected_return * 100.0
));
report.push_str(&format!(
" Volatility: {:.2}%\n",
comparison.higher_order.statistics.volatility * 100.0
));
report.push_str(&format!(
" Sharpe Ratio: {:.3}\n",
comparison.higher_order.statistics.sharpe_ratio
));
report.push_str(&format!(
" Skewness: {:.3}\n",
comparison.higher_order.statistics.skewness
));
report.push_str(&format!(
" Kurtosis: {:.3}\n",
comparison.higher_order.statistics.kurtosis
));
report.push_str(&format!(
" CVaR (95%): {:.2}%\n\n",
comparison.higher_order.risk_metrics.cvar_95 * 100.0
));
report.push_str("COMPARISON:\n");
report.push_str(&format!(
" Return Diff: {:+.2}%\n",
comparison.comparison.return_difference * 100.0
));
report.push_str(&format!(
" Volatility Diff: {:+.2}%\n",
comparison.comparison.volatility_difference * 100.0
));
report.push_str(&format!(
" Skewness Improvement: {:+.3}\n",
comparison.comparison.skewness_improvement
));
report.push_str(&format!(
" Kurtosis Reduction: {:+.3}\n",
comparison.comparison.kurtosis_reduction
));
report.push_str(&format!(
" Sharpe Diff: {:+.3}\n",
comparison.comparison.sharpe_difference
));
report.push_str(&format!(
" CVaR Improvement:{:+.2}%\n",
comparison.comparison.cvar_improvement * 100.0
));
report
}
}
#[cfg(test)]
mod tests {
use super::*;
use quantumport_shared::{
Asset, AssetClass, AssetReturns, OptimizationObjectives, PortfolioConstraints,
};
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, 0.02, -0.005, 0.01, 0.008, -0.012,
],
start_date: "2024-01-01".to_string(),
end_date: "2024-01-10".to_string(),
},
AssetReturns {
symbol: "B".to_string(),
returns: vec![
0.002, 0.001, 0.003, -0.001, 0.002, 0.001, 0.002, -0.001, 0.003, 0.001,
],
start_date: "2024-01-01".to_string(),
end_date: "2024-01-10".to_string(),
},
],
objectives: OptimizationObjectives::default(),
constraints: PortfolioConstraints::default(),
method: OptimizationMethod::HigherOrder,
}
}
#[test]
fn test_comparer_creation() {
let comparer = PortfolioComparer::new();
assert!(std::mem::size_of_val(&comparer) > 0);
}
#[test]
fn test_compare() {
let comparer = PortfolioComparer::new();
let request = create_test_request();
let result = comparer.compare(&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_compare_all_methods() {
let comparer = PortfolioComparer::new();
let request = create_test_request();
let result = comparer.compare_all_methods(&request);
assert!(result.is_ok());
let results = result.unwrap();
assert_eq!(results.len(), 5);
}
#[test]
fn test_generate_report() {
let comparer = PortfolioComparer::new();
let request = create_test_request();
let comparison = comparer.compare(&request).unwrap();
let report = PortfolioComparer::generate_report(&comparison);
assert!(report.contains("MEAN-VARIANCE"));
assert!(report.contains("HIGHER-ORDER"));
assert!(report.contains("COMPARISON"));
}
#[test]
fn test_comparison_metrics() {
let comparer = PortfolioComparer::new();
let request = create_test_request();
let comparison = comparer.compare(&request).unwrap();
// Metrics should be finite numbers
assert!(comparison.comparison.return_difference.is_finite());
assert!(comparison.comparison.volatility_difference.is_finite());
assert!(comparison.comparison.skewness_improvement.is_finite());
}
}