"""SKH_P_CHANDE — Chande-window sweep on the V1 base.

TASK: on the V1 base, sweep n_vola and n_volume (the Chande momentum windows that drive
BuzVola=Chande01(ATR) and BuzVolume=Chande01(volume)) in {8,13,21,34,55}. Does a different
vol/volume CYCLE window help the REGIME gate out-of-sample? Maximize min HOLD-OUT Sharpe.

V1 reference: SkyhookParams(ptn_n=55, sl_atr=2.5, tp_atr=6.0, vola_lo=35, vola_hi=95, vol_lo=0.0)
  with n_vola=n_volume=13 (defaults) -> minFull +0.69, minHold +0.64 (BTC 0.64 / ETH 0.64).

We keep EVERYTHING from V1 fixed (bands, pattern, exits) and vary only the two Chande windows.
Rank by min HOLD-OUT subject to minFull>=0.5 and >=20 trades both assets, then plateau-check
the winner (neighbors in the n_vola x n_volume grid must also be good), then full study +
causality + marginal.
"""
from __future__ import annotations
import sys, itertools
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/skyhook")
import skyhooklib as sk
from src.strategies.skyhook import SkyhookParams

ASSETS = ("BTC", "ETH")

# V1 base: everything except the two Chande windows stays fixed.
BASE = dict(ptn_n=55, sl_atr=2.5, tp_atr=6.0, vola_lo=35, vola_hi=95, vol_lo=0.0)

WINDOWS = [8, 13, 21, 34, 55]  # Fibonacci-ish cycle lengths for the Chande momentum


def mk(n_vola, n_volume):
    return SkyhookParams(n_vola=n_vola, n_volume=n_volume, **BASE)


def eval_combo(n_vola, n_volume):
    p = mk(n_vola, n_volume)
    res = {a: sk.run_asset(a, p, sk.FEE_RT) for a in ASSETS}
    min_full = min(res[a]["full"]["sharpe"] for a in ASSETS)
    min_hold = min(res[a]["holdout"]["sharpe"] for a in ASSETS)
    min_tr   = min(res[a]["full"]["n_trades"] for a in ASSETS)
    max_dd   = max(res[a]["full"]["maxdd"] for a in ASSETS)
    return dict(n_vola=n_vola, n_volume=n_volume,
                min_full=min_full, min_hold=min_hold, min_tr=min_tr, max_dd=max_dd,
                btc_hold=res["BTC"]["holdout"]["sharpe"], eth_hold=res["ETH"]["holdout"]["sharpe"],
                btc_full=res["BTC"]["full"]["sharpe"], eth_full=res["ETH"]["full"]["sharpe"])


def main():
    rows = []
    combos = list(itertools.product(WINDOWS, WINDOWS))
    print(f"Sweeping {len(combos)} (n_vola x n_volume) combos x 2 assets = {len(combos)*2} run_asset calls")
    for nv, nvol in combos:
        rows.append(eval_combo(nv, nvol))

    # V1 reference cell (n_vola=n_volume=13) for explicit comparison
    v1 = next(r for r in rows if r["n_vola"] == 13 and r["n_volume"] == 13)
    print(f"\nV1 cell (n_vola=13,n_volume=13): minF={v1['min_full']:+.2f} minH={v1['min_hold']:+.2f} "
          f"tr={v1['min_tr']} DD={v1['max_dd']*100:.0f}%  (btcH={v1['btc_hold']:+.2f} ethH={v1['eth_hold']:+.2f})")

    valid = [r for r in rows if r["min_full"] >= 0.5 and r["min_tr"] >= 20]
    valid.sort(key=lambda r: r["min_hold"], reverse=True)

    print("\n=== ALL combos by min HOLD-OUT (minF>=0.5 & tr>=20 marked OK) ===")
    print(f"{'n_vola':>7}{'n_vol':>6}  {'minF':>6}{'minH':>6}{'minTr':>6}{'maxDD':>7}  {'btcH':>6}{'ethH':>6}  ok")
    for r in sorted(rows, key=lambda r: r["min_hold"], reverse=True):
        ok = "OK" if (r["min_full"] >= 0.5 and r["min_tr"] >= 20) else "."
        mark = "  <-V1" if (r["n_vola"] == 13 and r["n_volume"] == 13) else ""
        print(f"{r['n_vola']:>7}{r['n_volume']:>6}  {r['min_full']:>6.2f}{r['min_hold']:>6.2f}"
              f"{r['min_tr']:>6}{r['max_dd']*100:>6.0f}%  {r['btc_hold']:>6.2f}{r['eth_hold']:>6.2f}  {ok}{mark}")

    if not valid:
        print("\nNo valid combo (minFull>=0.5 & >=20 trades). Best raw by minHold:")
        print(sorted(rows, key=lambda r: r["min_hold"], reverse=True)[0])
        return

    top = valid[0]
    print(f"\n=== WINNER: n_vola={top['n_vola']} n_volume={top['n_volume']} ===")
    print(f"  minFull={top['min_full']:+.2f} minHold={top['min_hold']:+.2f} minTr={top['min_tr']} maxDD={top['max_dd']*100:.0f}%")

    # plateau grid (full minHold table laid out as n_vola rows x n_volume cols)
    def find(nv, nvol):
        return next((r for r in rows if r["n_vola"] == nv and r["n_volume"] == nvol), None)
    print("\n  Plateau grid (minHold; rows=n_vola, cols=n_volume):")
    print("        " + "".join(f"{nvol:>7}" for nvol in WINDOWS))
    for nv in WINDOWS:
        cells = []
        for nvol in WINDOWS:
            r = find(nv, nvol)
            tag = "*" if (nv == top['n_vola'] and nvol == top['n_volume']) else " "
            cells.append(f"{r['min_hold']:>6.2f}{tag}")
        print(f"  nv={nv:>3} " + "".join(cells))

    # final study + causality + marginal on the winner
    p = mk(top['n_vola'], top['n_volume'])
    print("\n=== STUDY (winner) ===")
    rep = sk.study(f"SKH_P_CHANDE_nv{top['n_vola']}_nvol{top['n_volume']}", p)
    print(sk.fmt(rep))
    print("\ncausality:", sk.causality(p))
    print("\nmarginal:", sk.marginal(p))
    print("\nas_json:", sk.as_json(rep))


if __name__ == "__main__":
    main()
