Auto-discovered strategy
Symbol: SOL | Exchange: Binance | Role: mean_reversion
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +1.0% | 100.0% | 1 | 0.0% | 0.00 |
| 2021 | -8.4% | 27.3% | 11 | 15.8% | -0.58 |
| 2022 | -19.4% | 20.0% | 20 | 22.5% | -1.35 |
| 2023 | +7.9% | 45.5% | 22 | 12.8% | 0.54 |
| 2024 | +3.4% | 52.2% | 23 | 12.3% | 0.21 |
| 2025 | +2.8% | 42.9% | 7 | 5.1% | 0.47 |
| 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% | OK | 2026-01→ongoing | +0.0% | FAIL |
Not yet reviewed. Run: ./review_strategy.sh sol_rsi_confirmed_bounce
"""
SOL RSI Confirmed Bounce - Mean Reversion Strategy
===================================================
Mean reversion strategy for SOLUSDT that trades RSI oversold bounces
in ranging/choppy market conditions.
Strategy Logic:
- REGIME FILTER: Only trade when market is ranging (not trending)
- 20-bar range < 30%
- Bollinger Band width < 20%
- Not in clear downtrend (EMA20 >= EMA50 * 0.95)
- ENTRY: Confirmed RSI bounce
- RSI(14) dipped below 35 in last 3 bars
- RSI now recovering above 40 and rising
- Price making higher low (not new lows)
- Current bar is bullish
- Price below middle Bollinger Band (room to run)
- EXIT: Mean reversion targets
- RSI > 65 (overbought)
- Price at/above upper Bollinger Band
- Above middle BB with > 3% profit
- Time stop after 10 bars
- RSI turning down while still low
- RISK: 4% stop loss, 8% take profit (2:1 R/R)
Role: mean_reversion
Train Performance: +4.8% total (2024: +3.4%, 2025H1: +1.3%)
Metrics: 27 trades, 51.9% win rate, 12.3% max DD, 0.28 Sharpe
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, rsi, bollinger_bands, atr
def init_strategy():
return {
'name': 'sol_rsi_confirmed_bounce',
'role': 'momentum',
'warmup': 60,
'role': 'mean_reversion',
'subscriptions': [
{'symbol': 'SOLUSDT', 'exchange': 'binance', 'timeframe': '4h'},
],
'parameters': {
'rsi_period': 14,
'rsi_oversold': 35,
'rsi_recovery': 40,
'rsi_overbought': 65,
'bb_period': 20,
'bb_std': 2,
'ema_fast': 20,
'ema_slow': 50,
'stop_loss_pct': 4,
'take_profit_pct': 8,
'max_range_pct': 30,
'max_bb_width': 20,
}
}
def process_time_step(ctx):
key = ('SOLUSDT', 'binance')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
if i < 60:
return []
curr = bars[i]
prev = bars[i-1]
# Calculate indicators over lookback window
closes = [bars[j].close for j in range(max(0, i-120), i+1)]
highs = [bars[j].high for j in range(max(0, i-120), i+1)]
lows = [bars[j].low for j in range(max(0, i-120), i+1)]
# RSI (14 period)
rsi_vals = rsi(closes, 14)
if rsi_vals[-1] is None or len(rsi_vals) < 5:
return []
curr_rsi = rsi_vals[-1]
prev_rsi = rsi_vals[-2]
prev2_rsi = rsi_vals[-3]
prev3_rsi = rsi_vals[-4]
# EMAs for trend detection
ema20_vals = ema(closes, 20)
ema50_vals = ema(closes, 50)
if ema20_vals[-1] is None or ema50_vals[-1] is None:
return []
ema20 = ema20_vals[-1]
ema50 = ema50_vals[-1]
# Bollinger Bands for range and targets
bb_mid, bb_upper, bb_lower = bollinger_bands(closes, 20, 2.0)
if bb_upper[-1] is None or bb_lower[-1] is None:
return []
bb_width = (bb_upper[-1] - bb_lower[-1]) / bb_mid[-1] * 100
# ========== REGIME FILTER: RANGING (NOT STRONGLY TRENDING) ==========
# Calculate 20-bar price range
last_20_high = max(bars[j].high for j in range(i-20, i+1))
last_20_low = min(bars[j].low for j in range(i-20, i+1))
range_20 = (last_20_high - last_20_low) / last_20_low * 100
# Regime conditions
moderate_range = range_20 < 30 # Not trending hard
normal_bb = bb_width < 20 # Volatility not extreme
is_ranging = moderate_range and normal_bb
# HARD FILTER: No trading in clear downtrend
clear_downtrend = ema20 < ema50 * 0.95
actions = []
if key not in positions:
# Skip if in downtrend
if clear_downtrend:
return []
# Skip if not in ranging conditions
if not is_ranging:
return []
# ========== ENTRY: CONFIRMED RSI BOUNCE ==========
# Wait for RSI V-shape: oversold then confirmed recovery
# RSI dipped below 35 in last 3 bars
was_oversold = min(prev_rsi or 100, prev2_rsi or 100, prev3_rsi or 100) < 35
# RSI now clearly recovering above 40 and rising
confirmed_recovery = curr_rsi > 40 and curr_rsi > prev_rsi
# Price making higher low (not breaking down)
recent_swing_low = min(bars[j].low for j in range(i-5, i))
higher_low = curr.low >= recent_swing_low
# Current bar is bullish
bullish = curr.close > curr.open
# Still below middle BB (room to run to target)
room_to_run = curr.close < bb_mid[-1]
# Entry conditions met
if was_oversold and confirmed_recovery and higher_low and bullish and room_to_run:
actions.append({
'action': 'open_long',
'symbol': 'SOLUSDT',
'exchange': 'binance',
'size': 1.0,
'stop_loss_pct': 4,
'take_profit_pct': 8,
})
else:
# ========== EXIT CONDITIONS ==========
pos = positions[key]
bars_held = i - pos.entry_bar
current_pnl = (curr.close - pos.entry_price) / pos.entry_price * 100
# 1. RSI overbought - mean reverted successfully
if curr_rsi > 65:
actions.append({'action': 'close_long', 'symbol': 'SOLUSDT', 'exchange': 'binance'})
# 2. Upper Bollinger Band reached - target hit
elif curr.close >= bb_upper[-1]:
actions.append({'action': 'close_long', 'symbol': 'SOLUSDT', 'exchange': 'binance'})
# 3. Above middle BB with decent profit - partial target
elif curr.close > bb_mid[-1] and current_pnl > 3:
actions.append({'action': 'close_long', 'symbol': 'SOLUSDT', 'exchange': 'binance'})
# 4. Time stop - mean reversion should be quick
elif bars_held >= 10:
actions.append({'action': 'close_long', 'symbol': 'SOLUSDT', 'exchange': 'binance'})
# 5. RSI turning down while still in lower half - bounce failing
elif curr_rsi < prev_rsi and curr_rsi < 45 and bars_held >= 2:
actions.append({'action': 'close_long', 'symbol': 'SOLUSDT', 'exchange': 'binance'})
return actions
# Entry/Exit logic for documentation
ENTRY_LOGIC = """
REGIME FILTER (must pass ALL):
- 20-bar price range < 30% (not trending)
- Bollinger Band width < 20% (low volatility)
- EMA20 >= EMA50 * 0.95 (not in downtrend)
ENTRY (must pass ALL):
- RSI(14) was below 35 in last 3 bars
- RSI now > 40 AND rising
- Price making higher low (last 5 bars)
- Current bar is bullish (close > open)
- Price below middle Bollinger Band
"""
EXIT_LOGIC = """
EXIT when ANY:
- RSI > 65 (overbought - mean reverted)
- Price >= upper Bollinger Band
- Price > middle BB AND profit > 3%
- Held >= 10 bars (time stop)
- RSI dropping AND < 45 AND held >= 2 bars (bounce failing)
STOPS:
- Stop loss: 4%
- Take profit: 8%
"""