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