Do you know how transaction balance checking works in the various PTA apps, exactly ? I didn't. Here's my understanding, perhaps incomplete. Last updated 2025-06-10. When checking if transactions are balanced, PTA apps allow some tolerance. Why ? One reason is that when recording unit costs (eg with `@`), it's not always possible to record costs exactly, because their decimal digits may be repeating, too many, unknown, or rounded strangely by financial institutions. Below is a simplified example of this. Note when checking this entry, the AAA amount will be converted to USD, then the USD amounts are summed, expecting a total that's "close enough to zero". Each of ledger, hledger, beancount handles this in a different way: ### Ledger ```ledger 2025-01-01 Assets:A 1 AAA @ 1.005 USD Assets:B -1.00 USD ``` Ledger infers a local balance-checking precision (number of decimal places to consider, for USD, in this transaction) from the transaction's posting amounts (ignoring cost amounts). Here there's one USD posting amount, with two decimal places, so the balance-checking precision for USD in this transaction is 2, and so the sum of 0.005 USD is considered close enough to zero: ``` $ ledger print >/dev/null $ ``` "Close enough" means not more than half of the last decimal place considered. So if we increased that cost to 1.006 USD, the 0.006 USD sum would not be close enough. Also if we added one more decimal zero to the Assets:B amount (`-1.000 USD`), the balance-checking precision would be 3, making the 0.005 sum not close enough. If Ledger can't infer a display or balance-checking precision for a commodity (because it only appears in cost amounts and nowhere else), it uses precision 0, ie rounded to integers. ### hledger ```hledger 2025-01-01 Assets:A 1 AAA @ 1.005 USD Assets:B -1.00 USD ``` hledger infers a global display precision for USD from (1) all posting and `P` amounts in the journal, (2) `commodity` directives in the journal, and (3) any `-c/--commodity-style` options on the command line. And it reuses this as the balance-checking precision for USD in all transactions. Here the balancing precision 2 is inferred from the Assets:B posting amount, and the entry is accepted: ``` $ hledger check $ ``` As with Ledger, increasing the cost to 1.006, or adding one more zero to the Assets:B amount, would cause this to fail. A `commodity` directive can be used to override the precision inferred from amounts, eg allowing the following entry to be accepted: ```hledger commodity 1.00 USD 2025-01-01 Assets:A 1 AAA @ 1.005 USD Assets:B -1.000 USD ``` hledger docs recommend using `commodity` directives to declare each commodity's precision, to keep the display and balance-checking precisions stable and predictable, ie unaffected by other transactions and P directives elsewhere in the journal. If hledger can't infer a display/balance-checking precision for a commodity, it uses the natural precision of the calculated amounts, rounded to a maximum of 255 decimal places. ### Beancount Doc: [Beancount Precision & Tolerances](https://beancount.github.io/docs/precision_tolerances.html) ```beancount 2025-01-01 open Assets:A 2025-01-01 open Assets:B 2025-01-01 * Assets:A 1 AAA @ 1.005 USD Assets:B -1.00 USD ``` Beancount [infers a local balance-checking precision](https://beancount.github.io/docs/precision_tolerances.html#how-default-tolerances-are-determined) from the transaction's amounts, like Ledger, so this is accepted: ``` $ bean-check a.beancount $ ``` As with Ledger and hledger, increasing the cost to 1.006, or adding one more zero to the Assets:B amount, would cause this to fail. If Beancount can't infer a precision/tolerance for a commodity, it will use 0, ie rounded to integers. You can use the [inferred_tolerance_default](https://beancount.github.io/docs/precision_tolerances.html#configuration-for-default-tolerances) journal directive to increase the tolerance (reduce the balance-checking precision). Also you can set a tolerance other than half of the smallest decimal place. Eg: ```beancount option "inferred_tolerance_default" "USD:0.002" ``` Unlike hledger's directive, this one won't reduce the tolerance (increase the balance-checking precision). There is also a [tolerance_multiplier](https://beancount.github.io/docs/precision_tolerances.html#tolerance-multiplier) directive: ```beancount option "tolerance_multiplier" "0.6" ``` and a [infer_tolerance_from_cost](https://beancount.github.io/docs/precision_tolerances.html#infer-tolerance-from-cost) directive: ```beancount option "infer_tolerance_from_cost" "TRUE" ``` I don't understand their effects exactly. ## Issues With hledger: - hledger allows display precision to be controlled by a directive or a command line option. This is a good feature. However it conflates display precision and balance-checking precision, by using the same parameter for both. So if you want to increase the display precision temporarily, it may also disrupt balance-checking, making the journal unreadable. - hledger is quite dependent on commodity directives to make journal entries readable. Without them, - transaction balance-checking can be annoyingly dependent on transactions or P directives elsewhere in the journal (perhaps in another file). - it can reject some valid journals converted from Ledger or Beancount, if transaction balance-checking gets disrupted by more precise amounts seen elsewhere. This can be seen with `ledger print | hledger -f- check` or `bean-report FILE hledger | hledger -f- check` - hledger's commodity directives can mask inexact entries that wouldn't balance otherwise. Such entries become invalid if the directive is absent, eg if they were exported to Ledger or Beancount. - hledger could ignore P amounts when inferring precision, unless they are the only amounts in that commodity. (Since they usually have higher-than-normal precision, like cost amounts.) - hledger avoids inferring display precision from cost amounts, but it could safely infer other display attributes (like the symbol's side and spacing) from them, which would be useful when those are the only amounts in that commodity ## Proposals - Make hledger do balance-checking with local precisions, like Ledger and Beancount. Amounts in other transactions, P directives, commodity directives, and -c options no longer affect balance checking. See [#2402](https://github.com/simonmichael/hledger/pull/2402). - Problem: this reveals inexact entries which were previously masked by commodity directives, breaking the journal. - Solution ? Allow the local balance-checking precisions to be reduced by commodity directives, for backwards compatibility. Ie commodity directives could make balance checking less precise (only). If entries are exported without the commodity directive, they would be revealed as invalid, and that would be a time to fix them.