← Back to Blog
April 5, 2026programming

Market Regime Classification in Crypto: Trending, Sideways, Downtrend

By APIndicators

Every experienced trader has had this exact experience: a strategy that crushed last month suddenly starts bleeding. The logic did not change. The parameters did not drift. What changed is the market regime. Strategies have regimes they thrive in, and regimes where they systematically lose. The single biggest lever retail quant traders ignore is regime classification before strategy selection.

This post walks through the three regimes that matter for crypto (uptrend, downtrend, sideways), the features that separate them, and how APIndicators exposes regime classification via the /v1/pairs/{symbol}/market-type endpoint.

The Three Regimes That Actually Matter

You can overcomplicate regime classification. Hidden Markov models, Gaussian mixtures, structural break detection. For 95% of retail algo traders, three regimes are enough:

  • Uptrend: price is climbing, ADX is elevated, EMAs are stacked bullish, pullbacks get bought.
  • Downtrend: price is falling, ADX elevated, EMAs stacked bearish, rallies get sold.
  • Sideways: price oscillates in a range, ADX is low, mean reversion works, momentum dies.

Different strategies win in different regimes:

| Strategy | Uptrend | Downtrend | Sideways | |----------|---------|-----------|----------| | Trend-following (EMA cross) | Wins | Wins (short) | Bleeds | | Mean reversion (BB fade) | Bleeds | Bleeds | Wins | | Breakout (range break) | Wins early | Wins early | Bleeds | | ML signal with trend bias | Wins | Loses | Noisy |

If you run a single strategy across all regimes, you are leaving money on the table in the good regimes and losing money in the bad ones.

Features That Separate Regimes

APIndicators classifies regimes using five core inputs computed from OHLCV data:

  1. ADX(14): trend strength. ADX > 25 = trending, ADX < 18 = ranging.
  2. EMA stack: relative position of EMA 20, 50, 200. Bullish stack (20 > 50 > 200) = uptrend.
  3. 20-period slope: linear regression slope of close prices. Positive and large = uptrend.
  4. Realized volatility: standard deviation of returns over 24 hours. Extremely high volatility often marks regime breaks.
  5. Price vs VWAP: where is price relative to volume-weighted average?

The classification logic in simplified pseudocode:

def classify_regime(adx, ema_20, ema_50, ema_200, slope_20, close_vs_vwap):
    strong_trend = adx > 25

    if not strong_trend:
        return "sideways"

    bullish_stack = ema_20 > ema_50 > ema_200
    bearish_stack = ema_20 < ema_50 < ema_200

    if bullish_stack and slope_20 > 0 and close_vs_vwap > 0:
        return "uptrend"
    elif bearish_stack and slope_20 < 0 and close_vs_vwap < 0:
        return "downtrend"
    else:
        return "mixed"

APIndicators' production regime detector uses a more nuanced version of this logic, with smoothing to avoid regime flips every candle.

The Market-Type Endpoint

Hitting the endpoint directly:

import requests
import os

def get_market_type(symbol, interval="1h"):
    url = f"https://api.apindicators.com/v1/pairs/{symbol}/market-type"
    headers = {"Authorization": f"Bearer {os.environ['APINDICATORS_API_KEY']}"}
    params = {"interval": interval}
    r = requests.get(url, headers=headers, params=params, timeout=10)
    return r.json()

regime = get_market_type("BTCUSDT")
print(f"Regime: {regime['market_type']}")
print(f"Confidence: {regime['confidence']}")
print(f"ADX: {regime['adx']}")

Response shape:

{
  "symbol": "BTCUSDT",
  "interval": "1h",
  "market_type": "uptrend",
  "confidence": 0.82,
  "adx": 31.4,
  "slope_20": 0.0042,
  "updated_at": "2026-04-04T18:00:00Z"
}

Call this once per candle, route to the right strategy. Done.

Building a Regime-Aware Bot

Here is the full shape of a regime-aware strategy selector:

def select_strategy_for_regime(symbol):
    regime = get_market_type(symbol)
    market_type = regime["market_type"]
    confidence = regime["confidence"]

    if confidence < 0.60:
        return None

    if market_type == "uptrend":
        return "ema_pullback_long"
    elif market_type == "downtrend":
        return "ema_rally_short"
    elif market_type == "sideways":
        return "bollinger_mean_reversion"
    return None

def run_bot(symbol):
    strategy_name = select_strategy_for_regime(symbol)
    if strategy_name is None:
        print("Mixed regime, standing aside")
        return

    print(f"Running {strategy_name} on {symbol}")

Three practical extensions:

  1. Require regime persistence. Do not switch strategies every candle. Wait for 3+ consecutive periods in a new regime.
  2. Reduce size during regime transitions. Regime flips are high-uncertainty.
  3. Log regime at entry and exit. If a trade loses during a regime change, that is different from losing in a stable regime.

Real Performance Impact

In APIndicators V2 production data (Feb 18 - Mar 31, 973 real trades), BUY signals taken during "uptrend" regimes hit 62% win rate. Same BUY signals taken during "sideways" regimes hit 54%. The signal quality is identical. The regime determines whether the follow-through happens.

If you filter the same signals by regime, you trade fewer times but your edge per trade increases by 5-8 percentage points. That is the leverage from regime awareness.

Practical Takeaways

  1. Classify regime before selecting strategy. Do not run one strategy everywhere.
  2. ADX is your fastest regime indicator. Use it as the first filter.
  3. Wait for regime confidence before acting. Low-confidence readings are noise.
  4. Reduce size during transitions. The moment between regimes is the riskiest.
  5. Log regime with every trade so you can analyze per-regime performance later.

The /market-type endpoint is included in all APIndicators plans. Start using it at apindicators.com/docs or sign up at apindicators.com/pricing.