Note
Go to the end to download the full example code. or to run this example in your browser via JupyterLite or Binder
Minimum CVaR#
This tutorial uses the MeanRisk
optimization to find the
minimum CVaR (Conditional Value at Risk) portfolio.
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. Prices are transformed into linear returns (see data preparation) and split into a training set and a test set without shuffling to avoid data leakage.
import numpy as np
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 EqualWeighted, MeanRisk, ObjectiveFunction
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)
print(X_train.head())
AAPL AMD BAC ... UNH WMT XOM
Date ...
1990-01-03 0.007576 -0.030303 0.008045 ... -0.019355 0.000000 -0.010079
1990-01-04 0.003759 -0.015500 -0.021355 ... -0.009868 -0.005201 -0.009933
1990-01-05 0.003745 -0.031996 -0.021821 ... -0.043189 -0.010732 -0.005267
1990-01-08 0.003731 0.000000 0.005633 ... -0.020833 0.013630 0.015381
1990-01-09 -0.007435 0.016527 0.000000 ... -0.024823 -0.026619 -0.020114
[5 rows x 20 columns]
Model#
We create a Minimum CVaR model and then fit it on the training set.
portfolio_params
are parameters passed to the Portfolio
returned by the predict
method. It can be
omitted, here we use it to give a name to our minimum CVaR portfolio:
model = MeanRisk(
risk_measure=RiskMeasure.CVAR,
objective_function=ObjectiveFunction.MINIMIZE_RISK,
portfolio_params=dict(name="Min CVaR"),
)
model.fit(X_train)
model.weights_
array([2.16562529e-02, 1.41742527e-10, 3.64888981e-11, 1.47358386e-02,
1.35927671e-01, 1.91035768e-10, 3.58350612e-10, 2.10319630e-01,
4.89793803e-11, 8.14733606e-02, 1.92817471e-02, 4.13607836e-10,
7.79844555e-10, 1.26155697e-01, 4.04585759e-10, 1.52708811e-01,
1.20106233e-02, 6.41726679e-03, 1.01024013e-01, 1.18289085e-01])
To compare this model, we use an equal-weighted benchmark using
EqualWeighted
:
benchmark = EqualWeighted(portfolio_params=dict(name="Equal Weighted"))
# Even if `X` has no impact (as it is equal weighted), we still need to call `fit` for
# API consistency.
benchmark.fit(X_train)
benchmark.weights_
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])
Prediction#
We predict the model and the benchmark on the test set:
pred_model = model.predict(X_test)
pred_bench = benchmark.predict(X_test)
The predict
method returns a Portfolio
object.
Portfolio
is an array-container making it compatible
with scikit-learn
tools: calling np.asarray(pred_model)
gives the portfolio
returns (same as pred_model.returns
):
np.asarray(pred_model)
array([ 0.00354605, 0.00342423, -0.00105085, ..., 0.01069377,
0.00542224, -0.01217676])
The Portfolio
class contains a vast number of properties
and methods used for analysis.
pred_model.plot_cumulative_returns()
pred_model.plot_composition()
pred_model.summary()
print(pred_model.cvar)
print(pred_bench.cvar)
0.021741551637176403
0.025061083134673378
Analysis#
For improved analysis, we load both predicted portfolios into a
Population
:
population = Population([pred_model, pred_bench])
The Population
class also contains a
vast number of properties and methods used for analysis.
Let’s plot each portfolio composition:
population.plot_composition()
Note
Every plot
methods in skfolio
returns a plotly
figure.
To display a plotly figure, you may need to call show()
and change the
default renderer: https://plotly.com/python/renderers/
Let’s plot each portfolio cumulative returns:
fig = population.plot_cumulative_returns()
# show(fig) is only used for the documentation sticker.
show(fig)
Finally, let’s display the full summary of both strategies evaluated on the test set:
population.summary()
Conclusion#
From the analysis on the test set, we see that the Minimum CVaR portfolio outperforms the equal-weighted benchmark for all deviation and shortfall risk measures, except for the drawdown measures, and underperforms for the mean and ratio measures.
See also
This was a toy example, for more advanced concepts check the user guide or the other examples.
Total running time of the script: (0 minutes 1.488 seconds)