Het beheren van numerieke precisie is een hoeksteen van dubbel boekhouden. In digitale boekhouding, vooral bij meerdere valuta's, aandelenkoersen en fractionele aandelen, kunnen kleine afrondingsverschillen snel leiden tot frustrerende balanceringsfouten. Beancount biedt een geavanceerd maar intuïtief systeem voor het verwerken van precisie en het instellen van acceptabele toleranties. Deze gids leidt je door de werking ervan. ⚙️
Elk getal op deze pagina is gecontroleerd met Beancount 3.2.3, inclusief de grenzen: elk voorbeeld vermeldt welk residu wordt geaccepteerd en welke één cijfer te ver gaat.
Kernconcepten van Precisie
Het primaire doel van Beancount is ervoor te zorgen dat elke transactie tot nul balanceert. Echter, berekeningen met prijzen of kosten leveren vaak resultaten op met meer decimalen dan praktisch is om vast te leggen. Het tolerantiesysteem staat kleine, acceptabele onevenwichtigheden toe.
Automatische Tolerantie-Afleiding
Standaard leidt Beancount de vereiste tolerantie voor elke transactie automatisch af. Deze afleiding wordt individueel voor elke transactie uitgevoerd en afzonderlijk berekend voor elke betrokken valuta.
De regel is één vermenigvuldiging: de tolerantie voor een valuta is het kleinste cijfer dat in de boekingsbedragen van die valuta wordt gezien, maal de tolerance_multiplier-optie, die standaard 0.5 is. Bij die standaard is de tolerantie de helft van het laatste significante cijfer.
Beschouw bijvoorbeeld deze aankoop:
2013-04-03 * "Buy Fund"
Assets:Fund 10.22626 FUND {37.61 USD}
Assets:Cash -384.61 USDBeancount leidt de toleranties als volgt af:
- Voor de
FUND-grondstof heeft het getal10.226265 decimalen. De tolerantie is de helft van het laatste cijfer, dus $0.00001 \div 2 = 0.000005$FUND. - Voor de
USD-valuta heeft het getal-384.612 decimalen. De tolerantie is de helft van het laatste cijfer, dus $0.01 \div 2 = 0.005$USD.
Het kasdeel is waartegen de tolerantie wordt gemeten: 10.22626 × 37.61 is 384.6096386, dus deze transactie is 0.0003614 USD tekort om op nul uit te komen en wordt geladen. Rond het kasdeel af naar -384.60 en het verschil wordt 0.0096386 USD, voorbij de 0.005-tolerantie, en Beancount meldt Transaction does not balance.
Transactiegewichtsregels
Bij het controleren of een transactie balanceert, berekent Beancount het "gewicht" van elke boeking. De regels voor deze berekening zijn:
- Eenvoudig Bedrag: Als een boeking alleen een bedrag heeft (bijv.
Assets:Cash -100.00 USD), is het gewicht dat exacte bedrag. - Prijsboeking: Als een boeking een prijs per eenheid heeft (bijv.
10 FUND @ 38.46 USD), is het gewichtamount × price. - Kosten per Eenheid: Enkele accolades bevatten de kosten van één eenheid, dus
10 FUND {384.61 USD}weegt10 × 384.61 = 3,846.10 USD, niet384.61 USD. - Totale Kosten: Dubbele accolades bevatten de kosten van de volledige boeking, dus
10 FUND {{384.61 USD}}weegt384.61 USD. Beancount converteert dit naar kosten per eenheid van38.461 USDwanneer het de partij opslaat. - Kosten en Prijs: Als een boeking zowel kosten als een prijs per eenheid heeft (bijv.
10 FUND {384.61 USD} @ 400.00 USD), worden alleen de kosten gebruikt voor de balancering. De prijs wordt vastgelegd voor rapportage, niet voor rekenkunde.
Regels 3 en 4 zijn degenen die mensen een middag kosten, dus hier staan ze naast elkaar in een bestand dat laadt:
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 USDDe rekening eindigt met 20 FUND in twee partijen, met 4,230.71 USD aan kostprijsbasis tussen hen.
Precisie-Afleidingsregels
Het automatische afleidingssysteem volgt een paar specifieke regels:
- Getalnotatie
- Gehele bedragen (bijv.
10 USD) dragen niet bij aan precisie-afleiding. - Eén decimaal is het grofst dat een bedrag kan impliceren:
0.1 × 0.5 = 0.05eenheden. Daarbuiten heb jetolerance_multiplierof een standaard per valuta nodig, beide hieronder. - Kosten en prijzen (bijv.
{37.61 USD}) zijn uitgesloten van tolerantie-afleiding standaard. Alleen de primaire bedragen van de boekingen worden gebruikt. - Als boekingen voor dezelfde valuta verschillende precisies hebben (bijv.
-10.10 USDen5.123 USD), gebruikt Beancount de grofste (grootste) tolerantie. In dit geval zou het gebaseerd zijn op-10.10 USD, wat een tolerantie van $0.005$USDoplevert.
-
Standaardafhandeling Je kunt een globale of valuta-specifieke standaardtolerantie instellen als een transactie geen getallen met decimalen heeft waaruit deze kan worden afgeleid.
; 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" -
Tolerantievermenigvuldiger De optie is
tolerance_multiplier, en het is de fractie van het kleinste cijfer dat als tolerant wordt beschouwd — niet een percentage dat erbovenop komt. De standaard is0.5, dus het instellen van1.2maakt controles niet 20% losser: het maakt elke afgeleide tolerantie 2,4 keer de standaard.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 USDDe oudere naam
inferred_tolerance_multiplierstelt dezelfde waarde in maar meldtRenamed to 'tolerance_multiplier'.als laadfout. -
Kostengebaseerde Afleiding Hoewel kosten normaal worden genegeerd voor tolerantie-afleiding, kun je Beancount instrueren ze te gebruiken. Dit is handig wanneer het uiteindelijke bedrag (bijv. een contante opname) het meest precieze getal in een transactie is.
option "infer_tolerance_from_cost" "TRUE"
Hier is de gewone standaard, zonder opties, op zijn exacte grens:
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 USDSaldo-Asserties
Saldo-asserties (balance) worden gebruikt om te verifiëren dat het saldo van je rekening overeenkomt met een bekende waarde op een specifieke datum. Ze hebben ook een bijbehorende tolerantie.
Basisaanduiding
De tolerantie voor een balance-assertie wordt afgeleid uit het aantal decimalen in het bedrag, maar is twee keer zo gul als die binnen een transactie: tolerance_multiplier × 2 × the smallest digit. Bij de standaardvermenigvuldiger is dat precies één eenheid van het laatste decimaal dat je schreef.
; 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 RGAGXDe vergelijking is inclusief: een verschil dat exact gelijk is aan de tolerantie slaagt nog steeds. Voor het tweede voorbeeld slaagt elk saldo van $4.26$ tot $4.28$ voor de controle, en 4.2801 faalt met 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 RGAGXExpliciete Toleranties
Als de afgeleide tolerantie niet geschikt is, kun je er expliciet een specificeren met het tilde (~)-teken. Dit is de enige expliciete tolerantiesyntaxis die Beancount heeft, en het werkt alleen op balance-richtlijnen — een tilde binnen een transactieboeking is een syntaxisfout.
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 RGAGXVerhoog het bezit naar 4.2811 en dezelfde assertie faalt met 0.0101.
Afrondingsbeheer
Kleine residuen van kosten- en prijsberekeningen zijn normaal. Wat Beancount ermee doet is smaller dan het lijkt.
Tracking van Afrondingsfouten
De account_rounding-optie benoemt een rekening bedoeld om residuen te absorberen. Het neemt een volledige rekeningnaam en wordt exact opgeslagen zoals je het schrijft — geen eigen vermogen-voorvoegsel wordt toegevoegd, in tegenstelling tot de eigen vermogen rekeningopties.
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 USDIn deze transactie is . De transactie is onevenwichtig met $-0.00135$ USD, wat binnen de afgeleide 0.005 USD-tolerantie valt, dus het laadt.
Op Beancount 3.2.3 wordt er niets geboekt naar Equity:Rounding. De optie wordt geparset en opgeslagen, maar geen enkele fase van de lader voegt de residuele boeking in, dus de rekening eindigt op nul en een residu dat buiten tolerantie valt is nog steeds een fout in plaats van te worden opgeveegd:
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 USDBehandel account_rounding dus als inert op deze versie. Als je een residu vastgelegd wilt hebben in plaats van getolereerd, schrijf dan zelf de derde boeking:
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 USDDie versie balanceert naar exact nul, en het stof is zichtbaar in een rekening waarover je kunt rapporteren.
Afgeleide Getalprecisie
Beancount rondt de getallen die je schrijft niet af. Er is geen default_tolerance-optie — die bestaat niet en faalt de laad met Invalid option: 'default_tolerance' — en geen instelling kwantiseert opgeslagen bedragen.
-
Opslag is altijd exact. Schrijf
53.82135 USDen de grootboek houdt53.82135 USDvast, ongeacht je tolerantie-instellingen. Tolerantie bepaalt of een transactie wordt geaccepteerd; het bewerkt nooit een getal. -
Weergave is een aparte instelling.
display_precisionbepaalt met hoeveel fractionele cijfers een valuta wordt weergegeven, en verandert niets aan de opgeslagen waarde of de saldocontrole.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 -
Afronding is jouw boeking om te schrijven. Als je het residu uit de rekenkunde wilt, rond het bedrag dan af in de bron en boek het verschil expliciet, zoals in het voorbeeld met drie boekingen hierboven.
Implementatiedetails
Een paar technische punten verduidelijken hoe Beancount deze betrouwbaarheid bereikt.
-
Getalrepresentatie: Beancount gebruikt Python's
decimal-module, geen drijvende-kommagetallen. De standaardcontext draagt 28 significante cijfers — totale cijfers, niet cijfers na de komma — wat de binaire representatiefouten vermijdt die gebruikelijk zijn bij floats. -
DisplayContext-klasse: Deze interne klasse handelt alle getalopmaak af voor weergavedoeleinden. Het leidt de precisie van elke valuta af uit de getallen in je bestand, tenzij
display_precisionhet vastpint, en kan uitvoer opmaken met uitgelijnde kolommen en komma's. -
Precisie vs. Tolerantie: Het is cruciaal om deze twee concepten te onderscheiden:
- Precisie heeft betrekking op het weergaveformaat van een getal (hoeveel decimalen worden getoond).
- Tolerantie is de toegestane onevenwichtigheid die wordt gebruikt tijdens verificatiecontroles.
Beste Praktijken ✨
Hier zijn enkele praktische aanbevelingen voor het beheren van precisie in je grootboek.
Initiële Configuratie
Voor de meeste nieuwe grootboeken is dit een robuuste startconfiguratie:
; 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"Probleemoplossingstips
Als je balanceringsfouten tegenkomt:
- Voeg decimalen toe aan het bedrag van een boeking om een strakkere, nauwkeurigere lokale tolerantie-afleiding te creëren.
- Gebruik expliciete toleranties (
~) opbalance-asserties die falen vanwege voorspelbare verschillen. - Boek het residu naar een toegewijde rekening met een echte derde boeking, zodat je kunt rapporteren hoe vaak het voorkomt.
- Overweeg het instellen van valuta-specifieke standaarden als je vaak te maken hebt met valuta's met verschillende conventies (bijv. JPY heeft geen decimalen).
Migratiestrategie
Bij het toepassen van deze concepten op een bestaand, rommelig grootboek:
- Begin met een ruime globale tolerantie (bijv.
*:0.05) en een hogeretolerance_multiplierom het bestand te laten valideren. - Verklein de toleranties geleidelijk en los de fouten op die verschijnen.
- Voeg expliciete cijfers toe aan bedragen in problematische transacties zodat afleiding zijn werk kan doen.
- Monitor het saldo van de afrondingsrekening. Een groot of snelgroeiend saldo kan wijzen op een systemisch probleem dat onderzoek vereist.