diff --git a/src/quant_engine/alpha_factors.py b/src/quant_engine/alpha_factors.py index eaa15ea..2079417 100644 --- a/src/quant_engine/alpha_factors.py +++ b/src/quant_engine/alpha_factors.py @@ -216,6 +216,8 @@ def indneutralize(series: pd.Series, groups: pd.Series) -> pd.Series: # orchestration. The full alpha158 formula catalogue can continue to grow, # while callers use one validated dispatch entry point for the first ten # deterministic building blocks. +ALPHA158_PHASE1_MAX_WINDOW = 252 + ALPHA158_PHASE1_OPERATOR_SPECS: dict[str, dict[str, Any]] = { "rank": { "name": "rank", @@ -316,13 +318,13 @@ def evaluate_phase1_operator( raise KeyError(f"operator {name!r} not registered") is_windowed = bool(ALPHA158_PHASE1_OPERATOR_SPECS[name]["windowed"]) - if is_windowed and ( - window is None - or isinstance(window, bool) - or not isinstance(window, int) - or window <= 0 - ): - raise ValueError(f"window must be a positive integer for {name}") + if is_windowed: + if isinstance(window, bool) or not isinstance(window, int) or window <= 0: + raise ValueError(f"window must be a positive integer for {name}") + if window > ALPHA158_PHASE1_MAX_WINDOW: + raise ValueError( + f"window exceeds maximum supported value {ALPHA158_PHASE1_MAX_WINDOW} for {name}" + ) if not is_windowed and window is not None: raise ValueError(f"window is not supported for {name}") @@ -2888,6 +2890,7 @@ __all__ = [ "max_pair", "min_pair", "indneutralize", + "ALPHA158_PHASE1_MAX_WINDOW", "ALPHA158_PHASE1_OPERATOR_SPECS", "list_phase1_operators", "evaluate_phase1_operator", diff --git a/tests/test_alpha_factors.py b/tests/test_alpha_factors.py index 78cfe2a..6c3fc09 100644 --- a/tests/test_alpha_factors.py +++ b/tests/test_alpha_factors.py @@ -1321,3 +1321,10 @@ def test_phase1_dispatch_rejects_unknown_or_unused_arguments(): evaluate_phase1_operator("rank", values, window=2) with pytest.raises(ValueError, match="secondary"): evaluate_phase1_operator("rank", values, values) + + +def test_phase1_dispatch_rejects_window_above_supported_limit(): + values = pd.Series([1.0, 2.0, 3.0]) + + with pytest.raises(ValueError, match="maximum"): + evaluate_phase1_operator("ts_mean", values, window=2**63)