Skip to content
GeneratedAssembled automatically from moduleDocs.ts. No human has reviewed this page.

hcaa

Hierarchical Clustering Asset Allocation generalises HRP’s recursive bisection to risk measures other than variance. Seriation and the cluster tree are built the same way, but the split at each node weights the two sides by the chosen allocation_metric — cluster variance, standard deviation, Sharpe ratio, expected shortfall or conditional drawdown — so the same hierarchy can express a tail-risk budget rather than only a variance budget. Like HRP it never inverts the covariance matrix.

Use it in place of hrp when your risk budget is not variance: expected shortfall or conditional drawdown for a drawdown-controlled mandate, Sharpe when you have return views you are willing to defend. Use hrp when you do not, since the variance split needs no expected-return estimate at all. The clustering is only as good as the distance fed to it, so build that with codependence rather than raw correlation, and sanity-check the cluster count with onc.

σC2=wCΣCwC\sigma_C^2=w_C^{\top}\Sigma_C w_C

where ΣC\Sigma_C is the covariance sub-matrix of cluster CC and wCw_C its inverse-variance weights, normalised to sum to one within the cluster.

α=1mleftmleft+mright,wleft=α,wright=1α\alpha=1-\frac{m_{\text{left}}}{m_{\text{left}}+m_{\text{right}}},\qquad w_{\text{left}}\mathrel{*}=\alpha,\quad w_{\text{right}}\mathrel{*}=1-\alpha

where mCm_C is the risk of cluster CC under the chosen allocation_metric: cluster variance (σC2\sigma_C^2), standard deviation (σC\sigma_C), expected shortfall, or conditional drawdown. Lower risk on one side means a larger α\alpha for that side. This generalises the HRP split, which is the minimum_variance case. Two branches invert the sign: sharpe_ratio allocates α=SRleft/(SRleft+SRright)\alpha=\mathrm{SR}_{\text{left}}/(\mathrm{SR}_{\text{left}}+\mathrm{SR}_{\text{right}}) because higher is better there, and equal_weighting skips the split entirely.

use nalgebra::DMatrix;
use openquant::hcaa::HierarchicalClusteringAssetAllocation;
let asset_names: Vec<String> =
["SPY", "TLT", "GLD", "HYG"].iter().map(|s| s.to_string()).collect();
// rows = observations, cols = assets, in the same order as `asset_names`.
let prices = DMatrix::from_fn(250, 4, |i, j| 100.0 + (i as f64) * 0.05 + (j as f64) * 3.0);
// The constructor argument selects how expected returns are estimated
// ("mean" or "exponential"); it is not optional.
let mut hcaa = HierarchicalClusteringAssetAllocation::new("mean");
// allocate() fills the struct in place and returns Result<(), HcaaError>.
// It does not return the weights — read them from `hcaa.weights` afterwards.
hcaa.allocate(
&asset_names,
Some(&prices), // asset_prices
None, // asset_returns
None, // covariance_matrix
None, // expected_asset_returns
"minimum_variance", // allocation_metric
0.05, // confidence_level, used by the tail-risk metrics
None, // optimal_num_clusters — inferred when None
None, // resample_by
)?;
println!("weights: {:?}", hcaa.weights);
println!("seriation order: {:?}", hcaa.ordered_indices);
  • hcaa.allocate_hcaa
  • HierarchicalClusteringAssetAllocation
  • HcaaError
  • Cluster linkage choices influence allocations.
  • Use with robust codependence distances when possible.