Auto-discovered strategy
Symbol: BTC | Exchange: Bitfinex | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +38.4% | 34.5% | 29 | 20.4% | 0.95 |
| 2021 | +6.3% | 26.1% | 23 | 24.5% | 0.18 |
| 2022 | +1.4% | 40.0% | 5 | 5.8% | 0.16 |
| 2023 | +5.1% | 36.8% | 19 | 11.4% | 0.21 |
| 2024 | +40.8% | 36.0% | 25 | 19.6% | 1.29 |
| 2025 | +14.0% | 50.0% | 10 | 8.4% | 1.05 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | -1.4% | FAIL | 2026-01→ongoing | +0.0% | FAIL |
Not yet reviewed. Run: ./review_strategy.sh ema_cascade_breakout
"""
EMA Cascade Breakout Strategy
==============================
A momentum trend-following strategy using EMA cascade alignment
with volume-confirmed breakouts.
Exchange: bitfinex
Symbol: tBTCUSD
Role: momentum
Strategy Logic:
---------------
ENTRY (all conditions must be met):
1. Regime Filter: EMA50 > EMA200 (macro uptrend - golden cross)
2. EMA Cascade: EMA20 > EMA50 (momentum aligned)
3. Breakout: Price closes above 10-bar high
4. Volume: Current volume > 1.2x average (conviction)
EXIT (any condition triggers exit):
1. Price closes below 10-bar low (breakdown)
2. EMA20 < EMA50 (cascade breaks, momentum fading)
3. EMA50 < EMA200 (regime turns bearish)
Risk Management:
- Stop Loss: 5% (tight risk control)
- Take Profit: 15% (let winners run)
Key Design Principles:
- REGIME FILTER prevents trading in bear markets
- Uses RELATIVE indicators only (EMAs, N-bar high/low)
- ROUND parameter values (10, 20, 50, 200)
- Volume confirmation reduces false breakouts
Train Period Performance (2024-01-01 to 2025-06-30):
- 2024: +40.8%, 25 trades, 36% WR, Sharpe 1.29, Max DD 19.6%
- 2025 H1: +13.7%, 4 trades, 75% WR, Sharpe 1.65
- Total: +54.5%
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, sma
def init_strategy():
"""Initialize the EMA Cascade Breakout strategy."""
return {
'name': 'ema_cascade_breakout',
'role': 'momentum',
'warmup': 200,
'role': 'momentum', # Momentum strategy - allowed to lose bounded in bear
'subscriptions': [
{'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {
# EMA periods (all round numbers)
'ema_fast': 20,
'ema_slow': 50,
'ema_trend': 200,
# Breakout lookback
'breakout_period': 10,
# Volume multiplier
'volume_mult': 1.2,
# Risk management
'stop_loss_pct': 5.0,
'take_profit_pct': 15.0,
}
}
def process_time_step(ctx):
"""Process each time step and return trading actions."""
key = ('tBTCUSD', 'bitfinex')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
state = ctx['state']
# Need enough history for EMA200
if i < 200:
return []
# Pre-calculate indicators (cache in state for efficiency)
if 'ema20' not in state or len(state['ema20']) != len(bars):
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]
state['ema20'] = ema(closes, 20)
state['ema50'] = ema(closes, 50)
state['ema200'] = ema(closes, 200)
state['vol_sma'] = sma(volumes, 20)
state['highs'] = highs
state['lows'] = lows
# Current indicator values
e20 = state['ema20'][i]
e50 = state['ema50'][i]
e200 = state['ema200'][i]
vol_avg = state['vol_sma'][i]
bar = bars[i]
actions = []
# Recent high/low for breakout detection (10-bar lookback)
lookback = 10
recent_high = max(state['highs'][i-lookback:i])
recent_low = min(state['lows'][i-lookback:i])
if key not in positions:
# === ENTRY LOGIC ===
# REGIME FILTER (CRITICAL): Only trade in bull markets
# EMA50 > EMA200 is the "golden cross" - standard trend filter
# This prevents entering during bear markets
regime_bullish = e50 > e200
if not regime_bullish:
return [] # Stay FLAT in downtrends
# EMA CASCADE: Confirms momentum alignment at multiple timeframes
# EMA20 > EMA50 shows short-term strength within the uptrend
cascade_aligned = e20 > e50
# BREAKOUT: Price closes above recent 10-bar high
# This catches momentum continuation after consolidation
breakout = bar.close > recent_high
# VOLUME: Above-average volume confirms conviction
# Breakouts without volume often fail
volume_surge = bar.volume > vol_avg * 1.2
if cascade_aligned and breakout and volume_surge:
actions.append({
'action': 'open_long',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
'size': 1.0,
'stop_loss_pct': 5.0, # Tight stop for risk control
'take_profit_pct': 15.0, # Let winners run
})
else:
# === EXIT LOGIC ===
# Exit 1: Price breaks down below recent 10-bar low
# Shows buyers have lost control
breakdown = bar.close < recent_low
# Exit 2: EMA cascade breaks (momentum fading)
# EMA20 crossing below EMA50 is early warning
cascade_broken = e20 < e50
# Exit 3: Regime turns bearish (macro reversal)
# EMA50 crossing below EMA200 is major trend change
regime_bearish = e50 < e200
if breakdown or cascade_broken or regime_bearish:
actions.append({
'action': 'close_long',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
})
return actions
# For testing
if __name__ == '__main__':
from strategy import backtest_strategy
print("Backtesting EMA Cascade Breakout strategy...")
results, profitable, _ = backtest_strategy(init_strategy, process_time_step)