"""BRK08 — NR7 range-contraction breakout (signals, 1d) IDEA: A bar with the narrowest high-low range in the last 7 bars (NR7) is a setup for a volatility breakout. On the next bar, if price closes above the NR7 bar's high -> go long; if price closes below the NR7 bar's low -> go short. Entry at close on confirmation bar. Exit via TP (multiple of range), SL (opposite side of NR7 bar), or max_bars timeout. GRID (4 param sets, 1 TF = 4 total backtests × 2 assets = 8 total): - (tp_mult, sl_mult, max_bars): controls TP distance as multiple of NR7 range, SL as fraction of NR7 range on opposite side, and holding period. """ import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt") import altlib as al import numpy as np def nr7_signals(df, tp_mult=2.0, sl_mult=1.0, max_bars=5): """ NR7 breakout signals on daily bars. - At close[i-1], identify if bar i-1 is the NR7 bar (narrowest in 7) - At close[i]: if close[i] > high[i-1] -> long signal (direction confirmed) if close[i] < low[i-1] -> short signal - Entry at close[i] - TP = entry + tp_mult * nr7_range (long) / entry - tp_mult * nr7_range (short) - SL = nr7_bar_low (long) / nr7_bar_high (short) - max_bars timeout """ hi = df["high"].values.astype(float) lo = df["low"].values.astype(float) cl = df["close"].values.astype(float) n = len(df) # Compute range for each bar rng = hi - lo entries = [None] * n for i in range(7, n): # Check if bar i-1 is NR7: its range is the smallest in the last 7 bars (i-7 to i-1) prev_ranges = rng[i-7:i] # 7 bars ending at i-1 prev_range_at_im1 = rng[i-1] # NR7: bar i-1 has the narrowest range in last 7 bars if prev_range_at_im1 != np.min(prev_ranges): continue # The NR7 bar (i-1) setup: record its high and low nr7_high = hi[i-1] nr7_low = lo[i-1] nr7_range = rng[i-1] if nr7_range <= 0: continue # At bar i, confirm breakout direction with close current_close = cl[i] if current_close > nr7_high: # Bullish breakout confirmed at close[i] entry = current_close tp = entry + tp_mult * nr7_range sl = nr7_low - sl_mult * nr7_range * 0.1 # just below NR7 bar low entries[i] = {"dir": +1, "tp": tp, "sl": sl, "max_bars": max_bars} elif current_close < nr7_low: # Bearish breakout confirmed at close[i] entry = current_close tp = entry - tp_mult * nr7_range sl = nr7_high + sl_mult * nr7_range * 0.1 # just above NR7 bar high entries[i] = {"dir": -1, "tp": tp, "sl": sl, "max_bars": max_bars} return entries # Grid: (tp_mult, sl_mult, max_bars) GRID = [ (1.5, 1.0, 4), # tight TP, fast exit (2.0, 1.0, 5), # moderate TP (2.5, 1.0, 7), # wider TP, longer hold (2.0, 1.0, 10), # same TP, longer hold ] best_rep = None best_score = -999.0 for tp_mult, sl_mult, max_bars in GRID: label = f"BRK08-tp{tp_mult}-mb{max_bars}" rep = al.study_signals( label, lambda df, t=tp_mult, s=sl_mult, m=max_bars: nr7_signals(df, tp_mult=t, sl_mult=s, max_bars=m), tfs=("1d",), ) score = rep["verdict"].get("best_holdout_sharpe", -999.0) or -999.0 print(f"\n--- {label} ---") print(al.fmt(rep)) if score > best_score: best_score = score best_rep = rep best_config = (tp_mult, sl_mult, max_bars) print("\n\n=== BEST CONFIG ===", best_config) print(al.fmt(best_rep)) print("JSON:", al.as_json(best_rep))