Salta al contenuto principale

Precisione e Tolleranze

Scopri come i sistemi di precisione e tolleranza di Beancount aiutano a mantenere l'equilibrio nella contabilità a partita doppia, specialmente quando si gestiscono transazioni complesse che coinvolgono più valute e valori frazionari.

La gestione della precisione numerica è una pietra angolare della contabilità a partita doppia. Nella tenuta dei libri contabili digitali, specialmente quando si ha a che fare con più valute, prezzi di azioni e frazioni di azioni, piccole discrepanze di arrotondamento possono rapidamente portare a frustranti errori di bilanciamento. Beancount fornisce un sistema sofisticato ma intuitivo per gestire la precisione e impostare tolleranze accettabili. Questa guida ti accompagnerà attraverso il suo funzionamento. ⚙️

Ogni numero in questa pagina è stato verificato con Beancount 3.2.3, inclusi i valori limite: ogni esempio indica quale residuo viene accettato e quale supera la tolleranza di una cifra.

Concetti Fondamentali sulla Precisione

L'obiettivo primario di Beancount è garantire che ogni transazione sia bilanciata a zero. Tuttavia, i calcoli che coinvolgono prezzi o costi spesso producono risultati con più cifre decimali di quanto sia pratico registrare. Il sistema di tolleranza consente piccoli e accettabili squilibri.

Tolleranza Automatica

Per impostazione predefinita, Beancount deduce automaticamente la tolleranza necessaria per ogni transazione. Questa deduzione viene applicata singolarmente per ogni transazione e calcolata separatamente per ogni valuta coinvolta.

La regola è una moltiplicazione: la tolleranza per una valuta è la cifra più piccola osservata negli importi delle registrazioni in quella valuta, moltiplicata per l'opzione tolerance_multiplier, che per impostazione predefinita è 0.5. Con questa impostazione predefinita, la tolleranza è la metà dell'ultima cifra significativa.

Ad esempio, considera questo acquisto:

2013-04-03 * "Buy Fund"
  Assets:Fund     10.22626 FUND {37.61 USD}
  Assets:Cash     -384.61 USD

Beancount deduce le tolleranze come segue:

  • Per la valuta FUND, il numero 10.22626 ha 5 cifre decimali. La tolleranza è la metà dell'ultima cifra, quindi $0.00001 \div 2 = 0.000005$ FUND.
  • Per la valuta USD, il numero -384.61 ha 2 cifre decimali. La tolleranza è la metà dell'ultima cifra, quindi $0.01 \div 2 = 0.005$ USD.

Il valore in contanti è ciò contro cui viene misurata la tolleranza: 10.22626 × 37.61 è 384.6096386, quindi questa transazione ha un disavanzo di 0.0003614 USD rispetto allo zero e viene caricata. Se arrotondi la voce in contanti a -384.60, il divario diventa 0.0096386 USD, superando la tolleranza di 0.005, e Beancount segnala Transaction does not balance.

Regole per il Peso delle Registrazioni

Quando verifica se una transazione è bilanciata, Beancount calcola il "peso" di ogni registrazione. Le regole per questo calcolo sono:

  1. Importo Semplice: Se una registrazione ha solo un importo (es., Assets:Cash -100.00 USD), il suo peso è quell'importo esatto.
  2. Registrazione con Prezzo: Se una registrazione ha un prezzo unitario (es., 10 FUND @ 38.46 USD), il suo peso è amount × price.
  3. Costo Unitario: Le parentesi graffe singole contengono il costo di una singola unità, quindi 10 FUND {384.61 USD} pesa 10 × 384.61 = 3,846.10 USD, non 384.61 USD.
  4. Costo Totale: Le doppie parentesi graffe contengono il costo dell'intera registrazione, quindi 10 FUND {{384.61 USD}} pesa 384.61 USD. Beancount lo converte in un costo unitario di 38.461 USD quando lo memorizza.
  5. Costo e Prezzo: Se una registrazione ha sia un costo che un prezzo unitario (es., 10 FUND {384.61 USD} @ 400.00 USD), solo il costo viene utilizzato per il bilanciamento. Il prezzo viene registrato per il reporting, non per l'aritmetica.

Le regole 3 e 4 sono quelle che costano un pomeriggio di lavoro alle persone, quindi eccole affiancate in un file che viene caricato correttamente:

1970-01-01 open Assets:Fund
1970-01-01 open Assets:Cash
 
; Per-unit cost: ten units at 384.61 each, so 3,846.10 USD leaves the
; cash account.
2013-04-03 * "Broker" "Buy at a per-unit cost"
  Assets:Fund     10 FUND {384.61 USD}
  Assets:Cash  -3846.10 USD
 
; Total cost: the braces double and 384.61 USD is the entire purchase.
; The lot is stored at 38.461 USD per unit.
2013-04-04 * "Broker" "Buy at a total cost"
  Assets:Fund      10 FUND {{384.61 USD}}
  Assets:Cash   -384.61 USD

Il conto termina con 20 FUND in due lotti, con 4,230.71 USD di costo totale tra loro.

Regole per la Deduzione della Precisione

Il sistema di deduzione automatica segue alcune regole specifiche:

  1. Formato del Numero
  • Gli importi interi (es., 10 USD) non contribuiscono alla deduzione della precisione.
  • Una cifra decimale è la precisione più grossolana che un importo può implicare: 0.1 × 0.5 = 0.05 unità. Oltre a questo, hai bisogno di tolerance_multiplier o di un'impostazione predefinita per valuta, come descritto di seguito.
  • Costi e prezzi (es., {37.61 USD}) sono esclusi dalla deduzione della tolleranza per impostazione predefinita. Vengono utilizzati solo gli importi primari delle registrazioni.
  • Se le registrazioni per la stessa valuta hanno precisioni diverse (es., -10.10 USD e 5.123 USD), Beancount utilizza la tolleranza più grossolana (più grande). In questo caso, sarebbe basata su -10.10 USD, producendo una tolleranza di $0.005$ USD.
  1. Gestione dell'Impostazione Predefinita Puoi impostare una tolleranza predefinita globale o specifica per valuta se una transazione non ha numeri con cifre decimali da cui dedurla.

    ; Sets a default tolerance for all currencies without explicit rules
    option "inferred_tolerance_default" "*:0.001"
     
    ; Sets a specific default tolerance for USD
    option "inferred_tolerance_default" "USD:0.003"
  2. Moltiplicatore di Tolleranza L'opzione è tolerance_multiplier, ed è la frazione dell'ultima cifra significativa che conta come tollerabile — non una percentuale aggiunta. Il suo valore predefinito è 0.5, quindi impostandolo a 1.2 non si allentano i controlli del 20%: rende ogni tolleranza dedotta 2.4 volte quella predefinita.

    option "tolerance_multiplier" "1.2"
     
    1970-01-01 open Assets:Cash
    1970-01-01 open Expenses:Fees
     
    ; The coarsest amount has two decimals, so the tolerance is
    ; 1.2 x 0.01 = 0.012 USD, and this residual of exactly 0.012 passes.
    ; At the default 0.5 the tolerance would be 0.005 and this would fail.
    2024-05-01 * "Bank" "Wire fee"
      Expenses:Fees      100.00 USD
      Assets:Cash       -99.988 USD

    Il nome più vecchio inferred_tolerance_multiplier imposta lo stesso valore, ma segnala Renamed to 'tolerance_multiplier'. come errore di caricamento.

  3. Deduzione Basata sul Costo Sebbene i costi siano normalmente ignorati per la deduzione della tolleranza, puoi istruire Beancount a utilizzarli. Questo è utile quando l'importo finale (es., un prelievo di contante) è il numero più preciso in una transazione.

    option "infer_tolerance_from_cost" "TRUE"

Ecco l'impostazione predefinita semplice, senza opzioni aggiuntive, al suo esatto valore limite:

1970-01-01 open Assets:Cash
1970-01-01 open Expenses:Fees
 
; Two decimals on the coarsest amount, so the tolerance is
; 0.5 x 0.01 = 0.005 USD. This residual is exactly 0.005 and passes;
; -99.994 would be 0.006 and would fail.
2024-05-01 * "Bank" "Wire fee"
  Expenses:Fees      100.00 USD
  Assets:Cash       -99.995 USD

Asserzioni di Bilancio

Le asserzioni di bilancio (balance) vengono utilizzate per verificare che il saldo di un conto corrisponda a un valore noto in una data specifica. Anch'esse hanno una tolleranza associata.

Formato di Base

La tolleranza per un'asserzione di balance viene dedotta dal numero di cifre decimali dell'importo, ma è due volte più generosa di quella usata all'interno di una transazione: tolerance_multiplier × 2 × the smallest digit. Con il moltiplicatore predefinito, corrisponde esattamente a un'unità dell'ultima cifra decimale che hai scritto.

; Asserts the balance is 4.271 RGAGX with a tolerance of +/-0.001
2015-05-08 balance Assets:Fund  4.271 RGAGX
 
; Asserts the balance is 4.27 RGAGX with a tolerance of +/-0.01
2015-05-08 balance Assets:Fund  4.27 RGAGX

Il confronto è inclusivo: una differenza esattamente uguale alla tolleranza supera comunque il controllo. Per il secondo esempio, qualsiasi saldo da $4.26$ a $4.28$ supera il controllo, mentre 4.2801 fallisce con Balance failed for 'Assets:Fund': expected 4.27 RGAGX != accumulated 4.2801 RGAGX (0.0101 too much).

1970-01-01 open Assets:Fund
1970-01-01 open Equity:Opening-Balances
 
1970-01-02 * "Broker" "Opening position"
  Assets:Fund                4.28 RGAGX
  Equity:Opening-Balances   -4.28 RGAGX
 
; 4.28 is 0.01 away from the asserted 4.27, which is the whole tolerance.
2015-05-08 balance Assets:Fund   4.27 RGAGX

Tolleranze Esplicite

Se la tolleranza dedotta non è adatta, puoi specificarne una esplicitamente usando il carattere tilde (~). Questa è l'unica sintassi per tolleranze esplicite in Beancount e funziona solo con le direttive balance — una tilde all'interno di una registrazione di transazione è un errore di sintassi.

1970-01-01 open Assets:Fund
1970-01-01 open Equity:Opening-Balances
 
1970-01-02 * "Broker" "Opening position"
  Assets:Fund                4.281 RGAGX
  Equity:Opening-Balances   -4.281 RGAGX
 
; Asserts the balance is 4.271 RGAGX with a custom tolerance of
; +/-0.01 RGAGX, so anything from 4.261 to 4.281 passes.
2015-05-08 balance Assets:Fund   4.271 ~ 0.01 RGAGX

Porta il saldo a 4.2811 e la stessa asserzione fallisce di 0.0101.

Gestione dell'Arrotondamento

Piccoli residui derivanti dall'aritmetica di costi e prezzi sono normali. Ciò che Beancount fa con essi è più limitato di quanto sembri.

Tracciamento degli Errori di Arrotondamento

L'opzione account_rounding nomina un conto destinato ad assorbire i residui. Accetta un nome di conto completo e viene memorizzato esattamente come lo scrivi — non viene aggiunto alcun prefisso relativo al patrimonio netto, a differenza delle opzioni per i conti di patrimonio netto.

option "account_rounding" "Equity:Rounding"
 
1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
 
; 1.245 x 43.23 = 53.82135, so this is 0.00135 USD short of balancing.
2013-02-23 * "Broker" "Purchase"
  Assets:Invest     1.245 RGAGX {43.23 USD}
  Assets:Cash      -53.82 USD

In questa transazione, 1.245×43.23=53.821351.245 \times 43.23 = 53.82135. La transazione è sbilanciata di $-0.00135$ USD, che rientra nella tolleranza dedotta di 0.005 USD, quindi viene caricata.

Su Beancount 3.2.3 non viene registrato nulla su Equity:Rounding. L'opzione viene analizzata e memorizzata, ma nessuna fase del caricatore inserisce la registrazione residua, quindi il conto termina a zero e un residuo che è fuori tolleranza è comunque un errore piuttosto che essere spazzato via:

option "account_rounding" "Equity:Rounding"
 
1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
 
; 0.10135 USD out, far past the 0.005 tolerance. Setting
; account_rounding does not rescue it:
;   Transaction does not balance: (0.10135 USD)
2013-02-23 * "Broker" "Purchase"
  Assets:Invest     1.245 RGAGX {43.23 USD}
  Assets:Cash      -53.72 USD

Quindi tratta account_rounding come inerte su questa versione. Se vuoi che un residuo venga registrato piuttosto che tollerato, scrivi tu stesso la terza registrazione:

1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
 
2013-02-23 * "Broker" "Purchase"
  Assets:Invest      1.245 RGAGX {43.23 USD}
  Assets:Cash       -53.82 USD
  Equity:Rounding   -0.00135 USD

Quella versione si bilancia esattamente a zero e la polvere è visibile in un conto su cui puoi fare report.

Precisione Numerica Dedotta

Beancount non arrotonda i numeri che scrivi. Non esiste un'opzione default_tolerance — non esiste e fallisce il caricamento con Invalid option: 'default_tolerance' — e nessuna impostazione quantizza gli importi memorizzati.

  1. La memorizzazione è sempre esatta. Scrivi 53.82135 USD e il registro contiene 53.82135 USD, qualunque cosa dicano le tue impostazioni di tolleranza. La tolleranza decide se una transazione è accettata; non modifica mai un numero.

  2. La visualizzazione è un'impostazione separata. display_precision fissa quante cifre frazionarie vengono visualizzate per una valuta e non cambia nulla riguardo al valore memorizzato o al controllo del saldo.

    option "display_precision" "USD:0.01"
     
    1970-01-01 open Assets:Cash
    1970-01-01 open Income:Interest
     
    ; Rendered as 53.82 USD, stored as 53.82135 USD.
    2024-06-30 * "Bank" "Interest"
      Assets:Cash          53.82135 USD
      Income:Interest     -53.82135 USD
  3. L'arrotondamento è una registrazione che scrivi tu. Se vuoi eliminare il residuo dall'aritmetica, arrotonda l'importo nella fonte e registra la differenza esplicitamente, come nell'esempio con tre registrazioni sopra.

Dettagli Implementativi

Alcuni punti tecnici chiariscono come Beancount raggiunge questa affidabilità.

  1. Rappresentazione dei Numeri: Beancount utilizza il modulo decimal di Python, non numeri a virgola mobile. Il contesto predefinito porta 28 cifre significative — cifre totali, non cifre dopo il punto decimale — il che evita gli errori di rappresentazione binaria comuni ai float.

  2. Classe DisplayContext: Questa classe interna gestisce tutta la formattazione dei numeri per scopi di visualizzazione. Deduce la precisione di ciascuna valuta dai numeri nel tuo file a meno che display_precision non la fissi, e può formattare l'output con colonne allineate e virgole.

  3. Precisione vs. Tolleranza: È fondamentale distinguere questi due concetti:

  • La Precisione riguarda il formato di visualizzazione di un numero (quante cifre decimali vengono mostrate).
  • La Tolleranza è il margine consentito per lo squilibrio utilizzato durante i controlli di verifica.

Buone Pratiche ✨

Ecco alcuni consigli pratici per gestire la precisione nel tuo registro.

Configurazione Iniziale

Per la maggior parte dei nuovi registri, questa è una configurazione iniziale robusta:

; A floor for currencies that have no decimals to infer from
option "inferred_tolerance_default" "*:0.005"
 
; Leave the multiplier at its 0.5 default unless a real institution
; forces your hand; 1.2 would mean 2.4x the usual tolerance.
option "tolerance_multiplier" "0.5"

Suggerimenti per la Risoluzione dei Problemi

Se incontri errori di bilanciamento:

  • Aggiungi cifre decimali all'importo di una registrazione per creare una deduzione della tolleranza locale più precisa e accurata.
  • Usa tolleranze esplicite (~) sulle asserzioni di balance che falliscono a causa di discrepanze prevedibili.
  • Registra il residuo su un conto dedicato con una vera terza registrazione, così puoi fare report su quanto spesso accade.
  • Considera l'impostazione di valori predefiniti specifici per valuta se hai spesso a che fare con valute che hanno convenzioni diverse (es., JPY non ha decimali).

Strategia di Migrazione

Quando applichi questi concetti a un registro esistente e disordinato:

  1. Inizia con una tolleranza globale generosa (es., *:0.05) e un tolerance_multiplier più alto per far validare il file.
  2. Progressivamente restringi le tolleranze e correggi gli errori che emergono.
  3. Aggiungi cifre esplicite agli importi nelle transazioni problematiche per permettere alla deduzione di fare il suo lavoro.
  4. Monitora il saldo del conto di arrotondamento. Un saldo grande o in rapida crescita può segnalare un problema sistemico che richiede indagine.

Fonte: https://beancount.io/it/docs/Basics/precision