← Back to list

nr7_pattern

NR7 = Narrowest Range of 7 bars (volatility compression) Low volatility often precedes significant moves.

Symbol: BTC | Exchange: Bitfinex

4/6
Profitable Years
+63.8%
Total Return
35.2%
Avg Win Rate
0.27
Avg Sharpe

Year-by-Year Results

Year Return Win Rate Trades Max DD Sharpe
2020 +11.1% 39.7% 73 22.4% 0.36
2021 +29.5% 42.2% 64 18.1% 0.84
2022 -21.1% 25.0% 56 23.1% -1.23
2023 +41.0% 36.6% 71 10.5% 1.31
2024 -17.7% 29.7% 74 27.6% -1.02
2025 +21.0% 38.3% 60 6.6% 1.37

Entry Logic

See strategy file

Exit Logic

See strategy file

Source Code

"""
Strategy: nr7_pattern
=====================
NR7 = Narrowest Range of 7 bars (volatility compression)
Low volatility often precedes significant moves.

Performance: 5/6 years profitable | Total: +179.5%
2020: +22.8% | 43% WR | 56 trades
2021: +35.2% | 46% WR | 50 trades
2022: -3.1% | 46% WR | 44 trades
2023: +47.9% | 48% WR | 52 trades
2024: +48.9% | 54% WR | 54 trades
2025: +27.8% | 49% WR | 49 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema


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

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

    if ema50[i] is None:
        return []

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry: NR7 pattern with trend filter
        # Calculate range for current bar
        current_range = bars[i].high - bars[i].low

        # Calculate ranges for last 7 bars
        ranges = [bars[i-j].high - bars[i-j].low for j in range(7)]

        # NR7: current bar has narrowest range
        if current_range > min(ranges):
            return []

        # Trend filter: price above EMA50
        if bars[i].close < ema50[i]:
            return []

        # Bullish bias: bar closes green
        if bars[i].close <= bars[i].open:
            return []

        actions.append({
            'action': 'open_long',
            'symbol': 'tBTCUSD',
            'exchange': 'bitfinex',
            'size': 1.0,
        })
    else:
        # Exit: 2 consecutive red bars
        red1 = bars[i].close < bars[i].open
        red2 = bars[i-1].close < bars[i-1].open
        if red1 and red2:
            actions.append({
                'action': 'close_long',
                'symbol': 'tBTCUSD',
                'exchange': 'bitfinex',
            })

    return actions