feat(portfolio): reproducible accumulation forecast for the Deribit book
scripts/portfolio/forecast_deribit_book.py — projects the Deribit-only book (TP01+SKH01) forward by pure compounding (reinvest winnings), monthly-aligned, NO external contributions (not a PAC). Deterministic @historical & @conservative CAGR + Monte-Carlo block-bootstrap (median/p10/p90), plus €/day run-rate @conservative. Parametric (--capital/--years/--cons-frac). Honest caveats baked in (bull-crypto sample, no leverage, SKH01 not live, conditional projection). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
|||||||
|
"""PROIEZIONE ACCUMULO del book DERIBIT-ONLY (TP01 + SKH01) — compounding puro (reinvesti le
|
||||||
|
vincite), allineamento MENSILE, NESSUN versamento esterno (non e' un PAC).
|
||||||
|
|
||||||
|
Base: rendimenti mensili del book Deribit-only (rebal mensile, netto costo turnover). Proietta in
|
||||||
|
avanti l'equity da un capitale iniziale:
|
||||||
|
- DETERMINISTICO @CAGR storico e @CAGR conservativo (= storico × cons_frac, default metà);
|
||||||
|
- MONTE CARLO block-bootstrap dei rendimenti mensili storici (mediana + banda p10/p90);
|
||||||
|
- €/giorno run-rate (cresce col capitale, perche' si rigiocano le vincite).
|
||||||
|
|
||||||
|
⚠️ ONESTA': lo storico e' un BULL crypto ~2019-26 -> il futuro sara' quasi certamente piu' magro.
|
||||||
|
Pianificare sulla colonna conservativa; il MC non contiene un vero bear pluriennale (anche il p10
|
||||||
|
e' ottimista). Nessuna leva. SKH01 e' research/forward-monitor (solo TP01 e' armato live). Non e'
|
||||||
|
una garanzia: e' una proiezione condizionata "se il futuro somigliasse al passato".
|
||||||
|
|
||||||
|
uv run python scripts/portfolio/forecast_deribit_book.py
|
||||||
|
uv run python scripts/portfolio/forecast_deribit_book.py --capital 5000 --years 1,3,5,10 --cons-frac 0.5
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from src.portfolio.portfolio import StrategyPortfolio, rebalance_sim
|
||||||
|
from src.portfolio.sleeves import deribit_book_sleeves
|
||||||
|
|
||||||
|
|
||||||
|
def book_monthly_returns(rebal_days: int, cost_rate: float) -> pd.Series:
|
||||||
|
"""Rendimenti MENSILI del book Deribit-only (rebal periodico, netto costo)."""
|
||||||
|
sl = deribit_book_sleeves()
|
||||||
|
w = StrategyPortfolio(sl).weights()
|
||||||
|
cols = {s.name: s.daily() for s in sl}
|
||||||
|
r = rebalance_sim(cols, w, period_days=rebal_days, cost_rate=cost_rate)["daily"]
|
||||||
|
return ((1.0 + r).resample("ME").prod() - 1.0).dropna()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(description="Proiezione accumulo book Deribit-only (compounding, allineamento mensile)")
|
||||||
|
ap.add_argument("--capital", type=float, default=5000.0)
|
||||||
|
ap.add_argument("--years", type=str, default="1,2,3,5,8,10")
|
||||||
|
ap.add_argument("--cons-frac", type=float, default=0.5, help="CAGR conservativo = storico × questo (default 0.5)")
|
||||||
|
ap.add_argument("--sims", type=int, default=4000)
|
||||||
|
ap.add_argument("--block-months", type=int, default=3, help="blocco del bootstrap (mesi)")
|
||||||
|
ap.add_argument("--rebal-days", type=int, default=30)
|
||||||
|
ap.add_argument("--cost-rate", type=float, default=0.0005, help="fee per-lato sul turnover (Deribit taker)")
|
||||||
|
ap.add_argument("--seed", type=int, default=7)
|
||||||
|
a = ap.parse_args()
|
||||||
|
|
||||||
|
cap = a.capital
|
||||||
|
HY = [int(x) for x in a.years.split(",") if x.strip()]
|
||||||
|
m = book_monthly_returns(a.rebal_days, a.cost_rate)
|
||||||
|
mv = m.values
|
||||||
|
nm = len(mv)
|
||||||
|
g_month = float(np.prod(1.0 + mv) ** (1.0 / nm) - 1.0)
|
||||||
|
cagr = (1.0 + g_month) ** 12 - 1.0
|
||||||
|
vol_ann = float(mv.std() * np.sqrt(12))
|
||||||
|
cons_cagr = cagr * a.cons_frac
|
||||||
|
|
||||||
|
print("=" * 90)
|
||||||
|
print(f" PROIEZIONE ACCUMULO — book Deribit-only (TP01+SKH01) | compounding, allineamento mensile, no versamenti")
|
||||||
|
print(f" storico: {nm} mesi · CAGR {cagr*100:.1f}% · vol annua {vol_ann*100:.0f}% (bull crypto, no leva) | capitale €{cap:,.0f}")
|
||||||
|
print("=" * 90)
|
||||||
|
|
||||||
|
# Monte Carlo: block-bootstrap dei rendimenti mensili
|
||||||
|
rng = np.random.default_rng(a.seed)
|
||||||
|
blk = a.block_months
|
||||||
|
maxM = max(HY) * 12
|
||||||
|
nb = maxM // blk + 1
|
||||||
|
starts = rng.integers(0, nm - blk, size=(a.sims, nb))
|
||||||
|
paths = np.concatenate([mv[starts[:, k][:, None] + np.arange(blk)[None, :]] for k in range(nb)], axis=1)[:, :maxM]
|
||||||
|
eqMC = cap * np.cumprod(1.0 + paths, axis=1)
|
||||||
|
|
||||||
|
cons_m = (1.0 + cons_cagr) ** (1.0 / 12) - 1.0
|
||||||
|
print(f"\n ACCUMULO (reinvesti le vincite):")
|
||||||
|
print(f" {'oriz.':>6} | {'det @storico':>13} | {'det @conserv.':>14} | {'MC mediana':>11} | {'MC p10':>9} | {'MC p90':>10}")
|
||||||
|
print(f" {'':>6} | {'('+format(cagr*100,'.0f')+'%)':>13} | {'('+format(cons_cagr*100,'.0f')+'%)':>14} |")
|
||||||
|
print(" " + "-" * 80)
|
||||||
|
for y in HY:
|
||||||
|
mo = y * 12
|
||||||
|
det = cap * (1.0 + g_month) ** mo
|
||||||
|
detc = cap * (1.0 + cons_m) ** mo
|
||||||
|
c = eqMC[:, mo - 1]
|
||||||
|
print(f" {y:>4}a | €{det:>11,.0f} | €{detc:>12,.0f} | €{np.median(c):>9,.0f} | €{np.percentile(c,10):>7,.0f} | €{np.percentile(c,90):>8,.0f}")
|
||||||
|
|
||||||
|
# €/giorno run-rate @conservativo (cresce col capitale)
|
||||||
|
rd = (1.0 + cons_cagr) ** (1.0 / 365.0) - 1.0
|
||||||
|
print(f"\n €/GIORNO run-rate @conservativo ({cons_cagr*100:.1f}% CAGR) — cresce col capitale (rigiochi le vincite):")
|
||||||
|
print(f" {'oriz.':>6} | {'equity':>9} | {'€/giorno':>10} | {'€/mese':>8}")
|
||||||
|
print(" " + "-" * 42)
|
||||||
|
for y in [0] + HY:
|
||||||
|
E = cap * (1.0 + cons_cagr) ** y
|
||||||
|
print(f" {('oggi' if y==0 else str(y)+'a'):>6} | €{E:>7,.0f} | €{E*rd:>7,.2f} | €{E*rd*30:>6,.0f}")
|
||||||
|
E_end = cap * (1.0 + cons_cagr) ** max(HY)
|
||||||
|
print(f" media €/giorno su {max(HY)} anni: €{(E_end-cap)/(max(HY)*365):.2f}/g (profitto €{E_end-cap:,.0f})")
|
||||||
|
need = 50 * 365 / cons_cagr if cons_cagr > 0 else float('inf')
|
||||||
|
print(f" capitale per ~€50/giorno @{cons_cagr*100:.1f}%: ≈ €{need:,.0f}")
|
||||||
|
|
||||||
|
print(f"\n ⚠️ Proiezione condizionata (storico = bull crypto); pianifica sul conservativo. Nessuna leva.")
|
||||||
|
print(f" SKH01 = research/forward-monitor; solo TP01 e' armato live. Non e' una garanzia.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user