{"record":{"id":"c27fb7fc5821afda","repo":"HKUDS/Vibe-Trading","slug":"horizon-must-be-1-got-horizon","errorCode":null,"errorMessage":"horizon must be >= 1, got {horizon}","messagePattern":"horizon must be >= 1, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/risk.py","lineNumber":122,"sourceCode":"\n    Raises:\n        ValueError: If ``confidence`` is not strictly between 0 and 1.\n    \"\"\"\n    if not 0.0 < confidence < 1.0:\n        raise ValueError(f\"confidence must be in (0, 1), got {confidence}\")\n\n\ndef _validate_horizon(horizon: int) -> None:\n    \"\"\"Check that a holding period is a positive whole number of periods.\n\n    Args:\n        horizon: Holding period in periods (days for a daily return series).\n\n    Raises:\n        ValueError: If ``horizon`` is less than 1.\n    \"\"\"\n    if horizon < 1:\n        raise ValueError(f\"horizon must be >= 1, got {horizon}\")\n\n\ndef _tail_index(n: int, confidence: float) -> int:\n    \"\"\"Position of the VaR order statistic in an ascending-sorted sample.\n\n    Args:\n        n: Number of observations, at least 1.\n        confidence: Confidence level in (0, 1).\n\n    Returns:\n        The index ``ceil((1 - confidence) * n) - 1`` clamped into ``[0, n - 1]``.\n\n        This is the textbook lower quantile. The floor form ``floor((1-c)*n)``\n        agrees with it at every confidence level anyone uses -- 0.90, 0.95,\n        0.99 and friends are not exact binary fractions, so ``(1-c)*n`` is never\n        exactly an integer and the two expressions coincide. They diverge only\n        at levels like 0.5, 0.6 and 0.75, where the floor form picks the next\n        observation up and understates the loss. Being right by construction","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/risk.py#L104-L140","documentation":"_validate_horizon requires horizon >= 1 because VaR/CVaR are scaled over a whole number of holding periods (sqrt-time scaling for parametric VaR, overlapping aggregation for historical). Zero or negative horizons have no statistical meaning.","triggerScenarios":"historical_var(r, 0.95, horizon=0), parametric_var(r, 0.95, horizon=-5), or a horizon computed as floor(days/some_day_count) that evaluates to 0 for short periods.","commonSituations":"Computing horizon from user input in days where a bug or off-by-one yields 0; API defaults changed from 'hours' to 'periods' and old callers pass 0.","solutions":["Pass horizon=1 for a single-period VaR","Fix the horizon arithmetic that produced 0/negative (use max(1, computed))","Validate horizon in your config layer before calling"],"exampleFix":"// before\nvar = parametric_var(returns, 0.95, horizon=days // step)  # 0 when days < step\n// after\nvar = parametric_var(returns, 0.95, horizon=max(1, days // step))","handlingStrategy":"validation","validationCode":"assert isinstance(horizon, int) and horizon >= 1, \"horizon must be an int >= 1\"","typeGuard":"def is_valid_horizon(h) -> bool:\n    return isinstance(h, (int, np.integer)) and h >= 1","tryCatchPattern":"try:\n    var = parametric_var(r, 0.95, horizon=h)\nexcept ValueError as e:\n    if \"horizon must be >= 1\" in str(e):\n        var = parametric_var(r, 0.95, horizon=1)\n    else:\n        raise","preventionTips":["Clamp computed horizons with max(1, n)","Treat horizon as whole periods, not fractional days","Validate horizon when parsing user requests"],"tags":["quantlib","risk","validation","horizon","valueerror"],"backgroundTag":"argument-out-of-range","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}