数値精度の管理は、複式簿記の基盤です。デジタル簿記、特に複数通貨、株価、端株を扱う場合、わずかな丸め誤差がすぐに厄介な残高不一致エラーにつながる可能性があります。Beancountは、精度の処理と許容可能なトレランスの設定のための、洗練されつつも直感的なシステムを提供します。このガイドでは、その仕組みについて説明します。⚙️
このページのすべての数値は、境界値も含めてBeancount 3.2.3で検証されています。各例では、どの残差が許容され、どれが1桁大きすぎるかを示しています。
中核となる精度の概念
Beancountの主な目標は、すべての取引がゼロでバランスすることです。しかし、価格や原価を含む計算では、記録するのが現実的でない小数位を持つ結果が生じることがよくあります。トレランスシステムにより、小さく許容可能な不均衡が可能になります。
自動トレランス推論
デフォルトでは、Beancountは各取引に必要なトレランスを自動的に推論します。この推論は、取引ごとに個別に処理され、関与する通貨ごとに個別に計算されます。
ルールは単純な掛け算です。通貨のトレランスは、その通貨のポスティング金額で見られる最小の桁に、デフォルトで0.5であるtolerance_multiplierオプションを掛けたものです。このデフォルトでは、トレランスは最後の有効数字の半分です。
例えば、次の購入を考えてみましょう:
2013-04-03 * "Buy Fund"
Assets:Fund 10.22626 FUND {37.61 USD}
Assets:Cash -384.61 USDBeancountは以下のようにトレランスを推論します:
FUND商品の場合、数値10.22626は小数第5位を持ちます。トレランスは最後の桁の半分なので、$0.00001 \div 2 = 0.000005$FUNDとなります。USD通貨の場合、数値-384.61は小数第2位を持ちます。トレランスは最後の桁の半分なので、$0.01 \div 2 = 0.005$USDとなります。
キャッシュレッグがトレランスの測定対象となります:10.22626 × 37.61は384.6096386なので、この取引はゼロに対して0.0003614 USD不足しており、読み込みが成功します。キャッシュレッグを-384.60に丸めると、差は0.0096386 USDとなり、0.005のトレランスを超えるため、BeancountはTransaction does not balanceを報告します。
取引ウェイトのルール
取引のバランスをチェックする際、Beancountは各ポスティングの「ウェイト」を計算します。この計算のルールは以下の通りです:
- 単純な金額:ポスティングに金額のみがある場合(例:
Assets:Cash -100.00 USD)、そのウェイトはその正確な金額です。 - 価格ポスティング:ポスティングに単価がある場合(例:
10 FUND @ 38.46 USD)、そのウェイトはamount × priceです。 - 単価原価:単一の中括弧は1単位の原価を保持するため、
10 FUND {384.61 USD}のウェイトは10 × 384.61 = 3,846.10 USDであり、384.61 USDではありません。 - 総原価:二重の中括弧はポスティング全体の原価を保持するため、
10 FUND {{384.61 USD}}のウェイトは384.61 USDです。Beancountはロットを保存する際に、これを38.461 USDの単価原価に変換します。 - 原価と価格:ポスティングに原価と単価の両方がある場合(例:
10 FUND {384.61 USD} @ 400.00 USD)、バランス計算には原価のみが使用されます。価格は報告用に記録され、計算には使用されません。
ルール3と4は、人々が半日を費やす原因となるルールなので、ここに読み込みが成功するファイルで並べて示します:
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この口座は、2つのロットに20 FUNDで終わり、それらの間の原価ベースは4,230.71 USDです。
精度推論ルール
自動推論システムは、いくつかの特定のルールに従います:
- 数値形式
- 整数金額(例:
10 USD)は、精度推論に寄与しません。 - 小数点以下1桁は、金額が暗示できる最も粗い精度です:
0.1 × 0.5 = 0.05単位。それを超える場合は、tolerance_multiplierまたは通貨別のデフォルトが必要です(両方とも後述)。 - 原価と価格(例:
{37.61 USD})は、デフォルトではトレランス推論から除外されます。ポスティングの主要な金額のみが使用されます。 - 同じ通貨のポスティングが異なる精度を持つ場合(例:
-10.10 USDと5.123 USD)、Beancountは最も粗い(最大の)トレランスを使用します。この場合、-10.10 USDに基づき、$0.005$USDのトレランスとなります。
-
デフォルト処理 取引に推論元となる小数位の数値がない場合、グローバルまたは通貨別のデフォルトトレランスを設定できます。
; 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" -
トレランス乗数 オプションは
tolerance_multiplierで、これは許容可能とみなされる最小桁の割合であり、上乗せされるパーセンテージではありません。デフォルトは0.5なので、1.2に設定してもチェックが20%緩むわけではありません。推論されるすべてのトレランスがデフォルトの2.4倍になるだけです。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古い名前の
inferred_tolerance_multiplierは同じ値を設定しますが、Renamed to 'tolerance_multiplier'.というロードエラーを報告します。 -
原価ベースの推論 通常、原価はトレランス推論では無視されますが、Beancountにそれらを使用するように指示できます。これは、最終金額(例:現金の引き出し)が取引内で最も精度の高い数値である場合に役立ちます。
option "infer_tolerance_from_cost" "TRUE"
以下は、オプションを一切設定しない素のデフォルトが、ちょうど境界にある場合です:
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残高照合
残高照合(balance)は、特定の日付において口座の残高が既知の値と一致することを検証するために使用されます。これらにも関連するトレランスがあります。
基本形式
balance照合のトレランスは、金額の小数位数から推論されますが、取引内で使用されるものの2倍寛大です:tolerance_multiplier × 2 × the smallest digit。デフォルトの乗数では、これは書いた最後の小数位のちょうど1単位です。
; 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比較は包括的です:差がトレランスと正確に等しい場合でも合格します。2番目の例では、$4.26$から$4.28$までの残高はチェックに合格し、4.2801は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明示的なトレランス
推論されたトレランスが適切でない場合、チルダ(~)文字を使用して明示的に指定できます。これはBeancountが持つ唯一の明示的トレランス構文であり、balanceディレクティブでのみ機能します。取引ポスティング内のチルダは構文エラーです。
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保有額を4.2811に増やすと、同じ照合は0.0101の差で失敗します。
丸め管理
原価と価格の計算による小さな残差は正常です。Beancountがそれらに対して行うことは、見た目よりも限定的です。
丸め誤差の追跡
account_roundingオプションは、残差を吸収するための口座を指定します。完全な口座名を取り、equity口座オプションとは異なり、equityプレフィックスは追加されず、書かれたとおりに正確に保存されます。
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この取引では、です。取引は$-0.00135$ USDの不均衡があり、これは推論された0.005 USDトレランス内なので、読み込みが成功します。
Beancount 3.2.3では、Equity:Roundingには何も転記されません。 オプションは解析され保存されますが、ローダーのどの段階でも残差ポスティングが挿入されないため、口座はゼロのままであり、トレランス外の残差は吸収されるのではなく、依然としてエラーとなります:
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したがって、このバージョンではaccount_roundingは無効であるとみなしてください。残差を許容するのではなく記録したい場合は、3番目のポスティングを自分で書いてください:
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そのバージョンは正確にゼロでバランスし、端数は報告可能な口座に表示されます。
推論された数値精度
Beancountは、あなたが書いた数値を丸めません。default_toleranceオプションは存在せず、Invalid option: 'default_tolerance'というエラーで読み込みに失敗し、保存された金額を量子化する設定もありません。
-
保存は常に正確です。
53.82135 USDと書けば、トレランス設定に関係なく、台帳は53.82135 USDを保持します。トレランスは取引が受け入れられるかどうかを決定します。数値を編集することは決してありません。 -
表示は別の設定です。
display_precisionは、通貨が表示される小数桁数を固定しますが、保存された値や残高チェックには何も変更しません。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ポスティングの例のように、差額を明示的に計上してください。
実装の詳細
いくつかの技術的なポイントが、Beancountがこの信頼性をどのように達成しているかを明確にします。
-
数値表現:BeancountはPythonの
decimalモジュールを使用しており、浮動小数点数ではありません。デフォルトのコンテキストは28桁の有効数字(小数点以下の桁数ではなく、総桁数)を持ち、浮動小数点数に共通する2進表現エラーを回避します。 -
DisplayContextクラス:この内部クラスは、表示目的のすべての数値形式を処理します。
display_precisionで固定されていない限り、ファイル内の数値から各通貨の精度を推論し、整列した列とカンマで出力をフォーマットできます。 -
精度とトレランス:これら2つの概念を区別することが重要です:
- 精度は、数値の_表示形式_(表示される小数位数)に関係します。
- トレランスは、検証チェック中に使用される_不均衡の許容値_です。
ベストプラクティス ✨
台帳の精度を管理するための実用的な推奨事項をいくつか紹介します。
初期設定
ほとんどの新しい台帳では、これが堅牢な開始構成です:
; 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"トラブルシューティングのヒント
残高不一致エラーが発生した場合:
- ポスティングの金額に小数桁を追加して、より厳密で正確なローカルトレランス推論を作成します。
- 予測可能な差異により失敗する
balance照合には、明示的なトレランス(~)を使用します。 - 実際の3番目のポスティングで残差を専用口座に計上し、発生頻度を報告できるようにします。
- 異なる慣習を持つ通貨(例:JPYには小数がない)を頻繁に扱う場合は、通貨別のデフォルトの設定を検討してください。
移行戦略
これらの概念を既存の複雑な台帳に適用する場合:
- 寛大なグローバルトレランス(例:
*:0.05)と高いtolerance_multiplierから始めて、ファイルが検証に合格するようにします。 - 徐々にトレランスを厳しくし、表示されるエラーを修正します。
- 問題のある取引の金額に明示的な桁を追加して、推論が機能するようにします。
- 丸め口座の残高を監視します。残高が大きい、または急速に増加している場合は、調査が必要な体系的な問題を示している可能性があります。