← Back to list

consecutive_green_momentum

Buy after 2 consecutive bullish 4h candles with 1%+ body each and 2%+ combined gain above EMA50

Symbol: BTC | Exchange: Bitfinex

5/6
Profitable Years
+110.0%
Total Return
43.2%
Avg Win Rate
0.85
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +36.7% 53.3% 30 8.9% 1.55
2021 +16.9% 46.3% 41 13.7% 0.90
2022 +14.4% 50.0% 24 7.1% 1.39
2023 +34.3% 44.0% 25 7.5% 1.49
2024 +15.6% 38.5% 26 11.2% 0.99
2025 -8.0% 27.3% 11 9.5% -1.20

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: consecutive_green_momentum
====================================
Buy after 2 consecutive bullish 4h candles with 1%+ body each and 2%+ combined gain above EMA50

Performance: 5/6 years profitable | Total: +108.0%
2020: +33.5% | 53% WR | 30 trades
2021: +14.1% | 46% WR | 41 trades
2022: +15.2% | 50% WR | 24 trades
2023: +37.6% | 42% WR | 24 trades
2024: +16.1% | 39% WR | 26 trades
2025: -8.6% | 27% WR | 11 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema


def init_strategy():
    return {
        'name': 'consecutive_green_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 < 50:
        return []

    closes = [b.close for b in bars]
    ema50 = ema(closes, 50)

    if ema50[i] is None:
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: 2 consecutive bullish candles with 1%+ body each, 2%+ combined gain, above EMA50
        if bars[i].close <= ema50[i]:
            return []

        bar1 = bars[i-1]
        bar2 = bars[i]

        # Previous bar must be bullish with 1%+ body
        if bar1.close <= bar1.open:
            return []
        body1_pct = (bar1.close - bar1.open) / bar1.open
        if body1_pct < 0.01:
            return []

        # Current bar must be bullish with 1%+ body
        if bar2.close <= bar2.open:
            return []
        body2_pct = (bar2.close - bar2.open) / bar2.open
        if body2_pct < 0.01:
            return []

        # Current close must be higher than previous close
        if bar2.close <= bar1.close:
            return []

        # Combined 2-candle gain must be 2%+
        combined_gain = (bar2.close - bar1.open) / bar1.open
        if combined_gain < 0.02:
            return []

        actions.append({
            'action': 'open_long',
            'symbol': 'tBTCUSD',
            'exchange': 'bitfinex',
            'size': 1.0,
        })
    else:
        # Exit: price below EMA50 OR 2 consecutive bearish candles
        if bars[i].close < ema50[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })
        elif bars[i-1].close < bars[i-1].open and bars[i].close < bars[i].open:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })

    return actions