From a93cf05fab54668208ac09a0f7e253cf68a5f0f5 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Wed, 17 Jun 2026 12:24:29 +0200 Subject: [PATCH] fix(equity-factor): guard non-positive recent price in momentum_12_1 (delisted-name safety) Co-Authored-By: Claude Opus 4.8 (1M context) --- src/fxhnt/domain/strategies/equity_factor.py | 2 +- tests/unit/test_equity_factor_helpers.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/fxhnt/domain/strategies/equity_factor.py b/src/fxhnt/domain/strategies/equity_factor.py index 02e76de..b6b8e0b 100644 --- a/src/fxhnt/domain/strategies/equity_factor.py +++ b/src/fxhnt/domain/strategies/equity_factor.py @@ -29,7 +29,7 @@ def momentum_12_1_from_closes(closes: list[float]) -> float | None: return None p_then = closes[-_LOOKBACK] p_recent = closes[-_SKIP] - if p_then is None or p_then <= 0: + if p_then is None or p_then <= 0 or p_recent is None or p_recent <= 0: return None return p_recent / p_then - 1.0 diff --git a/tests/unit/test_equity_factor_helpers.py b/tests/unit/test_equity_factor_helpers.py index 9bd5d7c..1b22665 100644 --- a/tests/unit/test_equity_factor_helpers.py +++ b/tests/unit/test_equity_factor_helpers.py @@ -19,6 +19,18 @@ def test_momentum_12_1_none_when_insufficient_history(): assert momentum_12_1_from_closes(_ramp(50)) is None +def test_momentum_12_1_none_when_recent_price_nonpositive(): + closes = _ramp(300) + closes[-21] = 0.0 # the "recent" (skip) price is non-positive + assert momentum_12_1_from_closes(closes) is None + + +def test_momentum_12_1_none_when_lookback_price_nonpositive(): + closes = _ramp(300) + closes[-252] = 0.0 # the lookback base price is non-positive + assert momentum_12_1_from_closes(closes) is None + + def test_realized_vol_zero_for_constant_series(): v = realized_vol_from_closes([100.0] * 120) assert v is not None and v == 0.0