From 1b099bb47b515dd98a820cae9f7f82645d98f662 Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Thu, 28 May 2026 23:52:25 +0200 Subject: [PATCH] feat(analysis): tabella per-anno (PnL/DD) versioni migliorate + portafoglio Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/analysis/honest_yearly2.py | 74 ++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 scripts/analysis/honest_yearly2.py diff --git a/scripts/analysis/honest_yearly2.py b/scripts/analysis/honest_yearly2.py new file mode 100644 index 0000000..2f0c6d7 --- /dev/null +++ b/scripts/analysis/honest_yearly2.py @@ -0,0 +1,74 @@ +"""Tabella per-anno (PnL% e DD% intra-anno) delle versioni MIGLIORATE: +ROT02 (dual-momentum), le 3 sleeve e il PORTAFOGLIO combinato. +Tutto NETTO. Riusa gli engine di honest_improve / honest_improve2. +""" +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_improve2 import ( # noqa: E402 + dip_market_gated, _daily_equity, _norm, _tr_basket_daily, _rot_daily_equity, +) + + +def _year_dd(eq: pd.Series) -> dict[int, float]: + out = {} + for y, g in eq.groupby(eq.index.year): + peak = g.iloc[0]; dd = 0.0 + for v in g: + peak = max(peak, v); dd = max(dd, (peak - v) / peak if peak > 0 else 0.0) + out[int(y)] = dd * 100 + return out + + +def _year_pnl(eq: pd.Series) -> dict[int, float]: + out = {} + for y, g in eq.groupby(eq.index.year): + out[int(y)] = (g.iloc[-1] / g.iloc[0] - 1) * 100 + return out + + +def table(name, eq): + eq = _norm(eq) + dd = _year_dd(eq); pnl = _year_pnl(eq) + print(f"\n {name}") + print(f" {'Anno':>6s}{'PnL%':>9s}{'DD%':>7s}") + print(" " + "-" * 22) + for y in sorted(pnl): + print(f" {y:>6d}{pnl[y]:>+9.0f}{dd[y]:>7.0f}") + tot = (eq.iloc[-1] / eq.iloc[0] - 1) * 100 + print(f" {'TOT':>6s}{tot:>+9.0f}{_year_dd(eq) and max(_year_dd(eq).values()):>7.0f}(max anno)") + + +if __name__ == "__main__": + print("=" * 60) + print(" RISULTATI PER ANNO — versioni migliorate (NETTO)") + print("=" * 60) + + # ROT02 dal 2020 (dati paniere) + idx_rot = pd.date_range("2020-09-01", "2026-05-26", freq="1D", tz="UTC") + eq_rot = _rot_daily_equity(idx_rot) + table("ROT02 — dual-momentum rotation (1d)", eq_rot) + + # sleeve + portafoglio dal 2021 + idx = pd.date_range("2021-01-01", "2026-05-26", freq="1D", tz="UTC") + d = dip_market_gated("BTC", market_n=0, return_equity=True) + eq_dip = _norm(_daily_equity(d["eq_ts"], d["eq_v"], idx)) + eq_tr = _norm(_tr_basket_daily(["BNB", "BTC", "DOGE", "SOL", "XRP"], idx)) + eq_r2 = _norm(_rot_daily_equity(idx)) + table("Sleeve DIP01 — BTC (1h)", eq_dip) + table("Sleeve TR01 — basket (4h)", eq_tr) + table("Sleeve ROT02 (1d)", eq_r2) + + drets = pd.DataFrame({"DIP": eq_dip.pct_change().fillna(0), + "TR": eq_tr.pct_change().fillna(0), + "ROT": eq_r2.pct_change().fillna(0)}) + combo = (1 + drets.mean(axis=1)).cumprod() + table("PORTAFOGLIO equal-weight (daily rebal)", combo)