"""FASE 3 — conferma avversariale del SOLO candidato reale: trend-following long-only (MA-cross).

Protocollo onesto:
  1. SELEZIONE config SOLO sul pre-hold-out (< 2025-01-01). Niente sbirciate al hold-out.
  2. HOLD-OUT 2025-26 sbloccato UNA volta (la prova del nove, mai usato in ricerca).
  3. Breakdown PER ANNO vs buy&hold: il trend-LO deve "schivare" i bear (2018/2022).
  4. STRESS: fee 2x, lag di esecuzione (1 barra), slippage.
  5. DEFLATED SHARPE (Bailey & López de Prado): lo Sharpe regge alla correzione per multiple-testing?

    uv run python scripts/analysis/phase3_confirm.py
"""
from __future__ import annotations
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
import numpy as np
import pandas as pd
from scipy.stats import norm, skew, kurtosis

from src.data.downloader import load_data
from scripts.analysis.research_lab import (
    backtest, buy_hold, window_mask, ts, _net_series, HOLDOUT_START, BARS_PER_YEAR,
)
from scripts.analysis.phase2_families import ma_cross

GRID = [(12, 48), (24, 96), (48, 192), (24, 200), (96, 288)]   # MA-cross griglia (fast/slow)
REPR = (24, 96)                                                # config rappresentativa PRE-COMMITTATA
TF = "1h"


def lag(pos, k=1):
    """Esecuzione in ritardo di k barre (agisci k barre dopo la decisione)."""
    return np.concatenate([np.zeros(k), pos[:-k]])


def per_year(df, pos, tf):
    c = df["close"].values.astype(float)
    net, _, fwd, _ = _net_series(df, pos)
    yrs = ts(df).dt.year.values
    out = {}
    for y in sorted(set(yrs)):
        m = yrs == y
        if m.sum() < 2:
            continue
        strat = float(np.prod(1 + net[m]) - 1) * 100
        bh = float(np.prod(1 + fwd[m]) - 1) * 100
        expo = float(np.mean(np.abs(pos[m])))
        out[y] = (strat, bh, expo)
    return out


def deflated_sharpe(net, sr_trials_perbar, N):
    """DSR: prob. che il vero Sharpe > la soglia attesa-massima sotto N trial (multiple testing).
    Tutto in Sharpe PER BARRA. >0.95 = significativo dopo correzione."""
    sr = net.mean() / net.std()
    T = len(net)
    g3 = float(skew(net)); g4 = float(kurtosis(net, fisher=False))
    var_sr = float(np.var(sr_trials_perbar, ddof=1)) if len(sr_trials_perbar) > 1 else 0.0
    ge = 0.5772156649
    z1 = norm.ppf(1 - 1.0 / N); z2 = norm.ppf(1 - 1.0 / (N * np.e))
    sr0 = np.sqrt(var_sr) * ((1 - ge) * z1 + ge * z2)        # Sharpe atteso-massimo sotto null, N trial
    den = np.sqrt(max(1 - g3 * sr + (g4 - 1) / 4.0 * sr ** 2, 1e-9))
    dsr = float(norm.cdf((sr - sr0) * np.sqrt(T - 1) / den))
    bpy = BARS_PER_YEAR[TF]
    return dsr, sr * np.sqrt(bpy), sr0 * np.sqrt(bpy)


def main():
    print("=" * 96)
    print("  FASE 3 — conferma avversariale: TREND-following long-only (MA-cross) BTC/ETH")
    print("=" * 96)
    data = {a: load_data(a, TF) for a in ("BTC", "ETH")}

    # ---------- 1) selezione SOLO pre-hold-out ----------
    print(f"\n  (1) SELEZIONE su pre-hold-out (< {HOLDOUT_START}) — Sharpe per config (plateau = robusto)")
    for a in ("BTC", "ETH"):
        line = []
        for f, s in GRID:
            pos = ma_cross(data[a], f, s, "lo")
            sh = backtest(data[a], pos, TF, hi=HOLDOUT_START).sharpe
            line.append(f"{f}/{s}={sh:>4.2f}")
        print(f"    {a}: " + "  ".join(line))
    print(f"    -> config rappresentativa PRE-COMMITTATA per i test seguenti: {REPR[0]}/{REPR[1]}")

    # ---------- 2) HOLD-OUT 2025-26 (sbloccato una volta) ----------
    print(f"\n  (2) HOLD-OUT {HOLDOUT_START}+ — LA PROVA DEL NOVE (mai usato in ricerca)")
    for a in ("BTC", "ETH"):
        bh = buy_hold(data[a], TF, lo=HOLDOUT_START)
        print(f"    {a}: buy&hold hold-out  Sh {bh.sharpe:>5.2f}  ret {bh.ret*100:>+7.1f}%  DD {bh.maxdd*100:>4.1f}%")
        for f, s in GRID:
            pos = ma_cross(data[a], f, s, "lo")
            r = backtest(data[a], pos, TF, lo=HOLDOUT_START)
            star = " <-REPR" if (f, s) == REPR else ""
            print(f"        {f}/{s:<3d} Sh {r.sharpe:>5.2f}  ret {r.ret*100:>+7.1f}%  DD {r.maxdd*100:>4.1f}%  expo {r.exposure:.2f}{star}")

    # ---------- 3) per anno vs buy&hold (schiva i bear?) ----------
    print(f"\n  (3) PER ANNO — strat {REPR[0]}/{REPR[1]} vs buy&hold (expo = quanto è long; bear test 2018/2022)")
    for a in ("BTC", "ETH"):
        pos = ma_cross(data[a], *REPR, "lo")
        py = per_year(data[a], pos, TF)
        print(f"    {a}:")
        for y, (st, bh, ex) in py.items():
            flag = "  <- BEAR" if bh < -20 else ""
            print(f"       {y}: strat {st:>+7.0f}%  | buy&hold {bh:>+7.0f}%  | expo {ex:.2f}{flag}")

    # ---------- 4) stress ----------
    print(f"\n  (4) STRESS — strat {REPR[0]}/{REPR[1]} | FULL e HOLD-OUT Sharpe")
    print(f"    {'scenario':<24s}{'BTC FULL':>10s}{'BTC HO':>9s}{'ETH FULL':>10s}{'ETH HO':>9s}")
    scen = [
        ("base fee0.10%", dict(fee_rt=0.001), False),
        ("fee 0.20% (2x)", dict(fee_rt=0.002), False),
        ("lag 1 barra", dict(fee_rt=0.001), True),
        ("fee2x + lag", dict(fee_rt=0.002), True),
    ]
    for name, kw, do_lag in scen:
        row = [name]
        for a in ("BTC", "ETH"):
            pos = ma_cross(data[a], *REPR, "lo")
            if do_lag:
                pos = lag(pos, 1)
            full = backtest(data[a], pos, TF, **kw).sharpe
            ho = backtest(data[a], pos, TF, lo=HOLDOUT_START, **kw).sharpe
            row += [f"{full:>9.2f}", f"{ho:>8.2f}"]
        print(f"    {row[0]:<24s}{row[1]:>10s}{row[2]:>9s}{row[3]:>10s}{row[4]:>9s}")

    # ---------- 5) deflated Sharpe ----------
    print(f"\n  (5) DEFLATED SHARPE — corregge il multiple-testing (DSR>0.95 = regge)")
    # trial set = TUTTE le config trend long-only provate (proxy del numero di tentativi)
    N_TRIALS = 60      # stima conservativa dei backtest provati in Fase 2 (tutte le famiglie/asset/TF)
    for a in ("BTC", "ETH"):
        trials = [backtest(data[a], ma_cross(data[a], f, s, "lo"), TF, hi=HOLDOUT_START) for f, s in GRID]
        sr_trials = []
        for f, s in GRID:
            net, _, _, _ = _net_series(data[a], ma_cross(data[a], f, s, "lo"))
            m = window_mask(data[a], hi=HOLDOUT_START)
            sr_trials.append(net[m].mean() / net[m].std())
        net, _, _, _ = _net_series(data[a], ma_cross(data[a], *REPR, "lo"))
        m = window_mask(data[a], hi=HOLDOUT_START)
        dsr, sr_ann, sr0_ann = deflated_sharpe(net[m], sr_trials, N_TRIALS)
        verdict = "REGGE" if dsr > 0.95 else "NON regge"
        print(f"    {a} (pre-hold-out): Sharpe {sr_ann:.2f} vs soglia-max-attesa(N={N_TRIALS}) {sr0_ann:.2f} "
              f"-> DSR {dsr:.3f}  [{verdict}]")

    print("\n" + "=" * 96)
    print("  VERDETTO: edge ONESTO solo se (2) hold-out positivo, (3) schiva i bear, (4) regge lo")
    print("  stress, (5) DSR>0.95. Altrimenti: anche il trend era sample-luck del mercato toro.")
    print("=" * 96)


if __name__ == "__main__":
    main()
