HKUDS/Vibe-Trading · error · KeyError

granger_test: column(s) not in data: {missing}

Error message

granger_test: column(s) not in data: {missing}

What it means

granger_test raises KeyError listing the requested columns missing from the input DataFrame, because statsmodels would fail cryptically when selecting data[[y_col, x_col]] with unknown names.

Source

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

    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:
        returns: Daily return series as *fractions* (0.01 = 1%). Scaled to
            percent internally, which is what ``arch`` optimises well on.
        horizon: Number of days ahead to forecast.

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Print/inspect data.columns and fix the name
  2. Normalize column names to lowercase/snake_case before calling
  3. Ensure upstream steps that create derived columns (returns, spreads) executed

Example fix

# before
res = granger_test(df, x_col='Close', y_col='Volume')  # actual: 'close','volume'
# after
res = granger_test(df.rename(columns=str.lower), x_col='close', y_col='volume')
Defensive patterns

Strategy: validation

Validate before calling

missing = [c for c in (y_col, x_col) if c not in data.columns]
if missing:
    raise KeyError(f'missing columns: {missing}')

Type guard

def columns_present(data: pd.DataFrame, *cols: str) -> bool:
    return all(c in data.columns for c in cols)

Try / catch

try:
    granger_test(data, y_col, x_col)
except KeyError as e:
    # re-raise with available columns for debugging
    raise KeyError(f'{e}; available: {list(data.columns)}') from e

Prevention

When it happens

Trigger: Calling granger_test with a column name not in data.columns — typos, differing naming conventions ('Close' vs 'close'), or DataFrame built without the expected columns.

Common situations: Case-mismatched column names, DataFrames from CSVs with renamed headers, or pipelines where the feature-engineering step that creates the column (e.g. returns) never ran.

Related errors


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