use cvxrust::prelude::*;
fn main() {
println!("=== Portfolio Optimization ===\n");
let mu = constant_vec(vec![0.12, 0.10, 0.07, 0.05]);
#[rustfmt::skip]
let sigma = constant_matrix(vec![
0.04, 0.01, 0.00, -0.01,
0.01, 0.03, 0.00, 0.00,
0.00, 0.00, 0.02, 0.00,
-0.01, 0.00, 0.00, 0.01,
], 4, 4);
println!("Assets: A, B, C, D");
println!("Expected returns: [12%, 10%, 7%, 5%]");
println!("Target return: 9%\n");
let x = variable(4);
let risk = quad_form(&x, &sigma);
let target_return = 0.09;
let solution = Problem::minimize(risk)
.subject_to([
constraint!((dot(&mu, &x)) >= target_return),
constraint!((sum(&x)) == 1.0),
constraint!(x >= 0.0),
])
.solve()
.expect("Failed to solve");
println!("Optimal Portfolio:");
let portfolio = &solution[&x];
let assets = ["A", "B", "C", "D"];
for i in 0..4 {
println!(" Asset {}: {:.2}%", assets[i], portfolio[(i, 0)] * 100.0);
}
let variance = solution.value.unwrap();
let std_dev = variance.sqrt();
println!("\nPortfolio Statistics:");
println!(" Expected return: 9.00%");
println!(" Risk (std dev): {:.2}%", std_dev * 100.0);
println!(" Sharpe ratio: {:.4}", 0.09 / std_dev);
println!("\nDual Variables (Shadow Prices):");
if let Some(dual_return) = solution.constraint_dual(0) {
println!(" Return constraint dual: {:.4}", dual_return);
println!(" Interpretation: Increasing target return by 1% would");
println!(
" increase portfolio variance by ~{:.4}",
dual_return.abs() * 0.01
);
}
println!("\n--- Efficient Frontier ---\n");
for &target in &[0.06, 0.08, 0.10, 0.12] {
let y = variable(4);
let risk_y = quad_form(&y, &sigma);
if let Ok(sol) = Problem::minimize(risk_y)
.subject_to([
constraint!((dot(&mu, &y)) >= target),
constraint!((sum(&y)) == 1.0),
constraint!(y >= 0.0),
])
.solve()
{
let std = sol.value.unwrap().sqrt();
println!(
" Return: {:.1}% → Risk: {:.2}%",
target * 100.0,
std * 100.0
);
}
}
println!("\nHigher returns require accepting higher risk!");
}