I'm trying to make a limit order of convert using binance api. The Postman doc tell us that should be pass as parameter in the POST like we do with user_saldo that works just fine.
The error returned content: b'{"code":345214,"msg":"Placing a limit order has failed. Please try again later. Error code: 345124"}'
so the methods conversao don't work. I also tried to ajust the limitePrice value when it is a BUY and increase it when it's a SELL to avoid open a dead limit order, still same error.
Fixed using only 8 decimal units instead of 15 Another issue that give a diffent error is when I try to pass the quant_de_cpt it says this value is malformed.
What I'm doing wrong?
#!/usr/bin/env python
import os
import requests
import time
import hmac
import hashlib
class OperadorBinanceClient:
def __init__(self, api_key=None, api_secret=None):
self.api_key = api_key or os.environ.get('binance_api_key')
self.api_secret = api_secret or os.environ.get('binance_api_secret')
self.recv_window = 60000 # Valor em milissegundos
self.base_url = 'https://api.binance.com'
self.user_endpoint = '/sapi/v3/asset/getUserAsset'
self.conversao_endpoint = '/sapi/v1/convert/limit/placeOrder'
self.conversao_status_endpoint = '/sapi/v1/convert/orderStatus'
def _get_timestamp(self):
return str(int(time.time() * 1000))
def _generate_signature(self, query_string:str):
signature = hmac.new(self.api_secret.encode('utf-8'), query_string.encode('utf-8'), hashlib.sha256).hexdigest()
return signature
def user_saldo(self, coin='BRL'):
timestamp = self._get_timestamp()
query_string = f'recvWindow={self.recv_window}×tamp={timestamp}'
signature = self._generate_signature(query_string)
url = f'{self.base_url}{self.user_endpoint}?{query_string}&signature={signature}'
headers = {'X-MBX-APIKEY': self.api_key}
response = requests.post(url, headers=headers)
all_balances = response.json()
if coin == '':
return all_balances
filtered_balance = [item for item in all_balances if item['asset'] == coin]
# empty list coin not exist
return filtered_balance if len(filtered_balance) > 0 else None
def conversao2(self, de='BRL', para='ETH', quantidade=1, quantidade_de_crypto=0.1, preco_limite=None, lado='BUY', tipo_carteira='SPOT'):
timestamp = self._get_timestamp()
quant_de_cpt = f'{quantidade_de_crypto:.15f}'
data = {
'baseAsset': de,
'quoteAsset': para,
'limitPrice': preco_limite or '',
'baseAmount': quantidade,
'quoteAmount': quant_de_cpt,
'side': lado,
'expiredType': '1_H',
'recvWindow': 60000,
'timestamp': timestamp
}
signature = self._generate_signature('&'.join([f'{key}={data[key]}' for key in data]))
data['signature'] = signature
url = f'{self.base_url}{self.conversao_endpoint}'
headers = {'X-MBX-APIKEY': self.api_key, 'Content-Type': 'application/json'}
response = requests.post(url, headers=headers, data=data)
return response.json()
def conversao(self, de= 'BRL', para='ETH', quantidade=1, quantidade_de_crypto=0.1,preco_limite=None, lado='BUY', tipo_carteira='SPOT'):
timestamp = self._get_timestamp()
quant_de_cpt = f'{quantidade_de_crypto:.15f}'
#"eAmount={quant_de_cpt}
query_string = f'baseAsset={de}"eAsset={para}&limitPrice={preco_limite}&baseAmount={quantidade}&side={lado}&expiredType=1_H&recvWindow=60000×tamp={timestamp}'
signature = self._generate_signature(query_string)
url = f'{self.base_url}{self.conversao_endpoint}?{query_string}&signature={signature}'
headers = {'X-MBX-APIKEY': self.api_key, 'Content-Type': 'application/json'}
response = requests.post(url, headers=headers)
return response.json()
def conversao_status(self, id_conversao):
timestamp = self._get_timestamp()
query_string = f'orderId={id_conversao}&recvWindow=60000×tamp={timestamp}'
signature = self._generate_signature(query_string)
url = f'{self.base_url}{self.conversao_status_endpoint}?{query_string}&signature={signature}'
headers = {'X-MBX-APIKEY': self.api_key}
response = requests.get(url, headers=headers)
return response.json()
2 Answers 2
As for your second question, you can see in the documentation that quoteAmount should be a decimal, i.e. a number. You are however providing a string, specifically a formatted string. If you simple pass in the value as a number it should work.
Considering the first error, have you read through the FAQ to confirm that your order is valid? The error code seems unusual, are you sure that you aren't getting any of the documented error codes?
4 Comments
quoteAmount I'm just fixing the same amount of decimal precision as I usually see with 15 decimal units. But I just fix it now reducing to 8 like this quant_de_cpt = float(f'{quantidade_de_crypto:.8f}'). But it's not the string formatting because it should be passed as string in the query_string, isn't? But now with only 8 digits the malformat error is gone.limitPrice. All the values in the string is pretty well defined and valid. I'm not creating more then one orders at same time. so the query_string is like 'baseAsset=USDT"eAsset=ETH&limitPrice=3150.99&baseAmount=100"eAmount=0.03168172&side=BUY&expiredType=1_H&recvWindow=60000×tamp=1714060059458'float(f'{...}') is pointless because the additional zeroes are only for display purposes - just use the original number straight away.The issue was that the baseAmount should be what return the exchangeInfo endpoint '/sapi/v1/convert/exchangeInfo'
when you define the baseAsset it should be the asset valid as base like this
exchange_info=self.exchange_info(from_asset=de, to_asset=para)
if exchange_info[0]['fromIsBase']:
query_string = f'baseAsset={de}"eAsset={para}&limitPrice={preco_limite}"eAmount={quantidade}&side={lado}&walletType={tipo_carteira}&expiredType=1_D&recvWindow=60000×tamp={timestamp}'
else:
query_string = f'baseAsset={para}"eAsset={de}&limitPrice={preco_limite}&baseAmount={quantidade_de_crypto}&side={lado}&walletType={tipo_carteira}&expiredType=1_D&recvWindow=60000×tamp={timestamp}'
signature = self._generate_signature(query_string)
Comments
Explore related questions
See similar questions with these tags.