Note
Go to the end to download the full example code. or to run this example in your browser via Binder
Empirical Prior#
This tutorial shows how to use the EmpiricalPrior
estimator in
the MeanRisk
optimization.
A prior estimator fits a PriorModel
containing
the distribution estimate of asset returns. It represents the investor’s prior beliefs
about the model used to estimate such distribution.
The PriorModel
is a dataclass containing:
mu
: Expected returns estimation
covariance
: Covariance matrix estimation
returns
: assets returns estimation
cholesky
: Lower-triangular Cholesky factor of the covariance estimation (optional)
The EmpiricalPrior
estimator simply estimates the PriorModel
from a mu_estimator
and a covariance_estimator
.
In this tutorial we will build a Maximum Sharpe Ratio portfolio using the
EmpiricalPrior
estimator with James-Stein shrinkage for the estimation of expected
returns and Denoising for the estimation of the covariance matrix.
Data#
We load the S&P 500 dataset composed of the daily prices of 20 assets from the SPX 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 DenoiseCovariance, ShrunkMu
from skfolio.optimization import MeanRisk, ObjectiveFunction
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 Maximum Sharpe Ratio model with shrinkage for the estimation of the expected returns and denoising for the estimation of the covariance matrix:
model = MeanRisk(
risk_measure=RiskMeasure.VARIANCE,
objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
prior_estimator=EmpiricalPrior(
mu_estimator=ShrunkMu(), covariance_estimator=DenoiseCovariance()
),
portfolio_params=dict(name="Max Sharpe - ShrunkMu & DenoiseCovariance"),
)
model.fit(X_train)
model.weights_
array([5.30314924e-02, 3.93771054e-10, 1.58269647e-10, 5.78708311e-02,
1.05717669e-01, 4.89322196e-10, 1.23352411e-02, 1.64823756e-01,
2.96895546e-10, 8.40812344e-02, 8.98213665e-10, 9.90879685e-10,
6.51844546e-02, 7.44938306e-02, 9.78413867e-09, 1.27194222e-01,
3.87866505e-02, 6.81161325e-02, 4.35005322e-02, 1.04863941e-01])
Benchmark#
For comparison, we also create a Maximum Sharpe Ratio model using the default moments estimators:
bench = MeanRisk(
risk_measure=RiskMeasure.VARIANCE,
objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
portfolio_params=dict(name="Max Sharpe"),
)
bench.fit(X_train)
bench.weights_
array([9.43855248e-02, 2.09226948e-10, 7.30530766e-11, 1.20898034e-01,
3.18441126e-02, 1.28498817e-10, 1.28522027e-04, 1.24120028e-01,
1.43231855e-10, 2.78010014e-02, 2.19871439e-10, 2.49938736e-10,
1.16368341e-01, 5.73909314e-02, 1.74856659e-09, 1.09507654e-01,
8.64772972e-02, 1.84021249e-01, 1.34862950e-02, 3.35710067e-02])
Prediction#
We predict both models on the test set:
pred_model = model.predict(X_test)
pred_bench = bench.predict(X_test)
population = Population([pred_model, pred_bench])
fig = population.plot_cumulative_returns()
show(fig)
Total running time of the script: (0 minutes 1.151 seconds)