"""GATE: aggiungere ETH/BTC 30m (vincitore gioco sessione 2) AL BLEND attuale (1h+15m)? Domanda: il 30m e' un 3o timeframe utile dello spread ETH/BTC, o e' ridondante col 15m adiacente gia' deployato? Test (engine pairs_sim_flat, == worker): [1] correlazioni: 30m vs 1h, 30m vs 15m (se ~1 col 15m -> ridondante). [2] gate PORT06: baseline ATTUALE (6 pairs, incl 15m) vs +30m (7 pairs), mezza size. [3] robustezza ai costi (fee sweep) del 30m. uv run python scripts/analysis/pairs30m_gate.py """ from __future__ import annotations import sys from pathlib import Path 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 from scripts.analysis.pairs_research import pairs_sim, pairs_sim_flat from scripts.analysis.report_families import daily_from from scripts.portfolios._defs import PORTFOLIOS from src.portfolio.sleeves import all_sleeve_equities from src.portfolio import weighting as W WIN_30M = dict(n=53, z_in=1.947, z_exit=1.0, max_bars=24) # vincitore gioco sess.2 def daily(cfg, tf, flat_skip, pos=0.15): if flat_skip: r = pairs_sim_flat("ETH", "BTC", tf=tf, **cfg, flat_skip=True, pos=pos) else: r = pairs_sim("ETH", "BTC", tf=tf, **cfg, pos=pos) return daily_from(r["eq_ts"], r["eq_v"]), r def port_metrics(members, ids, clusters, caps): dr = pd.DataFrame({i: members[i].pct_change().fillna(0.0) for i in ids}) w = W.weight_vector("cap", ids, dr, caps=caps, clusters=clusters) drp = port_returns({i: members[i] for i in ids}, w) return metrics(drp), metrics(drp, lo=SPLIT), w def main(): p = PORTFOLIOS["PORT06"] eq_base = dict(all_sleeve_equities()) # include gia' PR_ETHBTC (1h) e PR_ETHBTC_15M e1h = eq_base["PR_ETHBTC"] e15 = eq_base["PR_ETHBTC_15M"] e30h, r30 = daily(WIN_30M, "30m", flat_skip=True, pos=0.075) # half size come il 15m print("=" * 92) print(" GATE — ETH/BTC 30m (vincitore gioco sess.2) sopra il BLEND 1h+15m attuale") print(f" 30m: {r30['trades']} trade, {r30.get('n_skip_entry',0)} ingressi flat saltati") print("=" * 92) def corr(a, b): return a.pct_change().fillna(0).corr(b.pct_change().fillna(0)) print("\n[1] CORRELAZIONI (rendimenti giornalieri):") print(f" 30m vs 1h : {corr(e30h, e1h):.3f}") print(f" 30m vs 15m: {corr(e30h, e15):.3f} <-- se alta, ridondante col 15m gia' deployato") print(f" (rif) 15m vs 1h: {corr(e15, e1h):.3f}") print(f"\n[2] GATE PORT06 (cap PAIRS 0.33 + SHAPE 0.0588) | OOS da {OOS_DATE}:") ids0 = list(p.sleeve_ids) cl0 = p.clusters caps = p.caps f0, o0, _ = port_metrics(eq_base, ids0, cl0, caps) # + 30m mem1 = dict(eq_base); mem1["PR_ETHBTC_30M"] = e30h ids1 = ids0 + ["PR_ETHBTC_30M"] cl1 = dict(cl0); cl1["PR_ETHBTC_30M"] = "ETH-rev" f1, o1, w1 = port_metrics(mem1, ids1, cl1, caps) print(f" {'config':<22}{'FULL Sh':>8}{'FULL DD%':>9}{'OOS Sh':>8}{'OOS DD%':>8}") print(f" {'ATTUALE (1h+15m)':<22}{f0['sharpe']:>8.2f}{f0['dd']:>9.2f}{o0['sharpe']:>8.2f}{o0['dd']:>8.2f}") print(f" {'+30m (1h+15m+30m)':<22}{f1['sharpe']:>8.2f}{f1['dd']:>9.2f}{o1['sharpe']:>8.2f}{o1['dd']:>8.2f}") ok = o1["sharpe"] >= o0["sharpe"] - 0.02 and o1["dd"] <= o0["dd"] + 1e-9 \ and f1["sharpe"] >= f0["sharpe"] - 0.02 and f1["dd"] <= f0["dd"] + 1e-9 print(f" => {'MIGLIORA (promosso)' if ok else 'NON migliora (bocciato)'}") print(f" peso pairs ETH/BTC: 1h {w1.get('PR_ETHBTC',0)*100:.1f}% + 15m " f"{w1.get('PR_ETHBTC_15M',0)*100:.1f}% + 30m {w1.get('PR_ETHBTC_30M',0)*100:.1f}% " f"= {(w1.get('PR_ETHBTC',0)+w1.get('PR_ETHBTC_15M',0)+w1.get('PR_ETHBTC_30M',0))*100:.1f}% su 7 coppie") print("\n[3] ROBUSTEZZA AI COSTI (30m standalone, Sharpe per fee RT/coppia):") for fee, lbl in [(0.001, "0.20% 1x"), (0.002, "0.40% 2x"), (0.003, "0.60% 3x"), (0.004, "0.80% 4x"), (0.006, "1.20% 6x")]: r = pairs_sim_flat("ETH", "BTC", tf="30m", **WIN_30M, flat_skip=True, fee_rt=fee) print(f" {lbl:<10} Sharpe {r['sharpe']:>6.2f}") if __name__ == "__main__": main()