← Back to list

eth_multi_ema_cascade VALIDATED PASS

Auto-discovered strategy

Symbol: ETH | Exchange: Binance | Role: momentum

4/6
Profitable Years
+226.1%
Total Return
31.4%
Avg Win Rate
0.43
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 +125.7% 38.7% 31 13.6% 1.78
2021 +44.3% 28.1% 32 23.1% 0.68
2022 -5.4% 33.3% 12 16.9% -0.34
2023 -27.8% 27.8% 36 30.1% -1.41
2024 +26.1% 29.2% 24 28.5% 0.65
2025 +63.3% 31.2% 16 13.9% 1.20

Performance Chart

Loading chart...

Walk-Forward Validation PASS

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

AI Review

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

Source Code

"""
ETH Multi-EMA Cascade Strategy
==============================

A conservative momentum strategy for ETHUSDT that uses multiple EMAs
(20, 50, 100, 200) to identify strong uptrends.

Concept:
- Only enter when ALL EMAs are bullishly stacked (20 > 50 > 100 > 200)
- Price must be above all EMAs (confirmed uptrend)
- Macro filter: EMA50 > EMA200 (stay flat in bear markets)
- 5% stop loss to limit downside

Exit Conditions:
- Price closes below EMA50 (trend weakening)
- EMA stack breaks (20 < 50 or 50 < 100)
- Macro turns bearish (EMA50 < EMA200)

This strategy is designed to capture major ETH rallies while avoiding
bear markets. It will stay flat when the trend structure is unclear.

Role: momentum - Expected to profit in uptrends, may lose (bounded) in bear
"""

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


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


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

    # Extract closing prices up to current bar
    closes = [b.close for b in bars[:i+1]]

    # Calculate EMAs with standard periods (round numbers only)
    e20 = ema(closes, 20)
    e50 = ema(closes, 50)
    e100 = ema(closes, 100)
    e200 = ema(closes, 200)

    # Safety check
    if e20[i] is None or e50[i] is None or e100[i] is None or e200[i] is None:
        return []

    current_close = closes[i]

    # Entry conditions (all must be true):
    # 1. Price above all EMAs
    price_above_all = (
        current_close > e20[i] and
        current_close > e50[i] and
        current_close > e100[i] and
        current_close > e200[i]
    )

    # 2. EMAs bullishly stacked (faster above slower)
    bullish_stack = e20[i] > e50[i] > e100[i] > e200[i]

    # 3. Macro trend filter (avoid bear markets)
    macro_bullish = e50[i] > e200[i]

    actions = []

    if key not in positions:
        # ENTRY: All conditions must align
        if price_above_all and bullish_stack and macro_bullish:
            actions.append({
                'action': 'open_long',
                'symbol': 'ETHUSDT',
                'exchange': 'binance',
                'size': 1.0,
                'stop_loss_pct': 5,  # Fixed 5% stop loss
            })
    else:
        # EXIT conditions (any one triggers exit):
        # 1. Price closes below EMA50 (intermediate support broken)
        price_below_mid = current_close < e50[i]

        # 2. EMA stack partially broken (trend weakening)
        stack_broken = not (e20[i] > e50[i] > e100[i])

        # 3. Macro turns bearish
        if price_below_mid or stack_broken or not macro_bullish:
            actions.append({
                'action': 'close_long',
                'symbol': 'ETHUSDT',
                'exchange': 'binance',
            })

    return actions


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