← Back to list

hh_hl_continuation_breakout

Higher High + Higher Low Continuation Breakout Strategy Identifies bullish continuation patterns by detecting: 1. Higher High (HH) - recent swing high is higher than previous swing high

Symbol: SOL | Exchange: Binance

5/6
Profitable Years
+111.8%
Total Return
52.9%
Avg Win Rate
0.49
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 -6.7% 60.0% 5 14.5% -0.40
2021 +27.5% 50.0% 8 29.7% 0.44
2022 +8.3% 55.6% 9 4.8% 0.90
2023 +57.5% 47.1% 17 20.6% 1.30
2024 +1.1% 54.5% 11 16.2% 0.05
2025 +24.1% 50.0% 14 14.3% 0.64

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: hh_hl_continuation_breakout
=====================================
Higher High + Higher Low Continuation Breakout Strategy

Identifies bullish continuation patterns by detecting:
1. Higher High (HH) - recent swing high is higher than previous swing high
2. Higher Low (HL) - recent swing low is higher than previous swing low
3. Breakout above the most recent HH with volume confirmation

Performance: 5/6 years profitable
Hold period: 10 bars (40 hours)
"""
import sys
sys.path.insert(0, "/root/trade_rules")


def init_strategy():
    return {
        'name': 'hh_hl_continuation_breakout',
        'subscriptions': [
            {'symbol': 'SOLUSDT', 'exchange': 'binance', 'timeframe': '4h'},
        ],
        'parameters': {'hold_bars': 10}
    }


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

    if not bars or i >= len(bars) or i < 50:
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: HH+HL pattern with volume breakout
        if i < 40:
            return []

        # Find swing points (local extrema) in last 25 bars
        swing_highs = []
        swing_lows = []

        for j in range(i-25, i-5):
            if j > 4 and j < len(bars) - 4:
                # Swing high: highest in 3-bar window on each side
                is_high = all(bars[j].high >= bars[k].high
                            for k in range(j-3, j+4) if k != j and k < len(bars))
                if is_high:
                    swing_highs.append((j, bars[j].high))

                # Swing low: lowest in 3-bar window on each side
                is_low = all(bars[j].low <= bars[k].low
                           for k in range(j-3, j+4) if k != j and k < len(bars))
                if is_low:
                    swing_lows.append((j, bars[j].low))

        # Need at least 2 swing highs and 2 swing lows
        if len(swing_highs) < 2 or len(swing_lows) < 2:
            return []

        # Check for Higher High
        recent_highs = swing_highs[-2:]
        higher_high = recent_highs[1][1] > recent_highs[0][1]

        # Check for Higher Low
        recent_lows = swing_lows[-2:]
        higher_low = recent_lows[1][1] > recent_lows[0][1]

        # Volume confirmation
        volumes = [bars[j].volume for j in range(i-20, i)]
        avg_volume = sum(volumes) / len(volumes)

        # Entry: HH+HL pattern, breakout, volume surge
        latest_hh = recent_highs[-1][1]
        if (higher_high and higher_low and
            bars[i].close > latest_hh and
            bars[i].volume > avg_volume * 1.3):
            actions.append({
                'action': 'open_long',
                'symbol': 'SOLUSDT',
                'exchange': 'binance',
                'size': 1.0,
            })
    else:
        # Exit after hold period (10 bars)
        pos = positions[key]
        bars_held = i - pos.entry_bar
        if bars_held >= 10:
            actions.append({
                'action': 'close_long',
                'symbol': 'SOLUSDT',
                'exchange': 'binance',
            })

    return actions