1aa5a0616a
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>
361 lines
20 KiB
Python
361 lines
20 KiB
Python
#!/usr/bin/env python
|
||
"""r0727_lumpsum_split.py — il capitale che e' GIA' fermo, e cosa fa alla decisione di venue.
|
||
|
||
LA DOMANDA. Sul conto che gira ci sono **$600**. Fermi altrove ce ne sono ~**€10.000** (Revolut:
|
||
€6.043 in XEON, che rende ~0% reale netto dopo inflazione e tasse, piu' VWCE/SPYM/PPFB) e €329 su
|
||
Degiro. Tutte le traiettorie pubblicate (25/07 e 26/07) partono da `START = 600.0` **cablato**: il
|
||
progetto ha misurato con cura il CALENDARIO dei versamenti (fattore 6) e non ha mai misurato un
|
||
**versamento iniziale**.
|
||
|
||
PERCHE' LE DUE DOMANDE SI MISURANO INSIEME. Un lump-sum e' il front-loading estremo, e il 26/07
|
||
aveva gia' misurato che il front-loading *mette piu' capitale sull'exchange prima* — cioe' proprio
|
||
dove vive il rischio di venue. E la decisione del 26/07 ("100% Deribit fino a $20k") e' stata
|
||
presa con **$600 sul conto, dove lo split e' impossibile per costruzione**. CLAUDE.md registra
|
||
l'impegno esplicito: *"Se cambia il piano (orizzonte, importo dei versamenti) si riapre PRIMA."*
|
||
Un lump-sum da €5-10k e' esattamente quel cambiamento. Misurare il lump-sum senza il rischio di
|
||
venue risponderebbe alla meta' comoda della domanda.
|
||
|
||
COSA NON DECIDE QUESTO SCRIPT. Quanto di quei €6.043 sia un vero fondo d'emergenza e quanto
|
||
capitale in attesa: e' una decisione dell'operatore, e un fondo d'emergenza NON e' capitale
|
||
disponibile. Qui si misura **cosa compra ogni euro che entra**, per ogni taglia, cosi' che la
|
||
decisione si prenda sul prezzo giusto.
|
||
|
||
ASSUNZIONI DICHIARATE
|
||
* Il costo-opportunita' del lump-sum e' ~0% reale (XEON: ~2% lordo, ~0 netto dopo inflazione e
|
||
26%). Tutti i numeri sono NOMINALI in USD, come ovunque nel progetto: il confronto e' quindi
|
||
fra "fermo a ~0 reale" e "dentro il book", non fra due investimenti rischiosi.
|
||
* Book = quello LIVE (TP01+SKH01 su Deribit), de-luckato ×0.89 (fattore MISURATO,
|
||
r0726_deluck_factor). E' lo stesso book di ogni traiettoria pubblicata -> i numeri sono
|
||
confrontabili con la tabella dei versamenti del 26/07.
|
||
* Bersaglio = $272.061, il muro del 26/07 (rendita perpetua a €50/g netti). Identico per CONC e
|
||
SPLIT: e' CONSERVATIVO contro lo split, che avrebbe un muro leggermente diverso.
|
||
* `p` (probabilita' annua di fallimento di un venue) NON e' stimata: e' una sensibilita'.
|
||
La sceglie l'operatore.
|
||
|
||
uv run python scripts/research/r0727_lumpsum_split.py
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import numpy as np
|
||
import pandas as pd
|
||
|
||
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
|
||
from src.portfolio.gtaa import GTAA_MIN_CAPITAL # noqa: E402
|
||
|
||
DELUCK = 0.89
|
||
START = 600.0 # il conto Deribit di oggi
|
||
YEARS = 20
|
||
N_PATHS = 4000
|
||
BLOCK = 20
|
||
SEED = 726 # lo stesso di r0726_venue_risk: consente la replica esatta
|
||
TARGET = VR.TARGET # $272.061
|
||
LUMPS_EUR = (0.0, 2_000.0, 5_000.0, 10_000.0)
|
||
P_GRID = (0.0, 0.005, 0.01, 0.02, 0.05)
|
||
# La configurazione realmente pianificata dall'operatore: il cap di config/live.json e' stato
|
||
# alzato il 26/07 "in previsione del versamento (EUR 5.000 + 500/mese -> equity ~$6.050)".
|
||
LUMP_FOCUS = 5_000.0
|
||
DEP_FOCUS = 500.0
|
||
|
||
# Split: la quota su IB parte dal 25% raccomandato da r0725_ib10k (oltre, la rendita cala:
|
||
# 0% -> €2.16/g, 50% -> €1.54/g su $11.5k). Ma la quota MINIMA e' imposta dall'eseguibilita'
|
||
# della gamba equity: sotto GTAA_MIN_CAPITAL non c'e' uno sleeve, c'e' cash su un secondo conto.
|
||
# Si accetta di salire fino a IB_SHARE_MAX pur di rendere lo split possibile; oltre, no.
|
||
CONC = {"Deribit": 1.0}
|
||
IB_SHARE = 0.25
|
||
IB_SHARE_MAX = 0.35
|
||
|
||
# Haircut sulla gamba IB nella variante realistica: costi a taglia piccola (CAGR 3.8% -> 3.0% a
|
||
# $3k, misurato il 25/07) + drag del veicolo UCITS (+0.10%/anno, misurato il 26/07).
|
||
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, 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
|
||
PER VENUE: la struttura (ordine di consumo dell'RNG compreso) e' identica, cosi' che con
|
||
lump=0, dep=250 e `p` scalare i numeri debbano coincidere ESATTAMENTE con quelli del 26/07.
|
||
|
||
`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
|
||
names = list(cfg)
|
||
J = pd.concat({k: series[k] for k in names}, axis=1, join="inner").fillna(0.0)
|
||
R = J.values.astype(float)
|
||
idx = CC._boot_paths(np.arange(len(R), dtype=float), n_paths, n_days, BLOCK, rng).astype(int)
|
||
dep = dep_eur * CC.EURUSD
|
||
|
||
w0 = np.array([cfg[k] for k in names], float)
|
||
cap = np.outer(np.full(n_paths, START + lump_usd), w0)
|
||
alive = np.ones((n_paths, len(names)), bool)
|
||
p_vec = np.array([p_annual.get(k, 0.0) if isinstance(p_annual, dict) else p_annual
|
||
for k in names], float)
|
||
haz = 1.0 - (1.0 - p_vec) ** (1.0 / 365.0) # (V,), broadcast sulle estrazioni
|
||
hit = np.zeros(n_paths, bool)
|
||
hit_day = np.full(n_paths, n_days + 1, int)
|
||
main_i = int(np.argmax(w0)) # il venue che porta il book
|
||
salvage = np.full(n_paths, np.nan) # cio' che resta NEL MOMENTO in cui il main cade
|
||
for t in range(n_days):
|
||
cap *= (1.0 + R[idx[:, t], :])
|
||
if (haz > 0).any():
|
||
dead = (rng.random((n_paths, len(names))) < haz) & alive
|
||
cap = np.where(dead, 0.0, cap)
|
||
alive &= ~dead
|
||
jm = dead[:, main_i]
|
||
if jm.any():
|
||
salvage[jm] = cap[jm].sum(axis=1)
|
||
if t % 30 == 0 and t > 0 and dep > 0:
|
||
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
|
||
tot_cap = cap.sum(axis=1)
|
||
newly = (~hit) & (tot_cap >= TARGET)
|
||
hit_day[newly] = t
|
||
hit |= newly
|
||
yrs = np.where(hit, hit_day / 365.0, np.nan)
|
||
fin = yrs[~np.isnan(yrs)]
|
||
# Cio' che distingue una quota dall'altra NON e' P(perso tutto) — che satura a qualunque
|
||
# quota > 0 — ma quanto si SALVA nel momento del fallimento. ⚠️ Volutamente NON si misura il
|
||
# capitale a 20 anni condizionato al fallimento: dopo la morte di un venue questo modello
|
||
# dirotta i versamenti ai superstiti (e li scarta se non ne restano), quindi quel numero
|
||
# attribuirebbe allo split il valore di continuare a versare — che si puo' fare comunque
|
||
# aprendo un altro conto. Il salvataggio istantaneo e' l'unica parte che lo split compra.
|
||
sv = salvage[~np.isnan(salvage)]
|
||
return dict(p_reach=float(hit.mean()),
|
||
median_salvage=float(np.median(sv)) if len(sv) > 20 else float("nan"),
|
||
median_yrs=float(np.median(fin)) if len(fin) > 50 else float("nan"),
|
||
p_all_dead=float((~alive.any(axis=1)).mean()),
|
||
median_end=float(np.median(cap.sum(axis=1))),
|
||
paid=START + lump_usd + dep * (n_days // 30))
|
||
|
||
|
||
def haircut(s: pd.Series, per_year: float) -> pd.Series:
|
||
"""Abbassa il drift di una serie giornaliera di `per_year` all'anno (costi non modellati)."""
|
||
return s - per_year / 365.0
|
||
|
||
|
||
def split_config(total_usd: float) -> dict | None:
|
||
"""Configurazione di split possibile a quel capitale, o None.
|
||
|
||
La quota IB non e' libera: dal basso la vincola l'ESEGUIBILITA' (sotto GTAA_MIN_CAPITAL la
|
||
gamba equity non esiste, e lo 'split' sarebbe cash fermo su un secondo conto), dall'alto la
|
||
vincola la RENDITA (r0725_ib10k: oltre il 25% si paga, oltre il 50% si dimezza). Fra i due
|
||
vincoli si prende la quota minima che rende la gamba eseguibile — mai meno del 25%
|
||
raccomandato, mai piu' di IB_SHARE_MAX.
|
||
"""
|
||
need = GTAA_MIN_CAPITAL / total_usd if total_usd > 0 else float("inf")
|
||
share = max(IB_SHARE, need)
|
||
if share > IB_SHARE_MAX:
|
||
return None
|
||
return {"Deribit": 1.0 - share, "IB": share}
|
||
|
||
|
||
def main() -> None:
|
||
print("=" * 104)
|
||
print(" r0727 — IL CAPITALE GIA' FERMO: cosa compra un versamento iniziale, e cosa fa al venue")
|
||
print("=" * 104)
|
||
|
||
series = VR.venue_series()
|
||
r_deribit = series["Deribit"].values.astype(float)
|
||
gross = CC.TARGET_EUR_DAY * 365 * CC.EURUSD / (1 - CC.TAX_RATE)
|
||
_, perp, wall = WR.perp_and_wall(r_deribit, 1.0, gross)
|
||
print(f" book live de-luckato ×{DELUCK} | rendita perpetua {perp:.2%} | bersaglio ${TARGET:,.0f}")
|
||
print(f" {N_PATHS} path, blocchi {BLOCK}g, orizzonte {YEARS}a, conto di partenza ${START:.0f}, "
|
||
f"EURUSD {CC.EURUSD}")
|
||
|
||
# ---------------------------------------------------------------- validazione
|
||
print("\n" + "-" * 104)
|
||
print(" VALIDAZIONE — con lump 0 e €250/mese si devono riprodurre i numeri del 26/07")
|
||
print("-" * 104)
|
||
for p in (0.0, 0.01):
|
||
mine = simulate(CONC, series, p, lump_usd=0.0, dep_eur=250.0)
|
||
theirs = VR.simulate({"Deribit": 1.0}, series, p, n_paths=N_PATHS, years=YEARS, seed=SEED)
|
||
ok = (abs(mine["p_reach"] - theirs["p_reach"]) < 1e-12
|
||
and abs(mine["p_all_dead"] - theirs["p_all_dead"]) < 1e-12)
|
||
print(f" p={p:.1%} P(arrivare) {mine['p_reach']:.4f} vs {theirs['p_reach']:.4f} · "
|
||
f"P(perso tutto) {mine['p_all_dead']:.4f} vs {theirs['p_all_dead']:.4f} "
|
||
f"-> {'IDENTICO' if ok else 'DIVERGE (la generalizzazione ha cambiato qualcosa)'}")
|
||
|
||
# ---------------------------------------------------------------- (1) cosa compra il lump
|
||
print("\n" + "=" * 104)
|
||
print(" (1) COSA COMPRA UN VERSAMENTO INIZIALE — senza rischio di venue (p=0), 100% Deribit")
|
||
print("=" * 104)
|
||
print(f"\n {'lump':>8}{'dep/mese':>10}{'tot versato':>14}{'anni al muro':>14}"
|
||
f"{'P(entro 20a)':>14}{'cap. mediano':>15}{'rendita a 20a':>16}")
|
||
grid = {}
|
||
for dep in (0.0, 250.0):
|
||
for L in LUMPS_EUR:
|
||
r = simulate(CONC, series, 0.0, lump_usd=L * CC.EURUSD, dep_eur=dep)
|
||
grid[(L, dep)] = r
|
||
rent = r["median_end"] * perp * (1 - CC.TAX_RATE) / 365.0 / CC.EURUSD
|
||
yrs = f"{r['median_yrs']:.1f}a" if not np.isnan(r["median_yrs"]) else "mai"
|
||
print(f" €{L:>7,.0f}{dep:>10,.0f}{r['paid']:>13,.0f}${yrs:>14}"
|
||
f"{r['p_reach']:>13.1%}{r['median_end']:>14,.0f}${rent:>13.2f} €/g")
|
||
|
||
# equivalenza: a quale versamento mensile equivale il lump, a parita' di traguardo mediano.
|
||
# NB: NON si confronta il lump col totale dei versamenti risparmiati — sarebbe la domanda
|
||
# sbagliata (il valore e' arrivare prima, non versare meno), e darebbe un rapporto <1 che
|
||
# invita alla conclusione opposta.
|
||
print("\n EQUIVALENZA — a quanti €/mese IN PIU', per sempre, equivale il lump-sum?")
|
||
base = grid[(0.0, 250.0)]["median_yrs"]
|
||
for L in LUMPS_EUR[1:]:
|
||
got = grid[(L, 250.0)]["median_yrs"]
|
||
lo, hi = 250.0, 3_000.0
|
||
for _ in range(12): # bisezione sul deposito a pari traguardo
|
||
mid = 0.5 * (lo + hi)
|
||
y = simulate(CONC, series, 0.0, lump_usd=0.0, dep_eur=mid)["median_yrs"]
|
||
if np.isnan(y) or y > got:
|
||
lo = mid
|
||
else:
|
||
hi = mid
|
||
eq = 0.5 * (lo + hi)
|
||
print(f" €{L:>6,.0f} oggi -> traguardo {got:.1f}a (invece di {base:.1f}a) "
|
||
f"= come versare €{eq:,.0f}/mese invece di €250, cioe' €{eq - 250:,.0f}/mese in piu' "
|
||
f"per {got:.0f} anni (€{(eq - 250) * 12 * got:,.0f})")
|
||
|
||
# ---------------------------------------------------------------- (2) venue
|
||
print("\n" + "=" * 104)
|
||
print(" (2) LO STESSO LUMP, COL RISCHIO DI VENUE DENTRO — concentrato vs split")
|
||
print(f" SPLIT = Deribit / IB, quota IB scelta dal CAPITALE (minimo eseguibile "
|
||
f"${GTAA_MIN_CAPITAL:,.0f}, tetto {IB_SHARE_MAX:.0%})")
|
||
print("=" * 104)
|
||
ib_real = {**series, "IB": haircut(series["IB"], IB_HAIRCUT_YR)}
|
||
for L in LUMPS_EUR:
|
||
tot = START + L * CC.EURUSD
|
||
cfg = split_config(tot)
|
||
if cfg:
|
||
why = (f"POSSIBILE a quota IB {cfg['IB']:.0%} (${tot * cfg['IB']:,.0f} sulla gamba equity; "
|
||
f"il minimo eseguibile e' ${GTAA_MIN_CAPITAL:,.0f})")
|
||
else:
|
||
why = (f"IMPOSSIBILE: servirebbe una quota IB del {GTAA_MIN_CAPITAL / tot:.0%} "
|
||
f"per fare ${GTAA_MIN_CAPITAL:,.0f}, oltre il tetto {IB_SHARE_MAX:.0%}")
|
||
print(f"\n lump €{L:,.0f} -> capitale iniziale ${tot:,.0f} · split {why}")
|
||
print(f" {'p annua':>9}{'P(arrivare) CONC':>19}{'SPLIT':>9}{'SPLIT+costi':>13}"
|
||
f"{'P(perso tutto) CONC':>22}{'SPLIT':>9}")
|
||
for p in P_GRID:
|
||
c = simulate(CONC, series, p, lump_usd=L * CC.EURUSD)
|
||
if cfg:
|
||
s = simulate(cfg, series, p, lump_usd=L * CC.EURUSD)
|
||
sr = simulate(cfg, ib_real, p, lump_usd=L * CC.EURUSD)
|
||
print(f" {p:>8.1%}{c['p_reach']:>18.1%}{s['p_reach']:>9.1%}{sr['p_reach']:>13.1%}"
|
||
f"{c['p_all_dead']:>21.1%}{s['p_all_dead']:>9.1%}")
|
||
else:
|
||
print(f" {p:>8.1%}{c['p_reach']:>18.1%}{'-':>9}{'-':>13}"
|
||
f"{c['p_all_dead']:>21.1%}{'-':>9}")
|
||
# --- lo split che si puo' fare OGGI: la gamba equity e' bloccata dal PRIIPs -------------
|
||
print("\n" + "-" * 104)
|
||
print(" (2-bis) E SE LA SECONDA GAMBA FOSSE SOLO LIQUIDITA'?")
|
||
print(" GTAA01 oggi NON e' deployabile (blocco PRIIPs sugli ETF USA; la via UCITS su Degiro")
|
||
print(" e' verificata ma in preparazione). Lo split disponibile SUBITO e' Deribit + un conto")
|
||
print(" fermo (XEON o simile, ~0% reale). Quanta protezione viene dalla DIVERSIFICAZIONE DI")
|
||
print(" VENUE e quanta dai ritorni della gamba equity?")
|
||
print("-" * 104)
|
||
L = 10_000.0
|
||
tot = START + L * CC.EURUSD
|
||
cfg = split_config(tot)
|
||
zero = {**series, "IB": series["IB"] * 0.0} # stessa griglia temporale, drift nullo
|
||
print(f"\n lump €{L:,.0f} (${tot:,.0f}), quota seconda gamba {cfg['IB']:.0%}")
|
||
print(f" {'p annua':>9}{'P(arrivare) CONC':>19}{'SPLIT-GTAA':>13}{'SPLIT-CASSA':>13}"
|
||
f"{'P(perso tutto) CONC':>22}{'SPLIT':>9}")
|
||
for p in P_GRID:
|
||
c = simulate(CONC, series, p, lump_usd=L * CC.EURUSD)
|
||
g = simulate(cfg, series, p, lump_usd=L * CC.EURUSD)
|
||
z = simulate(cfg, zero, p, lump_usd=L * CC.EURUSD)
|
||
print(f" {p:>8.1%}{c['p_reach']:>18.1%}{g['p_reach']:>13.1%}{z['p_reach']:>13.1%}"
|
||
f"{c['p_all_dead']:>21.1%}{g['p_all_dead']:>9.1%}")
|
||
print("\n (P(perso tutto) e' identica fra SPLIT-GTAA e SPLIT-CASSA: dipende da QUANTI conti")
|
||
print(" falliscono, non da cosa ci sta sopra. La differenza fra le due colonne di")
|
||
print(" P(arrivare) e' il prezzo di tenere ferma la seconda gamba invece di investirla.)")
|
||
|
||
print("\n ⚠️ La colonna P(perso tutto) sotto CONC non dipende dal lump: con UN conto solo,")
|
||
print(" 'almeno un fallimento' COINCIDE con 'perso tutto', e quella probabilita' e' una")
|
||
print(" proprieta' del tempo di esposizione, non del capitale. Il lump non la peggiora —")
|
||
print(" ma moltiplica cio' che quella probabilita' porta via.")
|
||
|
||
# ---------------------------------------------------------------- (2-ter) dove mettere 5k
|
||
print("\n" + "=" * 104)
|
||
print(f" (2-ter) DOVE METTERE €{LUMP_FOCUS:,.0f} — la configurazione realmente pianificata")
|
||
print(f" (€{LUMP_FOCUS:,.0f} + €{DEP_FOCUS:,.0f}/mese; il cap di `live.json` e' gia'")
|
||
print(" armato per questo caso dal 26/07)")
|
||
print("=" * 104)
|
||
tot5 = START + LUMP_FOCUS * CC.EURUSD
|
||
print(f"\n Capitale iniziale ${tot5:,.0f}. Cosa si sblocca a questa taglia: NIENTE.")
|
||
print(f" GTAA01 richiede ${GTAA_MIN_CAPITAL:,.0f} SULLA GAMBA "
|
||
f"(= {GTAA_MIN_CAPITAL / tot5:.0%} del conto: oltre ogni quota sensata) e non e'")
|
||
print(" deployabile comunque (PRIIPs); XS01 ~$20.000; XSR01 e' sotto gate fino al 23/10.")
|
||
print(" -> il book resta TP01+SKH01, e la sola domanda e' QUANTA parte NON sta sull'exchange.")
|
||
print("\n Seconda gamba = LIQUIDITA' ferma (nessuna soglia di eseguibilita', ~0% reale).")
|
||
print(" Due letture del rischio della liquidita', perche' non e' un exchange:")
|
||
print(" [A] cassa rischiosa come Deribit (conservativa contro lo split)")
|
||
print(" [B] cassa che non fallisce (conto bancario con tutela dei depositi)")
|
||
for p in (0.005, 0.01, 0.02, 0.05):
|
||
print(f"\n p(exchange) = {p:.1%}")
|
||
print(f" {'fuori exchange':>15}{'P(arrivare)':>13}{'anni':>8}"
|
||
f"{'P(perso tutto) [A]':>21}{'[B]':>8}{'salvato se cade':>17}")
|
||
for share in (0.0, 0.10, 0.25, 0.40):
|
||
cfg5 = {"Deribit": 1.0 - share, "IB": share} if share > 0 else {"Deribit": 1.0}
|
||
ser5 = {**series, "IB": series["IB"] * 0.0}
|
||
a = simulate(cfg5, ser5, p, lump_usd=LUMP_FOCUS * CC.EURUSD, dep_eur=DEP_FOCUS)
|
||
b = simulate(cfg5, ser5, {"Deribit": p, "IB": 0.0},
|
||
lump_usd=LUMP_FOCUS * CC.EURUSD, dep_eur=DEP_FOCUS)
|
||
yrs = f"{a['median_yrs']:.1f}a" if not np.isnan(a["median_yrs"]) else "mai"
|
||
resta = b["median_salvage"]
|
||
print(f" {share:>14.0%}{a['p_reach']:>13.1%}{yrs:>8}"
|
||
f"{a['p_all_dead']:>20.1%}{b['p_all_dead']:>8.1%}{resta:>16,.0f}$")
|
||
print("\n ⚠️ La colonna [B] e' quella realistica per un conto bancario in UE (tutela dei")
|
||
print(" depositi fino a €100k): tenere fuori una quota qualsiasi porta P(perso tutto)")
|
||
print(" a ZERO, perche' 'perso tutto' richiede che falliscano ENTRAMBI i conti.")
|
||
print(" La [A] e' il caso in cui anche la cassa e' a rischio: e' li' che si vede il")
|
||
print(" prezzo vero della protezione, e resta grande.")
|
||
|
||
# ---------------------------------------------------------------- (3) la soglia
|
||
print("\n" + "=" * 104)
|
||
print(" (3) DA QUALE CAPITALE LO SPLIT E' POSSIBILE SENZA DISTORCERE I PESI")
|
||
print("=" * 104)
|
||
ideale = GTAA_MIN_CAPITAL / IB_SHARE
|
||
minimo = GTAA_MIN_CAPITAL / IB_SHARE_MAX
|
||
tot10 = START + 10_000 * CC.EURUSD
|
||
print(f"\n soglia di eseguibilita' della gamba equity (GTAA_MIN_CAPITAL): ${GTAA_MIN_CAPITAL:,.0f}")
|
||
print(f" quota IB raccomandata da r0725_ib10k (oltre, cala la rendita): {IB_SHARE:.0%}")
|
||
print(f" -> split a quota RACCOMANDATA possibile da : ${ideale:,.0f}")
|
||
print(f" -> split possibile forzando la quota fino al {IB_SHARE_MAX:.0%} da : ${minimo:,.0f}")
|
||
print(f"\n Con un lump da €10.000 il conto sarebbe ${tot10:,.0f}: "
|
||
f"{'SOPRA' if tot10 >= ideale else 'SOTTO'} la soglia a quota")
|
||
print(f" raccomandata (${ideale:,.0f}) e {'sopra' if tot10 >= minimo else 'sotto'} "
|
||
f"quella a quota forzata (${minimo:,.0f}) -> lo split e' possibile, ma a")
|
||
print(f" quota IB {max(IB_SHARE, GTAA_MIN_CAPITAL / tot10):.0%}, non {IB_SHARE:.0%}: "
|
||
"e' il capitale a scegliere la quota, non la preferenza.")
|
||
print(f"\n La riapertura della decisione di venue e' fissata a ${20_000:,.0f}. Un lump da")
|
||
print(f" €10k porta il conto a ${tot10:,.0f} — sotto quella data ma sopra la fattibilita'")
|
||
print(" tecnica, ed e' un cambiamento del piano: CLAUDE.md dice di riaprire PRIMA in")
|
||
print(" questo caso, e questa tabella e' il materiale per farlo.")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|