CodeReclaimers / btce-api

Python wrapper around the public and trading APIs of BTC-e.com
MIT License
191 stars 123 forks source link

common.formatCurrency bug? #28

Closed g00dnight closed 10 years ago

g00dnight commented 10 years ago

Hi. When making an order for sale, for example: tapi.trade("btc_usd", "sell", 999.99, 0.109)

order for sale 0.108 btc created. It's just me or there is a bug in common.formatCurrency?

alanmcintyre commented 10 years ago

This is probably due to roundoff; because the number of digits you can use in an order is limited, the amounts will be truncated so that they will fit into the exchange's limitation on the number of digits. So if the order would have been for 0.1089981, and it's limited to 3 decimal places, it will get truncated to 0.108.

I will double-check this, though, to make sure there isn't an incorrect digit limit or other bug somewhere. Thanks for reporting it!

g00dnight commented 10 years ago

The order was for 0.109, just how I wrote in the code above.

alanmcintyre commented 10 years ago

Oh I see what's happening: 0.109 can't be represented exactly as a floating-point number. The closest floating-point number is 0.1089999999999999996 or something, and that gets truncated to 0.108.

You can get around this by handling all your value and amount values as decimal.Decimal, which is what the library does internally (notice that values coming from the server are Decimal instances instead of regular Python floats). For example:

btceapi.formatCurrency(0.109, 'btc_usd') 

returns '0.108'; If instead you import decimal and do this:

btceapi.formatCurrency(decimal.Decimal('0.109'), 'btc_usd') 

it returns '0.109'.

The reason I always truncate (round down) is that otherwise some orders will fail. For example, if you're trying to submit an order for exactly the funds you have available, and it gets rounded up just a tiny bit it will fail and tell you there are insufficient funds to place the order.

g00dnight commented 10 years ago

Yep, thanks for the advice, Alan.