Files
PythagorasGoal/scripts/research/r0726_tabella_anni.py
T
Adriano Dal Pastro 66b4b2b9c0 research: tabella anni parametrizzabile da riga di comando
"E con 1000 al mese?" non deve richiedere di modificare lo script: importo mensile e
versamento iniziale passano come argomenti. Modificarlo ogni volta significherebbe avere
due versioni dei numeri in giro senza sapere quale ha prodotto cosa.

  uv run python scripts/research/r0726_tabella_anni.py            # EUR 500/mese
  uv run python scripts/research/r0726_tabella_anni.py 1000       # altro mensile
  uv run python scripts/research/r0726_tabella_anni.py 1000 10000 # mensile + iniziale

EUR 1.000/mese: traguardo al capitale mediano all'anno 9 (2035) invece del 12; P(traguardo)
80% a 10 anni contro 16%. Il totale versato a 20 anni raddoppia ($270.917 vs $138.482).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 21:38:09 +00:00

124 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 # €500/mese (il piano)
uv run python scripts/research/r0726_tabella_anni.py 1000 # altro importo mensile
uv run python scripts/research/r0726_tabella_anni.py 1000 10000 # mensile + versamento iniziale
"""
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
# Parametrizzabili da riga di comando: chiedere "e con X al mese?" non deve richiedere di
# modificare lo script (e quindi di avere due versioni dei numeri in giro).
MENSILE_EUR = float(sys.argv[1]) if len(sys.argv) > 1 else 500.0
LUMP_EUR = float(sys.argv[2]) if len(sys.argv) > 2 else 5_000.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(f" PIANO €{LUMP_EUR:,.0f} SUBITO + €{MENSILE_EUR:,.0f}/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()