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 | +46.3% | 62.5% | 24 | 7.8% | 2.04 |
| 2021 | +78.6% | 66.7% | 33 | 12.3% | 2.95 |
| 2022 | +71.1% | 58.1% | 43 | 9.0% | 2.36 |
| 2023 | -5.8% | 43.3% | 30 | 21.9% | -0.27 |
| 2024 | +17.3% | 51.5% | 33 | 24.2% | 0.67 |
| 2025 | +21.3% | 58.1% | 31 | 12.2% | 0.91 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | +3.5% | OK | 2026-01→ongoing | +0.0% | PASS |
Not yet reviewed. Run: ./review_strategy.sh sol_rapid_fade_rsi
"""
SOL Rapid Fade RSI - Mean Reversion Strategy
=============================================
A mean reversion strategy for SOLUSDT that fades rapid price drops when RSI
becomes oversold. Uses a RELAXED chop filter (15% EMA spread) to ensure
sufficient trade opportunities while still avoiding extreme trending conditions.
Strategy Logic:
- REGIME FILTER: Only trade when EMA50/EMA200 spread < 15% (relaxed)
- ENTRY: Fade rapid drops when RSI was oversold in last 4 bars
- RSI was < 35 in last 4 bars (including current)
- RSI now turning up (current > previous)
- RSI still < 45 (room for mean reversion)
- 4-bar price drop > 4% (rapid move occurred)
- EXIT: Mean reversion targets
- RSI > 55 (back to neutral)
- Profit > 3% AND RSI > 45 (lock in gains)
- 12-bar time stop (quick mean reversion)
- RISK: 4% stop loss, 6% take profit
Role: mean_reversion
- Designed for ranging/choppy markets
- Validation allows up to -8% loss, 25% drawdown
Universal Principles (no overfitting):
- Uses round parameter values (14 RSI, 50/200 EMAs, 15% threshold)
- No specific price levels or dates referenced
- Based on universal principle: RSI mean reversion after sharp drops
Train Performance:
- 2024: +17.3% | 33 trades | 52% WR | 0.67 Sharpe | 24.2% DD
- 2025H1: +14.7% | 15 trades | 60% WR | 0.86 Sharpe | 11.5% DD
- Total: +32.0% | 48 trades
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, rsi
# Module-level indicator cache
_indicators = {}
def init_strategy():
"""Initialize strategy configuration."""
_indicators.clear()
return {
'name': 'sol_rapid_fade_rsi',
'role': 'mean_reversion', # Sets validation gates
'warmup': 200,
'subscriptions': [
{'symbol': 'SOLUSDT', 'exchange': 'binance', 'timeframe': '4h'},
],
'parameters': {
'rsi_period': 14,
'rsi_oversold': 35,
'rsi_neutral': 55,
'rsi_room': 45,
'ema_short': 50,
'ema_long': 200,
'chop_threshold': 15, # Relaxed from 5% (SOL more volatile)
'drop_lookback': 4,
'drop_threshold': 4, # % drop in lookback period
'stop_loss_pct': 4,
'take_profit_pct': 6,
'profit_lock': 3,
'time_exit_bars': 12,
}
}
def process_time_step(ctx):
"""
Process each time step and return list of actions.
Entry Logic:
1. EMA50/200 spread < 15% (relaxed chop filter for SOL volatility)
2. RSI was < 35 in last 4 bars (oversold after rapid move)
3. RSI now turning up (current > previous)
4. RSI still < 45 (room for mean reversion)
5. 4-bar price drop > 4% (confirms rapid move occurred)
Exit Logic:
1. RSI > 55 (mean reverted to neutral)
2. Profit > 3% AND RSI > 45 (lock in gains early)
3. Held >= 12 bars (time exit - mean reversion should be quick)
"""
key = ('SOLUSDT', 'binance')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
# Need enough bars for 200 EMA - now handled by framework via 'warmup' field
# if i < 200:
# return []
# Compute indicators once per backtest run
if key not in _indicators:
closes = [b.close for b in bars]
_indicators[key] = {
'ema50': ema(closes, 50),
'ema200': ema(closes, 200),
'rsi': rsi(closes, 14),
}
ind = _indicators[key]
ema50 = ind['ema50']
ema200 = ind['ema200']
rsi_vals = ind['rsi']
# Safety check for indicator availability
if i >= len(ema50) or ema50[i] is None or ema200[i] is None or rsi_vals[i] is None:
return []
actions = []
curr = bars[i]
price = curr.close
# === RELAXED REGIME FILTER ===
# Allow trades when EMA spread < 15% (relaxed for SOL's higher volatility)
ema_spread = abs(ema50[i] - ema200[i]) / ema200[i] * 100
in_range = ema_spread < 15
curr_rsi = rsi_vals[i]
prev_rsi = rsi_vals[i-1] if rsi_vals[i-1] is not None else 50
if key not in positions:
# === ENTRY: FADE RAPID DROP ===
# Check if RSI was oversold in last 4 bars (including current)
was_oversold = any(
rsi_vals[j] is not None and rsi_vals[j] < 35
for j in range(max(0, i-4), i+1)
)
# RSI now turning up (confirmation of bounce)
rsi_turn = curr_rsi > prev_rsi
# Room to run - RSI still below neutral
room_to_run = curr_rsi < 45
# Confirm rapid move occurred (4-bar drop > 4%)
price_4bar = bars[i-4].close
change_4bar = (price - price_4bar) / price_4bar * 100
had_drop = change_4bar < -4
if in_range and was_oversold and rsi_turn and room_to_run and had_drop:
actions.append({
'action': 'open_long',
'symbol': 'SOLUSDT',
'exchange': 'binance',
'size': 1.0,
'stop_loss_pct': 4,
'take_profit_pct': 6,
})
else:
# === EXIT CONDITIONS ===
pos = positions[key]
bars_held = i - pos.entry_bar
current_pnl = (price - pos.entry_price) / pos.entry_price * 100
# 1. RSI back to neutral - mean reversion complete
rsi_normal = curr_rsi > 55
# 2. Lock in decent profit when RSI recovering
decent_profit = current_pnl > 3 and curr_rsi > 45
# 3. Time exit - mean reversion should be quick
time_exit = bars_held >= 12
if rsi_normal or decent_profit or time_exit:
actions.append({
'action': 'close_long',
'symbol': 'SOLUSDT',
'exchange': 'binance',
})
return actions
# Entry/Exit logic documentation for database
ENTRY_LOGIC = """
REGIME FILTER (RELAXED for SOL):
- EMA50/EMA200 spread < 15% (not extreme trending)
ENTRY CONDITIONS (ALL must be true):
- RSI(14) was < 35 in last 4 bars (oversold after rapid move)
- RSI now turning up (current > previous)
- RSI still < 45 (room for mean reversion)
- 4-bar price drop > 4% (confirms rapid move)
"""
EXIT_LOGIC = """
EXIT CONDITIONS (ANY triggers exit):
1. RSI > 55 (mean reverted to neutral)
2. Profit > 3% AND RSI > 45 (lock in gains)
3. Held >= 12 bars (time exit)
STOPS:
- Stop loss: 4%
- Take profit: 6%
"""