Obiettivo: aprire github.com/vjt/decaf al pubblico senza esporre dati privati, con tre fixture pubbliche che coprono tutti i code path attualmente testati, e un flusso BYOD per chi vuole backtestare sui propri dati in locale.
tests/reference/
├── magnotta/ # EASY, 1 broker, 1 anno
│ ├── ibkr_flex.xml
│ ├── expected_2024.yaml
│ └── output/
│ ├── decaf_U66666660_2024.xlsx
│ ├── decaf_U66666660_2024.pdf
│ └── decaf_U66666660_2024.json
├── mosconi/ # MEDIO, 2 broker, 2 anni
│ ├── ibkr_flex.xml
│ ├── schwab_transactions.json
│ ├── schwab_year_end.pdf
│ ├── schwab_withholding.pdf
│ ├── expected_2023.yaml
│ ├── expected_2024.yaml
│ └── output/
│ ├── decaf_U66666606_MSC666_2023.{xlsx,pdf,json}
│ └── decaf_U66666606_MSC666_2024.{xlsx,pdf,json}
├── mascetti/ # STRESS TEST, 2 broker, 3 anni
│ ├── ibkr_flex.xml
│ ├── schwab_transactions.json
│ ├── schwab_year_end.pdf
│ ├── schwab_withholding.pdf
│ ├── expected_2023.yaml
│ ├── expected_2024.yaml
│ ├── expected_2025.yaml
│ └── output/
│ └── decaf_U66666066_CMT666_*.{xlsx,pdf,json}
└── private/ # .gitignore, solo locale
└── README.md
Tutte le fixture committano gli output (xlsx, pdf, json) accanto a expected_*.yaml. Questo serve da regressione visiva: se il formatter PDF/Excel cambia, il diff si vede immediatamente nei file binari/testuali.
Holder: Mario Magnotta
Codice fiscale: MGNMRA42J14A345Z (fake, L'Aquila)
Broker: IBKR solo
Account IBKR: U66666660
Anno fiscale: 2024
Scenario: investitore italiano semplice, 3 titoli, una perdita memorabile.
| Ticker | Nome | Settore | Note |
|---|---|---|---|
| SGRG | San Giorgio Industries SpA | Food & Beverage | Acquisto 500 @ 2.00, vendita 500 @ 1.504 → loss -247.90 EUR (= 480.000 ITL al cambio fisso 1936.27 del 2002) |
| CIMP | Cinque Imperia Holdings SpA | Diversified Financials | 100 azioni aperte a fine anno, IVAFE pro-rata |
| BGMP | Bongempi Alimentari SpA | Consumer Staples | 200 azioni, 1 dividendo lordo 45 EUR + ritenuta estera 15% = 6.75 EUR |
Cash balance IBKR = 1.247 USD tenuto a fine anno 2024 → IVAFE depositi 34.20 EUR fissi.
# tests/reference/magnotta/expected_2024.yaml
year: 2024
rw:
ivafe_titoli_eur: ~ # pro-rata su CIMP + BGMP
ivafe_depositi_eur: 34.20
valore_riepilogo_eur: ~
rt:
plusvalenze_titoli_eur: -247.90 # 480k lire di una volta
imposta_sostitutiva_eur: 0.00 # perdita, solo da riportare
soglia_forex_superata: false
rl:
dividendi_lordi_eur: 45.00
ritenute_estere_eur: 6.75
interessi_lordi_eur: 0.00- IBKR Flex XML parsing
- IVAFE titoli pro-rata (posizione aperta parziale)
- IVAFE depositi fisso
- RT loss (riportabile, no imposta)
- RL dividendi con ritenuta singolo paese
Holder: Germano Mosconi
Codice fiscale: MSCGMN41A02G489X (fake, San Bonifacio VR)
Broker: IBKR + Schwab
Account IBKR: U66666606
Account Schwab: MSC666
Anni fiscali: 2023–2024 (2)
Scenario: giornalista con stipendio parziale in RSU estere, portafoglio medio.
| Ticker | Nome | Settore | Note |
|---|---|---|---|
| MKEO | Mkeo Broadcasting Inc | Media & Entertainment | IBKR, acquisto 2023, dividendo semestrale |
| SBTP | Sbatter Porte Industries Ltd | Building Products | IBKR, held multi-anno, RW pro-rata |
| SBRS | Sbarra Spatial Services plc | Aerospace & Defense | IBKR, 2 lotti 2023 + 2024, FIFO su una vendita parziale |
| BSTM | Bestemmi Asset Management SA | Diversified Financials | IBKR, cash-like position (bond fund) |
| MOSC | Mosconi Holdings Inc | (employer RSU) | Schwab, 50 RSU vest 2024 + withholding 22% |
Cash balance Schwab = 820 USD a fine 2024 → IVAFE depositi 34.20 EUR fissi.
year: 2024
rw:
ivafe_titoli_eur: ~
ivafe_depositi_eur: 34.20
rt:
plusvalenze_titoli_eur: ~ # mix: gain SBRS FIFO + RSU vest FMV
imposta_sostitutiva_eur: ~
soglia_forex_superata: borderline # sforata ~3 giorni, sotto soglia 7gg
plusvalenze_forex_eur: 0.00
rl:
dividendi_lordi_eur: ~ # MKEO semestrale
ritenute_estere_eur: ~- Schwab JSON transactions parsing
- Schwab PDF Year-End parsing
- Schwab PDF Withholding parsing
- RSU vest con FMV lookup
- FIFO su lotti USD (una vendita parziale)
- Soglia forex borderline (non superata)
- Multi-anno
Holder: Conte Raffaello Mascetti
Codice fiscale: MSCRFL25A01F205X (fake, Firenze — come il film)
Broker: IBKR + Schwab
Account IBKR: U66666066
Account Schwab: CMT666
Anni fiscali: 2023–2024–2025 (3)
Scenario: high-net-worth confuso, 12+ posizioni, investimenti a tema supercazzola dappertutto.
| Ticker | Nome | Settore | Note |
|---|---|---|---|
| TPPC | Tarapia Pacific Corp | Shipping | IBKR, held 3 anni, 2 dividendi/anno |
| SPKZ | Supercazzola Beverages SpA | Consumer Staples | IBKR, FIFO 3 lotti |
| MSCT | Mascetti Enterprises Inc | Diversified Industrials | IBKR, dividendo con ritenuta US 30% |
| SCPL | Scappellamento Industries Ltd | Specialty Chemicals | IBKR, acquisto 2024 + vendita parziale 2025 con gain |
| COMR | Come Noi Mediterranean SpA | Diversified Financials | IBKR, posizione aperta tutti gli anni |
| BLPP | Blinda la Porta Group plc | Building Products | IBKR, dividendo con ritenuta UK 0% |
| PRST | Prematurata Restaurant Corp | Consumer Discretionary | IBKR, 2 vendite con gain |
| STZC | Stuzzica Industries Inc | Packaged Foods | IBKR, dividendo con ritenuta DE 26.375% |
| CLCN | Clacsonato Corp | Auto Parts | IBKR, vendita totale 2024 con loss |
| ANTN | Antanics SA | Biotech | IBKR, small-cap volatile, RT mix |
| CMTH | Conte Mascetti Holdings Inc | (employer RSU) | Schwab, RSU vest 2024 + 2025 con withholding |
| CSHB | (US Treasury balance) | Cash | Schwab, balance > 50k USD per soglia |
- Bonifico EUR→USD 55.000 EUR a febbraio 2024 (porta il saldo sopra soglia 51.645,69)
- Saldo USD tenuto sopra soglia per 14+ giorni lavorativi 2024 e 21+ giorni 2025
- Bonifico USD→EUR ottobre 2025 con FIFO gain forex
- Cash balance IBKR tutti i tre anni → IVAFE 34.20 ciascun anno
- Interessi lordi IBKR su cash USD 2024 e 2025 → RL rigo interessi con eventuale ritenuta
- Soglia forex superata (2 anni su 3)
- FIFO forex con gain realizzato
- FIFO titoli multi-lot (3 lotti SPKZ)
- Dividendi con 4 paesi emittenti diversi e ritenute: US 30%, UK 0%, DE 26.375%, IT 26%
- RSU vest multi-year (2024 + 2025)
- RL rigo interessi con ritenuta
- Posizione cash significativa oltre soglia
Tutti con "666" per segnalare visivamente che sono fake:
| Holder | IBKR | Schwab |
|---|---|---|
| Magnotta | U66666660 |
— |
| Mosconi | U66666606 |
MSC666 |
| Mascetti | U66666066 |
CMT666 |
# Fixture-based
python -m decaf backtest tests/reference/magnotta
python -m decaf backtest tests/reference/mosconi
python -m decaf backtest tests/reference/mascetti
# BYOD con directory privata
python -m decaf backtest ~/private/decaf-my-dataPer ogni expected_<year>.yaml trovato nella directory, decaf:
- Ingerisce i file input (IBKR XML + Schwab {JSON, PDFs})
- Genera il report per l'anno
- Compara i totali con
expected_<year>.yaml - Stampa diff per riga
- Exit code 1 se mismatch, 0 se OK
git filter-repo --path tests/reference --invert-paths
git push --force-with-lease origin masterBackup preventivo dei dati reali in ~/.local/share/decaf-backup/ fuori da ~/code/.
- Magnotta: 30 min (XML piccolo, 3 ticker)
- Mosconi: 1.5 h (XML + Schwab JSON + 2 PDF fittizi — reportlab)
- Mascetti: 2.5 h (XML grosso multi-anno + Schwab multi-anno + tanti scenari forex)
Per ogni fixture:
- Scrivere a mano i file input con i dati pensati
- Eseguire
python -m decaf fetch --file ...per popolare statement DB - Eseguire
python -m decaf report --year <Y>e salvare l'output - Ispezionare i totali, copiarli in
expected_<year>.yaml - Committare input + output + expected insieme
Comando decaf backtest <dir> in src/decaf/cli.py, legge expected_*.yaml, ingerisce input, compara.
@pytest.mark.parametrize("fixture_dir", list_reference_fixtures())
def test_backtest_fixture(fixture_dir):
run_backtest(fixture_dir)Sezione "Backtesting" con le 3 fixture come esempi e la sezione BYOD.
Disclaimer forte: "decaf non sostituisce il commercialista, verifica tutto prima di firmare il modello."
pytestverde sulle 3 fixture- Ripgrep dei pattern di ID reali (IBKR + Schwab di vjt) su repo + git history → zero match
decaf backtest tests/reference/magnottaexit 0- Idem per mosconi e mascetti
- Su una fixture privata locale:
decaf backtest ~/private/my-dataexit 0
GitHub Settings → Change visibility → Public.
| Passo | Tempo |
|---|---|
| Scrub history | 30 min |
| Fixture Magnotta | 30 min |
| Fixture Mosconi | 1.5 h |
| Fixture Mascetti | 2.5 h |
| CLI backtest | 45 min |
| Parametrizzare test_e2e | 30 min |
| README | 30 min |
| Verifica | 30 min |
| Totale | ~7 h |
(Il costo cresce rispetto alla stima di 2.5h perché Mascetti è un portafoglio grosso e multi-anno; ogni scenario forex richiede date + bonifici + FIFO coerenti, niente di automatico.)
- Schwab PDF: serve generarne di sintetici con
reportlabche il parser esistente sappia leggere. Serve leggeretest_schwab_parse.pye capire il formato atteso esatto. - Expected values: prima esecuzione di decaf fissa gli
expected_*.yaml. Se la logica cambia dopo, i valori vanno rivisti. Non è una prova formale di correttezza — è una regressione di output. - Output binari committati: gli
.xlsxe.pdfnon sono diffabili facilmente. Accettato come trade-off (regressione visiva vale di più). - Backup reali fuori dal repo: mettere in
~/.local/share/decaf-backup/e non in~/code/per evitare che ungit addper sbaglio li ripeschi.
- Tengo pydantic/dataclass per lo schema
expected.yamlo libero (dict)? - Vuoi che il CLI
decaf backtestaccetti anche--update-expectedper rigenerare gli YAML quando cambia la logica? - Per le fixture, il Schwab PDF lo genero con
reportlab(serio) o metto dei mock text-only che il parser intercetta (più pragmatico)?
v1: piano iniziale con fixture pino_paperino generica.
v2: 3 fixture tematiche (Magnotta easy, Mosconi medio, Mascetti stress test), coverage completa di 99 test esistenti, output PDF/XLS committati.