Source code for skfolio.descriptor._volatility._ew_volatility

"""Exponentially weighted volatility descriptors."""

# Copyright (c) 2023-2026
# Author: Hugo Delatte <hugo.delatte@skfoliolabs.com>
# SPDX-License-Identifier: BSD-3-Clause

from __future__ import annotations

from skfolio.descriptor._volatility._base import _BaseEWVolatility


[docs] class EWVolatility(_BaseEWVolatility): r"""Exponentially weighted volatility descriptor. Computes return volatility with an EWMA variance estimate: .. math:: :nowrap: \[ \begin{aligned} S_i(t) &= \lambda \cdot S_i(t-1) + (1 - \lambda) \cdot r_i(t)^2 \\[0.75em] \text{output}_i(t) &= \sqrt{\frac{S_i(t)}{1 - \lambda^{n_i(t)}}} \end{aligned} \] where :math:`\lambda = \exp(-\ln(2)/\text{half\_life})` and :math:`n_i(t)` is the number of valid returns for asset :math:`i`. This descriptor uses raw returns, so the estimate includes both systematic and idiosyncratic risk. Use :class:`EWResidualVolatility` to remove market exposure first. Parameters ---------- half_life : float, default=40.0 EWMA half-life in observations. min_periods : int, optional Minimum number of valid returns required for each asset. Until an asset reaches this count, its output is NaN. This warm-up period avoids exposing early EWMA values before the volatility estimate has sufficiently converged from its zero initialization. If `None`, defaults to :math:`\lceil\text{half\_life}\rceil`, with a minimum of 1. Attributes ---------- n_assets_ : int Number of assets seen during fitting. asset_names_ : ndarray of shape (n_assets,) Asset names seen during fitting. volatility_ : ndarray of shape (n_assets,) Last computed EWMA volatility. Contains NaN for inactive assets and assets that have not reached `min_periods` valid returns. Notes ----- The EWMA variance accumulator is initialized to zero and bias-corrected at output time using each asset's valid observation count, matching :class:`~skfolio.moments.EWVariance`. NaNs are treated as missing observations. Active assets with missing returns keep their previous EWMA state and inactive assets output NaN and restart their warm-up period when they become active again. Non-missing returns must be finite. The variance is computed assuming centered returns (no demeaning), which is the standard convention for EWMA variance estimation in cross-sectional factor models. See Also -------- EWDownsideVolatility : Downside variant using semi-deviation of returns. EWResidualVolatility : CAPM residual volatility (market exposure removed). Examples -------- >>> from skfolio.datasets import make_synthetic_characteristics >>> from skfolio.descriptor import EWVolatility >>> >>> X = make_synthetic_characteristics() >>> >>> descriptor = EWVolatility() >>> volatility = descriptor.fit_transform(X) """ def __init__(self, half_life: float = 40.0, min_periods: int | None = None): super().__init__( half_life=half_life, min_acceptable_return=None, min_periods=min_periods )