{"record":{"id":"33881a018e8ff7be","repo":"HKUDS/Vibe-Trading","slug":"ts-std-window-must-be-2-got-n","errorCode":null,"errorMessage":"ts_std window must be >= 2, got {n}","messagePattern":"ts_std window must be >= 2, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/factors/base.py","lineNumber":189,"sourceCode":"    y = _as_float(y)\n    cols = x.columns.union(y.columns)\n    xa = x.reindex(columns=cols)\n    ya = y.reindex(columns=cols)\n    cov = xa.rolling(window=n, min_periods=n).cov(ya)\n    return cov.replace([np.inf, -np.inf], np.nan)\n\n\ndef ts_mean(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Rolling mean per column, warmup → NaN.\"\"\"\n    if n < 1:\n        raise ValueError(f\"ts_mean window must be >= 1, got {n}\")\n    return df.rolling(window=n, min_periods=n).mean()\n\n\ndef ts_std(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Rolling sample std (ddof=1) per column, warmup → NaN.\"\"\"\n    if n < 2:\n        raise ValueError(f\"ts_std window must be >= 2, got {n}\")\n    return df.rolling(window=n, min_periods=n).std(ddof=1)\n\n\ndef ts_max(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Rolling max per column, warmup → NaN.\"\"\"\n    if n < 1:\n        raise ValueError(f\"ts_max window must be >= 1, got {n}\")\n    return df.rolling(window=n, min_periods=n).max()\n\n\ndef ts_min(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Rolling min per column, warmup → NaN.\"\"\"\n    if n < 1:\n        raise ValueError(f\"ts_min window must be >= 1, got {n}\")\n    return df.rolling(window=n, min_periods=n).min()\n\n\ndef _argmax_last(arr: np.ndarray) -> float:","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/factors/base.py#L171-L207","documentation":"ts_std requires n >= 2 because it computes a sample standard deviation with ddof=1, which is undefined for a single observation; n < 2 raises. Warmup rows are NaN per min_periods=n.","triggerScenarios":"ts_std(df, 1), ts_std(df, 0), or negative n — often a window copied from ts_mean/ts_rank (which allow 1) without adjusting for the stricter minimum. Called from compute() and tests.","commonSituations":"Refactoring a pipeline from ts_mean to ts_std without changing n; short series with auto-scaled windows; config defaults of 1 used across all rolling operators.","solutions":["Pass n >= 2 (need at least 2 points for sample std)","If ddof=0 semantics with n=1 are acceptable, compute df.rolling(n).std(ddof=0) yourself instead of ts_std","Validate each operator's minimum window in config validation"],"exampleFix":"# before\ns = ts_std(df, n=1)\n\n# after\ns = ts_std(df, n=20)","handlingStrategy":"validation","validationCode":"if not isinstance(n, int) or n < 2:\n    raise ValueError(f'invalid ts_std window: {n!r} (sample std needs >= 2)')\ns = ts_std(df, n)","typeGuard":"def is_valid_ts_std_window(n) -> bool:\n    return isinstance(n, int) and n >= 2","tryCatchPattern":null,"preventionTips":["ddof=1 sample std is undefined at n=1 — do not reuse ts_mean's minimum","If n=1 must be supported, use df.rolling(1).std(ddof=0) explicitly","Keep a per-operator minimum-window table for config validation"],"tags":["python","factors","rolling-window","standard-deviation","parameter-validation"],"backgroundTag":"rolling-window-invalid","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}