From fa18621eb394f3105c30f34553265c5e1bc6392f Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Mon, 27 Jul 2026 19:29:37 +0000 Subject: [PATCH] research(capitale): 50 EUR/g in 10 anni costa ~870 EUR/mese, non 500 Vincolo nuovo dell'operatore (49 anni, non oltre 10 anni). r0727_orizzonte10.py. Il piano attuale NON regge il vincolo: 5.000 EUR + 500/mese danno P(entro 10a) = 19.7% (mediana incondizionata ~11.4 anni). Quanto serve al mese per 10 anni, con lump 5.000: P=50% -> 689 EUR P=75% -> 870 EUR P=90% -> 1.047 EUR A P=75% si versano $119.273 per arrivare a $272.061: e' la frase del 26/07 col prezzo attaccato (a orizzonte corto non fai lavorare la strategia, compri il capitale coi bonifici). Tabella inversa (lump 5k), cio' che si compra in 10 anni: 500/m -> 36.90 EUR/g 800/m -> 55.57 1.000/m -> 72.66 1.500/m -> 105.94 Due difetti miei corretti prima di pubblicare: - la colonna "anni mediani" era CONDIZIONATA ai percorsi che arrivano: mostrava 9.4 anni accanto a P=15%, che e' contraddittorio. Ora e' etichettata "mediana SE ce la fa". - il muro stampato (254.524$, ricalcolato dalle serie di questo script) non era quello usato nelle simulazioni (272.061$ pubblicato). Le due costruzioni del book live danno rendite perpetue 10.91% e 11.67%; si tiene la conservativa e si dichiara la differenza. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/research/r0727_orizzonte10.py | 166 ++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 scripts/research/r0727_orizzonte10.py diff --git a/scripts/research/r0727_orizzonte10.py b/scripts/research/r0727_orizzonte10.py new file mode 100644 index 0000000..815ce20 --- /dev/null +++ b/scripts/research/r0727_orizzonte10.py @@ -0,0 +1,166 @@ +#!/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) +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 €5.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, 5_000.0, dep, years=ORIZZONTE) + b = LS.simulate(CONC, series, 0.0, lump_usd=5_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()