TickAtlas
Analysis 11 min read · March 28, 2026

Mean Reversion vs Trend Following: Which Strategy to Code

A data-driven comparison of mean reversion and trend following strategies. Learn when each approach works, how to detect the current regime, and which to implement first.

CG
By the TickAtlas team

Two Philosophies of Price

Every trading strategy is fundamentally based on one of two beliefs about price behavior: either prices tend to continue in the current direction (trend following), or prices tend to return to a central value (mean reversion). The market alternates between regimes where each approach works.

Trend Following

Buy when price is going up, sell when it is going down. Ride the trend until it reverses.

Indicators: MACD, EMA crossovers, ADX, Parabolic SAR

Win rate: 30-40% with large winners

Mean Reversion

Buy when price is "too low," sell when it is "too high." Bet on return to average.

Indicators: RSI, CCI, Bollinger Bands, Stochastic

Win rate: 60-70% with smaller winners

Detecting the Current Regime

ADX is the most reliable regime detector. High ADX (above 25) indicates a trending market; low ADX (below 20) indicates a ranging market.

python
import requests

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://tickatlas.com/v1"

def detect_regime(symbol: str, timeframe: str = "D1") -> dict:
    """Detect whether market is trending or ranging."""
    # Fetch ADX
    adx_resp = requests.get(
        f"{BASE_URL}/indicator",
        headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "ADX", "timeframe": timeframe},
    )
    adx_data = adx_resp.json()["data"]["values"]
    adx = adx_data["adx"]

    # Fetch Bollinger Band width for confirmation
    bb_resp = requests.get(
        f"{BASE_URL}/indicator",
        headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "BOLLINGER_BANDS",
                "timeframe": timeframe},
    )
    bb_data = bb_resp.json()["data"]["values"]
    bb_width = (bb_data["upper"] - bb_data["lower"]) / bb_data["middle"] * 100

    if adx > 30:
        regime = "STRONG_TREND"
        strategy = "trend_following"
    elif adx > 25:
        regime = "TRENDING"
        strategy = "trend_following"
    elif adx < 15:
        regime = "RANGE_BOUND"
        strategy = "mean_reversion"
    else:
        regime = "TRANSITIONING"
        strategy = "wait"

    return {
        "symbol": symbol,
        "adx": round(adx, 1),
        "bb_width_pct": round(bb_width, 2),
        "regime": regime,
        "recommended_strategy": strategy,
    }

# Check multiple symbols
for sym in ["EURUSD", "GBPUSD", "XAUUSD", "BTCUSD"]:
    result = detect_regime(sym)
    print(f"{sym}: {result['regime']} (ADX={result['adx']}) "
          f"-> {result['recommended_strategy']}")

Trend Following Implementation

python
def trend_following_signal(symbol: str, timeframe: str) -> dict:
    """EMA crossover trend following strategy."""
    ema_fast = requests.get(
        f"{BASE_URL}/indicator", headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "EMA_20", "timeframe": timeframe},
    ).json()["data"]["values"]["value"]

    ema_slow = requests.get(
        f"{BASE_URL}/indicator", headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "EMA_50", "timeframe": timeframe},
    ).json()["data"]["values"]["value"]

    macd = requests.get(
        f"{BASE_URL}/indicator", headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "MACD", "timeframe": timeframe},
    ).json()["data"]["values"]

    signal = None
    if ema_fast > ema_slow and macd["histogram"] > 0:
        signal = "LONG"
    elif ema_fast < ema_slow and macd["histogram"] < 0:
        signal = "SHORT"

    return {"symbol": symbol, "signal": signal, "type": "trend_following"}

Mean Reversion Implementation

python
def mean_reversion_signal(symbol: str, timeframe: str) -> dict:
    """RSI + Bollinger Band mean reversion strategy."""
    rsi = requests.get(
        f"{BASE_URL}/indicator", headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "RSI_14", "timeframe": timeframe},
    ).json()["data"]["values"]["rsi"]

    bb = requests.get(
        f"{BASE_URL}/indicator", headers={"X-API-Key": API_KEY},
        params={"symbol": symbol, "indicator": "BOLLINGER_BANDS",
                "timeframe": timeframe},
    ).json()["data"]

    close = bb["ohlcv"]["close"]
    lower = bb["values"]["lower"]
    upper = bb["values"]["upper"]

    signal = None
    if rsi < 30 and close <= lower:
        signal = "LONG"  # Oversold + at lower band
    elif rsi > 70 and close >= upper:
        signal = "SHORT"  # Overbought + at upper band

    return {"symbol": symbol, "signal": signal, "type": "mean_reversion"}

The Adaptive Approach

python
def adaptive_strategy(symbol: str, timeframe: str) -> dict:
    """Switch strategy based on current market regime."""
    regime = detect_regime(symbol)

    if regime["recommended_strategy"] == "trend_following":
        return trend_following_signal(symbol, timeframe)
    elif regime["recommended_strategy"] == "mean_reversion":
        return mean_reversion_signal(symbol, timeframe)
    else:
        return {"symbol": symbol, "signal": None, "type": "waiting"}

# Scan and adapt
for sym in ["EURUSD", "GBPUSD", "XAUUSD"]:
    result = adaptive_strategy(sym, "H4")
    if result["signal"]:
        print(f"{sym}: {result['signal']} ({result['type']})")
    else:
        print(f"{sym}: No signal")

Which to Code First?

Start with mean reversion if you prefer higher win rates

Mean reversion strategies win more often (60-70%) but each win is smaller. This is psychologically easier for most traders and simpler to code.

Start with trend following if you can tolerate frequent losses

Trend following wins less often (30-40%) but the winners are large enough to compensate. You need the discipline to take many small losses before the big win.

Build both and let the regime decide

The best approach is to implement both and use ADX or Bollinger Band width to automatically switch between them. This is more complex but far more robust.

Related Reading

Try this with live data

Every account gets $2.50 in free PAYG credits. No card required — paste your API key and run the code above against live broker data.