#!/usr/bin/env python """r0727_orizzonte10.py — €50/giorno entro 10 anni: cosa serve, e cosa si ottiene se non c'e'. IL VINCOLO NUOVO. L'operatore ha 49 anni e non vuole arrivare al capitale-rendita in piu' di **10 anni**. Le tabelle del 26/07 avevano l'orizzonte come variabile libera e il versamento come incognita, ma partivano da `START = 600` senza versamento iniziale; quelle del 27/07 hanno il versamento iniziale ma orizzonte 20 anni. Questa e' l'intersezione: **lump + 10 anni**. Il fatto gia' misurato che governa tutto (26/07): comprimere l'orizzonte da 20 a 10 anni costa **2.5× al mese E 2.5× in totale**, perche' a orizzonte corto il rendimento fa il 43% del lavoro invece del 77%. **A orizzonti corti non si fa lavorare la strategia: si COMPRA il capitale coi bonifici.** Questo script mette il prezzo su quella frase. TRE DOMANDE, IN ORDINE DI UTILITA' (1) Con il piano attuale (€5.000 + €500/mese), che probabilita' c'e' di farcela in 10 anni? (2) Quanto servirebbe al mese per farcela in 10 anni, a diversi livelli di confidenza? (3) Se quel numero non e' disponibile: **che rendita si compra in 10 anni con cio' che c'e'?** (la "domanda inversa" gia' codificata il 26/07: quando l'obiettivo dichiarato non e' raggiungibile, la tabella utile e' l'altra) uv run python scripts/research/r0727_orizzonte10.py """ from __future__ import annotations import sys from pathlib import Path import numpy as np ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "scripts" / "research")) import r0725_capcurve as CC # noqa: E402 import r0726_capwall_refresh as WR # noqa: E402 import r0726_venue_risk as VR # noqa: E402 import r0727_lumpsum_split as LS # noqa: E402 ETA = 49 ORIZZONTE = 10 LUMPS = (3_000.0, 5_000.0, 6_043.0, 10_000.0) # 10k = oltre XEON: tocca VWCE/SPYM/PPFB DEPS = (500.0, 800.0, 1_000.0, 1_500.0, 2_000.0) CONFIDENZE = (0.50, 0.75, 0.90) CONC = {"Deribit": 1.0} def p_entro(series: dict, lump_eur: float, dep_eur: float, years: int = ORIZZONTE, target: float | None = None) -> dict: old = LS.TARGET if target is not None: LS.TARGET = target try: return LS.simulate(CONC, series, 0.0, lump_usd=lump_eur * CC.EURUSD, dep_eur=dep_eur, years=years) finally: LS.TARGET = old def dep_per_confidenza(series: dict, lump_eur: float, p_target: float, target: float | None = None) -> float: """Versamento mensile minimo perche' P(traguardo entro l'orizzonte) >= p_target.""" lo, hi = 0.0, 12_000.0 for _ in range(16): mid = 0.5 * (lo + hi) if p_entro(series, lump_eur, mid, target=target)["p_reach"] >= p_target: hi = mid else: lo = mid return 0.5 * (lo + hi) def main() -> None: series = VR.venue_series() r = series["Deribit"].values.astype(float) gross = CC.TARGET_EUR_DAY * 365 * CC.EURUSD / (1 - CC.TAX_RATE) _, perp_loc, muro_loc = WR.perp_and_wall(r, 1.0, gross) # ⚠️ Si usa il muro PUBBLICATO ($272.061), non quello ricalcolato qui ($254.524): le due # costruzioni del "book live" (CC.book_series in r0726_capwall_refresh vs le repliche # ancorate combinate 75/25 di venue_series) danno rendite perpetue 10.91% e 11.67%. La # differenza e' del 6%; usare la piu' generosa renderebbe il piano piu' facile di quanto # ogni altra tabella data all'operatore dica. Si tiene la conservativa e si dichiara. muro = LS.TARGET perp = gross / muro print("=" * 100) print(f" r0727 — €{CC.TARGET_EUR_DAY:.0f}/GIORNO ENTRO {ORIZZONTE} ANNI " f"(da {ETA} a {ETA + ORIZZONTE} anni)") print("=" * 100) print(f" bersaglio ${muro:,.0f} · rendita perpetua {perp:.2%} · book live de-luckato " f"×{LS.DELUCK} · tutto su Deribit") print(f" (ricalcolando il muro dalle serie di QUESTO script uscirebbe ${muro_loc:,.0f} / " f"{perp_loc:.2%}: si tiene il valore") print(" pubblicato, che e' il piu' conservativo e il solo confrontabile con le altre tabelle)") # ------------------------------------------------------------ (1) il piano attuale print("\n" + "=" * 100) print(" (1) IL PIANO ATTUALE REGGE IL VINCOLO?") print("=" * 100) print(f"\n {'lump':>9}{'dep/mese':>11}{'P(entro 10a)':>15}{'mediana SE ce la fa':>22}" f"{'rendita a 10a':>17}") for lump in LUMPS: for dep in (500.0,): x = p_entro(series, lump, dep) rend = x["median_end"] * perp * (1 - CC.TAX_RATE) / 365.0 / CC.EURUSD yrs = f"{x['median_yrs']:.1f}a" if not np.isnan(x["median_yrs"]) else "n.d." print(f" €{lump:>8,.0f}{dep:>11,.0f}{x['p_reach']:>15.1%}{yrs:>22}" f"{rend:>14.2f} €/g") print("\n ⚠️ La colonna 'mediana SE ce la fa' e' CONDIZIONATA ai percorsi che arrivano:") print(" con P(entro 10a) del 15-23% quei 9.2-9.4 anni sono la mediana di una minoranza") print(" fortunata, NON il tempo atteso. La mediana incondizionata e' ~11.3-11.7 anni") print(" (misura del 27/07), cioe' FUORI dal vincolo.") # ------------------------------------------------------------ (2) quanto serve print("\n" + "=" * 100) print(f" (2) QUANTO SERVE AL MESE PER FARCELA IN {ORIZZONTE} ANNI") print("=" * 100) print(f"\n {'lump':>9}" + "".join(f"{f'P={c:.0%}':>13}" for c in CONFIDENZE) + f"{'totale versato @P=75%':>25}") for lump in LUMPS: row = [dep_per_confidenza(series, lump, c) for c in CONFIDENZE] tot = lump * CC.EURUSD + row[1] * CC.EURUSD * 12 * ORIZZONTE print(f" €{lump:>8,.0f}" + "".join(f"{d:>12,.0f}€" for d in row) + f"{tot:>24,.0f}$") print(f"\n ⚠️ Confronto col bersaglio (${muro:,.0f}): a P=75% si VERSA la maggior parte di") print(" cio' che si vuole avere. E' la frase del 26/07 col prezzo attaccato:") print(" a orizzonte corto non fai lavorare la strategia, COMPRI il capitale coi bonifici.") # ------------------------------------------------------------ (3) la domanda inversa print("\n" + "=" * 100) print(f" (3) SE QUEL NUMERO NON C'E': CHE RENDITA SI COMPRA IN {ORIZZONTE} ANNI") print("=" * 100) print(f"\n (lump €10.000; rendita NETTA mediana a {ORIZZONTE} anni, e in quanti anni") print(" si arriverebbe invece ai €50/g)") print(f"\n {'dep/mese':>10}{'cap. mediano a 10a':>21}{'rendita a 10a':>16}" f"{'anni per €50/g':>17}{'eta a quel punto':>19}") for dep in DEPS: a = p_entro(series, 10_000.0, dep, years=ORIZZONTE) b = LS.simulate(CONC, series, 0.0, lump_usd=10_000.0 * CC.EURUSD, dep_eur=dep, years=25) rend = a["median_end"] * perp * (1 - CC.TAX_RATE) / 365.0 / CC.EURUSD yrs = b["median_yrs"] print(f" {dep:>9,.0f}€{a['median_end']:>20,.0f}${rend:>13.2f} €/g" f"{yrs:>16.1f}a{ETA + yrs:>18.0f}") # ------------------------------------------------------------ le altre leve print("\n" + "=" * 100) print(" LE ALTRE DUE LEVE, PER COMPLETEZZA (misurate altrove, non qui)") print("=" * 100) # ⚠️ Le righe di leva si confrontano fra loro (stessa serie, stesso stimatore), NON col # muro pubblicato: mescolare i due stimatori attribuirebbe alla leva un −6% che e' solo # differenza di costruzione della serie. for k in (1.0, 1.25, 1.5): _, pk, mk = WR.perp_and_wall(r, k, gross) print(f" leva {k:.2f}x -> rendita perpetua {pk:.2%}, bersaglio ${mk:,.0f} " f"({mk / muro_loc - 1:+.0%} vs la stessa serie a leva 1)") print(" ⚠️ La leva abbassa il muro perche' alza il drift, ma alza il drawdown nella") print(" stessa proporzione, e `config/live.json` e' oggi cappato a leva lorda <=1x") print(" (con un test che si rompe se qualcuno lo alza). E' una decisione di RISCHIO,") print(" non una scorciatoia: il 25/07 ha misurato che la curva Sharpe del book e'") print(" PIATTA da $600 a $200k, quindi la leva non compra qualita', compra varianza.") print("\n Canale PROP (25/07): €600 come BIGLIETTO invece che come capitale —") print(" P(≥€50/g entro 3 anni) ≈ 6%, P(perdere tutto) 52-65%. E' l'unico percorso") print(" misurato che arriva a €50/g in meno di 10 anni senza capitale, ed e' una") print(" lotteria con probabilita' note, non un piano.") if __name__ == "__main__": main()