HKUDS/Vibe-Trading · error · ValueError

violations must be in [0, {observations}], got {violations}

Error message

violations must be in [0, {observations}], got {violations}

What it means

kupiec_pof requires the breach count to satisfy 0 <= violations <= observations; anything outside — negative counts or more breaches than days — raises ValueError, since the binomial likelihood behind the POF statistic is undefined there.

Source

Thrown at agent/src/quantlib/var_backtest.py:364

    Args:
        violations: Number of days the loss exceeded VaR.
        observations: Number of days tested.
        confidence: VaR confidence level the model claims, e.g. 0.99.
        significance: Level at which ``rejected`` is decided.

    Returns:
        A :class:`KupiecResult`.

    Raises:
        ValueError: If ``observations`` is not positive, if ``violations`` is
            negative or exceeds ``observations``, or if either probability is
            not strictly between 0 and 1.
    """
    if observations <= 0:
        raise ValueError(f"observations must be > 0, got {observations}")
    if not 0 <= violations <= observations:
        raise ValueError(
            f"violations must be in [0, {observations}], got {violations}"
        )
    if not 0.0 < confidence < 1.0:
        raise ValueError(f"confidence must be in (0, 1), got {confidence}")
    if not 0.0 < significance < 1.0:
        raise ValueError(f"significance must be in (0, 1), got {significance}")

    expected_rate = 1.0 - confidence
    observed_rate = violations / observations
    calm = observations - violations

    restricted = xlogy(calm, 1.0 - expected_rate) + xlogy(violations, expected_rate)
    unrestricted = xlogy(calm, 1.0 - observed_rate) + xlogy(violations, observed_rate)
    statistic = float(max(-2.0 * (restricted - unrestricted), 0.0))
    p_value = float(chi2.sf(statistic, df=1))

    return KupiecResult(
        observations=observations,

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Recompute violations from the same aligned sample used for observations: v = int((ret < -var_np).sum()); n = len(ret).
  2. Ensure both numbers describe the identical window of data.
  3. Clamp/validate counts before the call if derived from user input.

Example fix

# before
kupiec_pof(observations=200, violations=250, ...)
# after
viol = violation_indicator(ret, var)  # aligned
kupiec_pof(observations=len(viol), violations=int(viol.sum()), ...)
Defensive patterns

Strategy: validation

Validate before calling

assert 0 <= violations <= observations

Type guard

def valid_counts(n: int, v: int) -> bool:
    return 0 <= v <= n

Try / catch

except ValueError as e:
    if 'violations must be in' in str(e): recompute counts from aligned sample

Prevention

When it happens

Trigger: Passing violations=-3, or violations=250 with observations=200; commonly from double-counting a boolean array (sum of ints plus len), or subtracting counts and going negative.

Common situations: Computing violations as (rets < -var).sum() over a misaligned longer series than observations; mixing units (percents vs counts); bugs where violations is passed an array length instead of a count.

Related errors


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