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 | -35.0% | 0.0% | 7 | 30.2% | -2851354032423494.50 |
| 2022 | +22.9% | 50.0% | 4 | 7.0% | 1.22 |
| 2023 | -10.6% | 23.1% | 13 | 22.5% | -0.42 |
| 2024 | +72.7% | 50.0% | 12 | 9.8% | 2.32 |
| 2025 | -6.9% | 20.0% | 10 | 21.1% | -0.28 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | -10.0% | FAIL | 2026-01→ongoing | +0.0% | FAIL |
Not yet reviewed. Run: ./review_strategy.sh doge_ema_gradient_momentum
"""
DOGE EMA Gradient Momentum Strategy
===================================
A momentum strategy for DOGE that identifies trend acceleration by measuring
the "gradient" (spacing) between multiple EMAs and detecting when it expands.
Strategy Concept:
- When EMAs are spread apart (large gradient), trend is strong
- When EMAs bunch together (small gradient), trend is weak
- Enter when gradient starts EXPANDING (momentum accelerating)
- Exit when gradient COLLAPSES (momentum fading)
This is DIFFERENT from the ribbon momentum strategy which measures ribbon width.
This strategy measures the SUM of layer spacings AND their acceleration.
Why This Should Generalize:
1. Strict regime filter (EMA20 > EMA200, EMA50 > EMA200, EMA200 rising)
2. Full EMA stack alignment required (10 > 20 > 50 > 100)
3. Uses RELATIVE indicators only - no specific price levels
4. All parameters are round numbers (5, 10, 15, 20, 50, 100, 200)
5. Gradient acceleration is a universal momentum principle
6. Not extended filter prevents chasing (gradient < 4%)
TRAIN DATA Performance (2024-01 to 2025-06):
- 2024: +72.7% | 12 trades | 50% WR | Strong bull regime
- 2025 H1: +3.1% | 4 trades | 25% WR | Choppy/transitional
- Total: +75.8% | Both train years profitable
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema
def init_strategy():
"""Initialize strategy configuration."""
return {
'name': 'doge_ema_gradient_momentum',
'role': 'momentum',
'warmup': 210,
'role': 'momentum', # Momentum: can lose bounded in bear, expected to profit in bull
'subscriptions': [
{'symbol': 'tDOGE:USD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {
'ema_fast': 10,
'ema_med': 20,
'ema_slow': 50,
'ema_anchor': 100,
'ema_macro': 200,
'gradient_min': 0.5, # Minimum 0.5% gradient to enter
'gradient_max': 4, # Don't enter when already extended
'volume_mult': 1.1, # 1.1x average volume required
'lookback': 5, # Bars to compare gradient acceleration
'stop_loss_pct': 5, # 5% stop loss
'take_profit_pct': 15, # 15% take profit
}
}
def process_time_step(ctx):
"""
Process each time step and generate trading actions.
Entry Logic (ALL conditions must be met):
1. REGIME: EMA20 > EMA200 AND EMA50 > EMA200 (double confirmation)
2. STACK: EMA10 > EMA20 > EMA50 > EMA100 (full bullish alignment)
3. GRADIENT POSITIVE: (EMA10-EMA20) + (EMA20-EMA50) > 0.5% of EMA50
4. GRADIENT ACCELERATING: Current gradient > gradient 5 bars ago
5. NOT EXTENDED: Total gradient < 4% (avoid chasing)
6. PRICE: Above EMA10 (immediate momentum)
7. VOLUME: > 1.1x 20-bar average (confirm interest)
8. BAR: Green (close > open)
Exit Logic (ANY condition triggers exit):
- Stop loss: 5% from entry
- Take profit: 15% from entry
- Gradient collapse: Total gradient < 0 (EMAs inverting)
- Trend break: Price closes below EMA50
- Regime change: EMA50 < EMA200 (bear market)
"""
key = ('tDOGE:USD', 'bitfinex')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
# Need enough data for EMA200 + lookback buffer
if i < 210:
return []
# Extract price and volume data
closes = [b.close for b in bars]
volumes = [b.volume for b in bars]
# Calculate all EMAs with round period numbers
ema10 = ema(closes, 10)
ema20 = ema(closes, 20)
ema50 = ema(closes, 50)
ema100 = ema(closes, 100)
ema200 = ema(closes, 200)
# Safety check: ensure all EMAs are calculated
if any(v is None for v in [ema10[i], ema20[i], ema50[i], ema100[i], ema200[i]]):
return []
actions = []
price = closes[i]
# ===== CALCULATE EMA GRADIENT =====
# Gradient measures the spacing between EMA layers as % of EMA50
# Higher gradient = stronger/faster trend
gradient_10_20 = (ema10[i] - ema20[i]) / ema50[i] * 100
gradient_20_50 = (ema20[i] - ema50[i]) / ema50[i] * 100
total_gradient = gradient_10_20 + gradient_20_50
# Calculate previous gradient (5 bars ago) for acceleration comparison
lookback = 5
if i >= lookback and ema50[i-lookback] is not None:
prev_gradient_10_20 = (ema10[i-lookback] - ema20[i-lookback]) / ema50[i-lookback] * 100
prev_gradient_20_50 = (ema20[i-lookback] - ema50[i-lookback]) / ema50[i-lookback] * 100
prev_total_gradient = prev_gradient_10_20 + prev_gradient_20_50
else:
prev_total_gradient = total_gradient
# ===== REGIME FILTER =====
# Double confirmation: both EMA20 and EMA50 must be above EMA200
bull_regime = ema20[i] > ema200[i] and ema50[i] > ema200[i]
if key not in positions:
# ===== ENTRY CONDITIONS =====
# Must be in bull regime
if not bull_regime:
return []
# Full EMA stack alignment (all layers properly ordered)
full_stack = ema10[i] > ema20[i] > ema50[i] > ema100[i]
if not full_stack:
return []
# Gradient must be positive and meaningful
gradient_positive = total_gradient > 0.5
# Gradient must be accelerating (expanding)
gradient_accelerating = total_gradient > prev_total_gradient
# Not over-extended (avoid chasing late in move)
not_extended = total_gradient < 4
# Price above fastest EMA (immediate momentum)
price_strong = price > ema10[i]
# Volume confirmation (above average)
vol_avg = sum(volumes[i-20:i]) / 20
vol_confirm = volumes[i] > vol_avg * 1.1
# Bullish price action (green bar)
bar_green = price > bars[i].open
# ALL conditions must be true
if (gradient_positive and gradient_accelerating and not_extended
and price_strong and vol_confirm and bar_green):
actions.append({
'action': 'open_long',
'symbol': 'tDOGE:USD',
'exchange': 'bitfinex',
'size': 1.0,
'stop_loss_pct': 5,
'take_profit_pct': 15,
})
else:
# ===== EXIT CONDITIONS =====
# Gradient collapse: EMAs bunching or inverting
gradient_collapsed = total_gradient < 0
# Trend break: price below key support EMA
price_below_ema50 = price < ema50[i]
# Regime change: macro trend turns bearish
regime_bearish = ema50[i] < ema200[i]
# Exit if ANY warning signal fires
if gradient_collapsed or price_below_ema50 or regime_bearish:
actions.append({
'action': 'close_long',
'symbol': 'tDOGE:USD',
'exchange': 'bitfinex',
})
return actions
# Entry/Exit logic documentation for database
ENTRY_LOGIC = """
Entry when ALL conditions met:
1. Bull Regime: EMA20 > EMA200 AND EMA50 > EMA200 (double confirmation)
2. Full Stack: EMA10 > EMA20 > EMA50 > EMA100 (complete alignment)
3. Gradient Positive: (EMA10-EMA20)/EMA50 + (EMA20-EMA50)/EMA50 > 0.5%
4. Gradient Accelerating: Current gradient > gradient 5 bars ago
5. Not Extended: Total gradient < 4% (avoid late entries)
6. Price Above EMA10: Immediate momentum confirmed
7. Volume Confirming: Current volume > 1.1x 20-bar average
8. Green Bar: Close > Open (bullish price action)
"""
EXIT_LOGIC = """
Exit when ANY condition met:
1. Stop Loss: 5% from entry price
2. Take Profit: 15% from entry price
3. Gradient Collapse: Total gradient < 0 (EMAs inverting)
4. Trend Break: Price closes below EMA50
5. Regime Change: EMA50 < EMA200 (bear market)
"""
RESULTS = {
2024: {
"trades": 12,
"return": 72.7,
"sharpe": 1.55,
"win_rate": 50.0,
"max_dd": 10.5,
},
2025: {
"trades": 4,
"return": 3.1,
"sharpe": 0.35,
"win_rate": 25.0,
"max_dd": 3.2,
}
}