Calculate the Hierarchical Equal Risk Contribution (HERC) weights. The assets are clustered by their correlations and the dendrogram is cut into clusters. Within a cluster every asset gets a weight in inverse proportion to its variance; between clusters the weight is split top-down along the dendrogram, each branch getting a weight in inverse proportion to its risk, the sum of the variances of its clusters. Unlike Hierarchical Risk Parity, which halves the ordered list of assets, the splits follow the tree, so assets that belong together are never split up, and the weight is allocated over the clusters rather than over the assets: riskier clusters get less.

The clustering uses the correlation distance √((1 - ρ) / 2) with Ward linkage, as in the paper. Without a number of clusters, it is the one at the sharpest bend of the pooled within-cluster dispersion, the two-order difference of the gap statistic (Yue, Wang and Wei, 2008; Tibshirani, Walther and Hastie, 2001).

For more information, see the following papers:

  • Raffinot, T. (2018). “The Hierarchical Equal Risk Contribution Portfolio.” SSRN 3237540.
  • Raffinot, T. (2017). “Hierarchical Clustering-Based Asset Allocation.” The Journal of Portfolio Management, 44(2), 89-99.

Also known as: HERC, hierarchical clustering-based risk parity.

Hierarchical Equal Risk Contribution (HERC) in Python or via the MCP

Install the Finance Toolkit with:

pip install financetoolkit -U

Then call get_hierarchical_equal_risk_contribution_weights as shown below.

from financetoolkit import Toolkit

toolkit = Toolkit(
    ["AAPL", "MSFT", "JNJ", "XOM"],
    api_key="FINANCIAL_MODELING_PREP_KEY",
    start_date="2021-01-01",
    end_date="2025-12-31",
)

toolkit.portfolio.get_hierarchical_equal_risk_contribution_weights()[
    ["Weight", "Volatility", "Sharpe Ratio", "Risk Contribution", "Cluster"]
]

Or, after setting up the Finance Toolkit MCP, ask:

How would hierarchical equal risk contribution weight Apple, Microsoft, Johnson & Johnson and ExxonMobil, based on 2021 to 2025, and which clusters does it form?

Which returns:

  Weight Volatility Sharpe Ratio Risk Contribution Cluster
AAPL 0.2917 0.2786 0.5725 0.412 1
MSFT 0.3425 0.2571 0.6534 0.4348 1
JNJ 0.2645 0.1673 0.3884 0.1009 2
XOM 0.1014 0.2702 0.9524 0.0523 3
Portfolio 1 0.1715 0.8592 1 nan

Parameters

get_hierarchical_equal_risk_contribution_weights accepts the following parameters.

ParameterTypeDescriptionExample
linkage_methodstrThe linkage of the hierarchical clustering: “single”, “complete”, “average” or “ward”. Defaults to “ward”, as in the paper."ward", as in the paper
clustersint | NoneThe number of clusters. Defaults to None, chosen by the two-order difference of the gap statistic.None, chosen by the two-order difference of the gap statistic
codependencestrThe correlation the assets are clustered on, through the distance √((1 - ρ) / 2): “pearson” (the linear correlation of the covariance matrix), “spearman” (the rank correlation of the returns, Spearman, 1904) or “kendall” (the rank correlation τ_b of the returns, Kendall, 1938). The rank correlations depend only on the order of the returns, so a few extreme returns weigh less. Defaults to “pearson”."pearson"
periodstrThe frequency of the returns (daily, weekly, monthly, quarterly or yearly). Defaults to “daily”."weekly"
covariancestrThe estimator of the covariance matrix, which also gives the correlations that are clustered: “sample” (ddof=1), “ledoit_wolf” (towards the scaled identity, Ledoit and Wolf, 2004), “ledoit_wolf_constant_correlation” (towards the average correlation, Ledoit and Wolf, 2004), “ledoit_wolf_single_factor” (towards the covariance that the benchmark as a single factor implies, Ledoit and Wolf, 2003), “oas” (Chen et al., 2010), “semicovariance” (the co-movement below a zero return, Estrada, 2007), “exponential” (recent periods weigh most, RiskMetrics) or “denoised” (the eigenvalues of the correlation matrix that random returns could produce averaged, Laloux et al., 2000). The shrinkage and denoised estimators stabilize the clusters when there are many assets compared to the number of periods. Defaults to “sample”."sample"
covariance_spanintThe span in periods of the “exponential” covariance; the weight of a period halves after about span / 2.9 periods. Defaults to 180.180
minimum_weightfloat | dict[str, float]The lowest weight of every asset, or per asset, e.g. {“AAPL”: 0.05} (the others keep 0); it cannot be negative. Defaults to 0.0
maximum_weightfloat | dict[str, float]The highest weight of every asset, or per asset, e.g. {“AAPL”: 0.3} (the others keep 1). Defaults to 1.1
sector_boundsdict[str, tuple[float, float]] | NoneThe minimum and maximum total weight per sector, e.g. {“Technology”: (0, 0.4)}, with the sector of each asset from its company profile. Defaults to None.None
country_boundsdict[str, tuple[float, float]] | NoneThe minimum and maximum total weight per country, named in full or by its two-letter code, e.g. {“United States”: (0, 0.6)}, with the country of each asset from its company profile. Defaults to None.None
include_benchmarkboolWhether to include the benchmark as an asset. Defaults to False.True
roundingint | NoneThe number of decimals to round the results to. Defaults to None, which uses the Toolkit’s rounding.2
Share