← Back to list

swing_support_trend_bounce VALIDATED FAIL

Auto-discovered strategy

Symbol: BTC | Exchange: Binance | Role: momentum

4/6
Profitable Years
+153.7%
Total Return
35.9%
Avg Win Rate
0.82
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 +103.7% 57.9% 19 17.4% 2.73
2021 -0.2% 25.0% 16 16.7% -0.01
2022 -0.7% 25.0% 4 5.0% -0.08
2023 +10.4% 33.3% 15 10.1% 0.45
2024 +31.5% 37.5% 16 23.4% 1.01
2025 +9.1% 36.4% 11 7.6% 0.81

Performance Chart

Loading chart...

Walk-Forward Validation FAIL

0/1 Windows Profitable
-1.1% OOS Return
0.00 Median Sharpe
0.000 Score
Window Train Period Val Period Val Return Val Test Period Test Return Status
WF-1 2024-01→2025-06 2025-07→2025-12 -1.1% FAIL 2026-01→ongoing +0.0% FAIL

AI Review

Not yet reviewed. Run: ./review_strategy.sh swing_support_trend_bounce

Source Code

"""
Swing Support Trend Bounce Strategy
====================================

A support/resistance bounce strategy for BTCUSDT that:
1. Only trades in strong uptrends (full EMA cascade: EMA20 > EMA50 > EMA200)
2. Identifies swing low support levels from recent 20 bars
3. Enters on bounce from support with volume confirmation and breakout
4. Uses tight stops and regime-based exits

This strategy should stay FLAT during bear markets due to the
strict EMA cascade requirement - if EMAs are not aligned, no trades.

Entry conditions:
- EMA20 > EMA50 > EMA200 (full uptrend)
- EMA50 rising over 5 bars
- Price above EMA50
- Price tested swing low support (within 5%) in last 5 bars
- Price bounced above support
- Breakout above 10-bar high
- Bullish candle with >50% body ratio
- Volume >1.2x 20-bar average

Exit conditions:
- Close below EMA50 (support broken)
- EMA20 crosses below EMA50 (trend weakening)
- Stop loss: 5%
- Take profit: 15%

Training Performance (2024-2025H1):
- 2024: +31.5% | 16 trades | 38% WR | Sharpe 1.01 | MaxDD 23.4%
- 2025H1: +8.1% | 5 trades | 40% WR | Sharpe 0.87 | MaxDD 5.0%
- Total: +39.6%
"""

import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema


def init_strategy():
    return {
        'name': 'swing_support_trend_bounce',
        'role': 'momentum',  # Momentum: allows bounded loss in bear market
        'warmup': 200,
        'subscriptions': [
            {'symbol': 'BTCUSDT', 'exchange': 'binance', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


def process_time_step(ctx):
    """
    Swing Support Trend Bounce

    A support/resistance bounce strategy that:
    1. Only trades in strong uptrends (full EMA cascade)
    2. Identifies swing low support levels
    3. Enters on bounce from support with volume confirmation
    4. Uses tight stops and regime-based exits

    This strategy should stay flat during bear markets due to the
    strict EMA cascade requirement.
    """
    key = ('BTCUSDT', 'binance')
    bars = ctx['bars'][key]
    i = ctx['i']
    positions = ctx['positions']

    # Data
    closes = [b.close for b in bars[:i+1]]
    lows = [b.low for b in bars[:i+1]]
    highs = [b.high for b in bars[:i+1]]
    opens = [b.open for b in bars[:i+1]]
    volumes = [b.volume for b in bars[:i+1]]

    # EMAs (using round numbers)
    ema20 = ema(closes, 20)
    ema50 = ema(closes, 50)
    ema200 = ema(closes, 200)

    actions = []

    if key not in positions:
        # === ENTRY CONDITIONS ===

        # 1. All EMAs must be defined
        if any(e[i] is None for e in [ema20, ema50, ema200]):
            return []

        # 2. STRONG REGIME: EMA cascade (20 > 50 > 200)
        #    This keeps us flat in bear markets
        if not (ema20[i] > ema50[i] > ema200[i]):
            return []

        # 3. EMA50 must be rising (momentum)
        if i >= 5 and ema50[i-5] is not None:
            if ema50[i] <= ema50[i-5]:
                return []

        # 4. Price above EMA50 (structure intact)
        if closes[i] < ema50[i]:
            return []

        # 5. Define support from 20-bar swing low
        swing_low = min(lows[max(0, i-20):i])

        # 6. Price tested support zone (within 5%) in last 5 bars
        support_zone_top = swing_low * 1.05
        tested_support = False
        for j in range(i-5, i+1):
            if lows[j] <= support_zone_top:
                tested_support = True
                break

        if not tested_support:
            return []

        # 7. Now above support (bounced)
        if closes[i] < swing_low * 1.02:
            return []

        # 8. Breakout above 10-bar high (momentum resuming)
        recent_high = max(highs[max(0, i-10):i])
        if closes[i] <= recent_high:
            return []

        # 9. Bullish candle with strong body (>50%)
        is_bullish = closes[i] > opens[i]
        bar_range = highs[i] - lows[i]
        body_ratio = (closes[i] - opens[i]) / bar_range if bar_range > 0 else 0

        if not (is_bullish and body_ratio > 0.5):
            return []

        # 10. Volume above average (1.2x)
        avg_vol = sum(volumes[i-20:i]) / 20
        if volumes[i] < avg_vol * 1.2:
            return []

        # Enter with stops
        actions.append({
            'action': 'open_long',
            'symbol': 'BTCUSDT',
            'exchange': 'binance',
            'size': 1.0,
            'stop_loss_pct': 5.0,   # 5% stop
            'take_profit_pct': 15.0,  # 3:1 R/R
        })

    else:
        # === EXIT CONDITIONS ===

        # Exit if close below EMA50 (support broken)
        if closes[i] < ema50[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'BTCUSDT',
                'exchange': 'binance',
            })
        # Exit on EMA cascade breakdown
        elif ema20[i] < ema50[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'BTCUSDT',
                'exchange': 'binance',
            })

    return actions