HKUDS/Vibe-Trading · error · ValueError

granger_test needs max_lag >= 1, got {max_lag}

Error message

granger_test needs max_lag >= 1, got {max_lag}

What it means

granger_test requires max_lag >= 1; a max_lag of 0 or negative means no lags to test, which statsmodels' grangercausalitytests cannot handle meaningfully.

Source

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

        Dict mapping lag (int, 1..``max_lag``) to the SSR F-test p-value (float).
        A small p-value rejects "x does not Granger-cause y".

    Raises:
        ImportError: If ``statsmodels`` is not installed.
        KeyError: If either column is missing from ``data``.
        ValueError: If ``max_lag`` is below 1, or if ``x_col`` and ``y_col`` are
            the same column. Testing a series against itself hands statsmodels a
            duplicated column, where the F-test trivially fails to reject and
            every p-value comes back 1.0 -- an answer that looks like a finding.
    """
    if x_col == y_col:
        raise ValueError(
            f"x_col and y_col must differ; both are {x_col!r}. A series cannot "
            "Granger-cause itself and the test returns p=1.0 regardless."
        )
    stattools = _require("statsmodels.tsa.stattools", "statsmodels", "granger_test")
    if max_lag < 1:
        raise ValueError(f"granger_test needs max_lag >= 1, got {max_lag}")
    missing = [c for c in (y_col, x_col) if c not in data.columns]
    if missing:
        raise KeyError(f"granger_test: column(s) not in data: {missing}")

    # statsmodels >= 0.14 dropped the `verbose` kwarg and prints the full test
    # table to stdout unconditionally; swallow it so a library call stays quiet.
    with contextlib.redirect_stdout(io.StringIO()):
        results = stattools.grangercausalitytests(data[[y_col, x_col]].dropna(), maxlag=max_lag)
    return {lag: float(results[lag][0]["ssr_ftest"][1]) for lag in range(1, max_lag + 1)}


def fit_garch(returns: pd.Series, horizon: int = 5) -> dict:
    """Fit a GARCH(1,1) model and forecast forward volatility.

    Model: ``r_t = μ + ε_t`` with ``σ²_t = ω + α·ε²_{t-1} + β·σ²_{t-1}``.
    ``α + β`` is volatility persistence (typically 0.95-0.99 in equities).

    Args:

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Clamp the computed lag: max_lag = max(1, computed_lag)
  2. Validate config values before the call
  3. Choose a conventional lag such as 4 (quarterly) or 12 for daily data

Example fix

# before
res = granger_test(df, 'y', 'x', max_lag=computed_lag)
# after
res = granger_test(df, 'y', 'x', max_lag=max(1, computed_lag))
Defensive patterns

Strategy: validation

Validate before calling

max_lag = max(1, int(max_lag))
assert max_lag >= 1

Type guard

def valid_lag(max_lag: int) -> bool:
    return isinstance(max_lag, int) and max_lag >= 1

Try / catch

try:
    granger_test(data, y_col, x_col, max_lag=max_lag)
except ValueError as e:
    if 'max_lag >= 1' in str(e):
        return granger_test(data, y_col, x_col, max_lag=1)
    raise

Prevention

When it happens

Trigger: Calling granger_test(..., max_lag=0) or a negative value, often from a computed lag order (e.g. int(len(data) ** 0.3) on a tiny frame) or a config default of 0.

Common situations: Auto-tuning lag order that underflows to 0 on short series, config files with lag: 0, or arithmetic on user-supplied parameters.

Related errors


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