TL;DR: There are various rounding-related issues in ledger. This
email (a) shows the problems we've encountered, (b) proposes some
ideas for new directives that might be useful, and c) ends with some
open questions for discussion on this list.
Just imagine a conversation with your accounting. "So you spent
$27.54 and then you spent $27.54, but your total expenses are $55.09?
Why is that?" "Oh, you know, the expenses weren't really $27.54...
they were actually $27.543834". No, they weren't. In practice, each
expense was $27.54.
One final example: you buy 123 shares for 1.23456 each and later sell
them for 2.345 each. So first of all, how much money do you get?
123*2.345 = 288.435. But do you really get that amount? No, you get
288.44 if you're lucky (or 288.43 if you're not).
And how much capital gain was there? Again, the maths is simple:
123*(2.345-1.23456) = 136.58412
But are you really going to tell your tax authority that you made a
gain of 136.58412? No, you're going to tell them 136.58 (or 136.59).
Here's this example in ledger:
D 1000.00 EUR
2014-06-01 * Opening balance
Assets:Investments 123 XXX {1.23456 EUR} @ 1.23456 EUR
Equity:Opening balances -151.85 EUR
2014-06-22 * Sell some shares
Assets:Investments -123 XXX {1.23456 EUR} @ 2.345 EUR
Assets:Cash 288.44 EUR
Income:Capital gains -136.59 EUR
This balances and works fine. But if you change "D 1000.00 EUR" to
"D 1000.000 EUR", suddenly it won't balance:
Unbalanced remainder is:
-0.001 EUR
Amount to balance against:
288.440 EUR
This shouldn't happen. The display precision shouldn't affect the
calculation: as you can see from these examples, the extra ledger
keeps internally is causing lots of problems in real life.
Now it's great that ledger can keep precision if there's a need, but
in most real-life scenarios, we want to round to 2 digits of
precision.
Possible solution
-----------------
We could have a "commodity" directive called "precision" (or something
similar):
commodity EUR
precision 2
This would tell ledger to round to 2 digits of precision. This
rounding is done internally, i.e. ledger wouldn't keep more detail
than that precision.
But is this enough? I think we also need the ability to specify *how*
rounding is done? In most cases, it's probably just rounded properly,
i.e. 0.005+ is rounded up, everything below is rounded down.
But I can imagine that some companies just truncate: so even if you
should get 1.126, they would give you 1.12 instead of 1.23. (I don't
actually know if this is true, but it might be.)
And maybe there should be one option to make rounding "flexible" or
"permissive": in the example above, ledger would accept both 1.12 and
1.13 for the balancing check, so both of these examples would work:
2014-06-23 * Test 1
A 1 AAA @ $1.126
B $1.13
2014-06-23 * Test 2
A 1 AAA @ $1.126
B $1.12
I guess these are actually two separate questions: how should ledger do
rounding if nothing is specified for B:
2014-06-23 * Test 2
A 1 AAA @ $1.126
B
Is B $1.13 or $1.12?
And the second: what should ledger accept for the balancing check if
the user specifies a value as B (i.e. so either of the example above
is accepted).
Anyway, we could have something like (and possibly another directive
to say how "permissive" balancing should be):
commodity EUR
precision 2
rounding truncate
While precision is probably the same for one currency regardless of
the account, you should be able to specify the type of rounding for
each account (one bank may round properly while another may always
truncate):
account Foo
rounding truncate
account Bar
rounding round
In addition to these commodity/account directives, it might also be useful
to specify rounding on the command line to force it once. I'm not sure if
there's a use case for this.
Open questions
--------------
* Do we actually need to specify the precision? After all, ledger
keeps track of the precision you use in your input data (and by
default uses that for the display precision). Maybe we should just
use that precision as the precision for rounding if rounding is
enabled. In other words, we wouldn't need the "precision" directive
-- "rounding" would be enough. I cannot think of any use cases where
this wouldn't work, but maybe I'm missing something. (See below for
rounding to 1000s)
* If we do implement a "precision" directive and someone says
"precision 2", what would happen if someone then wrote a value like
10.123. Should this be rejected or should it override precision?
* What about currencies like CAN and AUD which have phased out the
cent coin? The smallest coin is 5c. I *assume*, bank and investment
accounts still show single cents, but I'm not sure (Martin Blais, can
you comment?).
* Does rounding only happen after the decimal point or are there
currencies where 600 would be rounded to 1000? (I'm thinking of
currencies where one million or billion is worth $1 or so). The
proposal assumes that rounding only happens after the decimal point
but maybe this assumption is wrong.
* Does anyone know how banks/currency exchanges/investment companies
actually do rounding in real life? Do they round correctly or do some
truncate?
* How does Bitcoin work? I know Bitcoin is divisible. So would they
want no rounding?
commodity BTC
rounding off (or precision 0)
Are there use cases where Bitcoin would be rounded?
* How do other accounting packages handle rounding?
* In the proposal above, I've assumed that users can specify rounding
(e.g. "truncate") as a directive. Another way to implement it would
be to allow users to specify a function that does the rounding: this
would be more powerful, but otoh, it makes things more difficult to
use (and probably to implement). And with a function, how do we know
what should balance?
--
Martin Michlmayr
http://www.cyrius.com/
--
---
You received this message because you are subscribed to the Google Groups "Ledger" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ledger-cli+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
--- You received this message because you are subscribed to the Google Groups "Ledger" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ledger-cli+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
D 1000.00 EUR commodity EUR precision 2 default 2012-01-01 * A 2 AAA @ 10.121 EUR A 1 BBB @ 20.121 EUR C Mention precision followed by an integer under commodity directive. This integer is the number of places after decimal to keep. +1 means one place after decimal and -1 means one place before decimal. '0' means round to no decimal places. It does not affect display precision. So if display precision is 3 then amount will be displayed to 3 decimal places however they are rounded to 2 decimal places if you have set precision to 2.
Example result
40.36 EUR A
-40.36 EUR C
--------------------
0
Known issues:
1. Cannot set rounding mode or truncate.
2. Display precision is not changed.
3. Range of integer is not checked.
Please review. Suggest new tests.
D 1000.00 EUR commodity EUR precision 2 default 2012-01-01 * A 3.69 EUR C -269 Boots @@ 3.69 EURBut this new patch solve that problem too. The patch passes current test suite. Few other tests from this post examples are
I have prepared a patch which adds precision feature to commodity directive. I have also attached some tests. It works as follows
D 1000.00 EUR commodity EUR precision 2 default 2012-01-01 * A 2 AAA @ 10.122 EUR A 1 BBB @ 20.123 EUR C Mention precision followed by an integer under commodity directive. This integer is the number of places after decimal to keep. +1 means one place after decimal and -1 means one place before decimal. '0' means round to no decimal places. It does not affect display precision. So if display precision is 3 then amount will be displayed to 3 decimal places however they are rounded to 2 decimal places if you have set precision to 2.
I think the question should be phrased in terms of what precision you want to represent numbers _internally_. Isn't a worthy goal to attempt to replicate the same precision as the real-world accounts they represent?
Rounding should occur at the point of calculating the "balance amount", that is, the amount that is used to balance against the rest of the postings of the transaction.
Thank you for posting this. Your second example explains weirdness that I was seeing but couldn't comprehend.So to put it succinctly, ledger balances as long as the rounded amounts balance. Perhaps I don't understand all of the nuances, but I have to say that I don't like that. If a transaction is off by a fraction of a cent, then that *does not balance* for me!
I am in the same boat as you where my bank reports one thing, but is actually stealing fractions of currency.
I haven't decided whether or not having an elided expense account or simply reporting the exchange rate that was actually used is better.
One of the things that makes me think an elided expense account is better is that the price history seems to be dependent upon transaction order. I am currently storing the price db in a separate file, so I'm never sure whether ledger will use the price that something traded at, or the price I put in the price db. For various reasons, it is important to me that it is the latter.
Anyway add me as a vote for balancing at full precision. I only want rounding to occur in reporting.
On Mon, Oct 6, 2014 at 5:20 PM, Mike Charlton <mike...@gmail.com> wrote:Thank you for posting this. Your second example explains weirdness that I was seeing but couldn't comprehend.So to put it succinctly, ledger balances as long as the rounded amounts balance. Perhaps I don't understand all of the nuances, but I have to say that I don't like that. If a transaction is off by a fraction of a cent, then that *does not balance* for me!Then you will never be able to balance anything that has a price or a cost. There has to be rounding somewhere.
I haven't decided whether or not having an elided expense account or simply reporting the exchange rate that was actually used is better.Note that an elided expense account effectively disables any balance checks.This is not a solution.
Correction: ledger reg liabilities.Yes, thanks.
On Oct 6, 2014, at 3:58 PM, Martin Blais <bl...@furius.ca> wrote:On Mon, Oct 6, 2014 at 5:20 PM, Mike Charlton <mike...@gmail.com> wrote:Thank you for posting this. Your second example explains weirdness that I was seeing but couldn't comprehend.So to put it succinctly, ledger balances as long as the rounded amounts balance. Perhaps I don't understand all of the nuances, but I have to say that I don't like that. If a transaction is off by a fraction of a cent, then that *does not balance* for me!Then you will never be able to balance anything that has a price or a cost. There has to be rounding somewhere.Strictly speaking, the price-adjustment option would be possible in ledger, which uses rational numbers as internal representation, allowing for exact balancing. But ledger is ill-equipped to solve this problem for other reasons, for example, its lack of lot matching.
Beancount could solve this by attaching a total cost to each inventory, in addition to a per-share cost (with the stipulation that | total cost - number * share cost | < tolerance).
You still have to face the fact, however, that if you have three shares worth $0.333 each, with a total cost to you of $1.00, and you sell these one at a time at the same price but you receive $0.33 each, even though their price has not changed, there is a penny that has disappeared. Is that an expense due to rounding or a capital loss? Again, it doesn’t really matter, but I feel like I should pick one.
I haven't decided whether or not having an elided expense account or simply reporting the exchange rate that was actually used is better.Note that an elided expense account effectively disables any balance checks.This is not a solution.If it is built into the system, the elided expense account can also balance check. That is, insert a ‘Expenses:Rounding’ transaction, but only if the difference is less than some tolerance (and only if there is a price/cost)The question is whether Expenses:Rounding is better than having the ability to run a report listing all transactions which don’t quite balance, so you feel good about your overall balance being out of whack.
Simon Michael:Correction: ledger reg liabilities.Yes, thanks.Nathan
--
Sorry to bring up an old topic. I followed this with some interest back when we discussed it, but my own thoughts hadn’t yet crystalized, so I was silent. Now I have thoughts to share.Martin Blais:I think the question should be phrased in terms of what precision you want to represent numbers _internally_. Isn't a worthy goal to attempt to replicate the same precision as the real-world accounts they represent?I agree. To me, display precision is much less important than the internal representation.
Martin again:Rounding should occur at the point of calculating the "balance amount", that is, the amount that is used to balance against the rest of the postings of the transaction.I agree again, but disagree with how this is implemented in both ledger and beancount (I am not familiar with hledger). In particular, I think that the only way to avoid strange situations is for every transaction to balance exactly, as in A+B=0 to the full precision of the internal representation.Let’s ignore the concept of elided values and start by thinking only about the whether a transaction balances.There are two cases.Case 1: Single currency. Postings should balance exactly.2014-01-01 * transferAssets:Bank1 $1.00Assets:Bank2 $-1.001It seems pretty obvious that this should trigger an error. Ledger gets this right because it requires all transactions to balance to the maximum precision specified in the document (not quite, we’ll get to this later). Beancount, with its fixed tolerance, lets this through (Even $-1.01 would pass). Even if the tolerance is exposed as an option, you give the user the opportunity to screw things up.
Case 2: Multiple currencies.2014-01-01 * buy AAA
Assets:Investments 2 AAA @ 10.1234 EUR
Assets:Bank -20.25 EURThe 2 AAA and the -20.25 EUR are actual amounts. The quoted rate may have been 10.1234, but the truth is, you bought at 10.125 EUR (sucker!)Note that this need to adjust price due to rounding does not arise solely from overprecise pricing, but also when you purchase fractional shares.2014-01-01 * buy VPMCXAssets:Investments 9.66 VPMCX @ 103.52 USDAssets:Bank 1000.00 USDAgain, the $1000 and the 9.66 shares are exact quantities. They do not represent something that has been rounded. You do not have an extra, invisible 0.0032 USD lying around. It is the $103.52 that is wrong.
So one solution is to simply record the share price you paid instead of the share price you were quoted. This is the same as Martin Machlmayr’s suggestion to use ‘@@' instead of ‘@'. But as Martin also pointed out, there is value in using ‘@‘, namely, your cost basis can match the bank’s report, the pricedb gets an accurate market price, and you get an extra check that you haven’t mistyped a number.A different solution is for the program to replace the cost basis that you specify with the true cost, as long as it is close enough. But this doesn’t solve the problem that you might want to attach the bank-reported cost to the posting, or see it in reports.
One final solution, and what I have settled on for now, is a rounding account. I insert one manually (with elided amount), but the program could easily do this for you. It sweeps away whatever is left over, and has accumulated 0.12 USD (in expenses) for me so far this year. The alternative is that my double-entry accounting ledger is out of balance by 12 cents per year. (Maybe not a big deal, but it bugs me.)The point is, no matter how you decide to round the amounts, you have to account for the rounding in some way or accept the fact that your system is out of balance.Note that either of these automated solutions could probably be implemented using beancount plugins.
Eliding postingsOnce you have what it means to balance figured out, it is easy to complete a transaction with an elided posting. Simply choose the value that minimizes the error. (And then either roll the error into an adjusted price or add a rounding transaction.) Obviously, to do this, you would need rules about precision, per currency and possibly per account. If you really want to, you can have custom rounding rules.In my experience, though, you will often still get the number wrong, because at the end of the day, the bank will do what it does for its own reasons. (In my 401k, for example, I contribute a fixed $X to buy Y fractional shares at price Z, but X - Y*Z has been as big as 2.5 cents because strictly speaking the bank is setting Y = round(X/Z, 3), even though I would never think of it that way, or record it that way in my ledger.)
I like the idea that the tolerance can be inferred automatically from the smallest digit in the input instead of using a fixed value. I should implement that in Beancount as well, there's no reason it couldn't do it.This should be a very easy change to make.At the moment Beancount uses "0.005" regardless of unit... this is a known pending issue (I don't have any Japanese users yet I suppose). I've been thinking that a per-commodity tolerance could be selected instead, i.e., a map of commodity to tolerance value, but while better, this is also unsatisfying.I wonder if inferring like Ledger works in all cases though; consider this case:Assets:Fidelity:Fund 11.27534 FUND {1.2357 USD}Assets:Fidelity:Cash -13.93 USDThe precise cash amount is 13.932937638 USD.How many digits should this use?
The debited cash is 13.93, this would be the _actual_ amount debited by the fund manager, so it can't be anything else.In this example, I would argue that you would want the _minimum_ number of digits to be used.(But certainly not the minimum if you're using an integer number of shares...)If we can come up with a rule that works well in all cases I'll implement it.
Case 2: Multiple currencies.2014-01-01 * buy AAA
Assets:Investments 2 AAA @ 10.1234 EUR
Assets:Bank -20.25 EURThe 2 AAA and the -20.25 EUR are actual amounts. The quoted rate may have been 10.1234, but the truth is, you bought at 10.125 EUR (sucker!)Note that this need to adjust price due to rounding does not arise solely from overprecise pricing, but also when you purchase fractional shares.2014-01-01 * buy VPMCXAssets:Investments 9.66 VPMCX @ 103.52 USDAssets:Bank 1000.00 USDAgain, the $1000 and the 9.66 shares are exact quantities. They do not represent something that has been rounded. You do not have an extra, invisible 0.0032 USD lying around. It is the $103.52 that is wrong.But is it wrong, really? Is the cost basis calculated with 103.52 USD or 103.519668737 USD?Correct or not, I think the bank and govt assume a cost basis calculated using 103.52 USD, so that's the correct one.Also, when you calculate your capital gains later, which of the prices should you use?Again, I think 103.52 is what you're supposed to use.
So one solution is to simply record the share price you paid instead of the share price you were quoted. This is the same as Martin Machlmayr’s suggestion to use ‘@@' instead of ‘@'. But as Martin also pointed out, there is value in using ‘@‘, namely, your cost basis can match the bank’s report, the pricedb gets an accurate market price, and you get an extra check that you haven’t mistyped a number.A different solution is for the program to replace the cost basis that you specify with the true cost, as long as it is close enough. But this doesn’t solve the problem that you might want to attach the bank-reported cost to the posting, or see it in reports.Both of these worry me, as per my other email, because that propagates cost basis that is unexpectedly exact. I'd rather use the rounded, inexactly calculated cost basis.
On Oct 6, 2014, at 8:30 PM, Martin Blais <bl...@furius.ca> wrote:I like the idea that the tolerance can be inferred automatically from the smallest digit in the input instead of using a fixed value. I should implement that in Beancount as well, there's no reason it couldn't do it.This should be a very easy change to make.At the moment Beancount uses "0.005" regardless of unit... this is a known pending issue (I don't have any Japanese users yet I suppose). I've been thinking that a per-commodity tolerance could be selected instead, i.e., a map of commodity to tolerance value, but while better, this is also unsatisfying.I wonder if inferring like Ledger works in all cases though; consider this case:Assets:Fidelity:Fund 11.27534 FUND {1.2357 USD}Assets:Fidelity:Cash -13.93 USDThe precise cash amount is 13.932937638 USD.How many digits should this use?Ledger would infer 2 digits of precision for USD and 5 for FUND. The price portion does not affect precision. Ledger also has a D directive to specify the minimum precision per currency.
The debited cash is 13.93, this would be the _actual_ amount debited by the fund manager, so it can't be anything else.In this example, I would argue that you would want the _minimum_ number of digits to be used.(But certainly not the minimum if you're using an integer number of shares...)If we can come up with a rule that works well in all cases I'll implement it.To be honest, I would prefer to explicitly specify precision, although this makes it harder to work correctly "out of the box".
On Mon, Oct 6, 2014 at 10:57 PM, Nathan Grigg <nat...@nathangrigg.net> wrote:On Oct 6, 2014, at 8:30 PM, Martin Blais <bl...@furius.ca> wrote:I like the idea that the tolerance can be inferred automatically from the smallest digit in the input instead of using a fixed value. I should implement that in Beancount as well, there's no reason it couldn't do it.This should be a very easy change to make.At the moment Beancount uses "0.005" regardless of unit... this is a known pending issue (I don't have any Japanese users yet I suppose). I've been thinking that a per-commodity tolerance could be selected instead, i.e., a map of commodity to tolerance value, but while better, this is also unsatisfying.I wonder if inferring like Ledger works in all cases though; consider this case:Assets:Fidelity:Fund 11.27534 FUND {1.2357 USD}Assets:Fidelity:Cash -13.93 USDThe precise cash amount is 13.932937638 USD.How many digits should this use?Ledger would infer 2 digits of precision for USD and 5 for FUND. The price portion does not affect precision. Ledger also has a D directive to specify the minimum precision per currency.I'm not sure what that means... there is no need for any precision on units of FUND, the balance amount of that leg is in USD calculated as 11.27534 x 1.2357. In USD. The balance residual is 11.27534 x 1.2357 + -13.93. The only precision that matters is that of USD.Does it use the precision inferred on units of FUND in any way?If so, how?
Which brings up an interesting option for inferring the precision: the maximum number of digits from all legs without cost/price conversions. And this should be calculated for each group of postings whose "balance amounts" are in a common commodity. That sounds right to me.The debited cash is 13.93, this would be the _actual_ amount debited by the fund manager, so it can't be anything else.In this example, I would argue that you would want the _minimum_ number of digits to be used.(But certainly not the minimum if you're using an integer number of shares...)If we can come up with a rule that works well in all cases I'll implement it.To be honest, I would prefer to explicitly specify precision, although this makes it harder to work correctly "out of the box".Why? Because you fear not having a minimum will create pathological cases where a user will insert a balance against an integer number of dollars and make a mistake on the cost that will go undetected?2014-09-09 *Assets:Invesments ...Assets:Cash -1000 USDJust curious. What cases do you see as problematic?