← Back to list

eth_vol_spike_hedge VALIDATED FAIL

Auto-discovered strategy

Symbol: ETH | Exchange: Bitfinex | Role: hedge

4/6
Profitable Years
+16.9%
Total Return
41.0%
Avg Win Rate
-0.03
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 +13.7% 54.5% 11 7.8% 0.84
2021 +8.8% 54.5% 11 7.8% 0.54
2022 -26.4% 20.0% 10 23.8% -2.95
2023 +0.0% 0.0% 0 0.0% 0.00
2024 +6.0% 60.0% 10 7.8% 0.43
2025 +14.8% 57.1% 7 4.0% 0.97

Performance Chart

Loading chart...

Walk-Forward Validation FAIL

0/1 Windows Profitable
+0.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 +0.0% FAIL 2026-01→ongoing +0.0% FAIL

AI Review Score: 25/100

overfitting inconsistent concentration complexity
## Critical Issues ### 1. Validation Period Failure (Fatal) The strategy produces **0% return with 0 trades** in the validation period, while showing 16.85% total return in training. This is a complete failure - the pattern it learned doesn't generalize to unseen data. ### 2. Severe Year-to-Year Inconsistency The year-by-year breakdown reveals extreme instability: - **2022**: -26.4% return, 20% win rate, -2.95 Sharpe (catastrophic failure) - **2023**: 0% return, 0 trades (pattern didn't exist) - **2020-2021, 2024-2025**: Positive returns with reasonable Sharpe This is classic overfitting to specific market conditions. A robust hedge should provide some protection consistently, not collapse in certain years. ### 3. Over-Engineered Entry Conditions The strategy requires **5 simultaneous conditions** for entry: 1. Volume > 3x average 2. Bar down > 5% 3. RSI < 30 4. Price < EMA50 5. Lower wick > 20% of range This is excessive filtering that creates a brittle pattern. The more conditions you stack, the more you're curve-fitting to historical coincidences rather than robust market dynamics. ### 4. Suspiciously Specific Thresholds Multiple parameters appear curve-fitted: - **3.0x volume threshold**: Why exactly 3.0? Not a round standard value - **5% drop requirement**: Too precise - **20% wick ratio**: Oddly specific - **RSI exit at 55**: Not a standard level (30/70 are standard) These look optimized to historical data rather than based on market theory. ### 5. Low Trade Count With only **49 total trades** across 5.5 years of training data (average 9 trades/year), there's insufficient statistical significance. The strategy is too selective, and results are highly dependent on a few specific setups. ### 6. Execution Concerns The strategy assumes: - Perfect detection of "capitulation" on a 4h bar close - Ability to enter at next bar open after extreme volatility - No slippage consideration during panic selling events In reality, entering during volume spikes involves significant slippage and execution risk. ## Why It Failed Validation The strategy learned a very specific pattern that occurred in certain historical periods but doesn't represent a persistent market inefficiency. The combination of 5 conditions creates a "phantom pattern" - it looks good in backtest but doesn't exist going forward. ## Positive Notes - Uses standard indicators (RSI, EMA) rather than proprietary metrics - No lookahead bias detected - Time-based exits show risk awareness - Code quality is clean and readable ## Recommendation **REJECT** - This strategy fails validation completely and shows severe overfitting. The 2022 collapse and 2023 absence demonstrate it doesn't work across market regimes. Starting from scratch with a simpler, more robust approach would be better than trying to salvage this.
Reviewed: 2026-01-14T06:10:31.085186

Source Code

"""
ETH Volume Spike Capitulation Hedge
====================================

A hedge strategy that goes LONG after extreme volume spike capitulation events
in ETH. Designed to capture the bounce that typically follows panic selling.

Role: hedge
Exchange: bitfinex
Symbol: tETHUSD

Concept:
- After extreme volume spike + price drop (capitulation), bounces typically follow
- Wait for very high confidence setup: volume >3x avg + >5% drop + RSI oversold
- Take quick profits on the bounce, tight stop loss
- Regime filter: only trade when below EMA50 (not in extended bull)

Entry Conditions (all must be met):
1. Volume > 3x 20-bar average (extreme spike)
2. Bar closed down > 5% (significant panic)
3. RSI(10) < 30 (deeply oversold)
4. Price below EMA50 (not extended, in pullback)
5. Lower wick > 20% of range (rejection/capitulation)

Exit Conditions:
1. RSI > 55 (momentum recovered - take profit)
2. Two consecutive green candles (bounce confirmed)
3. Time stop: 10 bars (quick in/out)
4. Take profit: 8%
5. Stop loss: 5%
"""

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


def init_strategy():
    return {
        'name': 'eth_vol_spike_hedge',
        'role': 'hedge',
        'warmup': 100,
        'subscriptions': [
            {'symbol': 'tETHUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {
            # Volume detection
            'vol_lookback': 20,
            'vol_threshold': 3.0,  # Volume > 3x average
            # Price drop threshold
            'min_drop_pct': 5,
            # RSI settings
            'rsi_period': 10,
            'rsi_oversold': 30,
            'rsi_exit': 55,
            # EMA regime filter
            'ema_period': 50,
            # Wick filter
            'min_wick_ratio': 0.2,
            # Exit settings
            'take_profit': 8,
            'stop_loss': 5,
            'time_stop': 10,
        }
    }


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

    # Extract price data up to current bar
    closes = [b.close for b in bars[:i+1]]
    highs = [b.high for b in bars[:i+1]]
    lows = [b.low for b in bars[:i+1]]
    opens = [b.open for b in bars[:i+1]]
    volumes = [b.volume for b in bars[:i+1]]

    idx = len(closes) - 1

    if idx < params['vol_lookback'] + 5:
        return []

    # Calculate indicators
    rsi_vals = rsi(closes, params['rsi_period'])
    ema_vals = ema(closes, params['ema_period'])

    # Handle RSI indexing
    if len(rsi_vals) == 0:
        return []
    rsi_idx = min(idx, len(rsi_vals) - 1)
    current_rsi = rsi_vals[rsi_idx]
    if current_rsi is None:
        return []

    # Get EMA value
    current_ema = ema_vals[idx] if idx < len(ema_vals) else None
    if current_ema is None:
        return []

    # === VOLUME SPIKE ===
    vol_start = max(0, idx - params['vol_lookback'])
    avg_vol = sum(volumes[vol_start:idx]) / params['vol_lookback'] if idx > vol_start else 1
    vol_spike = volumes[idx] > params['vol_threshold'] * avg_vol if avg_vol > 0 else False

    # === PRICE DROP ===
    bar_drop_pct = (closes[idx] - opens[idx]) / opens[idx] * 100 if opens[idx] > 0 else 0
    significant_drop = bar_drop_pct < -params['min_drop_pct']

    # === RSI OVERSOLD ===
    oversold = current_rsi < params['rsi_oversold']

    # === BELOW EMA50 ===
    below_ema = closes[idx] < current_ema * 1.02  # Allow small tolerance

    # === LOWER WICK (capitulation) ===
    bar_range = highs[idx] - lows[idx]
    lower_wick = min(opens[idx], closes[idx]) - lows[idx]
    wick_ratio = lower_wick / bar_range if bar_range > 0 else 0
    good_wick = wick_ratio > params['min_wick_ratio']

    actions = []

    if key not in positions:
        # Entry: all conditions must be met
        if vol_spike and significant_drop and oversold and below_ema and good_wick:
            state['green_count'] = 0

            actions.append({
                'action': 'open_long',
                'symbol': 'tETHUSD',
                'exchange': 'bitfinex',
                'size': 1.0,
                'take_profit_pct': params['take_profit'],
                'stop_loss_pct': params['stop_loss'],
            })
    else:
        pos = positions[key]
        if pos.side != 'long':
            return []

        bars_held = i - pos.entry_bar

        # Count green candles
        if closes[idx] > opens[idx]:
            state['green_count'] = state.get('green_count', 0) + 1
        else:
            state['green_count'] = 0

        # Exit conditions

        # 1. RSI recovery (primary exit)
        rsi_recovered = current_rsi > params['rsi_exit']

        # 2. Two consecutive green candles
        green_exit = state.get('green_count', 0) >= 2

        # 3. Time stop
        time_exit = bars_held >= params['time_stop']

        if rsi_recovered or green_exit or time_exit:
            actions.append({
                'action': 'close_long',
                'symbol': 'tETHUSD',
                'exchange': 'bitfinex',
            })

    return actions


# Testing
if __name__ == '__main__':
    from strategy import backtest_strategy
    results, profitable, _ = backtest_strategy(init_strategy, process_time_step)