Skip to main content
Pro Feature. Requires a Pro or Ultra subscription. Get started at api.mathematicalcompany.com
A market maker that uses a Hidden Markov Model to detect market regimes and adapts its quoting strategy: tight spreads in calm markets, wide spreads in volatile markets, and no quotes during crises.

Full Code

"""Regime-adaptive market maker using HMM regime detection."""

import horizon as hz
from horizon.context import FeedData


# Markov regime detector: 3 states (calm, volatile, crisis)
regime = hz.markov_regime(
    feed_name="polymarket",
    n_states=3,
    lookback=100,
    labels=["calm", "volatile", "crisis"],
)


def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
    """Adjust spread and size based on detected regime."""
    current_regime = ctx.params.get("regime_label", "calm")
    regime_probs = ctx.params.get("regime_probabilities", [1.0, 0.0, 0.0])

    if current_regime == "calm":
        # Tight spread, large size - capture spread in quiet markets
        spread = 0.02
        size = 15.0
    elif current_regime == "volatile":
        # Wide spread, small size - compensate for adverse selection
        spread = 0.06
        size = 5.0
    else:
        # Crisis: don't quote at all
        print(f"  Crisis regime detected (prob={regime_probs[2]:.2f}), pausing quotes")
        return []

    print(f"  Regime: {current_regime} | probs={[f'{p:.2f}' for p in regime_probs]} | spread={spread}")
    return hz.quotes(fair, spread, size=size)


hz.run(
    name="regime_adaptive_mm",
    markets=["election-winner"],
    feeds={
        "polymarket": hz.PolymarketBook("election-winner"),
    },
    pipeline=[regime, quoter],
    risk=hz.Risk(max_position=100, max_drawdown_pct=5),
    interval=1.0,
    mode="paper",
)

How It Works

  1. markov_regime() fits an HMM to recent price returns from the feed
  2. Each cycle, it classifies the market into one of 3 states and stores the label + probabilities in ctx.params
  3. The quoter reads the regime and adjusts:
  • Calm - tight 2-cent spread, 15 contracts (maximize fill rate)
  • Volatile - wide 6-cent spread, 5 contracts (reduce adverse selection)
  • Crisis - no quotes (preserve capital)
  1. Regime probabilities provide confidence: if the model is 90% sure it’s calm, you can trust the tight spread

Standalone HMM

You can also use the Rust HMM directly for research:
import horizon as hz

# Convert prices to log returns
prices = [0.50, 0.51, 0.49, 0.52, 0.48, 0.53, 0.47, 0.55, 0.45, 0.50]
returns = hz.prices_to_returns(prices)

# Fit a 3-state HMM
model = hz.MarkovRegimeModel(n_states=3)
model.fit(returns, max_iter=100)

# Predict current regime
state = model.predict(returns)
probs = model.state_probabilities(returns)

print(f"Current state: {state}")
print(f"State probabilities: {[f'{p:.3f}' for p in probs]}")
print(f"Transition matrix:")
for row in model.transition_matrix():
    print(f"  {[f'{x:.3f}' for x in row]}")

Run It

python examples/regime_adaptive_strategy.py

Extending

Combine regime detection with inventory skew for a more robust strategy:
hz.run(
    name="regime_skew_mm",
    markets=["btc-100k"],
    feeds={
        "polymarket": hz.PolymarketBook("btc-100k"),
    },
    pipeline=[
        regime,
        hz.inventory_skewer(gamma=0.15),
        quoter,
    ],
    risk=hz.Risk(max_position=100, max_drawdown_pct=5),
    interval=1.0,
    mode="paper",
)
See Markov Regime Detection for the full HMM reference.