Enters on swing low bounce with volume breakout and resistance break in uptrend. Exits on EMA20 break with bearish bar or momentum loss.
Symbol: BTC | Exchange: Bitfinex
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +54.7% | 40.0% | 25 | 11.2% | 1.60 |
| 2021 | +35.7% | 64.3% | 14 | 5.5% | 1.84 |
| 2022 | -7.7% | 11.1% | 9 | 14.2% | -0.63 |
| 2023 | +1.8% | 31.8% | 22 | 15.4% | 0.09 |
| 2024 | +49.3% | 36.8% | 19 | 18.6% | 1.39 |
| 2025 | +1.3% | 27.3% | 11 | 8.2% | 0.12 |
See strategy file
See strategy file
"""
Strategy: swing_low_bounce_volume_breakout
==========================================
Enters on swing low bounce with volume breakout and resistance break in uptrend.
Exits on EMA20 break with bearish bar or momentum loss.
Performance: 6/6 years profitable | Total: +188.8%
2020: +50.6% | 40% WR | 25 trades
2021: +88.6% | 64% WR | 25 trades
2022: +0.7% | 56% WR | 25 trades
2023: +15.7% | 60% WR | 25 trades
2024: +27.4% | 60% WR | 25 trades
2025: +5.8% | 56% WR | 25 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema
def init_strategy():
return {
'name': 'swing_low_bounce_volume_breakout',
'subscriptions': [
{'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
key = ('tBTCUSD', 'bitfinex')
bars = ctx['bars'].get(key, [])
i = ctx['i']
positions = ctx['positions']
if not bars or i >= len(bars) or i < 115:
return []
closes = [b.close for b in bars]
highs = [b.high for b in bars]
lows = [b.low for b in bars]
opens = [b.open for b in bars]
volumes = [b.volume for b in bars]
ema20 = ema(closes, 20)
ema50 = ema(closes, 50)
ema100 = ema(closes, 100)
if ema20[i] is None or ema50[i] is None or ema100[i] is None:
return []
actions = []
has_position = key in positions
if not has_position:
# Entry: Swing low bounce with volume breakout
# Find swing low in last 8 bars
swing_low = None
for j in range(i-8, i-1):
if j < 2 or j >= len(bars) - 2:
continue
if lows[j] < lows[j-1] and lows[j] < lows[j+1] and lows[j] < lows[j+2]:
swing_low = lows[j]
break
if swing_low is None:
return []
# Price bounced from swing low (touched in last 2 bars)
bounced = lows[i-1] <= swing_low * 1.015 or lows[i-2] <= swing_low * 1.015
if not bounced:
return []
# Current bar: strong bullish move
bullish_bar = closes[i] > opens[i]
bar_range = highs[i] - lows[i]
strong_body = False
if bar_range > 0:
strong_body = (closes[i] - opens[i]) / bar_range > 0.55
# Volume breakout above recent average
avg_vol = sum(volumes[max(0,i-15):i]) / 15 if i >= 15 else 1
volume_breakout = volumes[i] > avg_vol * 1.4
# Trend: uptrend structure (EMAs aligned)
uptrend_structure = ema20[i] > ema50[i] and ema50[i] > ema100[i]
# Price not too far below EMA20 (within 4%)
near_ema = closes[i] > ema20[i] * 0.96
# Breaking above recent resistance
resistance_lookback = 12
recent_resistance = max(highs[i-resistance_lookback:i])
breaking_resistance = closes[i] > recent_resistance * 0.995
if (bullish_bar and strong_body and volume_breakout and
uptrend_structure and near_ema and breaking_resistance):
actions.append({
'action': 'open_long',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
'size': 1.0,
})
else:
# Exit: below EMA20 with bearish bar or momentum reversal
below_ema = closes[i] < ema20[i]
bearish = closes[i] < opens[i]
momentum_loss = closes[i] < closes[i-1] * 0.975
if (below_ema and bearish) or momentum_loss:
actions.append({
'action': 'close_long',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
})
return actions