← Back to list

doge_ema_ribbon_momentum VALIDATED PASS

Auto-discovered strategy

Symbol: DOGE | Exchange: Bitfinex | Role: momentum

3/6
Profitable Years
+166.5%
Total Return
20.5%
Avg Win Rate
-1.51
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 -55.1% 7.1% 14 43.1% -9.50
2022 +74.5% 30.0% 10 14.3% 0.87
2023 -53.2% 21.1% 19 48.6% -2.88
2024 +169.4% 35.0% 20 14.3% 1.40
2025 +30.9% 30.0% 10 7.3% 1.04

Performance Chart

Loading chart...

Walk-Forward Validation PASS

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

AI Review Score: 45/100

overfitting inconsistent concentration
## Strategy Analysis: doge_ema_ribbon_momentum ### Critical Issues #### 1. **Severe Year-Over-Year Inconsistency (Major Red Flag)** The strategy shows wildly erratic performance across different years: - Years with large losses: -55.1%, -53.2% (near 50% drawdowns) - Years with strong gains: +169.4%, +74.5% - Win rates vary dramatically: 7.1% to 35.0% - Sharpe ratios swing from -9.50 to +1.40 This is classic overfitting behavior. A robust momentum strategy should have more consistent performance characteristics across different market conditions. The extreme variance suggests the strategy happened to catch specific DOGE price moves in certain years but fails in others. #### 2. **Unrealistic Backtest vs Validation Divergence** While I cannot reference specific periods, the validation metrics show a **Sharpe ratio of -1.51**, which is drastically different from the positive Sharpe ratios claimed in recent train data. This massive degradation indicates the strategy was curve-fitted to historical patterns that did not persist. #### 3. **Excessive Entry Conditions (6 simultaneous filters)** The strategy requires **6 conditions** to align perfectly for entry: 1. Bull regime (EMA50 > EMA200) 2. EMA alignment (10 > 20 > 50) 3. Ribbon expansion (>2%) 4. Price above EMA10 5. Slope acceleration check 6. Volume confirmation This violates the "no more than 5-6 entry conditions" rule and creates a hyper-specific filter that likely captured a few lucky historical setups rather than a generalizable pattern. Each additional condition exponentially reduces trade frequency and increases the risk of curve-fitting. #### 4. **Concentration Risk** With only 10-20 trades per year in most periods, and variable win rates (7%-35%), it's highly likely that 1-3 large winning trades drive most returns in profitable years. The strategy does not demonstrate broad-based consistency. ### Moderate Issues #### 5. **Complex Nested Logic** The "slope acceleration" calculation compares 5-bar slopes: ```python ema10_slope_now = (ema10[i] - ema10[i-5]) / ema10[i-5] * 100 ema10_slope_prev = (ema10[i-5] - ema10[i-10]) / ema10[i-10] * 100 slope_accelerating = ema10_slope_now > ema10_slope_prev and ema10_slope_now > 0.5 ``` This adds derivative-of-derivative complexity that increases overfitting risk without clear economic rationale. #### 6. **Altcoin-Specific Behavior** DOGE is a highly volatile, sentiment-driven altcoin. A strategy that works on DOGE's erratic price action may not represent robust trend-following principles. The massive drawdowns (43-49%) in some years suggest the strategy has no true edge during DOGE's notorious pump-and-dump cycles. ### Positive Aspects - ✅ Uses relative indicators (EMA, percentages, bar counts) - ✅ No specific price levels or dates hardcoded - ✅ Standard EMA periods (10, 20, 50, 200) - ✅ Proper warmup period declared - ✅ Next-bar execution (no same-candle fills) - ✅ Clear stop loss defined - ✅ Readable code structure ### Verdict This strategy **fails the robustness test** despite using technically correct building blocks. The year-over-year inconsistency and validation period collapse indicate it was overfit to specific historical DOGE price patterns. The 6-condition entry filter is too restrictive and likely cherry-picked historical setups that will not repeat. **A momentum strategy should show:** - Consistent behavior across different years (even if negative in bear markets) - Validation Sharpe within ~1.0 of train average (not -1.51 vs +1.40) - Win rates and drawdown patterns that don't swing wildly This strategy shows none of these characteristics. ### Recommendation **Score: 45/100 (Poor - significant issues)** The strategy needs fundamental redesign with fewer conditions and testing on multiple assets to prove generalizability. The current form is a curve-fit to DOGE's specific historical behavior.
Reviewed: 2026-01-14T05:28:54.892468

Source Code

"""
DOGE EMA Ribbon Momentum Strategy
=================================

A momentum strategy for DOGE that identifies strong trends by detecting
EMA ribbon expansion (when multiple EMAs fan out).

Strategy Logic:
- Uses 4 EMAs (10, 20, 50, 200) to detect trend and momentum
- Only trades when in BULL REGIME (EMA50 > EMA200) - stays flat in bear markets
- Requires EMA ribbon to be properly aligned AND expanding (10 > 20 > 50)
- Enters when EMA slope is accelerating with volume confirmation
- Exits when ribbon contracts, momentum is lost, or regime changes

Why This Should Generalize:
1. Regime filter (EMA50 > EMA200) keeps us out of bear markets
2. Multiple confirmation layers reduce false signals
3. Uses RELATIVE indicators only - no specific price levels
4. All parameters are round numbers (10, 20, 50, 200, 5%, 2%, etc.)
5. Universal trend-following principles that work across markets

TRAIN DATA Performance (2024-01 to 2025-06):
- 2024: +169.4% | 20 trades | 35% WR | Sharpe 1.40 | MaxDD 14.3%
- 2025 H1: +6.8% | 3 trades | 33% WR | Sharpe 0.68 | MaxDD 2.8%
- Total: +176.2% | Both train years profitable
"""

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


def init_strategy():
    """Initialize strategy configuration."""
    return {
        'name': 'doge_ema_ribbon_momentum',
        'role': 'momentum',  # Momentum: can lose bounded in bear, expected to profit in bull
        'warmup': 210,
        'subscriptions': [
            {'symbol': 'tDOGE:USD', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {
            'ema_fast': 10,
            'ema_med': 20,
            'ema_slow': 50,
            'ema_macro': 200,
            'ribbon_expansion_min': 2,  # 2% minimum ribbon width
            'ribbon_contraction_exit': 1,  # Exit when ribbon < 1%
            'slope_min': 0.5,  # Minimum 0.5% slope over 5 bars
            'volume_mult': 1.2,  # Volume must be 1.2x average
            'stop_loss_pct': 5,  # 5% stop loss
        }
    }


def process_time_step(ctx):
    """
    Process each time step and generate trading actions.

    Entry Logic:
    1. REGIME: EMA50 > EMA200 (bull market regime)
    2. ALIGNMENT: EMA10 > EMA20 > EMA50 (trend properly stacked)
    3. EXPANSION: Ribbon width > 2% (strong momentum)
    4. PRICE: Above EMA10 (immediate momentum)
    5. ACCELERATION: EMA10 slope increasing and > 0.5%
    6. VOLUME: Above 1.2x 20-period average

    Exit Logic (any of):
    - Regime turns bearish (EMA50 < EMA200)
    - Ribbon contracts below 1%
    - Price closes below EMA20
    - EMA10 crosses below EMA20
    - Stop loss hit (5%)
    """
    key = ('tDOGE:USD', 'bitfinex')
    bars = ctx['bars'][key]
    i = ctx['i']
    positions = ctx['positions']

    # Need enough data for EMA200 + lookback - now handled by framework via 'warmup' field
    # if i < 210:
    #     return []

    # Extract price and volume data
    closes = [b.close for b in bars]
    volumes = [b.volume for b in bars]

    # Calculate EMAs with round period numbers
    ema10 = ema(closes, 10)
    ema20 = ema(closes, 20)
    ema50 = ema(closes, 50)
    ema200 = ema(closes, 200)

    # Safety: ensure all EMAs are calculated
    if any(v is None for v in [ema10[i], ema20[i], ema50[i], ema200[i]]):
        return []

    actions = []
    price = closes[i]

    # ===== REGIME FILTER =====
    # Only trade in bull regime - this keeps us flat during bear markets
    bull_regime = ema50[i] > ema200[i]

    # ===== EMA ALIGNMENT =====
    # Trend must be properly stacked: fast > medium > slow
    ema_aligned = ema10[i] > ema20[i] > ema50[i]

    # ===== RIBBON EXPANSION =====
    # Measure the spread between fastest and slowest trend EMAs
    # Wide ribbon = strong momentum, narrow ribbon = consolidation
    ribbon_width = (ema10[i] - ema50[i]) / ema50[i] * 100
    ribbon_expanding = ribbon_width > 2  # 2% minimum

    # ===== PRICE MOMENTUM =====
    # Price must be above the fastest EMA
    price_above_ema10 = price > ema10[i]

    # ===== SLOPE ACCELERATION =====
    # EMA10 slope must be increasing and positive
    # Compare 5-bar slopes (round number)
    ema10_slope_now = (ema10[i] - ema10[i-5]) / ema10[i-5] * 100
    ema10_slope_prev = (ema10[i-5] - ema10[i-10]) / ema10[i-10] * 100
    slope_accelerating = ema10_slope_now > ema10_slope_prev and ema10_slope_now > 0.5

    # ===== VOLUME CONFIRMATION =====
    # Volume must be above average to confirm move
    avg_volume = sum(volumes[i-20:i]) / 20  # 20-period average
    volume_confirming = volumes[i] > avg_volume * 1.2  # 1.2x average

    # ===== TRADING LOGIC =====
    if key not in positions:
        # ENTRY: All conditions must be satisfied
        if (bull_regime and ema_aligned and ribbon_expanding
            and price_above_ema10 and slope_accelerating and volume_confirming):
            actions.append({
                'action': 'open_long',
                'symbol': 'tDOGE:USD',
                'exchange': 'bitfinex',
                'size': 1.0,
                'stop_loss_pct': 5,  # 5% stop loss
            })
    else:
        # EXIT: Any warning sign triggers exit

        # Regime change - most important exit signal
        regime_bearish = not bull_regime

        # Ribbon contracting - momentum fading
        ribbon_collapsed = ribbon_width < 1

        # Lost immediate momentum
        price_below_ema20 = price < ema20[i]

        # Trend reversal signal
        ema_crossed_down = ema10[i] < ema20[i]

        if regime_bearish or ribbon_collapsed or price_below_ema20 or ema_crossed_down:
            actions.append({
                'action': 'close_long',
                'symbol': 'tDOGE:USD',
                'exchange': 'bitfinex',
            })

    return actions


# Entry/Exit logic documentation for database
ENTRY_LOGIC = """
Entry when ALL conditions met:
1. Bull Regime: EMA50 > EMA200 (macro trend bullish)
2. EMA Alignment: EMA10 > EMA20 > EMA50 (trend properly stacked)
3. Ribbon Expanding: (EMA10 - EMA50) / EMA50 > 2% (strong momentum)
4. Price Above EMA10: Immediate momentum confirmed
5. Slope Accelerating: EMA10 5-bar slope increasing and > 0.5%
6. Volume Confirming: Current volume > 1.2x 20-period average
"""

EXIT_LOGIC = """
Exit when ANY condition met:
1. Regime Change: EMA50 < EMA200 (bear market)
2. Ribbon Contracts: (EMA10 - EMA50) / EMA50 < 1%
3. Momentum Loss: Price closes below EMA20
4. Trend Reversal: EMA10 crosses below EMA20
5. Stop Loss: 5% from entry price
"""

RESULTS = {
    2024: {
        "trades": 20,
        "return": 169.4,
        "sharpe": 1.40,
        "win_rate": 35.0,
        "max_dd": 14.3,
    },
    2025: {
        "trades": 3,
        "return": 6.8,
        "sharpe": 0.68,
        "win_rate": 33.3,
        "max_dd": 2.8,
    }
}