{"record":{"id":"367e2d97e8338bff","repo":"HKUDS/Vibe-Trading","slug":"ts-argmax-window-must-be-1-got-n","errorCode":null,"errorMessage":"ts_argmax window must be >= 1, got {n}","messagePattern":"ts_argmax window must be >= 1, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/factors/base.py","lineNumber":229,"sourceCode":"    return float(np.argmax(arr_filled))\n\n\ndef _argmin_last(arr: np.ndarray) -> float:\n    if np.isnan(arr).all():\n        return np.nan\n    arr_filled = np.where(np.isnan(arr), np.inf, arr)\n    return float(np.argmin(arr_filled))\n\n\ndef ts_argmax(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Rolling argmax (0-based index into the window), warmup → NaN.\n\n    Uses ``bottleneck.move_argmax`` when available (~350x faster).\n    Correction: ``bn.move_argmax`` returns distance from window end,\n    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_argmax window must be >= 1, got {n}\")\n    if HAS_BOTTLENECK:\n        arr = df.to_numpy(dtype=np.float64)\n        raw = bn.move_argmax(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(_argmax_last, raw=True)\n\n\ndef ts_argmin(df: pd.DataFrame, n: int) -> pd.DataFrame:\n    \"\"\"Rolling argmin (0-based index into the window), warmup → NaN.\n\n    Uses ``bottleneck.move_argmin`` when available (~350x faster).\n    Correction: ``bn.move_argmin`` returns distance from window end,\n    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:","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/factors/base.py#L211-L247","documentation":"ts_argmax returns the 0-based index of the max within each rolling window (bottleneck-accelerated with a distance-from-end correction). It requires window n >= 1; both bn.move_argmax and pandas rolling would otherwise fail or produce nonsense.","triggerScenarios":"Calling ts_argmax(df, 0), ts_argmax(df, -2), or benchmarking (bench_operators) with a window list containing 0.","commonSituations":"Benchmark scripts iterating windows like [0, 5, 10], user-supplied factor parameters, or off-by-one when converting from a 'lookback' convention where 0 means 'today only' (should be 1 here).","solutions":["Pass n >= 1","Map 'current value only' semantics to n=1, not 0","Sanitize window lists in bench configs"],"exampleFix":"// before\nts_argmax(df, 0)\n// after\nts_argmax(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":["Filter window lists in benchmark configs: [w for w in windows if w >= 1]","Map 'current bar only' semantics to n=1"],"tags":["rolling-window","argmax","bottleneck"],"backgroundTag":"invalid-window-size","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}