{"record":{"id":"3cb9b4e9be42fc59","repo":"HKUDS/Vibe-Trading","slug":"delay-requires-n-1-lookahead-ban-3cb9b4","errorCode":null,"errorMessage":"delay requires n >= 1 (lookahead ban)","messagePattern":"delay requires n >= 1 \\(lookahead ban\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/factors/zoo/alpha101/alpha_052.py","lineNumber":62,"sourceCode":"    'extras_required': [],\n    'requires_sector': False,\n    'universe': ['equity_us', 'equity_in', 'equity_kr'],\n    'frequency': ['1D'],\n    'decay_horizon': 5,\n    'min_warmup_bars': 240,\n    'notes': 'Very long lookback (>= ~100 bars); produces NaN warmup on short panels which may trigger the >95% NaN registry guard.',\n}\n\n\ndef _rolling_sum(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Rolling window sum; warmup -> NaN.\"\"\"\n    return df.rolling(window=n, min_periods=n).sum()\n\n\ndef _delay(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Backward shift by n (lookahead-safe; n>=1 required).\"\"\"\n    if n < 1:\n        raise ValueError(\"delay requires n >= 1 (lookahead ban)\")\n    return df.shift(n)\n\n\ndef compute(panel: dict) -> pd.DataFrame:\n    \"\"\"Compute the alpha on the OHLCV+ panel and return a wide DataFrame.\"\"\"\n    close = panel[\"close\"]\n    low = panel[\"low\"]\n    volume = panel[\"volume\"]\n\n    returns = close.pct_change(fill_method=None)\n    # Helper aliases (local closures keep the file standalone & purity-safe).\n    rolling_sum = _rolling_sum\n    delay = _delay\n    out = ((-1.0 * ts_min(low, 5)) + delay(ts_min(low, 5), 5)) * rank((rolling_sum(returns, 240) - rolling_sum(returns, 20)) / 220.0) * ts_rank(volume, 5)\n    return out\n","sourceCodeStart":44,"sourceCodeEnd":78,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/factors/zoo/alpha101/alpha_052.py#L44-L78","documentation":"The _delay guard in alpha_052.py rejects n < 1 to keep the alpha lookahead-safe: df.shift(0) would expose the current bar, so ValueError is raised before shifting. The rolling helper directly above shows this file mixes windows and lags; only the lag argument can trigger it.","triggerScenarios":"A _delay(df, n) call with n <= 0 inside alpha #52's compute(), e.g. from a lag constant typo or computed window collapsing to 0.","commonSituations":"Transcribing Alpha#52 (which pairs rolling sums with delays) and swapping a window/lag value; refactors of the shared helper block; config-driven lag lists containing 0.","solutions":["Verify every _delay argument in alpha_052.py is an int >= 1","Use the operand directly if current-bar access is intended","Validate lag configs at ingestion; reject < 1 early with a descriptive error","Re-run compute against a fixture panel"],"exampleFix":"// before\nval = _delay(_sum(close, 5), 0)\n// after\nval = _sum(close, 5)","handlingStrategy":"validation","validationCode":"if window < 1 or lag < 1:\n    raise ValueError(f'window and lag must be >= 1, got window={window}, lag={lag}')\nout = _delay(_sum(close, window), lag)","typeGuard":"def is_valid_delay(n: int) -> bool:\n    return isinstance(n, int) and not isinstance(n, bool) and n >= 1","tryCatchPattern":null,"preventionTips":["Don't confuse window args with lag args when editing","Use the unshifted value for lag 0 intent","Validate combined window/lag configs upfront"],"tags":["pandas","alpha101","lookahead-bias","input-validation"],"backgroundTag":"invalid-window-argument","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}