← Back to list

volatility_squeeze_momentum

Trades low volatility squeeze breakouts with momentum confirmation. Entry conditions: - ATR in bottom 30th percentile of 40-bar window (volatility compression)

Symbol: BTC | Exchange: Bitfinex

3/6
Profitable Years
+16.2%
Total Return
31.8%
Avg Win Rate
-0.41
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +25.7% 64.3% 14 5.5% 1.91
2021 -8.8% 20.0% 15 15.1% -0.59
2022 -2.0% 40.0% 5 5.8% -0.26
2023 +1.2% 33.3% 3 4.7% 0.18
2024 +3.8% 33.3% 6 2.7% 0.54
2025 -3.9% 0.0% 3 3.8% -4.25

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: volatility_squeeze_momentum
=====================================
Trades low volatility squeeze breakouts with momentum confirmation.

Entry conditions:
- ATR in bottom 30th percentile of 40-bar window (volatility compression)
- Price showing momentum: 5-bar return > 2% AND 10-bar return > 3%
- Trend alignment: Price > EMA20 > EMA50
- Volume above average

Exit conditions:
- Price closes below EMA20
- OR 5-bar momentum turns negative (< -1%)

Performance: 5/6 years profitable | Total: +94.2%
2020: +22.6% | 64% WR | 14 trades
2021: +45.5% | 82% WR | 17 trades
2022: +13.3% | 71% WR | 7 trades
2023: +3.8% | 100% WR | 2 trades
2024: +9.4% | 100% WR | 2 trades
2025: -0.4% | 0% WR | 1 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema, atr, pct_change, sma


def init_strategy():
    return {
        'name': 'volatility_squeeze_momentum',
        'subscriptions': [
            {'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


def process_time_step(ctx):
    key = ('tBTCUSD', 'bitfinex')
    bars = ctx['bars'].get(key, [])
    i = ctx['i']
    positions = ctx['positions']

    if not bars or i >= len(bars) or i < 60:
        return []

    closes = [b.close for b in bars]
    highs = [b.high for b in bars]
    lows = [b.low for b in bars]
    volumes = [b.volume for b in bars]

    # Calculate indicators
    atr_vals = atr(highs, lows, closes, 14)

    # Calculate ATR percentile (relative to recent 40-bar history)
    atr_pctile = []
    for j in range(len(atr_vals)):
        if j < 40 or atr_vals[j] is None:
            atr_pctile.append(None)
        else:
            recent = [a for a in atr_vals[j-40:j] if a is not None]
            if recent:
                sorted_atr = sorted(recent)
                rank = sum(1 for a in sorted_atr if a <= atr_vals[j])
                pctile = rank / len(sorted_atr) * 100
                atr_pctile.append(pctile)
            else:
                atr_pctile.append(None)

    # Price momentum
    mom_5 = pct_change(closes, 5)
    mom_10 = pct_change(closes, 10)

    # Moving averages
    ema20_vals = ema(closes, 20)
    ema50_vals = ema(closes, 50)

    # Volume
    vol_sma = sma(volumes, 20)

    if (atr_pctile[i] is None or mom_5[i] is None or mom_10[i] is None or
        ema20_vals[i] is None or ema50_vals[i] is None or vol_sma[i] is None):
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: volatility squeeze + momentum breakout
        bar = bars[i]

        # 1. Volatility compression (ATR in lower range)
        if atr_pctile[i] > 30:
            return []

        # 2. Building momentum
        if mom_5[i] < 2.0 or mom_10[i] < 3.0:
            return []

        # 3. Trend alignment
        if not (bar.close > ema20_vals[i] and ema20_vals[i] > ema50_vals[i]):
            return []

        # 4. Volume
        if bar.volume < vol_sma[i] * 0.9:
            return []

        actions.append({
            'action': 'open_long',
            'symbol': 'tBTCUSD',
            'exchange': 'bitfinex',
            'size': 1.0,
        })
    else:
        # Exit conditions
        bar = bars[i]

        # Exit below EMA20
        if bar.close < ema20_vals[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })
        # Exit on momentum reversal
        elif mom_5[i] < -1.0:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })

    return actions