Risk Parity - Variance#

This tutorial uses the RiskBudgeting optimization to find the risk parity portfolio with variance as the risk measure.

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.optimization import InverseVolatility, RiskBudgeting
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.33, shuffle=False)

Model#

We create the risk parity model and then fit it on the training set:

model = RiskBudgeting(
    risk_measure=RiskMeasure.VARIANCE,
    portfolio_params=dict(name="Risk Parity - Variance"),
)
model.fit(X_train)
model.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])

To compare this model, we use an inverse volatility benchmark using the InverseVolatility estimator.

bench = InverseVolatility(portfolio_params=dict(name="Inverse Vol"))
bench.fit(X_train)
bench.weights_
array([0.03306735, 0.02548697, 0.03551377, 0.0296872 , 0.06358463,
       0.05434705, 0.04742354, 0.07049715, 0.03882539, 0.06697905,
       0.05570808, 0.05576851, 0.04723274, 0.06351213, 0.05581397,
       0.0676481 , 0.02564642, 0.03970752, 0.05744543, 0.06610498])

Risk Contribution Analysis#

Let’s analyze the risk contribution of both models on the training set. As expected, the risk parity model has the same variance contribution for each asset:

ptf_model_train = model.predict(X_train)
ptf_model_train.plot_contribution(measure=RiskMeasure.ANNUALIZED_VARIANCE)


And the inverse volatility model has non-equal variance contribution. This is because the correlation is not taken into account in an inverse volatility model:

ptf_bench_train = bench.predict(X_train)
ptf_bench_train.plot_contribution(measure=RiskMeasure.ANNUALIZED_VARIANCE)


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)

The predict method returns a Portfolio object.

Analysis#

For improved analysis, we load both predicted portfolios into a Population:

population = Population([ptf_model_test, ptf_bench_test])

Let’s plot each portfolio composition:

population.plot_composition()


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 - Variance Inverse Vol
Mean 0.065% 0.064%
Annualized Mean 16.40% 16.06%
Variance 0.010% 0.010%
Annualized Variance 2.63% 2.56%
Semi-Variance 0.0054% 0.0053%
Annualized Semi-Variance 1.36% 1.33%
Standard Deviation 1.02% 1.01%
Annualized Standard Deviation 16.22% 16.00%
Semi-Deviation 0.73% 0.73%
Annualized Semi-Deviation 11.66% 11.54%
Mean Absolute Deviation 0.66% 0.65%
CVaR at 95% 2.37% 2.35%
EVaR at 95% 5.23% 5.29%
Worst Realization 10.34% 10.49%
CDaR at 95% 12.39% 12.22%
MAX Drawdown 34.30% 34.83%
Average Drawdown 2.40% 2.34%
EDaR at 95% 19.86% 20.05%
First Lower Partial Moment 0.33% 0.32%
Ulcer Index 0.041 0.040
Gini Mean Difference 1.00% 0.98%
Value at Risk at 95% 1.46% 1.45%
Drawdown at Risk at 95% 8.97% 8.79%
Entropic Risk Measure at 95% 3.00 3.00
Fourth Central Moment 0.000023% 0.000022%
Fourth Lower Partial Moment 0.000011% 0.000011%
Skew -5.68% -14.94%
Kurtosis 2065.52% 2117.02%
Sharpe Ratio 0.064 0.063
Annualized Sharpe Ratio 1.01 1.00
Sortino Ratio 0.089 0.088
Annualized Sortino Ratio 1.41 1.39
Mean Absolute Deviation Ratio 0.098 0.098
First Lower Partial Moment Ratio 0.20 0.20
Value at Risk Ratio at 95% 0.045 0.044
CVaR Ratio at 95% 0.027 0.027
Entropic Risk Measure Ratio at 95% 0.00022 0.00021
EVaR Ratio at 95% 0.012 0.012
Worst Realization Ratio 0.0063 0.0061
Drawdown at Risk Ratio at 95% 0.0073 0.0073
CDaR Ratio at 95% 0.0053 0.0052
Calmar Ratio 0.0019 0.0018
Average Drawdown Ratio 0.027 0.027
EDaR Ratio at 95% 0.0033 0.0032
Ulcer Index Ratio 0.016 0.016
Gini Mean Difference Ratio 0.065 0.065
Effective Number of Assets 18.99586745481971 18.460872007821077
Assets Number 20 20


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

Gallery generated by Sphinx-Gallery