{"record":{"id":"22abbf62091f291d","repo":"HKUDS/Vibe-Trading","slug":"decay-linear-window-must-be-1-got-n","errorCode":null,"errorMessage":"decay_linear window must be >= 1, got {n}","messagePattern":"decay_linear window must be >= 1, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/factors/base.py","lineNumber":275,"sourceCode":"\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:\n        if np.isnan(arr).any():\n            return np.nan\n        return float(np.dot(arr, weights))\n\n    arr = df.to_numpy(dtype=np.float64)\n    T, C = arr.shape\n    if T < n:\n        return df.rolling(window=n, min_periods=n).apply(_apply, raw=True)\n\n    windows = sliding_window_view(arr, window_shape=n, axis=0)  # (T-n+1, C, n)\n    nan_mask = np.isnan(windows).any(axis=2)  # (T-n+1, C)\n    weighted = np.where(nan_mask[..., np.newaxis], 0.0, windows)\n    dot = np.einsum(\"ijk,k->ij\", weighted, weights)\n","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/factors/base.py#L257-L293","documentation":"decay_linear computes a linearly weighted moving average (weights n..1 normalized) via sliding_window_view + einsum with causal alignment. Window n must be >= 1; zero/negative windows make the weight vector empty and meaningless.","triggerScenarios":"Calling decay_linear(df, 0) or with a negative window; parameterized alphas with a bad decay window.","commonSituations":"Factor specs with decay: 0 intending 'no smoothing' (should just use raw factor), window sweeps including 0, or computed windows on short panels.","solutions":["Use n >= 1; for 'no decay' use n=1 (weights [1]) or skip the operator","Validate config windows before compute()","Clamp computed windows: max(1, n)"],"exampleFix":"// before\ndecay_linear(df, 0)\n// after\ndecay_linear(df, 1)","handlingStrategy":"validation","validationCode":"if not isinstance(n, int) or n < 1: raise ValueError(f'window must be int >= 1, got {n!r}')","typeGuard":"def is_valid_window(n: object) -> bool:\n    return isinstance(n, int) and not isinstance(n, bool) and n >= 1","tryCatchPattern":null,"preventionTips":["Use n=1 or skip the operator for 'no smoothing'","Clamp generated windows: max(1, n)"],"tags":["decay","rolling-window","validation"],"backgroundTag":"invalid-window-size","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}