<a id="sphx-glr-auto-examples-mean-risk-plot-14-black-litterman-factor-model-py"></a>

<a id="black-litterman-factor-model"></a>

# Black & Litterman Factor Model

This tutorial shows how to use the [`TimeSeriesFactorModel`](https://skfolio.org/generated/skfolio.prior.TimeSeriesFactorModel.html.md#skfolio.prior.TimeSeriesFactorModel) estimator coupled
with the [`BlackLitterman`](https://skfolio.org/generated/skfolio.prior.BlackLitterman.html.md#skfolio.prior.BlackLitterman) estimator in the
[`MeanRisk`](https://skfolio.org/generated/skfolio.optimization.MeanRisk.html.md#skfolio.optimization.MeanRisk) optimization.

The Black & Litterman Factor Model is a Factor Model in which we incorporate views on
factors using the Black & Litterman Model.

In the previous two tutorials, we introduced the Factor Model and the Black & Litterman
separately. In this tutorial we show how we can merge them together by building a
Maximum Sharpe Ratio portfolio using the `TimeSeriesFactorModel` estimator.

<a id="data"></a>

## Data

We load the S&P 500 [dataset](https://skfolio.org/user_guide/datasets.html.md#datasets) composed of the daily prices of 20
assets from the SPX Index composition and the Factors dataset composed of the daily
prices of 5 ETFs representing common factors:

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

from skfolio import Population, RiskMeasure
from skfolio.datasets import load_factors_dataset, load_sp500_dataset
from skfolio.optimization import MeanRisk, ObjectiveFunction
from skfolio.preprocessing import prices_to_returns
from skfolio.prior import BlackLitterman, TimeSeriesFactorModel

prices = load_sp500_dataset()
factor_prices = load_factors_dataset()

prices = prices["2014":]
factor_prices = factor_prices["2014":]

X, factors = prices_to_returns(prices, factor_prices)
X_train, X_test, factors_train, factors_test = train_test_split(
    X, factors, test_size=0.33, shuffle=False
)
```

<a id="analyst-views"></a>

## Analyst views

Let’s assume we are able to accurately estimate views about future realization of the
factors. We estimate that the factor Size will have an expected return of 10% p.a.
(absolute view) and will outperform the factor Value by 3% p.a. (relative view). We
also estimate the factor Momentum will outperform the factor Quality by 2% p.a
(relative view). By converting these annualized estimates into daily estimates to be
homogeneous with the input `X`, we get:

```Python
factor_views = [
    "SIZE == 0.00039",
    "SIZE - VLUE == 0.00011 ",
    "MTUM - QUAL == 0.00007",
]
```

<a id="id1"></a>

## Black & Litterman Factor Model

We create a Maximum Sharpe Ratio model using the Black & Litterman Factor Model that
we fit on the training set:

```Python
model_bl_factor = MeanRisk(
    risk_measure=RiskMeasure.VARIANCE,
    objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
    prior_estimator=TimeSeriesFactorModel(
        factor_prior_estimator=BlackLitterman(views=factor_views),
    ),
    portfolio_params=dict(name="Black & Litterman Factor Model"),
)
model_bl_factor.fit(X_train, factors=factors_train)
model_bl_factor.weights_
```

```none
array([5.55998971e-02, 2.42678756e-02, 3.63991372e-02, 1.97989097e-02,
       4.38883299e-08, 1.69582846e-02, 1.34103109e-01, 3.52966988e-07,
       2.78013243e-02, 9.56133325e-02, 6.72490988e-02, 9.04221682e-02,
       1.24559355e-01, 8.98176230e-02, 5.85674115e-02, 2.83525179e-02,
       6.69595123e-03, 1.02316969e-01, 2.14765919e-02, 4.61287984e-08])
```

For comparison, we also create a Maximum Sharpe Ratio model using a simple Factor
Model:

```Python
model_factor = MeanRisk(
    risk_measure=RiskMeasure.VARIANCE,
    objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
    prior_estimator=TimeSeriesFactorModel(),
    portfolio_params=dict(name="Factor Model"),
)
model_factor.fit(X_train, factors=factors_train)
model_factor.weights_
```

```none
array([1.03294289e-06, 1.27482685e-03, 4.19682806e-07, 3.34130827e-06,
       7.36838290e-07, 1.28824409e-06, 5.13031432e-02, 6.35619183e-02,
       6.14804837e-07, 1.79106051e-01, 5.03130911e-02, 7.13734379e-02,
       4.13002526e-02, 2.27978407e-01, 5.13348034e-02, 1.44130375e-01,
       2.99026118e-07, 6.19737850e-02, 5.63413085e-02, 8.67773201e-07])
```

<a id="prediction"></a>

## Prediction

We predict both models on the test set:

```Python
ptf_bl_factor_test = model_bl_factor.predict(X_test)
ptf_factor_test = model_factor.predict(X_test)

population = Population([ptf_bl_factor_test, ptf_factor_test])

population.plot_cumulative_returns()
```

<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>[plotly figure stripped from llms output]
<br />
<br />

Because our factor views were accurate, the Black & Litterman Factor Model
outperformed the simple Factor Model on the test set.

Let’s plot the portfolios compositions:

```Python
fig = population.plot_composition()
show(fig)
```

[plotly figure stripped from llms output]<style>html[data-theme="dark"] div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}@media (prefers-color-scheme: dark){html:not([data-theme="light"]) div.output_subarea:has(.plotly-graph-div){background:#fff;border-radius:0.25rem;padding:0.5rem}}</style><script>if (!window.plotlySphinxGalleryResize) {window.plotlySphinxGalleryResize = true;window.addEventListener("load", function () {document.querySelectorAll(".plotly-graph-div").forEach(function (gd) { Plotly.Plots.resize(gd); });});}</script>

<a id="going-further"></a>

## Going Further

The API design makes it possible to created nested models without limits.
In the below example, we re-apply a Black & Litterman model incorporating assets
views. But instead of using the empirical moments, we use the above Black & Litterman
factor model:

```Python
assets_views = [
    "AAPL == 0.00098",
    "AAPL - GE == 0.00086",
    "JPM - GE == 0.00059",
]

model = BlackLitterman(
    views=assets_views,
    prior_estimator=TimeSeriesFactorModel(
        factor_prior_estimator=BlackLitterman(views=factor_views),
    ),
)

model.fit(X, factors=factors)
print(model.return_distribution_.covariance.shape)
```

```none
(20, 20)
```

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

<a id="sphx-glr-download-auto-examples-mean-risk-plot-14-black-litterman-factor-model-py"></a>
