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) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-07-27 19:17:59 +00:00
parent 17b0f2cb47
commit 1aa5a0616a
2 changed files with 170 additions and 2 deletions
+158
View File
@@ -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()
+12 -2
View File
@@ -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, 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, 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. """Accumulo con capitale iniziale `START + lump_usd` e jump di venue.
Generalizza `r0726_venue_risk.simulate` sul capitale iniziale, sul deposito e sul rischio 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 `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 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. 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) rng = np.random.default_rng(seed)
n_days = years * 365 n_days = years * 365
@@ -119,7 +124,12 @@ def simulate(cfg: dict, series: dict, p_annual: float | dict, lump_usd: float =
if jm.any(): if jm.any():
salvage[jm] = cap[jm].sum(axis=1) salvage[jm] = cap[jm].sum(axis=1)
if t % 30 == 0 and t > 0 and dep > 0: 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) tot = wa.sum(axis=1, keepdims=True)
share = np.divide(wa, tot, out=np.zeros_like(wa), where=tot > 0) share = np.divide(wa, tot, out=np.zeros_like(wa), where=tot > 0)
cap += dep * share cap += dep * share