(
cov: &DMatrix<f64>,
exp_ret: &[f64],
risk_free: f64,
bounds: &[(f64, f64)],
)
| 515 | } |
| 516 | |
| 517 | fn solve_max_sharpe( |
| 518 | cov: &DMatrix<f64>, |
| 519 | exp_ret: &[f64], |
| 520 | risk_free: f64, |
| 521 | bounds: &[(f64, f64)], |
| 522 | ) -> Result<Vec<f64>, ClaError> { |
| 523 | check_bounds_feasible(bounds)?; |
| 524 | let n = cov.nrows(); |
| 525 | if n == 0 || exp_ret.len() != n { |
| 526 | return Err(ClaError::DimensionMismatch); |
| 527 | } |
| 528 | let excess: Vec<f64> = exp_ret.iter().map(|r| r - risk_free).collect(); |
| 529 | let inv = cov.clone().try_inverse().ok_or(ClaError::NoData)?; |
| 530 | let excess_vec = DVector::from_vec(excess); |
| 531 | let mut w: Vec<f64> = (inv * excess_vec).data.as_vec().clone(); |
| 532 | let sum: f64 = w.iter().sum(); |
| 533 | if sum.abs() > 1e-12 { |
| 534 | for wi in w.iter_mut() { |
| 535 | *wi /= sum; |
| 536 | } |
| 537 | } |
| 538 | project_to_bounds(&mut w, bounds)?; |
| 539 | Ok(w) |
| 540 | } |
| 541 | |
| 542 | fn dot(a: &[f64], b: &[f64]) -> f64 { |
| 543 | a.iter().zip(b.iter()).map(|(x, y)| x * y).sum() |
no test coverage detected