"""Validazione FINALE delle 3 strategie oneste selezionate. Per ciascuna: per-asset FULL/OOS/DD/anni-positivi + sweep fee (0/0.05/0.10/0.20% RT). Tutto NETTO, ingresso eseguibile, OOS = ultimo 30%, leva 3x. S1 DIP — long-only dip-buy z-score reversion (1h) [regime: reversione] S2 TREND — long-only EMA 20/100 trend-following (4h) [regime: momentum singolo] S3 ROT — rotazione cross-sectional momentum sul paniere (1d) [regime: forza relativa] """ 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 atr, ema, get_df, simulate, oos_split, available_assets from scripts.analysis.honest_trend import simulate_position, ema_dual_signal, oos as trend_oos from scripts.analysis.honest_rotation import build_panel, simulate_rotation FEES = [0.0, 0.0005, 0.001, 0.002] # ---- S1 DIP ---- def dip_entries(df, n=50, z_in=2.5, sl_atr=2.5, max_bars=24): c = df["close"].values ma = pd.Series(c).rolling(n).mean().values sd = pd.Series(c).rolling(n).std().values a = atr(df, 14) z = (c - ma) / np.where(sd == 0, np.nan, sd) ents = [] for i in range(n + 14, len(c)): if np.isnan(z[i]) or np.isnan(a[i]): continue if z[i] <= -z_in and z[i - 1] > -z_in: ents.append({"i": i, "d": 1, "tp": ma[i], "sl": c[i] - sl_atr * a[i], "max_bars": max_bars}) return ents def validate_dip(assets): print("\n" + "=" * 100) print(" S1 DIP — long-only dip-buy z-score reversion | 1h | n=50 z=2.5 sl=2.5ATR mb=24") print("=" * 100) print(f" {'Asset':<6s}{'Trd':>6s}{'Win%':>7s}{'FULL%':>9s}{'OOS%':>9s}{'DD%':>6s}{'Exp%':>6s}{'AnniP':>8s}" f"{' fee-sweep OOS% (0/0.05/0.10/0.20)':<40s}") ok = 0 for a in assets: df = get_df(a, "1h"); ents = dip_entries(df) if len(ents) < 30: continue full = simulate(ents, df); _, oe = oos_split(ents, df); oos = simulate(oe, df) sweep = " ".join(f"{simulate(oe, df, fee_rt=f).ret:+.0f}" for f in FEES) good = full.ret > 0 and oos.ret > 0 ok += good print(f" {a:<6s}{full.trades:>6d}{full.win:>7.1f}{full.ret:>+9.0f}{oos.ret:>+9.0f}" f"{full.dd:>6.0f}{full.exposure:>6.0f}{f'{full.pos_years}/{full.n_years}':>8s} [{sweep}]" f"{' OK' if good else ''}") print(f" -> robusto (FULL+OOS>0) su {ok}/{len(assets)} asset") def validate_trend(assets): print("\n" + "=" * 100) print(" S2 TREND — long-only EMA 20/100 trend | 4h") print("=" * 100) print(f" {'Asset':<6s}{'Flip':>6s}{'FULL%':>9s}{'OOS%':>9s}{'DD%':>6s}{'Exp%':>6s}{'AnniP':>8s}") ok = 0 for a in assets: df = get_df(a, "4h"); sig = ema_dual_signal(df, 20, 100, long_only=True) full = simulate_position(sig, df); oos = trend_oos(sig, df) good = full["ret"] > 0 and oos["ret"] > 0 ok += good print(f" {a:<6s}{full['flips']:>6d}{full['ret']:>+9.0f}{oos['ret']:>+9.0f}" f"{full['dd']:>6.0f}{full['exposure']:>6.0f}{(str(full['pos_years'])+'/'+str(full['n_years'])):>8s}" f"{' OK' if good else ''}") print(f" -> robusto su {ok}/{len(assets)} asset") def validate_rot(assets): print("\n" + "=" * 100) print(" S3 ROT — rotazione cross-sectional momentum | 1d | lb=60 top2 su tutto il paniere") print("=" * 100) panel = build_panel(assets, "1d") print(f" Paniere {list(panel.columns)} {panel.shape[0]} barre {panel.index[0].date()}->{panel.index[-1].date()}") print(f" {'fee RT':<10s}{'FULL%':>9s}{'OOS%':>9s}{'DD%':>6s}{'AnniP':>8s}") for f in FEES: full = simulate_rotation(panel, lookback=60, top_k=2, fee_rt=f) oos = simulate_rotation(panel, lookback=60, top_k=2, fee_rt=f, oos_frac=0.30) anni = str(full['pos_years']) + '/' + str(full['n_years']) print(f" {f*100:>5.2f}%RT {full['ret']:>+9.0f}{oos['ret']:>+9.0f}{full['dd']:>6.0f}{anni:>8s}") # per-anno alla fee reale full = simulate_rotation(panel, lookback=60, top_k=2, fee_rt=0.001) print(" per-anno (fee 0.10%): " + " ".join(f"{y}:{v:+.0f}%" for y, v in sorted(full["yearly"].items()))) if __name__ == "__main__": assets = available_assets() print(f"VALIDAZIONE FINALE — asset disponibili: {assets}") validate_dip(assets) validate_trend(assets) validate_rot(assets)