Auto-discovered strategy
Symbol: DOGE | Exchange: Binance | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | -5.9% | 20.0% | 25 | 22.2% | -0.22 |
| 2021 | +8.2% | 30.0% | 40 | 26.0% | 0.20 |
| 2022 | +22.0% | 41.7% | 12 | 14.5% | 0.91 |
| 2023 | -9.0% | 31.8% | 22 | 22.9% | -0.36 |
| 2024 | +87.9% | 45.7% | 35 | 16.7% | 2.23 |
| 2025 | +12.9% | 35.7% | 14 | 16.7% | 0.51 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | +5.9% | OK | 2026-01→ongoing | +0.0% | PASS |
Not yet reviewed. Run: ./review_strategy.sh doge_vol_expansion
"""
DOGE Volatility Expansion Strategy
===================================
Concept: Enter DOGEUSDT when volatility expands from compression during bullish trends.
Market Principle:
- Volatility is mean-reverting: periods of low volatility (compression) are followed
by periods of high volatility (expansion)
- The direction of expansion often follows the prevailing trend
- By filtering for bullish regime (EMA50 > EMA200), we trade expansion breakouts
in the direction of the dominant trend
Entry Conditions (ALL must be true):
1. Regime filter: EMA50 > EMA200 (bullish macro trend)
2. Recent compression: Bollinger Band width percentile < 20 in last 10 bars
3. Current expansion: BB width percentile increasing (expanding volatility)
4. Price momentum: Close > EMA20 (short-term bullish)
5. Volume confirmation: Volume >= 20-bar average
Exit Conditions:
1. Take profit: 10% gain (captures expansion moves)
2. Stop loss: 5% loss (limits downside)
3. Signal exit: Close < EMA50 (trend weakening)
Risk Management:
- Strict regime filter prevents trading in bear markets
- 5% stop loss caps individual trade risk
- No trades during volatility compression (waits for breakout)
Train Performance (2024-01 to 2025-06):
- 2024: +87.9% | 35 trades | 46% WR
- 2025 (H1): +5.0% | 6 trades | 33% WR
- Total: +93% | Max DD: 16.7% | Sharpe: 2.17
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, sma, bollinger_bands
# Global indicator cache
_indicator_cache = {}
def init_strategy():
return {
'name': 'doge_vol_expansion',
'role': 'momentum', # Volatility breakout is momentum strategy
'warmup': 200, # Need 200 bars for EMA200
'subscriptions': [
{'symbol': 'DOGEUSDT', 'exchange': 'binance', 'timeframe': '4h'},
],
'parameters': {
# EMA periods for trend detection
'ema_fast': 20,
'ema_mid': 50,
'ema_slow': 200,
# Bollinger Band settings
'bb_period': 20,
# Compression detection
'compression_lookback': 10,
'compression_threshold': 20, # percentile
# Risk management
'take_profit_pct': 10,
'stop_loss_pct': 5,
}
}
def compute_all_indicators(bars):
"""Compute all indicators once per bar sequence."""
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]
# EMAs for trend
ema20 = ema(closes, 20)
ema50 = ema(closes, 50)
ema200 = ema(closes, 200)
# Bollinger Bands for volatility
middle, upper, lower = bollinger_bands(closes, 20, 2.0)
# Volume average
vol_sma = sma(volumes, 20)
# BB width as % of price (volatility measure)
bb_width = [
(upper[i] - lower[i]) / middle[i] * 100 if middle[i] else None
for i in range(len(closes))
]
# Rolling percentile of BB width (compression detection)
bb_percentile = [None] * len(closes)
for i in range(50, len(closes)):
window = [bb_width[j] for j in range(i-50, i) if bb_width[j] is not None]
if window and bb_width[i]:
sorted_w = sorted(window)
percentile = sum(1 for w in sorted_w if w <= bb_width[i]) / len(sorted_w) * 100
bb_percentile[i] = percentile
return {
'closes': closes,
'volumes': volumes,
'ema20': ema20,
'ema50': ema50,
'ema200': ema200,
'bb_percentile': bb_percentile,
'vol_sma': vol_sma,
}
def process_time_step(ctx):
key = ('DOGEUSDT', 'binance')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
params = ctx['parameters']
# Compute indicators (cached)
cache_key = id(bars)
if cache_key not in _indicator_cache:
_indicator_cache[cache_key] = compute_all_indicators(bars)
ind = _indicator_cache[cache_key]
actions = []
has_position = key in positions
if not has_position:
# === ENTRY LOGIC ===
# Safety check - ensure all indicators exist
if not all([
ind['ema50'][i], ind['ema200'][i], ind['ema20'][i],
ind['bb_percentile'][i], ind['vol_sma'][i]
]):
return []
# 1. REGIME FILTER: Bullish trend only
# EMA50 > EMA200 indicates bullish macro environment
if ind['ema50'][i] <= ind['ema200'][i]:
return []
# 2. RECENT COMPRESSION: BB percentile < 20 in last 10 bars
# Volatility was squeezed recently
was_compressed = False
for j in range(max(50, i-10), i-2):
if ind['bb_percentile'][j] and ind['bb_percentile'][j] < 20:
was_compressed = True
break
if not was_compressed:
return []
# 3. CURRENT EXPANSION: BB percentile increasing
# Volatility is now expanding (breakout potential)
if ind['bb_percentile'][i-3] and ind['bb_percentile'][i] < ind['bb_percentile'][i-3]:
return []
# 4. PRICE MOMENTUM: Close > EMA20
# Price showing short-term strength
if ind['closes'][i] <= ind['ema20'][i]:
return []
# 5. VOLUME CONFIRMATION: Volume >= average
# Institutional participation
if ind['volumes'][i] < ind['vol_sma'][i]:
return []
# All conditions met - enter long
actions.append({
'action': 'open_long',
'symbol': 'DOGEUSDT',
'exchange': 'binance',
'size': 1.0,
'take_profit_pct': params['take_profit_pct'],
'stop_loss_pct': params['stop_loss_pct'],
})
else:
# === EXIT LOGIC ===
# (TP/SL handled by framework)
# Signal exit: Close below EMA50 = trend weakening
if ind['ema50'][i] and ind['closes'][i] < ind['ema50'][i]:
actions.append({
'action': 'close_long',
'symbol': 'DOGEUSDT',
'exchange': 'binance',
})
return actions
# For standalone testing
if __name__ == '__main__':
from strategy import backtest_strategy
results, profitable, _ = backtest_strategy(init_strategy, process_time_step, verbose=True)