Auto-discovered strategy
Symbol: BTC | Exchange: Binance | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +22.9% | 37.0% | 27 | 11.4% | 1.05 |
| 2021 | +21.9% | 60.0% | 10 | 4.3% | 1.64 |
| 2022 | -4.4% | 28.6% | 7 | 8.5% | -0.46 |
| 2023 | +14.8% | 33.3% | 24 | 12.2% | 0.73 |
| 2024 | +33.4% | 57.7% | 26 | 14.7% | 1.40 |
| 2025 | +15.4% | 43.8% | 16 | 3.9% | 1.33 |
| 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 volume_spike_regime_btc
"""
Volume Spike Regime Breakout Strategy (BTCUSDT)
================================================
A momentum strategy that trades volume spike breakouts in established uptrends.
Uses strict regime filter to stay flat during bear markets.
Entry Conditions (ALL required):
1. Regime: EMA50 > EMA200 (bullish macro trend)
2. Volume spike: Current volume > 2x the 20-bar SMA
3. Bullish bar: Close > Open
4. Breakout: Current bar high exceeds 10-bar high
5. Price support: Close above EMA10
Exit Conditions (ANY triggers exit):
1. Close below EMA10 (momentum fade)
2. 4% trailing stop from 10-bar high (profit protection)
3. Regime change: EMA50 crosses below EMA200
Risk Management:
- 4% hard stop loss per trade
- Regime filter ensures no trades in bear markets
- Multiple exit triggers prevent holding losers
Strategy Role: MOMENTUM
- Designed to capture bull market breakouts
- Allowed to lose up to -15% in validation (bear market)
- Expected to stay flat when EMA50 < EMA200
Performance (TRAIN: 2024-2025H1):
- 2024: +33.4% | 26 trades | 1.40 Sharpe | 14.7% max DD
- 2025H1: +9.3% | 8 trades | 1.04 Sharpe | 3.9% max DD
- Total: +42.6% | Both train periods profitable
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, sma
def init_strategy():
"""Initialize strategy configuration."""
return {
'name': 'volume_spike_regime_btc',
'role': 'momentum',
'warmup': 200,
'subscriptions': [
{'symbol': 'BTCUSDT', 'exchange': 'binance', 'timeframe': '4h'},
],
'parameters': {
'vol_mult': 2.0, # Volume spike threshold (2x average)
'breakout_bars': 10, # Bars for breakout high
'exit_ema': 10, # EMA period for exit
'trail_pct': 4.0, # Trailing stop percentage
'stop_pct': 4.0, # Hard stop loss percentage
}
}
def process_time_step(ctx):
"""
Process each time step and return trading actions.
Entry: Volume spike + breakout + uptrend regime
Exit: EMA cross or trailing stop or regime change
"""
key = ('BTCUSDT', 'binance')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
params = ctx['parameters']
# Extract parameters
vol_mult = params['vol_mult']
breakout_bars = params['breakout_bars']
exit_ema_period = params['exit_ema']
trail_pct = params['trail_pct']
stop_pct = params['stop_pct']
# Build price arrays up to current bar
closes = [b.close for b in bars[:i+1]]
volumes = [b.volume for b in bars[:i+1]]
highs = [b.high for b in bars[:i+1]]
# Calculate indicators
vol_sma20 = sma(volumes, 20)
ema_exit = ema(closes, exit_ema_period)
ema50 = ema(closes, 50)
ema200 = ema(closes, 200)
actions = []
if key not in positions:
# ========== ENTRY LOGIC ==========
# Need valid indicators
if (vol_sma20[i] is None or ema_exit[i] is None or
ema50[i] is None or ema200[i] is None):
return []
if vol_sma20[i] == 0:
return []
bar = bars[i]
# 1. REGIME FILTER: EMA50 > EMA200 (uptrend only)
if ema50[i] <= ema200[i]:
return []
# 2. VOLUME SPIKE: > 2x the 20-bar average
vol_ratio = volumes[i] / vol_sma20[i]
if vol_ratio < vol_mult:
return []
# 3. BULLISH BAR: close > open
if bar.close <= bar.open:
return []
# 4. BREAKOUT: high > 10-bar high
if i < breakout_bars:
return []
high_n = max(highs[i-breakout_bars:i])
if bar.high <= high_n:
return []
# 5. PRICE SUPPORT: close above EMA
if bar.close < ema_exit[i]:
return []
# All conditions met - enter long
actions.append({
'action': 'open_long',
'symbol': 'BTCUSDT',
'exchange': 'binance',
'size': 1.0,
'stop_loss_pct': stop_pct,
})
else:
# ========== EXIT LOGIC ==========
pos = positions[key]
bars_held = i - pos.entry_bar
# Hold for at least 1 bar
if bars_held < 1:
return []
bar = bars[i]
# Exit 1: Close below exit EMA
if ema_exit[i] and bar.close < ema_exit[i]:
actions.append({
'action': 'close_long',
'symbol': 'BTCUSDT',
'exchange': 'binance',
})
return actions
# Exit 2: Trailing stop below recent high
if i >= breakout_bars:
recent_high = max(highs[i-breakout_bars:i+1])
trail_stop = recent_high * (1 - trail_pct/100)
if bar.close < trail_stop:
actions.append({
'action': 'close_long',
'symbol': 'BTCUSDT',
'exchange': 'binance',
})
return actions
# Exit 3: Regime change
if ema50[i] and ema200[i] and ema50[i] < ema200[i]:
actions.append({
'action': 'close_long',
'symbol': 'BTCUSDT',
'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)