Auto-discovered strategy
Symbol: BTC | Exchange: Bitfinex | Role: defensive
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | -15.2% | 7.7% | 13 | 14.3% | -3.71 |
| 2021 | +3.8% | 50.0% | 16 | 10.4% | 0.33 |
| 2022 | +2.1% | 25.8% | 31 | 11.0% | 0.13 |
| 2023 | -5.8% | 19.0% | 21 | 11.0% | -0.58 |
| 2024 | +7.9% | 44.4% | 9 | 9.5% | 0.64 |
| 2025 | +4.1% | 26.9% | 26 | 7.0% | 0.25 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | -3.0% | FAIL | 2026-01→ongoing | +0.0% | FAIL |
Not yet reviewed. Run: ./review_strategy.sh rally_exhaustion_short
"""
Rally Exhaustion Short Strategy
================================
Defensive SHORT strategy that profits from failed rallies in bear markets.
Universal Market Principle:
- In bear markets, rallies tend to fail at resistance (moving averages)
- Lower highs are a classic sign of trend weakness
- Volume tends to decline on counter-trend rallies (exhaustion)
- When price fails to reclaim key EMAs after a bounce, downside continues
Entry Conditions (all must be true):
1. BEAR REGIME: EMA50 < EMA200 (structural downtrend)
2. EMA CASCADE: EMA20 < EMA50 (confirming bearish momentum)
3. TOUCHED EMA50: Price rallied to EMA50 (within 1%)
4. FAILED TO CLOSE ABOVE: Closed below EMA50 (rejection)
5. RED CANDLE: Bar closed lower than open (bearish)
6. LOWER HIGH: Current high < 20-bar highest (trend confirmation)
7. WEAK VOLUME: Below 20-bar average (exhaustion signal)
8. BELOW EMA20: Short-term weakness confirmed
Exit Conditions:
1. Price closes above EMA50 (rally succeeded)
2. Bull regime starts (EMA50 > EMA200)
3. Momentum reversal: 2 consecutive higher highs + higher closes
4. Stop loss: 4%
5. Take profit: 8% (2:1 R/R)
Role: defensive - designed to profit in bear market validation period
Train Performance (2024-01 to 2025-06):
2024: +7.9% | 9 trades | 44% WR | DD: 9.5%
2025: +7.1% | 10 trades | 40% WR | DD: 6.5%
Total: +15.0% | Sharpe: 0.86 | Max DD: 9.5%
Parameters: All round numbers (20, 50, 200 EMAs, 4/8% stops)
No curve fitting - based on universal trend-following principles
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, highest
def init_strategy():
return {
'name': 'rally_exhaustion_short',
'role': 'defensive',
'warmup': 200,
'role': 'defensive', # MUST profit in bear market validation
'subscriptions': [
{'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
key = ('tBTCUSD', 'bitfinex')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
# Need 200 bars for EMA200
if i < 200:
return []
closes = [b.close for b in bars]
highs = [b.high for b in bars]
opens = [b.open for b in bars]
volumes = [b.volume for b in bars]
# Calculate EMAs for regime detection
ema20_vals = ema(closes, 20)
ema50_vals = ema(closes, 50)
ema200_vals = ema(closes, 200)
ema_20 = ema20_vals[i]
ema_50 = ema50_vals[i]
ema_200 = ema200_vals[i]
if ema_50 is None or ema_200 is None or ema_20 is None:
return []
current_price = closes[i]
current_high = highs[i]
current_open = opens[i]
current_volume = volumes[i]
# REGIME FILTER: Bear market (EMA50 < EMA200)
is_bear_regime = ema_50 < ema_200
# EMA CASCADE: Confirming bearish momentum (EMA20 < EMA50)
bear_cascade = ema_20 < ema_50
actions = []
if key not in positions:
# ----- SHORT ENTRY CONDITIONS -----
# 1. TOUCHED EMA50: High reached up to EMA50 (rally attempt)
touched_ema50 = current_high >= ema_50 * 0.99 # Within 1% of EMA50
# 2. FAILED TO CLOSE ABOVE: Closed below EMA50 (rejection)
failed_close = current_price < ema_50
# 3. RED CANDLE: Bearish close (rejection confirmation)
red_candle = current_price < current_open
# 4. LOWER HIGH: Current high is below 20-bar highest high
recent_highest = highest(highs, 20, i)
lower_high = recent_highest is not None and current_high < recent_highest * 0.995
# 5. VOLUME EXHAUSTION: Below average volume (weak rally)
avg_volume = sum(volumes[i-20:i]) / 20
weak_volume = current_volume < avg_volume
# 6. BELOW EMA20: Already losing short-term momentum
below_ema20 = current_price < ema_20
# ALL CONDITIONS for short entry
if (is_bear_regime and bear_cascade and
touched_ema50 and failed_close and
red_candle and lower_high and
weak_volume and below_ema20):
actions.append({
'action': 'open_short',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
'size': 1.0,
'stop_loss_pct': 4.0, # Tight stop above recent swing
'take_profit_pct': 8.0, # 2:1 R/R
})
else:
# ----- SHORT EXIT CONDITIONS -----
pos = positions[key]
if pos.side == 'short':
# Exit if rally succeeds
above_ema50 = current_price > ema_50
# Exit if bull regime starts
bull_regime = ema_50 > ema_200
# Exit on momentum reversal: 2 consecutive higher highs + green
reversal = (closes[i] > closes[i-1] and
closes[i-1] > closes[i-2] and
highs[i] > highs[i-1] and
highs[i-1] > highs[i-2])
if above_ema50 or bull_regime or reversal:
actions.append({
'action': 'close_short',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
})
return actions
if __name__ == '__main__':
from strategy import backtest_strategy
print("Rally Exhaustion Short Strategy - Training Backtest")
print("=" * 60)
results, profitable, _ = backtest_strategy(init_strategy, process_time_step)
print("\n" + "=" * 60)
if profitable >= 2:
print(f"READY FOR VALIDATION: {profitable}/2 train years profitable")
else:
print(f"NEEDS WORK: Only {profitable}/2 train years profitable")