HKUDS/Vibe-Trading · error · ValueError

observations must be > 0, got {observations}

Error message

observations must be > 0, got {observations}

What it means

kupiec_pof (Kupiec proportion-of-failures unconditional coverage test) requires a positive observation count; observations <= 0 raises ValueError. Zero or negative samples make the likelihood-ratio statistic undefined (division and log-likelihoods per observation).

Source

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

    convention makes the zero-breach and all-breach cases well defined rather
    than special.

    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))

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Verify observations comes from the length of the aligned, finite sample (len(violations), not 0).
  2. Check upstream for empty/NaN-filtered inputs producing zero observations.
  3. Confirm argument order matches (observations, violations, confidence, significance).

Example fix

# before
kupiec_pof(0, 0, confidence=0.99, significance=0.05)
# after
assert len(violations) > 0
kupiec_pof(len(violations), int(violations.sum()), 0.99, 0.05)
Defensive patterns

Strategy: validation

Validate before calling

assert observations > 0, observations

Type guard

def positive_int(n) -> bool:
    return isinstance(n, int) and n > 0

Try / catch

except ValueError as e:
    if 'observations must be > 0' in str(e): raise DataError('empty backtest sample') from e

Prevention

When it happens

Trigger: Calling kupiec_pof(observations=0, ...) — often because violations/observations were extracted from an empty violation indicator array via sum() and len() on empty data.

Common situations: Wrapping var_backtest outputs where the finite-pair filter removed everything; counters computed from empty DataFrames; passing counts in the wrong argument order so a small violations number lands in observations.

Related errors


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