#### NOTE
[Go to the end](#sphx-glr-download-auto-examples-online-learning-plot-online-schur-changing-universe-py)
to download the full example code or to run this example in your browser via JupyterLite.

<a id="sphx-glr-auto-examples-online-learning-plot-online-schur-changing-universe-py"></a>

<a id="online-schur-allocation-with-a-changing-universe"></a>

# Online Schur Allocation with a Changing Universe

This tutorial demonstrates online learning with
[`SchurComplementary`](https://skfolio.org/generated/skfolio.optimization.SchurComplementary.html.md#skfolio.optimization.SchurComplementary). We follow a newly listed asset
through its warm-up period, then examine a holiday and a delisting.

`partial_fit` updates the estimates with new observations and computes a new
allocation. Assets can become investable or leave the investment universe without
restarting estimation.

<a id="returns-and-active-mask"></a>

## Returns and Active Mask

We generate 180 observations for four assets with a common market component.
The data includes three events:

* Asset “C” is listed at observation 61.
* Asset “A” has a holiday at observations 101 to 105.
* Asset “B” is delisted at observation 141.

The `active_mask` distinguishes missing observations from inactive periods.
During a holiday, a NaN return with `active_mask=True` tells the EW estimators
to freeze the asset’s mean and variance estimates until observations resume.
Before listing and after delisting, `active_mask=False` resets its estimates
and marks it unavailable for allocation. See [Fixed Asset Schema in Online Learning](https://skfolio.org/user_guide/data_representation.html.md#fixed-asset-schema) for
the convention used to represent changing universes in the data.

```Python
import numpy as np
import pandas as pd
from plotly.io import show
from sklearn import set_config

from skfolio.distance import CovarianceDistance
from skfolio.model_selection import online_predict
from skfolio.moments import EWCovariance, EWMu
from skfolio.optimization import SchurComplementary
from skfolio.prior import EmpiricalPrior
from skfolio.seriation import SpectralSeriation

set_config(enable_metadata_routing=True)

rng = np.random.default_rng(42)
market = rng.normal(0, 0.006, size=(180, 1))
X = pd.DataFrame(
    market + rng.normal(0, 0.01, size=(180, 4)),
    index=pd.RangeIndex(1, 181, name="Observation"),
    columns=["A", "B", "C", "D"],
)
active_mask = pd.DataFrame(True, index=X.index, columns=X.columns)

X.loc[:60, "C"] = np.nan
active_mask.loc[:60, "C"] = False

X.loc[101:105, "A"] = np.nan

X.loc[141:, "B"] = np.nan
active_mask.loc[141:, "B"] = False
```

We inspect six consecutive observations around the listing of asset “C”.
Its first return appears at observation 61.

```Python
X.loc[58:63]
```

[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="warm-up-holidays-and-delisting"></a>

## Warm-Up, Holidays and Delisting

Asset “C” completes warm-up with its twentieth valid return at observation 80
and first receives a nonzero weight at observation 81.

The EW estimators also correct the initialization bias caused by starting
their estimates at zero. They rescale the estimates using each asset’s valid
observation count, so early estimates are not damped toward zero. This
correction is separate from the warm-up threshold, which controls when the
estimates become available for allocation.

During the holiday for asset “A” (observations 101 to 105), its mean and
variance estimates freeze. It remains investable, and its target weight can
change as the estimates for other assets are updated.

When asset “B” is delisted at observation 141, `active_mask=False` resets its
EW estimates. The next portfolio, at observation 142, assigns it zero weight.
If the asset re-enters the universe later, it needs another warm-up period.

See [Updates and Failure Handling](https://skfolio.org/user_guide/online_learning.html.md#online-failure-handling) for handling failed updates.

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

<a id="sphx-glr-download-auto-examples-online-learning-plot-online-schur-changing-universe-py"></a>
[![Launch JupyterLite](auto_examples/online_learning/images/jupyterlite_badge_logo.svg)](../../lite/lab/index.html?path=auto_examples/online_learning/plot_online_schur_changing_universe.ipynb)

[`Download Jupyter notebook: plot_online_schur_changing_universe.ipynb`](https://skfolio.org/auto_examples/online_learning/_downloads/48a595f5a58599277c32369f15e71b4b/plot_online_schur_changing_universe.ipynb)

[`Download Python source code: plot_online_schur_changing_universe.py`](https://skfolio.org/auto_examples/online_learning/_downloads/c85121aabaab0302051b013b830dadfa/plot_online_schur_changing_universe.py)

[`Download zipped: plot_online_schur_changing_universe.zip`](https://skfolio.org/auto_examples/online_learning/_downloads/8235a05e7a09b3f41f37ed8fc7d95404/plot_online_schur_changing_universe.zip)

[Gallery generated by Sphinx-Gallery](https://sphinx-gallery.github.io)
