portfolio_optimization
Portfolio Construction and Risk
Mean-variance and constrained allocation methods with ergonomic APIs.
Why This Module Exists
Provides production-ready portfolio construction primitives with explicit options and constraints.
Key Public APIs
allocate_inverse_varianceallocate_min_volallocate_max_sharpeallocate_efficient_riskAllocationOptions
Core Math
Constrained Mean-Variance Program
\[\begin{aligned}\min_{w}\;&\frac{1}{2}w^T\Sigma w-\lambda\mu^T w\\\text{s.t. }&\mathbf 1^T w=1,\quad l_i\le w_i\le u_i\end{aligned}\]
Minimum Variance / Maximum Sharpe / Efficient Return
\[\begin{aligned}w_{MV}&=\arg\min_w\;w^T\Sigma w\\w_{MSR}&=\arg\max_w\;\frac{w^T(\mu-r_f\mathbf 1)}{\sqrt{w^T\Sigma w}}\\w_{ER}(r^*)&=\arg\min_w\;w^T\Sigma w\;\text{s.t. }w^T\mu\ge r^*\end{aligned}\]
Exponential Mean Estimator
\[\mu_t=\frac{\sum_{k=0}^{T-1}(1-\alpha)^k r_{t-k}}{\sum_{k=0}^{T-1}(1-\alpha)^k},\qquad \alpha=\frac{2}{\text{span}+1}\]
Code Examples
End-to-end: Compute and Compare Core Allocators
use nalgebra::DMatrix;
use openquant::portfolio_optimization::{
allocate_inverse_variance,
allocate_min_vol,
allocate_max_sharpe,
allocate_efficient_risk,
};
// rows=time, cols=assets
let prices: DMatrix<f64> = /* load matrix */ DMatrix::zeros(252, 6);
let ivp = allocate_inverse_variance(&prices)?;
let mv = allocate_min_vol(&prices, None, None)?;
let msr = allocate_max_sharpe(&prices, 0.01, None, None)?;
let er = allocate_efficient_risk(&prices, 0.12, None, None)?;
assert_eq!(ivp.weights.len(), prices.ncols());
assert!((mv.weights.iter().sum::<f64>() - 1.0).abs() < 1e-6);
assert!((msr.weights.iter().sum::<f64>() - 1.0).abs() < 1e-6);
assert!((er.weights.iter().sum::<f64>() - 1.0).abs() < 1e-6);
End-to-end: Constrained Allocation with Exponential Returns and Resampling
use std::collections::HashMap;
use openquant::portfolio_optimization::{
allocate_max_sharpe_with,
AllocationOptions,
ReturnsMethod,
};
let mut bounds = HashMap::new();
// Cap concentration in first asset; enforce long-only defaults elsewhere
bounds.insert(0usize, (0.0, 0.20));
let opts = AllocationOptions {
risk_free_rate: 0.02,
returns_method: ReturnsMethod::Exponential { span: 60 },
resample_by: Some("W"),
bounds: Some(bounds),
tuple_bounds: Some((0.0, 0.40)),
..Default::default()
};
let constrained = allocate_max_sharpe_with(&prices, &opts)?;
assert!(constrained.weights.iter().all(|w| *w >= -1e-10));
Implementation Notes
- Optimizer output is only as good as mean/covariance assumptions; stress-test inputs and rebalance frequency.
- Constraint design (asset caps, sector caps, long/short bounds) is usually more important than small objective tweaks.
- Track turnover, realized slippage, and drift between target and filled weights in production.