Auto-discovered strategy
Symbol: DOGE | Exchange: Bitfinex | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +0.0% | 0.0% | 0 | 0.0% | 0.00 |
| 2021 | -31.7% | 35.5% | 31 | 43.3% | -1.16 |
| 2022 | +6.0% | 45.7% | 46 | 30.3% | 0.20 |
| 2023 | +4.7% | 39.0% | 82 | 34.5% | 0.13 |
| 2024 | +68.4% | 50.5% | 91 | 24.4% | 1.63 |
| 2025 | -2.9% | 40.0% | 40 | 18.5% | -0.11 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | -7.8% | OK | 2026-01→ongoing | +0.0% | PASS |
Not yet reviewed. Run: ./review_strategy.sh doge_ema_cascade_pullback
"""
DOGE EMA Cascade Pullback Strategy
===================================
A momentum trend-following strategy that buys pullbacks to EMA20
during strong uptrends (when EMA20 > EMA50 > EMA200).
Core Logic:
- Entry: In strong uptrend (EMA cascade intact), buy when price pulls
back to touch the EMA20 and bounces (closes above it)
- Exit: ATR-based stop loss (1.5x ATR), 5% take profit, trend break
(close below EMA50), or 20-bar time stop
Bear Market Protection:
- Strategy stays FLAT when EMA cascade is broken
- No entries when EMA50 < EMA200 (macro downtrend)
- Automatic trend-break exit prevents holding through crashes
Parameters (all round numbers):
- EMA periods: 20, 50, 200
- ATR period: 20
- ATR multiplier: 1.5 (for stop loss)
- Take profit: 5%
- Time stop: 20 bars
- Pullback tolerance: 1%
Symbol: tDOGE:USD (Bitfinex)
Timeframe: 4h
Role: momentum
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, atr
def init_strategy():
return {
'name': 'doge_ema_cascade_pullback',
'role': 'momentum', # Can lose up to -15% in bear, DD < 40%
'warmup': 200, # Need 200 bars for EMA200
'subscriptions': [
{'symbol': 'tDOGE:USD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
"""
Process each time step for the EMA Cascade Pullback strategy.
Entry conditions (all must be true):
1. EMA cascade intact: EMA20 > EMA50 > EMA200 (strong uptrend)
2. Price above EMA200 (not in crash territory)
3. Price pulls back to touch EMA20 (within 1%)
4. Price closes above EMA20 (bounce confirmed)
Exit conditions:
1. Take profit at 5%
2. Stop loss at 1.5x ATR (typically 4-8%)
3. Price closes below EMA50 (trend break)
4. Time stop at 20 bars
"""
key = ('tDOGE:USD', 'bitfinex')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
state = ctx['state']
# Parameters - all round numbers to avoid overfitting
EMA_FAST = 20
EMA_MED = 50
EMA_SLOW = 200
ATR_PERIOD = 20
ATR_MULT = 1.5
TAKE_PROFIT_PCT = 5
TIME_STOP_BARS = 20
PULLBACK_TOLERANCE = 0.01 # 1%
# Calculate indicators using available data up to current bar
closes = [b.close for b in bars[:i+1]]
highs = [b.high for b in bars[:i+1]]
lows = [b.low for b in bars[:i+1]]
ema_fast = ema(closes, EMA_FAST)
ema_med = ema(closes, EMA_MED)
ema_slow = ema(closes, EMA_SLOW)
atr_vals = atr(highs, lows, closes, ATR_PERIOD)
actions = []
if key not in positions:
# Check entry conditions
if (ema_fast[-1] and ema_med[-1] and ema_slow[-1] and atr_vals[-1]):
# 1. EMA cascade intact (strong uptrend)
cascade_intact = (ema_fast[-1] > ema_med[-1] > ema_slow[-1])
# 2. Price above EMA200
price_above_slow = closes[-1] > ema_slow[-1]
# 3. Price pulled back to touch EMA20
pullback_to_fast = lows[-1] <= ema_fast[-1] * (1 + PULLBACK_TOLERANCE)
# 4. Price closed above EMA20 (bounce)
bounce_confirmed = closes[-1] >= ema_fast[-1] * (1 - PULLBACK_TOLERANCE)
if cascade_intact and price_above_slow and pullback_to_fast and bounce_confirmed:
# Calculate dynamic stop based on ATR
stop_loss_price = closes[-1] - ATR_MULT * atr_vals[-1]
stop_loss_pct = (closes[-1] - stop_loss_price) / closes[-1] * 100
# Clamp stop loss to reasonable range (2-10%)
stop_loss_pct = max(2, min(stop_loss_pct, 10))
actions.append({
'action': 'open_long',
'symbol': 'tDOGE:USD',
'exchange': 'bitfinex',
'size': 1.0,
'take_profit_pct': TAKE_PROFIT_PCT,
'stop_loss_pct': stop_loss_pct,
})
state['entry_bar'] = i
else:
# Check exit conditions (besides TP/SL which framework handles)
bars_held = i - state.get('entry_bar', i)
# Time stop - exit if held too long without reaching TP/SL
if bars_held >= TIME_STOP_BARS:
actions.append({
'action': 'close_long',
'symbol': 'tDOGE:USD',
'exchange': 'bitfinex',
})
# Trend break - exit if price closes below EMA50
elif ema_med[-1] and closes[-1] < ema_med[-1]:
actions.append({
'action': 'close_long',
'symbol': 'tDOGE:USD',
'exchange': 'bitfinex',
})
return actions
# Self-test when run directly
if __name__ == '__main__':
from strategy import backtest_strategy
results, profitable, _ = backtest_strategy(init_strategy, process_time_step, verbose=True)
print(f"\nProfitable training periods: {profitable}/2")