Hierarchical Equal Risk Contribution (HERC)
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.
| Parameter | Type | Description | Example |
|---|---|---|---|
linkage_method | str | The linkage of the hierarchical clustering: “single”, “complete”, “average” or “ward”. Defaults to “ward”, as in the paper. | "ward", as in the paper |
clusters | int | None | The 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 |
codependence | str | The 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" |
period | str | The frequency of the returns (daily, weekly, monthly, quarterly or yearly). Defaults to “daily”. | "weekly" |
covariance | str | The 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_span | int | The span in periods of the “exponential” covariance; the weight of a period halves after about span / 2.9 periods. Defaults to 180. | 180 |
minimum_weight | float | 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_weight | float | 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_bounds | dict[str, tuple[float, float]] | None | The 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_bounds | dict[str, tuple[float, float]] | None | The 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_benchmark | bool | Whether to include the benchmark as an asset. Defaults to False. | True |
rounding | int | None | The number of decimals to round the results to. Defaults to None, which uses the Toolkit’s rounding. | 2 |