#!/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()