(
&mut self,
asset_prices: Option<AssetPricesInput<'_>>,
expected_asset_returns: Option<&DMatrix<f64>>,
covariance_matrix: Option<&DMatrix<f64>>,
resample_by: O
| 121 | } |
| 122 | |
| 123 | pub fn allocate( |
| 124 | &mut self, |
| 125 | asset_prices: Option<AssetPricesInput<'_>>, |
| 126 | expected_asset_returns: Option<&DMatrix<f64>>, |
| 127 | covariance_matrix: Option<&DMatrix<f64>>, |
| 128 | resample_by: Option<&str>, |
| 129 | solution: Option<&str>, |
| 130 | ) -> Result<(), ClaError> { |
| 131 | if asset_prices.is_none() && expected_asset_returns.is_none() && covariance_matrix.is_none() |
| 132 | { |
| 133 | return Err(ClaError::MissingInputs); |
| 134 | } |
| 135 | if let Some(AssetPricesInput::RawMatrix(_)) = asset_prices { |
| 136 | return Err(ClaError::InvalidAssetPrices("Asset prices matrix must be a dataframe")); |
| 137 | } |
| 138 | if let Some(AssetPricesInput::Prices(prices)) = asset_prices { |
| 139 | if prices.index.len() != prices.data.nrows() || prices.index.is_empty() { |
| 140 | return Err(ClaError::InvalidAssetPrices("Asset prices index must be datetime")); |
| 141 | } |
| 142 | self._initialise(&prices.data, resample_by, expected_asset_returns, covariance_matrix)?; |
| 143 | } else if let (Some(exp), Some(cov)) = (expected_asset_returns, covariance_matrix) { |
| 144 | self.expected_returns = normalize_expected_returns(exp)?; |
| 145 | self.cov_matrix = cov.clone_owned(); |
| 146 | self.lower_bounds = vec![0.0; self.expected_returns.nrows()]; |
| 147 | self.upper_bounds = vec![1.0; self.expected_returns.nrows()]; |
| 148 | self.weights.clear(); |
| 149 | self.lambdas.clear(); |
| 150 | self.gammas.clear(); |
| 151 | self.free_weights.clear(); |
| 152 | } else { |
| 153 | return Err(ClaError::MissingInputs); |
| 154 | } |
| 155 | |
| 156 | let solution = solution.unwrap_or("cla_turning_points"); |
| 157 | let bounds = build_bounds(self.expected_returns.nrows(), &self.weight_bounds)?; |
| 158 | match solution { |
| 159 | "cla_turning_points" | "min_volatility" => { |
| 160 | let w = solve_min_vol(&self.cov_matrix, &bounds)?; |
| 161 | self.weights = vec![w]; |
| 162 | } |
| 163 | "max_sharpe" => { |
| 164 | let exp = self.expected_returns.column(0).iter().copied().collect::<Vec<_>>(); |
| 165 | let w = solve_max_sharpe(&self.cov_matrix, &exp, 0.0, &bounds)?; |
| 166 | self.weights = vec![w]; |
| 167 | } |
| 168 | "efficient_frontier" => { |
| 169 | let w = solve_min_vol(&self.cov_matrix, &bounds)?; |
| 170 | let points = 100; |
| 171 | self.weights = vec![w; points]; |
| 172 | self.efficient_frontier_means = Vec::with_capacity(points); |
| 173 | self.efficient_frontier_sigma = Vec::with_capacity(points); |
| 174 | for weights in self.weights.iter() { |
| 175 | let mean = dot(weights, self.expected_returns.column(0).as_slice()); |
| 176 | let sigma = quad_risk(&self.cov_matrix, weights).sqrt(); |
| 177 | self.efficient_frontier_means.push(mean); |
| 178 | self.efficient_frontier_sigma.push(sigma); |
| 179 | } |
| 180 | } |
nothing calls this directly
no test coverage detected