diff --git a/docs/research/RESULTS-0822.md b/docs/research/RESULTS-0822.md index 4d1b099..00ebac5 100644 --- a/docs/research/RESULTS-0822.md +++ b/docs/research/RESULTS-0822.md @@ -701,6 +701,7 @@ stessi hanno nominato senza poterli eseguire**. Stesso contratto di consegna, st | 33 | WORST-DAY | il peggior giorno del libro, MISURATO: il gradino di leva e' autorizzabile? | **LEAD** — l'aritmetica passa, **il knob non esiste** | | 34 | CC01-2022 | il carry riaperto sui dati che gli mancavano per costruzione | **RESTA SCARTATO** — per la ragione giusta, ma non quella pubblicata | | 35 | FUNDING | il costo mai prezzato di TENERE una posizione aperta | **LEAD** — **−2,16%/anno di drift**, muri **+17,5%** | +| 36 | PIANO-VERO | la tabella congiunta fisco+funding, che non esisteva | 🚨 **€250/m: P(20a) 92% → 14-26%** | | 22 | MAKER | l'esecuzione passiva e' una fonte di ritorno, al netto del costo di non essere eseguiti? | **SCARTATO** — il segno dipende da `<` contro `<=` | | 23 | BOCPD | un rilevatore di cambio di regime vero batte il miglior lookback COSTANTE? | **SCARTATO** — filone chiuso definitivamente | | 24 | CRITICO | cosa NON e' stato misurato, e quale singola misura mancante vale di piu' | **3 correzioni all'ondata**, 1 regola mia ritirata | @@ -1712,3 +1713,70 @@ molto piu' basso, il −2,16% scenderebbe verso il **−1,39%** della finestra p **VERDETTO: LEAD** (misura di politica, non strategia). **`IL FUNDING COSTA 2,16%/ANNO NETTO AL LIBRO` — quanto la fortuna d'ancora ×0,89 che il progetto ha misurato con enorme cura, e sposta i muri di capitale del +17,5%.** + +--- + +## 36 — PIANO-VERO (la tabella congiunta) — **la risposta alla domanda del progetto** + +`scripts/research/r0822d_piano_vero.py` (145 s). **Nessuna selezione** → niente DSR. Muri a 4.000 +path (la taglia dei pubblicati), traiettorie a 3.000; **risoluzione MC misurata su 3 semi: 0,7% sul +muro, 1,5 €/m sul versamento** — dichiarata. + +**Il buco:** le due correzioni misurate al piano — **fisco d'accumulo** (07/08) e **funding** (22/08) — +erano state applicate **separatamente allo stesso numero lordo**, e la tabella con entrambe non +esisteva. L'agente ha dichiarato **prima di misurare** di aspettarsi un congiunto **meno severo della +somma** (meno drift → meno plusvalenza → meno imposta). + +✅ **Replica 6/6 prima di pubblicare, due esatte AL DOLLARO:** sul vintage **2026-08-07** lo stesso +codice rida' **$272.061** (lordo) e **$258.338** (netto fisco); riproduce anche funding +−2,1597/−1,3931/−0,5508 %/anno, perpetua 10,73→9,14%, muro +17,5%, traiettoria 16,4a/90% → 18,7a/64%. +⚠️ **Sulla serie di OGGI la stessa riga da' $278.033 (+2,2%)** — 15 giorni di dati in piu', `data/raw/` +gitignored: **stessa lezione GTAA del 07/08** → ogni confronto fra lenti e' fatto **sullo stesso +vintage**. + +| lente (de-luck ×0,89) | drift | perpetua | muro | **€250/m da $635: P(20a)** | +|---|---|---|---|---| +| L0 LORDO (26/07) | 17,11% | 10,68% | $278k | **90%** *(pubbl. 92%)* | +| L1 +FISCO (07/08) | 17,11% | 7,52% | $264k | **49%** *(pubbl. 52%)* | +| L2 +FUNDING (22/08) | 15,19% | 9,06% | $328k | **63%** *(pubbl. 64%)* | +| 🚨 **L3 CONGIUNTA** | **15,19%** | **6,35%** | **$313k** | **14%** ← il numero che mancava | +| L3b congiunta, funding "strumento vero" −1,39%/a | 15,87% | 6,72% | $296k | **26%** | + +🚨 **LE DUE CORREZIONI NON SI COMPENSANO: SI SOMMANO — e nel versamento necessario il congiunto e' ++10% PEGGIORE della loro somma.** +📌 **L'attesa dell'agente era sbagliata dove conta, e il perche' e' il risultato trasferibile:** il +meccanismo "meno drift → meno imposta" **esiste** (+9,97% di capitale recuperato; il fisco toglie il +50,9% a funding OFF e il 46,0% a funding ON), **ma la funzione versamento→probabilita' e' CONVESSA e +ne ribalta il segno**. Interazione misurata **in tre monete**: **+9,97% sul capitale**, **−$815 sul +muro** (0,26%, *sotto* la risoluzione MC), **+26 €/mese sul versamento (+10%, segno stabile su 3 +semi**: +26/+29/+22). +**REGOLA: non contare su una compensazione fra correzioni misurate separatamente — la si misura, e in +piu' di una MONETA, perche' un'interazione piccola cambia segno con la moneta.** + +**Quanto versare (L3, da $635, nessun lump):** +| orizzonte | P=50% | P=75% | **P=90%** | totale versato @P=90% | **quota del bersaglio** | +|---|---|---|---|---|---| +| **10 anni** | €1.323 | €1.534 | **€1.733** | **$229.189** | **73%** | +| 15 anni | €656 | €784 | **€920** | $183.129 | 58% | +| 20 anni | €360 | €445 | **€541** | $143.804 | 46% | +Il **solo funding** aggiunge **1,27× / 1,33× / 1,40×** sopra la richiesta della lente fisco-only. + +**Rendita €/g mediana a 20 anni (L3):** €250/m → **31,85 €/g, P(≥50/g) 8,8%** · €500/m → 63,05, +**P 77,1%** · €800/m → 100,50, **P 99,2%**. +📌 **Il LUMP, che nelle tabelle nette non era mai entrato:** **€10.000 oggi** + €250/m porta P(20a) da +**14% a 45%**; €5.000 la porta a 30%. + +**Trappole evitate per costruzione** (tutte gia' pagate da questo progetto): non-arrivo = **+∞, mai +−1**; **mediana condizionata mai stampata senza la sua P accanto**; risoluzione MC misurata e +dichiarata. **Rischio di venue NON incluso di proposito**: e' **rovina, non costo** (a p=5% il mediano +e' zero comunque) — dichiarato. + +**VERDETTO: LEAD** (misura di politica). +``` +CON TUTTE LE CORREZIONI, €250/MESE DA $635: 23,4 anni (mediana incondizionata; + 22,1 condizionata all'arrivo), P(20a) 14% [banda funding "strumento vero": 22,2 anni, P 26%] +PER €50/GIORNO IN 10 ANNI SERVONO: €1.733/mese (P=90%), totale versato $229.189 +``` +📌 **A 10 anni si versano $229k per arrivare a ~$313k: il rendimento fa il 27%, i bonifici il 73%.** +E' la forma piu' netta finora della lezione gia' scritta: **a orizzonte corto non fai lavorare la +strategia, COMPRI il capitale coi bonifici.** diff --git a/scripts/research/r0822d_piano_vero.py b/scripts/research/r0822d_piano_vero.py new file mode 100644 index 0000000..70a38dd --- /dev/null +++ b/scripts/research/r0822d_piano_vero.py @@ -0,0 +1,695 @@ +#!/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()