oTree Forum >

CurrencyField not rounded in first use but rounded in second use

#1 by BenG

Hi,

I observed some odd behavior when using a CurrencyField. I searched the forum but couldn't find a directly related post. 

I have a Stackelberg/Cournot game. I calculate the payoff for each participant in their 2-player group as price*quantity with price = 1-sum_quantities/1000.

class Group(BaseGroup):
    this_price = models.CurrencyField()

def set_payoffs(group):
    players = group.get_players()
    group.this_price = (1 - (sum(p.my_quantity for p in players) / 1000))
    if group.this_price < 0:
        group.this_price = 0
    for p in players:
        p.payoff = group.this_price * p.my_quantity

Now, the odd thing is that for the first player's payoff calculation it uses the float, not rounded price, while for the second player's payoff calculation it uses the price rounded to 2 digits. So, if
p1's my_quantity = 333
p2's my_quantity = 333
then
p1's payoff = 111.22 (0.334 * 333)
p2's payoff = 109.89 (0.33 * 333)

1) Is this a bug or a feature?
2) How do I get it to use 0.334 for both calculations? Should I create a float for this_price and only for displaying it create another display_price as CurrencyField?

Thanks and cheers

/ben

#2 by Chris_oTree

simplest way to avoid issues like this is to set your decimal places setting in settings.py to a higher number like 4, then when displaying values in the template use |to1 / |to2 filter to round it.

#3 by Chris_oTree

It could be related to field load/save. for example if i set a currency field player.foo = 0.12345, then its value will continue to be 0.12345, until the current request is finished (e.g. the page load finishes). then it is saved to the database as 0.12. when it is reloaded its value will be 0.12. if you do a calculation in the same request vs in a subsequent request, you can get a different result. The 2 solutions are (1) increase the DB number of decimal places so that these errors are not significant enough to matter, or (2) ensure that whenever you assign to a DB field, you round to the correct number of decimal places, e.g. player.foo = round(x, 2)

#4 by Chris_oTree

Hi, I just added a new feature to the beta to help with issues like this, see the new DecimalField. You can define your desired precision for storage and display separately: https://otree.readthedocs.io/en/latest/misc/version_history.html

#5 by BenG

Thanks, Chris! This will come in very handy at various places, not just the problem I mentioned above. 

I admire your dedication to finding quick and versatile solutions to oTree issues.

Write a reply

Set forum username