Auto-discovered strategy
Symbol: ETH | Exchange: Bitfinex | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +20.2% | 44.0% | 25 | 13.3% | 0.73 |
| 2021 | +12.0% | 37.0% | 27 | 25.2% | 0.40 |
| 2022 | -20.0% | 18.2% | 11 | 25.2% | -1.30 |
| 2023 | -14.4% | 22.2% | 18 | 20.0% | -0.71 |
| 2024 | +15.2% | 38.9% | 18 | 28.4% | 0.62 |
| 2025 | -0.8% | 31.2% | 16 | 22.4% | -0.04 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | -12.8% | FAIL | 2026-01→ongoing | +0.0% | FAIL |
"""
ETH EMA Alignment Momentum Strategy
====================================
A momentum strategy that enters when ETH price and EMAs are fully aligned
in an uptrend, with balanced risk/reward through capped gains.
CONCEPT:
Uses a 4-point EMA alignment score to identify strong uptrends:
- Price > EMA20 (+1)
- EMA20 > EMA50 (+1)
- EMA50 > EMA100 (+1)
- EMA100 > EMA200 (+1)
ENTRY CONDITIONS:
- Perfect alignment score (4/4) just formed
- EMA50 slope positive (>0.5% over 20 bars)
- Price not overextended (<8% above EMA50)
EXIT CONDITIONS:
- Take profit: 8% (capped to distribute gains)
- Stop loss: 4%
- Signal: Alignment score ≤ 2 OR EMA50 slope negative
ROBUSTNESS FEATURES:
- Uses only relative indicators (EMAs, slopes, percentages)
- Round parameter values only (4, 8, 20, 50, 100, 200)
- Regime filter prevents trading in downtrends
- Capped TP prevents over-reliance on few big winners
Train Results (2024-2025H1):
- 2024: +15.2% | 18 trades | 39% WR
- 2025 (partial): +12.0% | 6 trades | 50% WR
- Total: +27.2% | Max DD: 28.4%
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema
def init_strategy():
return {
'name': 'eth_ema_alignment_balanced',
'role': 'momentum',
'warmup': 200,
'subscriptions': [
{'symbol': 'tETHUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
"""
Process each time step to generate trading signals.
Uses EMA alignment score and slope confirmation for entries,
with capped TP/SL for balanced returns.
"""
key = ('tETHUSD', 'bitfinex')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
state = ctx['state']
actions = []
# Calculate EMAs
closes = [b.close for b in bars[:i+1]]
ema20 = ema(closes, 20)
ema50 = ema(closes, 50)
ema100 = ema(closes, 100)
ema200 = ema(closes, 200)
price = bars[i].close
e20 = ema20[-1]
e50 = ema50[-1]
e100 = ema100[-1]
e200 = ema200[-1]
# Need all EMAs calculated
if None in [e20, e50, e100, e200] or i < 20:
return []
# Calculate alignment score (0-4)
score = sum([
price > e20,
e20 > e50,
e50 > e100,
e100 > e200
])
# EMA50 slope over 20 bars
e50_20_ago = ema50[-21] if len(ema50) > 20 else e50
ema50_slope = (e50 - e50_20_ago) / e50_20_ago * 100 if e50_20_ago else 0
# Track previous score for transition detection
prev_score = state.get('prev_score', 0)
state['prev_score'] = score
# Price extension from EMA50
price_ext = (price - e50) / e50 * 100
if key not in positions:
# ENTRY CONDITIONS:
# 1. Fresh alignment (score just became 4)
# 2. EMA50 slope confirms uptrend (> 0.5%)
# 3. Not overextended (< 8% above EMA50)
fresh_alignment = score == 4 and prev_score < 4
slope_confirms = ema50_slope > 0.5
not_extended = price_ext < 8
if fresh_alignment and slope_confirms and not_extended:
actions.append({
'action': 'open_long',
'symbol': 'tETHUSD',
'exchange': 'bitfinex',
'size': 1.0,
'stop_loss_pct': 4,
'take_profit_pct': 8,
})
state['entry_price'] = price
else:
# EXIT CONDITIONS (signal-based, TP/SL handled by framework):
# 1. Alignment breakdown (score ≤ 2)
# 2. EMA50 slope turns negative
breakdown = score <= 2
slope_negative = ema50_slope < 0
if breakdown or slope_negative:
actions.append({
'action': 'close_long',
'symbol': 'tETHUSD',
'exchange': 'bitfinex',
})
state.pop('entry_price', None)
return actions