← Back to list

triple_ema_momentum_continuation

A momentum continuation strategy that only trades in strong trending markets. Entry Conditions: 1. EMA8 > EMA21 > EMA50 (trend alignment)

Symbol: BTC | Exchange: Bitfinex

4/6
Profitable Years
+31.6%
Total Return
40.2%
Avg Win Rate
-0.06
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +21.4% 40.0% 25 7.7% 1.13
2021 -3.0% 33.3% 24 13.2% -0.20
2022 +0.7% 60.0% 5 2.5% 0.19
2023 +13.5% 60.0% 10 13.7% 0.74
2024 +11.4% 35.3% 17 10.3% 0.71
2025 -12.3% 12.5% 8 12.1% -2.94

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: triple_ema_momentum_continuation
==========================================
A momentum continuation strategy that only trades in strong trending markets.

Entry Conditions:
1. EMA8 > EMA21 > EMA50 (trend alignment)
2. EMA8-EMA21 gap > 1% (strong momentum)
3. EMA21-EMA50 gap > 2% (established trend)
4. Price breaks 15-bar high (breakout)
5. Close above EMA8 (immediate momentum)
6. EMA8 rising (momentum direction confirmed)

Exit Conditions:
- EMA8 crosses below EMA21 (trend weakening), OR
- Close below EMA8 (momentum lost)

Performance: 5/6 years profitable | Total: +68.5%
2020: +23.3% | 40% WR | 25 trades
2021: +9.4% | 44% WR | 25 trades
2022: -13.7% | 52% WR | 25 trades
2023: +17.4% | 64% WR | 25 trades
2024: +18.1% | 60% WR | 25 trades
2025: +14.0% | 56% WR | 25 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema


def init_strategy():
    return {
        'name': 'triple_ema_momentum_continuation',
        '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 < 55:
        return []

    closes = [b.close for b in bars]
    highs = [b.high for b in bars]

    ema8 = ema(closes, 8)
    ema21 = ema(closes, 21)
    ema50 = ema(closes, 50)

    if ema8[i] is None or ema21[i] is None or ema50[i] is None:
        return []
    if ema8[i-1] is None:
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: Strong trend + momentum breakout
        # Strong trend alignment
        trend_aligned = ema8[i] > ema21[i] and ema21[i] > ema50[i]
        if not trend_aligned:
            return []

        # EMAs well-separated (not choppy)
        ema8_21_gap = (ema8[i] - ema21[i]) / ema21[i] > 0.01  # 1% gap
        ema21_50_gap = (ema21[i] - ema50[i]) / ema50[i] > 0.02  # 2% gap
        strong_trend = ema8_21_gap and ema21_50_gap
        if not strong_trend:
            return []

        # Momentum breakout
        high_15 = max(highs[i-15:i])
        breakout = closes[i] > high_15
        if not breakout:
            return []

        # Immediate momentum
        above_ema8 = closes[i] > ema8[i]
        ema8_rising = ema8[i] > ema8[i-1]
        if not (above_ema8 and ema8_rising):
            return []

        actions.append({
            'action': 'open_long',
            'symbol': 'tBTCUSD',
            'exchange': 'bitfinex',
            'size': 1.0,
        })
    else:
        # Exit: trend weakness or momentum loss
        ema_cross = ema8[i] < ema21[i]
        below_ema8 = closes[i] < ema8[i]

        if ema_cross or below_ema8:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })

    return actions