{"record":{"id":"bf1e7d0917f4da02","repo":"HKUDS/Vibe-Trading","slug":"delay-requires-n-1-lookahead-ban-bf1e7d","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_048.py","lineNumber":62,"sourceCode":"    'extras_required': [],\n    'requires_sector': True,\n    'universe': ['equity_us', 'equity_in', 'equity_kr'],\n    'frequency': ['1D'],\n    'decay_horizon': 5,\n    'min_warmup_bars': 251,\n    'notes': \"Industry neutralization implemented via per-row sector group demean (panel['sector'] required). When sector tag is absent the registry rejects via SkipAlpha; the compute() also has a degraded global demean fallback. This is a partial approximation of the paper's IndClass.industry/subindustry/sector neutralization.\",\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 _ind_neutralize(x: pd.DataFrame, panel: dict) -> pd.DataFrame:\n    \"\"\"Industry/sector neutralize: subtract the row-wise sector group mean.\n\n    If panel has a 'sector' DataFrame (same shape as close), subtract the\n    per-sector cross-sectional mean per row. If absent, degrade to global\n    cross-sectional demean (subtract row mean). This is a degraded fallback\n    relative to the paper's industry/subindustry neutralization; see notes.\n    \"\"\"\n    sector_df = panel.get(\"sector\")\n    if sector_df is None:\n        row_mean = x.mean(axis=1, skipna=True)\n        return x.sub(row_mean, axis=0)\n    # Per-row group demean. Iterate rows; numpy-fast enough for small panels.\n    arr = x.to_numpy(dtype=np.float64, na_value=np.nan).copy()\n    sec_arr = sector_df.to_numpy()","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/factors/zoo/alpha101/alpha_048.py#L44-L80","documentation":"alpha_048.py raises this ValueError from _delay when n < 1, upholding the no-lookahead invariant before calling df.shift. The file also neutralizes by industry (_ind_neutralize), but the failure is purely in the lag argument.","triggerScenarios":"compute() for alpha #48 passes a non-positive lag to _delay while assembling the inputs to _ind_neutralize — e.g. `_delay(x, n)` with n == 0 from a mistyped constant.","commonSituations":"Porting Alpha#48 (industry-neutral formulation) and miswriting one lag; refactors that rewrite lag arithmetic; config-driven lags permitting 0.","solutions":["Inspect every _delay call site in alpha_048.py; fix lags < 1","Pass the frame directly when 'today' is meant","Reject lag parameters < 1 at the config/boundary layer","Add a unit test for compute() with a toy panel"],"exampleFix":"// before\nraw = _delay(close, 0) / _delay(close, 2)\n// after\nraw = close / _delay(close, 2)","handlingStrategy":"validation","validationCode":"if lag < 1:\n    raise ValueError(f\"delay lag must be >= 1, got {lag}\")\nneutral = _ind_neutralize(_delay(close, lag), panel)","typeGuard":"def is_valid_delay(n: int) -> bool:\n    return isinstance(n, int) and not isinstance(n, bool) and n >= 1","tryCatchPattern":null,"preventionTips":["Use the raw frame for current-bar access","Validate config lags before invoking the zoo","Smoke-test each alpha after lag edits"],"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"}