HKUDS/Vibe-Trading · error · ValueError

n_regimes must be at least 2, got {n_regimes}

Error message

n_regimes must be at least 2, got {n_regimes}

What it means

fit_markov_regime requires n_regimes >= 2; a Markov-switching model with one regime is just a constant-variance model and statsmodels' MarkovRegression cannot fit it, so the library rejects it early.

Source

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

        ``expected_durations`` (numpy array, ``1 / (1 - p_ii)`` in periods),
        ``smoothed_probabilities`` (DataFrame on the input index, one column per
        regime), ``current_regime`` (int) and ``current_regime_probability``
        (float) for the last observation, ``converged`` (bool), and ``llf``,
        ``aic``, ``bic`` (float).

    Raises:
        ImportError: If ``statsmodels`` is not installed.
        ValueError: If ``n_regimes`` is below 2, or fewer than 50 finite
            observations survive -- an EM fit on a shorter series produces
            regimes that are numerically fine and substantively meaningless.
    """
    markov = _require(
        "statsmodels.tsa.regime_switching.markov_regression",
        "statsmodels",
        "fit_markov_regime",
    )
    if n_regimes < 2:
        raise ValueError(f"n_regimes must be at least 2, got {n_regimes}")

    series = pd.Series(returns, dtype=float).dropna()
    if series.size < 50:
        raise ValueError(
            f"fit_markov_regime needs at least 50 finite observations, got {series.size}"
        )

    # Passed as a Series, not an array: statsmodels only names the fitted
    # parameters (``const[k]``, ``sigma2[k]``) when the input is pandas, and
    # positional unpacking of that vector would silently break if the package
    # ever reorders it.
    model = markov.MarkovRegression(
        series * 100,
        k_regimes=n_regimes,
        trend="c",
        switching_variance=switching_variance,
    )
    with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Use n_regimes >= 2 (2 or 3 is typical for calm/turbulent markets)
  2. Start model-selection loops at 2
  3. Validate config: assert n_regimes >= 2

Example fix

# before
for k in range(1, 5):
    res = fit_markov_regime(returns, n_regimes=k)
# after
for k in range(2, 5):
    res = fit_markov_regime(returns, n_regimes=k)
Defensive patterns

Strategy: validation

Validate before calling

assert n_regimes >= 2, 'Markov regime models need at least 2 regimes'

Type guard

def valid_regime_count(k: int) -> bool:
    return isinstance(k, int) and k >= 2

Try / catch

try:
    fit_markov_regime(returns, n_regimes=k)
except ValueError as e:
    if 'at least 2' in str(e):
        continue  # skip k=1 in selection loops
    raise

Prevention

When it happens

Trigger: Calling fit_markov_regime(returns, n_regimes=1) or 0, often from a loop over [1,2,3] regime counts or a config value of 1.

Common situations: Model-selection loops that start at 1 regime, config typos, or BIC-driven selection that picks 1 before the guard.

Related errors


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