Python-binance: Insufficient balance API problem

Created on 23 Jan 2018  路  28Comments  路  Source: sammchardy/python-binance

Hi, I'm using the API to buy a selected quantity on the market. Say I'm on the ETHUSDT marked and want to use my x amount of USDT to buy y amount of ETH. I then input exactly the value I am able to afford in ETH, but the API returns the Account has insufficient balance for requested action, even though I have enough in my account. If I then quickly go to the market in my browser and select to sell 100% of my USDT, the suggested buy value is exactly the one I'm trying to pass through the API.

https://www.reddit.com/r/binance/comments/7r46ph/insufficient_balance_api_problem/ copy paste

Most helpful comment

This is a problem in how Python handles decimals. I was also stuck with this until I could correctly format the values sent to the API.

Use this function to format both quantity and price, using the filters corresponding to the transaction symbol.

import math

def floatPrecision(f, n):
    n = int(math.log10(1 / float(n)))
    f = math.floor(float(f) * 10 ** n) / 10 ** n
    f = "{:0.0{}f}".format(float(f), n)
    return str(int(f)) if int(n) == 0 else f

An example:

symbol_info = client.get_symbol_info('ETHUSDT')
tick_size = float(filter(lambda f: f['filterType'] == 'PRICE_FILTER', symbol_info['filters'])[0]['tickSize'])
step_size = float(filter(lambda f: f['filterType'] == 'LOT_SIZE', symbol_info['filters'])[0]['stepSize'])

price = float(filter(lambda x: x['symbol'] == 'ETHUSDT', client.get_all_tickers())[0]['price'])
price = floatPrecision(price, tick_size)
usdt_balance = float(client.get_asset_balance(asset='USDT'))
quantity = floatPrecision(usdt_balance / float(price), step_size)

buy = client.order_limit_buy(symbol='ETHUSDT',quantity=quantity,price=price)

All 28 comments

I checked with a limit order to verify that I was sending the correct quantity, that the price lined up, and they did, sending a amount less than the full available amount also worked. The limit order did not go through, even though it matched the market and thus should have been immediately matched or rather denied. (it appeared in the online order form).
Running the same market or order purchase online went through smoothly so everything points back to the api.

Along those lines I cannot tell if it's specifically this library or the api itself, but I think a test I did with the node library pointed towards the api itself.
The website uses the function market_sell_submit within the java script called trade.min.js 1.1.320,
if (!i.test(t)) return e.showerror('market_sellQuanity', 'market_sellform-error', f.instant('inputCorrectbuyQuanity').replace('###', Number(e.currentProduct.minTrade).toFixed(e.currentProduct.qtyTick))), !1;
^ checks quantity,
below is the actual order placement
var a = { method: 'post', url: '/exchange/private/order', data: $.param({ quantity: t, symbol: e.product.symbol, side: 'SELL', type: 'MARKET' }) };

any help?

This is a problem in how Python handles decimals. I was also stuck with this until I could correctly format the values sent to the API.

Use this function to format both quantity and price, using the filters corresponding to the transaction symbol.

import math

def floatPrecision(f, n):
    n = int(math.log10(1 / float(n)))
    f = math.floor(float(f) * 10 ** n) / 10 ** n
    f = "{:0.0{}f}".format(float(f), n)
    return str(int(f)) if int(n) == 0 else f

An example:

symbol_info = client.get_symbol_info('ETHUSDT')
tick_size = float(filter(lambda f: f['filterType'] == 'PRICE_FILTER', symbol_info['filters'])[0]['tickSize'])
step_size = float(filter(lambda f: f['filterType'] == 'LOT_SIZE', symbol_info['filters'])[0]['stepSize'])

price = float(filter(lambda x: x['symbol'] == 'ETHUSDT', client.get_all_tickers())[0]['price'])
price = floatPrecision(price, tick_size)
usdt_balance = float(client.get_asset_balance(asset='USDT'))
quantity = floatPrecision(usdt_balance / float(price), step_size)

buy = client.order_limit_buy(symbol='ETHUSDT',quantity=quantity,price=price)

@jsmsalt I didn鈥檛 mention but I鈥檓 using the decimal.Decimal class of python, so I don鈥檛 believe I鈥檓 facing that issue, but I鈥檒l go ahead and try your code anyway, thanks

Just confirmed, both give the same number when converted to string, which also matches up with the number binance's website gives.

So in other words I'm trying to sell 100%, and the api refuses to accept my order, even though the same order would go through on the website.

@Aaahh Did you check that all these conditions are satisfied before you send the order?

price >= minPrice
price <= maxPrice
(price-minPrice) % tickSize == 0
quantity >= minQty
quantity <= maxQty
(quantity - minQty) % stepSize == 0
(price * quantity) >= minNotional

https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#filters

@jsmsalt
It鈥檚 a market order, but when I do use limit the order goes through but is never filled (or at least in a reasonable amount of time before I cancel it)

But to answer you questions when using a limit order yes
And the ones that apply to a market order yes

I'm passing the value as a string, which I assume works, should I try passing the float?

This is happening because you do not have enough units to give as fees. For eg, you bought 100units of BTCUSDT and are selling 100 units of BTCUSDT. It will never go unless you have fees equivalent of BNB and are using BNB to pay fees.
If you bought 100 units of BTC, then you only got 99.95 BTC, rest 0.05BTC went as fees.

It works in browser because you sell "100%" amount and not look at the fractions you paid as fees.

I'm having the same issue as the OP.

Having USDT holdings, using python decimal.Decimal to represent numbers.

Sending a market order to sell 1 USDT to ETH _(ETHUSDT pair)_ results in APIError(code=-2010): Account has insufficient balance for requested action excpetion.

My usual base market is against ETH and This happens to me only with USDT. Any other transaction between ETH and other coins passes without any issues.

@Aaahh Have you managed to solve it? I agree that it seems to be originated from the Binance API side

@pulkitsharma I use BNB for fees...

@hemulin well thats it abit discouraging, but have you sold 100% of another item?

I鈥檓 looking into using the here exchange/private

@Aaahh
If I understand your question correctly, yes. It happened that I've made a trade with 100% of an asset at market order.
I first ask the API how much of let's say ABC I hold now. Then, using the LOT_SIZE filter, rounding (floor) the quantity to the nearest decimal point within step_size for that ABC asset, and then execute sell@market order for the resulting quantity. It passes without a problem.

Didn't understand the line regarding the here exchange/private

@hemulin
Understood correct, but have you market bought aka sold another asset at 100%?

The exchange/private is the url the binance website used to process orders

@Aaahh You mean you have ticked that check box that says use BNB to pay fees, but do you own any BNB?

@pulkitsharma yes and I鈥檝e tested the trade on the websit

@Aaahh

"have you market bought aka sold another asset at 100%?"

Yes, I have executed both 100% (by my calculations as described here) buy and sell orders at market for another assets against ETH.

"The exchange/private is the url the binance website used to process orders"

Please do update if you've had any luck there. Thanks

@sammchardy
Could you please advise on how you think it is best to solve this issue?

Hi @hemulin I got a bit lost in this, looked like you were helping each other out there :)

Yes you need to format the amounts and prices with correct decimal places to satisfy the particular trading pair quirks.

This happens to me only with USDT

If this is true, then I would suggest talking to Binance about it on the API Telegram as a start https://t.me/binance_api_english

@samjosephmark
Thanks for the quick reply (and this awesome project).
Didn't know about the group.
From quick search there, it looks like @Aaahh (based on the avatar) has already asked that question. I would re-ask. Will report back any findings.

Cheers.

@Aaahh - I think I've got it!

tl;dr - All you need to do is to swap the quantities and the trade types.

Longer explanation:

Let's say that our base market is ETH and that a Trade object is

{
  "symbol": "ABC",
  "type": "buy", # or "sell"
  "quantity": decimal.Decimal('15.4') # IMPORTANT! Minimum for USDT trades is 10
}

Usually, for regular tokens that are paired with ETH (e.g 'GVT' which would be designated as GVTETH) The following execution passes without any issues:

symbol_pair = '{symbol}ETH'.format(symbol=trade['symbol']) # 'GVTETH'
client.order_market_buy(symbol=symbol_pair, quantity=trade['quantity'])

The problem as we saw is with the USDT market.

The reason for this is that the market is actually ETHUSDT (and not USDTETH like the regular tokens). Now, changing the market name is not enough, you actually operate on a USDT market and must flip the trade type AND the trade quantity to represent quantities of ETH

So if we want to sell 10.5 USDT to ETH and keep the "normal" structure we usually operate with (like the regular ETH paired tokens), we would have a trade which looks like this:

{
   'type': 'sell',
   'symbol': 'USDT',
   'quantity': Decimal('10.5')
}

And the code that would be executed correctly is:
_(the usd_eth_price is well.. the current USD price of ETH. One can find it using the get_all_tickers() and looking for the ETHUSDT symbol)_

symbol_pair = 'ETH{symbol}'.format(symbol=trade['symbol']) # 'ETHUSDT'
flipped_quantity = round(trade['quantity'] / usd_eth_price, 3) # this can be more precise using the LOT_SIZE filter. For this simplified example it just works.
if trade['type'] == 'sell': # Flipping types of trades
  client.order_market_buy(symbol=symbol_pair, quantity=flipped_quantity)
else:
  client.order_market_sell(symbol=symbol_pair, quantity=flipped_quantity)

I've tested it multiple times with multiple quantities in both directions and it worked well for me.

@jsmsalt I used your code above, but have this error:

tick_size = float(filter(lambda f: f['filterType'] == 'PRICE_FILTER', symbol_info['filters'])[0]['tickSize'])
TypeError: 'filter' object is not subscriptable

Please advise how to fix it. I am using python 3. Thanks

@carlg32 same problem in python 3, did you fix it?

unfortunately, not yet.

same getting the same error
'filter' object is not subscriptable

A bit late to the party here, but I believe the OP @jsmsalt forgot to wrap the filter() method with the list() method. Also the dictionary key ['free'] was missing from the usdt_balance function. Awesome work though, this solved my problems!

working example:

import math

def floatPrecision(f, n):
    n = int(math.log10(1 / float(n)))
    f = math.floor(float(f) * 10 ** n) / 10 ** n
    f = "{:0.0{}f}".format(float(f), n)
    return str(int(f)) if int(n) == 0 else f

symbol_info = client.get_symbol_info('ETHUSDT')

tick_size = float(list(filter(lambda f: f['filterType'] == 'PRICE_FILTER', symbol_info['filters']))[0]['tickSize'])
step_size = float(list(filter(lambda f: f['filterType'] == 'LOT_SIZE', symbol_info['filters']))[0]['stepSize'])
price = float(list(filter(lambda x: x['symbol'] == 'ETHUSDT', client.get_all_tickers()))[0]['price'])

price = floatPrecision(price, tick_size)
usdt_balance = float(client.get_asset_balance(asset='USDT')['free'])
quantity = floatPrecision(usdt_balance / float(price), step_size)

buy = client.order_limit_buy(symbol='ETHUSDT',quantity=quantity,price=price)

Hi,
I am having the same issue. I am trading on LINK/USDT with the following set:

Free Balance: LINK 162.4899426 USDT 41.89336591582292 (LINK equivalent)
Total Balance: LINK 2415.10426585806 (USDT equivalent) USDT 622.30081466
amount: buy 17.38 - sell 16.86

Open Margin Order {'code': -2010, 'msg': 'Account has insufficient balance for requested action.'}

weither I buy or sell, I do have the funds, so what's going on?

Was this page helpful?
0 / 5 - 0 ratings