Binance Trading API – Creating Your First Simple Crypto-Trading Bot

Disclaimer: The bot built here should be used only as a learning tool. If you choose to do real trading on Binance, then you have to build your own criteria and logic for trading. The author is not responsible for any losses incurred if you choose to use the code developed here on Binance.

Note: If you register on Binance via the Binance links provided in this article, we’ll get a small kickback and you’ll support the Finxter mission. In the name of all Finxters learning with us, and in the name of all Finxter creators, many thanks!

What is Binance?

Binance is a digital currency trading platform. It supports many cryptocurrency trading such as Bitcoin, Ethereum, Dogecoin, Tether etc. Binance has its own native cryptocurrency called Binance coin (BNB). It offers only crypto-to-crypto exchange. This means before you start trading on Binance you need to buy some cryptocurrency. You can buy the cryptocurrency on other trading platforms such as coinbase, kraken, Bitfinex, Bitstamp and so on.

Compared to other platforms, Binance fees are lowest. This makes it the most popular and largest trading platform.

Binance API

Binance offers RESTful APIs such as GET, PUT, POST using HTTP requests to send and receive data. It also supports the Websocket interface to exchange streams of data. e.g. to receive the latest Bitcoin price, account related updates and much more.

The API’s are available here for reference: Change Log – Binance API Documentation.

To ease the development of accessing these server API’s, there is a Python client package available which provides an interface for Binance servers. We will make use of this client python package for our bot design. The python package is available here: python-binance v1.0.12 documentation. This package allows us to connect to the Binance servers to send and receive data or stream.

Even though the python API makes our life easy to access Binance and provide a high level of security, there can be some disadvantages. These can include constantly evolving API interfaces which can break the code, however this also means that there are constant improvements on API, when there is an update in Binance infrastructure. Hence we can consider this disadvantage as a positive thing.

Setting a Binance Account

To design our bot we will not use the actual Binance account which needs real cryptocurrency. We shall rather use a testnet Binance account which uses fake or virtual cryptocurrency, so that we do not lose any money 🙂

However in this section we will cover how to set up a Binance account. To access Binance API, you need to first create an account with Binance. You can set up an account here:

Binance: Bitcoin Exchange | Cryptocurrency Exchange.

Creating an account is simple. You need to register with an email. You will receive a confirmation on your email. After confirmation, the next step would be a puzzle to ensure that you are not a bot. Finally, you need to enable the two factor authentication(2FA) using either Google Authenticator or your phone number. Below Fig 1 shows after creating an account on Binance (See green tick for 2FA and my 0 balance :-)).

Fig 1

Next you need to create an API key and secret to communicate with the Binance server.

Click settings->API management, See Fig 2

Fig 2

Provide a key label with some name, it will create the API key and secret. Please store them in a safe place as it will be displayed only once during creation.

Leave the other options Enable Spot & Margin Trading, Enable Margin, Enable Withdrawals, Enable Margin, Enable Vanilla Options as it is, as we are not actually trading with our bot. Enable them only if you do actual trading and have enough experience in trading (see Fig 3).

Fig 3

                                                                      

Setting a Binance Testnet Account

As mentioned we will use this account mostly for our bot testing as it provides you with fake or virtual currency. To create an account with testnet Binance link Binance Testnet (see Fig 4).

Fig 4

Please select Login with GitHub (You need to have a GitHub account. Please create a GitHub account if you don’t have one).

After you login , you need to once again create an API label to generate key and secret.

Select Generate HMAC_SHA256 Key, provide label name and click generate. This should generate the API key and secret which you need to keep it in a safe place. These are needed during our bot design. See Fig 5 after login into testnet.

Fig 5

Some Binance Trading Terms

Binance supports various trading options. We will cover in brief Spot, Margin and Futures trading.

Spot Trading

As with the name, here trading happens on the spot (a.k.a current price). In spot trading, you need to execute the trade immediately with the price for buying and selling at that instant for which the market participants are bidding for. Hence to instantly buy or sell, you need to have the available assets to pay for the trade by the date of settlement.

For example, if you are buying US$1,000 worth of Dogecoin with spot trading, you will need a US$1,000 balance in your account by the date of settlement (mostly T+2 days of the trade).

Margin Trading

In margin trading we buy stocks with borrowed funds. Buying power increases with borrowing – If you can’t afford to buy a house or any other valuable asset right now then by borrowing funds you can. Similarly, the same is possible with stocks.

Margin trading, aka buying on margin, involves borrowing money from your stock broker (e.g. Binance, Coinbase).

For example, if you decide to buy $1,000 worth of Ethereum. you pay $500 in cash and borrow – buy on margin – the other $500 from (say Binance).

Futures Trading

In futures trading, you agree on a contract to buy or sell cryptocurrency at a later date. The seller will sell and the buyer must purchase the underlying asset (cryptocurrency) at the set price, regardless of the market price at the expiration date.

Setting Up the Development Environment

I am using VS code as an IDE on Linux OS, but you are free to choose your own favorite IDE and OS for development. As a best practice, it is always good to create a python virtual environment so that we can install the necessary packages as part of the virtual environment. Below shell code to setup virtual environment on Linux

My python version is 3.9, but any older version like 3.7 or 3.8 would be fine.

$ mkdir binance_crypto_trading
$ cd binance_crypto_trading
$ pip install virtualenv
# here crypto is our virtual env, but you can use any name here
$ virtualenv crypto
$ source crypto/bin/activate    # activate the virtual env

Here is the output of my VS code terminal:

Note: Based on your OS, creating a virtualenv process may differ. Please adapt accordingly for Windows and Mac.

In the next part, we will continue with the design of our trading bot on Binance testnet which supports only spot trading.

Trading Logic 1

In this section we will discuss the trading logic we plan to use to develop our first bot. The logic would be to buy ethereum when bitcoin reaches a particular value.

Note: Please follow the video to understand the building of the bot.

Algorithm to use:

Buy/Sell eth when btc reaches a certain value

  • (e.g. If BTC is greater than 10k and less than 40k then we buy eth)
  • (e.g. If BTC is greater than 40k and then we sell eth)

Let’s start coding

import os
from binance.client import Client
from binance import ThreadedWebsocketManager
import pprint
from time import sleep

TEST_NET = True

if __name__ == "__main__":
    if TEST_NET:
        # passkey (saved in bashrc for linux)
        api_key = os.environ.get('BINANCE_TESTNET_KEY')

        # secret (saved in bashrc for linux)
        api_secret = os.environ.get('BINANCE_TESTNET_PASSWORD')

        client = Client(api_key, api_secret, testnet=True)
        print("Using Binance TestNet server")
    main()

Step 1.)

Firstly we import the necessary packages such as os, pprint and binance Client. We then read the passkey and secret stored in the bashrc file or exported in the terminal. Note that we connect to the testnet Binance account (fake or virtual currency) and not the actual Binance account (real currency). For the same we initiate the Client class with the key and secret.

Step 2.)

def main():
    pprint.pprint(client.get_account())
    print(client.get_asset_balance(asset='BNB'))   # BTC, USDT, ETH
    
    #get latest price from Binance API
    eth_price = client.get_symbol_ticker(symbol="ETHUSDT")
    print(eth_price)

In the second step, we define main() and use the client to read some important account information using get_account(), get_asset_balance() or get_symbol_ticker(). These functions are part of python-binance package. To know exact function details, see here:

Step 3.)

We need to instantiate ThreadedWebsocketManager(), so that we register to a particular websocket stream such as 24hr ticker or Kline/Candlestick, depth streams. Also we define a dict variable btc_price, so that we can start receiving the price of bitcoin in this variable.

if __name__ == "__main__":
    if TEST_NET:
        api_key = os.environ.get('BINANCE_TESTNET_KEY')
        api_secret = os.environ.get('BINANCE_TESTNET_PASSWORD')
        client = Client(api_key, api_secret, testnet=True)
        print("Using Binance TestNet server")

    # Add btc price and instantiate ThreadedWebsocketManager()
    btc_price = {'BTCUSDT': None, 'error': False}
    twm = ThreadedWebsocketManager()
    main()

Step 4.)

Start the websocket manager and register a callback to the symbol bitcoin (‘BTCUSDT’)

using the start_symbol_ticker_socket(). This would give back the prices of bitcoin in the last 24 hrs through the callback registered.

Also add join() as we don’t want the main thread to exit as we have to test the callback

def main():
    pprint.pprint(client.get_account())
    print(client.get_asset_balance(asset='BNB'))
    eth_price = client.get_symbol_ticker(symbol="ETHUSDT")
    print(eth_price)

    # Start the websocket manager and register
    # callback for the bitcoin price
    twm.start()
    twm.start_symbol_ticker_socket(callback=btc_values_received,
                                   symbol='BTCUSDT')

    # To keep the ThreadedWebsocketManager running using join()
    # to join it to the main thread.
    twm.join() 

Step 5.)

We add the callback function registered and perform a test to see if we start receiving the bitcoin prices. This is the best time to run the program and check if we start receiving the bitcoin price information with a regular callback of btc_values_received().

# Get the BTC value in the last 24 hrs
def btc_values_received(msg):
    ''' Process the btc values received in the last 24 hrs '''
    
    pprint.pprint(msg)
    
    if msg['e'] != 'error':
        print(msg['e'])
        btc_price['BTCUSDT'] = float(msg['c'])
    else:
        btc_price['error'] = True

The keys ‘e’ and ‘c’ provide the message type and closing value of bitcoin respectively.

Store the value in btc_price['BTCUSDT']. Refer to the payload of the start_symbol_ticker_socket stream here:

Step 6.)

def main():
    pprint.pprint(client.get_account())
    print(client.get_asset_balance(asset='BNB'))
    eth_price = client.get_symbol_ticker(symbol="ETHUSDT")
    print(eth_price)

    twm.start()
    twm.start_symbol_ticker_socket(callback=btc_values_received, symbol='BTCUSDT')

    # wait here to receive some btc value initially through websocket callback
    while not btc_price['BTCUSDT']:
        sleep(0.1)

    # call buy ETH function with a while loop to keep a track on btc price
    buy_and_sell_ETH_at_BTC()
    # twm.join() # to stop main thread exit.
    twm.stop()

Now introduce a small while loop. Loop until we receive an initial value of bitcoin price from the callback btc_values_received() using a small sleep of 0.1 secs. As soon as we receive the initial bitcoin price, exit the while loop and call the function buy_and_sell_ETH_at_BTC() which can keep our main thread alive. We will implement  this function in the next step.  Due to this function, there is no further need to have twm.join() as its whole purpose is to keep the main thread alive.

Step 7.)

Here we implement buy_and_sell_ETH_at_BTC() which contains the trading logic. Based on the bitcoin price we can buy or sell ethereum.

# Buy or sell ETHUSDT when BTC reaches a particular value
def buy_and_sell_ETH_at_BTC():
    while True:
        # error check to make sure WebSocket is working
        if btc_price['error']:
            # stop and restart socket (cleanup)
            twm.stop()
            sleep(2)
            twm.start()
            btc_price['error'] = False
        else:
            if 1000 < btc_price['BTCUSDT'] < 40000:   # bitcoin price
                try:
                    print("Buying when BTCUSDTprice:",btc_price['BTCUSDT'])
                    order = client.order_market_buy(symbol='ETHUSDT', quantity=1)
                    pprint.pprint(order)
                    break
                except Exception as e:
                    print(e)
                    break
            else:
                try:
                    print("Selling when BTCUSDT price:",btc_price['BTCUSDT'])
                    order = client.order_market_sell(symbol='ETHUSDT', quantity=1)
                    pprint.pprint(order)
                    break
                except Exception as e:
                    print(e)
                    break
            sleep(0.1)

If bitcoin price received an error, perform a cleanup i.e. stop and restart the twm socket.

Otherwise, if the bitcoin price is between 1000 (in $) and 40,000 (in $) we buy ethereum using client.order_market_buy(). Else, we sell ethereum when bitcoin price >40,000 (in $) using client.order_market_sell(). Both, buy and sell functions take the symbol (cryptocurrency) and quantity to buy/sell as arguments. As there is a possibility of errors or exceptions in placing the order with the Binance testnet server, use try except block to capture the same. Adding break is necessary as the order needs to be placed only once and not infinitely due to the loop.

Concluding Trading Logic 1

Running the complete program would result in either buying or selling the ethereum when bitcoin reaches a particular price or value. If there was a failure or an exception, the print statements added would capture the same to give us a sense of things going wrong which can be debugged further.

Trading Logic 2

In this bot the trading logic involves RSI (relative strength index). If the RSI is >70 we sell ethereum and if RSI < 30 we buy ethereum. Refer to Relative Strength Index (RSI) – Overview, Guide, How RSI Works to learn more on RSI.

Note: Please follow the video course on the Finxter academy to understand the building of the bot.

The steps 1, 2, 3 and 4 remains the same as previous bot (copy and paste), with one minor change, see below (a symbol is added to buy or sell)

if __name__ == "__main__":
    if TEST_NET:
        api_key = os.environ.get('BINANCE_TESTNET_KEY')
        api_secret = os.environ.get('BINANCE_TESTNET_PASSWORD')
        client = Client(api_key, api_secret, testnet=True)
        print("Using Binance TestNet server")

    twm = ThreadedWebsocketManager()
    
    symbol = 'ETHUSDT'
    #'ETHUSDT' can be changed say to say (BNBUSDT)
    
    main()

In the main() function register to kline/candlestick stream instead of ticker stream of the previous bot, as below

def main():
    twm.start()

    twm.start_kline_socket(callback=handle_kline_message,
                           symbol=symbol, interval='1m')

    twm.join()  # main will exit if no join added

start_kline_socket() is a kline/candlestick stream with a callback (handle_kline_message), symbol (any cryptocurrency, in this case ‘ETHUSDT’) and interval (usually 1min). Add twm.join() to ensure that the main thread does not exit.

Step 5.)

Implement the callback function and run the program to check if the callback happens.

def handle_kline_message(candle_msg):
    pprint.pprint(f"kline message type: {candle_msg['e']}")
    pprint.pprint(candle_msg)
    kline = candle_msg['k']   # access the key 'k'
    is_kline_closed = kline['x']   # if true, then its end of current kline
    kline_close_value = kline['c']  # last or closing ETH value

In the callback, print the received payload (candle_msg) and the key value ‘e’. Extract the key ‘k’ into kline and using kline extract the keys ‘x’ and ‘c’. The key ‘e’ corresponds to msg type (in this case it prints kline), key ‘k’ is the actual kline msg that contains key ‘x’ (if the value is True, then its end of old kline interval and beginning of new interval) and the key ‘c’ corresponds to the last closing value of the symbol (here ethereum).

Step 6.)

Import ta-lib and numpy as it is needed to perform RSI calculations, and add an empty list kline_closed_values which will hold all the closing prices.

import talib
import numpy
kline_closed_values = []
RSI_MIN_PERIOD  = 2 # usually 14, but to test lets use 2 to reduce waiting time

Next is to add code for RSI calculations.

def handle_kline_message(candle_msg):
    pprint.pprint(f"kline message type: {candle_msg['e']}")
    pprint.pprint(candle_msg)
    kline = candle_msg['k']   # access the key 'k'
    is_kline_closed = kline['x']   # if true, then its end of current kline
    kline_close_value = kline['c']  # last or closing ETH value
    if is_kline_closed:
        print("kline closed at: {}".format(kline_close_value))
        kline_closed_values.append(float(kline_close_value))
        print(kline_closed_values)

        ## RSI calculations
        if len(kline_closed_values) > RSI_MIN_PERIOD:
            kline_np_closed_values = numpy.array(kline_closed_values)
            rsi = talib.RSI(kline_np_closed_values, RSI_MIN_PERIOD)
            print("RSI values:", rsi)
            last_calc_rsi = rsi[-1]
            print("RSI for trading caldculations: {}".format(last_calc_rsi))
            success = rsi_trading_logic(last_calc_rsi)
            print("trading was:",success)
            twm.stop()

Calculate RSI only when we reach a count > RSI_MIN_PERIOD. Ta-lib uses numpy arrays instead of the regular list, hence use numpy.array to get NumPy values.

  • Use the last calculated RSI only, as the rsi returned is a list.
  • Since the list gets updated; we must only use the last RSI value.
  • Call to the function rsi_trading_logic(last_calc_rsi) which implements the trading logic.
  • Stop the websocket stream with twm.stop().

Step 7.)

Finally, implement the trading logic rsi_trading_logic(). As the criteria to sell is RSI must be > 70 and to buy is RSI must be < 30, introduce two variables which mark these boundaries RSI_OVERBOUGHT and RSI_OVERSOLD:

RSI_OVERBOUGHT = 70
RSI_OVERSOLD = 30

# RSI logic to trade
def rsi_trading_logic(last_rsi):
    if last_rsi > RSI_OVERBOUGHT:
        try:
            print(" Sell Sell Sell...")
            order = client.order_market_sell(symbol=symbol, quantity=1)
            pprint.pprint(order)
            return True
        except Exception as e:
            print(e)
            return False
    elif last_rsi < RSI_OVERSOLD:
        try:
            print("Buy Buy Buy...")
            order = client.order_market_buy(symbol=symbol, quantity=1)
            pprint.pprint(order)
            return True
        except Exception as e:
            print(e)
            return False
    else:
        print("Do nothing.. Just Wait and Watch !!")
        # Add more code here such that if nothing happens, continue running instead of stopping

If last calculated rsi > RSI_OVERBOUGHT then sell using order_market_sell and if rsi < RSI_OVERSOLD then buy using order_market_buy. These functions are the same as used in bot1 design with try, except block.

Concluding Trading Logic 2

Running the whole program may result in buying or selling the ethereum with the given quantity. Further debugging may be needed if there is an exception. As we buy or sell only once, break out of the loop and stop the webstream traffic.

Important Note: Sometimes selling ethereum or bitcoin on testnet may show as success, but the order would have the status as EXPIRED. This is because the symbol has no liquidity on testnet. As per the link I can’t do Market order (always status ‘EXPIRED’), try other symbols such as ‘BNBUSDT’ instead of ’ETHUSDT’ or ‘BTCUSDT’.

Buying ethereum or bitcoin depends on the balance available in one’s Binance testnet account.

Conclusion

In this article we successfully designed two bots with different logic for trading on Binance testnet.

It is important to note that as the bots are using testnet, it may not always be successful as you have limited assets which testnet provides you every month.

Also it is to be noted that as per the Binance testnet “The Spot Test Network is periodically reset to a blank state. That includes all pending and executed orders. During that reset procedure, all users automatically receive a fresh allowance of all assets. These resets happen approximately once per month, and we do not offer prior notification for them” 

With some experience and expertise, you will be able to build the bot to actually trade with real currency on Binance.