<a id="sphx-glr-auto-examples-metadata-routing-plot-1-implied-volatility-py"></a>

<a id="using-implied-volatility-with-metadata-routing"></a>

# Using Implied Volatility with Metadata Routing

This tutorial shows how to use [metadata routing](https://skfolio.org/user_guide/metadata_routing.html.md#metadata-routing).

We will use the [`ImpliedCovariance`](https://skfolio.org/generated/skfolio.moments.ImpliedCovariance.html.md#skfolio.moments.ImpliedCovariance) estimator inside
optimization models and grid search procedures to show how the implied volatility
time series can be routed.

<a id="load-datasets"></a>

## Load Datasets

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 S&P 500 Index composition and the implied volatility time series
of these 20 assets starting from 2010-01-04 up to 2022-12-28.

```Python
import numpy as np
import pandas as pd
import plotly.express as px
from plotly.io import show
from sklearn import set_config
from sklearn.model_selection import GridSearchCV, train_test_split

from skfolio import Population, RatioMeasure
from skfolio.datasets import load_sp500_dataset, load_sp500_implied_vol_dataset
from skfolio.metrics import make_scorer
from skfolio.model_selection import WalkForward, cross_val_predict
from skfolio.moments import (
    EmpiricalCovariance,
    GerberCovariance,
    ImpliedCovariance,
    LedoitWolf,
)
from skfolio.optimization import InverseVolatility, MeanRisk
from skfolio.preprocessing import prices_to_returns
from skfolio.prior import EmpiricalPrior

prices = load_sp500_dataset()
implied_vol = load_sp500_implied_vol_dataset()

X = prices_to_returns(prices)
X = X.loc["2010":]

implied_vol.head()
```

[plotly figure stripped from llms output]
<br />
<br />

```Python
population.plot_cumulative_returns()
```

[plotly figure stripped from llms output]
<br />
<br />

<a id="hyper-parameters-tuning"></a>

## Hyper-Parameters Tuning

In this section, we show how to use metadata routing with `GridSearchCV`.
First, we split the data into a train and a test set:

```Python
X_train, X_test, implied_vol_train, implied_vol_test = train_test_split(
    X, implied_vol, test_size=1 / 2, shuffle=False
)
```

We create a Minimum Variance that uses the `ImpliedCovariance` estimator:

```Python
model = MeanRisk(
    prior_estimator=EmpiricalPrior(
        covariance_estimator=ImpliedCovariance().set_fit_request(implied_vol=True)
    )
)
```

Then, we find the hyper-parameters of the `ImpliedCovariance` estimator that
maximizes the out-of-sample Sharpe Ratio of the Minimum Variance model:

```Python
grid_search = GridSearchCV(
    estimator=model,
    param_grid={
        "prior_estimator__covariance_estimator__window_size": np.arange(5, 50, 3),
        "prior_estimator__covariance_estimator__prior_covariance_estimator": [
            LedoitWolf(),
            GerberCovariance(),
            EmpiricalCovariance(),
        ],
    },
    return_train_score=True,
    scoring=make_scorer(RatioMeasure.ANNUALIZED_SHARPE_RATIO),
    n_jobs=-1,
    cv=cv,
)
grid_search.fit(X_train, implied_vol=implied_vol_train)
gs_model = grid_search.best_estimator_
print(gs_model)
```

```none
MeanRisk(prior_estimator=EmpiricalPrior(covariance_estimator=ImpliedCovariance(annualization_factor=252.0,
                                                                               prior_covariance_estimator=GerberCovariance(),
                                                                               window_size=np.int64(17))))
```

Let’s plot the out-of-sample Sharpe Ratio as a function of the window size and
the prior covariance estimator used to compute the correlation matrix:

```Python
cv_results = grid_search.cv_results_

df = pd.DataFrame(
    {
        "Prior Cov Estimator": [
            str(x)
            for x in cv_results[
                "param_prior_estimator__covariance_estimator__prior_covariance_estimator"
            ]
        ],
        "Window Size": cv_results[
            "param_prior_estimator__covariance_estimator__window_size"
        ],
        "Test Sharpe Ratio": cv_results["mean_test_score"],
        "error": cv_results["std_test_score"] / 10,  # one tenth of std for readability
    }
)
px.line(
    df,
    x="Window Size",
    y="Test Sharpe Ratio",
    color="Prior Cov Estimator",
    error_y="error",
    title="Out-of-Sample Sharpe Ratio",
)
```

[plotly figure stripped from llms output]
<br />
<br />

Finally, we compare the optimal Grid Search model with a naive Minimum Variance
benchmark on the **test set**:

```Python
pred_gs_model = cross_val_predict(
    gs_model, X_test, params={"implied_vol": implied_vol_test}, cv=cv, n_jobs=-1
)
pred_gs_model.name = "GS Model"

benchmark = MeanRisk()
pred_bench = cross_val_predict(benchmark, X_test, cv=cv)
pred_bench.name = "Benchmark"

population = Population([pred_bench, pred_gs_model])
summary = population.summary()
print(summary.loc[["Annualized Standard Deviation", "Annualized Sharpe Ratio"]])
```

```none
                              Benchmark GS Model
Annualized Standard Deviation    18.12%   18.28%
Annualized Sharpe Ratio            0.59     0.67
```

```Python
population.plot_cumulative_returns()
```

[plotly figure stripped from llms output]
<br />
<br />

<a id="conclusion"></a>

## Conclusion

This was a toy example to introduce the metadata routing API.
For more information, see [Metadata Routing User Guide](https://skfolio.org/user_guide/metadata_routing.html.md#metadata-routing).

**Total running time of the script:** (1 minutes 37.157 seconds)

<a id="sphx-glr-download-auto-examples-metadata-routing-plot-1-implied-volatility-py"></a>
