HKUDS/Vibe-Trading · error · ValueError

lags must be < the number of observations; got lags={lags} f

Error message

lags must be < the number of observations; got lags={lags} for {clean.size} observations

What it means

After dropping NaNs, autocorrelation_test requires lags to be strictly less than the number of remaining observations. statsmodels' acorr_ljungbox silently caps lags and then dies on a numpy shape mismatch that names neither argument, so this guard gives an actionable message up front.

Source

Thrown at agent/src/quantlib/timeseries.py:699

        Dict with keys ``durbin_watson`` (float), ``dw_interpretation`` (str,
        one of ``'positive autocorrelation'`` / ``'no autocorrelation'`` /
        ``'negative autocorrelation'``), ``ljung_box_p`` (numpy array of
        p-values, one per lag), ``has_autocorrelation`` (bool) and ``fix`` (str).

    Raises:
        ImportError: If ``statsmodels`` is not installed.
        ValueError: If ``lags`` is below 1.
    """
    diagnostic = _require("statsmodels.stats.diagnostic", "statsmodels", "autocorrelation_test")
    stattools = _require("statsmodels.stats.stattools", "statsmodels", "autocorrelation_test")
    if lags < 1:
        raise ValueError(f"autocorrelation_test needs lags >= 1, got {lags}")

    clean = pd.Series(residuals, dtype=float).dropna()
    if lags >= clean.size:
        # acorr_ljungbox silently caps lags and then dies on a shape mismatch
        # with a numpy message that names neither argument.
        raise ValueError(
            f"lags must be < the number of observations; got lags={lags} "
            f"for {clean.size} observations"
        )
    dw = float(stattools.durbin_watson(clean))
    lb_p = np.asarray(diagnostic.acorr_ljungbox(clean, lags=lags)["lb_pvalue"].values, dtype=float)

    if dw < _DW_POSITIVE_BELOW:
        interpretation = "positive autocorrelation"
    elif dw < _DW_NEGATIVE_ABOVE:
        interpretation = "no autocorrelation"
    else:
        interpretation = "negative autocorrelation"

    return {
        "durbin_watson": dw,
        "dw_interpretation": interpretation,
        "ljung_box_p": lb_p,
        "has_autocorrelation": bool(np.any(lb_p < significance)),

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Reduce lags below the number of non-NaN observations, e.g. lags < clean.size.
  2. Derive lags from the actual cleaned length: lags = min(desired_lags, clean_size - 1).
  3. Check residuals length and NaN count before calling.

Example fix

// before
autocorrelation_test(residuals, lags=40)  # residuals has 40 obs -> error
// after
clean_n = pd.Series(residuals).dropna().size
autocorrelation_test(residuals, lags=min(40, clean_n - 1))
Defensive patterns

Strategy: validation

Validate before calling

clean_n = pd.Series(residuals).dropna().size
lags = max(1, min(desired_lags, clean_n - 1))
autocorrelation_test(residuals, lags=lags)

Try / catch

try:
    result = autocorrelation_test(residuals, lags=lags)
except ValueError as e:
    if "observations" in str(e):
        lags = max(1, clean_n - 1)
        result = autocorrelation_test(residuals, lags=lags)
    else:
        raise

Prevention

When it happens

Trigger: autocorrelation_test(residuals, lags=50) on a 40-row series, or a series whose NaN-dropped size (clean.size) falls to <= lags; passing lags == n is also rejected.

Common situations: Running diagnostics on short backtest windows, small samples after dropna, or reusing a lag count tuned on a longer dataset.

Related errors


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