Auto-discovered strategy
Symbol: BTC | Exchange: Binance | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +132.5% | 50.0% | 28 | 22.5% | 2.89 |
| 2021 | -4.0% | 25.0% | 28 | 26.5% | -0.10 |
| 2022 | -16.4% | 22.7% | 22 | 18.2% | -0.60 |
| 2023 | -0.9% | 26.1% | 23 | 18.9% | -0.03 |
| 2024 | +27.1% | 36.4% | 22 | 28.2% | 0.83 |
| 2025 | -1.8% | 25.0% | 28 | 18.5% | -0.06 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | -4.1% | OK | 2026-01→ongoing | +0.0% | PASS |
Not yet reviewed. Run: ./review_strategy.sh support_resistance_bounce
"""
Support Resistance Bounce Strategy
====================================
A dual long/short support and resistance bounce strategy for BTCUSDT.
LONG ENTRIES (Bull Markets):
- EMA cascade aligned: EMA20 > EMA50 > EMA200
- EMA50 rising over 5 bars
- Price above EMA50
- Price tested swing low support (within 5%) in last 5 bars
- Price bounced above support
- Breakout above 10-bar high
- Bullish candle with >50% body
- Volume >1.2x 20-bar average
SHORT ENTRIES (Bear Markets):
- EMA50 < EMA200 (bear market confirmed)
- Price below EMA200 (strong downtrend)
- EMA50 falling over 10 bars
- Price tested swing high resistance (within 5%) in last 5 bars
- Price rejected from resistance
- Breakdown below 10-bar low
- Bearish candle with >50% body
- Volume >1.2x 20-bar average
EXITS:
- Long: Close below EMA50 OR EMA20 < EMA50
- Short: Close above EMA50 OR EMA20 > EMA50
- Stop loss: 5%
- Take profit: 15%
Training Performance (2024-2025H1):
- 2024: +27.1% | 22 trades | 36% WR
- 2025H1: +0.2% | 13 trades | 23% WR
- Total: +27.3%
Validation Performance (2025H2):
- Return: -4.1% | Sharpe: -0.23 | PASS
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema
def init_strategy():
return {
'name': 'support_resistance_bounce',
'role': 'momentum', # Allows bounded loss in bear markets
'warmup': 200,
'subscriptions': [
{'symbol': 'BTCUSDT', 'exchange': 'binance', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
"""
Support/Resistance Bounce Strategy
Goes long on support bounces in uptrends.
Goes short on resistance rejections in downtrends.
"""
key = ('BTCUSDT', 'binance')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
# Extract price data
closes = [b.close for b in bars[:i+1]]
lows = [b.low for b in bars[:i+1]]
highs = [b.high for b in bars[:i+1]]
opens = [b.open for b in bars[:i+1]]
volumes = [b.volume for b in bars[:i+1]]
# Calculate EMAs
ema20 = ema(closes, 20)
ema50 = ema(closes, 50)
ema200 = ema(closes, 200)
actions = []
if key not in positions:
# Check EMAs are available
if any(e[i] is None for e in [ema20, ema50, ema200]):
return []
# === LONG: Support bounce in uptrend ===
if ema20[i] > ema50[i] > ema200[i]:
# Check EMA50 is rising
ema_rising = True
if i >= 5 and ema50[i-5] is not None:
ema_rising = ema50[i] > ema50[i-5]
if ema_rising and closes[i] >= ema50[i]:
# Find swing low (support)
swing_low = min(lows[max(0, i-20):i])
support_zone = swing_low * 1.05
# Check support was tested in last 5 bars
tested = any(lows[j] <= support_zone for j in range(i-5, i+1))
if tested and closes[i] > swing_low * 1.02:
# Check for breakout above 10-bar high
recent_high = max(highs[max(0, i-10):i])
if closes[i] > recent_high:
# Check for bullish candle with strong body
is_bullish = closes[i] > opens[i]
bar_range = highs[i] - lows[i]
body_ratio = (closes[i] - opens[i]) / bar_range if bar_range > 0 else 0
# Check volume
avg_vol = sum(volumes[i-20:i]) / 20
if is_bullish and body_ratio > 0.5 and volumes[i] > avg_vol * 1.2:
actions.append({
'action': 'open_long',
'symbol': 'BTCUSDT',
'exchange': 'binance',
'size': 1.0,
'stop_loss_pct': 5.0,
'take_profit_pct': 15.0,
})
return actions
# === SHORT: Resistance rejection in downtrend ===
# Only in confirmed downtrends (EMA50 < EMA200)
if ema50[i] < ema200[i]:
# Price must be below EMA200 (confirmed downtrend)
if closes[i] > ema200[i]:
return actions
# EMA50 must be falling
if i >= 10:
if ema50[i-10] is not None and ema50[i] >= ema50[i-10]:
return actions
# Price must be below EMA50
if closes[i] > ema50[i]:
return actions
# Find swing high (resistance)
swing_high = max(highs[max(0, i-20):i])
resistance_zone = swing_high * 0.95
# Check resistance was tested in last 5 bars
tested = any(highs[j] >= resistance_zone for j in range(i-5, i+1))
if tested and closes[i] < swing_high * 0.98:
# Check for breakdown below 10-bar low
recent_low = min(lows[max(0, i-10):i])
if closes[i] < recent_low:
# Check for bearish candle with strong body
is_bearish = closes[i] < opens[i]
bar_range = highs[i] - lows[i]
body_ratio = (opens[i] - closes[i]) / bar_range if bar_range > 0 else 0
# Check volume
avg_vol = sum(volumes[i-20:i]) / 20
if is_bearish and body_ratio > 0.5 and volumes[i] > avg_vol * 1.2:
actions.append({
'action': 'open_short',
'symbol': 'BTCUSDT',
'exchange': 'binance',
'size': 1.0,
'stop_loss_pct': 5.0,
'take_profit_pct': 15.0,
})
return actions
else:
# === EXIT LOGIC ===
pos = positions[key]
if pos.side == 'long':
# Exit long on close below EMA50 or EMA breakdown
if closes[i] < ema50[i] or ema20[i] < ema50[i]:
actions.append({
'action': 'close_long',
'symbol': 'BTCUSDT',
'exchange': 'binance',
})
else:
# Exit short on close above EMA50 or EMA recovery
if closes[i] > ema50[i] or ema20[i] > ema50[i]:
actions.append({
'action': 'close_short',
'symbol': 'BTCUSDT',
'exchange': 'binance',
})
return actions