"""BRK10 — Vol-contraction (squeeze) long
HYPOTHESIS: When Bollinger bandwidth is in its low expanding-percentile (squeeze detected),
go long-flat on subsequent upside close > midline. Honest entry at close[i].

Strategy logic:
- Compute Bollinger bandwidth = (upper - lower) / middle
- Compute expanding percentile of bandwidth to define "squeeze" (low vol percentile)
- Long signal: bandwidth in low percentile (squeeze) AND close > midline (momentum up)
- Vol-targeted position, long-flat (no short)

Internal grid (<=4 configs, total backtests <=6):
  - bb_win: Bollinger window [20, 30]
  - squeeze_pct: bandwidth percentile threshold [25, 20]
  Best config picked by min(BTC/ETH) hold-out Sharpe.
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt")
import altlib as al
import numpy as np
import pandas as pd


def make_target(df: pd.DataFrame, bb_win: int = 20, k: float = 2.0,
                squeeze_pct: float = 25.0) -> np.ndarray:
    """
    BRK10: vol-contraction squeeze long.

    - Compute BB bandwidth = (upper - lower) / mid (all causal via bbands)
    - Use expanding percentile of bandwidth to define squeeze
    - Long when: bandwidth <= squeeze_pct expanding percentile AND close > midline
    - Vol-targeted position, long-flat
    """
    c = df["close"].values.astype(float)
    n = len(c)

    # Bollinger bands (causal: uses data <= i)
    upper, mid, lower = al.bbands(c, win=bb_win, k=k)

    # Bandwidth = (upper - lower) / mid; avoid div by zero
    bw = np.where(mid > 0, (upper - lower) / mid, np.nan)

    # Expanding percentile of bandwidth (causal: uses data <= i)
    # squeeze = bandwidth is in the lower squeeze_pct% of historical values
    squeeze_mask = np.zeros(n, dtype=bool)
    bw_series = pd.Series(bw)

    for i in range(bb_win, n):
        hist = bw_series.iloc[:i+1].dropna().values
        if len(hist) < bb_win:
            continue
        threshold = np.percentile(hist, squeeze_pct)
        if np.isfinite(bw[i]) and bw[i] <= threshold:
            squeeze_mask[i] = True

    # Direction: long when squeeze AND close > midline
    # NaN midline bars -> flat
    direction = np.where(
        squeeze_mask & np.isfinite(mid) & (c > mid),
        1.0,
        0.0
    )

    # Vol-targeted, long-flat
    target = al.vol_target(direction, df, target_vol=0.20, vol_win_days=30, leverage_cap=2.0)
    return target


# Grid: bb_win x squeeze_pct (4 configs, both tested on 1d only -> 4 total backtests <= 6)
GRID = [
    dict(bb_win=20, squeeze_pct=25.0),
    dict(bb_win=20, squeeze_pct=20.0),
    dict(bb_win=30, squeeze_pct=25.0),
    dict(bb_win=30, squeeze_pct=20.0),
]

best_rep = None
best_score = -9999.0
best_cfg = None

TFS = ("1d",)

for cfg in GRID:
    print(f"\n--- Testing config: {cfg} ---")
    label = f"BRK10_bb{cfg['bb_win']}_sq{int(cfg['squeeze_pct'])}"
    fn = lambda df, c=cfg: make_target(df, bb_win=c["bb_win"], squeeze_pct=c["squeeze_pct"])
    rep = al.study_weights(label, fn, tfs=TFS)

    # Score = min holdout Sharpe across assets in best TF
    score = rep["verdict"].get("best_holdout_sharpe", -9999.0) or -9999.0
    print(al.fmt(rep))

    if score > best_score:
        best_score = score
        best_rep = rep
        best_cfg = cfg

print("\n" + "=" * 70)
print(f"BEST CONFIG: {best_cfg}")
print(al.fmt(best_rep))
print("JSON:", al.as_json(best_rep))
