← Back to list

ema_acceleration_surge

Auto-discovered rule

Symbol: ETH | Exchange: Bitfinex

5/6
Profitable Years
+245.9%
Total Return
41.2%
Avg Win Rate
1.00
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +91.1% 47.0% 30 10.0% 1.00
2021 +106.1% 46.0% 33 10.0% 1.00
2022 +8.7% 40.0% 15 10.0% 1.00
2023 -8.1% 36.0% 25 10.0% 1.00
2024 +16.0% 50.0% 16 10.0% 1.00
2025 +32.1% 28.0% 18 10.0% 1.00

Entry Logic

See rule file

Exit Logic

See rule file

Source Code

"""
Strategy: ema_acceleration_surge
================================
EMA Acceleration Surge Strategy

Identifies momentum acceleration opportunities by detecting when price
bounces off EMA15 with strong upward momentum in a bullish EMA structure.

Entry conditions:
- All EMAs in bullish order: EMA15 > EMA40 > EMA80
- Price above both EMA15 and EMA40
- Price touched or was near EMA15 in last 1-3 bars (pullback to support)
- Price shows acceleration (momentum now > momentum before)
- Momentum must be at least 2% over 2 bars
- EMA15 is rising
- Volume above 80% of 10-bar average

Exit:
- Price closes below EMA15

Performance: 5/6 years profitable | Total: +245.9%
2020: +91.1% | 47% WR | 30 trades
2021: +106.1% | 46% WR | 33 trades
2022: +8.7% | 40% WR | 15 trades
2023: -8.1% | 36% WR | 25 trades
2024: +16.0% | 50% WR | 16 trades
2025: +32.1% | 28% WR | 18 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema


def init_strategy():
    return {
        'name': 'ema_acceleration_surge',
        'subscriptions': [
            {'symbol': 'tETHUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
        ],
        'parameters': {}
    }


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

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

    closes = [b.close for b in bars]
    ema15 = ema(closes, 15)
    ema40 = ema(closes, 40)
    ema80 = ema(closes, 80)

    if ema15[i] is None or ema40[i] is None or ema80[i] is None:
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # All EMAs in bullish order
        if not (ema15[i] > ema40[i] and ema40[i] > ema80[i]):
            return []

        # Price must be above all EMAs
        if not (closes[i] > ema15[i] and closes[i] > ema40[i]):
            return []

        # Price was near or below EMA15 recently (1-3 bars ago)
        touched_ema15 = False
        for j in range(1, 4):
            if i-j >= 0:
                dist = abs(closes[i-j] - ema15[i-j]) / ema15[i-j]
                if dist < 0.01 or closes[i-j] < ema15[i-j]:
                    touched_ema15 = True
                    break

        if not touched_ema15:
            return []

        # Price must show acceleration (current bar stronger than previous)
        momentum_now = (closes[i] - closes[i-2]) / closes[i-2]
        momentum_prev = (closes[i-2] - closes[i-4]) / closes[i-4]

        if momentum_now <= momentum_prev or momentum_now < 0.02:
            return []

        # EMA15 must be rising
        ema15_rising = ema15[i] > ema15[i-3]
        if not ema15_rising:
            return []

        # Volume check: current bar should show strength
        if hasattr(bars[i], 'volume') and bars[i].volume > 0:
            avg_vol = sum([bars[i-j].volume for j in range(1, 11)]) / 10
            if bars[i].volume < avg_vol * 0.8:
                return []

        actions.append({
            'action': 'open_long',
            'symbol': 'tETHUSD',
            'exchange': 'bitfinex',
            'size': 1.0,
        })
    else:
        # Exit when price closes below EMA15
        if closes[i] < ema15[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'tETHUSD',
                'exchange': 'bitfinex',
            })

    return actions