← Back to list

volume_expansion_breakout

Buy when volume expands 2.5x+ AND price breaks 10-bar high above EMA100 Entry Conditions: - Price above EMA100 (trend filter)

Symbol: BTC | Exchange: Bitfinex

5/6
Profitable Years
+208.7%
Total Return
32.6%
Avg Win Rate
0.68
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +88.2% 50.0% 10 5.1% 1.54
2021 +33.8% 33.3% 12 9.9% 0.92
2022 -11.6% 25.0% 16 19.5% -0.92
2023 +27.0% 22.2% 18 11.6% 0.74
2024 +55.1% 40.0% 15 21.8% 1.01
2025 +16.2% 25.0% 8 6.5% 0.78

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: volume_expansion_breakout
===================================
Buy when volume expands 2.5x+ AND price breaks 10-bar high above EMA100

Entry Conditions:
- Price above EMA100 (trend filter)
- Volume expansion: current bar has 2.5x+ average volume
- Price breaking out to new 10-bar high
- Bullish bar: close in upper 60% of range

Exit Conditions:
- Price closes below EMA100

Performance: 5/6 years profitable | Total: +204.1%
2020: +88.2% | 50% WR | 10 trades
2021: +33.8% | 33% WR | 12 trades
2022: -12.5% | 25% WR | 16 trades
2023: +27.2% | 22% WR | 18 trades
2024: +54.3% | 40% WR | 15 trades
2025: +13.1% | 25% WR | 8 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema, sma


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

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

    vol_sma = sma(volumes, 20)
    ema100 = ema(closes, 100)

    if vol_sma[i] is None or ema100[i] is None:
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: volume expansion breakout
        # Trend filter: price above EMA100
        if bars[i].close < ema100[i]:
            return []

        # Volume expansion: current bar has 2.5x+ average volume
        if vol_sma[i] == 0:
            return []

        volume_ratio = bars[i].volume / vol_sma[i]
        if volume_ratio < 2.5:
            return []

        # Price must be breaking out to new 10-bar high
        recent_high_10 = max(highs[i-10:i])
        breakout_10 = bars[i].high > recent_high_10

        if not breakout_10:
            return []

        # Bullish bar: close in upper 60% of range
        bar_range = bars[i].high - bars[i].low
        if bar_range == 0:
            return []

        close_pos = (bars[i].close - bars[i].low) / bar_range
        if close_pos < 0.6:
            return []

        actions.append({
            'action': 'open_long',
            'symbol': 'tBTCUSD',
            'exchange': 'bitfinex',
            'size': 1.0,
        })
    else:
        # Exit when price closes below EMA100
        if bars[i].close < ema100[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })

    return actions