From 1aa5a0616a94632c825108ddd1760a4706eb17ae Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Mon, 27 Jul 2026 19:17:59 +0000 Subject: [PATCH] research(capitale): 3k o 5k -> ~3.7 mesi su 11.5 anni, non e' una decisione importante r0727_3k_vs_5k.py. Domanda dell'operatore, poi ristretta a "fregatene del fuori": quanto versare su Deribit dei 6.043 EUR fermi in XEON. Tutto su Deribit, 500 EUR/mese, bersaglio 272.061$: 3.000 EUR -> 11.73 anni 4.000 -> 11.58 5.000 -> 11.42 6.043 -> 11.28 Ogni 1.000 EUR in piu' all'inizio vale ~1.8 MESI. P(entro 20a) 100% in tutti i casi. Il motivo strutturale: fino al traguardo entrano ~69.000 EUR di versamenti, quindi il versamento iniziale e' il 4-9% del flusso totale. La decisione che conta e' la SOSTENIBILITA' dei 500/mese (26/07: smettere al 5o anno porta P(muro) dal 90% al 53% = venti volte l'effetto misurato qui). Aggiunto anche il taglio con il venue dentro (quota fuori 46% vs 16%) e il fatto che con i versamenti su Deribit quella quota si DILUISCE: 4.0 anni di copertura versando 3k, 0.7 anni versando 5k -> la differenza fra i due e' temporanea, non una postura permanente. simulate() accetta dep_to (dove vanno i versamenti); replica del 26/07 preservata. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/research/r0727_3k_vs_5k.py | 158 ++++++++++++++++++++++++ scripts/research/r0727_lumpsum_split.py | 14 ++- 2 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 scripts/research/r0727_3k_vs_5k.py diff --git a/scripts/research/r0727_3k_vs_5k.py b/scripts/research/r0727_3k_vs_5k.py new file mode 100644 index 0000000..76123ea --- /dev/null +++ b/scripts/research/r0727_3k_vs_5k.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +"""r0727_3k_vs_5k.py — "ma quindi 3k o 5k?": la scelta e' a TOTALE FISSO, e ha una vita utile. + +LA DOMANDA, RIFORMULATA. Non e' "quanto capitale metto a lavorare" ma **come divido un importo +gia' esistente**: il pozzo e' €6.043 (XEON) + il conto Deribit di oggi. Versarne 3k o 5k non +cambia il totale, cambia la QUOTA che resta fuori dall'exchange: + + versare €3.000 -> dentro €3.550 (54%) · fuori €3.043 (46%) + versare €5.000 -> dentro €5.550 (84%) · fuori €1.043 (16%) + +IL FATTO CHE DECIDE, E CHE MANCAVA. Con **€500/mese** in ingresso il pozzo iniziale viene +sommerso: dopo due anni sono entrati €12.000, il doppio dell'intero capitale di partenza. Se i +versamenti vanno su Deribit — che e' cio' che succede, perche' e' li' che sta il book — **la +quota tenuta fuori si diluisce da sola**, e la protezione comprata oggi ha una vita utile finita. +Misurare 3k-vs-5k assumendo che la quota resti costante (convenzione del 26/07) risponderebbe a +una domanda che nessuno sta facendo. + +Si misurano quindi ENTRAMBE le convenzioni, perche' la differenza fra loro E' la risposta: + [MANTENUTA] i versamenti si ripartiscono per peso -> la quota fuori resta quella scelta oggi + [DILUITA] i versamenti vanno tutti su Deribit -> la quota fuori tende a zero + + uv run python scripts/research/r0727_3k_vs_5k.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_venue_risk as VR # noqa: E402 +import r0727_lumpsum_split as LS # noqa: E402 + +POZZO_EUR = 6_043.0 # XEON su Revolut +DEP_EUR = 500.0 # il piano dichiarato in config/live.json (26/07) +OPZIONI = (3_000.0, 5_000.0) +P_GRID = (0.0, 0.005, 0.01, 0.02, 0.05) + + +def quota_fuori(versato_eur: float) -> float: + dentro = LS.START + versato_eur * CC.EURUSD + fuori = (POZZO_EUR - versato_eur) * CC.EURUSD + return fuori / (dentro + fuori) + + +def anni_di_copertura(share0: float, dep_eur: float = DEP_EUR) -> float: + """Quanti anni prima che la quota fuori scenda sotto il 10% (la soglia sotto cui, dalla + misura del 27/07, il salvataggio diventa marginale) se i versamenti vanno su Deribit. + Solo aritmetica dei flussi: ignora i rendimenti, che accelerano la diluizione.""" + totale = LS.START + POZZO_EUR * CC.EURUSD + fuori = share0 * totale + dep = dep_eur * CC.EURUSD + for m in range(1, 601): + tot = totale + dep * m + if fuori / tot < 0.10: + return m / 12.0 + return float("inf") + + +def main() -> None: + series = VR.venue_series() + zero = {**series, "IB": series["IB"] * 0.0} # seconda gamba = liquidita' ferma + totale = LS.START + POZZO_EUR * CC.EURUSD + + print("=" * 100) + print(" r0727 — 3k o 5k? La scelta e' a TOTALE FISSO: cambia solo la quota fuori dall'exchange") + print("=" * 100) + print(f" pozzo €{POZZO_EUR:,.0f} (XEON) + conto ${LS.START:,.0f} = ${totale:,.0f} totali · " + f"versamento €{DEP_EUR:,.0f}/mese · orizzonte {LS.YEARS} anni") + print(f" bersaglio ${LS.TARGET:,.0f} · book live de-luckato ×{LS.DELUCK}") + + print("\n " + "-" * 96) + print(f" {'versi':>8}{'dentro':>12}{'fuori':>12}{'quota fuori':>14}" + f"{'anni prima che scenda sotto il 10%':>38}") + for v in OPZIONI: + q = quota_fuori(v) + print(f" €{v:>7,.0f}{LS.START + v * CC.EURUSD:>11,.0f}${(POZZO_EUR - v) * CC.EURUSD:>11,.0f}$" + f"{q:>13.0%}{anni_di_copertura(q):>36.1f}") + print("\n ⚠️ Con i versamenti su Deribit la quota fuori si DILUISCE: e' cio' che rende la") + print(" differenza fra 3k e 5k un fatto temporaneo, non una postura permanente.") + + for dep_to, etichetta in ((None, "MANTENUTA (versamenti ripartiti: la quota resta quella di oggi)"), + ("Deribit", "DILUITA (versamenti su Deribit: la quota fuori tende a zero)")): + print("\n" + "=" * 100) + print(f" QUOTA {etichetta}") + print("=" * 100) + print(f"\n {'p annua':>8}", end="") + for v in OPZIONI: + print(f"{f'--- versi €{v:,.0f} ---':>34}", end="") + print() + print(f" {'':>8}", end="") + for _ in OPZIONI: + print(f"{'P(arriv)':>11}{'anni':>8}{'P(0) banca':>15}", end="") + print() + for p in P_GRID: + print(f" {p:>7.1%}", end="") + for v in OPZIONI: + q = quota_fuori(v) + cfg = {"Deribit": 1.0 - q, "IB": q} + r = LS.simulate(cfg, zero, {"Deribit": p, "IB": 0.0}, + lump_usd=totale - LS.START, dep_eur=DEP_EUR, dep_to=dep_to) + yrs = f"{r['median_yrs']:.1f}a" if not np.isnan(r["median_yrs"]) else "mai" + print(f"{r['p_reach']:>11.1%}{yrs:>8}{r['p_all_dead']:>15.1%}", end="") + print() + + print("\n" + "=" * 100) + print(" COSA COSTA DAVVERO LA DIFFERENZA") + print("=" * 100) + for p in (0.01, 0.02): + out = [] + for v in OPZIONI: + q = quota_fuori(v) + cfg = {"Deribit": 1.0 - q, "IB": q} + out.append(LS.simulate(cfg, zero, {"Deribit": p, "IB": 0.0}, + lump_usd=totale - LS.START, dep_eur=DEP_EUR, dep_to="Deribit")) + d_reach = (out[1]["p_reach"] - out[0]["p_reach"]) * 100 + d_yrs = out[0]["median_yrs"] - out[1]["median_yrs"] + d_salv = out[0]["median_salvage"] - out[1]["median_salvage"] + print(f"\n p={p:.0%}: versare 5k invece di 3k -> P(arrivare) {d_reach:+.1f}pp, " + f"traguardo {d_yrs:+.2f} anni prima,") + print(f" salvataggio mediano ${out[1]['median_salvage']:,.0f} invece di " + f"${out[0]['median_salvage']:,.0f} (−${d_salv:,.0f})") + + +def solo_deribit() -> None: + """La domanda spogliata dal venue: se tutto va su Deribit, quanto conta versare di piu'? + + E' la lettura che l'operatore ha chiesto esplicitamente ("fregatene del fuori"), ed e' anche + quella che mostra il vero ordine di grandezza: con €500/mese il pozzo iniziale e' il 3-8% + del flusso totale, quindi la scelta fra 3k e 5k vale MESI, non anni. + """ + series = VR.venue_series() + print("\n" + "=" * 100) + print(" SENZA IL VENUE: tutto su Deribit, quanto vale versare di piu' all'inizio?") + print("=" * 100) + print(f"\n {'versi':>9}{'capitale iniziale':>20}{'anni al muro':>15}{'P(entro 20a)':>15}" + f"{'cap. mediano 20a':>20}") + base = None + for v in (3_000.0, 4_000.0, 5_000.0, POZZO_EUR): + r = LS.simulate({"Deribit": 1.0}, series, 0.0, lump_usd=v * CC.EURUSD, dep_eur=DEP_EUR) + base = base if base is not None else r["median_yrs"] + print(f" €{v:>8,.0f}{LS.START + v * CC.EURUSD:>19,.0f}${r['median_yrs']:>14.2f}a" + f"{r['p_reach']:>15.1%}{r['median_end']:>19,.0f}$") + print(f"\n Ogni €1.000 in piu' all'inizio vale ~1.8 MESI sul traguardo mediano.") + print(f" Fino al traguardo (~11.5 anni) a €{DEP_EUR:,.0f}/mese entrano " + f"€{DEP_EUR * 12 * 11.5:,.0f}: il versamento iniziale e' una frazione del flusso,") + print(" e la decisione che conta resta la SOSTENIBILITA' dei €500/mese (26/07: smettere") + print(" al 5° anno porta P(muro) dal 90% al 53% — venti volte l'effetto misurato qui).") + + +if __name__ == "__main__": + main() + solo_deribit() diff --git a/scripts/research/r0727_lumpsum_split.py b/scripts/research/r0727_lumpsum_split.py index c5b043e..49f53ac 100644 --- a/scripts/research/r0727_lumpsum_split.py +++ b/scripts/research/r0727_lumpsum_split.py @@ -80,7 +80,7 @@ IB_HAIRCUT_YR = 0.008 + 0.001 def simulate(cfg: dict, series: dict, p_annual: float | dict, lump_usd: float = 0.0, dep_eur: float = 250.0, n_paths: int = N_PATHS, years: int = YEARS, - seed: int = SEED) -> dict: + seed: int = SEED, dep_to: str | None = None) -> dict: """Accumulo con capitale iniziale `START + lump_usd` e jump di venue. Generalizza `r0726_venue_risk.simulate` sul capitale iniziale, sul deposito e sul rischio @@ -90,6 +90,11 @@ def simulate(cfg: dict, series: dict, p_annual: float | dict, lump_usd: float = `p_annual` puo' essere un dict {venue: p}: un conto bancario/liquidita' NON fallisce come un exchange (tutela dei depositi fino a €100k), e attribuirgli lo stesso hazard e' una scelta CONSERVATIVA contro lo split — che va poi misurata, non assunta. + + `dep_to`: dove vanno i VERSAMENTI. None = ripartiti per peso fra i superstiti (convenzione + del 26/07, mantiene la quota costante); un nome di venue = tutti li'. La differenza non e' + cosmetica: con versamenti concentrati su un venue la quota tenuta fuori si DILUISCE nel + tempo, e la protezione iniziale ha una vita utile finita. """ rng = np.random.default_rng(seed) n_days = years * 365 @@ -119,7 +124,12 @@ def simulate(cfg: dict, series: dict, p_annual: float | dict, lump_usd: float = if jm.any(): salvage[jm] = cap[jm].sum(axis=1) if t % 30 == 0 and t > 0 and dep > 0: - wa = alive * w0 + if dep_to is None: + wa = alive * w0 + else: # tutti i versamenti su un venue solo + wa = np.zeros_like(cap) + wa[:, names.index(dep_to)] = 1.0 + wa = wa * alive tot = wa.sum(axis=1, keepdims=True) share = np.divide(wa, tot, out=np.zeros_like(wa), where=tot > 0) cap += dep * share