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:
@@ -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()
|
||||
Reference in New Issue
Block a user