Skip to content

Instantly share code, notes, and snippets.

@vjt
Last active April 17, 2026 20:21
Show Gist options
  • Select an option

  • Save vjt/1e8ac9384a62732e8542594d406052d0 to your computer and use it in GitHub Desktop.

Select an option

Save vjt/1e8ac9384a62732e8542594d406052d0 to your computer and use it in GitHub Desktop.
decaf — piano per open-source (scrub + fixture sintetiche + BYOD backtest)

Decaf — Piano per open-source (v2, finale)

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.

Layout finale

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.

Fixture 1 — Mario Magnotta (EASY)

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.

Portafoglio

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

Add IVAFE depositi

Cash balance IBKR = 1.247 USD tenuto a fine anno 2024 → IVAFE depositi 34.20 EUR fissi.

Expected totali 2024

# 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

Path esercitati

  • 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

Fixture 2 — Germano Mosconi (MEDIO)

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.

Portafoglio

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%

Add IVAFE depositi

Cash balance Schwab = 820 USD a fine 2024 → IVAFE depositi 34.20 EUR fissi.

Expected (estratto 2024)

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: ~

Path esercitati (aggiuntivi rispetto a Magnotta)

  • 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

Fixture 3 — Conte Raffaello Mascetti (STRESS TEST)

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.

Portafoglio

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

Add forex intensivo

  • 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

Add IVAFE depositi + interessi IBKR

  • 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

Path esercitati (aggiuntivi rispetto a Mosconi)

  • 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

Account ID theme

Tutti con "666" per segnalare visivamente che sono fake:

Holder IBKR Schwab
Magnotta U66666660
Mosconi U66666606 MSC666
Mascetti U66666066 CMT666

CLI backtest

# 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-data

Per ogni expected_<year>.yaml trovato nella directory, decaf:

  1. Ingerisce i file input (IBKR XML + Schwab {JSON, PDFs})
  2. Genera il report per l'anno
  3. Compara i totali con expected_<year>.yaml
  4. Stampa diff per riga
  5. Exit code 1 se mismatch, 0 se OK

Passi concreti

1. Scrub history (30 min)

git filter-repo --path tests/reference --invert-paths
git push --force-with-lease origin master

Backup preventivo dei dati reali in ~/.local/share/decaf-backup/ fuori da ~/code/.

2. Scrivere le 3 fixture (4–5 h)

  • 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:

  1. Scrivere a mano i file input con i dati pensati
  2. Eseguire python -m decaf fetch --file ... per popolare statement DB
  3. Eseguire python -m decaf report --year <Y> e salvare l'output
  4. Ispezionare i totali, copiarli in expected_<year>.yaml
  5. Committare input + output + expected insieme

3. CLI backtest (45 min)

Comando decaf backtest <dir> in src/decaf/cli.py, legge expected_*.yaml, ingerisce input, compara.

4. Parametrizzare test_e2e.py (30 min)

@pytest.mark.parametrize("fixture_dir", list_reference_fixtures())
def test_backtest_fixture(fixture_dir):
    run_backtest(fixture_dir)

5. Update README (30 min)

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."

6. Verifica finale (30 min)

  • pytest verde sulle 3 fixture
  • Ripgrep dei pattern di ID reali (IBKR + Schwab di vjt) su repo + git history → zero match
  • decaf backtest tests/reference/magnotta exit 0
  • Idem per mosconi e mascetti
  • Su una fixture privata locale: decaf backtest ~/private/my-data exit 0

7. Flip a public

GitHub Settings → Change visibility → Public.

Tempi totali stimati

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.)

Rischi / attenzioni

  • Schwab PDF: serve generarne di sintetici con reportlab che il parser esistente sappia leggere. Serve leggere test_schwab_parse.py e 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 .xlsx e .pdf non 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 un git add per sbaglio li ripeschi.

Domande aperte (finali)

  1. Tengo pydantic/dataclass per lo schema expected.yaml o libero (dict)?
  2. Vuoi che il CLI decaf backtest accetti anche --update-expected per rigenerare gli YAML quando cambia la logica?
  3. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment