Auto-discovered strategy
Symbol: ETH | Exchange: Binance | Role: momentum
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 |
| 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 |
Not yet reviewed. Run: ./review_strategy.sh eth_multi_ema_cascade
"""
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)