← Back to list

eth_dow_momentum VALIDATED FAIL

Auto-discovered strategy

Symbol: ETH | Exchange: Binance | Role: momentum

3/6
Profitable Years
+21.4%
Total Return
53.1%
Avg Win Rate
0.33
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 -3.3% 51.4% 74 20.4% -0.15
2021 -13.7% 49.2% 61 28.2% -0.54
2022 -6.5% 51.9% 27 14.9% -0.44
2023 +6.9% 46.4% 56 8.6% 0.51
2024 +21.4% 57.4% 47 9.1% 1.52
2025 +16.6% 62.5% 40 13.7% 1.10

Performance Chart

Loading chart...

Walk-Forward Validation FAIL

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

AI Review

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

Source Code

"""
ETH Day-of-Week Momentum Strategy
=================================

Exploits observed day-of-week momentum patterns in ETHUSDT on Binance.

Key Observations:
- Wednesday 16:00 UTC shows strong momentum continuation in uptrends
- Sunday 16:00 UTC shows positive returns as week kickoff
- Both patterns stronger when EMA50 > EMA200 (bull market regime)

Strategy Logic:
- ENTRY: Sunday or Wednesday at 16:00 UTC when in uptrend (EMA50 > EMA200)
- EXIT: After 3 bars (12 hours) OR if trend flips (EMA50 < EMA200)
- RISK: 5% stop loss, 5% take profit

Rationale:
- Mid-week and week-start show consistent momentum in bull markets
- Short holding period reduces exposure to weekend gaps
- Trend filter stays flat during bear markets
"""

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


def init_strategy():
    return {
        'name': 'eth_dow_momentum',
        'role': 'momentum',  # Can lose bounded amount in bear markets
        'warmup': 200,       # Need EMA200
        'subscriptions': [
            {'symbol': 'ETHUSDT', 'exchange': 'binance', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


def process_time_step(ctx):
    key = ('ETHUSDT', 'binance')
    bars = ctx['bars'][key]
    i = ctx['i']
    positions = ctx['positions']
    state = ctx['state']

    # Calculate EMAs
    closes = [b.close for b in bars[:i+1]]
    ema50_val = ema(closes, 50)[-1] if len(closes) >= 50 else None
    ema200_val = ema(closes, 200)[-1] if len(closes) >= 200 else None

    actions = []
    current_bar = bars[i]
    dow = current_bar.timestamp.weekday()  # 0=Monday, 6=Sunday
    hour = current_bar.timestamp.hour

    # Track entry bar for exit timing
    if 'entry_bar' not in state:
        state['entry_bar'] = None

    has_position = key in positions

    if not has_position:
        # ENTRY CONDITIONS:
        # 1. Uptrend: EMA50 > EMA200
        # 2. Day-of-week: Sunday or Wednesday at 16:00 UTC
        is_uptrend = (ema50_val is not None and
                      ema200_val is not None and
                      ema50_val > ema200_val)

        is_entry_time = (dow == 6 and hour == 16) or (dow == 2 and hour == 16)

        if is_uptrend and is_entry_time:
            actions.append({
                'action': 'open_long',
                'symbol': 'ETHUSDT',
                'exchange': 'binance',
                'size': 1.0,
                'stop_loss_pct': 5,      # 5% stop loss
                'take_profit_pct': 5,    # 5% take profit (caps gains for robustness)
            })
            state['entry_bar'] = i
    else:
        # EXIT CONDITIONS:
        # 1. Held for 3 bars (~12 hours)
        # 2. Trend flips (EMA50 < EMA200)
        bars_held = i - state['entry_bar'] if state['entry_bar'] else 0

        trend_flip = (ema50_val is not None and
                      ema200_val is not None and
                      ema50_val < ema200_val)

        should_exit = bars_held >= 3 or trend_flip

        if should_exit:
            actions.append({
                'action': 'close_long',
                'symbol': 'ETHUSDT',
                'exchange': 'binance',
            })
            state['entry_bar'] = None

    return actions


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