HKUDS/Vibe-Trading · error · ValueError

find_hedge_ratio needs y and x sharing one index

Error message

find_hedge_ratio needs y and x sharing one index

What it means

find_hedge_ratio aligns y and x positionally via pd.concat on their indices; if the two inputs do not share the same index, concat produces a longer union frame and the length check fails. This guard prevents silently regressing misaligned observations.

Source

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

    Returns:
        Dict with keys ``hedge_ratio`` (β, float), ``intercept`` (α, float),
        ``spread_mean`` (float), ``spread_std`` (float, sample ddof=1) and
        ``half_life`` (float, in observation periods).

    Raises:
        ImportError: If ``statsmodels`` is not installed.
        ValueError: If the series differ in length, carry different indices,
            fewer than 3 aligned non-NaN observations remain, or ``x`` is
            constant. A flat hedging leg is refused rather than fitted:
            ``sm.add_constant`` leaves an already-constant column alone, so the
            design would silently collapse to one column and the β lookup would
            be a bare ``IndexError``.
    """
    sm = _require("statsmodels.api", "statsmodels", "find_hedge_ratio")
    frame = pd.concat({"y": pd.Series(y, dtype=float), "x": pd.Series(x, dtype=float)}, axis=1)
    if len(frame) != len(pd.Series(y)) or len(frame) != len(pd.Series(x)):
        raise ValueError("find_hedge_ratio needs y and x sharing one index")
    frame = frame.dropna()
    if len(frame) < 3:
        raise ValueError(f"find_hedge_ratio needs at least 3 aligned observations, got {len(frame)}")
    if frame["x"].std(ddof=0) == 0:
        raise ValueError("find_hedge_ratio needs an x that varies; this one is constant")

    params = _ols_params(frame["y"], sm.add_constant(frame[["x"]]))
    intercept, beta = float(params[0]), float(params[1])
    spread = frame["y"] - beta * frame["x"]

    return {
        "hedge_ratio": beta,
        "intercept": intercept,
        "spread_mean": float(spread.mean()),
        "spread_std": float(spread.std()),
        "half_life": compute_half_life(spread),
    }

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Align both inputs on a common index before calling: y, x = y.align(x, join='inner')
  2. Reset both to positional: pass .to_numpy() for both y and x
  3. Verify with (y.index == x.index).all() before the call

Example fix

# before
ratio = find_hedge_ratio(prices_a, prices_b)  # different calendars
# after
common = prices_a.index.intersection(prices_b.index)
ratio = find_hedge_ratio(prices_a.loc[common], prices_b.loc[common])
Defensive patterns

Strategy: validation

Validate before calling

if isinstance(y, pd.Series) and isinstance(x, pd.Series):
    assert y.index.equals(x.index), 'y and x must share one index'
else:
    y = pd.Series(y, dtype=float)
    x = pd.Series(x, dtype=float)

Type guard

def share_index(y, x) -> bool:
    if not (isinstance(y, pd.Series) and isinstance(x, pd.Series)):
        return True  # positional
    return y.index.equals(x.index)

Try / catch

try:
    find_hedge_ratio(y, x)
except ValueError as e:
    if 'sharing one index' in str(e):
        y, x = y.align(x, join='inner')
        return find_hedge_ratio(y, x)
    raise

Prevention

When it happens

Trigger: Passing two pd.Series with different DatetimeIndexes (different dates or ranges), overlapping-but-not-identical indices, or mixing a Series with a plain list/array whose default RangeIndex differs from the other's index.

Common situations: Loading two price series from different sources with different trading calendars, reindexing one series but not the other, or passing y as a Series and x as a numpy array.

Related errors


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