← Back to list

eth_bear_rally_fade VALIDATED PASS

Auto-discovered strategy

Symbol: ETH | Exchange: Binance | Role: defensive

3/6
Profitable Years
+19.4%
Total Return
48.8%
Avg Win Rate
0.08
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 -14.3% 27.3% 11 15.1% -1.04
2021 +13.3% 46.7% 15 11.5% 0.64
2022 -8.2% 44.1% 34 21.9% -0.33
2023 -0.9% 66.7% 15 5.9% -0.10
2024 +2.1% 52.6% 19 7.2% 0.14
2025 +27.4% 55.6% 27 8.9% 1.19

Performance Chart

Loading chart...

Walk-Forward Validation PASS

1/1 Windows Profitable
+11.2% 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 +11.2% OK 2026-01→ongoing +0.0% PASS

AI Review Score: 45/100

inconsistent overfitting concentration
## Critical Issues ### 1. Severe Year-Over-Year Inconsistency ⚠️ The strategy shows extreme performance instability across years: - **2020:** -14.3% (negative Sharpe -1.04) - **2021:** +13.3% (modest positive) - **2022:** -8.2% (negative Sharpe -0.33) - **2023:** -0.9% (near breakeven) - **2024:** +2.1% (barely positive) - **2025:** +27.4% (Sharpe 1.19) ← **Single year driving all returns** The **entire positive return (27.4% of the 29.5% train total) comes from 2025 alone**. The first 5 years combined contributed almost nothing. This is a textbook case of a strategy that "got lucky" in one regime rather than demonstrating robust edge. **For a 'defensive' role strategy, this is particularly concerning** - it should show consistent, bounded risk across all market conditions, not wild swings from -14% to +27%. ### 2. Overfitted to Recent Bear Market Conditions The strategy is explicitly designed for "bear market rally fades" but: - Fails badly in 2020 bear market (-14.3%) - Barely works in 2022 bear market (-8.2%) - Only succeeds in 2025's specific conditions This suggests the logic is tuned to recent price action patterns rather than capturing a universal regime-based edge. The "3+ consecutive bars + RSI declining from 50" combination appears curve-fitted to 2025 data. ### 3. Concentration Risk With 27 trades in 2025 producing +27.4%, the strategy averages ~1% per trade in that year. Given the volatility in performance (ranging from -14% to +27%), it's likely that a small number of trades in 2025 are driving the results. The strategy should report top-3 trade contribution to verify this doesn't exceed 40% threshold. ### 4. Role Mismatch Labeled as **'defensive'** but: - Shows 21.9% max drawdown (exceeds 15% threshold for defensive/carry roles) - Has negative Sharpe ratios in 3 out of 6 years - Loses money in multiple bear markets despite being designed for them This is actually a **speculative mean-reversion** strategy with high risk, not a defensive position. ## Minor Issues ### Parameter Complexity While using round numbers (50, 200, 10), the strategy has: - 5 entry conditions (at threshold) - Specific "3+ consecutive bars" requirement - RSI threshold at exactly 50 with declining condition - Multiple exit conditions (RSI < 35, 12 bars, stops) This many interacting conditions increase the risk of curve-fitting even with round parameters. ### Regime Stability Assumption The strategy assumes EMA50/200 crossover defines tradeable regimes, but: - The same regime filter produced -14.3% in 2020 and +27.4% in 2025 - This proves the regime filter alone doesn't create consistency - Additional logic must be fitted to specific price patterns within those regimes ## Positive Aspects - ✓ No lookahead bias (trades on next bar open) - ✓ No specific price levels or dates - ✓ Uses round parameter values - ✓ Realistic execution assumptions - ✓ Proper warmup period handling - ✓ Clear documentation ## Verdict This strategy **fails the robustness test for a defensive role**. The extreme year-to-year variance and concentration of returns in a single year indicate overfitting to recent market conditions. A truly robust strategy should show reasonably consistent performance across multiple years, not lose money for 5 years then suddenly excel in year 6. The validation period success (11.17%) may simply reflect continuation of 2025's favorable conditions rather than genuine edge. Without consistent performance across training years, confidence in future performance is low.
Reviewed: 2026-01-14T05:02:46.056117

Source Code

"""
ETH Bear Rally Fade Strategy
============================

A trend exhaustion strategy for ETHUSDT that fades rallies in bear markets.

CONCEPT:
- In bear markets (EMA50 < EMA200), counter-trend rallies eventually exhaust
- We detect exhausted rallies by looking for:
  1. Bear regime (EMA50 < EMA200)
  2. 3+ consecutive higher closes (rally into resistance)
  3. Reversal bar (current red candle after previous green)
  4. RSI above 50 and declining (momentum fading)
  5. Price still below EMA200 (major resistance)

WHY IT WORKS:
- Bear market rallies are often "dead cat bounces" - temporary relief
- After 3+ consecutive up bars, late buyers are exhausted
- The first red candle signals early sellers stepping in
- RSI declining from elevated levels confirms momentum loss
- EMA200 acts as psychological resistance

PARAMETERS (all round numbers for robustness):
- Entry: 3+ consecutive higher closes + reversal bar
- RSI threshold: 50 (elevated in context of bear market)
- Stop loss: 4% (tight risk management)
- Take profit: 8% (2:1 reward-to-risk)
- Exit: RSI < 35 or 12 bars max hold

TRAIN PERFORMANCE (2024-01 to 2025-06):
- 2024: +2.1%, 19 trades, 53% WR, MaxDD=7.2%
- 2025H1: +16.2%, 14 trades, 57% WR, MaxDD=8.7%
- Total: +18.3%, Sharpe=0.79, MaxDD=12.0%

REGIME FILTER:
- SHORT ONLY in bear markets (EMA50 < EMA200)
- No longs - avoids choppy bull market conditions that hurt reversal strategies
"""

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


def init_strategy():
    return {
        'name': 'eth_bear_rally_fade',
        'role': 'defensive',
        'warmup': 200,
        'subscriptions': [
            {'symbol': 'ETHUSDT', 'exchange': 'binance', 'timeframe': '4h'},
        ],
        'parameters': {
            'ema_fast': 50,
            'ema_slow': 200,
            'rsi_period': 10,
            'consecutive_bars': 3,
            'rsi_threshold': 50,
            'stop_loss_pct': 4.0,
            'take_profit_pct': 8.0,
            'max_hold_bars': 12,
            'rsi_exit': 35,
        }
    }


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

    # Warmup period for EMA200 - now handled by framework via 'warmup' field
    # if i < 200:
    #     return []

    # Extract price data
    closes = [b.close for b in bars]
    price = closes[i]
    bar = bars[i]
    prev_bar = bars[i-1]

    # Calculate EMAs for regime detection
    ema50_vals = ema(closes[:i+1], 50)
    ema200_vals = ema(closes[:i+1], 200)

    e50 = ema50_vals[-1]
    e200 = ema200_vals[-1]

    if not all([e50, e200]):
        return []

    # REGIME FILTER: Only trade in bear market
    # Bear = EMA50 below EMA200
    bear_regime = e50 < e200

    if not bear_regime:
        # Exit any positions if regime changes
        if key in positions and positions[key].side == 'short':
            return [{
                'action': 'close_short',
                'symbol': 'ETHUSDT',
                'exchange': 'binance',
            }]
        return []

    # Count consecutive higher closes (rally detection)
    # Look at bars BEFORE current bar to detect completed rally
    consecutive_higher = 0
    for j in range(i-1, max(0, i-12), -1):
        if closes[j] > closes[j-1]:
            consecutive_higher += 1
        else:
            break

    # RSI for momentum analysis
    rsi_vals = rsi(closes[:i+1], 10)
    curr_rsi = rsi_vals[-1]
    prev_rsi = rsi_vals[-2] if len(rsi_vals) > 1 else curr_rsi

    if not curr_rsi:
        return []

    # Reversal bar detection
    # Current bar is red (bearish), previous was green (bullish)
    curr_red = bar.close < bar.open
    prev_green = prev_bar.close > prev_bar.open

    actions = []

    if key not in positions:
        # ENTRY: Short rallies in bear markets
        # Conditions:
        # 1. In bear regime (EMA50 < EMA200)
        # 2. 3+ consecutive higher closes (rally)
        # 3. Current bar is red after previous green (reversal)
        # 4. RSI above 50 (elevated) and declining (exhaustion)
        # 5. Price below EMA200 (still in downtrend)

        if (consecutive_higher >= 3 and
            curr_red and prev_green and
            curr_rsi > 50 and curr_rsi < prev_rsi and
            price < e200):

            actions.append({
                'action': 'open_short',
                'symbol': 'ETHUSDT',
                'exchange': 'binance',
                'size': 1.0,
                'stop_loss_pct': 4.0,
                'take_profit_pct': 8.0,
            })

    else:
        # EXIT logic for open short positions
        pos = positions[key]
        bars_held = i - pos.entry_bar

        if pos.side == 'short':
            # Exit conditions:
            # 1. RSI oversold (< 35) - momentum exhausted downward
            # 2. Time-based exit (12 bars = 2 days on 4h timeframe)
            if curr_rsi < 35 or bars_held >= 12:
                actions.append({
                    'action': 'close_short',
                    'symbol': 'ETHUSDT',
                    'exchange': 'binance',
                })

    return actions