Auto-discovered strategy
Symbol: ETH | Exchange: Binance | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +53.3% | 46.2% | 52 | 20.7% | 1.18 |
| 2021 | +43.1% | 52.8% | 36 | 19.3% | 1.25 |
| 2022 | +0.3% | 46.2% | 13 | 17.2% | 0.01 |
| 2023 | -24.9% | 34.6% | 26 | 28.4% | -1.44 |
| 2024 | +16.8% | 44.0% | 25 | 29.3% | 0.59 |
| 2025 | +38.7% | 45.8% | 24 | 19.6% | 1.35 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | -4.0% | OK | 2026-01→ongoing | +0.0% | PASS |
Not yet reviewed. Run: ./review_strategy.sh eth_ema_momentum_breakout
"""
ETH EMA Momentum Breakout Strategy
===================================
Momentum continuation strategy for ETHUSDT that trades breakouts
in confirmed uptrends with multiple confirmation signals.
CONCEPT:
In strong bull markets (EMA50 > EMA200), when price shows momentum
continuation through consecutive higher closes AND breaks above
recent highs with good volume, this signals trend continuation.
ENTRY CONDITIONS (8 total):
1. Bull regime: EMA50 > EMA200 (macro trend filter)
2. EMA alignment: EMA20 > EMA50 (short-term trend strong)
3. Price above EMA20 (momentum intact)
4. Momentum: 2 consecutive higher closes
5. Breakout: Close above 5-bar high
6. Bar quality: Range > 0.7x ATR (not a doji)
7. Volume: Above 90% of 20-bar average
8. Bullish bar: Close > Open
9. EMA50 rising (trend confirmation)
EXIT CONDITIONS:
1. Close below EMA50 (trend weakening)
2. EMA20 crosses below EMA50 (bearish cross)
3. Time exit: 20 bars maximum hold
4. Stop loss: 5%
5. Take profit: 9%
TRAIN RESULTS (2024-01 to 2025-06):
2024: +16.8% | ~24 trades | Sharpe ~0.6
2025H1: +13.4% | ~8 trades | Sharpe ~0.7
Total: +30.1%
ROBUSTNESS FEATURES:
- Uses only relative indicators (EMAs, ATR, volume ratios)
- Round parameters: 5, 20, 50, 200, 5%, 9%
- Bull-market filter prevents trades in downtrends
- Multiple confirmation signals reduce false breakouts
- Balanced TP/SL for consistent risk/reward
Symbol: ETHUSDT
Exchange: binance
Timeframe: 4h
Role: momentum
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, sma, atr
def init_strategy():
return {
'name': 'eth_ema_momentum_breakout',
'role': 'momentum',
'warmup': 200, # EMA200 needs 200 bars
'subscriptions': [
{'symbol': 'ETHUSDT', 'exchange': 'binance', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
"""
Process each time step and return trading actions.
Entry Logic:
- Bull regime (EMA50 > EMA200)
- EMA cascade alignment (EMA20 > EMA50)
- Price above EMA20
- Momentum: 2 consecutive higher closes
- Breakout above 5-bar high
- Bar quality check (range > 0.7x ATR)
- Volume confirmation
- Bullish close
- EMA50 rising
Exit Logic:
- Close below EMA50
- EMA20 crosses below EMA50
- Time exit (20 bars)
- Stop loss 5%, Take profit 9%
"""
key = ('ETHUSDT', 'binance')
bars = ctx['bars'].get(key, [])
i = ctx['i']
positions = ctx['positions']
if not bars or i >= len(bars):
return []
closes = [b.close for b in bars]
highs = [b.high for b in bars]
lows = [b.low for b in bars]
volumes = [b.volume for b in bars]
# Calculate indicators
ema20 = ema(closes, 20)
ema50 = ema(closes, 50)
ema200 = ema(closes, 200)
atr_vals = atr(highs, lows, closes, 20)
vol_ma = sma(volumes, 20)
# Ensure all indicators are valid
if any(x[i] is None for x in [ema20, ema50, ema200, atr_vals, vol_ma]):
return []
actions = []
has_position = key in positions
if not has_position:
# ==================== ENTRY CONDITIONS ====================
# 1. REGIME: Bull market (EMA50 > EMA200)
bull_regime = ema50[i] > ema200[i]
if not bull_regime:
return []
# 2. EMA ALIGNMENT: EMA20 > EMA50
ema_aligned = ema20[i] > ema50[i]
if not ema_aligned:
return []
# 3. PRICE ABOVE EMA20
above_ema = closes[i] > ema20[i]
if not above_ema:
return []
# 4. MOMENTUM: 2 consecutive higher closes
if i < 2:
return []
momentum = closes[i] > closes[i-1] > closes[i-2]
if not momentum:
return []
# 5. BREAKOUT: Close above 5-bar high
high_5 = max(highs[max(i-5, 0):i])
breakout = closes[i] > high_5
if not breakout:
return []
# 6. BAR QUALITY: Range > 0.7x ATR (not a doji)
bar_range = highs[i] - lows[i]
decent_bar = bar_range > atr_vals[i] * 0.7
if not decent_bar:
return []
# 7. VOLUME: Above 90% of 20-bar average
vol_ok = volumes[i] > vol_ma[i] * 0.9
if not vol_ok:
return []
# 8. BULLISH BAR: Close > Open
bullish = bars[i].close > bars[i].open
if not bullish:
return []
# 9. EMA50 RISING (over 10 bars)
ema50_rising = ema50[i] > ema50[i-10] if i >= 10 else False
if not ema50_rising:
return []
# All conditions met - ENTER LONG
actions.append({
'action': 'open_long',
'symbol': 'ETHUSDT',
'exchange': 'binance',
'size': 1.0,
'stop_loss_pct': 5.0, # 5% stop loss
'take_profit_pct': 9.0, # 9% take profit
})
else:
# ==================== EXIT CONDITIONS ====================
pos = positions[key]
bars_held = i - pos.entry_bar
# 1. Close below EMA50 (trend weakening)
below_ema50 = closes[i] < ema50[i]
# 2. EMA20 crosses below EMA50 (bearish cross)
bearish_cross = ema20[i] < ema50[i]
# 3. Time exit: 20 bars maximum hold
time_exit = bars_held >= 20
if below_ema50 or bearish_cross or time_exit:
actions.append({
'action': 'close_long',
'symbol': 'ETHUSDT',
'exchange': 'binance',
})
return actions
# Allow direct execution for testing
if __name__ == "__main__":
from strategy import backtest_strategy
results, profitable, _ = backtest_strategy(init_strategy, process_time_step, verbose=True)