HKUDS/Vibe-Trading · error · ValueError

horizon must be >= 1, got {horizon}

Error message

horizon must be >= 1, got {horizon}

What it means

_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.

Source

Thrown at agent/src/quantlib/risk.py:122

    Raises:
        ValueError: If ``confidence`` is not strictly between 0 and 1.
    """
    if not 0.0 < confidence < 1.0:
        raise ValueError(f"confidence must be in (0, 1), got {confidence}")


def _validate_horizon(horizon: int) -> None:
    """Check that a holding period is a positive whole number of periods.

    Args:
        horizon: Holding period in periods (days for a daily return series).

    Raises:
        ValueError: If ``horizon`` is less than 1.
    """
    if horizon < 1:
        raise ValueError(f"horizon must be >= 1, got {horizon}")


def _tail_index(n: int, confidence: float) -> int:
    """Position of the VaR order statistic in an ascending-sorted sample.

    Args:
        n: Number of observations, at least 1.
        confidence: Confidence level in (0, 1).

    Returns:
        The index ``ceil((1 - confidence) * n) - 1`` clamped into ``[0, n - 1]``.

        This is the textbook lower quantile. The floor form ``floor((1-c)*n)``
        agrees with it at every confidence level anyone uses -- 0.90, 0.95,
        0.99 and friends are not exact binary fractions, so ``(1-c)*n`` is never
        exactly an integer and the two expressions coincide. They diverge only
        at levels like 0.5, 0.6 and 0.75, where the floor form picks the next
        observation up and understates the loss. Being right by construction

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Pass horizon=1 for a single-period VaR
  2. Fix the horizon arithmetic that produced 0/negative (use max(1, computed))
  3. Validate horizon in your config layer before calling

Example fix

// before
var = parametric_var(returns, 0.95, horizon=days // step)  # 0 when days < step
// after
var = parametric_var(returns, 0.95, horizon=max(1, days // step))
Defensive patterns

Strategy: validation

Validate before calling

assert isinstance(horizon, int) and horizon >= 1, "horizon must be an int >= 1"

Type guard

def is_valid_horizon(h) -> bool:
    return isinstance(h, (int, np.integer)) and h >= 1

Try / catch

try:
    var = parametric_var(r, 0.95, horizon=h)
except ValueError as e:
    if "horizon must be >= 1" in str(e):
        var = parametric_var(r, 0.95, horizon=1)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/c27fb7fc5821afda. Report an issue: GitHub.