067c89fc16
10.000 EUR + 500/mese: P(entro 10 anni) 33.9% (contro 19.7% con 5k), mediana 10.7 anni, rendita a 10 anni 42.25 EUR/g. Raddoppia quasi la probabilita' ma resta fuori dal vincolo. Versamento richiesto con lump 10k: P=50% -> 604 EUR/m, P=75% -> 793, P=90% -> 975. Cioe' 5.000 EUR in piu' oggi valgono 77 EUR/mese per 10 anni (~9.240): meno del 2.45x misurato su 20 anni, perche' a orizzonte corto il lump compone meno. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
167 lines
8.4 KiB
Python
167 lines
8.4 KiB
Python
#!/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()
|