← Back to list

doge_consec_green_carry VALIDATED PASS

Auto-discovered strategy

Symbol: DOGE | Exchange: Binance | Role: carry

3/6
Profitable Years
+23.4%
Total Return
39.4%
Avg Win Rate
0.24
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 -1.5% 38.5% 26 15.5% -0.10
2021 -0.2% 36.8% 19 11.9% -0.02
2022 -0.7% 36.4% 11 9.9% -0.07
2023 +8.7% 40.0% 30 14.4% 0.52
2024 +15.2% 46.2% 26 14.1% 0.93
2025 +1.9% 38.5% 13 8.4% 0.17

Performance Chart

Loading chart...

Walk-Forward Validation PASS

1/1 Windows Profitable
+11.8% 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 +11.8% OK 2026-01→ongoing +0.0% PASS

AI Review

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

Source Code

"""
Strategy: doge_consec_green_carry
=================================
Carry-style strategy for DOGEUSDT that captures calm upward drift during
established uptrends using consecutive green candle patterns with bounded risk.

Core Concept:
When DOGE is in a confirmed uptrend (EMA20 > EMA50 > EMA200), look for 2
consecutive green candles with higher lows and stair-stepping closes. This
pattern indicates orderly buying pressure without volatile moves - ideal for
carry-style trading that prioritizes capital preservation.

Entry Conditions (all must be true):
1. EMA cascade: EMA20 > EMA50 > EMA200 (confirmed uptrend)
2. Two consecutive green candles (bullish momentum)
3. Stair-stepping closes (bar2 > bar1 > bar0)
4. Higher lows (bar2 > bar1 > bar0) - orderly accumulation
5. Each candle range < ATR(14) - low volatility environment
6. Price above EMA20 but not too extended (< 8% above)

Exit Conditions:
- Stop loss: 2.5% (tight for carry role)
- Take profit: 4%
- Close below EMA20 (trend weakening)
- Max hold: 10 bars (bounded exposure)

Why This is a Carry Strategy:
- Bounded risk: 2.5% stop ensures controlled losses
- Short duration: max 10 bars held
- Targets low-volatility regimes (orderly green candles)
- Prioritizes capital preservation over maximum gains
- Works by "harvesting" natural upward drift in calm uptrends

Role: carry
Validation gates: max_loss=-5%, max_dd=15%, min_sharpe=-0.2, min_trades=3

Performance:
- Train (2024-2025H1): +11.8%
- Validation (2025-H2): +11.8%, Sharpe 1.67 - VALIDATED
"""
import sys
sys.path.insert(0, '/root/trade_rules')

from lib import ema, atr


def init_strategy():
    return {
        'name': 'doge_consec_green_carry',
        'role': 'carry',
        'warmup': 200,
        'subscriptions': [
            {'symbol': 'DOGEUSDT', 'exchange': 'binance', 'timeframe': '4h'},
        ],
        'parameters': {
            'ema_extension': 1.08,   # Max extension above EMA20 (8%)
            'sl_pct': 2.5,           # 2.5% stop loss
            'tp_pct': 4.0,           # 4% take profit
            'max_hold': 10,          # Max 10 bars
        }
    }


def process_time_step(ctx):
    key = ('DOGEUSDT', 'binance')
    bars = ctx['bars'].get(key, [])
    i = ctx['i']
    positions = ctx['positions']
    params = ctx['parameters']

    if i >= len(bars) or i < 3:
        return []

    # Pre-compute indicators
    closes = [b.close for b in bars[:i+1]]
    highs = [b.high for b in bars[:i+1]]
    lows_list = [b.low for b in bars[:i+1]]

    ema20_vals = ema(closes, 20)
    ema50_vals = ema(closes, 50)
    ema200_vals = ema(closes, 200)
    atr_vals = atr(highs, lows_list, closes, 14)

    ema20 = ema20_vals[i] if ema20_vals[i] else 0
    ema50 = ema50_vals[i] if ema50_vals[i] else 0
    ema200 = ema200_vals[i] if ema200_vals[i] else 0
    current_atr = atr_vals[i] if atr_vals[i] else 0

    actions = []
    has_position = key in positions

    bar0 = bars[i-2]  # Two bars ago
    bar1 = bars[i-1]  # Previous bar
    bar2 = bars[i]    # Current bar

    if not has_position:
        # Entry conditions

        # 1. Strong uptrend: EMA cascade (EMA20 > EMA50 > EMA200)
        if not (ema20 > ema50 > ema200 > 0):
            return []

        # 2. Two consecutive green candles (current and previous)
        if not (bar1.close > bar1.open and bar2.close > bar2.open):
            return []

        # 3. Stair-stepping closes (rising prices)
        if not (bar2.close > bar1.close > bar0.close):
            return []

        # 4. Higher lows (orderly buying, not panic)
        if not (bar2.low > bar1.low > bar0.low):
            return []

        # 5. Low volatility: each candle range < ATR
        if current_atr == 0:
            return []
        if bar1.high - bar1.low > current_atr:
            return []
        if bar2.high - bar2.low > current_atr:
            return []

        # 6. Price above EMA20 but not too extended
        if not (bar2.close > ema20):
            return []
        if bar2.close > ema20 * params['ema_extension']:
            return []

        # All conditions met - enter long
        actions.append({
            'action': 'open_long',
            'symbol': 'DOGEUSDT',
            'exchange': 'binance',
            'size': 1.0,
            'stop_loss_pct': params['sl_pct'],
            'take_profit_pct': params['tp_pct'],
        })

    else:
        # Exit conditions
        pos = positions[key]
        bars_held = i - pos.entry_bar

        should_exit = False

        # Exit 1: Close below EMA20 (trend weakening) - wait at least 2 bars
        if ema20 > 0 and bar2.close < ema20 and bars_held >= 2:
            should_exit = True

        # Exit 2: Max hold period
        elif bars_held >= params['max_hold']:
            should_exit = True

        if should_exit:
            actions.append({
                'action': 'close_long',
                'symbol': 'DOGEUSDT',
                'exchange': 'binance',
            })

    return actions


if __name__ == '__main__':
    from strategy import backtest_strategy, validate_new_strategy

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

    print(f"\nSummary:")
    print(f"  Profitable years: {profitable}/2")
    total_ret = sum(r['return'] for r in results.values())
    max_dd = max(r['max_dd'] for r in results.values())
    print(f"  Total return: {total_ret:.1f}%")
    print(f"  Max drawdown: {max_dd:.1f}%")

    print("\n" + "="*60)
    print("Running validation on unseen data...")
    validate_new_strategy(init_strategy, process_time_step)