"""GATE PORT06 — fade MR01/MR02/MR07 a 15m (origine: probe ACCEL50 2026-06-12).

Domanda onesta: i 6 sleeve fade girati a 15m (parametri live 1h NON ri-tunati,
trasferimento pre-registrato anti-overfit) MIGLIORANO il PORT06, o sono solo una
variante piu' veloce e correlata degli STESSI fade 1h?

Metodo (engine CANONICO build_trades/fade_daily_equity, NON le classi Strategy):
  [1] PARITA': il builder locale a tf='1h' == sleeve canonico di build_everything.
  [2] STANDALONE daily 1h vs 15m per twin (Sharpe/DD FULL e OOS su IDX comune)
      + stress fee 2x (0.20% RT) sul 15m (4x trade -> fee di prim'ordine).
  [3] CORRELAZIONE daily 15m vs twin 1h: se ~1 e' ridondante (il pairs 15m
      passo' a 0.37).
  [4] GATE PORT06: baseline vs ADD (19+6 sleeve) vs SWAP (15m al posto del 1h)
      vs BLEND (sleeve fade = 0.5*1h + 0.5*15m). Promosso se vs baseline l'OOS
      Sharpe non peggiora E il DD scende (criterio standard dei gate).

    uv run python scripts/analysis/fade15m_port06_gate.py
"""
from __future__ import annotations

import sys
from pathlib import Path

import numpy as np
import pandas as pd

PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))

from src.data.downloader import load_data
from src.portfolio import weighting as W
from scripts.analysis.combine_portfolio import (
    IDX, SPLIT, OOS_DATE, _norm, metrics, port_returns,
)
from scripts.analysis.risk_management import strats_for, build_trades, INIT, POS
from scripts.analysis.report_families import build_everything
from scripts.portfolios._defs import PORTFOLIOS

FADE_IDS = [f"{nm}_{a}" for a in ("BTC", "ETH") for nm in ("MR01", "MR02", "MR07")]


def fade_daily_tf(asset: str, fn, params, tf: str, fee_rt: float = 0.001) -> pd.Series:
    """fade_daily_equity canonico, parametrizzato sul timeframe (stesso engine)."""
    df = load_data(asset, tf)
    ts = pd.to_datetime(df["timestamp"], unit="ms", utc=True)
    trades = build_trades(fn(df, **params), df, fee_rt=fee_rt, trend_max=3.0)
    n = len(df)
    eq = np.full(n, INIT, dtype=float)
    cap = INIT
    for i, j, ret in sorted(trades, key=lambda t: t[1]):
        cap = max(cap + cap * POS * ret, 10.0)
        eq[j:] = cap
    s = pd.Series(eq, index=ts).resample("1D").last().reindex(IDX).ffill().bfill()
    return _norm(s)


def build_fade15(fee_rt: float = 0.001) -> dict[str, pd.Series]:
    out = {}
    for asset in ("BTC", "ETH"):
        for nm, (fn, params) in strats_for(asset).items():
            out[f"{nm}_{asset}_15M"] = fade_daily_tf(asset, fn, params, "15m", fee_rt)
    return out


def std(label: str, eq: pd.Series) -> str:
    r = eq.pct_change().fillna(0.0)
    f, o = metrics(r), metrics(r, lo=SPLIT)
    return (f"  {label:<16s} FULL ret{f['ret']:>+8.0f}% DD{f['dd']:>6.1f}% Sh{f['sharpe']:>6.2f}"
            f" | OOS ret{o['ret']:>+7.0f}% DD{o['dd']:>5.1f}% Sh{o['sharpe']:>6.2f}")


def port_metrics(members: dict[str, pd.Series], p) -> tuple[dict, dict]:
    ids = list(members)
    dr = pd.DataFrame({i: members[i].pct_change().fillna(0.0) for i in ids})
    clusters = {i: i for i in ids}
    w = W.weight_vector(p.weighting, ids, dr, weights=p.weights,
                        caps=p.caps, clusters=clusters, lookback=p.vol_lookback)
    drp = port_returns(members, w)
    return metrics(drp), metrics(drp, lo=SPLIT)


def prow(label: str, fo: tuple[dict, dict]) -> str:
    f, o = fo
    return (f"  {label:<22s} FULL CAGR{f['cagr']:>5.0f}% DD{f['dd']:>6.2f}% Sh{f['sharpe']:>6.2f}"
            f" | OOS CAGR{o['cagr']:>5.0f}% DD{o['dd']:>6.2f}% Sh{o['sharpe']:>6.2f}")


def main() -> None:
    print("=" * 100)
    print("  GATE PORT06 — fade 15m (MR01/02/07 x BTC/ETH) vs 1h deployato")
    print(f"  Finestra comune {IDX[0].date()} -> {IDX[-1].date()}, OOS da {OOS_DATE}")
    print("=" * 100)
    print("\nCostruzione sleeve canonici (2-3 min)...")
    S, pairs, tsm, shape = build_everything()
    canon = {**S, **pairs, **tsm, **shape}

    # [1] PARITA' del builder locale a 1h
    print("\n[1] PARITA' builder locale 1h == canonico")
    for sid in FADE_IDS:
        nm, asset = sid.split("_")
        fn, params = strats_for(asset)[nm]
        mine = fade_daily_tf(asset, fn, params, "1h")
        diff = float((mine - canon[sid]).abs().max())
        print(f"  {sid:<10s} max|diff| = {diff:.2e}  {'OK' if diff < 1e-9 else 'VIOLAZIONE!'}")

    # [2] STANDALONE 1h vs 15m + stress fee 2x sul 15m
    print("\n[2] STANDALONE daily (engine canonico, pos 0.15 lev 3, fee 0.10% RT)")
    fade15 = build_fade15()
    fade15_fee2 = build_fade15(fee_rt=0.002)
    for sid in FADE_IDS:
        print(std(sid + " 1h", canon[sid]))
        print(std(sid + " 15m", fade15[sid + "_15M"]))
        print(std(sid + " 15m f2x", fade15_fee2[sid + "_15M"]))

    # [3] CORRELAZIONE twin 15m vs 1h
    print("\n[3] CORRELAZIONE daily 15m vs twin 1h (pairs 15m promosso a 0.37)")
    cors = []
    for sid in FADE_IDS:
        c = canon[sid].pct_change().corr(fade15[sid + "_15M"].pct_change())
        cors.append(c)
        print(f"  {sid:<10s} corr = {c:.2f}")
    print(f"  media = {np.mean(cors):.2f}")

    # [4] GATE PORT06
    print("\n[4] GATE PORT06 (weighting cap PAIRS 0.33 / SHAPE 0.0588)")
    p = PORTFOLIOS["PORT06"]
    base = {sid: canon[sid] for sid in p.sleeve_ids}
    add = {**base, **fade15}
    swap = dict(base)
    blend = dict(base)
    for sid in FADE_IDS:
        e15 = fade15[sid + "_15M"]
        swap[sid] = e15
        rb = 0.5 * base[sid].pct_change().fillna(0.0) + 0.5 * e15.pct_change().fillna(0.0)
        eq = (1 + rb).cumprod()
        blend[sid] = eq / eq.iloc[0]
    rows = {"BASELINE (1h)": port_metrics(base, p), "ADD (+6 sleeve 15m)": port_metrics(add, p),
            "SWAP (15m al posto 1h)": port_metrics(swap, p), "BLEND 50/50": port_metrics(blend, p)}
    for label, fo in rows.items():
        print(prow(label, fo))

    fb, ob = rows["BASELINE (1h)"]
    print("\nVERDETTO (criterio: OOS Sharpe non peggiora E DD scende vs baseline):")
    for label in ("ADD (+6 sleeve 15m)", "SWAP (15m al posto 1h)", "BLEND 50/50"):
        f, o = rows[label]
        ok = o["sharpe"] >= ob["sharpe"] - 1e-9 and (o["dd"] < ob["dd"] or f["dd"] < fb["dd"])
        print(f"  {label:<22s} -> {'PROMOSSO' if ok else 'bocciato'}")


if __name__ == "__main__":
    main()
