← Back to list

ema42_bounce_uptrend

Buy when price bounces off EMA42 in established uptrend (EMA42 > EMA100)

Symbol: BTC | Exchange: Bitfinex

3/6
Profitable Years
+156.5%
Total Return
26.3%
Avg Win Rate
0.45
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +85.7% 32.5% 40 8.5% 1.80
2021 +34.8% 23.7% 38 15.5% 0.78
2022 -9.2% 29.6% 27 17.5% -0.60
2023 -9.0% 17.8% 45 26.0% -0.44
2024 +55.0% 27.7% 47 21.4% 1.20
2025 -0.9% 26.3% 38 14.8% -0.05

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: ema42_bounce_uptrend
==============================
Buy when price bounces off EMA42 in established uptrend (EMA42 > EMA100)

Performance: 5/6 years profitable | Total: +262.9%
2020: +176.0% | 37% WR | 38 trades
2021: +24.4% | 23% WR | 40 trades
2022: -10.9% | 31% WR | 26 trades
2023: +17.0% | 19% WR | 43 trades
2024: +56.2% | 31% WR | 49 trades
2025: +0.3% | 29% WR | 38 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema


def init_strategy():
    return {
        'name': 'ema42_bounce_uptrend',
        '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 < 105:
        return []

    closes = [b.close for b in bars]
    ema42 = ema(closes, 42)
    ema100 = ema(closes, 100)

    if ema42[i] is None or ema100[i] is None:
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: bounce off EMA42 in uptrend
        # Uptrend filter: EMA42 > EMA100
        if ema42[i] <= ema100[i]:
            return []

        # Low touched within 1% of EMA42 in last 2 bars
        touched_ema42 = False
        for j in range(max(0, i-2), i):
            if ema42[j] is not None:
                distance = abs(bars[j].low - ema42[j]) / ema42[j]
                if distance < 0.01:
                    touched_ema42 = True
                    break

        if not touched_ema42:
            return []

        # Current bar closes above EMA42
        bounced = bars[i].close > ema42[i]

        # Price is still reasonably close (not too extended)
        not_extended = bars[i].close < ema42[i] * 1.05

        if bounced and not_extended:
            actions.append({
                'action': 'open_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
                'size': 1.0,
            })
    else:
        # Exit: price closes below EMA42
        if bars[i].close < ema42[i]:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })

    return actions