Auto-discovered strategy
Symbol: ETH | Exchange: Bitfinex | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +68.5% | 49.5% | 93 | 17.4% | 1.87 |
| 2021 | -0.5% | 37.2% | 94 | 32.8% | -0.01 |
| 2022 | -11.8% | 34.4% | 32 | 23.8% | -0.61 |
| 2023 | -13.4% | 36.2% | 47 | 29.6% | -0.64 |
| 2024 | +38.2% | 51.0% | 49 | 14.5% | 1.52 |
| 2025 | +21.2% | 47.6% | 42 | 18.7% | 0.90 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | +1.9% | OK | 2026-01→ongoing | +0.0% | PASS |
Not yet reviewed. Run: ./review_strategy.sh eth_momentum_continuation
"""
ETH Momentum Continuation Strategy
==================================
A trend-following momentum strategy for ETH that enters on breakouts
in confirmed uptrends and exits when momentum weakens.
Strategy Concept:
-----------------
This strategy captures momentum continuation in established uptrends.
It waits for price to make a new high while the trend is confirmed
by EMAs and volume. The idea is that breakouts in strong trends
tend to continue, especially when supported by volume.
Entry Conditions (all must be true):
1. Uptrend structure: Price > EMA50 > EMA100 (bullish cascade)
2. Rising momentum: EMA50 higher than 5 bars ago
3. Breakout signal: Price makes new 10-bar high
4. Volume confirmation: Current volume > 20-bar average
Exit Conditions (any triggers exit):
1. Hard stop: 3% loss from entry (risk management)
2. Profit target: 5% gain from entry
3. Time exit: 15 bars maximum hold
4. Trend break: Close below EMA50 (after 3 bar minimum hold)
Risk Management:
----------------
- Uses EMA cascade as regime filter (no trades in downtrends)
- Volume confirmation reduces false breakouts
- Fixed stop loss protects against adverse moves
- Time exit prevents getting stuck in choppy conditions
Expected Behavior:
------------------
- Performs well in trending markets
- Will have small losses in choppy/ranging conditions
- May underperform or lose in strong bear markets
- Win rate around 45-50%, relies on winners being larger than losers
Train Period Performance (2024-01-01 to 2025-06-30):
- 70 trades, 36.5% total return
- Win rate: 45.7%
- Max drawdown: 18.9%
- Profit factor: 1.36
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, sma
def init_strategy():
"""Initialize the strategy configuration."""
return {
'name': 'eth_momentum_continuation',
'role': 'momentum',
'warmup': 100,
'subscriptions': [
{'symbol': 'tETHUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
"""
Process each time step and return trading actions.
Args:
ctx: Context dict containing bars, positions, state, etc.
Returns:
List of action dicts (open_long, close_long, etc.)
"""
key = ('tETHUSD', 'bitfinex')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
# Build price/volume arrays up to current bar
closes = [b.close for b in bars[:i+1]]
highs = [b.high for b in bars[:i+1]]
volumes = [b.volume for b in bars[:i+1]]
# Calculate indicators
ema50_arr = ema(closes, 50)
ema100_arr = ema(closes, 100)
sma_vol_arr = sma(volumes, 20)
# Get current indicator values
ema50 = ema50_arr[i] if i < len(ema50_arr) else None
ema100 = ema100_arr[i] if i < len(ema100_arr) else None
sma_vol = sma_vol_arr[i] if i < len(sma_vol_arr) else None
ema50_prev = ema50_arr[i-5] if i >= 5 and i-5 < len(ema50_arr) else None
actions = []
if key not in positions:
# ===== ENTRY LOGIC =====
# Validate indicator availability
if not (ema50 and ema100 and sma_vol and ema50_prev):
return []
if i < 10:
return []
# Condition 1: Uptrend structure (EMA cascade)
if not (closes[i] > ema50 > ema100):
return []
# Condition 2: Rising momentum (EMA50 trending up)
if ema50 <= ema50_prev:
return []
# Condition 3: 10-bar high breakout
recent_high = max(highs[i-10:i])
if highs[i] <= recent_high:
return []
# Condition 4: Volume confirmation
if volumes[i] < sma_vol:
return []
# All conditions met - enter long
actions.append({
'action': 'open_long',
'symbol': 'tETHUSD',
'exchange': 'bitfinex',
'size': 1.0,
'stop_loss_pct': 3,
'take_profit_pct': 5
})
else:
# ===== EXIT LOGIC =====
pos = positions[key]
bars_held = i - pos.entry_bar
if not ema50:
return []
should_exit = False
# Exit condition 1: Time exit (15 bars max)
if bars_held >= 15:
should_exit = True
# Exit condition 2: Trend break (close below EMA50)
if bars_held >= 3 and bars[i].close < ema50:
should_exit = True
if should_exit:
actions.append({
'action': 'close_long',
'symbol': 'tETHUSD',
'exchange': 'bitfinex'
})
return actions