Question

While dealing with dollar figures in a program to do automatic trading in the stock market ("algorithmic trading" as it is being called) the numbers are difficult to manage. In my opinion this is not duplicate because I haven't spotted a suitable solution in other similar questions/answers.

If I run this alone [update, turns out this was version 2.7.2 at home]:

price_list = [1.0, 2.0, 2.5, 3.0, 4.0, 4.5, 4.6, 4.6, 4.7]
print price_list

...it prints the way you see it above. However in my thousand line program, Python seems to be saying, you didn't really mean that, what you really meant was:

>nasdaq_crunching_4d.py
[1.0, 2.0, 2.5, 3.0, 4.0, 4.5, 4.5999999999999996, 4.5999999999999996, 4.7000000000000002]

[ Update: It didn't have anything to do with just the two lines vs in my program. That extra digits output was on the remote host I use, Bluehost is at version 2.6.6. I'm surprised anyone is using 2.6, looks it came with their CentOS and they provide instructions for upgrade per user, fine. However the remainder of the question still has validity to it. ]

...and then before you know it, you're trying to wrap your head around gargantuan lists (small appetizer, too spicy in my opinion):

'slopes': [-40.280000000000001, 79.689999999999998, -40.56000000000... 
80.340000000000003, -40.420000000000002, 80.159999999999997, -40.28...
79.849999999999994, -40.090000000000003, 79.439999999999998, -39.96... 
79.129999999999995, -40.350000000000001, 79.829999999999998, -40.39...
80.030000000000001, -40.729999999999997, 80.719999999999999, -40.93... 

I want to tell Python, no, I didn't mean 4.5999999999999996, I meant precisely 4.60 and followed by a billion and one zeroes if you need that, and I only want two of those digits so you can ignore the rest of the zeroes. However if it is 4.60, show me 4.60 by the way, not 4.6, and definitely not 4.5999999999999996.

This is also not palatable:

'lst': [Decimal('124.75'), Decimal('125.18'), Decimal('125.18...
'ma_b': {'max': 4, 'now': Decimal('125.36'), 'lth': 5, 'slp':...
Decimal('125.18'), Decimal('125.25'), Decimal('125.36')]}, 'm...
0.06], 'lst': [Decimal('125.19'), Decimal('125.17' '118.19'),...

You might want to know that I'm importing math (to use various functions from it), and have tried round(), float(), "%.2f" and maybe others I'm forgetting right now. There is a large and complex dict containing various lists of ticker symbols, prices, moving averages, slopes and a lot of other floats plus strings and other dicts etc.

Is there some way that dollars can be consistently and simply just dollars and cents? Thanks.

Was it helpful?

Solution 4

A quick and dirty way is to redefine Decimal itself at the beginning of your main module:

from decimal import Decimal

Decimal.__repr__ = lambda self: "{0:.2f}".format(self)

This would apply to all decimals, including any created in external modules.

Another way would be to make your own Decimal subclass, but defining new types for simple conveniences like this is a bad habit to get into, I think: If any external function returns an object of the base type, then you need to convert it to a new object of your type which is needlessly inefficient. Also, Decimal uses slots and is meant to be an immutable type, so any subclass would need to take this into account to preserve efficiency.

A better way would be to define your own function for displaying it the way you want. This way you limit the reformatting to specifically when you want to use it. However this function would need to re-implement the recursive list output algorithm. Fortunately, there is the repr library which greatly simplifies this. This allows you to define custom repr implementations per type, with built-in support for recursive objects like lists and dictionaries:

from decimal import Decimal
from repr import Repr

class CustomRepr(Repr):
    def repr_Decimal(self, obj, level):
        return "{0:.2f}".format(obj)

cr = CustomRepr()

data = {"money": [Decimal("42")]}

print cr.repr(data)

Output:

{'money': [42.00]}

OTHER TIPS

You keep printing lists, and lists use repr() represent their contents. If you want more control over the formatting, then you'll have to use proper formatting:

print '[{}]'.format(', '.join([format(n, '.2f') for n in numbers_list]))

would format your output to a list-like string with all your floating point numbers printed with 2 digits after the decimal point:

>>> numbers_list = [1.0, 2.0, 2.5, 3.0, 4.0, 4.5, 4.5999999999999996, 4.5999999999999996, 4.7000000000000002]
>>> print '[{}]'.format(', '.join([format(n, '.2f') for n in numbers_list]))
[1.00, 2.00, 2.50, 3.00, 4.00, 4.50, 4.60, 4.60, 4.70]

Perhaps floating point is not the best representation for a dollars-and-cents value; store only cents as integers instead (4 dollars 20 cents becomes 420 cents). Cents are not divisible, for most purposes, after all.

You're using floating point arithmetic and expecting accuracy. This pretty much never works, in any language.

Use integers to represent a fixed fraction of a cent (so instead of 4.60, 460000000 millionths of a penny).

You won't be able to easily implement accuracy and a variable amount of decimal places

If you store it as a decimal:

annoying_list=[decimal.Decimal(3.42), decimal.Decimal(2.56)]
for  i in annoying_list: print float(i)

returns:

3.42

2.56

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top