← Back to list

doge_bear_protector VALIDATED PASS

Auto-discovered strategy

Symbol: DOGE | Exchange: Binance | Role: defensive

3/6
Profitable Years
-27.4%
Total Return
29.9%
Avg Win Rate
-0.27
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 +5.1% 33.3% 6 7.2% 0.43
2021 -53.9% 20.0% 15 44.8% -1.78
2022 -3.7% 36.4% 11 27.6% -0.18
2023 -12.3% 22.2% 9 15.3% -1.20
2024 +9.7% 30.0% 10 14.4% 0.37
2025 +27.7% 37.5% 16 20.5% 0.75

Performance Chart

Loading chart...

Walk-Forward Validation PASS

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

AI Review Score: 35/100

overfitting inconsistent concentration
## Defensive Strategy Review: doge_bear_protector ### Critical Issues **1. Severe Year-to-Year Inconsistency (Major Red Flag)** The strategy shows extreme performance variance across different years: - One year: -53.9% (massive loss) - Another year: 27.7% (strong gain) - Sharpe ratios swing wildly from -1.78 to +0.75 - Max drawdown of 44.8% in one year violates the 30% defensive role limit This pattern indicates the strategy is **highly regime-specific** rather than robust. A defensive strategy must provide consistent downside protection across multiple market environments, not just work in one favorable period while catastrophically failing in others. **2. Complex Entry Filter Stack (6+ Conditions)** The entry logic chains together 6 distinct conditions: 1. `bear_bars >= 20` (sustained regime check) 2. `ema50 < ema100 < ema200` (cascade) 3. 20-bar low calculation 4. `closes[i] < low_20` (breakdown) 5. `30 < rsi < 50` (specific RSI range) 6. No existing position check This violates the "no more than 5-6 entry conditions" guideline and creates a **highly specific pattern** that may not recur. Each additional filter exponentially reduces trade frequency and increases curve-fitting risk. **3. Magic Number Parameters** Several non-standard values appear: - `bear_bars >= 20` - why exactly 20 consecutive bars? - `30 < rsi < 50` - narrow RSI band suggests parameter optimization - `stop_pct = max(4, min(..., 8))` - 4% and 8% bounds seem fitted - `take_profit_pct = stop_pct * 2.5` - why 2.5x specifically? - 20-bar lookback for low - not a standard value (should be 20, which it is, but combined with other lookbacks creates complexity) While individual values may be reasonable, the **combination** suggests these were tuned together to fit historical patterns. **4. RSI Range Filtering** The condition `30 < rsi_vals[i] < 50` is a narrow band that likely emerged from backtesting to avoid losses. This type of "sweet spot" filtering is a classic overfitting pattern - it works great on past data but fragile to market changes. ### Minor Issues **5. Consecutive Regime Counter Logic** The `bear_bars` counter resets to zero on any single bar break, requiring an uninterrupted 20-bar sequence: ```python for j in range(max(0, i-50), i+1): if ema50[j] < ema200[j]: bear_bars += 1 else: bear_bars = 0 # Hard reset ``` This makes the strategy hypersensitive to brief noise and may miss valid downtrends with minor interruptions. **6. Role Mismatch Potential** While classified as "defensive," the strategy: - Shows 44.8% drawdown in one year (far exceeds 30% limit) - Has negative overall return (-27.4%) - Only profits in specific bear market conditions True defensive strategies should **consistently** protect capital across various environments, not just succeed in ideal downtrends. ### What's Actually Good - Uses only relative indicators (EMAs, ATR, RSI) - No specific price levels or dates - Executes on next bar (realistic) - Clear documentation - Proper warmup period (200 bars) - EMA cascade concept is sound ### Bottom Line This strategy likely **overfit to one or two favorable years** in the training data where extended DOGE bear markets aligned perfectly with the narrow entry criteria. The catastrophic -53.9% year and extreme Sharpe volatility prove it's not robust. The validation period success appears to be **luck matching one regime** rather than genuine edge. A defensive strategy must be consistent - this one is a coin flip depending on market structure.
Reviewed: 2026-01-14T09:24:22.888647

Source Code

"""
DOGE Bear Protector - Defensive Short Strategy
===============================================

A defensive strategy that profits during bear markets by shorting DOGE
when strong downtrend conditions are confirmed.

Strategy Concept:
- ONLY trades SHORT positions (never long)
- Requires sustained bear regime (EMA50 < EMA200 for 20+ bars)
- Uses EMA cascade confirmation (EMA50 < EMA100 < EMA200)
- Enters on 20-bar low breakdowns with RSI weakness (30-50)
- Tight risk management with ATR-based stops

Why This Works:
- Defensive strategies need to profit when markets decline
- Multiple confirmation filters reduce false signals
- Sustained regime requirement avoids choppy conditions
- RSI filter avoids oversold bounce traps

Role: defensive
- Must profit during validation (bear market period)
- Max drawdown < 30%
- Designed to hedge long portfolios during downtrends
"""

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


def init_strategy():
    return {
        'name': 'doge_bear_protector',
        'role': 'defensive',
        'warmup': 200,
        'subscriptions': [
            {'symbol': 'DOGEUSDT', 'exchange': 'binance', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


def simple_rsi(prices, period=14):
    """Calculate RSI indicator"""
    if len(prices) < period + 1:
        return [None] * len(prices)
    result = [None] * period
    gains, losses = [], []
    for i in range(1, len(prices)):
        change = prices[i] - prices[i-1]
        gains.append(max(0, change))
        losses.append(max(0, -change))
    avg_gain = sum(gains[:period]) / period
    avg_loss = sum(losses[:period]) / period
    for i in range(period, len(gains)):
        avg_gain = (avg_gain * (period - 1) + gains[i]) / period
        avg_loss = (avg_loss * (period - 1) + losses[i]) / period
        if avg_loss == 0:
            result.append(100)
        else:
            result.append(100 - (100 / (1 + avg_gain / avg_loss)))
    return result


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

    # Extract price series
    closes = [b.close for b in bars]
    highs = [b.high for b in bars]
    lows = [b.low for b in bars]

    # Compute indicators
    ema50 = ema(closes, 50)
    ema100 = ema(closes, 100)
    ema200 = ema(closes, 200)
    atr_vals = atr(highs, lows, closes, 14)
    rsi_vals = simple_rsi(closes, 14)

    # Safety checks
    if ema50[i] is None or ema100[i] is None or ema200[i] is None:
        return []
    if atr_vals[i] is None or rsi_vals[i] is None:
        return []

    actions = []

    # Count consecutive bear bars (EMA50 < EMA200)
    bear_bars = 0
    for j in range(max(0, i-50), i+1):
        if ema50[j] is not None and ema200[j] is not None and ema50[j] < ema200[j]:
            bear_bars += 1
        else:
            bear_bars = 0  # Reset if regime changes

    # Entry conditions
    is_sustained_bear = bear_bars >= 20  # Sustained bearish regime
    cascade = ema50[i] < ema100[i] < ema200[i]  # Full EMA cascade
    low_20 = min(lows[i-20:i])  # 20-bar low
    breakdown = closes[i] < low_20  # Price breaks below support
    rsi_weak = 30 < rsi_vals[i] < 50  # Weak but not oversold

    if key not in positions:
        # SHORT entry: All conditions must align
        if is_sustained_bear and cascade and breakdown and rsi_weak:
            # ATR-based stop loss (2x ATR, clamped 4-8%)
            stop_pct = max(4, min((atr_vals[i] * 2) / closes[i] * 100, 8))

            actions.append({
                'action': 'open_short',
                'symbol': 'DOGEUSDT',
                'exchange': 'binance',
                'size': 1.0,
                'stop_loss_pct': stop_pct,
                'take_profit_pct': stop_pct * 2.5,  # 2.5:1 reward/risk
            })
    else:
        pos = positions[key]
        if pos.side == 'short':
            # Exit conditions
            regime_change = ema50[i] > ema200[i]  # Bull regime
            price_recovery = closes[i] > ema50[i]  # Price above EMA50
            rsi_oversold = rsi_vals[i] < 20  # Bounce likely

            if regime_change or price_recovery or rsi_oversold:
                actions.append({
                    'action': 'close_short',
                    'symbol': 'DOGEUSDT',
                    'exchange': 'binance',
                })

    return actions


# For testing
if __name__ == '__main__':
    from strategy import backtest_strategy
    results, profitable, _ = backtest_strategy(init_strategy, process_time_step)