Source code for skfolio.descriptor._reversal._reversal

"""Fixed-window short-term reversal descriptor."""

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

from __future__ import annotations

from skfolio.descriptor._base import _BaseRollingLogReturn
from skfolio.typing import FloatArray


[docs] class Reversal(_BaseRollingLogReturn): r"""Fixed-window short-term reversal descriptor. Computes the negated cumulative log return over a trailing window: .. math:: \text{reversal}(t) = -\sum_{k=t-w+1}^{t} \log(1 + r_k) where :math:`w` is the `window` size and :math:`r_k` is the asset return at observation :math:`k`. High values indicate recent poor performance (reversal candidates). Short-term reversal captures mean reversion in returns driven by temporary price pressure, liquidity provision and microstructure effects [1]_ [2]_. The output is NaN until an asset has a full active lookback window. Active assets with missing returns contribute zero to the log-return sum. Non-missing `returns` values must be finite and greater than `-1`. The descriptor is returned in log-return space. Log cumulative returns are more symmetric than simple cumulative returns, which makes them better suited to cross-sectional standardization. Because the logarithm is monotonic, log-space and simple cumulative returns produce the same cross-sectional rankings when returns are finite and greater than `-1`. Parameters ---------- window : int, default=21 Number of trailing observations for the cumulative return. Common choices for daily data: - `window=1`: 1-day reversal - `window=5`: 1-week reversal - `window=21`: 1-month reversal (default) Attributes ---------- n_assets_ : int Number of assets seen during fitting. asset_names_ : ndarray of shape (n_assets,) Asset names seen during fitting. reversal_ : ndarray of shape (n_assets,) Last short-term reversal value for each asset. Notes ----- Two code paths are used depending on context: - Batch (first call with sufficient data): vectorized cumsum over the full panel. Time :math:`O(T \cdot n)`, space :math:`O(T \cdot n)`. - Online (subsequent calls or streaming): ring buffer of size :math:`w` (the window) with a running sum. Per observation: one subtract (value leaving the window), one add (current value), one write. Time :math:`O(n)` per step, space :math:`O(w \cdot n)`, zero allocation. After a batch computation, the ring buffer state is populated for subsequent online calls. References ---------- .. [1] "Evidence of predictable behavior of security returns" The Journal of Finance. Jegadeesh, N. (1990). .. [2] "Fads, martingales, and market efficiency" The Quarterly Journal of Economics. Lehmann, B. N. (1990). See Also -------- EWMomentum : Exponentially weighted momentum (medium/long-term). RollingMomentum : Fixed-window momentum with optional skip. Examples -------- >>> from skfolio.datasets import make_synthetic_characteristics >>> from skfolio.descriptor import Reversal >>> >>> X = make_synthetic_characteristics() >>> >>> # 1-month reversal (default) >>> descriptor = Reversal() >>> reversal = descriptor.fit_transform(X) >>> >>> # 1-day reversal >>> descriptor = Reversal(window=1) >>> reversal_1d = descriptor.fit_transform(X) >>> >>> # 1-week reversal >>> descriptor = Reversal(window=5) >>> reversal_5d = descriptor.fit_transform(X) """ _FITTED_ATTR = "reversal_" _TRANSFORM_SIGN = -1.0 reversal_: FloatArray def __init__(self, window: int = 21): super().__init__(window=window, skip=0, exponentiate=False)