Metadata Routing#
This document shows how you can use the metadata routing mechanism to route metadata to the estimators consuming them. For a complete explanation, you can refer to the scikit-learn documentation
A full example is available here: Using Implied Volatility with Metadata Routing
Let’s suppose you use the ImpliedCovariance estimator
inside a MeanRisk estimator.
In addition to the assets’ returns X, the ImpliedCovariance estimator also needs
the assets’ implied volatilities passed to its fit method.
In order to route the implied volatilities time series from the MeanRisk estimator
to the ImpliedCovariance estimator, we need metadata routing.
First, a few imports and some random data for the rest of the script:
from sklearn import set_config
from skfolio.moments import ImpliedCovariance
from skfolio.optimization import MeanRisk
from skfolio.prior import EmpiricalPrior
from skfolio.preprocessing import prices_to_returns
from skfolio.datasets import load_sp500_dataset, load_sp500_implied_vol_dataset
prices = load_sp500_dataset()
implied_vol = load_sp500_implied_vol_dataset()
X = prices_to_returns(prices)
X = X.loc["2010":]
Metadata routing is available only if explicitly enabled:
set_config(enable_metadata_routing=True)
ImpliedCovariance requires an explicit request for implied_vol:
model = MeanRisk(
prior_estimator=EmpiricalPrior(
covariance_estimator=ImpliedCovariance(
).set_fit_request(implied_vol=True)
)
)
model.fit(X, implied_vol=implied_vol)
print(model.weights_)
Default Requests#
EWMu, EWCovariance,
EWVariance,
RegimeAdjustedEWCovariance and
RegimeAdjustedEWVariance request active_mask by
default for both fit and partial_fit. With metadata routing enabled, pass
the mask to the enclosing prior or optimizer without configuring each child:
from skfolio.moments import EWCovariance, EWMu
prior = EmpiricalPrior(
mu_estimator=EWMu(),
covariance_estimator=EWCovariance(),
)
prior.partial_fit(X_batch, active_mask=active_batch)
The mask is optional. Omitting it treats all assets as active.
Use set_fit_request or set_partial_fit_request to override a default:
False stops forwarding the mask to that child, None raises if it is
supplied, and a string requests it under another name. These settings affect
routing through a parent, not direct calls to the child.
CharacteristicsFactorModel also requests
characteristics by default, and TimeSeriesFactorModel
requests factors by default.
Masks must match the observations and columns consumed by each estimator.
When a factor prior and an asset learner need different masks, use an alias
such as set_fit_request(active_mask="factor_active_mask") on the factor
moment estimators.
CharacteristicsFactorModel supplies its residual variance and correlation
estimators with masks derived from characteristics. To pass a separate
active_mask to another learner in the same model, disable the external mask
requests on these residual estimators. Their internally supplied panel masks
are still used.
Distance Inputs in HRP and Schur#
HRP and Schur can compute distances from prior scenarios, the prior covariance or input returns, as described in Seriation for HRP and Schur. These inputs differ in how observation metadata is routed.
During partial_fit, with distance_from_prior=True, distances fitted on prior
scenarios receive neither the incoming targets nor the incoming observation metadata.
For example, an active_mask describes the new return batch, which may differ from
the prior’s scenarios. If that distance uses an EW covariance estimator, setting its
set_fit_request(active_mask=False) allows the mask to reach the prior without
forwarding it to the distance.
With metadata routing enabled, scenario distances receive the prior’s
sample_weight only when they or their nested estimators request it. Without
routing, the distance’s fit must explicitly accept sample_weight. These
probabilities replace weights routed from the optimizer’s fit or partial_fit.
A prior with sample_weight=None gives scenarios equal weight.
Built-in distances fitted on scenario rows do not use sample_weight, even when
portfolio risk uses weighted scenarios. To weight a CovarianceDistance, supply
a covariance estimator that accepts weights and call
set_fit_request(sample_weight=True) on it. A distance using the prior’s covariance
retains any weighting already incorporated into that covariance.
With distance_from_prior=False, the distance receives targets and requested
metadata aligned with the optimizer’s input returns. An EW covariance estimator
requests active_mask by default. Precomputed covariance distances and seriation
receive no observation metadata, so CovarianceDistance("precomputed") needs no
mask configuration.
During batch fit, distances fitted on prior scenarios retain routing for metadata
other than sample_weight. The caller is responsible for aligning that metadata
with the scenarios.