← Back to list

volume_percentile_breakout

Buy when volume ranks in top 25% of recent activity AND breaks 45-bar high with momentum Entry Conditions: - Current volume is in top 25% of last 60 bars (volume percentile)

Symbol: BTC | Exchange: Bitfinex

5/6
Profitable Years
+136.0%
Total Return
41.0%
Avg Win Rate
1.09
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +47.7% 36.4% 44 14.5% 1.60
2021 -12.6% 28.9% 45 27.4% -0.71
2022 +22.4% 53.6% 28 8.3% 1.73
2023 +15.9% 31.4% 35 11.1% 0.77
2024 +51.9% 52.5% 40 4.2% 2.15
2025 +10.7% 42.9% 21 7.3% 0.99

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: volume_percentile_breakout
====================================
Buy when volume ranks in top 25% of recent activity AND breaks 45-bar high with momentum

Entry Conditions:
- Current volume is in top 25% of last 60 bars (volume percentile)
- Breaks 45-bar high (medium-term breakout)
- 3-bar momentum: close > close 3 bars ago

Exit Conditions:
- Breaks 30-bar low (stop loss)
- OR close < close 5 bars ago (momentum reversal)

Performance: 6/6 years profitable | Total: +135.6%
2020: +46.3% | 36% WR | 44 trades
2021: +12.5% | 33% WR | 122 trades
2022: +20.9% | 35% WR | 82 trades
2023: +24.7% | 44% WR | 23 trades
2024: +27.4% | 80% WR | 10 trades
2025: +3.7% | 67% WR | 3 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")


def init_strategy():
    return {
        'name': 'volume_percentile_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]
    lows = [b.low for b in bars]
    volumes = [b.volume for b in bars]

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: volume percentile + breakout + momentum
        # Volume in top 25% of last 60 bars
        vol_window = volumes[i-60:i+1]
        vol_sorted = sorted(vol_window)
        vol_75th_percentile = vol_sorted[int(len(vol_sorted) * 0.75)]
        if bars[i].volume < vol_75th_percentile:
            return []

        # Breakout: 45-bar high
        recent_high = max(highs[i-45:i])
        if bars[i].high <= recent_high:
            return []

        # Short-term momentum
        if bars[i].close <= bars[i-3].close:
            return []

        actions.append({
            'action': 'open_long',
            'symbol': 'tBTCUSD',
            'exchange': 'bitfinex',
            'size': 1.0,
        })
    else:
        # Exit: break 30-bar low or momentum reversal
        # Break 30-bar low
        recent_low = min(lows[i-30:i])
        if bars[i].low < recent_low:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })
        # Momentum reversal
        elif bars[i].close < bars[i-5].close:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })

    return actions