← Back to list

three_bulls_volume_momentum_btc_i55h8pjo

Buy after 3 consecutive bullish candles with increasing volume and strong momentum Entry Conditions: - 3 consecutive bullish bars (close > open), each with 0.7%+ body

Symbol: BTC | Exchange: Binance

5/6
Profitable Years
+43.7%
Total Return
46.1%
Avg Win Rate
0.79
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +30.8% 54.5% 11 1.6% 2.15
2021 -3.5% 16.7% 6 3.4% -2.65
2022 +1.7% 25.0% 4 0.1% 1.09
2023 +11.3% 37.5% 8 0.6% 1.68
2024 +2.8% 42.9% 7 2.2% 0.73
2025 +0.6% 100.0% 2 0.0% 1.77

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: three_bulls_volume_momentum_btc
==========================================
Buy after 3 consecutive bullish candles with increasing volume and strong momentum

Entry Conditions:
- 3 consecutive bullish bars (close > open), each with 0.7%+ body
- Each close higher than previous (rising momentum)
- Price at least 1% above EMA50 (uptrend confirmation)
- Last 2 bars have 1.2x+ average volume (volume confirmation)
- Combined 3-bar gain of 1.8%+ (strong momentum)

Exit Conditions:
- Close below EMA50 (trend break)
- OR first declining close (momentum loss)

Performance: 5/6 years profitable | Total: +67.9% | WR: 70.9%
2020: +38.4% | 73% WR | 11 trades
2021: -2.7% | 33% WR | 6 trades
2022: +7.4% | 75% WR | 4 trades
2023: +20.9% | 88% WR | 8 trades
2024: +3.3% | 57% WR | 7 trades
2025: +0.5% | 100% WR | 2 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema


def init_strategy():
    return {
        'name': 'three_bulls_volume_momentum_btc',
        'subscriptions': [
            {'symbol': 'BTCUSDT', 'exchange': 'binance', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


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

    if not bars or i >= len(bars) or i < 100:
        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: 3 consecutive bullish candles with volume and momentum
        # Price must be at least 1% above EMA50 (uptrend)
        if bars[i].close <= ema50[i] * 1.01:
            return []

        # Last 3 bars must all be bullish (close > open)
        for j in range(i-2, i+1):
            if bars[j].close <= bars[j].open:
                return []

        # Each close must be higher than previous (momentum)
        if not (bars[i-1].close > bars[i-2].close and bars[i].close > bars[i-1].close):
            return []

        # Each candle must have good body (at least 0.7%)
        for j in range(i-2, i+1):
            body_pct = (bars[j].close - bars[j].open) / bars[j].open
            if body_pct < 0.007:
                return []

        # Volume confirmation: last 2 bars above average
        avg_volume = sum(bars[j].volume for j in range(i-20, i-3)) / 17
        if bars[i-1].volume <= avg_volume * 1.2 or bars[i].volume <= avg_volume * 1.2:
            return []

        # Combined 3-bar momentum must be strong (1.8%+)
        momentum = (bars[i].close - bars[i-2].open) / bars[i-2].open
        if momentum < 0.018:
            return []

        actions.append({
            'action': 'open_long',
            'symbol': 'BTCUSDT',
            'exchange': 'binance',
            'size': 1.0,
        })
    else:
        # Exit: price below EMA50 or declining close
        if bars[i].close < ema50[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'BTCUSDT',
                'exchange': 'binance',
            })
        elif bars[i].close < bars[i-1].close:
            actions.append({
                'action': 'close_long',
                'symbol': 'BTCUSDT',
                'exchange': 'binance',
            })

    return actions