← Back to list

eth_chop_reversal_safe VALIDATED FAIL

Auto-discovered strategy

Symbol: ETH | Exchange: Bitfinex | Role: mean_reversion

2/6
Profitable Years
-35.2%
Total Return
57.0%
Avg Win Rate
-0.29
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 -13.7% 54.8% 42 19.2% -0.66
2021 -34.2% 46.2% 39 37.3% -1.54
2022 -14.7% 51.3% 39 21.1% -0.67
2023 +10.3% 63.7% 80 22.7% 0.48
2024 +33.2% 70.9% 55 15.2% 1.46
2025 -16.1% 55.3% 47 33.0% -0.80

Performance Chart

Loading chart...

Walk-Forward Validation FAIL

0/1 Windows Profitable
-27.0% OOS Return
0.00 Median Sharpe
0.000 Score
Window Train Period Val Period Val Return Val Test Period Test Return Status
WF-1 2024-01→2025-06 2025-07→2025-12 -27.0% FAIL 2026-01→ongoing +0.0% FAIL

AI Review

Not yet reviewed. Run: ./review_strategy.sh eth_chop_reversal_safe

Source Code

"""
Strategy: eth_chop_reversal_safe
================================
Mean reversion ETH range trading - fade extremes with strict chop regime filter.

Role: mean_reversion

Symbol: tETHUSD | Exchange: bitfinex | Timeframe: 4h

Logic:
- Only trade in CHOP regime (EMA50 within 5% of EMA200)
- BUY when bullish rejection candle appears at lower Bollinger Band
- SELL when bearish rejection candle appears at upper Bollinger Band
- Exit at EMA20 (mean reversion target) or on regime change
- Strict stop loss (4%) and take profit (6%)

Entry Conditions (Long):
1. Chop regime: |EMA50 - EMA200| / EMA200 < 5%
2. Not in strong downtrend (EMA50 > EMA200 * 0.95)
3. Bullish rejection candle: close in upper 50%, lower wick > 40% of range, green candle
4. Near support: low < lower Bollinger Band * 1.02
5. Significant bar: range > ATR(20) * 0.5
6. Decent volume: volume > 20-bar avg * 0.5

Entry Conditions (Short):
1. Chop regime: |EMA50 - EMA200| / EMA200 < 5%
2. Not in strong uptrend (EMA50 < EMA200 * 1.05)
3. Bearish rejection candle: close in lower 50%, upper wick > 40% of range, red candle
4. Near resistance: high > upper Bollinger Band * 0.98
5. Significant bar: range > ATR(20) * 0.5
6. Decent volume: volume > 20-bar avg * 0.5

Exit Conditions:
1. Price crosses EMA20 (mean reversion complete) after 2 bars
2. Regime change to strong trend (exit to avoid trending against position)
3. Time stop: 20 bars maximum hold
4. Stop loss: 4%
5. Take profit: 6%

Train Results (2024-01 to 2025-06):
  2024: +33.2% | 55 trades | 71% WR | 15.2% DD | 1.46 Sharpe
  2025: +8.2%  | 12 trades | 67% WR | 4.0% DD  | 1.07 Sharpe
  Total: +41.4% | 2/2 train years profitable
"""

import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema, bollinger_bands, atr


def init_strategy():
    return {
        'name': 'eth_chop_reversal_safe',
        'role': 'mean_reversion',
        'warmup': 220,
        'role': 'mean_reversion',
        'subscriptions': [
            {'symbol': 'tETHUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {
            'ema_fast': 20,
            'ema_slow': 50,
            'ema_trend': 200,
            'bb_period': 20,
            'bb_std': 2.0,
            'atr_period': 20,
            'chop_threshold': 5.0,
            'stop_loss_pct': 4.0,
            'take_profit_pct': 6.0,
        }
    }


def process_time_step(ctx):
    key = ('tETHUSD', 'bitfinex')
    bars = ctx['bars'][key]
    i = ctx['i']
    positions = ctx['positions']

    # Need enough warmup for EMA200
    if i < 220:
        return []

    # Calculate indicators
    closes = [bars[j].close for j in range(i + 1)]
    highs = [bars[j].high for j in range(i + 1)]
    lows = [bars[j].low for j in range(i + 1)]
    volumes = [bars[j].volume for j in range(i + 1)]

    ema20 = ema(closes, 20)
    ema50 = ema(closes, 50)
    ema200 = ema(closes, 200)
    bb_mid, bb_upper, bb_lower = bollinger_bands(closes, 20, 2.0)
    atr_vals = atr(highs, lows, closes, 20)

    # Current values
    curr_close = closes[-1]
    curr_ema20 = ema20[-1]
    curr_ema50 = ema50[-1]
    curr_ema200 = ema200[-1]
    curr_bb_lower = bb_lower[-1]
    curr_bb_mid = bb_mid[-1]
    curr_bb_upper = bb_upper[-1]
    curr_atr = atr_vals[-1]

    if curr_ema50 is None or curr_ema200 is None or curr_bb_lower is None or curr_atr is None:
        return []

    # REGIME DETECTION: Chop = EMA50 within 5% of EMA200
    ema_spread = abs(curr_ema50 - curr_ema200) / curr_ema200 * 100
    is_chop = ema_spread < 5.0

    # Trend detection for safety
    is_downtrend = curr_ema50 < curr_ema200 * 0.95
    is_uptrend = curr_ema50 > curr_ema200 * 1.05

    # Volume average
    vol_avg = sum(volumes[-20:]) / 20
    curr_vol = volumes[-1]

    actions = []
    curr_bar = bars[i]

    if key not in positions:
        # Only trade in chop regime
        if is_chop:
            bar_range = curr_bar.high - curr_bar.low
            if bar_range > 0:
                # Candle analysis
                lower_wick = min(curr_bar.open, curr_bar.close) - curr_bar.low
                upper_wick = curr_bar.high - max(curr_bar.open, curr_bar.close)
                close_pos = (curr_bar.close - curr_bar.low) / bar_range
                lower_wick_ratio = lower_wick / bar_range
                upper_wick_ratio = upper_wick / bar_range

                # Quality filters
                big_enough = bar_range > curr_atr * 0.5
                vol_ok = curr_vol > vol_avg * 0.5

                # LONG: Bullish rejection at support
                is_bullish = (
                    close_pos > 0.5 and
                    lower_wick_ratio > 0.4 and
                    curr_bar.close >= curr_bar.open
                )
                near_support = curr_bar.low < curr_bb_lower * 1.02

                if is_bullish and near_support and big_enough and vol_ok and not is_downtrend:
                    actions.append({
                        'action': 'open_long',
                        'symbol': 'tETHUSD',
                        'exchange': 'bitfinex',
                        'size': 1.0,
                        'stop_loss_pct': 4.0,
                        'take_profit_pct': 6.0,
                    })

                # SHORT: Bearish rejection at resistance
                is_bearish = (
                    close_pos < 0.5 and
                    upper_wick_ratio > 0.4 and
                    curr_bar.close <= curr_bar.open
                )
                near_resistance = curr_bar.high > curr_bb_upper * 0.98

                if is_bearish and near_resistance and big_enough and vol_ok and not is_uptrend:
                    actions.append({
                        'action': 'open_short',
                        'symbol': 'tETHUSD',
                        'exchange': 'bitfinex',
                        'size': 1.0,
                        'stop_loss_pct': 4.0,
                        'take_profit_pct': 6.0,
                    })
    else:
        # EXIT CONDITIONS
        pos = positions[key]
        bars_held = i - pos.entry_bar

        # Regime change exit (trend against position)
        regime_exit = (
            (pos.side == 'long' and is_downtrend) or
            (pos.side == 'short' and is_uptrend)
        )

        if pos.side == 'long':
            # Mean reversion target: price above EMA20
            if curr_close > curr_ema20 and bars_held >= 2:
                actions.append({
                    'action': 'close_long',
                    'symbol': 'tETHUSD',
                    'exchange': 'bitfinex',
                })
            # Regime change exit
            elif regime_exit and bars_held >= 2:
                actions.append({
                    'action': 'close_long',
                    'symbol': 'tETHUSD',
                    'exchange': 'bitfinex',
                })
            # Time stop
            elif bars_held >= 20:
                actions.append({
                    'action': 'close_long',
                    'symbol': 'tETHUSD',
                    'exchange': 'bitfinex',
                })
        else:  # short
            # Mean reversion target: price below EMA20
            if curr_close < curr_ema20 and bars_held >= 2:
                actions.append({
                    'action': 'close_short',
                    'symbol': 'tETHUSD',
                    'exchange': 'bitfinex',
                })
            # Regime change exit
            elif regime_exit and bars_held >= 2:
                actions.append({
                    'action': 'close_short',
                    'symbol': 'tETHUSD',
                    'exchange': 'bitfinex',
                })
            # Time stop
            elif bars_held >= 20:
                actions.append({
                    'action': 'close_short',
                    'symbol': 'tETHUSD',
                    'exchange': 'bitfinex',
                })

    return actions


# For standalone testing
if __name__ == '__main__':
    from strategy import backtest_strategy
    results, profitable, _ = backtest_strategy(init_strategy, process_time_step)
    print(f"\nProfitable periods: {profitable}")