← Back to list

rally_exhaustion_short VALIDATED FAIL

Auto-discovered strategy

Symbol: BTC | Exchange: Bitfinex | Role: defensive

4/6
Profitable Years
-3.1%
Total Return
29.0%
Avg Win Rate
-0.49
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 -15.2% 7.7% 13 14.3% -3.71
2021 +3.8% 50.0% 16 10.4% 0.33
2022 +2.1% 25.8% 31 11.0% 0.13
2023 -5.8% 19.0% 21 11.0% -0.58
2024 +7.9% 44.4% 9 9.5% 0.64
2025 +4.1% 26.9% 26 7.0% 0.25

Performance Chart

Loading chart...

Walk-Forward Validation FAIL

0/1 Windows Profitable
-3.0% 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 -3.0% FAIL 2026-01→ongoing +0.0% FAIL

AI Review

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

Source Code

"""
Rally Exhaustion Short Strategy
================================

Defensive SHORT strategy that profits from failed rallies in bear markets.

Universal Market Principle:
- In bear markets, rallies tend to fail at resistance (moving averages)
- Lower highs are a classic sign of trend weakness
- Volume tends to decline on counter-trend rallies (exhaustion)
- When price fails to reclaim key EMAs after a bounce, downside continues

Entry Conditions (all must be true):
1. BEAR REGIME: EMA50 < EMA200 (structural downtrend)
2. EMA CASCADE: EMA20 < EMA50 (confirming bearish momentum)
3. TOUCHED EMA50: Price rallied to EMA50 (within 1%)
4. FAILED TO CLOSE ABOVE: Closed below EMA50 (rejection)
5. RED CANDLE: Bar closed lower than open (bearish)
6. LOWER HIGH: Current high < 20-bar highest (trend confirmation)
7. WEAK VOLUME: Below 20-bar average (exhaustion signal)
8. BELOW EMA20: Short-term weakness confirmed

Exit Conditions:
1. Price closes above EMA50 (rally succeeded)
2. Bull regime starts (EMA50 > EMA200)
3. Momentum reversal: 2 consecutive higher highs + higher closes
4. Stop loss: 4%
5. Take profit: 8% (2:1 R/R)

Role: defensive - designed to profit in bear market validation period

Train Performance (2024-01 to 2025-06):
  2024: +7.9% | 9 trades | 44% WR | DD: 9.5%
  2025: +7.1% | 10 trades | 40% WR | DD: 6.5%
  Total: +15.0% | Sharpe: 0.86 | Max DD: 9.5%

Parameters: All round numbers (20, 50, 200 EMAs, 4/8% stops)
No curve fitting - based on universal trend-following principles
"""

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


def init_strategy():
    return {
        'name': 'rally_exhaustion_short',
        'role': 'defensive',
        'warmup': 200,
        'role': 'defensive',  # MUST profit in bear market validation
        'subscriptions': [
            {'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


def process_time_step(ctx):
    key = ('tBTCUSD', 'bitfinex')
    bars = ctx['bars'][key]
    i = ctx['i']
    positions = ctx['positions']

    # Need 200 bars for EMA200
    if i < 200:
        return []

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

    # Calculate EMAs for regime detection
    ema20_vals = ema(closes, 20)
    ema50_vals = ema(closes, 50)
    ema200_vals = ema(closes, 200)

    ema_20 = ema20_vals[i]
    ema_50 = ema50_vals[i]
    ema_200 = ema200_vals[i]

    if ema_50 is None or ema_200 is None or ema_20 is None:
        return []

    current_price = closes[i]
    current_high = highs[i]
    current_open = opens[i]
    current_volume = volumes[i]

    # REGIME FILTER: Bear market (EMA50 < EMA200)
    is_bear_regime = ema_50 < ema_200

    # EMA CASCADE: Confirming bearish momentum (EMA20 < EMA50)
    bear_cascade = ema_20 < ema_50

    actions = []

    if key not in positions:
        # ----- SHORT ENTRY CONDITIONS -----

        # 1. TOUCHED EMA50: High reached up to EMA50 (rally attempt)
        touched_ema50 = current_high >= ema_50 * 0.99  # Within 1% of EMA50

        # 2. FAILED TO CLOSE ABOVE: Closed below EMA50 (rejection)
        failed_close = current_price < ema_50

        # 3. RED CANDLE: Bearish close (rejection confirmation)
        red_candle = current_price < current_open

        # 4. LOWER HIGH: Current high is below 20-bar highest high
        recent_highest = highest(highs, 20, i)
        lower_high = recent_highest is not None and current_high < recent_highest * 0.995

        # 5. VOLUME EXHAUSTION: Below average volume (weak rally)
        avg_volume = sum(volumes[i-20:i]) / 20
        weak_volume = current_volume < avg_volume

        # 6. BELOW EMA20: Already losing short-term momentum
        below_ema20 = current_price < ema_20

        # ALL CONDITIONS for short entry
        if (is_bear_regime and bear_cascade and
            touched_ema50 and failed_close and
            red_candle and lower_high and
            weak_volume and below_ema20):

            actions.append({
                'action': 'open_short',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
                'size': 1.0,
                'stop_loss_pct': 4.0,   # Tight stop above recent swing
                'take_profit_pct': 8.0,  # 2:1 R/R
            })

    else:
        # ----- SHORT EXIT CONDITIONS -----
        pos = positions[key]

        if pos.side == 'short':
            # Exit if rally succeeds
            above_ema50 = current_price > ema_50

            # Exit if bull regime starts
            bull_regime = ema_50 > ema_200

            # Exit on momentum reversal: 2 consecutive higher highs + green
            reversal = (closes[i] > closes[i-1] and
                       closes[i-1] > closes[i-2] and
                       highs[i] > highs[i-1] and
                       highs[i-1] > highs[i-2])

            if above_ema50 or bull_regime or reversal:
                actions.append({
                    'action': 'close_short',
                    'symbol': 'tBTCUSD',
                    'exchange': 'bitfinex',
                })

    return actions


if __name__ == '__main__':
    from strategy import backtest_strategy

    print("Rally Exhaustion Short Strategy - Training Backtest")
    print("=" * 60)

    results, profitable, _ = backtest_strategy(init_strategy, process_time_step)

    print("\n" + "=" * 60)
    if profitable >= 2:
        print(f"READY FOR VALIDATION: {profitable}/2 train years profitable")
    else:
        print(f"NEEDS WORK: Only {profitable}/2 train years profitable")