Files
PythagorasGoal/scripts/research/r0727_tasse.py
T
Adriano Dal Pastro 0a77290636 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>
2026-07-27 19:51:42 +00:00

190 lines
9.1 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
"""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()