{"record":{"id":"0da6ccc223ce783a","repo":"HKUDS/Vibe-Trading","slug":"delay-requires-n-1-lookahead-ban-0da6cc","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_032.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': 235,\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    vwap = panel[\"vwap\"]\n\n\n    # Helper aliases (local closures keep the file standalone & purity-safe).\n    rolling_sum = _rolling_sum\n    delay = _delay\n    out = scale(rolling_sum(close, 7) / 7.0 - close) + 20.0 * scale(ts_corr(vwap, delay(close, 5), 230))\n    return out\n","sourceCodeStart":44,"sourceCodeEnd":77,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/factors/zoo/alpha101/alpha_032.py#L44-L77","documentation":"The _delay helper in alpha_032.py enforces the lookahead ban: shifting a DataFrame by n < 1 would reference the current or a future row, so it raises ValueError('delay requires n >= 1 (lookahead ban)') before df.shift(n). This is a hard precondition, not a runtime data issue.","triggerScenarios":"compute(panel) for alpha #32 calls _delay with n <= 0 — typically a mistyped constant or a computed window (e.g. `_delay(x, d - 1)` with d == 1) inside the alpha's formula chain.","commonSituations":"Porting Alpha#32's definition where a delay argument is derived arithmetically; refactoring shared helpers and accidentally changing the delay argument; config-driven window parameters set to 0.","solutions":["Inspect every _delay call in alpha_032.py and ensure each argument is an int >= 1","Replace any _delay(x, 0) with plain `x` (the identity the formula intends)","Guard parameterized values at compute() entry: `if n < 1: raise ValueError(...)` with a config-specific message","Add a unit test asserting compute() works on a small synthetic panel"],"exampleFix":"// before\ncond = _delay(close, 0) > _delay(close, 1)\n// after\ncond = close > _delay(close, 1)","handlingStrategy":"validation","validationCode":"LAGS = {'d1': 1, 'd2': 5}\nassert all(v >= 1 for v in LAGS.values()), f\"invalid lags: {LAGS}\"\n# then use _delay(df, LAGS['d1']) etc.","typeGuard":"def is_valid_delay(n: int) -> bool:\n    return isinstance(n, int) and not isinstance(n, bool) and n >= 1","tryCatchPattern":null,"preventionTips":["Translate 'delay(x, 0)' in reference formulas as plain x","Keep lag arithmetic explicit and avoid expressions like n - 1 near the boundary","Review every _delay literal when porting an alpha"],"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"}