Ventures of an ex indie game developer

Building a cryptocurrency trading bot tutorial, step 3/x

Let's look at another exchange this time. Meet Binance, the cheapest and possibly best one. It's based in China, which I'm not sure if it's a good or bad thing, but so far I haven't seen any drawbacks of using it. The principles between exchanges are all the same, but the details vary. Most give you as a user two keys which you can copy-paste into your code. Binance wants you to pass one key in the HTTP request and use the other one to hash your parameters.

First you need some stuff from the standard library, then you set your keys:

from binascii import hexlify
from hashlib import sha256
from hmac import new
import requests
import time
from urllib.parse import urlencode

key    = b'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
secret = b'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'

Also, all vital Binance requires that you pass a timestamp so that network congestion doesn't allow for acting on old data or instructions. They want milliseconds:

def now_ms():
    return int(time.time() * 1000)

We'll be using requests.Session, so we set the key in the header of a session like so:

def binance_set_session_headers(session):
    headers = {
        'Accept': 'application/json',
        'X-MBX-APIKEY': key
    }
    session.headers.update(headers)

Aha. Next we use the other key to hash our parameters. Something like this:

def binance_sign(data):
    hmac = new(key=secret, msg=data.encode(), digestmod=sha256)
    data = hexlify(hmac.digest())
    return data.decode()

Now we have everything we need. This is how to do a HTTP GET of some data:

def binance_signed_get(path, **kwargs):
    params = urlencode(kwargs)
    hash = binance_sign(params)
    params += ('&' if params else '') + 'signature=' + hash
    url = 'https://api.binance.com' + path + '?' + params
    with requests.Session() as session:
        binance_set_session_headers(session)
        r = session.get(url)
        return r.json()

We can use it to fetch our balance:

balance = binance_signed_get('/api/v3/account', timestamp=now_ms())
print(balance)

In order to place an order we want to do a HTTP POST instead of a GET. So you could modify your function to this to be able to either GET or POST:

def binance_signed(path, method='get', **kwargs):
    params = urlencode(kwargs)
    hash = binance_sign(params)
    params += ('&' if params else '') + 'signature=' + hash
    url = 'https://api.binance.com' + path + '?' + params
    with requests.Session() as session:
        binance_set_session_headers(session)
        call = getattr(session, method)
        r = call(url)
        return r.json()

To place a limit order you now do this:

r = binance_signed('/api/v3/order', method='post', symbol='BTCUSDT', side='BUY', type='LIMIT',
        quantity='0.5', price='8680.000', timeInForce='GTC', timestamp=now_ms())
print(r)

Deposit a few bucks and play with it!

About the author

Mitt foto
Gothenburg, Sweden