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.29957573e-02, 5.62258516e-07, 2.19431587e-07, 5.78329525e-02,
       1.05704853e-01, 6.42635886e-07, 1.25145591e-02, 1.64813030e-01,
       3.95272189e-07, 8.40687639e-02, 1.20296728e-06, 1.33374541e-06,
       6.51482298e-02, 7.44911613e-02, 7.02327401e-06, 1.27177410e-01,
       3.87852705e-02, 6.81199403e-02, 4.34872838e-02, 1.04849409e-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.43810178e-02, 1.88320390e-07, 8.18774110e-08, 1.20884991e-01,
       3.18380243e-02, 1.39452541e-07, 2.51662503e-04, 1.24116120e-01,
       1.54557788e-07, 2.77907315e-02, 2.04004716e-07, 2.27967039e-07,
       1.16354111e-01, 5.73833574e-02, 1.60433892e-06, 1.09504559e-01,
       8.64771747e-02, 1.84015519e-01, 1.34326017e-02, 3.35675295e-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.126 seconds)

Gallery generated by Sphinx-Gallery