Files
PythagorasGoal/scripts/research/r0727_tasse.py
T
Adriano Dal Pastro b1c3ff1bb8 docs(fisco): quadro fiscale verificato sulle fonti — la citazione normativa era sbagliata
Verificato sulle fonti (Fisco Oggi dell'Agenzia, Eutekne, Fiscomania, Circolare AdE 30/E
del 27/10/2023, guide professionali) cio' che il progetto assumeva senza averlo mai
controllato. NON e' un parere fiscale.

CONFERMATO, e nessun numero del piano cambia: 33% sulle plusvalenze cripto realizzate dal
1/1/2026 (art. 67 c.1 lett. c-sexies TUIR); franchigia EUR 2.000 abolita dal 2025;
minusvalenze riportabili 4 periodi ma solo contro plusvalenze cripto (art. 68 c. 9-bis);
patrimoniale 2 per mille sul valore al 31/12; regime dichiarativo per gli exchange esteri.

CORRETTO: il progetto citava «L.199/2025» come origine del 33% in 5 punti (r0725_capcurve,
r0725_ib10k x2, r0727_tasse, r0807_asset_compare, diario 24/07). E' falso. Il 33% dal 2026
e l'abolizione della franchigia vengono dalla L. 207/2024 art. 1 c. 23-29. La L. 199/2025
art. 1 c. 28 ritaglia il 26% per i soli token e-money denominati in EURO: BTC/ETH e le
stablecoin in dollari restano al 33%.

RESTA APERTA la domanda che vale $22k di muro: la Circolare 30/E non tratta i derivati, e
le fonti professionali collocano i derivati su cripto fuori dalle cripto-attivita'
(c-quater, RT Sez. II, 26%) — ma parlando di CFD di broker UE regolati in euro, non di
contratti inverse marginati e regolati IN CRIPTO su sede extra-UE. Registrata la domanda
da porre al commercialista nei termini esatti.

Trovata per strada una conseguenza modellistica: se i derivati sono c-quater, sono un
comparto di compensazione separato → il buffer di carry UNICO di r0807_piano_netto e
r0727_tasse e' ottimistico sulla coda (non sull'aliquota).

REGOLA: una fonte normativa citata in un commento di codice si verifica come un numero —
questa era sbagliata da settimane in 5 file e nessun test poteva accorgersene.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 19:51:50 +00:00

201 lines
10 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 realizzate dal **1/1/2026**: fissata dalla
**L. 207/2024 art. 1 c. 23-29** (che ha anche abolito la franchigia di EUR 2.000 gia' dal
2025), su `art. 67 c. 1 lett. c-sexies` TUIR. La **L. 199/2025 art. 1 c. 28** (Bilancio 2026)
NON l'ha toccata: ha solo ritagliato il 26% per i token di moneta elettronica denominati in
**euro** — BTC/ETH e le stablecoin in dollari restano al 33%. *(Verificato 2026-08-07; il
progetto citava erroneamente la L.199/2025 come fonte del 33%.)*
Si misura anche a **26%** perche' resta aperta — e NON risolta da alcun documento
dell'Agenzia — la qualificazione dei **DERIVATI su cripto**: le fonti professionali li
collocano fuori dalle cripto-attivita' (`lett. c-quater`, quadro RT Sezione II, 26%) ma
trattano CFD di broker UE regolati in euro, non contratti *inverse* marginati e regolati IN
CRIPTO su sede extra-UE. La differenza vale piu' di molte decisioni prese qui.
* **minusvalenze compensabili nei 4 periodi d'imposta successivi** (`art. 68 c. 9-bis` TUIR;
modellato: buffer a 4 anni). ⚠️ Le minusvalenze cripto compensano SOLO plusvalenze cripto:
se i derivati Deribit fossero `c-quater`, sarebbero un comparto SEPARATO e il buffer unico
di questo modello sarebbe ottimistico sulla coda (non sull'aliquota).
* imposta **0.2% annua sul valore** delle cripto-attivita' detenute al 31/12 (dovuta sopra
EUR 12/anno, quadro RW/W).
* ⚠️ **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()