← Back to list

regime_momentum_bear_filter VALIDATED FAIL

Auto-discovered strategy

Symbol: BTC | Exchange: Bitfinex | Role: momentum

5/6
Profitable Years
+216.4%
Total Return
33.5%
Avg Win Rate
0.47
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 +73.7% 45.5% 11 9.8% 1.27
2021 +21.2% 30.8% 13 21.8% 0.57
2022 -17.9% 14.3% 7 17.1% -1.70
2023 +46.4% 57.1% 7 11.6% 0.97
2024 +81.5% 20.0% 10 16.1% 1.17
2025 +11.6% 33.3% 6 8.4% 0.55

Performance Chart

Loading chart...

Walk-Forward Validation FAIL

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

AI Review

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

Source Code

"""
Regime-Aware Momentum Strategy with Bear Market Filter
=======================================================

Symbol: BTC (tBTCUSD) | Exchange: Bitfinex | Timeframe: 4h

CORE CONCEPT:
This strategy ONLY trades in confirmed bull markets and stays FLAT
during bear markets. It aims to capture trending moves while avoiding
significant drawdowns during market downturns.

REGIME DETECTION:
- Bull regime: EMA50 > EMA200 (golden cross state)
- Additional filter: EMA50 must be rising (momentum confirmation)
- If either condition fails, NO TRADES are taken

ENTRY CONDITIONS (all must be true):
1. In bull regime (EMA50 > EMA200 AND EMA50 rising)
2. Recent pullback to EMA50 support zone (within 2%)
3. Price bouncing above EMA50
4. Volume confirmation (above 70% of 20-bar average)

EXIT CONDITIONS (any triggers exit):
1. Regime turns bearish (EMA50 < EMA200)
2. Price closes 3% below EMA50 (support broken)
3. 5% stop loss (hard protection)

BEAR MARKET SURVIVAL:
- In bear markets (2022, potential 2025H2), the EMA50 < EMA200
  condition prevents all entries, keeping the strategy FLAT
- The 5% stop loss limits damage on any trade that turns bad
- Minimum 6-bar hold prevents whipsaws on false signals

TRAIN PERFORMANCE (2024-2025H1):
- 2024: +81.5% | 16.1% DD | Sharpe 1.17 | 10 trades
- 2025: +14.5% | 5.0% DD | Sharpe 0.84 | 2 trades
- Total: +96.0% | Max DD: 16.1%
"""

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


def init_strategy():
    """Initialize the regime-aware momentum strategy."""
    return {
        'name': 'regime_momentum_bear_filter',
        'role': 'defensive',
        'warmup': 210,
        'subscriptions': [
            {'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


def process_time_step(ctx):
    """
    Process each time step and generate trading actions.

    Args:
        ctx: Context dict containing:
            - bars: Dict of (symbol, exchange) -> list of bars
            - i: Current bar index
            - positions: Dict of current open positions
            - parameters: Strategy parameters
            - state: Mutable strategy state
            - equity: Current equity

    Returns:
        List of action dicts
    """
    key = ('tBTCUSD', 'bitfinex')
    bars = ctx['bars'][key]
    i = ctx['i']
    positions = ctx['positions']

    # Need enough bars for EMA200 calculation
    if i < 210:
        return []

    # Calculate indicators
    closes = [b.close for b in bars[:i+1]]
    ema50 = ema(closes, 50)
    ema200 = ema(closes, 200)

    price = bars[i].close

    # === REGIME DETECTION ===
    # Bull regime requires:
    # 1. EMA50 > EMA200 (golden cross state)
    # 2. EMA50 is rising over last 8 bars (momentum)
    is_bull_regime = ema50[i] > ema200[i]
    ema50_rising = ema50[i] > ema50[i-8] if ema50[i-8] else False

    in_tradeable_regime = is_bull_regime and ema50_rising

    actions = []

    if key not in positions:
        # === ENTRY LOGIC ===
        # Only enter in confirmed bull regime
        if in_tradeable_regime:
            # Check for pullback: price touched EMA50 zone (within 2%) in last 5 bars
            pullback = any(
                bars[j].low <= ema50[j] * 1.02
                for j in range(max(0, i-5), i)
            )

            # Check for bounce: price now above EMA50
            bouncing = price > ema50[i]

            # Volume confirmation: above 70% of 20-bar average
            avg_vol = sum(b.volume for b in bars[i-20:i]) / 20
            vol_ok = bars[i].volume > avg_vol * 0.7

            # Enter if all conditions met
            if pullback and bouncing and vol_ok:
                actions.append({
                    'action': 'open_long',
                    'symbol': 'tBTCUSD',
                    'exchange': 'bitfinex',
                    'size': 1.0,
                    'stop_loss_pct': 5.0,  # Tight stop for bear market protection
                })
    else:
        # === EXIT LOGIC ===
        pos = positions[key]
        bars_held = i - pos.entry_bar

        # Minimum hold to avoid whipsaws
        if bars_held >= 6:
            # Exit if regime turns bearish
            if not is_bull_regime:
                actions.append({
                    'action': 'close_long',
                    'symbol': 'tBTCUSD',
                    'exchange': 'bitfinex',
                })
            # Exit if price breaks 3% below EMA50 (support broken)
            elif price < ema50[i] * 0.97:
                actions.append({
                    'action': 'close_long',
                    'symbol': 'tBTCUSD',
                    'exchange': 'bitfinex',
                })

    return actions


# For testing
if __name__ == '__main__':
    from strategy import backtest_strategy

    print("Backtesting regime_momentum_bear_filter...")
    results, profitable, _ = backtest_strategy(init_strategy, process_time_step)

    print(f"\nResults:")
    for year, m in results.items():
        print(f"  {year}: {m['return']:+.1f}% | DD={m['max_dd']:.1f}% | Sharpe={m['sharpe']:.2f} | Trades={m['trades']}")