Issues Placing a Trade with Binance API using Python

谁都会走 提交于 2021-02-10 12:13:26

问题


I am trying to place a trade on the US version of Binance API without the use of external libraries.

I can successfully get prices and display my account balance using GET requests and urllib. The first example code works and I can pass my API_KEY and SECRET_KEY without issues (those values are private, they aren't displayed here and are located in settings.py).

Placing a trade requires POST and I'm not sure where I went wrong, my POST requests don't work but GET requests work fine. To my understanding of the docs to make a POST request I'm supposed to encode the parameters using urllib.parse.urlencode() and pass it into the data parameter in urllib.request.Request().

Doing so doesn't throw an error but when I try to open the request with urllib.request.urlopen() I get an error:

Traceback (most recent call last):  
File "C:\Users\user\PycharmProjects\test\test.py", line 80, in <module>   place_trade(symbol='BTCUSD', side='BUY', order_type='MARKET', quantity=1)
File "C:\Users\user\PycharmProjects\test\test.py", line 73, in place_trade   response = urllib.request.urlopen(req)
File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 214, in urlopen   return opener.open(url, data, timeout)
File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 523, in open   response = meth(req, response)
File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 632, in http_response   response = self.parent.error(
File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 561, in error   return self._call_chain(*args)
File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 494, in _call_chain   result = func(*args)
File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 641, in http_error_default   raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 500: Internal Server Error

The HTTP Return Codes state:

HTTP 5XX return codes are used for internal errors; the issue is on Binance's side. It is important to NOT treat this as a failure operation; the execution status is UNKNOWN and could have been a success.

However I don't believe that to be the case because I can run the other code's function get_account_balance() without issues. I'm not sure what I'm doing wrong, both codes are almost identical except for making GET vs. POST requests.

Code to get account balance - works fine:

import json
import time
import hmac
import settings
import hashlib
import urllib.parse
import urllib.request


def get_account_balance():

    # Setup header with API_KEY
    headers = {'X-MBX-APIKEY': settings.API_KEY}

    # Params requires timestamp in MS
    params = {'timestamp': int(time.time() * 1000)}

    # Encode params into url
    url = 'https://api.binance.us/api/v3/account?' + urllib.parse.urlencode(params)

    # Create a HMAC SHA256 signature
    secret = bytes(settings.SECRET_KEY.encode('utf-8'))
    signature = hmac.new(secret, urllib.parse.urlencode(params).encode('utf-8'), hashlib.sha256).hexdigest()

    # Add signature to url
    url += f'&signature={signature}'

    # Make a request
    req = urllib.request.Request(url, headers=headers)
    
    # Read and decode response
    response = urllib.request.urlopen(req).read().decode('utf-8')
    
    # Convert to json
    response_json = json.loads(response)

    # Print balances for all coins not at 0
    for entry in response_json['balances']:
        if entry['free'] == '0.00000000':
            continue
        print(entry)


get_account_balance()

Code to place trade - does not work:

import json
import time
import hmac
import settings
import hashlib
import urllib.parse
import urllib.request


def place_trade(symbol, side, order_type, quantity):

    # Setup header with API_KEY
    headers = {'X-MBX-APIKEY': settings.API_KEY}

    # Params require symbol, side, type, quantity and timestamp (for market orders)
    params = {
        'symbol': symbol,
        'side': side,
        'type': order_type,
        'quantity': quantity,
        'timestamp': int(time.time() * 1000)
    }

    # Encode params into url
    url = 'https://api.binance.us/api/v3/order/test?' + urllib.parse.urlencode(params)

    # Create a HMAC SHA256 signature
    secret = bytes(settings.SECRET_KEY.encode('utf-8'))
    signature = hmac.new(secret, urllib.parse.urlencode(params).encode('utf-8'), hashlib.sha256).hexdigest()

    # Add signature to url
    url += f'&signature={signature}'

    # Encode params
    data = urllib.parse.urlencode(params).encode('ascii')

    # Make a POST request
    req = urllib.request.Request(url, data, headers)

    # Open request and convert to string and then to json
    response = urllib.request.urlopen(req)                         # <- line with error
    response_str = response.read().decode('utf-8')
    response_json = json.loads(response_str)

    print(response_json)


place_trade(symbol='BTCUSD', side='BUY', order_type='MARKET', quantity=1)

References

  • urlib docs

  • US Binance Home Page

  • US Binance API Github

  • API New Order Endpoint

  • API New Order Endpoint - For Testing*

This endpoint is used in the example but both endpoints function the same and have the same error

I also looked at the library python-binance for examples

  • PyPi

  • GitHub

  • Docs

EDIT

I can place a successful order using the requests library. I went through the library's source code but can't figure out how to properly format a POST request using urllib

Code to place trade using requests library - works:

import hmac
import time
import hashlib
import requests
import settings
import urllib.parse

session = requests.session()
session.headers.update({'X-MBX-APIKEY': settings.API_KEY})


url = 'https://api.binance.us/api/v3/order/test'


params = {
        'symbol': 'BTCUSD',
        'side': 'BUY',
        'type': 'MARKET',
        'quantity': 1,
        'timestamp': int(time.time() * 1000)
    }

secret = bytes(settings.SECRET_KEY.encode('utf-8'))
signature = hmac.new(secret, urllib.parse.urlencode(params).encode('utf-8'), hashlib.sha256).hexdigest()

params['signature'] = signature

result = session.post(url, params)

print(result)
print(result.text)

回答1:


The problem was with the following lines:

# Encode params into url
url = 'https://api.binance.us/api/v3/order/test?' + urllib.parse.urlencode(params)

# Add signature to url
url += f'&signature={signature}'

Apparently, when using urllib GET request payloads have to be encoded into the url itself, however POST requests require you to pass them into into the data parameter:

data = urllib.parse.urlencode(params).encode('ascii')
req = urllib.request.Request(url, data=data, headers=headers)

In my question, I was passing my payload through the url AND the data parameter. Removing the url payload fixes the issue. Side note for anyone stumbling upon this, putting a question mark ? after the url is optional for POST requests but not GET requests when using urllib

Working code to post a trade without external libraries:

import json
import time
import hmac
import hashlib
import settings
import urllib.parse
import urllib.request

params = {
        'symbol': 'BTCUSD',
        'side': 'BUY',
        'type': 'MARKET',
        'quantity': 1,
        'timestamp': int(time.time() * 1000)
}

secret = bytes(settings.SECRET_KEY.encode('utf-8'))
signature = hmac.new(secret, urllib.parse.urlencode(params).encode('utf-8'), hashlib.sha256).hexdigest()

params['signature'] = signature

url = 'https://api.binance.us/api/v3/order/test'
headers = {'X-MBX-APIKEY': settings.API_KEY}

data = urllib.parse.urlencode(params).encode('ascii')
req = urllib.request.Request(url, data=data, headers=headers)

response = urllib.request.urlopen(req)
response_str = response.read().decode('utf-8')
response_json = json.loads(response_str)

print(response_json)


来源:https://stackoverflow.com/questions/66003521/issues-placing-a-trade-with-binance-api-using-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!