Balancing transactions with multiple commodities: floating point computations

An unbalanced transaction occurs due to rounding:

chris@MacBook_Air_M2_2022 finance % hledger reg -f investments.ledger
hledger: Error: /Users/chris/investments.ledger:95-102:
95 | 2024-08-16 ! Invested cash
   |     assets:inv:fidelity:ira    500.903 FDFIX @ $23.74
   |     assets:inv:fidelity:ira    126.478 FJTDX @ $10.04
   |     assets:inv:fidelity:ira    431.607 FITFX @ $13.73
   |     assets:inv:fidelity:ira     60.574 FLXSX @ $15.42
   |     assets:inv:fidelity:ira     55.964 FLAPX @ $17.90
   |     assets:inv:fidelity:ira     762.672 FIBUX @ $9.25
   |     assets:inv:fidelity:ira               $-28,077.76

This multi-commodity transaction is unbalanced.
The real postings' sum should be 0 but is: $0.00313
Consider adjusting this entry's amounts, adding missing postings,
or recording conversion price(s) with @, @@ or equity postings.

Check math with python script:

# Commodity Prices
FDFIX = 23.74
FJTDX = 10.04
FITFX = 13.73
FLXSX = 15.42
FLAPX = 17.90
FIBUX = 9.25
# Number of Shares
FDFIX_shares = 500.903
FJTDX_shares = 126.478 
FITFX_shares = 431.607
FLXSX_shares = 60.574
FLAPX_shares = 55.964
FIBUX_shares = 762.672
# Total cost of buying shares
T = FDFIX_shares * FDFIX + FJTDX_shares * FJTDX + FITFX_shares * FITFX + FLXSX_shares * FLXSX + FLAPX_shares * FLAPX + FIBUX_shares * FIBUX 
print(T)
chris@MacBook_Air_M2_2022 hledger_test % python3 test.py
28077.763130000003

Solution

As Simon has commented below, this problem was solved by setting the Commodity display style as follows:

# Commodity display style
commodity $1,000.00

Interestingly enough, hledger does not return an error when the transaction is isolated into a separate ledger file, even if there is not a commodity display style specified in the journal file.

The quantity of FIBUX in your python script doesn't match the ledger entry:

2 Likes

General remarks: I don't think you must be accurate to the nearest penny (smallest unit of currency); approximate accounting seems possible, in theory. But in practice I believe it's simplest to be exact; it makes reconciling and error checking easier and more certain.

In my experience (mainly hledger-centric, Beancount and Ledger also have their ways of handling decimals) decimal digits aren't too much of a problem; sometimes you need more of them in cost amounts, to make a transaction balance. And they don't accumulate to cause larger errors, in my experience. One reason is that we reconcile with real-world balances regularly, which makes any significant error visible immediately, signalling that some entry isn't correct.

Some related docs:

Thank you Simon,

Adjusting commodity display style to commodity $1,000.00 resolved the error.

Thank you, I revised the post above with a different example (that hopefully does not have typos).

Aha, you may have seen my first too hasty reply; beancounter diagnosed the first problem accurately (transposed digits). But yes, declaring the commodity’s display precision is sometimes needed in hledger. The variability is because without a declaration, it is inferred from the amounts in the journal, so a small example may behave differently from a real world journal. So indeed it’s good practice to just declare it.

1 Like

May I ask where the Haskell function that balances each double entry transaction is located?

balanceTransaction or journalBalanceTransactions in hledger-lib:Hledger.Data.Balancing.

PS FWIW I would like to change this to use only the precisions inferred from the local transaction, like Ledger; but haven't got around to it.

Thank you!