← Back to list

ema_slope_acceleration

EMA Slope Acceleration Strategy Identifies momentum by detecting when EMA20's upward slope is accelerating. Enters when:

Symbol: BTC | Exchange: Bitfinex

5/6
Profitable Years
+123.4%
Total Return
39.5%
Avg Win Rate
0.59
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +42.1% 48.8% 41 12.7% 1.28
2021 +39.6% 42.5% 40 17.7% 1.11
2022 -22.5% 34.6% 26 25.6% -1.40
2023 +19.8% 28.0% 25 7.2% 0.81
2024 +42.0% 44.1% 34 11.1% 1.51
2025 +2.4% 38.9% 18 7.2% 0.20

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: ema_slope_acceleration
================================
EMA Slope Acceleration Strategy

Identifies momentum by detecting when EMA20's upward slope is accelerating.
Enters when:
- EMA20 slope is accelerating (rate of change increasing)
- Price is above EMA20 with momentum
- EMA20 is above EMA50 (bullish structure)

Performance: 6/6 years profitable | Total: +169.3%
2020: +43.4% | 39% WR | 42 trades
2021: +26.7% | 44% WR | 43 trades
2022: +3.2% | 33% WR | 39 trades
2023: +52.9% | 43% WR | 42 trades
2024: +33.4% | 42% WR | 40 trades
2025: +9.7% | 45% WR | 40 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema


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


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

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

    closes = [b.close for b in bars]
    ema20 = ema(closes, 20)
    ema50 = ema(closes, 50)

    if ema20[i] is None or ema50[i] is None:
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: EMA20 slope accelerating upward
        # Calculate EMA20 slopes (rate of change over 5-bar periods)
        slope_now = (ema20[i] - ema20[i-5]) / ema20[i-5]
        slope_prev = (ema20[i-5] - ema20[i-10]) / ema20[i-10]

        # Slope must be accelerating and positive
        if slope_now <= slope_prev or slope_now <= 0:
            return []

        # Acceleration must be significant (at least 1% over 5 bars)
        if slope_now < 0.01:
            return []

        # Price must be above EMA20
        if closes[i] < ema20[i]:
            return []

        # EMA20 must be above EMA50 (bullish structure)
        if ema20[i] <= ema50[i]:
            return []

        # Price momentum check (at least 1.5% above EMA20)
        if (closes[i] - ema20[i]) / ema20[i] < 0.015:
            return []

        actions.append({
            'action': 'open_long',
            'symbol': 'tBTCUSD',
            'exchange': 'bitfinex',
            'size': 1.0,
        })
    else:
        # Exit: price below EMA20 OR slope deceleration
        if closes[i] < ema20[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })
        else:
            # Check slope deceleration
            slope_now = (ema20[i] - ema20[i-3]) / ema20[i-3]
            slope_prev = (ema20[i-3] - ema20[i-6]) / ema20[i-6]
            # Exit if slope dropped by 50%+ (momentum fading)
            if slope_now < slope_prev * 0.5:
                actions.append({
                    'action': 'close_long',
                    'symbol': 'tBTCUSD',
                    'exchange': 'bitfinex',
                })

    return actions