HKUDS/Vibe-Trading · error · ValueError

parametric_var needs at least 2 observations for a std estim

Error message

parametric_var needs at least 2 observations for a std estimate

What it means

parametric_var fits a normal distribution to the sample, which requires a sample standard deviation (ddof=1). With fewer than 2 finite observations the std is undefined, so the function refuses rather than dividing by zero.

Source

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

    Args:
        returns: Periodic (usually daily) return series. The mean and the
            sample standard deviation (ddof=1) are estimated from it.
        confidence: Confidence level, commonly 0.95 or 0.99.
        horizon: Holding period in periods, scaled by square-root-of-time.

    Returns:
        The VaR as a positive loss magnitude: ``-(mu + z * sigma) * sqrt(horizon)``
        where ``z = norm.ppf(1 - confidence)``.

    Raises:
        ValueError: If ``returns`` holds fewer than two finite observations,
            ``confidence`` is outside (0, 1), or ``horizon`` is below 1.
    """
    _validate_confidence(confidence)
    _validate_horizon(horizon)
    values = _clean_returns(returns)
    if values.size < 2:
        raise ValueError("parametric_var needs at least 2 observations for a std estimate")
    mu = float(values.mean())
    sigma = float(values.std(ddof=1))
    z = float(norm.ppf(1.0 - confidence))
    return float(-(mu + z * sigma) * np.sqrt(horizon))


def historical_cvar(
    returns: pd.Series | np.ndarray | Sequence[float],
    confidence: float = 0.95,
    horizon: int = 1,
) -> float:
    """Conditional VaR (expected shortfall) from the empirical distribution.

    The average loss *given* that the VaR threshold was breached. Unlike VaR it
    is subadditive, so it can be decomposed across a portfolio, which is why
    Basel III moved to it.

    Args:

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Use historical_var instead if you must handle tiny samples (it works with 1 point, though the estimate is crude)
  2. Ensure the rolling window is at least 2 (preferably far more) before calling
  3. Skip the metric when values.size < 2 and report insufficient data

Example fix

// before
var = parametric_var(window_returns, 0.95)  # window of 1
// after
var = (parametric_var(window_returns, 0.95) if len(window_returns) >= 2 else historical_var(window_returns, 0.95))
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
finite = np.asarray(returns, dtype=float)
finite = finite[np.isfinite(finite)]
if finite.size >= 2:
    var = parametric_var(finite, 0.95)
else:
    var = historical_var(finite, 0.95) if finite.size else float("nan")

Type guard

def enough_for_parametric(r) -> bool:
    import numpy as np
    return int(np.isfinite(np.asarray(r, dtype=float)).sum()) >= 2

Try / catch

try:
    var = parametric_var(window, 0.95)
except ValueError as e:
    if "at least 2 observations" in str(e):
        var = historical_var(window, 0.95)
    else:
        raise

Prevention

When it happens

Trigger: parametric_var([0.01], 0.95) with a single return, or a two-row price series whose diff yields one NaN dropped by _clean_returns leaving one point.

Common situations: Backtesting loops where a rolling window is shorter than expected; newly started strategies with one day of returns; filtered datasets that shrink to a single row.

Related errors


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