decaf-tax 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- decaf_tax-0.1.0/LICENSE +21 -0
- decaf_tax-0.1.0/PKG-INFO +309 -0
- decaf_tax-0.1.0/README.md +269 -0
- decaf_tax-0.1.0/pyproject.toml +93 -0
- decaf_tax-0.1.0/setup.cfg +4 -0
- decaf_tax-0.1.0/src/decaf/__init__.py +3 -0
- decaf_tax-0.1.0/src/decaf/__main__.py +5 -0
- decaf_tax-0.1.0/src/decaf/cli.py +715 -0
- decaf_tax-0.1.0/src/decaf/ecb_cache.py +197 -0
- decaf_tax-0.1.0/src/decaf/forex.py +247 -0
- decaf_tax-0.1.0/src/decaf/forex_gains.py +281 -0
- decaf_tax-0.1.0/src/decaf/fx.py +156 -0
- decaf_tax-0.1.0/src/decaf/holidays.py +80 -0
- decaf_tax-0.1.0/src/decaf/models.py +242 -0
- decaf_tax-0.1.0/src/decaf/output_cli.py +323 -0
- decaf_tax-0.1.0/src/decaf/output_pdf.py +311 -0
- decaf_tax-0.1.0/src/decaf/output_xls.py +248 -0
- decaf_tax-0.1.0/src/decaf/output_yaml.py +34 -0
- decaf_tax-0.1.0/src/decaf/parse.py +294 -0
- decaf_tax-0.1.0/src/decaf/prices.py +108 -0
- decaf_tax-0.1.0/src/decaf/quadro_rl.py +73 -0
- decaf_tax-0.1.0/src/decaf/quadro_rt.py +91 -0
- decaf_tax-0.1.0/src/decaf/quadro_rw.py +412 -0
- decaf_tax-0.1.0/src/decaf/schwab_auth.py +256 -0
- decaf_tax-0.1.0/src/decaf/schwab_client.py +106 -0
- decaf_tax-0.1.0/src/decaf/schwab_gains_pdf.py +143 -0
- decaf_tax-0.1.0/src/decaf/schwab_parse.py +507 -0
- decaf_tax-0.1.0/src/decaf/schwab_vest_pdf.py +142 -0
- decaf_tax-0.1.0/src/decaf/statement_store.py +505 -0
- decaf_tax-0.1.0/src/decaf_tax.egg-info/PKG-INFO +309 -0
- decaf_tax-0.1.0/src/decaf_tax.egg-info/SOURCES.txt +43 -0
- decaf_tax-0.1.0/src/decaf_tax.egg-info/dependency_links.txt +1 -0
- decaf_tax-0.1.0/src/decaf_tax.egg-info/entry_points.txt +2 -0
- decaf_tax-0.1.0/src/decaf_tax.egg-info/requires.txt +19 -0
- decaf_tax-0.1.0/src/decaf_tax.egg-info/top_level.txt +1 -0
- decaf_tax-0.1.0/tests/test_architecture.py +410 -0
- decaf_tax-0.1.0/tests/test_e2e.py +133 -0
- decaf_tax-0.1.0/tests/test_forex.py +215 -0
- decaf_tax-0.1.0/tests/test_forex_gains.py +421 -0
- decaf_tax-0.1.0/tests/test_fx.py +98 -0
- decaf_tax-0.1.0/tests/test_holidays.py +98 -0
- decaf_tax-0.1.0/tests/test_parse.py +297 -0
- decaf_tax-0.1.0/tests/test_prices.py +66 -0
- decaf_tax-0.1.0/tests/test_schwab_parse.py +72 -0
- decaf_tax-0.1.0/tests/test_statement_store.py +345 -0
decaf_tax-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marcello Barnaba
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
decaf_tax-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: decaf-tax
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: De-CAF: Italian tax report generator for foreign investments. Modello Redditi PF — no commercialista needed.
|
|
5
|
+
Author-email: Marcello Barnaba <vjt@openssl.it>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/vjt/decaf
|
|
8
|
+
Project-URL: Repository, https://github.com/vjt/decaf
|
|
9
|
+
Project-URL: Issues, https://github.com/vjt/decaf/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/vjt/decaf/blob/master/CHANGELOG.md
|
|
11
|
+
Keywords: italian-tax,ivafe,quadro-rw,quadro-rt,quadro-rl,modello-redditi,ibkr,schwab,ecb,forex,fifo
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
17
|
+
Classifier: Natural Language :: Italian
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: aiohttp>=3.9
|
|
22
|
+
Requires-Dist: aiosqlite>=0.20
|
|
23
|
+
Requires-Dist: python-dotenv>=1.0
|
|
24
|
+
Requires-Dist: openpyxl>=3.1
|
|
25
|
+
Requires-Dist: fpdf2>=2.8
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
Requires-Dist: yfinance>=0.2
|
|
28
|
+
Requires-Dist: pydantic>=2.0
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Requires-Dist: ibkr-flex-client>=0.1
|
|
31
|
+
Requires-Dist: ecb-fx-rates>=0.1
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-timeout>=2.3; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.8; extra == "dev"
|
|
37
|
+
Requires-Dist: pyright>=1.1; extra == "dev"
|
|
38
|
+
Requires-Dist: reportlab>=4.0; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<img src="doc/img/logo.png" alt="decaf logo" width="180">
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
# decaf
|
|
46
|
+
|
|
47
|
+
**De-CAF** — Generatore di report fiscale per investimenti esteri. Niente commercialista.
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<img src="doc/img/cover.png" alt="Mascetti, Mosconi e Magnotta alle prese con la dichiarazione dei redditi">
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
Scarica i dati dai tuoi broker esteri e i tassi BCE, poi calcola tutto il necessario per il **Modello Redditi PF**:
|
|
54
|
+
|
|
55
|
+
- **Quadro RW** — Monitoraggio attività finanziarie estere + IVAFE
|
|
56
|
+
- **Quadro RT** — Plusvalenze di natura finanziaria (26%)
|
|
57
|
+
- **Quadro RL** — Redditi di capitale (interessi, dividendi, ritenute estere)
|
|
58
|
+
- **Soglia valutaria** — Analisi art. 67(1)(c-ter) TUIR
|
|
59
|
+
|
|
60
|
+
Output: tabelle colorate nel terminale, Excel (un foglio per quadro), PDF e YAML.
|
|
61
|
+
|
|
62
|
+
📖 **Manuale completo**: [doc/decaf_manual.pdf](doc/decaf_manual.pdf) — guida fiscale, normativa con riferimenti alla Gazzetta Ufficiale, architettura, internals per broker, setup Flex Query. Rigenerato ad ogni cambio in `doc/` via pre-commit hook.
|
|
63
|
+
|
|
64
|
+
> ⚠️ **Disclaimer.** Questo strumento automatizza i calcoli ma **non sostituisce un commercialista**. Le leggi fiscali cambiano, i tuoi dati e la tua situazione sono tuoi — verifica sempre i numeri prima di firmare il Modello Redditi. Gli autori non si assumono responsabilità per errori, omissioni, o interpretazioni della normativa. Usalo come punto di partenza, non come oracolo.
|
|
65
|
+
|
|
66
|
+
## Broker Supportati
|
|
67
|
+
|
|
68
|
+
| Broker | Sorgente dati | Note |
|
|
69
|
+
|--------|--------------|------|
|
|
70
|
+
| **Interactive Brokers** (Irlanda) | Flex Query API o file XML | Automatico |
|
|
71
|
+
| **Charles Schwab** (account EAC/RSU) | 3 file: PDF Year-End Summary + PDF Withholding + JSON transazioni | Manuale da schwab.com |
|
|
72
|
+
|
|
73
|
+
## Prerequisiti
|
|
74
|
+
|
|
75
|
+
**Linux (Debian/Ubuntu)**:
|
|
76
|
+
```bash
|
|
77
|
+
sudo apt install python3 python3-venv poppler-utils git
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**macOS**:
|
|
81
|
+
```bash
|
|
82
|
+
brew install python poppler git
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`poppler-utils` (`pdftotext`) serve al parsing dei PDF Schwab. Windows non testato.
|
|
86
|
+
|
|
87
|
+
## Installazione
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/vjt/decaf.git
|
|
91
|
+
cd decaf
|
|
92
|
+
mkdir private # qui metterai i tuoi file broker (gitignored)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Non serve creare il `venv` a mano: lo script `./decaf.sh` lo crea alla prima invocazione e aggiorna le dipendenze automaticamente quando cambia `pyproject.toml` (utile dopo un `git pull`). Le due librerie vendor (`ibkr-flex-client`, `ecb-fx-rates`) sono pubblicate su PyPI, quindi non serve `--recursive` per l'uso normale — vedi la sezione [Sviluppo](#sviluppo) se vuoi modificarle localmente.
|
|
96
|
+
|
|
97
|
+
## Primo utilizzo
|
|
98
|
+
|
|
99
|
+
### 1. Metti i file broker in `private/`
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
private/
|
|
103
|
+
├── flexquery.xml # IBKR — esportato da Flex Query
|
|
104
|
+
├── Individual_XXX_Transactions_*.json # Schwab — Accounts → History → Export (JSON)
|
|
105
|
+
├── Year-End Summary*.PDF # Schwab — Statements → Tax Documents
|
|
106
|
+
└── Annual Withholding Statement*.PDF # Schwab — Equity Award Center → Documents
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Prima volta con IBKR?** Devi configurare una Flex Query dal portale Interactive Brokers — serve sia per il download via API sia per esportare l'XML. Guida completa con screenshot: **[doc/QUERY_SETUP.md](doc/QUERY_SETUP.md)**. Una volta configurata, puoi saltare il file e usare l'API mettendo `IBKR_TOKEN` + `IBKR_QUERY_ID` in `.env` alla radice del repo (gitignored).
|
|
110
|
+
|
|
111
|
+
Per Schwab i tre file contengono dati diversi e servono tutti:
|
|
112
|
+
|
|
113
|
+
| File | Cosa contiene |
|
|
114
|
+
|------|---------------|
|
|
115
|
+
| `Individual_*.json` | Dividendi, ritenute (RL), vendite, bonifici (forex FIFO) |
|
|
116
|
+
| `Year-End Summary*.PDF` | Plusvalenze per lotto (RT) |
|
|
117
|
+
| `Annual Withholding*.PDF` | FMV al vest per IVAFE (RW) |
|
|
118
|
+
|
|
119
|
+
### 2. Carica i dati nel DB locale
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# IBKR — da file
|
|
123
|
+
./decaf.sh fetch --file private/flexquery.xml
|
|
124
|
+
|
|
125
|
+
# IBKR — da API (richiede .env)
|
|
126
|
+
./decaf.sh fetch
|
|
127
|
+
|
|
128
|
+
# Schwab
|
|
129
|
+
./decaf.sh fetch --broker schwab \
|
|
130
|
+
--file private/Individual_*_Transactions_*.json \
|
|
131
|
+
--gains-pdfs "private/Year-End Summary*.PDF" \
|
|
132
|
+
--vest-pdfs "private/Annual Withholding Statement*.PDF"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
I caricamenti sono idempotenti — puoi rieseguirli senza duplicare. Il DB sta in `~/.cache/decaf/`.
|
|
136
|
+
|
|
137
|
+
### 3. Genera il report
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
./decaf.sh report --year 2025 --output-dir private/
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Produce `decaf_2025.yaml` + `.xlsx` + `.pdf` in `private/` (pure `private/` è gitignored), e stampa tabelle colorate nel terminale con totali per quadro, etichette AdE, e riferimenti normativi.
|
|
144
|
+
|
|
145
|
+
## Esempi
|
|
146
|
+
|
|
147
|
+
[`examples/`](examples/) contiene gli output reali generati su tre fixture sintetiche:
|
|
148
|
+
|
|
149
|
+
| Fixture | Anni | Copre |
|
|
150
|
+
|---------|------|-------|
|
|
151
|
+
| [`magnotta/`](examples/magnotta/) | 2024 | IBKR-only, caso base |
|
|
152
|
+
| [`mosconi/`](examples/mosconi/) | 2023-2024 | IBKR + Schwab, RSU, stesso ticker a 2 broker |
|
|
153
|
+
| [`mascetti/`](examples/mascetti/) | 2024-2025 | Stress — soglia forex, FIFO multi-lotto, 4 ritenute diverse |
|
|
154
|
+
|
|
155
|
+
Ogni sotto-directory contiene `decaf_<year>.{yaml,xlsx,pdf}`. Input corrispondenti in [`tests/reference/`](tests/reference/).
|
|
156
|
+
|
|
157
|
+
## File di Output
|
|
158
|
+
|
|
159
|
+
| File | Formato | Uso | Esempio |
|
|
160
|
+
|------|---------|-----|---------|
|
|
161
|
+
| `decaf_<year>.xlsx` | Excel | Un foglio per quadro + riepilogo | [mascetti/decaf_2025.xlsx](examples/mascetti/decaf_2025.xlsx) |
|
|
162
|
+
| `decaf_<year>.pdf` | PDF | Prospetto con tabelle e totali | [mascetti/decaf_2025.pdf](examples/mascetti/decaf_2025.pdf) |
|
|
163
|
+
| `decaf_<year>.yaml` | YAML | Dump completo del `TaxReport` — diffabile, stabile tra run | [mascetti/decaf_2025.yaml](examples/mascetti/decaf_2025.yaml) |
|
|
164
|
+
|
|
165
|
+
## Come Funziona
|
|
166
|
+
|
|
167
|
+
1. **Fetch** — Scarica dati dal broker (API o file) e tassi BCE. Salva tutto in SQLite.
|
|
168
|
+
2. **Report** — Carica da SQLite, converte USD→EUR al cambio BCE, calcola:
|
|
169
|
+
- **Soglia valutaria**: ricostruisce il saldo giornaliero in valuta estera, verifica 7+ giorni lavorativi consecutivi sopra €51.645,69
|
|
170
|
+
- **IVAFE**: 0.2% annuo sul valore di mercato dei titoli (pro-rata per giorni), €34.20 fisso per depositi
|
|
171
|
+
- **Plusvalenze titoli**: converte il P/L del broker in EUR al tasso BCE alla data di regolamento
|
|
172
|
+
- **Plusvalenze valutarie**: se soglia superata, calcola i guadagni forex con FIFO sui lotti USD (acquisti da vendite titoli, dividendi, interessi → cessioni tramite conversioni EUR.USD e bonifici)
|
|
173
|
+
- **Redditi di capitale**: abbina interessi lordi con ritenute estere
|
|
174
|
+
3. **Output** — Genera i file e il report terminale
|
|
175
|
+
|
|
176
|
+
## Regole Fiscali Implementate
|
|
177
|
+
|
|
178
|
+
| Regola | Riferimento | Implementazione |
|
|
179
|
+
|--------|------------|-----------------|
|
|
180
|
+
| IVAFE titoli | D.L. 201/2011, art. 19 | 0.2% su valore di mercato, pro-rata giorni |
|
|
181
|
+
| IVAFE depositi | D.L. 201/2011 | €34.20 fisso annuo |
|
|
182
|
+
| Plusvalenze titoli | Art. 67(1)(c-bis) TUIR | 26% imposta sostitutiva |
|
|
183
|
+
| Plusvalenze valutarie | Art. 67(1)(c-ter) TUIR | FIFO su lotti USD, 26% se soglia superata |
|
|
184
|
+
| Soglia valutaria | Art. 67(1)(c-ter) TUIR | €51.645,69 per 7+ giorni lavorativi |
|
|
185
|
+
| Cambio | D.P.R. 917/1986 | Tassi BCE (cambio ufficiale AdE) |
|
|
186
|
+
| Quadro RW | Modello Redditi PF, Sez. II-A | Cod. 20 titoli, Cod. 1 depositi |
|
|
187
|
+
| Quadro RT | Modello Redditi PF, righi RT21+ | Sez. II-A, imposta sostitutiva 26% |
|
|
188
|
+
| Quadro RL | Modello Redditi PF, rigo RL2 | Sez. I, redditi di capitale esteri |
|
|
189
|
+
|
|
190
|
+
## Bring Your Own Data — Backtesting
|
|
191
|
+
|
|
192
|
+
Il comando `decaf backtest <dir>` riesegue l'intera pipeline su una directory di file broker e confronta l'output con oracoli YAML committati. Utile per:
|
|
193
|
+
|
|
194
|
+
- verificare che un cambio di codice non alteri output storici;
|
|
195
|
+
- congelare i risultati dell'anno N come regressione per l'anno N+1;
|
|
196
|
+
- condividere casi di test senza toccare dati sensibili.
|
|
197
|
+
|
|
198
|
+
Guida approfondita: [doc/BACKTEST.md](doc/BACKTEST.md).
|
|
199
|
+
|
|
200
|
+
### Layout della directory
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
tests/reference/mascetti/
|
|
204
|
+
├── ibkr_flex_2024.xml # IBKR XML per anno
|
|
205
|
+
├── ibkr_flex_2025.xml
|
|
206
|
+
├── Individual_XXX066_Transactions_*.json # Schwab JSON per anno
|
|
207
|
+
├── Year-End Summary*.PDF # Schwab YES PDF per anno
|
|
208
|
+
├── Annual Withholding*.PDF # Schwab AWH PDF per anno
|
|
209
|
+
├── prices.yaml # opzionale — override prezzi
|
|
210
|
+
├── decaf_2024.yaml # oracolo per anno
|
|
211
|
+
└── decaf_2025.yaml
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
L'anno fiscale di ogni file si ricava dal nome: `ibkr_flex_<year>.xml` per l'XML, le date nei nomi Schwab per JSON/PDF. Gli oracoli sono obbligatori solo per gli anni che vuoi verificare.
|
|
215
|
+
|
|
216
|
+
### Comandi
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Rigenera oracoli (uso iniziale o dopo modifiche volute)
|
|
220
|
+
./decaf.sh backtest tests/reference/mascetti --update
|
|
221
|
+
|
|
222
|
+
# Verifica regressione (exit 0 = match, 1 = diff)
|
|
223
|
+
./decaf.sh backtest tests/reference/mascetti
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Il comando:
|
|
227
|
+
1. crea un DB SQLite temporaneo in `/tmp/decaf_bt_<pid>.db`;
|
|
228
|
+
2. ingestisce tutti i file broker trovati nella directory;
|
|
229
|
+
3. calcola il report per ogni anno con oracolo;
|
|
230
|
+
4. confronta il dump YAML completo contro l'oracolo (`--update` lo sovrascrive invece).
|
|
231
|
+
|
|
232
|
+
Exit code: `0` = tutti gli anni matchano, `1` = almeno un anno diverge.
|
|
233
|
+
|
|
234
|
+
### Override di prezzo (`prices.yaml`)
|
|
235
|
+
|
|
236
|
+
Pinna i prezzi di fine anno per simboli che yfinance non risolve (ticker sintetici, delistati, esteri) o che vuoi controllare esplicitamente:
|
|
237
|
+
|
|
238
|
+
```yaml
|
|
239
|
+
2024:
|
|
240
|
+
MSCT: 14.00
|
|
241
|
+
SPKZ: 18.00
|
|
242
|
+
2025:
|
|
243
|
+
ANTN: 6.00
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Il dizionario è consultato **due volte** per ogni anno fiscale:
|
|
247
|
+
- blocco `<year>` → prezzo a fine anno (IVAFE al 31/12);
|
|
248
|
+
- blocco `<year-1>` → prezzo a fine anno precedente (usato come `initial_value` nel calcolo pro-rata IVAFE per titoli portati dall'anno precedente).
|
|
249
|
+
|
|
250
|
+
Senza override, entrambi i lookup passano a yfinance.
|
|
251
|
+
|
|
252
|
+
### Fixture sintetiche incluse
|
|
253
|
+
|
|
254
|
+
| Fixture | Anni | Copertura |
|
|
255
|
+
|---------|------|-----------|
|
|
256
|
+
| `magnotta/` | 2024 | IBKR singolo, caso base — IVAFE pro-rata, loss RT, dividendo con ritenuta |
|
|
257
|
+
| `mosconi/` | 2023-2024 | IBKR + Schwab, FIFO su vendita parziale, RSU vest, multi-anno |
|
|
258
|
+
| `mascetti/` | 2024-2025 | Stress test — soglia forex superata 2 anni, FIFO multi-lotto, RSU multi-anno, dividendi con 4 ritenute diverse (US 30%, UK 0%, DE 26.375%, IT 26%) |
|
|
259
|
+
|
|
260
|
+
Nomi dei personaggi:
|
|
261
|
+
- `mascetti/` — Il Conte Raffaello Mascetti, [personaggio immaginario del film *Amici Miei*](https://it.wikipedia.org/wiki/Amici_miei)
|
|
262
|
+
- `mosconi/` — [Germano Mosconi](https://it.wikipedia.org/wiki/Germano_Mosconi), leggendario giornalista veronese
|
|
263
|
+
- `magnotta/` — [Mario Magnotta](https://it.wikipedia.org/wiki/Mario_Magnotta), icona internet ante-litteram di L'Aquila
|
|
264
|
+
|
|
265
|
+
Account IDs contengono `666` per distinguerli visivamente da account reali.
|
|
266
|
+
|
|
267
|
+
## Sviluppo
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
source .venv/bin/activate
|
|
271
|
+
scripts/lint.sh # ruff + pyright
|
|
272
|
+
scripts/test.sh # pytest -x
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
143 test: holidays, XML parsing, FX service, forex threshold, forex FIFO gains, statement store, Schwab PDF parsing, end-to-end regression su tre fixture sintetiche.
|
|
276
|
+
|
|
277
|
+
Richiede Python 3.12+. Le dipendenze sono gestite da `./decaf.sh` (primo avvio crea `.venv/` + installa, run successivi aggiornano solo se `pyproject.toml` è cambiato).
|
|
278
|
+
|
|
279
|
+
Per rigenerare il manuale PDF (`scripts/manual.sh`, lanciato anche dal pre-commit hook quando cambia `doc/`) serve pandoc + xelatex:
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
# Linux (Debian/Ubuntu)
|
|
283
|
+
sudo apt install pandoc texlive-xetex texlive-latex-recommended texlive-latex-extra
|
|
284
|
+
|
|
285
|
+
# macOS
|
|
286
|
+
brew install pandoc
|
|
287
|
+
brew install --cask mactex-no-gui
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Se vuoi modificare le due librerie vendor (`ibkr-flex-client`, `ecb-fx-rates`), clona con i submodule:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
git submodule update --init --recursive
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`./decaf.sh` rileva automaticamente `vendor/<dep>/pyproject.toml` e installa quelle versioni in modalità editable, sovrascrivendo le pin PyPI. Fai le tue modifiche in `vendor/<dep>/`, i test di decaf le useranno subito.
|
|
297
|
+
|
|
298
|
+
I submodule sono via HTTPS. Se hai accesso push e preferisci SSH, scopi la riscrittura alle sole due repo dei submodule:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
git config --global url."git@github.com:vjt/ibkr-flex-client.git".insteadOf "https://github.com/vjt/ibkr-flex-client.git"
|
|
302
|
+
git config --global url."git@github.com:vjt/ecb-fx-rates.git".insteadOf "https://github.com/vjt/ecb-fx-rates.git"
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Nessun altro repo (nemmeno altri di `vjt/`) viene toccato.
|
|
306
|
+
|
|
307
|
+
## Licenza
|
|
308
|
+
|
|
309
|
+
MIT
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="doc/img/logo.png" alt="decaf logo" width="180">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# decaf
|
|
6
|
+
|
|
7
|
+
**De-CAF** — Generatore di report fiscale per investimenti esteri. Niente commercialista.
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="doc/img/cover.png" alt="Mascetti, Mosconi e Magnotta alle prese con la dichiarazione dei redditi">
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
Scarica i dati dai tuoi broker esteri e i tassi BCE, poi calcola tutto il necessario per il **Modello Redditi PF**:
|
|
14
|
+
|
|
15
|
+
- **Quadro RW** — Monitoraggio attività finanziarie estere + IVAFE
|
|
16
|
+
- **Quadro RT** — Plusvalenze di natura finanziaria (26%)
|
|
17
|
+
- **Quadro RL** — Redditi di capitale (interessi, dividendi, ritenute estere)
|
|
18
|
+
- **Soglia valutaria** — Analisi art. 67(1)(c-ter) TUIR
|
|
19
|
+
|
|
20
|
+
Output: tabelle colorate nel terminale, Excel (un foglio per quadro), PDF e YAML.
|
|
21
|
+
|
|
22
|
+
📖 **Manuale completo**: [doc/decaf_manual.pdf](doc/decaf_manual.pdf) — guida fiscale, normativa con riferimenti alla Gazzetta Ufficiale, architettura, internals per broker, setup Flex Query. Rigenerato ad ogni cambio in `doc/` via pre-commit hook.
|
|
23
|
+
|
|
24
|
+
> ⚠️ **Disclaimer.** Questo strumento automatizza i calcoli ma **non sostituisce un commercialista**. Le leggi fiscali cambiano, i tuoi dati e la tua situazione sono tuoi — verifica sempre i numeri prima di firmare il Modello Redditi. Gli autori non si assumono responsabilità per errori, omissioni, o interpretazioni della normativa. Usalo come punto di partenza, non come oracolo.
|
|
25
|
+
|
|
26
|
+
## Broker Supportati
|
|
27
|
+
|
|
28
|
+
| Broker | Sorgente dati | Note |
|
|
29
|
+
|--------|--------------|------|
|
|
30
|
+
| **Interactive Brokers** (Irlanda) | Flex Query API o file XML | Automatico |
|
|
31
|
+
| **Charles Schwab** (account EAC/RSU) | 3 file: PDF Year-End Summary + PDF Withholding + JSON transazioni | Manuale da schwab.com |
|
|
32
|
+
|
|
33
|
+
## Prerequisiti
|
|
34
|
+
|
|
35
|
+
**Linux (Debian/Ubuntu)**:
|
|
36
|
+
```bash
|
|
37
|
+
sudo apt install python3 python3-venv poppler-utils git
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**macOS**:
|
|
41
|
+
```bash
|
|
42
|
+
brew install python poppler git
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`poppler-utils` (`pdftotext`) serve al parsing dei PDF Schwab. Windows non testato.
|
|
46
|
+
|
|
47
|
+
## Installazione
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/vjt/decaf.git
|
|
51
|
+
cd decaf
|
|
52
|
+
mkdir private # qui metterai i tuoi file broker (gitignored)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Non serve creare il `venv` a mano: lo script `./decaf.sh` lo crea alla prima invocazione e aggiorna le dipendenze automaticamente quando cambia `pyproject.toml` (utile dopo un `git pull`). Le due librerie vendor (`ibkr-flex-client`, `ecb-fx-rates`) sono pubblicate su PyPI, quindi non serve `--recursive` per l'uso normale — vedi la sezione [Sviluppo](#sviluppo) se vuoi modificarle localmente.
|
|
56
|
+
|
|
57
|
+
## Primo utilizzo
|
|
58
|
+
|
|
59
|
+
### 1. Metti i file broker in `private/`
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
private/
|
|
63
|
+
├── flexquery.xml # IBKR — esportato da Flex Query
|
|
64
|
+
├── Individual_XXX_Transactions_*.json # Schwab — Accounts → History → Export (JSON)
|
|
65
|
+
├── Year-End Summary*.PDF # Schwab — Statements → Tax Documents
|
|
66
|
+
└── Annual Withholding Statement*.PDF # Schwab — Equity Award Center → Documents
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Prima volta con IBKR?** Devi configurare una Flex Query dal portale Interactive Brokers — serve sia per il download via API sia per esportare l'XML. Guida completa con screenshot: **[doc/QUERY_SETUP.md](doc/QUERY_SETUP.md)**. Una volta configurata, puoi saltare il file e usare l'API mettendo `IBKR_TOKEN` + `IBKR_QUERY_ID` in `.env` alla radice del repo (gitignored).
|
|
70
|
+
|
|
71
|
+
Per Schwab i tre file contengono dati diversi e servono tutti:
|
|
72
|
+
|
|
73
|
+
| File | Cosa contiene |
|
|
74
|
+
|------|---------------|
|
|
75
|
+
| `Individual_*.json` | Dividendi, ritenute (RL), vendite, bonifici (forex FIFO) |
|
|
76
|
+
| `Year-End Summary*.PDF` | Plusvalenze per lotto (RT) |
|
|
77
|
+
| `Annual Withholding*.PDF` | FMV al vest per IVAFE (RW) |
|
|
78
|
+
|
|
79
|
+
### 2. Carica i dati nel DB locale
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# IBKR — da file
|
|
83
|
+
./decaf.sh fetch --file private/flexquery.xml
|
|
84
|
+
|
|
85
|
+
# IBKR — da API (richiede .env)
|
|
86
|
+
./decaf.sh fetch
|
|
87
|
+
|
|
88
|
+
# Schwab
|
|
89
|
+
./decaf.sh fetch --broker schwab \
|
|
90
|
+
--file private/Individual_*_Transactions_*.json \
|
|
91
|
+
--gains-pdfs "private/Year-End Summary*.PDF" \
|
|
92
|
+
--vest-pdfs "private/Annual Withholding Statement*.PDF"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
I caricamenti sono idempotenti — puoi rieseguirli senza duplicare. Il DB sta in `~/.cache/decaf/`.
|
|
96
|
+
|
|
97
|
+
### 3. Genera il report
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
./decaf.sh report --year 2025 --output-dir private/
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Produce `decaf_2025.yaml` + `.xlsx` + `.pdf` in `private/` (pure `private/` è gitignored), e stampa tabelle colorate nel terminale con totali per quadro, etichette AdE, e riferimenti normativi.
|
|
104
|
+
|
|
105
|
+
## Esempi
|
|
106
|
+
|
|
107
|
+
[`examples/`](examples/) contiene gli output reali generati su tre fixture sintetiche:
|
|
108
|
+
|
|
109
|
+
| Fixture | Anni | Copre |
|
|
110
|
+
|---------|------|-------|
|
|
111
|
+
| [`magnotta/`](examples/magnotta/) | 2024 | IBKR-only, caso base |
|
|
112
|
+
| [`mosconi/`](examples/mosconi/) | 2023-2024 | IBKR + Schwab, RSU, stesso ticker a 2 broker |
|
|
113
|
+
| [`mascetti/`](examples/mascetti/) | 2024-2025 | Stress — soglia forex, FIFO multi-lotto, 4 ritenute diverse |
|
|
114
|
+
|
|
115
|
+
Ogni sotto-directory contiene `decaf_<year>.{yaml,xlsx,pdf}`. Input corrispondenti in [`tests/reference/`](tests/reference/).
|
|
116
|
+
|
|
117
|
+
## File di Output
|
|
118
|
+
|
|
119
|
+
| File | Formato | Uso | Esempio |
|
|
120
|
+
|------|---------|-----|---------|
|
|
121
|
+
| `decaf_<year>.xlsx` | Excel | Un foglio per quadro + riepilogo | [mascetti/decaf_2025.xlsx](examples/mascetti/decaf_2025.xlsx) |
|
|
122
|
+
| `decaf_<year>.pdf` | PDF | Prospetto con tabelle e totali | [mascetti/decaf_2025.pdf](examples/mascetti/decaf_2025.pdf) |
|
|
123
|
+
| `decaf_<year>.yaml` | YAML | Dump completo del `TaxReport` — diffabile, stabile tra run | [mascetti/decaf_2025.yaml](examples/mascetti/decaf_2025.yaml) |
|
|
124
|
+
|
|
125
|
+
## Come Funziona
|
|
126
|
+
|
|
127
|
+
1. **Fetch** — Scarica dati dal broker (API o file) e tassi BCE. Salva tutto in SQLite.
|
|
128
|
+
2. **Report** — Carica da SQLite, converte USD→EUR al cambio BCE, calcola:
|
|
129
|
+
- **Soglia valutaria**: ricostruisce il saldo giornaliero in valuta estera, verifica 7+ giorni lavorativi consecutivi sopra €51.645,69
|
|
130
|
+
- **IVAFE**: 0.2% annuo sul valore di mercato dei titoli (pro-rata per giorni), €34.20 fisso per depositi
|
|
131
|
+
- **Plusvalenze titoli**: converte il P/L del broker in EUR al tasso BCE alla data di regolamento
|
|
132
|
+
- **Plusvalenze valutarie**: se soglia superata, calcola i guadagni forex con FIFO sui lotti USD (acquisti da vendite titoli, dividendi, interessi → cessioni tramite conversioni EUR.USD e bonifici)
|
|
133
|
+
- **Redditi di capitale**: abbina interessi lordi con ritenute estere
|
|
134
|
+
3. **Output** — Genera i file e il report terminale
|
|
135
|
+
|
|
136
|
+
## Regole Fiscali Implementate
|
|
137
|
+
|
|
138
|
+
| Regola | Riferimento | Implementazione |
|
|
139
|
+
|--------|------------|-----------------|
|
|
140
|
+
| IVAFE titoli | D.L. 201/2011, art. 19 | 0.2% su valore di mercato, pro-rata giorni |
|
|
141
|
+
| IVAFE depositi | D.L. 201/2011 | €34.20 fisso annuo |
|
|
142
|
+
| Plusvalenze titoli | Art. 67(1)(c-bis) TUIR | 26% imposta sostitutiva |
|
|
143
|
+
| Plusvalenze valutarie | Art. 67(1)(c-ter) TUIR | FIFO su lotti USD, 26% se soglia superata |
|
|
144
|
+
| Soglia valutaria | Art. 67(1)(c-ter) TUIR | €51.645,69 per 7+ giorni lavorativi |
|
|
145
|
+
| Cambio | D.P.R. 917/1986 | Tassi BCE (cambio ufficiale AdE) |
|
|
146
|
+
| Quadro RW | Modello Redditi PF, Sez. II-A | Cod. 20 titoli, Cod. 1 depositi |
|
|
147
|
+
| Quadro RT | Modello Redditi PF, righi RT21+ | Sez. II-A, imposta sostitutiva 26% |
|
|
148
|
+
| Quadro RL | Modello Redditi PF, rigo RL2 | Sez. I, redditi di capitale esteri |
|
|
149
|
+
|
|
150
|
+
## Bring Your Own Data — Backtesting
|
|
151
|
+
|
|
152
|
+
Il comando `decaf backtest <dir>` riesegue l'intera pipeline su una directory di file broker e confronta l'output con oracoli YAML committati. Utile per:
|
|
153
|
+
|
|
154
|
+
- verificare che un cambio di codice non alteri output storici;
|
|
155
|
+
- congelare i risultati dell'anno N come regressione per l'anno N+1;
|
|
156
|
+
- condividere casi di test senza toccare dati sensibili.
|
|
157
|
+
|
|
158
|
+
Guida approfondita: [doc/BACKTEST.md](doc/BACKTEST.md).
|
|
159
|
+
|
|
160
|
+
### Layout della directory
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
tests/reference/mascetti/
|
|
164
|
+
├── ibkr_flex_2024.xml # IBKR XML per anno
|
|
165
|
+
├── ibkr_flex_2025.xml
|
|
166
|
+
├── Individual_XXX066_Transactions_*.json # Schwab JSON per anno
|
|
167
|
+
├── Year-End Summary*.PDF # Schwab YES PDF per anno
|
|
168
|
+
├── Annual Withholding*.PDF # Schwab AWH PDF per anno
|
|
169
|
+
├── prices.yaml # opzionale — override prezzi
|
|
170
|
+
├── decaf_2024.yaml # oracolo per anno
|
|
171
|
+
└── decaf_2025.yaml
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
L'anno fiscale di ogni file si ricava dal nome: `ibkr_flex_<year>.xml` per l'XML, le date nei nomi Schwab per JSON/PDF. Gli oracoli sono obbligatori solo per gli anni che vuoi verificare.
|
|
175
|
+
|
|
176
|
+
### Comandi
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Rigenera oracoli (uso iniziale o dopo modifiche volute)
|
|
180
|
+
./decaf.sh backtest tests/reference/mascetti --update
|
|
181
|
+
|
|
182
|
+
# Verifica regressione (exit 0 = match, 1 = diff)
|
|
183
|
+
./decaf.sh backtest tests/reference/mascetti
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Il comando:
|
|
187
|
+
1. crea un DB SQLite temporaneo in `/tmp/decaf_bt_<pid>.db`;
|
|
188
|
+
2. ingestisce tutti i file broker trovati nella directory;
|
|
189
|
+
3. calcola il report per ogni anno con oracolo;
|
|
190
|
+
4. confronta il dump YAML completo contro l'oracolo (`--update` lo sovrascrive invece).
|
|
191
|
+
|
|
192
|
+
Exit code: `0` = tutti gli anni matchano, `1` = almeno un anno diverge.
|
|
193
|
+
|
|
194
|
+
### Override di prezzo (`prices.yaml`)
|
|
195
|
+
|
|
196
|
+
Pinna i prezzi di fine anno per simboli che yfinance non risolve (ticker sintetici, delistati, esteri) o che vuoi controllare esplicitamente:
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
2024:
|
|
200
|
+
MSCT: 14.00
|
|
201
|
+
SPKZ: 18.00
|
|
202
|
+
2025:
|
|
203
|
+
ANTN: 6.00
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Il dizionario è consultato **due volte** per ogni anno fiscale:
|
|
207
|
+
- blocco `<year>` → prezzo a fine anno (IVAFE al 31/12);
|
|
208
|
+
- blocco `<year-1>` → prezzo a fine anno precedente (usato come `initial_value` nel calcolo pro-rata IVAFE per titoli portati dall'anno precedente).
|
|
209
|
+
|
|
210
|
+
Senza override, entrambi i lookup passano a yfinance.
|
|
211
|
+
|
|
212
|
+
### Fixture sintetiche incluse
|
|
213
|
+
|
|
214
|
+
| Fixture | Anni | Copertura |
|
|
215
|
+
|---------|------|-----------|
|
|
216
|
+
| `magnotta/` | 2024 | IBKR singolo, caso base — IVAFE pro-rata, loss RT, dividendo con ritenuta |
|
|
217
|
+
| `mosconi/` | 2023-2024 | IBKR + Schwab, FIFO su vendita parziale, RSU vest, multi-anno |
|
|
218
|
+
| `mascetti/` | 2024-2025 | Stress test — soglia forex superata 2 anni, FIFO multi-lotto, RSU multi-anno, dividendi con 4 ritenute diverse (US 30%, UK 0%, DE 26.375%, IT 26%) |
|
|
219
|
+
|
|
220
|
+
Nomi dei personaggi:
|
|
221
|
+
- `mascetti/` — Il Conte Raffaello Mascetti, [personaggio immaginario del film *Amici Miei*](https://it.wikipedia.org/wiki/Amici_miei)
|
|
222
|
+
- `mosconi/` — [Germano Mosconi](https://it.wikipedia.org/wiki/Germano_Mosconi), leggendario giornalista veronese
|
|
223
|
+
- `magnotta/` — [Mario Magnotta](https://it.wikipedia.org/wiki/Mario_Magnotta), icona internet ante-litteram di L'Aquila
|
|
224
|
+
|
|
225
|
+
Account IDs contengono `666` per distinguerli visivamente da account reali.
|
|
226
|
+
|
|
227
|
+
## Sviluppo
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
source .venv/bin/activate
|
|
231
|
+
scripts/lint.sh # ruff + pyright
|
|
232
|
+
scripts/test.sh # pytest -x
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
143 test: holidays, XML parsing, FX service, forex threshold, forex FIFO gains, statement store, Schwab PDF parsing, end-to-end regression su tre fixture sintetiche.
|
|
236
|
+
|
|
237
|
+
Richiede Python 3.12+. Le dipendenze sono gestite da `./decaf.sh` (primo avvio crea `.venv/` + installa, run successivi aggiornano solo se `pyproject.toml` è cambiato).
|
|
238
|
+
|
|
239
|
+
Per rigenerare il manuale PDF (`scripts/manual.sh`, lanciato anche dal pre-commit hook quando cambia `doc/`) serve pandoc + xelatex:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Linux (Debian/Ubuntu)
|
|
243
|
+
sudo apt install pandoc texlive-xetex texlive-latex-recommended texlive-latex-extra
|
|
244
|
+
|
|
245
|
+
# macOS
|
|
246
|
+
brew install pandoc
|
|
247
|
+
brew install --cask mactex-no-gui
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Se vuoi modificare le due librerie vendor (`ibkr-flex-client`, `ecb-fx-rates`), clona con i submodule:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
git submodule update --init --recursive
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`./decaf.sh` rileva automaticamente `vendor/<dep>/pyproject.toml` e installa quelle versioni in modalità editable, sovrascrivendo le pin PyPI. Fai le tue modifiche in `vendor/<dep>/`, i test di decaf le useranno subito.
|
|
257
|
+
|
|
258
|
+
I submodule sono via HTTPS. Se hai accesso push e preferisci SSH, scopi la riscrittura alle sole due repo dei submodule:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
git config --global url."git@github.com:vjt/ibkr-flex-client.git".insteadOf "https://github.com/vjt/ibkr-flex-client.git"
|
|
262
|
+
git config --global url."git@github.com:vjt/ecb-fx-rates.git".insteadOf "https://github.com/vjt/ecb-fx-rates.git"
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Nessun altro repo (nemmeno altri di `vjt/`) viene toccato.
|
|
266
|
+
|
|
267
|
+
## Licenza
|
|
268
|
+
|
|
269
|
+
MIT
|