Risk Parity - Covariance shrinkage#

This tutorial shows how to incorporate covariance shrinkage in the RiskBudgeting optimization.

Data#

We load the S&P 500 dataset composed of the daily prices of 20 assets from the S&P 500 Index composition starting from 1990-01-02 up to 2022-12-28:

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

from skfolio import Population, RiskMeasure
from skfolio.datasets import load_sp500_dataset
from skfolio.moments import ShrunkCovariance
from skfolio.optimization import RiskBudgeting
from skfolio.preprocessing import prices_to_returns
from skfolio.prior import EmpiricalPrior

prices = load_sp500_dataset()

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

Model#

We create a risk parity model by using ShrunkCovariance as the covariance estimator then fit it on the training set:

model = RiskBudgeting(
    risk_measure=RiskMeasure.VARIANCE,
    prior_estimator=EmpiricalPrior(
        covariance_estimator=ShrunkCovariance(shrinkage=0.9)
    ),
    portfolio_params=dict(name="Risk Parity - Covariance Shrinkage"),
)
model.fit(X_train)
model.weights_
array([0.04774388, 0.0437015 , 0.0450291 , 0.04647791, 0.05284612,
       0.04907476, 0.04852985, 0.05373832, 0.04539473, 0.05360597,
       0.05178557, 0.05138231, 0.04927258, 0.05375682, 0.05112774,
       0.05417536, 0.04755091, 0.04988152, 0.05199293, 0.05293211])

To compare this model, we use a basic risk parity without covariance shrinkage:

bench = RiskBudgeting(
    risk_measure=RiskMeasure.VARIANCE,
    portfolio_params=dict(name="Risk Parity - Basic"),
)
bench.fit(X_train)
bench.weights_
array([0.04135261, 0.03210859, 0.03372588, 0.03785038, 0.06105344,
       0.04432799, 0.04252186, 0.06593408, 0.03451951, 0.06469301,
       0.05418934, 0.05209466, 0.04535479, 0.06568174, 0.05104143,
       0.06894357, 0.0404652 , 0.04667615, 0.05627036, 0.06119539])

Prediction#

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

ptf_model_test = model.predict(X_test)
ptf_bench_test = bench.predict(X_test)

Analysis#

For improved analysis, it’s possible to load both predicted portfolios into a Population:

population = Population([ptf_model_test, ptf_bench_test])

Let’s plot each portfolio cumulative returns:

fig = population.plot_cumulative_returns()
show(fig)

Finally, we print a full summary of both strategies evaluated on the test set:

population.summary()
Risk Parity - Covariance Shrinkage Risk Parity - Basic
Mean 0.068% 0.065%
Annualized Mean 17.04% 16.40%
Variance 0.011% 0.010%
Annualized Variance 2.84% 2.63%
Semi-Variance 0.0058% 0.0054%
Annualized Semi-Variance 1.47% 1.36%
Standard Deviation 1.06% 1.02%
Annualized Standard Deviation 16.86% 16.22%
Semi-Deviation 0.76% 0.73%
Annualized Semi-Deviation 12.11% 11.66%
Mean Absolute Deviation 0.69% 0.66%
CVaR at 95% 2.46% 2.37%
EVaR at 95% 5.37% 5.23%
Worst Realization 10.66% 10.34%
CDaR at 95% 13.02% 12.39%
MAX Drawdown 34.58% 34.30%
Average Drawdown 2.58% 2.40%
EDaR at 95% 20.30% 19.86%
First Lower Partial Moment 0.35% 0.33%
Ulcer Index 0.043 0.041
Gini Mean Difference 1.04% 1.00%
Value at Risk at 95% 1.51% 1.46%
Drawdown at Risk at 95% 9.30% 8.97%
Entropic Risk Measure at 95% 3.00 3.00
Fourth Central Moment 0.000025% 0.000023%
Fourth Lower Partial Moment 0.000012% 0.000011%
Skew -3.68% -5.68%
Kurtosis 1996.81% 2065.52%
Sharpe Ratio 0.064 0.064
Annualized Sharpe Ratio 1.01 1.01
Sortino Ratio 0.089 0.089
Annualized Sortino Ratio 1.41 1.41
Mean Absolute Deviation Ratio 0.097 0.098
First Lower Partial Moment Ratio 0.19 0.20
Value at Risk Ratio at 95% 0.045 0.045
CVaR Ratio at 95% 0.027 0.027
Entropic Risk Measure Ratio at 95% 0.00023 0.00022
EVaR Ratio at 95% 0.013 0.012
Worst Realization Ratio 0.0063 0.0063
Drawdown at Risk Ratio at 95% 0.0073 0.0073
CDaR Ratio at 95% 0.0052 0.0053
Calmar Ratio 0.0020 0.0019
Average Drawdown Ratio 0.026 0.027
EDaR Ratio at 95% 0.0033 0.0033
Ulcer Index Ratio 0.016 0.016
Gini Mean Difference Ratio 0.065 0.065
Effective Number of Assets 19.92102950954716 18.99586745481971
Assets Number 20 20


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

Gallery generated by Sphinx-Gallery