"""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))
