<a id="migration"></a>

<a id="migration-guide"></a>

# Migration Guide

`skfolio` follows [semantic versioning](https://semver.org). The public API remains
backward compatible within a major series. Deprecated functionality raises a
`FutureWarning` and is removed in the next major release.

This page documents the changes required to upgrade between major versions.

<a id="migration-1-0"></a>

<a id="migrating-to-1-0"></a>

## Migrating to 1.0

Version 1.0 introduces the stable public API. The parameters and aliases deprecated
during the 0.x series are removed in this release.

<a id="exponentially-weighted-moments"></a>

### Exponentially Weighted Moments

[`EWMu`](https://skfolio.org/generated/skfolio.moments.EWMu.html.md#skfolio.moments.EWMu) and [`EWCovariance`](https://skfolio.org/generated/skfolio.moments.EWCovariance.html.md#skfolio.moments.EWCovariance) no longer
accept `alpha`. Use `half_life`, the number of observations for a weight to decay
to 50%.

Before:

```python
EWMu(alpha=0.2)
EWCovariance(alpha=0.2)
```

After:

```python
EWMu(half_life=3.11)
EWCovariance(half_life=3.11)
```

The half-life equivalent of a given `alpha` is

$$
\text{half-life} = \frac{-1}{\log_2(1 - \alpha)}

$$

For example, `alpha=0.2` corresponds to a `half_life` of approximately $3.11$
and `alpha=0.02` to $34.31$. The decay factor is
$\lambda = 2^{-1/\text{half-life}}$, computed by
[`half_life_to_decay_factor`](https://skfolio.org/generated/skfolio.utils.tools.half_life_to_decay_factor.html.md#skfolio.utils.tools.half_life_to_decay_factor).

Passing `alpha` raises a `TypeError`.

<a id="walk-forward-cross-validation"></a>

### Walk-Forward Cross-Validation

[`WalkForward`](https://skfolio.org/generated/skfolio.model_selection.WalkForward.html.md#skfolio.model_selection.WalkForward) no longer accepts `expend_train`. Use
`expand_train`, which has identical behavior.

Before:

```python
WalkForward(test_size=60, train_size=252, expend_train=True)
```

After:

```python
WalkForward(test_size=60, train_size=252, expand_train=True)
```

<a id="factor-models"></a>

### Factor Models

The `FactorModel` prior estimator is replaced by
[`TimeSeriesFactorModel`](https://skfolio.org/generated/skfolio.prior.TimeSeriesFactorModel.html.md#skfolio.prior.TimeSeriesFactorModel), and `factors` is now a keyword-only
argument of `fit`.

Before:

```python
from skfolio.optimization import MeanRisk
from skfolio.prior import FactorModel

model = MeanRisk(prior_estimator=FactorModel())
model.fit(X_train, y_train)
```

After:

```python
from skfolio.optimization import MeanRisk
from skfolio.prior import TimeSeriesFactorModel

model = MeanRisk(prior_estimator=TimeSeriesFactorModel())
model.fit(X_train, factors=factors_train)
```

#### WARNING
`FactorModel` now refers to a different object: the fitted factor model container
exposed on `factor_model`, holding the
loading matrix, the factor and idiosyncratic moments, and the realized factor
returns. The import therefore still resolves, and estimator arguments passed to
`FactorModel` raise a `TypeError` for unexpected keyword arguments rather than an
`ImportError`.

[`CharacteristicsFactorModel`](https://skfolio.org/generated/skfolio.prior.CharacteristicsFactorModel.html.md#skfolio.prior.CharacteristicsFactorModel) provides a cross-sectional
alternative, fitted from point-in-time asset characteristics rather than factor return
time series. See [Factor Models](https://skfolio.org/user_guide/factor_models.html.md#factor-models).

<a id="uncertainty-sets"></a>

### Uncertainty Sets

[`UncertaintySet`](https://skfolio.org/generated/skfolio.uncertainty_set.UncertaintySet.html.md#skfolio.uncertainty_set.UncertaintySet) now describes a general norm-ball
rather than an ellipsoid, which allows box and diamond sets to use the same
representation. The ellipsoid is the $p = 2$ case.

The field names changed as follows:

| Before         | After      | Description                                                                                    |
|----------------|------------|------------------------------------------------------------------------------------------------|
| `k`            | `radius`   | Size $\kappa$ of the normalized uncertainty ball.                                              |
| `sigma`        | `geometry` | Linear map $L$ with $S = L L^{T}$ for an ellipsoid with shape<br/>matrix $S$. May be low-rank. |
| not applicable | `norm`     | Norm $p$ selecting the shape, defaulting to $2$ for an ellipsoid.                              |

This only affects code that constructs an `UncertaintySet` directly or reads the fitted
`uncertainty_set_` attribute. Passing an uncertainty set estimator to
[`MeanRisk`](https://skfolio.org/generated/skfolio.optimization.MeanRisk.html.md#skfolio.optimization.MeanRisk) is unchanged.

Two factor-model estimators are added:
[`OrthogonalMuUncertaintySet`](https://skfolio.org/generated/skfolio.uncertainty_set.OrthogonalMuUncertaintySet.html.md#skfolio.uncertainty_set.OrthogonalMuUncertaintySet) and
[`OrthogonalCovarianceUncertaintySet`](https://skfolio.org/generated/skfolio.uncertainty_set.OrthogonalCovarianceUncertaintySet.html.md#skfolio.uncertainty_set.OrthogonalCovarianceUncertaintySet). See
[Uncertainty Set](https://skfolio.org/user_guide/uncertainty_set.html.md#uncertainty-set-estimator).

<a id="migration-scheduled-removals"></a>

<a id="scheduled-for-removal-in-2-0"></a>

## Scheduled for Removal in 2.0

The following remain available throughout 1.x and raise a `FutureWarning`:

* `annualized_factor`, on [`Portfolio`](https://skfolio.org/generated/skfolio.portfolio.Portfolio.html.md#skfolio.portfolio.Portfolio) and
  [`ImpliedCovariance`](https://skfolio.org/generated/skfolio.moments.ImpliedCovariance.html.md#skfolio.moments.ImpliedCovariance). Use `annualization_factor`.
* `non_denominated_sort`, as a function and as a
  [`Population`](https://skfolio.org/generated/skfolio.population.Population.html.md#skfolio.population.Population) method. Use `non_dominated_sort`.
