"""GATE PORT06: position_size dei PAIRS live (improvement-sweep punto 8).

Contesto. Tutta la validazione PR01 e' a pos=0.15 (lev 3 -> esposizione 0.45 della
fetta), ma il live gira col position_size GLOBALE 0.5 a leva 2 -> esposizione 1.0
per coppia: ~2.2x il validato, su una famiglia SENZA stop (exit solo |z|<=z_exit o
max_bars) e col DD grezzo piu' alto (ETH/BTC ~48%). Scelta deliberata (commento yml)
ma mai gateata. Evidenza live: ADA/ETH 2026-06-05 net -8.52% = -4.26% sleeve a 0.5
(sarebbe -1.28% a 0.15).

Confronto, a livello PORT06 (pesi cap canonici PAIRS 0.33 + SHAPE 0.0588): le 5
equity pairs ricostruite con pairs_sim alla LEVA LIVE (2x) e pos su griglia:
  LIVE-OGGI  pos=0.50 (esposizione 1.00)
  P-0.25     pos=0.25 (0.50)
  P-0.20     pos=0.20 (0.40)
  P-0.15     pos=0.15 (0.30)   <- pos canonica alla leva live
Ancora CANONICO (pos 0.15 lev 3, eff 0.45) = le equity di build_everything.

Parita' preliminare: pairs_sim(pos=0.15, lev=3) deve riprodurre le equity canoniche.

GATE (sweep): scegliere il punto sul trade-off DD/ritorno nel range 0.15-0.25;
promosso se vs LIVE-OGGI l'OOS Sharpe non peggiora e il DD (PORT06 e famiglia) scende.

    uv run python scripts/analysis/pairspos_port06_impact.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 scripts.analysis.combine_portfolio import port_returns, metrics, SPLIT, OOS_DATE, IDX
from scripts.analysis.pairs_research import pairs_sim
from scripts.analysis.report_families import daily_from
from scripts.strategies.PR01_pairs_reversion import PAIRS
from scripts.portfolios._defs import PORTFOLIOS
from src.portfolio import weighting as W

LEV_LIVE = 2.0
GRID = [0.50, 0.25, 0.20, 0.15]


def pair_equities(pos: float, lev: float):
    """{PR_AB: (equity giornaliera, dd_grezzo%, ret%)} con pairs_sim a (pos, lev)."""
    out = {}
    for a, b, p in PAIRS:
        r = pairs_sim(a, b, **{**p, "pos": pos, "lev": lev})
        # pairs_sim ritorna dd e ret GIA' in percento
        out[f"PR_{a}{b}"] = (daily_from(r["eq_ts"], r["eq_v"]),
                             float(r["dd"]), float(r["ret"]))
    return out


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


def fam_metrics(eqs: dict[str, pd.Series]):
    """Famiglia pairs standalone equal-weight."""
    dr = port_returns(eqs)
    return metrics(dr), metrics(dr, lo=SPLIT)


def main():
    p = PORTFOLIOS["PORT06"]
    pair_ids = [s.sid for s in p.sleeves if s.sid.startswith("PR_")]
    print("=" * 100)
    print("  GATE PORT06 — position_size PAIRS live (lev 2x) | validazione storica: pos 0.15 lev 3")
    print(f"  coppie: {pair_ids} | caps={p.caps}")
    print("=" * 100)

    print("\n[1] build_everything() canonico (cache)...")
    from src.portfolio.sleeves import all_sleeve_equities
    eq_base = dict(all_sleeve_equities())

    # --- [2] PARITA': pairs_sim(0.15, lev3) == canonico ---
    print("\n[2] PARITA' pairs_sim(pos=0.15, lev=3) vs canonico:")
    canon = pair_equities(0.15, 3.0)
    parity_ok = True
    for sid in pair_ids:
        rep, _, _ = canon[sid]
        base = eq_base[sid]
        corr = base.pct_change().fillna(0).corr(rep.pct_change().fillna(0))
        rb = (base.iloc[-1] / base.iloc[0] - 1) * 100
        rr = (rep.iloc[-1] / rep.iloc[0] - 1) * 100
        flag = "" if (corr > 0.999 and abs(rr - rb) <= max(1.0, abs(rb) * 0.01)) else "  <-- MISMATCH"
        if flag:
            parity_ok = False
        print(f"    {sid:<11s}corr={corr:.5f}  ret canon {rb:+.0f}% vs replay {rr:+.0f}%{flag}")
    print(f"    PARITA' {'OK' if parity_ok else 'FALLITA'}.")
    if not parity_ok:
        print("    >>> STOP.")
        return

    # --- [3] griglia pos alla leva live ---
    print(f"\n[3] PORT06 e famiglia PAIRS per pos (lev={LEV_LIVE:.0f}x) | OOS da {OOS_DATE}")
    print(f"  {'variante':<12s}{'eff':>5s} | {'FULL Sh':>8s}{'FULL DD%':>9s}{'CAGR':>6s}"
          f" | {'OOS Sh':>7s}{'OOS DD%':>8s} | {'famSh':>6s}{'famDD%':>7s}"
          f" | {'worst pair DD% (grezzo)':>24s}")
    print("  " + "-" * 96)
    res = {}
    for pos in GRID:
        eqs = pair_equities(pos, LEV_LIVE)
        members = dict(eq_base)
        for sid in pair_ids:
            members[sid] = eqs[sid][0]
        f, o = port_metrics(members, p)
        ff, _fo = fam_metrics({sid: eqs[sid][0] for sid in pair_ids})
        worst = max(((sid, eqs[sid][1]) for sid in pair_ids), key=lambda x: x[1])
        res[pos] = (f, o, ff, worst, eqs)
        tag = "LIVE-OGGI" if pos == 0.50 else f"P-{pos:.2f}"
        print(f"  {tag:<12s}{pos*LEV_LIVE:>5.2f} | {f['sharpe']:>8.2f}{f['dd']:>9.2f}{f['cagr']:>5.0f}%"
              f" | {o['sharpe']:>7.2f}{o['dd']:>8.2f} | {ff['sharpe']:>6.2f}{ff['dd']:>7.1f}"
              f" | {worst[0]} {worst[1]:>5.1f}")

    # --- [4] per-coppia al pos live vs candidato ---
    print("\n[4] Per-coppia (DD grezzo % | ret %) a pos 0.50 vs 0.20:")
    e50, e20 = res[0.50][4], res[0.20][4]
    for sid in pair_ids:
        print(f"    {sid:<11s} DD {e50[sid][1]:>5.1f} -> {e20[sid][1]:>5.1f}   "
              f"ret {e50[sid][2]:>+8.0f}% -> {e20[sid][2]:>+8.0f}%")

    # --- GATE: candidati vs LIVE-OGGI ---
    f_l, o_l = res[0.50][0], res[0.50][1]
    print("\n" + "=" * 100)
    print("  GATE vs LIVE-OGGI (pos 0.50): OOS Sharpe non peggiora E DD scende (PORT06)")
    print("=" * 100)
    for pos in (0.25, 0.20, 0.15):
        f_c, o_c = res[pos][0], res[pos][1]
        ok = (o_c["sharpe"] >= o_l["sharpe"] - 0.02 and o_c["dd"] < o_l["dd"]
              and f_c["sharpe"] >= f_l["sharpe"] - 0.02)
        print(f"  pos {pos:.2f}: OOS Sh {o_l['sharpe']:.2f}->{o_c['sharpe']:.2f} "
              f"DD {o_l['dd']:.2f}->{o_c['dd']:.2f} | FULL Sh {f_l['sharpe']:.2f}->{f_c['sharpe']:.2f} "
              f"DD {f_l['dd']:.2f}->{f_c['dd']:.2f}  => {'PROMOSSO' if ok else 'bocciato'}")


if __name__ == "__main__":
    main()
