<a id="sphx-glr-auto-examples-clustering-plot-2-herc-cdar-py"></a>

<a id="hierarchical-equal-risk-contribution-cdar"></a>

# Hierarchical Equal Risk Contribution - CDaR

This tutorial introduces the
[`HierarchicalEqualRiskContribution`](https://skfolio.org/generated/skfolio.optimization.HierarchicalEqualRiskContribution.html.md#skfolio.optimization.HierarchicalEqualRiskContribution) optimization.

The Hierarchical Equal Risk Contribution (HERC) is a portfolio optimization method
developed by Thomas Raffinot.

This algorithm uses a distance matrix to compute hierarchical clusters using the
Hierarchical Tree Clustering algorithm. It then computes, for each cluster, the
total cluster risk of an inverse-risk allocation.

The final step is the top-down recursive division of the dendrogram, where the
assets weights are updated using a naive risk parity within clusters.

It differs from the Hierarchical Risk Parity by exploiting the dendrogram shape
during the top-down recursive division instead of bisecting it.

#### NOTE
The default linkage method is set to the Ward variance minimization algorithm,
which is more stable and has better properties than the single-linkage method

In this example, we will use the CDaR risk measure.

<a id="data"></a>

## Data

We load the S&P 500 [dataset](https://skfolio.org/user_guide/datasets.html.md#datasets) composed of the daily prices of 20
assets from the S&P 500 Index composition starting from 2020-01-02 up to 2022-12-28:

```Python
from plotly.io import show
from sklearn.model_selection import train_test_split

from skfolio import Population, RiskMeasure
from skfolio.cluster import HierarchicalClustering, LinkageMethod
from skfolio.datasets import load_sp500_dataset
from skfolio.distance import KendallDistance
from skfolio.optimization import (
    EqualWeighted,
    HierarchicalEqualRiskContribution,
)
from skfolio.preprocessing import prices_to_returns

prices = load_sp500_dataset()

X = prices_to_returns(prices)
X_train, X_test = train_test_split(X, test_size=0.5, shuffle=False)
```

<a id="model"></a>

## Model

We create a CVaR Hierarchical Equal Risk Contribution model and then fit it on the
training set:

```Python
model1 = HierarchicalEqualRiskContribution(
    risk_measure=RiskMeasure.CDAR, portfolio_params=dict(name="HERC-CDaR-Ward-Pearson")
)
model1.fit(X_train)
model1.weights_
```

```none
array([0.05814153, 0.04927288, 0.09420898, 0.05726203, 0.02409012,
       0.08083036, 0.08070112, 0.02983878, 0.06656721, 0.02730592,
       0.01757816, 0.01140416, 0.0933616 , 0.04726087, 0.02170548,
       0.02428936, 0.00827991, 0.02575021, 0.14895433, 0.03319698])
```

<a id="risk-contribution"></a>

## Risk Contribution

Let’s analyze the risk contribution of the model on the training set:

```Python
ptf1 = model1.predict(X_train)
ptf1.plot_contribution(measure=RiskMeasure.CDAR)
```

<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>[plotly figure stripped from llms output]
<br />
<br />

<a id="dendrogram"></a>

## Dendrogram

To analyze the clusters structure, we plot the dendrogram.
The blue lines represent distinct clusters composed of a single asset.
The remaining colors represent clusters of more than one asset:

```Python
fig = model1.hierarchical_clustering_estimator_.plot_dendrogram(heatmap=False)
show(fig)
```

[plotly figure stripped from llms output]<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>

<br/>

The horizontal axis represents the assets. The links between clusters are represented
as upside-down U-shaped lines. The height of the U indicates the distance between the
clusters. For example, the link representing the cluster containing Assets HD and WMT
has a distance of 0.5 (called cophenetic distance).

When `heatmap` is set to True, the heatmap of the reordered distance matrix is
displayed below the dendrogram and clusters are outlined with yellow squares:

```Python
model1.hierarchical_clustering_estimator_.plot_dendrogram()
```

<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>[plotly figure stripped from llms output]
<br />
<br />

<a id="linkage-methods"></a>

## Linkage Methods

The clustering can be greatly affected by the choice of the linkage method.
In the [`HierarchicalEqualRiskContribution`](https://skfolio.org/generated/skfolio.optimization.HierarchicalEqualRiskContribution.html.md#skfolio.optimization.HierarchicalEqualRiskContribution) estimator, the
default linkage method is set to the Ward variance minimization algorithm which is
more stable and has better properties than the single-linkage method, which suffers
from the chaining effect.

And because HERC rely on the dendrogram structure as opposed
to HRP, the choice of the linkage method will have a greater impact on the allocation.

To show this effect, let’s create a second model with the single-linkage method:

```Python
model2 = HierarchicalEqualRiskContribution(
    risk_measure=RiskMeasure.CDAR,
    hierarchical_clustering_estimator=HierarchicalClustering(
        linkage_method=LinkageMethod.SINGLE,
    ),
    portfolio_params=dict(name="HERC-CDaR-Single-Pearson"),
)
model2.fit(X_train)
model2.hierarchical_clustering_estimator_.plot_dendrogram(heatmap=True)
```

<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>[plotly figure stripped from llms output]
<br />
<br />

We can see that the clustering has been greatly affected by the change of the linkage
method. Let’s analyze the risk contribution of this model on the training set:

```Python
ptf2 = model2.predict(X_train)
ptf2.plot_contribution(measure=RiskMeasure.CDAR)
```

<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>[plotly figure stripped from llms output]
<br />
<br />

The risk of that second model is very concentrated. We can already conclude that the
single-linkage method is not appropriate for this dataset. This will be confirmed
below on the test set.

<a id="distance-estimator"></a>

## Distance Estimator

The distance metric used also has an important effect on the clustering.
The default is to use the distance of the pearson correlation matrix.
This can be changed using the [distance estimators](https://skfolio.org/user_guide/distance.html.md#distance).
For example, let’s create a third model with a distance computed from the absolute
value of the Kendal correlation matrix:

```Python
model3 = HierarchicalEqualRiskContribution(
    risk_measure=RiskMeasure.CDAR,
    distance_estimator=KendallDistance(absolute=True),
    portfolio_params=dict(name="HERC-CDaR-Ward-Kendal"),
)
model3.fit(X_train)
model3.hierarchical_clustering_estimator_.plot_dendrogram(heatmap=True)
```

<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>[plotly figure stripped from llms output]
<br />
<br />

To compare the models, we use an equal weighted benchmark using
the [`EqualWeighted`](https://skfolio.org/generated/skfolio.optimization.EqualWeighted.html.md#skfolio.optimization.EqualWeighted) estimator:

```Python
bench = EqualWeighted()
bench.fit(X_train)
bench.weights_
```

```none
array([0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05,
       0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05])
```

<a id="prediction"></a>

## Prediction

We predict the models and the benchmark on the test set:

```Python
population_test = Population([])
for model in [model1, model2, model3, bench]:
    population_test.append(model.predict(X_test))

population_test.plot_cumulative_returns()
```

<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>[plotly figure stripped from llms output]
<br />
<br />

<a id="composition"></a>

## Composition

From the below composition, we notice that the model with single-linkage method is
highly concentrated:

```Python
population_test.plot_composition()
```

<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>[plotly figure stripped from llms output]
<br />
<br />

**Total running time of the script:** (0 minutes 2.690 seconds)

<a id="sphx-glr-download-auto-examples-clustering-plot-2-herc-cdar-py"></a>
