"""Verifica indipendente + ricerca TSM01 — Time-Series Momentum multi-orizzonte.

Long-only, multi-crypto, bassa frequenza. Per ogni asset il segnale è il CONSENSO
dei segni del momentum su più orizzonti lunghi (3/6/12 mesi); si tengono equal-weight
gli asset con consenso pieno positivo. Overlay risk-off: cash se BTC < SMA100.

Distinta da ROT02 (cross-sectional ranking): qui conta la PERSISTENZA assoluta lenta
di ogni asset, non la classifica relativa. Correlazione con ROT02 ~0.62 -> fattore
parzialmente indipendente, utile come diversificatore (NON come motore di ritorno:
rende meno di ROT02 a parita' di OOS). DD basso.

Anti-overfit: edge su ALTOPIANO (36/36 config orizzonti x thr x regime_n restano OOS+),
walk-forward stabile (4 anni up, 2 piatti per risk-off, mai un anno negativo), regge
fee 0.40% RT. Gran parte del DD basso viene dall'overlay risk-off SMA100 (condiviso),
la struttura multi-orizzonte aggiunge ~+38pp OOS e alza lo Sharpe 0.58->1.07.
Default gross=0.30 (era 0.45): stesso Sharpe ma DD 22%->15% (scelta robusta, non la piu' redditizia).

Engine onesto: pesi a close[i] da soli rendimenti passati, realizzo i->i+1, fee
one-way fee_rt/2 sul turnover. NETTO, leva implicita gross. OOS = ultimo 30%.
"""
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.honest_lab import available_assets, FEE_RT
from scripts.analysis.honest_rotation import build_panel

GROSS, OOS_FRAC = 0.30, 0.30   # gross 0.30 (anti-overfit): stesso Sharpe di 0.45, DD piu' basso


def tsmom_sim(horizons=(63, 126, 252), thr=1.0, regime_n=100, gross=GROSS,
              fee_rt=FEE_RT, oos_frac=0.0, cheat=False):
    """horizons in giorni. thr=1.0 -> consenso pieno (tutti i segni positivi)."""
    panel = build_panel(available_assets(), "1d")
    cols = list(panel.columns); P = panel.values; T, N = P.shape
    rets = np.zeros_like(P); rets[1:] = P[1:] / P[:-1] - 1
    years = panel.index.year.values
    btc = P[:, cols.index("BTC")]
    bma = pd.Series(btc).rolling(regime_n).mean().values
    start = max(max(horizons) + 1, regime_n + 1, int(T * (1 - oos_frac)) if oos_frac else 0)
    cap = 1000.0; w = np.zeros(N); eq = [cap]; yearly = {}
    eq_ts: list = []; eq_v: list = []
    for i in range(start, T - 1):
        risk_on = btc[i] > bma[i] if not np.isnan(bma[i]) else False
        wi = i + 1 if cheat else i               # cheat: usa il futuro (test no-look-ahead)
        score = np.zeros(N)
        for h in horizons:
            score += np.sign(P[wi] / P[wi - h] - 1)
        score /= len(horizons)
        chosen = [j for j in range(N) if score[j] >= thr] if risk_on else []
        nw = np.zeros(N)
        for j in chosen:
            nw[j] = gross / len(chosen)
        cap -= cap * np.abs(nw - w).sum() * (fee_rt / 2); w = nw
        cap = max(cap * (1 + float(np.dot(w, rets[i + 1]))), 10.0)
        eq.append(cap)
        eq_ts.append(panel.index[i + 1]); eq_v.append(cap)
        y = int(years[i]); yearly[y] = yearly.get(y, 0.0) + float(np.dot(w, rets[i + 1])) * 100
    eq = np.array(eq); peak = np.maximum.accumulate(eq)
    dd = float(np.max((peak - eq) / peak) * 100)
    yrs = (panel.index[-1] - panel.index[start]).days / 365.25 or 1
    rets_d = np.diff(eq) / eq[:-1]
    sharpe = float(np.mean(rets_d) / np.std(rets_d) * np.sqrt(365)) if np.std(rets_d) > 0 else 0.0
    return dict(ret=(cap / 1000 - 1) * 100, cagr=((cap / 1000) ** (1 / yrs) - 1) * 100,
                dd=dd, sharpe=sharpe, yearly=yearly, eq_ts=eq_ts, eq_v=eq_v,
                pos_years=sum(1 for v in yearly.values() if v > 0), n_years=len(yearly))


def main():
    print("=" * 90)
    print("  TSM01 — TSMOM multi-orizzonte (3/6/12m consenso pieno) + risk-off SMA100")
    print("=" * 90)
    # no-look-ahead: cheat deve esplodere
    base = tsmom_sim()
    ch = tsmom_sim(cheat=True)
    print(f"  no-look-ahead: onesto FULL={base['ret']:+.0f}%  vs  cheat(futuro)={ch['ret']:+.0f}%  -> "
          f"{'OK (il cheat esplode -> niente leak)' if ch['ret'] > base['ret'] * 2 else 'CONTROLLARE'}")
    o = tsmom_sim(oos_frac=1 - OOS_FRAC)
    hi = tsmom_sim(fee_rt=0.002)
    print(f"\n  FULL {base['ret']:+.0f}%  CAGR {base['cagr']:.0f}%  DD {base['dd']:.0f}%  "
          f"Sharpe {base['sharpe']:.2f}  anni+ {base['pos_years']}/{base['n_years']}")
    print(f"  OOS  {o['ret']:+.0f}%  DD {o['dd']:.0f}%   |  fee 0.40% RT: FULL {hi['ret']:+.0f}%")
    print("  Per-anno: " + "  ".join(f"{y}:{v:+.0f}%" for y, v in sorted(base["yearly"].items())))


if __name__ == "__main__":
    main()
