You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Binance Convert Limit Order API调用报错及参数格式问题求助

通过Binance API创建Convert限价订单失败的问题排查

我尝试通过Binance API创建Convert限价订单,按照Postman文档,参数和正常工作的user_saldo接口一样用POST传递,但调用conversao方法时返回错误:

content: b'{"code":345214,"msg":"Placing a limit order has failed. Please try again later. Error code: 345124"}'

我调整过limitPrice(买入调低、卖出调高避免无效订单),但错误依旧。后来把小数位数从15位改成8位解决了部分问题,但传入quant_de_cpt时又提示格式错误。请问哪里操作有误?

以下是相关Python代码:

#!/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}&timestamp={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}'
        
        #&quoteAmount={quant_de_cpt}
        query_string = f'baseAsset={de}&quoteAsset={para}&limitPrice={preco_limite}&baseAmount={quantidade}&side={lado}&expiredType=1_H&recvWindow=60000&timestamp={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&timestamp={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()

URL参数示例


问题排查与解决方案

1. 参数传递格式错误

Binance的Convert限价订单接口要求参数通过**表单格式(application/x-www-form-urlencoded)**传递,而非代码中设置的JSON格式。你在conversao和conversao2方法里指定了Content-Type: application/json,这会导致接口无法正确解析参数,直接触发345124错误。

2. 数量参数精度不匹配

Binance对不同交易对的数量精度有严格限制,不能统一用15位或8位小数。必须先通过/api/v3/exchangeInfo接口获取目标交易对的精度规则:

  • 找到对应交易对(如ETHBRL)的baseAssetPrecision(基础资产精度)和quoteAssetPrecision(报价资产精度)
  • 根据返回的精度值格式化金额参数,避免多余小数位导致格式错误

3. 冗余参数冲突

Convert限价订单中,baseAmount和quoteAmount是互斥的:

  • 当side=BUY时,只需传入quoteAmount(用报价资产的金额购买基础资产,比如用BRL买ETH时传BRL的金额)
  • 当side=SELL时,只需传入baseAmount(卖出基础资产的数量,比如卖出ETH时传ETH的数量)
    同时传入两个参数会导致接口报错。

修正后的代码示例

def conversao_corrigida(self, de='BRL', para='ETH', quantidade=None, preco_limite=None, lado='BUY'):
    timestamp = self._get_timestamp()
    # 获取交易对精度规则
    exchange_info = requests.get(f"{self.base_url}/api/v3/exchangeInfo").json()
    symbol = f"{para}{de}" if lado == 'BUY' else f"{de}{para}"
    target_symbol = next(item for item in exchange_info['symbols'] if item['symbol'] == symbol)
    
    params = {
        'baseAsset': de,
        'quoteAsset': para,
        'limitPrice': preco_limite,
        'side': lado,
        'expiredType': '1_H',
        'recvWindow': 60000,
        'timestamp': timestamp
    }
    
    # 根据交易方向添加对应数量参数,并按精度格式化
    if lado == 'BUY':
        precision = target_symbol['quoteAssetPrecision']
        params['quoteAmount'] = f"{quantidade:.{precision}f}"
    else:
        precision = target_symbol['baseAssetPrecision']
        params['baseAmount'] = f"{quantidade:.{precision}f}"
    
    # 按排序后的参数生成签名,避免顺序问题导致签名无效
    sorted_params = sorted(params.items())
    query_string = '&'.join([f'{key}={value}' for key, value in sorted_params])
    signature = self._generate_signature(query_string)
    params['signature'] = signature
    
    url = f'{self.base_url}{self.conversao_endpoint}'
    headers = {'X-MBX-APIKEY': self.api_key}
    # 使用表单格式传递参数,无需手动设置Content-Type
    response = requests.post(url, headers=headers, data=params)
    return response.json()

额外注意事项

  • limitPrice的精度同样需要参考exchangeInfo中的pricePrecision字段
  • 签名生成时必须保证参数顺序和实际传递的一致,建议排序后拼接
  • 测试优先使用Binance测试网,避免真实资产损失

内容的提问来源于stack exchange,提问作者Joao Victor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:22:01