{"record":{"id":"544ca8ec2b6dc275","repo":"HKUDS/Vibe-Trading","slug":"delay-requires-n-1-lookahead-ban-544ca8","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_045.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': 25,\n    'notes': '',\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    volume = panel[\"volume\"]\n\n\n    # Helper aliases (local closures keep the file standalone & purity-safe).\n    rolling_sum = _rolling_sum\n    delay = _delay\n    out = -1.0 * (rank(rolling_sum(delay(close, 5), 20) / 20.0) * ts_corr(close, volume, 2) * rank(ts_corr(rolling_sum(close, 5), rolling_sum(close, 20), 2)))\n    return out\n","sourceCodeStart":44,"sourceCodeEnd":77,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/factors/zoo/alpha101/alpha_045.py#L44-L77","documentation":"The _delay helper in alpha_045.py raises ValueError for n < 1 to enforce the lookahead ban — df.shift(0) would return current-bar data and taint the factor with information unavailable at decision time. The guard fires before any pandas work happens.","triggerScenarios":"A _delay call inside alpha #45's compute() receives 0 or a negative int, e.g. `_delay(x, k)` where k is computed from another window and hits 0.","commonSituations":"Transcribing Alpha#45 (which combines rank/correlation with delays) and mis-entering one lag; refactoring the helper's callers; config-driven lag grids including 0.","solutions":["Audit all _delay call sites in alpha_045.py for arguments >= 1","Replace _delay(x, 0) with x where identity is intended","If lags are external, validate them at the boundary with a clear error naming the parameter","Run the factor over synthetic data to confirm the fix"],"exampleFix":"// before\nv = _delay(volume, 0) / _delay(volume, 5)\n// after\nv = volume / _delay(volume, 5)","handlingStrategy":"validation","validationCode":"def clamp_lag(n):\n    return max(1, int(n)) if str(n).strip() not in ('', '0') else 1\n# better: reject explicitly\nif n < 1: raise ValueError(f\"lag must be >= 1, got {n}\")","typeGuard":"def is_valid_delay(n: int) -> bool:\n    return isinstance(n, int) and not isinstance(n, bool) and n >= 1","tryCatchPattern":null,"preventionTips":["Substitute raw frame for delay 0 semantics","Validate externally supplied lags before compute","Keep lag constants under unit-test pinning"],"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"}