research(capitale): il fisco durante l'ACCUMULO non era mai stato contato — vale il 31% a 10 anni

r0727_tasse.py. TAX_RATE=0.33 compare in tutto il progetto in UN SOLO punto: la lordizzazione
del bersaglio (fase di RENDITA). L'accumulo compone al lordo per dieci o vent'anni.

10.000 EUR + 500/mese, 10 anni, capitale mediano:
  nessuna imposta (modello pubblicato)  229.638$   P(entro 10a) 34.5%
  plus 26%                              173.284$                 8.5%
  plus 33% + patrimoniale 0.2%          158.401$                 3.8%   = -31%

Versamento necessario a 10 anni (lump 10k): P=50% da 602 a 880 EUR/m; P=75% da 790 a 1.051.
Cioe' +33%: il numero dato stamattina (790-870 EUR/m) era al lordo del fisco.

L'errore e' COMPOSTO, non una tantum: -16.8% a 5 anni, -31% a 10, -44% a 15, -55% a 20.

Assunzioni dichiarate (NON un parere fiscale): 33% cripto da L.199/2025 (misurato anche a 26%,
perche' e' aperto se i derivati di sede estera seguano quel regime), minusvalenze riportabili
4 anni, 0.2% annuo sul valore. Il modello tassa la variazione ANNUA di valore, quindi anche la
parte non realizzata a cavallo del 31/12: e' un LIMITE SUPERIORE rispetto alla pura
realizzazione, ma il turnover del book lo rende stretto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal Pastro
2026-07-27 19:51:42 +00:00
parent 067c89fc16
commit 0a77290636
+189
View File
@@ -0,0 +1,189 @@
#!/usr/bin/env python
"""r0727_tasse.py — il fisco durante l'ACCUMULO: la voce che nessuna traiettoria ha mai contato.
IL BUCO. `TAX_RATE = 0.33` compare in tutto il progetto in UN SOLO punto: la lordizzazione del
bersaglio (per netto €50/giorno servono €74.6/giorno lordi, da cui il muro di $272.061) e la
conversione capitale->rendita. **L'accumulo compone al LORDO**: `cap *= (1 + r)` ogni giorno, per
dieci o vent'anni, senza che il fisco tocchi mai il capitale in crescita.
Non e' un dettaglio contabile. Un book che apre e chiude posizioni ogni giorno **realizza** i
guadagni in continuazione: in Italia le plusvalenze si tassano per CASSA, anno per anno, non
alla fine. Quindi il modello sta capitalizzando soldi che ogni anno andrebbero versati, e
l'effetto composto di quell'errore cresce col tempo — proprio sull'orizzonte in cui si sta
decidendo.
COSA SI MISURA QUI
(1) quanto costa la tassazione annua sulle plusvalenze realizzate;
(2) quanto costa in piu' l'imposta patrimoniale sul valore delle cripto-attivita';
(3) di quanto si sposta il versamento mensile necessario per l'obiettivo a 10 anni.
⚠️ ASSUNZIONI FISCALI DICHIARATE — NON SONO UN PARERE FISCALE, e vanno confermate:
* aliquota **33%** sulle plusvalenze cripto (l'assunzione gia' cablata nel progetto:
"L.199/2025, esenzione abolita dal 2026"). Si misura anche a **26%**, perche' e' aperta la
questione se i DERIVATI su cripto di una sede estera seguano il regime cripto o quello dei
redditi diversi di natura finanziaria: la differenza vale piu' di molte decisioni prese qui.
* **minusvalenze compensabili nei 4 periodi d'imposta successivi** (modellato: buffer a 4 anni).
* imposta **0.2% annua sul valore** delle cripto-attivita' detenute (analoga all'IVAFE).
* ⚠️ **il modello tassa la variazione ANNUA di valore** (fine anno inizio anno versamenti),
cioe' anche la parte NON realizzata di una posizione aperta a cavallo del 31 dicembre. E'
quindi un **LIMITE SUPERIORE** rispetto a una tassazione per pura realizzazione. Quanto sia
superiore dipende dal turnover: TP01 ribilancia ogni giorno e SKH01 chiude round-trip
discreti, quindi la quasi totalita' del risultato annuo E' realizzata entro l'anno — il vero
numero sta fra la riga senza imposte e questa, molto piu' vicino a questa.
uv run python scripts/research/r0727_tasse.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_venue_risk as VR # noqa: E402
import r0727_lumpsum_split as LS # noqa: E402
ALIQUOTE = (0.0, 0.26, 0.33)
PATRIMONIALE = 0.002 # 0.2% annuo sul valore (analoga IVAFE)
CARRY_ANNI = 4 # minusvalenze riportabili
N_PATHS = 4000
BLOCK = 20
SEED = 727
def accumula(r: np.ndarray, lump_eur: float, dep_eur: float, years: int,
aliquota: float, patrimoniale: float = 0.0,
n_paths: int = N_PATHS, seed: int = SEED) -> dict:
"""Accumulo con imposta ANNUA sulle plusvalenze realizzate + patrimoniale sul valore.
La plusvalenza dell'anno e' `capitale_fine - capitale_inizio - versamenti_dell_anno`
(i versamenti non sono reddito). Le minusvalenze vanno in un buffer che scade dopo
`CARRY_ANNI` anni, come le minusvalenze da redditi diversi.
"""
rng = np.random.default_rng(seed)
n_days = years * 365
paths = CC._boot_paths(r, n_paths, n_days, BLOCK, rng)
dep = dep_eur * CC.EURUSD
cap = np.full(n_paths, LS.START + lump_eur * CC.EURUSD)
hit = np.zeros(n_paths, bool) # traguardo toccato in QUALUNQUE momento
carry = np.zeros((n_paths, CARRY_ANNI)) # minusvalenze per anno di formazione
imposte = np.zeros(n_paths)
anno_start = cap.copy()
versato_anno = np.zeros(n_paths)
for t in range(n_days):
cap *= (1.0 + paths[:, t])
hit |= cap >= LS.TARGET
if t % 30 == 0 and t > 0 and dep > 0:
cap += dep
versato_anno += dep
if (t + 1) % 365 == 0:
plus = cap - anno_start - versato_anno
if aliquota > 0:
disponibili = carry.sum(axis=1)
usate = np.minimum(np.maximum(plus, 0.0), disponibili)
imponibile = np.maximum(plus, 0.0) - usate
# consuma il carry dal piu' vecchio
resid = usate.copy()
for k in range(CARRY_ANNI - 1, -1, -1): # dalla PIU' VECCHIA, che scade prima
presa = np.minimum(carry[:, k], resid)
carry[:, k] -= presa
resid -= presa
dovuta = imponibile * aliquota
cap -= np.minimum(dovuta, cap)
imposte += dovuta
carry = np.roll(carry, 1, axis=1) # invecchia: l'ultimo scade
carry[:, 0] = np.maximum(-plus, 0.0)
if patrimoniale > 0:
p = cap * patrimoniale
cap -= np.minimum(p, cap)
imposte += p
anno_start = cap.copy()
versato_anno[:] = 0.0
# stessa definizione delle altre tabelle: traguardo toccato in qualunque momento
return dict(median_end=float(np.median(cap)), p_reach=float(hit.mean()),
imposte=float(np.median(imposte)),
versato=LS.START + lump_eur * CC.EURUSD + dep * (n_days // 30))
def dep_necessario(r: np.ndarray, lump_eur: float, years: int, aliquota: float,
patrimoniale: float, p_target: float = 0.50) -> float:
lo, hi = 0.0, 15_000.0
for _ in range(14):
mid = 0.5 * (lo + hi)
if accumula(r, lump_eur, mid, years, aliquota, patrimoniale)["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)
print("=" * 100)
print(" r0727 — IL FISCO DURANTE L'ACCUMULO (mai contato in nessuna traiettoria)")
print("=" * 100)
print(f" bersaglio ${LS.TARGET:,.0f} (gia' lordizzato al {CC.TAX_RATE:.0%} per il PRELIEVO) · "
f"book de-luckato ×{LS.DELUCK}")
print(f" ⚠️ il {CC.TAX_RATE:.0%} nel bersaglio riguarda la fase di RENDITA; qui si misura "
"cio' che manca: l'imposta")
print(" annua durante i 10 anni di crescita. Le due NON si sovrappongono.")
# ------------------------------------------------------ (1) il costo, a 10 anni
print("\n" + "=" * 100)
print(" (1) QUANTO COSTA — €10.000 + €500/mese, orizzonte 10 anni")
print("=" * 100)
print(f"\n {'regime':<44}{'cap. mediano':>16}{'P(entro 10a)':>15}{'imposte pagate':>18}")
base = None
for al in ALIQUOTE:
for patr in ((0.0, PATRIMONIALE) if al > 0 else (0.0,)):
x = accumula(r, 10_000.0, 500.0, 10, al, patr)
base = base if base is not None else x["median_end"]
et = "nessuna imposta (il modello pubblicato)" if al == 0 else (
f"plus {al:.0%}" + (f" + patrimoniale {patr:.1%}" if patr else ""))
print(f" {et:<44}{x['median_end']:>15,.0f}${x['p_reach']:>15.1%}"
f"{x['imposte']:>17,.0f}$")
x33 = accumula(r, 10_000.0, 500.0, 10, 0.33, PATRIMONIALE)
print(f"\n Costo del regime piu' severo vs il modello pubblicato: "
f"{x33['median_end'] / base - 1:+.1%} di capitale a 10 anni")
# ------------------------------------------------------ (2) il versamento necessario
print("\n" + "=" * 100)
print(" (2) DI QUANTO SI SPOSTA IL VERSAMENTO NECESSARIO (P=50%, 10 anni)")
print("=" * 100)
for pt in (0.50, 0.75):
print(f"\n a P={pt:.0%}")
print(f" {'lump':>9}{'senza imposte':>16}{'plus 26%':>13}{'plus 33%':>13}"
f"{'33% + patrim.':>16}")
for lump in (5_000.0, 10_000.0):
cells = [dep_necessario(r, lump, 10, 0.0, 0.0, pt),
dep_necessario(r, lump, 10, 0.26, PATRIMONIALE, pt),
dep_necessario(r, lump, 10, 0.33, 0.0, pt),
dep_necessario(r, lump, 10, 0.33, PATRIMONIALE, pt)]
print(f"{lump:>8,.0f}" + "".join(f"{c:>12,.0f}" for c in cells))
# ------------------------------------------------------ (3) l'orizzonte
print("\n" + "=" * 100)
print(" (3) LO STESSO PIANO, PIU' AVANTI NEL TEMPO (€10.000 + €500/mese)")
print("=" * 100)
print(f"\n {'anni':>6}{'senza imposte':>17}{'con 33% + patrim.':>21}{'differenza':>14}")
for yrs in (5, 10, 15, 20):
a = accumula(r, 10_000.0, 500.0, yrs, 0.0, 0.0)["median_end"]
b = accumula(r, 10_000.0, 500.0, yrs, 0.33, PATRIMONIALE)["median_end"]
print(f" {yrs:>5}a{a:>16,.0f}${b:>20,.0f}${b / a - 1:>13.1%}")
print("\n ⚠️ Il divario si allarga col tempo: e' un errore COMPOSTO, non una tassa una tantum.")
print(" Ed e' il motivo per cui va corretto adesso e non 'quando ci arriviamo'.")
if __name__ == "__main__":
main()