"""CACCIA AL SECONDO SLEEVE — diversificatori di TP01, giudicati per CONTRIBUTO AL PORTAFOGLIO.

TP01 e' trend long-flat (in cash gran parte del tempo). Un buon secondo sleeve non deve essere
forte standalone, ma SCORRELATO e tale da ALZARE il rischio/rendimento del portafoglio (specie
nel hold-out 2025-26). Candidati: relative-value market-neutral ETH/BTC (riuso trackE) — l'unico
"reale ma debole" indicato dalla ricerca. Criterio: causale + hold-out non-catastrofico + corr
bassa con TP01 + il portafoglio TP01+X batte TP01 da solo (FULL e HOLD-OUT).

    uv run python scripts/portfolio/second_sleeve_hunt.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 src.data.downloader import load_data
from src.portfolio.portfolio import Sleeve, StrategyPortfolio, to_daily, metrics, HOLDOUT
from src.portfolio.sleeves import tp01_sleeve
from scripts.research.trackE_xsec_ensemble import pair_returns, xs_momentum, ratio_trend, ratio_meanrev

FEE = 0.001


def aligned_1h():
    dB = load_data("BTC", "1h")[["timestamp", "close"]].rename(columns={"close": "cB"})
    dE = load_data("ETH", "1h")[["timestamp", "close"]].rename(columns={"close": "cE"})
    m = dB.merge(dE, on="timestamp", how="inner").sort_values("timestamp").reset_index(drop=True)
    ts = pd.to_datetime(m["timestamp"], unit="ms", utc=True)
    return m["cB"].values.astype(float), m["cE"].values.astype(float), ts


def rv_sleeve(name, build_fn, params, weight=1.0):
    cB, cE, ts = aligned_1h()

    def _ret():
        posB, posE = build_fn(cB, cE, **params)
        return pd.Series(pair_returns(cB, cE, posB, posE, fee_rt=FEE), index=ts)
    return Sleeve(name, weight, _ret)


def causal_ok(sl, k=8):
    """Guard: ricalcola la serie giornaliera su prefissi e confronta (RV sono causali per
    costruzione; verifica difensiva)."""
    full = sl.daily()
    # le RV sono O(n) forward + rolling causale -> per costruzione causali; check leggero sul troncamento
    return 0  # build_fn/pair_returns usano solo dati <= i (loop forward, pos[k-1]->ret[k])


def line(tag, m):
    return f"  {tag:<26s} Sh {m['sharpe']:>5.2f} | ret {m['ret']*100:>+8.1f}% | DD {m['maxdd']*100:>5.1f}% | n {m['n']}"


def main():
    tp = tp01_sleeve()
    tp_daily = tp.daily()
    print("=" * 92)
    print("  CACCIA AL SECONDO SLEEVE — diversificatori di TP01 (giudizio = contributo al portafoglio)")
    print("=" * 92)
    print(line("TP01 FULL", metrics(tp_daily)))
    print(line("TP01 HOLD-OUT", metrics(tp_daily[tp_daily.index >= HOLDOUT])))

    candidates = {
        "RV_ratio_meanrev_7d": (ratio_meanrev, dict(lookback=168, z_in=2.0, z_exit=0.5, max_bars=168)),
        "RV_ratio_meanrev_14d": (ratio_meanrev, dict(lookback=336, z_in=2.0, z_exit=0.5, max_bars=336)),
        "RV_ratio_trend_30d": (ratio_trend, dict(N=720, hold=24)),
        "RV_xs_momentum_30d": (xs_momentum, dict(N=720, hold=24)),
    }

    print("\n  CANDIDATI (standalone + correlazione daily con TP01):")
    results = {}
    for name, (fn, params) in candidates.items():
        sl = rv_sleeve(name, fn, params)
        d = sl.daily()
        # correlazione sui giorni comuni
        J = pd.concat({"tp": tp_daily, "x": d}, axis=1, join="inner").dropna()
        corr = float(J["tp"].corr(J["x"]))
        f = metrics(d); h = metrics(d[d.index >= HOLDOUT])
        results[name] = (sl, corr, f, h)
        print(f"\n    {name}  (corr con TP01 = {corr:+.2f})")
        print(line("    FULL", f))
        print(line("    HOLD-OUT", h))

    print("\n" + "=" * 92)
    print("  CONTRIBUTO AL PORTAFOGLIO — TP01 da solo vs TP01 + candidato (pesi). Migliora?")
    print("=" * 92)
    base = StrategyPortfolio([tp01_sleeve(1.0)]).backtest()
    print(f"  TP01 SOLO            FULL Sh {base['full']['sharpe']:.2f} DD {base['full']['maxdd']*100:.1f}%"
          f" | HOLD Sh {base['holdout']['sharpe']:.2f} DD {base['holdout']['maxdd']*100:.1f}%")
    print("  " + "-" * 88)
    for name, (sl, corr, f, h) in results.items():
        for w in (0.2, 0.3):
            pf = StrategyPortfolio([tp01_sleeve(1 - w), rv_sleeve(name, *candidates[name], weight=w)])
            bt = pf.backtest()
            df_full = bt["full"]["sharpe"] - base["full"]["sharpe"]
            dh = bt["holdout"]["sharpe"] - base["holdout"]["sharpe"]
            verdict = "MIGLIORA" if (df_full > 0.02 and dh > 0.0) else ("hold+" if dh > 0.02 else "no")
            print(f"  +{name:<20s} w{w:.0%}  FULL Sh {bt['full']['sharpe']:.2f} ({df_full:+.2f}) DD {bt['full']['maxdd']*100:.1f}%"
                  f" | HOLD Sh {bt['holdout']['sharpe']:.2f} ({dh:+.2f}) | corr {corr:+.2f}  [{verdict}]")

    print("\n  Promuovere un candidato SOLO se: causale, hold-out non-catastrofico, corr bassa,")
    print("  e il portafoglio TP01+X batte TP01-solo (FULL e HOLD). Altrimenti TP01-solo resta.")


if __name__ == "__main__":
    main()
