diff --git a/scripts/research/r0726_tabella_anni.py b/scripts/research/r0726_tabella_anni.py new file mode 100644 index 0000000..9b842df --- /dev/null +++ b/scripts/research/r0726_tabella_anni.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +"""r0726_tabella_anni.py — la tabella anno per anno del piano dichiarato (€5.000 + €500/mese). + +Non aggiunge misure: mette in forma leggibile quelle di `r0726_piano_5k500.py`, anno per anno e +con gli anni di CALENDARIO, piu' tre colonne che nelle tabelle precedenti mancavano e che servono +a leggerla onestamente: + + * QUANTO HAI VERSATO fino a quel punto — senza, il capitale sembra un rendimento; + * la BANDA p10-p90, non solo la mediana — meta' dei percorsi sta fuori dalla colonna centrale; + * la SOGLIA attraversata, quando ce n'e' una (GTAA01, decisione venue, traguardo). + +Ipotesi dichiarate (le stesse di tutto il filone): fattore d'ancora ×0.89 MISURATO, book live +TP01+SKH01 sul path reale, fisco 33%, EURUSD 1.09, NESSUN rischio di venue e NESSUN decadimento +dell'edge — i due scenari peggiori sono misurati altrove (`r0726_venue_risk`, `r0726_decadimento`) +e vanno letti insieme a questa. + + uv run python scripts/research/r0726_tabella_anni.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_deposits as DP # noqa: E402 + +OGGI = pd.Timestamp("2026-07-26") +CONTO = 596.92 +LUMP_EUR, MENSILE_EUR = 5_000.0, 500.0 +N_PATHS, BLOCK, YEARS, SEED = 6000, 20, 20, 20260726 + +SOGLIE = [(13_000, "GTAA01 entra nel book"), + (20_000, "si riapre la decisione venue"), + (117_000, "XS01 eseguibile")] + + +def main() -> None: + r = DP.deluck_returns() + gross = CC.TARGET_EUR_DAY * 365 * CC.EURUSD / (1 - CC.TAX_RATE) + _, perp, wall = WR.perp_and_wall(r, 1.0, gross) + start = CONTO + LUMP_EUR * CC.EURUSD + dep = MENSILE_EUR * CC.EURUSD + + rng = np.random.default_rng(SEED) + n_days = 365 * YEARS + paths = CC._boot_paths(r, N_PATHS, n_days, BLOCK, rng) + + cap = np.full(N_PATHS, start) + paid = start + snaps, paid_at = {}, {} + for t in range(n_days): + cap = cap * (1.0 + paths[:, t]) + if t % 30 == 0 and t > 0: + cap += dep + paid += dep + if (t + 1) % 365 == 0: + y = (t + 1) // 365 + snaps[y], paid_at[y] = cap.copy(), paid + + print("=" * 104) + print(" PIANO €5.000 SUBITO + €500/MESE — anno per anno") + print("=" * 104) + print(f" partenza ${start:,.0f} · bersaglio €{CC.TARGET_EUR_DAY:.0f}/g = ${wall:,.0f} " + f"· rendita perpetua {perp:.2%} · fattore d'ancora ×{DP.DELUCK}") + print(f" {N_PATHS:,} percorsi bootstrap · SENZA rischio di venue e SENZA decadimento " + f"dell'edge (misurati a parte)") + + print(f"\n {'anno':>5}{'':>7}{'versato':>11}{'p10':>12}{'MEDIANO':>13}{'p90':>13}" + f"{'rendita med.':>15}{'P(traguardo)':>14} soglia") + print(" " + "-" * 100) + viste = set() + for y in range(1, YEARS + 1): + s = snaps[y] + med = float(np.median(s)) + rent = med * perp * (1 - CC.TAX_RATE) / 365.0 / CC.EURUSD + p_wall = float((s >= wall).mean()) + nota = "" + for lv, txt in SOGLIE: + if lv not in viste and med >= lv: + viste.add(lv) + nota = f"<- {txt}" + if wall not in viste and med >= wall: + viste.add(wall) + nota = "<- ⭐ TRAGUARDO (capitale mediano)" + anno_cal = (OGGI + pd.DateOffset(years=y)).year + print(f" {y:>5}{anno_cal:>7}{paid_at[y]:>10,.0f}${np.percentile(s,10):>11,.0f}" + f"${med:>12,.0f}${np.percentile(s,90):>12,.0f}" + f"{rent:>12.2f} €/g{p_wall:>13.1%} {nota}") + + # da dove viene il capitale + print("\n" + "=" * 104) + print(" DA DOVE VIENE IL CAPITALE (mediano)") + print("=" * 104) + print(f"\n {'anno':>5}{'versato':>12}{'rendimento':>14}{'quota da rendimento':>22}") + for y in (1, 3, 5, 10, 15, 20): + med = float(np.median(snaps[y])) + ret = med - paid_at[y] + print(f" {y:>5}{paid_at[y]:>11,.0f}${ret:>13,.0f}${ret/med:>21.0%}") + print("\n All'inizio il capitale E' quello che hai versato; il rendimento diventa la parte") + print(" principale solo dopo ~8-10 anni. E' la ragione per cui i primi anni si giudicano") + print(" sui VERSAMENTI e non sui risultati.") + + print("\n ⚠️ La colonna MEDIANA non e' una previsione: meta' dei percorsi sta sotto. La banda") + print(" p10-p90 e' larga per costruzione (vol ~8%/anno composta per 20 anni), e i due") + print(" scenari che la tabella NON contiene — fallimento del venue e morte dell'edge —") + print(" sono misurati in r0726_venue_risk.py e r0726_decadimento.py.") + + +if __name__ == "__main__": + main()