Auto-discovered strategy
Symbol: BTC | Exchange: Bitfinex | Role: hedge
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +8.0% | 100.0% | 1 | 0.0% | 0.00 |
| 2021 | +16.0% | 60.0% | 5 | 4.0% | 1.22 |
| 2022 | -7.3% | 28.6% | 7 | 12.3% | -0.67 |
| 2023 | -10.1% | 20.0% | 5 | 9.8% | -2.99 |
| 2024 | +15.3% | 75.0% | 4 | 0.9% | 1.83 |
| 2025 | +7.6% | 40.0% | 5 | 4.3% | 0.63 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | +3.6% | OK | 2026-01→ongoing | +0.0% | PASS |
Not yet reviewed. Run: ./review_strategy.sh volatility_spike_hedge
"""
Volatility Spike Hedge Strategy
===============================
A hedge strategy that goes SHORT when volatility spikes occur during
established bearish trends. Designed to profit from crash/panic events.
Role: hedge
Exchange: bitfinex
Symbol: tBTCUSD
Entry Conditions (all must be met):
1. Price > 5% below EMA200 (significant weakness)
2. EMA cascade bearish: EMA10 < EMA20 < EMA50 < EMA200
3. Volatility just expanded: ATR10 crosses above 1.2x ATR50
4. RSI(10) < 40 (bearish momentum)
5. Two consecutive red candles (selling pressure)
Exit Conditions (any triggers exit):
1. RSI(10) > 55 (momentum recovery)
2. EMA10 crosses above EMA20 (trend shift)
3. Time stop: 30 bars
4. Take profit: 8%
5. Stop loss: 4%
Train Performance (2024-01-01 to 2025-06-30):
- Total Return: +19.32%
- Sharpe Ratio: 1.60
- Win Rate: 66.7%
- Max Drawdown: 4.0%
- Trades: 6 (very selective)
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, rsi, atr
def init_strategy():
return {
'name': 'volatility_spike_hedge',
'role': 'hedge',
'warmup': 200,
'subscriptions': [
{'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {
# EMA periods (round numbers)
'ema_fast': 10,
'ema_mid': 20,
'ema_slow': 50,
'ema_trend': 200,
# RSI settings
'rsi_period': 10,
'rsi_entry': 40,
'rsi_exit': 55,
# ATR volatility detection
'atr_fast': 10,
'atr_slow': 50,
'vol_threshold': 1.2, # ATR10 > 1.2x ATR50
# Entry filter
'weakness_threshold': -5, # % below EMA200
# Exit settings
'take_profit': 8,
'stop_loss': 4,
'time_stop': 30,
}
}
def process_time_step(ctx):
key = ('tBTCUSD', 'bitfinex')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
state = ctx['state']
params = ctx['parameters']
# Need enough data for EMA200 - now handled by framework via 'warmup' field
# if i < 200:
# return []
# Extract price data
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]]
opens = [b.open for b in bars[:i+1]]
# Calculate indicators
ema_10 = ema(closes, params['ema_fast'])
ema_20 = ema(closes, params['ema_mid'])
ema_50 = ema(closes, params['ema_slow'])
ema_200 = ema(closes, params['ema_trend'])
rsi_10 = rsi(closes, params['rsi_period'])
atr_10 = atr(highs, lows, closes, params['atr_fast'])
atr_50 = atr(highs, lows, closes, params['atr_slow'])
idx = len(closes) - 1
rsi_idx = len(rsi_10) - 1
# Check indicator availability
if ema_50[idx] is None or ema_200[idx] is None:
return []
if ema_10[idx] is None or ema_20[idx] is None:
return []
if rsi_10[rsi_idx] is None or atr_10[idx] is None or atr_50[idx] is None:
return []
# Entry conditions
# 1. Significant weakness: price > 5% below EMA200
below_ema200_pct = (closes[idx] - ema_200[idx]) / ema_200[idx] * 100
significant_weakness = below_ema200_pct < params['weakness_threshold']
# 2. EMA cascade bearish
ema_cascade = (
ema_10[idx] < ema_20[idx] < ema_50[idx] < ema_200[idx]
)
# 3. Volatility just expanded (ATR10 crossed above threshold)
vol_threshold = params['vol_threshold']
vol_expanding = atr_10[idx] > vol_threshold * atr_50[idx] if atr_50[idx] > 0 else False
prev_atr10 = atr_10[idx-1] if idx > 0 else None
prev_atr50 = atr_50[idx-1] if idx > 0 else None
vol_was_low = (
prev_atr10 < vol_threshold * prev_atr50
if prev_atr10 and prev_atr50 and prev_atr50 > 0
else True
)
vol_just_expanded = vol_expanding and vol_was_low
# 4. RSI bearish
rsi_bearish = rsi_10[rsi_idx] < params['rsi_entry']
# 5. Consecutive red candles
consecutive_red = (
closes[idx] < opens[idx] and
closes[idx-1] < opens[idx-1]
if idx > 0 else False
)
actions = []
if key not in positions:
# Entry: all conditions must be met
if (significant_weakness and ema_cascade and
vol_just_expanded and rsi_bearish and consecutive_red):
state['entry_high'] = closes[idx]
actions.append({
'action': 'open_short',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
'size': 1.0,
'take_profit_pct': params['take_profit'],
'stop_loss_pct': params['stop_loss'],
})
else:
pos = positions[key]
bars_held = i - pos.entry_bar
# Track high for potential trailing stop
if 'entry_high' in state:
state['entry_high'] = max(state['entry_high'], closes[idx])
# Exit conditions
# 1. RSI recovery
rsi_recovery = rsi_10[rsi_idx] > params['rsi_exit']
# 2. EMA10 crosses above EMA20 (momentum shift)
prev_ema10 = ema_10[idx-1] if idx > 0 else None
prev_ema20 = ema_20[idx-1] if idx > 0 else None
ema_cross_up = (
ema_10[idx] > ema_20[idx] and
prev_ema10 <= prev_ema20
if prev_ema10 and prev_ema20 else False
)
# 3. Time stop
time_exit = bars_held >= params['time_stop']
if rsi_recovery or ema_cross_up or time_exit:
actions.append({
'action': 'close_short',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
})
return actions
# Testing
if __name__ == '__main__':
from strategy import backtest_strategy
results, profitable, _ = backtest_strategy(init_strategy, process_time_step)