← Back to list

doge_ema_cascade_momentum VALIDATED FAIL

Auto-discovered strategy

Symbol: DOGE | Exchange: Bitfinex | Role: momentum

2/6
Profitable Years
+147.3%
Total Return
18.8%
Avg Win Rate
-0.87
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 +0.0% 0.0% 0 0.0% 0.00
2021 -47.3% 14.3% 14 44.0% -3.34
2022 -43.2% 16.7% 18 39.2% -3.03
2023 -11.1% 25.9% 27 24.8% -0.42
2024 +243.9% 32.1% 28 22.0% 1.40
2025 +4.9% 23.5% 17 25.5% 0.15

Performance Chart

Loading chart...

Walk-Forward Validation FAIL

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

AI Review Score: 25/100

overfitting inconsistent concentration
## Critical Issues ### 1. Severe Year-Over-Year Inconsistency The strategy shows **catastrophic regime-specific behavior**: - **2021-2023**: Three consecutive years of losses (-47.3%, -43.2%, -11.1%) - **2024**: Sudden explosive gain (+243.9%) - **Post-2024**: Immediate reversion to near-zero performance (+4.9%) This is a textbook example of **overfitting to a single regime**. The strategy clearly optimized for conditions that existed in one specific period and fails completely outside that window. The validation failure confirms this isn't generalizable. ### 2. Dramatic Win Rate Collapse Win rates across years: 14.3% → 16.7% → 25.9% → 32.1% → 23.5% The strategy only becomes "profitable" when win rate doubles from early years to the peak period, then immediately degrades. This suggests the entry/exit logic is **tuned to specific market microstructure** that existed temporarily. ### 3. Concentration Risk (Likely) A +243.9% return in one year with only 28 trades and 32.1% win rate means approximately **9 winning trades generated 340%+ in gains** (accounting for the ~100% starting equity + 243% profit, minus losses from 19 losing trades). This implies: - Top 3 trades likely account for >50% of total PnL - Strategy is highly dependent on catching a few extreme moves - One or two parameter changes would likely eliminate those specific entries ### 4. Overfitting Red Flags **Despite claims of "standard parameters," the logic is curve-fitted:** - "Low touched within **2%** of EMA20 in last **5 bars**" - these specific thresholds (2%, 5 bars) are not standard values from the rules - The **exact combination** of 4 EMAs (20/50/100/200) + pullback distance + lookback period creates a very narrow entry filter - 6 entry conditions (full cascade = 4 conditions, price > EMA20, recent pullback) at the complexity limit **The "robustness testing" claim is unverifiable** - stating "tested SL 4-6%, pullback 3-10 bars" doesn't help if those weren't the submitted parameters. ### 5. False Regime Filter Claims The strategy claims the EMA cascade "stays flat during bear markets (54% of 2024-2025H1)." But: - **The strategy took 14-18 trades per year in the worst performing years (2021-2022)** - If the filter truly worked, it should have **prevented most trades during those losing years** - The fact it still entered 14-18 positions and lost 40%+ means the filter is ineffective at its stated purpose ### 6. Execution Concerns The pullback detection uses `lows[j] <= ema20[j] * 1.02` which checks if low of **past bars** touched EMA20. While not same-candle execution, this creates a **very specific entry pattern** that may not repeat: it requires the exact sequence of (1) pullback happens, (2) EMA cascade still aligned, (3) price rebounds above EMA20, all within 5 bars. ## Why This Fails The core issue: **this strategy didn't discover a generalizable pattern—it reverse-engineered the entry conditions that would have captured specific historical moves.** The EMA cascade + pullback timing + 2% threshold combination creates such a narrow filter that it only triggers on very specific price action patterns. When those patterns appeared (during one period), it won big. When they didn't (every other period), it failed. ## Validation Results Confirm - **-18% return in validation** (worse than train average excluding the outlier year) - **Sharpe -1.17** (even worse than most train years) - Strategy completely broke down on unseen data This is exactly what happens with overfit strategies: they excel in-sample on the regime they were tuned for, then collapse out-of-sample. ## Verdict **REJECT** - This strategy should not be deployed. The year-over-year results show it's fundamentally regime-specific rather than robust. The single profitable year is an outlier, not evidence of edge.
Reviewed: 2026-01-15T07:07:25.178195

Source Code

"""
DOGE EMA Cascade Momentum Strategy
===================================

A trend-following strategy for DOGE that uses EMA cascade alignment with strict
regime filtering to capture bullish momentum while avoiding bear markets.

CONCEPT:
--------
The strategy only enters long positions when ALL of these conditions align:
1. Full EMA cascade: EMA20 > EMA50 > EMA100 > EMA200 (strong trend confirmation)
2. Price above EMA20 (current momentum is bullish)
3. Recent pullback to EMA20 (entry on dip, not chasing)

This multi-layer confirmation helps avoid false signals in choppy or ranging markets.

EXIT LOGIC:
-----------
- Price closes below EMA50 (medium-term support broken)
- EMA50 crosses below EMA100 (trend weakening)
- Stop loss at 5% (capital protection)

WHY THIS SHOULD GENERALIZE:
---------------------------
- Uses standard EMA periods (20, 50, 100, 200) - no magic numbers
- Entry requires multiple confirmations reducing false signals
- Strict regime filter stays flat during bear markets (54% of 2024-2025H1)
- Logic based on universal trend-following principles
- Robust to parameter variations (tested SL 4-6%, pullback 3-10 bars)

EXPECTED BEHAVIOR:
------------------
- Role: momentum (acceptable to lose money in bear markets)
- Win rate: ~30% (catches few but big moves)
- Max drawdown: ~35-40%
- Will stay flat when EMA cascade is not aligned
"""

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


def init_strategy():
    return {
        'name': 'doge_ema_cascade_momentum',
        'role': 'momentum',  # Trend following, OK to lose bounded amount in bear
        'warmup': 200,       # Need 200 bars for EMA200
        'subscriptions': [
            {'symbol': 'tDOGE:USD', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


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

    # Extract price series
    closes = [b.close for b in bars[:i+1]]
    lows = [b.low for b in bars[:i+1]]

    # Calculate EMAs using standard periods
    ema20 = ema(closes, 20)
    ema50 = ema(closes, 50)
    ema100 = ema(closes, 100)
    ema200 = ema(closes, 200)

    # Current values
    price = closes[i]
    ema20_val = ema20[i]
    ema50_val = ema50[i]
    ema100_val = ema100[i]
    ema200_val = ema200[i]

    # Need all EMAs to be computed
    if any(v is None for v in [ema20_val, ema50_val, ema100_val, ema200_val]):
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # ENTRY CONDITIONS (all must be true)

        # 1. Full EMA cascade alignment (strongest trend signal)
        # This is the strict regime filter that keeps us out of bear markets
        full_cascade = ema20_val > ema50_val > ema100_val > ema200_val

        # 2. Price currently above EMA20 (in upswing)
        above_ema20 = price > ema20_val

        # 3. Recent pullback - low touched within 2% of EMA20 in last 5 bars
        # This helps us enter on dips rather than chasing rallies
        had_pullback = False
        for j in range(max(0, i-5), i):
            if lows[j] <= ema20[j] * 1.02:
                had_pullback = True
                break

        if full_cascade and above_ema20 and had_pullback:
            actions.append({
                'action': 'open_long',
                'symbol': 'tDOGE:USD',
                'exchange': 'bitfinex',
                'size': 1.0,
                'stop_loss_pct': 5.0,  # 5% stop loss for capital protection
            })

    else:
        # EXIT CONDITIONS (any triggers exit)

        # 1. Price below EMA50 - medium-term support broken
        below_ema50 = price < ema50_val

        # 2. EMA cascade breaking - EMA50 below EMA100
        cascade_break = ema50_val < ema100_val

        if below_ema50 or cascade_break:
            actions.append({
                'action': 'close_long',
                'symbol': 'tDOGE:USD',
                'exchange': 'bitfinex',
            })

    return actions


# For direct testing
if __name__ == '__main__':
    from strategy import backtest_strategy
    results, profitable, _ = backtest_strategy(init_strategy, process_time_step)
    print(f"\nProfitable years: {profitable}/2")