696 lines
38 KiB
Python
696 lines
38 KiB
Python
#!/usr/bin/env python
|
|
"""r0822d_piano_vero.py — LA TABELLA DEL PIANO CON TUTTE LE CORREZIONI DENTRO, UNA VOLTA SOLA.
|
|
|
|
IL BUCO. L'obiettivo dichiarato del progetto e' 50 EUR/giorno e la domanda operativa e' «quanto
|
|
ci vuole, versando quanto». La tabella pubblicata che risponde e' stata corretta DUE VOLTE, ogni
|
|
volta partendo dallo stesso numero LORDO e mai dall'altra correzione:
|
|
|
|
riga «250 EUR/mese», P(traguardo entro 20 anni)
|
|
* 92% al LORDO (26/07, fattore d'ancora x0.89)
|
|
* 52% col FISCO d'accumulo (07/08, r0807_piano_netto)
|
|
* 64% col FUNDING (22/08, r0822d_funding)
|
|
* ??? con ENTRAMBI <- non esiste
|
|
|
|
Le due lenti sono state applicate SEPARATAMENTE. Questo script produce la lente unica, coerente,
|
|
con dentro tutte le correzioni MISURATE (nessuna scelta a mano), e dice quanto ci vuole davvero.
|
|
|
|
L'ORDINE DI APPLICAZIONE, e perche' quello.
|
|
1. FUNDING — e' un costo sul rendimento LORDO: si paga sul nozionale, ogni ora, prima che
|
|
esista una plusvalenza. Entra DENTRO la serie dei ritorni, giorno per giorno
|
|
(`r0822d_funding.book_75_25(rate)`), non come haircut a valle.
|
|
2. ANCORA x0.89 — e' una correzione sul DRIFT della serie (fortuna di calendario misurata,
|
|
r0726_deluck_factor). Si applica alla serie, dopo il funding, come fa
|
|
`r0822d_funding.sezione4c` — cosi' i numeri restano confrontabili con quelli pubblicati.
|
|
La convenzione alternativa (de-luckare il lordo e POI togliere il funding) e' riportata
|
|
come sensibilita': vale 0.24pp di drift.
|
|
3. FISCO — tassa la PLUSVALENZA, a fine anno, sul percorso GIA' netto di funding. L'ordine
|
|
inverso (tassare la plusvalenza lorda e poi sottrarre il funding dal capitale gia' tassato)
|
|
farebbe pagare il 33% su un guadagno mai incassato.
|
|
Da qui l'effetto di SECONDO ORDINE che la somma delle due correzioni non contiene: meno drift
|
|
-> meno plusvalenza -> meno imposta. Il congiunto e' meno severo della somma, e di quanto e'
|
|
una MISURA (sezione 3), non un'assunzione.
|
|
|
|
COSA NON C'E' DENTRO, DI PROPOSITO. Il rischio di venue. Non e' un costo, e' un rischio di
|
|
ROVINA: a p=5% annuo il capitale mediano a 20 anni e' ZERO per qualunque calendario di
|
|
versamenti (26/07). Sta su un asse separato e ci resta; metterlo qui trasformerebbe una tabella
|
|
di pianificazione in una tabella di sopravvivenza.
|
|
|
|
ASSUNZIONI FISCALI, dichiarate e NON un parere fiscale (le stesse del 27/07 e del 07/08):
|
|
33% sulle plusvalenze cripto realizzate, minusvalenze in carry 4 anni, patrimoniale 0.2%/anno
|
|
sul valore. Il modello tassa la variazione ANNUA di valore = limite superiore, stretto perche'
|
|
il libro realizza quasi tutto entro l'anno.
|
|
|
|
TRAPPOLE GIA' PAGATE DA QUESTO PROGETTO, evitate qui per costruzione:
|
|
* un NON-ARRIVO si codifica +inf, mai -1 (con -1 la mediana MIGLIORA quando la colonna
|
|
peggiora: errore commesso il 07/08). Qui si stampano DUE mediane: quella incondizionata
|
|
(non-arrivo = +inf, quindi «>25a») e quella condizionata, che non compare MAI senza la sua
|
|
probabilita' accanto.
|
|
* un Monte Carlo ha una RISOLUZIONE: misurata su 3 semi e dichiarata (sezione 2).
|
|
* la serie del libro e' su giorni di CALENDARIO (cripto, 365/anno): l'annualizzazione a 365 e'
|
|
corretta e `to_daily()` non serve. Sarebbe servito su una serie di giorni di borsa.
|
|
|
|
uv run python scripts/research/r0822d_piano_vero.py
|
|
uv run python scripts/research/r0822d_piano_vero.py --rebuild # ignora la cache di serie
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
ROOT = Path("/opt/docker/PythagorasGoal")
|
|
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
|
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
import r0725_capcurve as CC # noqa: E402 TAX_RATE, EURUSD, _boot_paths, book_series
|
|
import r0726_capwall_refresh as WR # noqa: E402 perp_and_wall, trajectory (2000 path)
|
|
import r0727_lumpsum_split as LS # noqa: E402 TARGET pubblicato
|
|
import r0727_tasse as TX # noqa: E402 PATRIMONIALE, CARRY_ANNI
|
|
import r0807_piano_netto as PN # noqa: E402 passo_fiscale, sopravvivenza, accumula
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Costanti dichiarate
|
|
# ---------------------------------------------------------------------------
|
|
DELUCK = 0.89 # fattore d'ancora MISURATO (r0726_deluck_factor), non a occhio
|
|
START = 635.0 # il conto Deribit VERO di oggi (le tabelle pubblicate usano 600)
|
|
START_PUB = 600.0 # solo per la replica
|
|
N_WALL = 4000 # path per il muro (la taglia a cui furono calcolati i pubblicati)
|
|
N_TRAJ = 3000 # path per le traiettorie (idem, seed 725)
|
|
N_DEP = 3000 # path per la bisezione sul versamento
|
|
BLOCK = 20
|
|
SEED_TRAJ = 725
|
|
SEED_DEP = 20260822
|
|
SEED_WALL = PN.SEED_MURO # 725
|
|
ANNI_TRAJ = 25 # 20 troncherebbe la coda: convenzione di WR.trajectory
|
|
VERSAMENTI = (0, 250, 500, 800, 1_000, 2_000)
|
|
LUMPS_EUR = (0.0, 2_000.0, 5_000.0, 10_000.0)
|
|
ORIZZONTI = (10, 15, 20)
|
|
CONFIDENZE = (0.50, 0.75, 0.90)
|
|
VINTAGE_PUB = "2026-08-07" # la data in cui furono calcolati $272.061 / $258.338
|
|
COSTO_STRUMENTO_VERO = 0.013931 # -1.39%/anno: funding misurato sulla sola finestra >= 2022-03
|
|
|
|
CACHE = Path("/tmp/claude-1001/-opt-docker-PythagorasGoal/"
|
|
"b6cc75e7-14f8-4c32-bd07-ab8a0d2aaee6/scratchpad/pianovero")
|
|
if not CACHE.parent.parent.exists():
|
|
CACHE = Path("/tmp/pythagoras_pianovero")
|
|
REBUILD = "--rebuild" in sys.argv
|
|
T0 = time.time()
|
|
|
|
|
|
def sez(t: str) -> None:
|
|
print("\n" + "=" * 100)
|
|
print(f" {t}")
|
|
print("=" * 100)
|
|
|
|
|
|
def clock() -> str:
|
|
return f"[{time.time() - T0:5.0f}s]"
|
|
|
|
|
|
# ===========================================================================
|
|
# 0) LE SERIE
|
|
# ===========================================================================
|
|
def costruisci_serie() -> pd.DataFrame:
|
|
"""Le due serie giornaliere del libro live 75/25: senza e con funding.
|
|
|
|
Nessuna re-implementazione: `r0822d_funding.book_75_25` e' la stessa lente di
|
|
`CC.book_series('hourly')` e la replica bit-exact e' verificata qui sotto.
|
|
"""
|
|
f = CACHE / "book.parquet"
|
|
if f.exists() and not REBUILD:
|
|
return pd.read_parquet(f)
|
|
# Se la cache dello storico funding c'e', si usa quella: nessuna chiamata al venue (il
|
|
# progetto gira su una VPS con soldi veri e un rate limit Deribit per-IP gia' saturato una
|
|
# volta, 29/07). Se NON c'e', si lascia che `fetch_funding` la scarichi: cosi' lo script
|
|
# gira anche da solo, su una macchina pulita.
|
|
for c in (Path("/tmp/claude-1001/-opt-docker-PythagorasGoal/"
|
|
"b6cc75e7-14f8-4c32-bd07-ab8a0d2aaee6/scratchpad/fund"),
|
|
Path("/tmp/pythagoras_funding_cache")):
|
|
if any(c.glob("*.parquet")):
|
|
sys.argv.append("--no-net")
|
|
break
|
|
import r0822d_funding as FD # noqa: E402 (USE_NET si decide all'import)
|
|
F = {}
|
|
for instr, (start, _asset, _kind) in FD.INSTR.items():
|
|
d = FD.fetch_funding(instr, start).copy()
|
|
d["dt"] = pd.to_datetime(d["ts"], unit="ms", utc=True)
|
|
F[instr] = d.drop_duplicates(subset="ts").sort_values("dt").set_index("dt")
|
|
rate = FD.build_rate(F)
|
|
A, B = FD.book_75_25(None), FD.book_75_25(rate)
|
|
out = pd.DataFrame({"nofund": A, "fund": B.reindex(A.index)})
|
|
CACHE.mkdir(parents=True, exist_ok=True)
|
|
out.to_parquet(f)
|
|
return out
|
|
|
|
|
|
def deluck(r: np.ndarray) -> np.ndarray:
|
|
"""Toglie dal DRIFT la fortuna d'ancora misurata. La vol non si tocca: fra le ancore era
|
|
invariata (7.80% -> 7.76%), la fortuna sta tutta nel drift (r0726_deluck_factor)."""
|
|
r = np.asarray(r, float)
|
|
return r - (1.0 - DELUCK) * r.mean()
|
|
|
|
|
|
# ===========================================================================
|
|
# LE LENTI
|
|
# ===========================================================================
|
|
class Lente:
|
|
"""Una lente = (serie di ritorni, convenzione di prelievo, regime fiscale d'accumulo).
|
|
|
|
`lordizza=True` -> convenzione PUBBLICATA: il prelievo si lordizza al 33% e il capitale
|
|
investito non paga mai imposte. Incoerente (r0807), tenuta per replica.
|
|
`lordizza=False` -> convenzione COERENTE: imposte ogni anno dentro il portafoglio, prelievo
|
|
gia' netto.
|
|
"""
|
|
|
|
def __init__(self, nome: str, r: np.ndarray, lordizza: bool, nota: str = ""):
|
|
self.nome, self.r, self.lordizza, self.nota = nome, np.asarray(r, float), lordizza, nota
|
|
self.aliq = 0.0 if lordizza else CC.TAX_RATE
|
|
self.patr = 0.0 if lordizza else TX.PATRIMONIALE
|
|
self.perp = float("nan")
|
|
self.muro = float("nan")
|
|
|
|
@property
|
|
def prelievo(self) -> float:
|
|
netto = CC.TARGET_EUR_DAY * 365 * CC.EURUSD
|
|
return netto / (1 - CC.TAX_RATE) if self.lordizza else netto
|
|
|
|
@property
|
|
def drift(self) -> float:
|
|
return float(self.r.mean()) * 365.0
|
|
|
|
@property
|
|
def vol(self) -> float:
|
|
return float(self.r.std()) * 365.0 ** 0.5
|
|
|
|
|
|
def muro_di(lente: Lente, n_paths: int = N_WALL, seed: int = SEED_WALL) -> tuple[float, float]:
|
|
"""(rendita perpetua, capitale necessario). Perpetua = il prelievo piu' alto con
|
|
P(capitale a 20 anni >= capitale iniziale) >= 90% — definizione del 25/07, invariata.
|
|
|
|
Bisezione identica a `PN.muro`, esposta solo per poter cambiare SEME e misurare la
|
|
risoluzione Monte Carlo. Il controllo positivo (a seme 725 deve dare esattamente `PN.muro`)
|
|
e' in sezione 2.
|
|
"""
|
|
lo, hi = 0.0, 0.40
|
|
for _ in range(13):
|
|
mid = (lo + hi) / 2
|
|
s = PN.sopravvivenza(lente.r, 1e6, 1e6 * mid, 20, lente.aliq, lente.patr,
|
|
n_paths=n_paths, seed=seed)
|
|
if s["p10_end"] >= 1e6:
|
|
lo = mid
|
|
else:
|
|
hi = mid
|
|
return lo, (lente.prelievo / lo if lo > 0.002 else float("inf"))
|
|
|
|
|
|
# ===========================================================================
|
|
# lettura onesta di un vettore di primi passaggi
|
|
# ===========================================================================
|
|
def leggi(colpito: np.ndarray, anni_sim: int = ANNI_TRAJ) -> dict:
|
|
"""Mediana INCONDIZIONATA (non-arrivo = +inf) + mediana condizionata + P(20 anni).
|
|
|
|
⚠️ Il non-arrivo e' +inf, MAI -1: con -1 la mediana scende quanto peggio va la colonna
|
|
(errore commesso il 07/08 e corretto lo stesso giorno). E la mediana condizionata non si
|
|
stampa mai da sola: descrive solo chi arriva.
|
|
"""
|
|
y = np.where(colpito >= 0, colpito / 365.0, np.inf)
|
|
arr = y[np.isfinite(y)]
|
|
return dict(
|
|
med=float(np.median(y)), # +inf se meno di meta' arriva
|
|
med_cond=float(np.median(arr)) if len(arr) > 100 else float("nan"),
|
|
p20=float((y <= 20.0).mean()),
|
|
p_arr=float(np.isfinite(y).mean()),
|
|
anni_sim=anni_sim)
|
|
|
|
|
|
def fmt_med(d: dict) -> str:
|
|
m = d["med"]
|
|
return f">{d['anni_sim']}a" if not np.isfinite(m) else f"{m:.1f}a"
|
|
|
|
|
|
def fmt_cond(d: dict) -> str:
|
|
c = d["med_cond"]
|
|
return "—" if np.isnan(c) else f"{c:.1f}a"
|
|
|
|
|
|
# ===========================================================================
|
|
# bisezione sul versamento (start parametrico: le tabelle pubblicate partono da $600)
|
|
# ===========================================================================
|
|
def dep_per_conf(paths: np.ndarray, lente: Lente, conf: float, start: float,
|
|
lump_eur: float = 0.0) -> tuple[float, float]:
|
|
"""(versamento mensile EUR minimo per P(traguardo entro l'orizzonte) >= conf, totale versato).
|
|
|
|
I path si estraggono UNA volta e si riusano a ogni passo: con campioni diversi per passo la
|
|
funzione non sarebbe monotona e la bisezione oscillerebbe (lezione WR.solve_deposit).
|
|
"""
|
|
def p(dep: float) -> float:
|
|
c = PN.accumula(paths, dep, lente.muro, lente.aliq, lente.patr,
|
|
start=start, lump_eur=lump_eur)["colpito"]
|
|
return float((c >= 0).mean())
|
|
|
|
lo, hi = 0.0, 6_000.0
|
|
if p(hi) < conf:
|
|
return float("inf"), float("nan")
|
|
for _ in range(12):
|
|
mid = (lo + hi) / 2
|
|
if p(mid) >= conf:
|
|
hi = mid
|
|
else:
|
|
lo = mid
|
|
versato = PN.accumula(paths, hi, lente.muro, lente.aliq, lente.patr,
|
|
start=start, lump_eur=lump_eur)["versato"]
|
|
return hi, versato
|
|
|
|
|
|
# ===========================================================================
|
|
def main() -> None:
|
|
print("=" * 100)
|
|
print(" r0822d — IL PIANO VERO: fisco d'accumulo E funding nella STESSA tabella")
|
|
print("=" * 100)
|
|
|
|
B = costruisci_serie()
|
|
idx = pd.DatetimeIndex(B.index)
|
|
A_raw, F_raw = B["nofund"].values.astype(float), B["fund"].values.astype(float)
|
|
print(f"\n serie giornaliera del libro LIVE (TP01 75 + SKH01 25, lente 'hourly', path live)")
|
|
print(f" {len(B)} giorni, dal {idx.min().date()} al {idx.max().date()} {clock()}")
|
|
print(f" drift lordo {A_raw.mean() * 365:.3%} · drift netto funding {F_raw.mean() * 365:.3%}")
|
|
print(" ⚠️ giorni di CALENDARIO (cripto): annualizzazione a 365 corretta, `to_daily()` non")
|
|
print(" serve qui — servirebbe su una serie di giorni di borsa (lezione 25/07 su GTAA01).")
|
|
|
|
# =====================================================================
|
|
sez("1) CONTROLLO DI REPLICA — prima di pubblicare un numero nuovo, riprodurre i vecchi")
|
|
print("\n Regola del progetto (07/08): «prima di pubblicare un numero nuovo, far riprodurre")
|
|
print(" alla macchina quello vecchio». Cinque controlli, tutti su codice IMPORTATO.\n")
|
|
ok = {}
|
|
|
|
# (R1) la serie senza funding e' bit-exact la serie del libro gia' pubblicata
|
|
C = CC.book_series("hourly")
|
|
d = float(np.max(np.abs(A_raw - C.reindex(idx).values)))
|
|
ok["R1"] = d == 0.0
|
|
print(f" R1 serie 'senza funding' vs CC.book_series('hourly') max|dif| = {d:.3e} "
|
|
f"{'BIT-EXACT' if ok['R1'] else 'FALLITA'}")
|
|
|
|
# (R2) il costo del funding per finestra
|
|
print(f"\n R2 costo del funding per finestra (pubblicato 22/08: -2,16% / -1,39% / -0,55%)")
|
|
fin = [("piena (proxy inverse pre-2022)", None, -0.0216),
|
|
(">= 2022-03-15 (strumento vero)", "2022-03-15", -0.0139),
|
|
(">= 2025-01-01", "2025-01-01", -0.0055)]
|
|
r2 = True
|
|
for lab, lo, atteso in fin:
|
|
m = np.ones(len(B), bool) if lo is None else np.asarray(idx >= pd.Timestamp(lo, tz="UTC"))
|
|
got = -(A_raw[m].mean() - F_raw[m].mean()) * 365
|
|
r2 &= abs(got - atteso) < 5e-4
|
|
print(f" {lab:<34} n={m.sum():>5} misurato {got:+.4%} atteso {atteso:+.2%}")
|
|
ok["R2"] = r2
|
|
|
|
# (R3-R4) i muri pubblicati, sul VINTAGE su cui furono calcolati
|
|
print(f"\n R3/R4 i muri pubblicati ($272.061 lordo / $258.338 netto fisco) sul vintage")
|
|
print(f" {VINTAGE_PUB} — la data in cui furono calcolati.")
|
|
mv = np.asarray(idx <= pd.Timestamp(VINTAGE_PUB, tz="UTC"))
|
|
Lv0 = Lente("vintage LORDO", deluck(A_raw[mv]), True)
|
|
Lv1 = Lente("vintage +FISCO", deluck(A_raw[mv]), False)
|
|
Lv0.perp, Lv0.muro = PN.muro(Lv0.r, Lv0.prelievo, 0.0, 0.0)
|
|
Lv1.perp, Lv1.muro = PN.muro(Lv1.r, Lv1.prelievo, Lv1.aliq, Lv1.patr)
|
|
ok["R3"] = abs(Lv0.muro - LS.TARGET) < 1.0
|
|
ok["R4"] = abs(Lv1.muro - 258_338.0) < 1.0
|
|
print(f" LORDO perpetua {Lv0.perp:.4%} muro ${Lv0.muro:,.0f} atteso $272,061 "
|
|
f"{'ESATTO AL DOLLARO' if ok['R3'] else 'DIVERSO'}")
|
|
print(f" NETTO perpetua {Lv1.perp:.4%} muro ${Lv1.muro:,.0f} atteso $258,338 "
|
|
f"{'ESATTO AL DOLLARO' if ok['R4'] else 'DIVERSO'} {clock()}")
|
|
|
|
# (R5) i numeri del funding, alla SUA taglia MC (2000 path)
|
|
print(f"\n R5 i numeri del filone funding (22/08), alla sua taglia dichiarata (2000 path):")
|
|
g = CC.TARGET_EUR_DAY * 365 * CC.EURUSD / (1 - CC.TAX_RATE)
|
|
rA, rF = deluck(A_raw), deluck(F_raw)
|
|
_, pA, wA = WR.perp_and_wall(rA, 1.0, g)
|
|
_, pF, wF = WR.perp_and_wall(rF, 1.0, g)
|
|
tA = WR.trajectory(rA, wA, n_paths=2000)[250]
|
|
tF = WR.trajectory(rF, wF, n_paths=2000)[250]
|
|
ok["R5"] = abs(wF / wA - 1 - 0.175) < 0.004 and abs(pF - 0.0914) < 3e-4
|
|
print(f" perpetua {pA:.2%} -> {pF:.2%} (pubblicato 10,73% -> 9,14%)")
|
|
print(f" muro ${wA:,.0f} -> ${wF:,.0f} = {wF / wA - 1:+.1%} (pubblicato $276,6k -> "
|
|
f"$325,0k = +17,5%)")
|
|
print(f" traiettoria EUR250/mese {tA['p50']:.1f}a P20 {tA['p20y']:.0%} -> "
|
|
f"{tF['p50']:.1f}a P20 {tF['p20y']:.0%} (pubblicato 16,4a/90% -> 18,7a/64%) {clock()}")
|
|
|
|
# (R6) la colonna LORDA e la colonna NETTA delle traiettorie, sul vintage, da $600
|
|
print(f"\n R6 le due colonne di traiettoria pubblicate, sul vintage {VINTAGE_PUB}, da $600:")
|
|
rng = np.random.default_rng(SEED_TRAJ)
|
|
pv = CC._boot_paths(Lv0.r, N_TRAJ, ANNI_TRAJ * 365, BLOCK, rng)
|
|
att_l = {250: 16.3, 500: 12.4, 800: 10.0, 1_000: 9.0, 2_000: 6.0}
|
|
att_n = {250: 19.8, 500: 14.7, 800: 11.4, 1_000: 10.0, 2_000: 6.4}
|
|
print(f" {'EUR/m':>7}{'LORDO mis.':>13}{'atteso':>9}{'P20':>7}"
|
|
f"{' |':>3}{'NETTO mis.':>13}{'atteso':>9}{'P20':>7}")
|
|
r6 = True
|
|
for dep in (250, 500, 800, 1_000, 2_000):
|
|
l = leggi(PN.accumula(pv, dep, Lv0.muro, 0.0, 0.0, start=START_PUB)["colpito"])
|
|
n = leggi(PN.accumula(pv, dep, Lv1.muro, Lv1.aliq, Lv1.patr, start=START_PUB)["colpito"])
|
|
r6 &= abs(l["med_cond"] - att_l[dep]) < 0.25 and abs(n["med_cond"] - att_n[dep]) < 0.25
|
|
print(f" {dep:>7}{fmt_cond(l):>13}{att_l[dep]:>9.1f}{l['p20']:>7.0%}"
|
|
f"{' |':>3}{fmt_cond(n):>13}{att_n[dep]:>9.1f}{n['p20']:>7.0%}")
|
|
ok["R6"] = r6
|
|
del pv
|
|
print(f"\n ESITO REPLICA: {sum(ok.values())}/{len(ok)} — "
|
|
+ ", ".join(f"{k}={'OK' if v else 'NO'}" for k, v in ok.items()))
|
|
if not all(ok.values()):
|
|
raise SystemExit("REPLICA INCOMPLETA: un numero nuovo sarebbe indistinguibile da un bug.")
|
|
print("""
|
|
⚠️ E IL FATTO CHE VA DETTO: la replica e' esatta AL DOLLARO solo sul vintage del 07/08.
|
|
Su TUTTA la serie di oggi lo stesso codice, senza una riga cambiata, da' muri diversi:""")
|
|
L0t = Lente("oggi LORDO", rA, True)
|
|
L0t.perp, L0t.muro = PN.muro(L0t.r, L0t.prelievo, 0.0, 0.0)
|
|
print(f" muro LORDO: ${Lv0.muro:,.0f} (vintage {VINTAGE_PUB}, {mv.sum()} giorni) -> "
|
|
f"${L0t.muro:,.0f} (oggi, {len(B)} giorni) = {L0t.muro / Lv0.muro - 1:+.1%}")
|
|
print(" `data/raw/` e' gitignored e il cron lo allunga ogni notte: 15 giorni in piu' di")
|
|
print(" dati spostano il muro del ~2%, cioe' PIU' della risoluzione Monte Carlo. E' la")
|
|
print(" stessa lezione del 07/08 sul gate di GTAA01 (un numero cambia senza che il codice")
|
|
print(" cambi = i dati non sono versionati). Percio' qui sotto ogni confronto fra lenti e'")
|
|
print(" fatto sullo STESSO vintage: un grado di liberta' per volta.")
|
|
|
|
# =====================================================================
|
|
sez("2) LE QUATTRO LENTI E IL MURO — la riga che non esisteva")
|
|
print("""
|
|
Tutte de-luckate x0.89. Le prime due sono le lenti pubblicate; la terza e' quella del filone
|
|
funding di stanotte; la quarta e' la lente unica con dentro TUTTO.""")
|
|
Lo = {
|
|
"L0 LORDO (26/07)": Lente("L0", rA, True, "prelievo lordizzato, nessuna imposta in accumulo"),
|
|
"L1 +FISCO (07/08)": Lente("L1", rA, False, "imposte ogni anno nel portafoglio"),
|
|
"L2 +FUNDING (22/08)": Lente("L2", rF, True, "funding esatto, convenzione pubblicata"),
|
|
"L3 CONGIUNTA": Lente("L3", rF, False, "funding esatto + fisco coerente"),
|
|
}
|
|
rFb = deluck(A_raw - COSTO_STRUMENTO_VERO / 365.0)
|
|
Lo["L3b CONGIUNTA (str. vero)"] = Lente("L3b", rFb, False,
|
|
"funding -1,39%/anno (finestra >= 2022-03)")
|
|
print(f"\n {'lente':<27}{'drift':>9}{'vol':>8}{'prelievo/a':>13}{'perpetua':>10}{'muro':>13}")
|
|
for k, L in Lo.items():
|
|
L.perp, L.muro = muro_di(L)
|
|
print(f" {k:<27}{L.drift:>9.2%}{L.vol:>8.2%}{L.prelievo:>13,.0f}"
|
|
f"{L.perp:>10.2%}{L.muro:>13,.0f}")
|
|
print(f" {clock()}")
|
|
|
|
# controllo positivo sulla bisezione con seme + risoluzione Monte Carlo
|
|
p_ctrl, w_ctrl = PN.muro(Lo["L3 CONGIUNTA"].r, Lo["L3 CONGIUNTA"].prelievo,
|
|
CC.TAX_RATE, TX.PATRIMONIALE)
|
|
same = abs(w_ctrl - Lo["L3 CONGIUNTA"].muro) < 1e-6
|
|
print(f"\n controllo positivo: `muro_di` a seme {SEED_WALL} == `PN.muro` importato "
|
|
f"${w_ctrl:,.0f} vs ${Lo['L3 CONGIUNTA'].muro:,.0f} {'OK' if same else 'DIVERGE'}")
|
|
semi = [Lo["L3 CONGIUNTA"].muro]
|
|
for s in (2026, 8_22):
|
|
semi.append(muro_di(Lo["L3 CONGIUNTA"], seed=s)[1])
|
|
sp = (max(semi) - min(semi)) / np.mean(semi)
|
|
print(f" RISOLUZIONE MONTE CARLO del muro, misurata su 3 semi a {N_WALL} path: "
|
|
+ " / ".join(f"${x:,.0f}" for x in semi))
|
|
print(f" ampiezza {sp:.1%} -> si citano TRE cifre significative (${Lo['L3 CONGIUNTA'].muro/1000:.0f}k), "
|
|
f"non il dollaro. {clock()}")
|
|
|
|
# sensibilita' sull'ordine de-luck / funding
|
|
alt = Lente("alt", deluck(A_raw) - (A_raw - F_raw).mean(), False)
|
|
alt.perp, alt.muro = muro_di(alt)
|
|
print(f"\n sensibilita' all'ORDINE de-luck/funding (convenzione alternativa: de-luckare il")
|
|
print(f" drift LORDO e poi togliere il funding): drift {alt.drift:.2%} contro "
|
|
f"{Lo['L3 CONGIUNTA'].drift:.2%}, muro ${alt.muro:,.0f} contro "
|
|
f"${Lo['L3 CONGIUNTA'].muro:,.0f} ({alt.muro / Lo['L3 CONGIUNTA'].muro - 1:+.1%}).")
|
|
print(" Sotto la risoluzione MC: la scelta di convenzione non decide niente. Si tiene quella")
|
|
print(" di `r0822d_funding.sezione4c`, per confrontabilita' con i numeri gia' pubblicati.")
|
|
|
|
# =====================================================================
|
|
sez("3) LE DUE CORREZIONI SI SOMMANO? — l'effetto di secondo ordine, misurato")
|
|
print("""
|
|
ATTESA DICHIARATA PRIMA DI MISURARE: meno drift -> meno plusvalenza -> meno imposta, quindi
|
|
il congiunto dovrebbe essere MENO severo della somma delle due correzioni prese da sole.
|
|
Misurato come interazione di un fattoriale 2x2 (funding OFF/ON x fisco OFF/ON) in TRE monete,
|
|
perche' un'interazione piccola puo' cambiare SEGNO a seconda di come la si misura.""")
|
|
|
|
# (3a) accumulo puro: il capitale a 20 anni non dipende dal muro -> isola il meccanismo
|
|
print("\n (3a) SUL CAPITALE ACCUMULATO — la domanda pura sul meccanismo.")
|
|
print(" Capitale mediano a 20 anni, EUR250/mese, partenza $%.0f. Non dipende dal muro:" % START)
|
|
print(" qui dentro c'e' SOLO l'accumulo, cioe' l'unico posto dove il meccanismo vive.")
|
|
capf = {}
|
|
for k in ("L0 LORDO (26/07)", "L1 +FISCO (07/08)", "L2 +FUNDING (22/08)", "L3 CONGIUNTA"):
|
|
L = Lo[k]
|
|
rr = np.random.default_rng(SEED_DEP)
|
|
pp = CC._boot_paths(L.r, N_DEP, 20 * 365, BLOCK, rr)
|
|
capf[k] = float(np.median(PN.accumula(pp, 250, L.muro, L.aliq, L.patr,
|
|
start=START)["cap"]))
|
|
del pp
|
|
print(f"\n {'':<14}{'fisco OFF':>16}{'fisco ON':>16}{'rapporto ON/OFF':>19}")
|
|
for lab, a, b in (("funding OFF", "L0 LORDO (26/07)", "L1 +FISCO (07/08)"),
|
|
("funding ON ", "L2 +FUNDING (22/08)", "L3 CONGIUNTA")):
|
|
print(f" {lab:<14}{capf[a]:>16,.0f}{capf[b]:>16,.0f}"
|
|
f"{capf[b] / capf[a]:>19.4f}")
|
|
dragOFF = capf["L1 +FISCO (07/08)"] / capf["L0 LORDO (26/07)"]
|
|
dragON = capf["L3 CONGIUNTA"] / capf["L2 +FUNDING (22/08)"]
|
|
inter_log = (np.log(capf["L3 CONGIUNTA"]) - np.log(capf["L1 +FISCO (07/08)"])
|
|
- np.log(capf["L2 +FUNDING (22/08)"]) + np.log(capf["L0 LORDO (26/07)"]))
|
|
verso_drag = "MENO" if dragON > dragOFF else "DI PIU'"
|
|
print(f"\n Il fisco toglie il {1 - dragOFF:.1%} del capitale a funding OFF e il "
|
|
f"{1 - dragON:.1%} a funding ON.")
|
|
esito_mecc = ("ESISTE ed e' nel verso previsto" if dragON > dragOFF else "NON si vede")
|
|
print(f" -> il meccanismo {esito_mecc}: con meno drift il fisco morde {verso_drag}.")
|
|
print(f" Interazione moltiplicativa (log-capitale): {inter_log:+.4f} = "
|
|
f"{np.expm1(inter_log):+.2%} di capitale.")
|
|
print(" ⚠️ Ma e' UNA FRAZIONE: il fisco resta la correzione dominante in entrambe le")
|
|
print(" colonne, e la 'compensazione' non ne recupera che una parte trascurabile.")
|
|
|
|
# (3b) il muro
|
|
inter_w = (Lo["L3 CONGIUNTA"].muro - Lo["L1 +FISCO (07/08)"].muro
|
|
- Lo["L2 +FUNDING (22/08)"].muro + Lo["L0 LORDO (26/07)"].muro)
|
|
print("\n (3b) SUL MURO — la fase di prelievo.")
|
|
print(f" ${Lo['L0 LORDO (26/07)'].muro:,.0f} (L0) · ${Lo['L1 +FISCO (07/08)'].muro:,.0f} (L1) · "
|
|
f"${Lo['L2 +FUNDING (22/08)'].muro:,.0f} (L2) · ${Lo['L3 CONGIUNTA'].muro:,.0f} (L3)")
|
|
print(f" interazione ${inter_w:+,.0f} = {abs(inter_w) / Lo['L3 CONGIUNTA'].muro:.2%} del muro "
|
|
f"congiunto, cioe' SOTTO la risoluzione Monte Carlo ({sp:.1%}): indistinguibile da zero.")
|
|
|
|
# (3c) il versamento necessario
|
|
print("\n (3c) SUL VERSAMENTO NECESSARIO — la moneta in cui si decide.")
|
|
print(f" Orizzonte 20 anni, P=90%, partenza ${START:.0f}. Ogni lente col PROPRIO muro.")
|
|
# ⚠️ ogni lente ha la SUA serie di ritorni: i path si riestraggono per lente (stesso seme,
|
|
# quindi stessi INDICI di blocco: il confronto fra lenti resta appaiato) e non si riusano.
|
|
dep90 = {}
|
|
for k in ("L0 LORDO (26/07)", "L1 +FISCO (07/08)", "L2 +FUNDING (22/08)", "L3 CONGIUNTA"):
|
|
L = Lo[k]
|
|
rr = np.random.default_rng(SEED_DEP)
|
|
pp = CC._boot_paths(L.r, N_DEP, 20 * 365, BLOCK, rr)
|
|
dep90[k], _ = dep_per_conf(pp, L, 0.90, START)
|
|
del pp
|
|
b = dep90["L0 LORDO (26/07)"]
|
|
d_fisco = dep90["L1 +FISCO (07/08)"] - b
|
|
d_fund = dep90["L2 +FUNDING (22/08)"] - b
|
|
d_joint = dep90["L3 CONGIUNTA"] - b
|
|
inter = d_joint - d_fisco - d_fund
|
|
print(f"\n {'lente':<27}{'EUR/mese per P=90% a 20 anni':>32}{'vs L0':>10}")
|
|
for k in ("L0 LORDO (26/07)", "L1 +FISCO (07/08)", "L2 +FUNDING (22/08)", "L3 CONGIUNTA"):
|
|
print(f" {k:<27}{dep90[k]:>32,.0f}{dep90[k] - b:>+10,.0f}")
|
|
print(f"\n somma ingenua : {d_fisco:+,.0f} {d_fund:+,.0f} = {d_fisco + d_fund:+,.0f} EUR/mese")
|
|
print(f" MISURATO : {d_joint:+,.0f} EUR/mese")
|
|
print(f" INTERAZIONE : {inter:+,.0f} EUR/mese ({inter / (d_fisco + d_fund):+.0%} della somma)")
|
|
# ⚠️ un'interazione piccola e' una DIFFERENZA DI DIFFERENZE: prima di dichiararne il SEGNO
|
|
# va misurato il rumore Monte Carlo che la circonda, o si pubblica un segno di rumore.
|
|
inters = [inter]
|
|
for sd in (4_242, 90_909):
|
|
dd = {}
|
|
for k in ("L0 LORDO (26/07)", "L1 +FISCO (07/08)", "L2 +FUNDING (22/08)", "L3 CONGIUNTA"):
|
|
L = Lo[k]
|
|
rr = np.random.default_rng(sd)
|
|
pp = CC._boot_paths(L.r, N_DEP, 20 * 365, BLOCK, rr)
|
|
dd[k], _ = dep_per_conf(pp, L, 0.90, START)
|
|
del pp
|
|
inters.append(dd["L3 CONGIUNTA"] - dd["L1 +FISCO (07/08)"]
|
|
- dd["L2 +FUNDING (22/08)"] + dd["L0 LORDO (26/07)"])
|
|
segno_ok = all(x > 0 for x in inters) or all(x < 0 for x in inters)
|
|
et_segno = "STABILE" if segno_ok else "NON stabile: e' rumore"
|
|
print(" su 3 semi: " + " / ".join(f"{x:+,.0f}" for x in inters)
|
|
+ f" -> segno {et_segno}"
|
|
+ f" (bisezione a {6000 / 2 ** 12:.1f} EUR/m di risoluzione)")
|
|
verso = "MENO" if np.median(inters) < 0 else "PIU'"
|
|
print(f"\n 📌 ESITO, e la mia attesa era SBAGLIATA nella moneta che conta. Il meccanismo")
|
|
print(f" esiste (3a: il fisco morde meno dove il drift e' minore) ma nel VERSAMENTO")
|
|
if segno_ok:
|
|
print(f" necessario il congiunto e' {verso} severo della somma ({np.median(inters):+,.0f} EUR/m,")
|
|
print(" segno stabile su 3 semi): la funzione «versamento -> probabilita'» e' convessa")
|
|
print(" e ribalta il segno di un'interazione gia' piccola.")
|
|
else:
|
|
print(f" necessario l'interazione NON ha segno stabile fra i semi ({inters[0]:+,.0f} / "
|
|
f"{inters[1]:+,.0f} / {inters[2]:+,.0f} EUR/m):")
|
|
print(" e' rumore Monte Carlo, e va detto invece di pubblicarne il segno.")
|
|
print(" Sul MURO l'interazione e' sotto la risoluzione MC.")
|
|
print(f" REGOLA OPERATIVA: NON contare su una compensazione fra le due correzioni —")
|
|
print(f" si sommano, e il congiunto resta {d_joint / max(d_fisco, d_fund):.2f}x la piu' grande delle due")
|
|
print(f" presa da sola. {clock()}")
|
|
|
|
sez(f"4) TRAIETTORIA da ${START:.0f} — anni al capitale-rendita, nelle quattro lenti")
|
|
print(f"""
|
|
Partenza ${START:.0f} = il conto Deribit VERO (le tabelle pubblicate partono da $600).
|
|
Ogni lente usa il PROPRIO muro. {N_TRAJ} path, blocchi da {BLOCK} giorni, orizzonte {ANNI_TRAJ} anni.
|
|
DUE mediane: 'mediana' = incondizionata (non-arrivo = +inf -> '>{ANNI_TRAJ}a'), 'cond.' = solo su chi
|
|
arriva — e la seconda non si legge senza la P accanto.""")
|
|
traj = {}
|
|
for k in Lo:
|
|
L = Lo[k]
|
|
rr = np.random.default_rng(SEED_TRAJ)
|
|
pp = CC._boot_paths(L.r, N_TRAJ, ANNI_TRAJ * 365, BLOCK, rr)
|
|
traj[k] = {dep: leggi(PN.accumula(pp, dep, L.muro, L.aliq, L.patr,
|
|
start=START)["colpito"]) for dep in VERSAMENTI}
|
|
del pp
|
|
hdr = f" {'EUR/m':>7}"
|
|
for k in Lo:
|
|
hdr += f"{k.split()[0]:>22}"
|
|
print("\n" + hdr)
|
|
print(f" {'':>7}" + "".join(f"{'mediana cond. P20':>22}" for _ in Lo))
|
|
for dep in VERSAMENTI:
|
|
row = f" {dep:>7}"
|
|
for k in Lo:
|
|
d = traj[k][dep]
|
|
row += f"{fmt_med(d):>9}{fmt_cond(d):>7}{d['p20']:>6.0%}"
|
|
print(row)
|
|
print(f" {clock()}")
|
|
d250 = {k: traj[k][250] for k in Lo}
|
|
print(f"""
|
|
📌 LA RIGA CHE MANCAVA. «EUR250/mese» P(traguardo entro 20 anni):
|
|
L0 LORDO {d250['L0 LORDO (26/07)']['p20']:>5.0%} (pubblicato 92% — qui {d250['L0 LORDO (26/07)']['p20']:.0%} su serie piu' lunga e da $635)
|
|
L1 +FISCO {d250['L1 +FISCO (07/08)']['p20']:>5.0%} (pubblicato 52%)
|
|
L2 +FUNDING {d250['L2 +FUNDING (22/08)']['p20']:>5.0%} (pubblicato 64%)
|
|
L3 CONGIUNTA {d250['L3 CONGIUNTA']['p20']:>5.0%} <- IL NUMERO ONESTO
|
|
L3b str. vero {d250['L3b CONGIUNTA (str. vero)']['p20']:>5.0%} (col funding misurato solo dove lo strumento esiste)
|
|
|
|
⚠️ Le due colonne di funding sono una BANDA, non una scelta: -2,16%/anno usa il proxy
|
|
inverse sul 40% del campione (conservativa), -1,39%/anno usa solo la finestra in cui il
|
|
perpetual lineare USDC esiste davvero. La verita' sta fra 14% e 26%, non su un numero.
|
|
P(traguardo entro i {ANNI_TRAJ} anni simulati), stessa riga: L3 {d250['L3 CONGIUNTA']['p_arr']:.0%} · L3b {d250['L3b CONGIUNTA (str. vero)']['p_arr']:.0%}
|
|
— cioe' anche allungando di 5 anni l'orizzonte una quota non trascurabile non arriva.""")
|
|
|
|
# --- il lump: i soldi gia' fermi ---
|
|
sez(f"4-bis) E SE PARTO CON UN VERSAMENTO INIZIALE — ${START:.0f} + un lump, lente CONGIUNTA")
|
|
print(f"""
|
|
Il progetto ha misurato (27/07) che EUR10.000 fermi oggi valgono 2,45x, ma quel lump non e'
|
|
MAI entrato nelle tabelle nette. Qui e' nella lente congiunta. Lo stesso muro, gli stessi path.""")
|
|
L3 = Lo["L3 CONGIUNTA"]
|
|
rr = np.random.default_rng(SEED_TRAJ)
|
|
p3 = CC._boot_paths(L3.r, N_TRAJ, ANNI_TRAJ * 365, BLOCK, rr)
|
|
print(f"\n {'lump EUR':>10}" + "".join(f"{f'EUR{d}/mese':>26}" for d in (0, 250, 500)))
|
|
print(f" {'':>10}" + "".join(f"{'mediana cond. P20':>26}" for _ in range(3)))
|
|
for lump in LUMPS_EUR:
|
|
row = f" {lump:>10,.0f}"
|
|
for dep in (0, 250, 500):
|
|
d = leggi(PN.accumula(p3, dep, L3.muro, L3.aliq, L3.patr,
|
|
start=START, lump_eur=lump)["colpito"])
|
|
row += f"{fmt_med(d):>11}{fmt_cond(d):>8}{d['p20']:>7.0%}"
|
|
print(row)
|
|
del p3
|
|
print(f" {clock()}")
|
|
print("\n ⚠️ Un lump non e' capitale disponibile se e' il fondo d'emergenza (nota 27/07),")
|
|
print(" e a EUR10.000 sul conto si riapre la decisione di venue (soglia dichiarata $20k,")
|
|
print(" ma il piano cambia -> CLAUDE.md dice di riaprire PRIMA, non dopo).")
|
|
|
|
# =====================================================================
|
|
sez("5) QUANTO VERSARE PER UN ORIZZONTE DATO — lente CONGIUNTA, e il totale versato accanto")
|
|
print(f"""
|
|
bersaglio ${L3.muro:,.0f} (lente congiunta) · partenza ${START:.0f} · nessun lump
|
|
La colonna 'tot. versato' e' il confronto che conta: quanto del bersaglio lo COMPRI coi
|
|
bonifici invece di farlo produrre alla strategia.""")
|
|
L1 = Lo["L1 +FISCO (07/08)"]
|
|
print(f"\n {'orizzonte':>10}" + "".join(f"{f'P={c:.0%}':>13}" for c in CONFIDENZE)
|
|
+ f"{'tot.versato@90%':>18}{'% bersaglio':>13}{' | solo fisco @90%':>21}")
|
|
riga10 = None
|
|
for h in ORIZZONTI:
|
|
rr = np.random.default_rng(SEED_DEP)
|
|
ph = CC._boot_paths(L3.r, N_DEP, h * 365, BLOCK, rr)
|
|
celle, tot90, dep90h = [], float("nan"), float("nan")
|
|
for c in CONFIDENZE:
|
|
v, vers = dep_per_conf(ph, L3, c, START)
|
|
celle.append(f"EUR{v:,.0f}/m" if np.isfinite(v) else "irraggiung.")
|
|
if c == 0.90:
|
|
tot90, dep90h = vers, v
|
|
del ph
|
|
rr = np.random.default_rng(SEED_DEP)
|
|
p1 = CC._boot_paths(L1.r, N_DEP, h * 365, BLOCK, rr)
|
|
v1, _ = dep_per_conf(p1, L1, 0.90, START)
|
|
del p1
|
|
quota = tot90 / L3.muro if np.isfinite(tot90) else float("nan")
|
|
if h == 10:
|
|
rr = np.random.default_rng(SEED_DEP)
|
|
pb = CC._boot_paths(Lo["L3b CONGIUNTA (str. vero)"].r, N_DEP, h * 365, BLOCK, rr)
|
|
vb, versb = dep_per_conf(pb, Lo["L3b CONGIUNTA (str. vero)"], 0.90, START)
|
|
del pb
|
|
riga10 = (dep90h, tot90, quota, vb, versb)
|
|
print(f" {f'{h} anni':>10}" + "".join(f"{x:>13}" for x in celle)
|
|
+ f"{f'${tot90:,.0f}':>18}{quota:>13.0%}"
|
|
+ f"{f' | EUR{v1:,.0f}/m ({dep90h / v1:.2f}x)':>21}")
|
|
print(f" {clock()}")
|
|
print("""
|
|
📌 A 10 anni il rendimento fa la minoranza del lavoro: il resto lo fanno i bonifici. E'
|
|
la lezione gia' scritta il 26/07, qui con le due correzioni dentro: comprimere
|
|
l'orizzonte non fa lavorare la strategia, COMPRA il capitale.""")
|
|
|
|
# =====================================================================
|
|
sez("6) LA DOMANDA INVERSA — che rendita compra quello che posso permettermi")
|
|
print(f"""
|
|
Rendita NETTA mediana in EUR/giorno = capitale x perpetua({L3.perp:.2%}). Le imposte sono gia'
|
|
dentro la perpetua: il numero e' netto e NON va lordizzato una seconda volta.
|
|
Lente CONGIUNTA, partenza ${START:.0f}, nessun lump.""")
|
|
print(f"\n {'EUR/mese':>9}" + "".join(f"{f'{y}a':>12}" for y in (5, 10, 15, 20))
|
|
+ f"{'P(>=EUR50/g a 20a)':>21}")
|
|
for dep in VERSAMENTI:
|
|
celle, p50 = [], 0.0
|
|
for y in (5, 10, 15, 20):
|
|
rr = np.random.default_rng(SEED_DEP)
|
|
py = CC._boot_paths(L3.r, N_DEP, y * 365, BLOCK, rr)
|
|
cap = PN.accumula(py, dep, L3.muro, L3.aliq, L3.patr, start=START)["cap"]
|
|
del py
|
|
rend = cap * L3.perp / 365.0 / CC.EURUSD
|
|
celle.append(f"{np.median(rend):,.2f}")
|
|
if y == 20:
|
|
p50 = float((rend >= CC.TARGET_EUR_DAY).mean())
|
|
print(f" {dep:>9}" + "".join(f"{x:>12}" for x in celle) + f"{p50:>21.1%}")
|
|
print(f" {clock()}")
|
|
|
|
# =====================================================================
|
|
sez("VERDETTO")
|
|
j250 = traj["L3 CONGIUNTA"][250]
|
|
d10, t10, q10, d10b, t10b = riga10
|
|
print(f"""
|
|
1. LA TABELLA ORA ESISTE, ed e' peggiore di entrambe le lenti separate:
|
|
EUR250/mese, P(entro 20 anni) — LORDO {d250['L0 LORDO (26/07)']['p20']:.0%} · +FISCO {d250['L1 +FISCO (07/08)']['p20']:.0%} · +FUNDING {d250['L2 +FUNDING (22/08)']['p20']:.0%} · CONGIUNTA {j250['p20']:.0%}.
|
|
Il muro passa da ${Lo['L0 LORDO (26/07)'].muro:,.0f} a ${L3.muro:,.0f} ({L3.muro / Lo['L0 LORDO (26/07)'].muro - 1:+.0%}).
|
|
|
|
2. LE DUE CORREZIONI SI SOMMANO — la mia attesa («meno drift -> meno imposta -> congiunto
|
|
meno severo della somma») e' MISURATA e vale poco, e nella moneta che decide NON la
|
|
conferma. Interazione: {np.expm1(inter_log):+.2%} sul capitale accumulato (li' il meccanismo
|
|
c'e'), ${inter_w:+,.0f} sul muro (sotto la risoluzione MC), {np.median(inters):+,.0f} EUR/mese sul
|
|
versamento necessario ({np.median(inters) / (d_fisco + d_fund):+.0%} della somma, segno {"stabile" if segno_ok else "NON stabile"}
|
|
su 3 semi). Il congiunto resta
|
|
{d_joint / max(d_fisco, d_fund):.2f}x la piu' grande delle due correzioni presa da sola.
|
|
|
|
3. COSA NON CAMBIA A NESSUNA LENTE: senza versamenti il capitale-rendita non si raggiunge
|
|
MAI (P=0% a 20 anni in tutte e cinque le colonne). L'accumulo viene dai bonifici, non dal
|
|
rendimento — e l'ordine delle leve del piano e' invariato.
|
|
|
|
4. IL RISCHIO DI VENUE RESTA FUORI, DI PROPOSITO: e' rovina, non costo. A p=5% annuo il
|
|
capitale mediano a 20 anni e' ZERO per ogni riga di ogni tabella qui sopra. Nessuna di
|
|
queste righe e' valida se il conto sparisce.
|
|
|
|
5. L'OBIETTIVO DICHIARATO, senza addolcirlo: EUR50/giorno NON e' raggiungibile con versamenti
|
|
plausibili in un orizzonte breve. A 10 anni servono EUR{d10:,.0f}/mese (P=90%), cioe' ${t10:,.0f}
|
|
versati per arrivare a ${L3.muro:,.0f}: il {q10:.0%} del bersaglio lo compri coi bonifici.
|
|
La tabella utile e' quella INVERSA (sezione 6): non «quando arrivo a 50», ma «cosa compro
|
|
con quello che ho».
|
|
""")
|
|
print(f"\nCON TUTTE LE CORREZIONI, EUR250/MESE DA ${START:.0f}: {fmt_med(j250)[:-1]} anni "
|
|
f"(mediana incondizionata; cond. all'arrivo {fmt_cond(j250)}), "
|
|
f"P(20a) {j250['p20']:.0%}")
|
|
print(f"PER EUR50/GIORNO IN 10 ANNI SERVONO: EUR{d10:,.0f}/mese (P=90%), "
|
|
f"totale versato ${t10:,.0f}")
|
|
print(f" [banda di funding: lente conservativa -2,16%/a come sopra; con il funding dello")
|
|
print(f" strumento vero -1,39%/a -> EUR250/mese fa {fmt_med(d250['L3b CONGIUNTA (str. vero)'])} "
|
|
f"con P(20a) {d250['L3b CONGIUNTA (str. vero)']['p20']:.0%}, e i 10 anni costano "
|
|
f"EUR{d10b:,.0f}/mese (${t10b:,.0f})]")
|
|
print(f"\n{clock()} fine.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|