"""Studio: combinare TUTTE le strategie (fade + honest) migliora i risultati?

Due famiglie con meccanismi e orizzonti diversi:
  FADE (intraday 1h, long/short, BTC/ETH): MR01 boll, MR02 donchian, MR07
       return-reversal — tutte col filtro trend 3.0 ATR. (MR03 keltner -> waste.)
  HONEST (long-only, multi-regime, multi-crypto): DIP01 (dip-buy 1h BTC),
       TR01 (EMA trend 4h basket), ROT02 (dual-momentum rotation 1d).

Metodo: per ogni sleeve si costruisce l'equity GIORNALIERA normalizzata su un
indice comune (2021-01-01 -> 2026-05-26), si passa ai rendimenti giornalieri,
si misura la correlazione cross-famiglia e si confrontano i portafogli
equal-weight (ribilanciati ogni giorno) e inverse-vol. Metriche FULL e OOS
(ultimo 30% della finestra comune): ritorno, CAGR, max DD, Sharpe annualizzato.

Tutto NETTO (fee gia' incluse nelle sleeve), leva 3x, pos 15% per sleeve.
"""
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 scripts.analysis.risk_management import strats_for, build_trades, INIT
# curve daily honest gia' pronte nell'altra famiglia
from scripts.analysis.honest_improve2 import (
    _daily_equity, _norm, dip_market_gated, _tr_basket_daily, _rot_daily_equity,
)

IDX = pd.date_range("2021-01-01", "2026-05-26", freq="1D", tz="UTC")
OOS_FRAC = 0.30
SPLIT = int(len(IDX) * (1 - OOS_FRAC))          # confine OOS sulla finestra comune
OOS_DATE = IDX[SPLIT].date()
ANN = 365.0                                      # giorni/anno per annualizzare


# ---------------- equity giornaliere ----------------
# SWAP fade 1h -> 15m (2026-06-12, scelta utente). Gate fade15m_port06_gate.py: parametri
# 1h NON ri-tunati trasferiti a 15m, corr 15m-1h media 0.26, SWAP promosso (FULL CAGR
# 74->101% DD 3.46->2.47%, OOS Sh 10.07->10.86; OOS DD 1.48->2.09 accettato), edge ETH
# regge il flat-entry-skip. Il canonico segue il deployato per tenere la parita' delle
# due facce. Diario docs/diary/2026-06-12-fade15m-gate.md.
FADE_TF = "15m"


def fade_daily_equity(asset: str, fn, params, tf: str = FADE_TF) -> pd.Series:
    """Equity giornaliera di uno sleeve fade: trade (filtro trend 3.0) -> equity -> daily."""
    df = load_data(asset, tf)
    ts = pd.to_datetime(df["timestamp"], unit="ms", utc=True)
    trades = build_trades(fn(df, **params), df, 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 * 0.15 * ret, 10.0)
        eq[j:] = cap
    s = pd.Series(eq, index=ts).resample("1D").last().reindex(IDX).ffill().bfill()
    return _norm(s)


def build_all_sleeves() -> dict[str, pd.Series]:
    sleeves: dict[str, pd.Series] = {}
    # --- FADE: 6 sleeve (15m dal 2026-06-12, vedi FADE_TF) ---
    for asset in ["BTC", "ETH"]:
        for nm, (fn, params) in strats_for(asset).items():
            sleeves[f"{nm}_{asset}"] = fade_daily_equity(asset, fn, params)
    # --- HONEST: 3 sleeve (riuso le funzioni dell'altra famiglia) ---
    d = dip_market_gated("BTC", market_n=0, return_equity=True)
    sleeves["DIP01_BTC"] = _norm(_daily_equity(d["eq_ts"], d["eq_v"], IDX))
    sleeves["TR01_basket"] = _norm(_tr_basket_daily(["BNB", "BTC", "DOGE", "SOL", "XRP"], IDX))
    sleeves["ROT02_rot"] = _norm(_rot_daily_equity(IDX))
    return sleeves


# ---------------- metriche ----------------
def metrics(daily_ret: pd.Series, lo: int = 0, hi: int | None = None) -> dict:
    r = daily_ret.iloc[lo:hi]
    eq = (1 + r).cumprod()
    peak = eq.cummax(); dd = float(((peak - eq) / peak).max() * 100)
    yrs = len(r) / ANN
    tot = (eq.iloc[-1] - 1) * 100
    cagr = ((eq.iloc[-1]) ** (1 / yrs) - 1) * 100 if yrs > 0 else 0.0
    sharpe = float(r.mean() / r.std() * np.sqrt(ANN)) if r.std() > 0 else 0.0
    return dict(ret=tot, cagr=cagr, dd=dd, sharpe=sharpe)


def yearly_returns(daily_ret: pd.Series) -> dict[int, float]:
    """Rendimento % netto per anno solare dai rendimenti giornalieri composti."""
    g = daily_ret.groupby(daily_ret.index.year).apply(lambda x: ((1 + x).prod() - 1) * 100)
    return {int(y): float(v) for y, v in g.items()}


def port_returns(members: dict[str, pd.Series], weights: dict[str, float] | None = None) -> pd.Series:
    """Rendimenti giornalieri di un portafoglio ribilanciato ogni giorno ai pesi dati."""
    dr = pd.DataFrame({k: v.pct_change().fillna(0.0) for k, v in members.items()})
    if weights is None:
        return dr.mean(axis=1)
    w = pd.Series(weights); w = w / w.sum()
    return (dr * w).sum(axis=1)


def inv_vol_weights(members: dict[str, pd.Series], lo=0, hi=None) -> dict[str, float]:
    """Pesi inversamente proporzionali alla volatilita' (stimata sulla finestra train)."""
    vol = {k: v.pct_change().iloc[lo:hi].std() for k, v in members.items()}
    inv = {k: (1.0 / s if s and s > 0 else 0.0) for k, s in vol.items()}
    tot = sum(inv.values())
    return {k: x / tot for k, x in inv.items()}


# ---------------- report ----------------
def row(label, dr):
    f = metrics(dr); o = metrics(dr, lo=SPLIT)
    print(f"  {label:<26s}{f['ret']:>+9.0f}{f['cagr']:>7.0f}{f['dd']:>7.1f}{f['sharpe']:>7.2f}"
          f"   | {o['ret']:>+9.0f}{o['cagr']:>7.0f}{o['dd']:>7.1f}{o['sharpe']:>7.2f}")


def main():
    print("Costruzione equity giornaliere (puo' richiedere ~1 min)...")
    S = build_all_sleeves()
    fade = {k: v for k, v in S.items() if k.startswith("MR")}
    honest = {k: v for k, v in S.items() if not k.startswith("MR")}

    # --- correlazione cross-famiglia ---
    dr = pd.DataFrame({k: v.pct_change().fillna(0.0) for k, v in S.items()})
    corr = dr.corr()
    fade_k, hon_k = list(fade), list(honest)
    cross = corr.loc[fade_k, hon_k]
    print("\n" + "=" * 92)
    print(f"  CORRELAZIONE rendimenti giornalieri — FADE (righe) vs HONEST (colonne) | {IDX[0].date()}->{IDX[-1].date()}")
    print("=" * 92)
    print(f"  {'':<12s}" + "".join(f"{c:>13s}" for c in hon_k))
    for f in fade_k:
        print(f"  {f:<12s}" + "".join(f"{cross.loc[f,c]:>13.2f}" for c in hon_k))
    intra_fade = corr.loc[fade_k, fade_k].values[np.triu_indices(len(fade_k), 1)].mean()
    intra_hon = corr.loc[hon_k, hon_k].values[np.triu_indices(len(hon_k), 1)].mean()
    print(f"\n  Corr media intra-FADE {intra_fade:+.2f} | intra-HONEST {intra_hon:+.2f} | "
          f"cross-famiglia {cross.values.mean():+.2f}  (piu' bassa = piu' diversificazione)")

    # --- confronto portafogli ---
    print("\n" + "=" * 92)
    print(f"  PORTAFOGLI equal-weight (ribil. giornaliero) | OOS da {OOS_DATE} | leva3x pos15%/sleeve")
    print("=" * 92)
    print(f"  {'portafoglio':<26s}{'Ret%':>9s}{'CAGR':>7s}{'DD%':>7s}{'Shrp':>7s}"
          f"   | {'oRet%':>9s}{'oCAGR':>7s}{'oDD%':>7s}{'oShrp':>7s}")
    print("  " + "-" * 88)
    row("FADE only (8 sleeve)", port_returns(fade))
    row("HONEST only (3 sleeve)", port_returns(honest))
    row("ALL equal-weight (11)", port_returns(S))
    # 50/50 fra le due famiglie (ogni famiglia equipesata al suo interno)
    fr, hr = port_returns(fade), port_returns(honest)
    row("ALL 50/50 famiglie", (fr + hr) / 2)
    # inverse-vol sul train, applicato a tutti gli 11 sleeve
    w = inv_vol_weights(S, lo=0, hi=SPLIT)
    row("ALL inverse-vol", port_returns(S, w))
    print("  " + "-" * 88)
    print("  Sharpe annualizzato sui rendimenti giornalieri. Confronta DD e Sharpe:")
    print("  se il combinato ha DD piu' basso e Sharpe piu' alto delle singole famiglie, combinare conviene.")


if __name__ == "__main__":
    main()
