"""PAPER PORTFOLIO — forward-only del portafoglio attivo (TP01 + XS01), simulato. Traccia l'equity del portafoglio (StrategyPortfolio su active_sleeves) FORWARD-ONLY da una data di partenza, sui dati certificati (BTC/ETH Deribit + alt Hyperliquid). Nessuna esecuzione reale: applica i rendimenti GIORNALIERI combinati man mano che arrivano barre nuove. Stato persistente. Il dashboard (src/live/dashboard.py) legge questo stato + ricalcola il backtest a colpo d'occhio. uv run python scripts/live/paper_portfolio.py # avanza (init al 1o run) uv run python scripts/live/paper_portfolio.py --status # solo stato uv run python scripts/live/paper_portfolio.py --reset # azzera (riparte da ora) """ from __future__ import annotations import sys, json from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(PROJECT_ROOT)) import numpy as np, pandas as pd from src.portfolio.portfolio import StrategyPortfolio from src.portfolio.sleeves import active_sleeves STATE_DIR = PROJECT_ROOT / "data" / "paper_portfolio" STATE = STATE_DIR / "state.json" EQ = STATE_DIR / "equity.csv" INITIAL = 2000.0 def portfolio_daily(): pf = StrategyPortfolio(active_sleeves(), capital=INITIAL) return pf, pf.combined_daily() def load(): return json.loads(STATE.read_text()) if STATE.exists() else None def save(st): STATE_DIR.mkdir(parents=True, exist_ok=True) STATE.write_text(json.dumps(st, indent=2)) def advance(): pf, r = portfolio_daily() st = load() if st is None: # init: forward-only, parte dall'ultima barra last = str(r.index[-1]) st = dict(start=last, last=last, equity=INITIAL, initial=INITIAL, peak=INITIAL, max_dd=0.0, n_days=0) save(st) STATE_DIR.mkdir(parents=True, exist_ok=True) EQ.write_text("date,equity\n" + f"{last},{INITIAL}\n") return st last = pd.Timestamp(st["last"]) new = r[r.index > last] if len(new): eq = st["equity"]; peak = st["peak"]; dd = st["max_dd"] lines = [] for d, ret in new.items(): eq *= (1.0 + float(ret)); peak = max(peak, eq); dd = max(dd, (peak - eq) / peak if peak > 0 else 0) lines.append(f"{d},{eq:.4f}") st.update(equity=eq, last=str(new.index[-1]), peak=peak, max_dd=dd, n_days=st["n_days"] + len(new)) save(st) with open(EQ, "a") as f: f.write("\n".join(lines) + "\n") return st def main(): a = sys.argv[1:] if "--reset" in a: for f in (STATE, EQ): f.unlink(missing_ok=True) print("paper portfolio azzerato.") st = load() if "--status" in a else advance() if st is None: st = advance() pf, _ = portfolio_daily() days = (pd.Timestamp(st["last"]) - pd.Timestamp(st["start"])).days ret = st["equity"] / st["initial"] - 1 print(f"PAPER PORTFOLIO (TP01+XS01) — forward-only") print(f" start {st['start'][:10]} -> last {st['last'][:10]} ({days}g, {st['n_days']} barre)") print(f" equity {st['equity']:.2f} (start {st['initial']:.0f}) ret {ret*100:+.2f}% maxDD {st['max_dd']*100:.1f}%") print(f" posizioni correnti: {pf.current_positions()}") if __name__ == "__main__": main()