proper handling of rouding and tolerance in the code

99 views
Skip to first unread message

siri...@gmail.com

unread,
May 26, 2017, 9:21:48 AM5/26/17
to Beancount
Ciao Martin,

I'm building some reporting/form generation on top of Beancount, and have a question about how best to handle rounding/tolerance in the code.

The problem I'm trying to solve is to safely assert invariants which will detect accounting errors.

Let me give an example:


from beancount.query.query import run_query
from beancount.query.query_render import render_text
from beancount.core.amount import Amount, mul as amount_mul
from beancount.core.number import Decimal


def total_query_(entries, options, currency, query_string):
   
'''Runs a query, then returns the last value (or 0 if no result) as an Amount
    '''

    qy_types
, qy_rows = run_query(entries, options, query_string)
   
if len(qy_rows):
       
return qy_rows[-1].total.get_units(currency)
   
else:
       
return Amount(Decimal('0.00'), currency)


taxable_earnings
= total_query_(entries, options, currency,
   
'''SELECT sum(position) as total
    WHERE account ~ "EarningsBlahBlah"
    '''
.format(fiscal_quarter.query())
   
)

tax_owed
= total_query_(entries, options, currency,
   
'''SELECT sum(position) as total
    WHERE account ~ "SpecificTaxAccount"
    '''
.format(fiscal_quarter.query())
   
)

tax_rate
= Decimal('0.07')
currency
= 'WHATEVER'

# invariant: bookings to SpecificTaxAccount must equal EarningsBlahBlah times the tax rate
assert amount_mul(taxable_earnings, tax_rate) == tax_owed, (
   
'taxable earnings {0} * {1} = {2} != tax_owed {3}'.format(
            taxable_earnings
, tax_rate,
           
amount_mul(taxable_earnings, tax_rate),
            tax_owed
)
   
)

This might fail because 1.4188 != 1.42
However, accounting errors might create a (perfectly valid, error-free) ledger where there is much more discrepancy between the figures - and I need to catch that.

As a note, I'm running with:

option  "inferred_tolerance_default"    "*:0.005"

It's not immediately obvious to me how to best plug into Beancount's "standard" rounding approach (and TBH I don't fully understand that approach yet :P)

I could of course just hack something like this:

assert round(amount_mul(taxable_earnings, tax_rate).number, 2) == round(tax_owed.number, 2)

... but that's so ugly it hurts and I'm also hard-coding a rounding value disregarding beancount's tolerance figures.

Any advice is appreciated.

Thank you and all the best,

Sirio

Martin Blais

unread,
May 26, 2017, 1:53:39 PM5/26/17
to Beancount
Hi Sirio,
IIRC the SQL client doesn't use nor respect the functionality implemented by DisplayContext:
I want to rewrite the SQL client entirely at some point, and I figured I'd leave that for the rewrite.

Read this doc for info on tolerances:

Also, reading the source code of the DisplayContext and its related classes should provide a good overview of how that's all implemented:





--
You received this message because you are subscribed to the Google Groups "Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beancount+unsubscribe@googlegroups.com.
To post to this group, send email to bean...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beancount/2ddc2c79-952c-448d-ad95-ddf6490b9b2a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

siri...@gmail.com

unread,
May 26, 2017, 10:56:59 PM5/26/17
to Beancount
Thank you sir; understood.
Really appreciate the help :)

best,

Sirio
To unsubscribe from this group and stop receiving emails from it, send an email to beancount+...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages