Zum Hauptinhalt springen

Präzision & Toleranzen

Erfahren Sie, wie Beancounts Präzisions- und Toleranzsysteme dazu beitragen, die Bilanz in der doppelten Buchführung auszugleichen, insbesondere bei komplexen Transaktionen mit mehreren Währungen und Bruchteilen.

Die Verwaltung numerischer Präzision ist ein Eckpfeiler der doppelten Buchführung. In der digitalen Buchhaltung, insbesondere bei mehreren Währungen, Aktienkursen und Bruchteilen von Aktien, können kleine Rundungsabweichungen schnell zu frustrierenden Bilanzierungsfehlern führen. Beancount bietet ein ausgeklügeltes, aber intuitives System zur Handhabung von Präzision und zur Festlegung akzeptabler Toleranzen. Dieser Leitfaden erklärt, wie es funktioniert. ⚙️

Jede Zahl auf dieser Seite wurde gegen Beancount 3.2.3 geprüft, einschließlich der Grenzfälle: Jeder Eintrag gibt an, welcher Restbetrag akzeptiert wird und welcher um eine Ziffer zu hoch ist.

Kernkonzepte der Präzision

Beancounts Hauptziel ist es, sicherzustellen, dass jede Transaktion auf Null ausgeglichen ist. Berechnungen mit Preisen oder Kosten erzeugen jedoch oft Ergebnisse mit mehr Dezimalstellen, als praktisch aufzuzeichnen sind. Das Toleranzsystem erlaubt kleine, akzeptable Ungleichgewichte.

Automatische Toleranzableitung

Standardmäßig leitet Beancount die erforderliche Toleranz für jede Transaktion automatisch ab. Diese Ableitung erfolgt für jede Transaktion einzeln und wird für jede beteiligte Währung separat berechnet.

Die Regel ist eine Multiplikation: Die Toleranz für eine Währung ist die kleinste Ziffer, die in den Buchungen dieser Währung vorkommt, multipliziert mit der Option tolerance_multiplier, die standardmäßig 0.5 beträgt. Bei diesem Standardwert ist die Toleranz die Hälfte der letzten signifikanten Ziffer.

Betrachten Sie beispielsweise diesen Kauf:

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

Beancount leitet die Toleranzen wie folgt ab:

  • Für das Wertpapier FUND hat der Betrag 10.22626 fünf Dezimalstellen. Die Toleranz ist die Hälfte der letzten Ziffer, also $0.00001 \div 2 = 0.000005$ FUND.
  • Für die Währung USD hat der Betrag -384.61 zwei Dezimalstellen. Die Toleranz ist die Hälfte der letzten Ziffer, also $0.01 \div 2 = 0.005$ USD.

Der Bargeldanteil ist der Maßstab für die Toleranz: 10.22626 × 37.61 ergibt 384.6096386, die Transaktion ist also 0.0003614 USD von Null entfernt und wird geladen. Runden Sie den Bargeldanteil auf -384.60, beträgt die Differenz 0.0096386 USD, was über der Toleranz von 0.005 liegt, und Beancount meldet Transaction does not balance.

Regeln für das Gewicht von Buchungen

Wenn Beancount prüft, ob eine Transaktion ausgeglichen ist, berechnet es das „Gewicht" jeder Buchung. Die Regeln für diese Berechnung sind:

  1. Einfacher Betrag: Wenn eine Buchung nur einen Betrag hat (z. B. Assets:Cash -100.00 USD), ist ihr Gewicht genau dieser Betrag.
  2. Preisbuchung: Wenn eine Buchung einen Preis pro Einheit hat (z. B. 10 FUND @ 38.46 USD), ist ihr Gewicht amount × price.
  3. Einzelpreis: Einfache geschweifte Klammern enthalten die Kosten für eine Einheit, also wiegt 10 FUND {384.61 USD} 10 × 384.61 = 3,846.10 USD, nicht 384.61 USD.
  4. Gesamtkosten: Doppelte geschweifte Klammern enthalten die Kosten für die gesamte Buchung, also wiegt 10 FUND {{384.61 USD}} 384.61 USD. Beancount wandelt dies in einen Einzelpreis von 38.461 USD um, wenn es die Charge speichert.
  5. Kosten und Preis: Wenn eine Buchung sowohl Kosten als auch einen Preis pro Einheit hat (z. B. 10 FUND {384.61 USD} @ 400.00 USD), wird nur die Kosten für den Ausgleich verwendet. Der Preis wird für die Berichterstattung notiert, nicht für die Arithmetik.

Regeln 3 und 4 sind diejenigen, die Leuten einen Nachmittag kosten, daher hier nebeneinander in einer Datei, die geladen wird:

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

Das Konto endet mit 20 FUND in zwei Chargen, mit 4,230.71 USD Kostenbasis zwischen ihnen.

Regeln für die Ableitung der Präzision

Das automatische Ableitungssystem folgt einigen spezifischen Regeln:

  1. Zahlenformat
  • Ganzzahlige Beträge (z. B. 10 USD) tragen nicht zur Präzisionsableitung bei.
  • Eine Dezimalstelle ist die gröbste Genauigkeit, die ein Betrag implizieren kann: 0.1 × 0.5 = 0.05 Einheiten. Darüber hinaus benötigen Sie tolerance_multiplier oder eine währungsabhängige Standardeinstellung (siehe unten).
  • Kosten und Preise (z. B. {37.61 USD}) sind standardmäßig von der Toleranzableitung ausgeschlossen. Nur die primären Beträge der Buchungen werden verwendet.
  • Wenn Buchungen für dieselbe Währung unterschiedliche Präzisionen haben (z. B. -10.10 USD und 5.123 USD), verwendet Beancount die gröbste (größte) Toleranz. In diesem Fall wäre es basierend auf -10.10 USD, was eine Toleranz von $0,005$ USD ergibt.
  1. Standardbehandlung Sie können eine globale oder währungsabhängige Standardtoleranz festlegen, wenn eine Transaktion keine Dezimalzahlen enthält, aus denen sie abgeleitet werden kann.

    ; 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. Toleranzmultiplikator Die Option ist tolerance_multiplier und gibt den Bruchteil der kleinsten Ziffer an, der als tolerierbar gilt — kein prozentualer Aufschlag. Der Standardwert ist 0.5, daher lockert die Einstellung 1.2 die Prüfungen nicht um 20 %, sondern macht jede abgeleitete Toleranz 2,4-mal so groß wie den Standardwert.

    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

    Der ältere Name inferred_tolerance_multiplier setzt denselben Wert, meldet aber beim Laden Renamed to 'tolerance_multiplier'. als Fehler.

  3. Kostenbasierte Ableitung Obwohl Kosten normalerweise für die Toleranzableitung ignoriert werden, können Sie Beancount anweisen, sie zu verwenden. Dies ist hilfreich, wenn der Endbetrag (z. B. eine Barabhebung) die präziseste Zahl in einer Transaktion ist.

    option "infer_tolerance_from_cost" "TRUE"

Hier ist der Standard ohne Optionen an seiner exakten Grenze:

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

Bilanzprüfungen

Bilanzprüfungen (balance) werden verwendet, um zu überprüfen, ob der Saldo Ihres Kontos an einem bestimmten Datum einem bekannten Wert entspricht. Sie haben ebenfalls eine zugehörige Toleranz.

Grundformat

Die Toleranz für eine balance-Prüfung wird aus der Anzahl der Dezimalstellen des Betrags abgeleitet, ist jedoch doppelt so großzügig wie die in einer Transaktion: tolerance_multiplier × 2 × the smallest digit. Beim Standardmultiplikator entspricht dies genau einer Einheit der letzten Dezimalstelle, die Sie geschrieben haben.

; 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

Der Vergleich ist inklusiv: Eine Differenz, die exakt der Toleranz entspricht, besteht die Prüfung. Im zweiten Beispiel gelten alle Salden von $4,26$ bis $4,28$ als bestanden, während 4.2801 mit Balance failed for 'Assets:Fund': expected 4.27 RGAGX != accumulated 4.2801 RGAGX (0.0101 too much) fehlschlägt.

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

Explizite Toleranzen

Wenn die abgeleitete Toleranz nicht geeignet ist, können Sie eine explizit mit der Tilde (~) angeben. Dies ist die einzige explizite Toleranzsyntax in Beancount und funktioniert nur bei balance-Anweisungen — eine Tilde innerhalb einer Transaktionsbuchung ist ein Syntaxfehler.

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

Erhöhen Sie den Bestand auf 4.2811, schlägt dieselbe Prüfung um 0.0101 fehl.

Rundungsverwaltung

Kleine Restbeträge aus Kosten- und Preisberechnungen sind normal. Was Beancount damit macht, ist enger gefasst, als es aussieht.

Verfolgung von Rundungsfehlern

Die Option account_rounding benennt ein Konto, das Restbeträge aufnehmen soll. Sie nimmt einen vollständigen Kontonamen und wird genau so gespeichert, wie Sie sie schreiben — ohne Eigenkapital-Präfix, im Gegensatz zu den Eigenkapitalkonto-Optionen.

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 dieser Transaktion ergibt $1.245 \times 43.23 = 53.82135$. Die Transaktion ist um $-0.00135$ USD unausgeglichen, was innerhalb der abgeleiteten Toleranz von 0.005 USD liegt, also wird sie geladen.

Unter Beancount 3.2.3 wird nichts auf Equity:Rounding gebucht. Die Option wird geparst und gespeichert, aber kein Schritt des Laders fügt die Restbuchung ein. Das Konto endet bei Null, und ein Restbetrag, der außerhalb der Toleranz liegt, bleibt ein Fehler, anstatt aufgefangen zu werden:

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

Behandeln Sie account_rounding daher auf dieser Version als inert. Wenn Sie einen Restbetrag aufzeichnen möchten, anstatt ihn zu tolerieren, schreiben Sie die dritte Buchung selbst:

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

Diese Version gleicht exakt auf Null aus, und der „Staub" ist auf einem Konto sichtbar, über das Sie berichten können.

Abgeleitete Zahlenpräzision

Beancount rundet die von Ihnen geschriebenen Zahlen nicht. Es gibt keine Option default_tolerance — sie existiert nicht und führt beim Laden zu Invalid option: 'default_tolerance' — und keine Einstellung quantisiert gespeicherte Beträge.

  1. Speicherung ist immer exakt. Schreiben Sie 53.82135 USD und das Hauptbuch speichert 53.82135 USD, unabhängig von Ihren Tolereanzeinstellungen. Toleranz entscheidet nur, ob eine Transaktion akzeptiert wird; sie ändert niemals eine Zahl.

  2. Anzeige ist eine separate Einstellung. display_precision legt fest, mit wie vielen Nachkommastellen eine Währung dargestellt wird, und ändert nichts am gespeicherten Wert oder an der Bilanzprüfung.

    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. Rundung ist Ihre Buchung, die Sie schreiben müssen. Wenn Sie den Restbetrag aus der Arithmetik entfernen möchten, runden Sie den Betrag in der Quelle und buchen Sie die Differenz explizit, wie im Beispiel mit drei Buchungen oben.

Implementierungsdetails

Einige technische Punkte verdeutlichen, wie Beancount diese Zuverlässigkeit erreicht.

  1. Zahlendarstellung: Beancount verwendet Pythons decimal-Modul, nicht Gleitkommazahlen. Der Standardkontext hat 28 signifikante Ziffern — Gesamtziffern, nicht Ziffern nach dem Komma — was die für Gleitkommazahlen typischen Binärdarstellungsfehler vermeidet.

  2. DisplayContext-Klasse: Diese interne Klasse übernimmt die gesamte Zahlenformatierung für Anzeigezwecke. Sie leitet die Präzision jeder Währung aus den Zahlen in Ihrer Datei ab, sofern nicht display_precision sie festlegt, und kann Ausgaben mit ausgerichteten Spalten und Kommas formatieren.

  3. Präzision vs. Toleranz: Es ist entscheidend, diese beiden Konzepte zu unterscheiden:

  • Präzision bezieht sich auf das Anzeigeformat einer Zahl (wie viele Dezimalstellen angezeigt werden).
  • Toleranz ist die zulässige Abweichung bei Prüfungen während der Verifizierung.

Best Practices ✨

Hier sind einige praktische Empfehlungen zur Verwaltung der Präzision in Ihrem Hauptbuch.

Ersteinrichtung

Für die meisten neuen Hauptbücher ist dies eine robuste Startkonfiguration:

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

Tipps zur Fehlerbehebung

Wenn Sie Bilanzierungsfehler feststellen:

  • Fügen Sie Dezimalstellen zum Betrag einer Buchung hinzu, um eine engere, genauere lokale Toleranzableitung zu erstellen.
  • Verwenden Sie explizite Toleranzen (~) bei balance-Prüfungen, die aufgrund vorhersehbarer Abweichungen fehlschlagen.
  • Buchen Sie den Restbetrag auf ein dediziertes Konto mit einer echten dritten Buchung, damit Sie darüber berichten können, wie oft dies vorkommt.
  • Erwägen Sie währungsabhängige Standardwerte, wenn Sie häufig mit Währungen mit unterschiedlichen Konventionen umgehen (z. B. JPY ohne Dezimalstellen).

Migrationsstrategie

Wenn Sie diese Konzepte auf ein bestehendes, unübersichtliches Hauptbuch anwenden:

  1. Beginnen Sie mit einer großzügigen globalen Toleranz (z. B. *:0.05) und einem höheren tolerance_multiplier, um die Datei zu validieren.
  2. Verschärfen Sie die Toleranzen schrittweise und beheben Sie die auftretenden Fehler.
  3. Fügen Sie explizite Ziffern zu Beträgen in problematischen Transaktionen hinzu, damit die Ableitung ihre Arbeit tun kann.
  4. Überwachen Sie den Saldo des Rundungskontos. Ein großer oder schnell wachsender Saldo kann auf ein systemisches Problem hinweisen, das untersucht werden muss.

Quelle: https://beancount.io/de/docs/Basics/precision