{"record":{"id":"c2407ce4b9fe3cc5","repo":"HKUDS/Vibe-Trading","slug":"delta-lag-must-be-1-lookahead-ban-got-d","errorCode":null,"errorMessage":"delta lag must be >= 1 (lookahead ban), got {d}","messagePattern":"delta lag must be >= 1 \\(lookahead ban\\), got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/factors/base.py","lineNumber":261,"sourceCode":"    so we convert via ``(n - 1) - bn_result`` to get 0-based index from start.\n    \"\"\"\n    if n < 1:\n        raise ValueError(f\"ts_argmin window must be >= 1, got {n}\")\n    if HAS_BOTTLENECK:\n        arr = df.to_numpy(dtype=np.float64)\n        raw = bn.move_argmin(arr, window=n, min_count=n, axis=0)\n        corrected = (n - 1) - raw\n        return pd.DataFrame(corrected, index=df.index, columns=df.columns)\n    return df.rolling(window=n, min_periods=n).apply(_argmin_last, raw=True)\n\n\ndef delta(df: pd.DataFrame, d: int) -> pd.DataFrame:\n    \"\"\"First difference at lag ``d``: ``df - df.shift(d)``.\n\n    Lookahead ban: ``d >= 1`` strictly. Negative lag forbidden.\n    \"\"\"\n    if d < 1:\n        raise ValueError(f\"delta lag must be >= 1 (lookahead ban), got {d}\")\n    return df - df.shift(d)\n\n\ndef decay_linear(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Linear decay-weighted moving average, weights ``n, n-1, ..., 1`` normalized.\n\n    Warmup (first ``n-1`` rows) → NaN.\n\n    Uses numpy ``sliding_window_view`` + ``einsum`` for vectorized computation\n    (~40x faster than pandas rolling().apply()). Causal alignment is guaranteed:\n    output[i] depends only on input[i-n+1:i+1].\n    \"\"\"\n    if n < 1:\n        raise ValueError(f\"decay_linear window must be >= 1, got {n}\")\n    weights = np.arange(n, 0, -1, dtype=np.float64)\n    weights /= weights.sum()\n\n    def _apply(arr: np.ndarray) -> float:","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/factors/base.py#L243-L279","documentation":"delta computes df - df.shift(d), the first difference at lag d. To enforce the lookahead ban (no negative shifts and no d=0 which is a no-op/identity), it requires d >= 1 strictly.","triggerScenarios":"Calling delta(df, 0), delta(df, -1), or passing d=0 expecting current values.","commonSituations":"Translating 'change from today' semantics where 0 feels natural, off-by-one from other libraries where lag 0 is allowed, or user configs with lag: 0.","solutions":["Use d >= 1; for yesterday-vs-today diff use d=1","If you truly want d=0 identity, skip delta and use df directly","Validate lag parameters in config parsing"],"exampleFix":"// before\ndelta(df, 0)\n// after\ndelta(df, 1)","handlingStrategy":"validation","validationCode":"if not isinstance(d, int) or d < 1: raise ValueError(f'lag must be int >= 1, got {d!r}')","typeGuard":"def is_valid_lag(d: object) -> bool:\n    return isinstance(d, int) and not isinstance(d, bool) and d >= 1","tryCatchPattern":null,"preventionTips":["Treat d=0 as a design smell: it means you want raw values, not a diff","Enforce the lookahead ban in your own wrapper too"],"tags":["lookahead-ban","lag","validation"],"backgroundTag":"invalid-lag-parameter","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}