HKUDS/Vibe-Trading · error · ValueError

fit_ornstein_uhlenbeck needs a series that varies; this one…

Error message

fit_ornstein_uhlenbeck needs a series that varies; this one is constant

What it means

fit_ornstein_uhlenbeck rejects input series whose lagged values have zero (ddof=0) standard deviation, i.e. a constant series. Mean-reversion estimation via OLS on lag pairs is degenerate when the regressor never varies, so the library raises instead of returning meaningless parameters.

Solutions

  1. Check the series varies before calling: s.std(ddof=0) > 0
  2. Inspect upstream data ingestion for a stuck feed or repeated fill-forward values
  3. If constant data is legitimate, skip the OU fit or handle it as zero-volatility case

Example fix

# before
res = fit_ornstein_uhlenbeck(pd.Series([5.0] * 100))
# after
if s.std(ddof=0) == 0:
    raise ValueError("series is constant; cannot fit OU")
res = fit_ornstein_uhlenbeck(s)
Defensive patterns

Strategy: validation

Validate before calling

s = pd.Series(data, dtype=float).dropna()
if len(s) < 4 or s.shift(1).std(ddof=0) == 0:
    raise ValueError('series must vary and have >= 3 lag pairs')

Type guard

def is_fittable_ou(s: pd.Series) -> bool:
    s = s.dropna()
    return len(s) >= 4 and s.shift(1).dropna().std(ddof=0) > 0

Try / catch

try:
    fit_ornstein_uhlenbeck(s)
except ValueError as e:
    if 'series that varies' in str(e):
        logger.warning('constant series; skipping OU fit: %s', e)
    else:
        raise

Prevention

When it happens

Trigger: Calling fit_ornstein_uhlenbeck with a series of identical values (e.g. np.full(100, 42.0)), or data that becomes constant after dropna of the lag frame (first N-1 values equal).

Common situations: Feeding prices from illiquid assets pinned at a fix, dummy/placeholder data in tests, or a mis-scaled series that rounds to a single value.

Related errors


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

Appendix: source

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

    Raises:
        ImportError: If ``statsmodels`` is not installed.
        ValueError: If ``dt <= 0``, fewer than 3 valid lag pairs remain, or series is constant.
    """
    if dt <= 0:
        raise ValueError(f"dt must be strictly positive, got {dt}")

    sm = _require("statsmodels.api", "statsmodels", "fit_ornstein_uhlenbeck")
    s = pd.Series(series, dtype=float).dropna()
    if not np.isfinite(s.values).all():
        raise ValueError("series contains non-finite values")

    lagged = s.shift(1)
    frame = pd.concat({"curr": s, "lag": lagged}, axis=1).dropna()
    if len(frame) < 3:
        raise ValueError(f"fit_ornstein_uhlenbeck needs at least 3 lag pairs, got {len(frame)}")
    if frame["lag"].std(ddof=0) == 0:
        raise ValueError("fit_ornstein_uhlenbeck needs a series that varies; this one is constant")

    exog = sm.add_constant(frame[["lag"]])
    params = _ols_params(frame["curr"], exog)
    a = float(params[0])
    b = float(params[1])

    residuals = frame["curr"] - (a + b * frame["lag"])
    n = len(frame)
    dof = max(1, n - 2)
    sigma_eps_sq = float(np.sum(residuals**2) / dof)
    sigma_eps = float(np.sqrt(sigma_eps_sq))

    if 0.0 < b < 1.0:
        theta = float(-np.log(b) / dt)
        mu = float(a / (1.0 - b))
        half_life = float(np.log(2.0) / theta)
        one_minus_b2 = float(1.0 - b**2)
        stat_var = float(sigma_eps_sq / one_minus_b2)

View on GitHub (pinned to 80ffdda44c)