Bollinger Bands Algorithm – Python Binance API for Crypto Trading

A Bollinger Band consists of three lines: a simple moving average, an upper band, and a lower band. The assumption is that if the real price crosses over one of the bands, this can be seen as a signal to trade in or our of a given asset. For cryptocurrencies, breakout trades are more frequently used due to the higher volatility.

This article is based on the full trading tutorial on the Not-Satoshi blog.

Intro

Before we begin, I would like to make a small request

  • If you don’t know the basics of binance and python-binance API.
  • If you want to know how to set up the development environment, set up a binance account or binance-testnet account. Then, you should please go through the previous course (Creating Your First Simple Crypto-Trading Bot with Binance API)  where these are explained in detail.
  • Please be aware of the following note:
##################### Disclaimer!! ###################################
# The bots built here with python 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 as part of the course on Binance.
####################################################################

Another important point:

In the algorithms we discuss, there are multiple buy/sell points to buy/sell crypto. It is up to you as to how to want to write the logic for buying and selling, e.g. In the bots we develop, buying or selling a crypto asset happens at all the buy/sell points using a for loop for each buy and sell point.

There can be multiple ways to implement the buy/sell logic, some are mentioned below

  1.  You can keep separate loops to buy and sell and keep looping until at least one buy and one sell occurs and then break.
  2.  You can choose to buy/sell only for a particular buy/sell signal. i.e. if market price is <= or >= a particular value from the buy/sell list. In this case, no for loop is needed here.
  3.  You can choose to buy/sell, by placing only limit orders and not market orders with the prices from the buy/sell list.

And so on….

Let’s Begin the journey

Now that we are clear on all these things we discussed we can start with our first trading algorithm – SMA. So see you soon in our first algorithm!!

PS: Follow the videos, along with the tutorial to get a better understanding of algorithms!

Feel free to check out the full code at the Finxter GitHub repository here.

Bollinger Band Basics

The Bollinger band is comprised of two bands lower and upper which form the boundaries of the tradeable asset such as crypto, stocks, etc. based on the historical prices. The lower and upper boundaries are calculated using Standard Deviation(SD).

Standard Deviation(SD) is a statistical tool that measures the deviation or dispersion of the data from the mean or average.

SD = √ ⎨ ⅀ (x-u)^2⎬   
             N
where u is the mean or average, x is the dataset and N is the number of elements in the dataset.  

The upper and lower bands are calculated as

upper band = SMA + 2 x SD
lower band = SMA - 2 x SD
where SMA is the simple moving average over a period of 20, and SD is the standard deviation

Below is an example representation of the Bollinger band. The middle line is the SMA for a period of 20. The upper and the lower lines are the 2 x standard deviation from the SMA line and form the boundary. An asset such as the crypto or stock values usually lies in between the upper and lower bands. Whenever the asset crosses the upper boundary, it is time to sell, and similarly, when the asset crosses the lower boundary, it is time to buy.

Fig: Bollinger band

Bot Trading Logic

Let’s start coding the Bollinger band algorithm. As previously followed, we will design it in steps.

Step 1: Housekeeping

import os
from binance.client import Client
import pprint
import pandas as pd     # needs pip install
import numpy as np
import matplotlib.pyplot as plt   # needs pip install

if __name__ == "__main__":
    # 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")

    pprint.pprint(client.get_account())
    # Change symbol here e.g. BTCUSDT, BNBBTC, ETHUSDT, NEOBTC
    symbol = 'BTCUSDT'  
    main()

Import the necessary packages (binance client, pandas, NumPy, and Matplotlib). At the start retrieve the Binance testnet API key and password using os.environ.get(). Initialize the Binance client with key, password, and testnet=true (We use only the testnet for the bot).

Any symbol can be used, here we use the bitcoin ‘BTCUSDT ‘ and trigger main().

Step 2: Data

def get_data_frame():
    # valid intervals - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M
    # request historical candle (or klines) data using timestamp from above, interval    either every min, hr, day or month
    # starttime = '30 minutes ago UTC' for last 30 mins time
    # e.g. client.get_historical_klines(symbol='ETHUSDTUSDT', '1m', starttime)
    # starttime = '1 Dec, 2017', '1 Jan, 2018'  for last month of 2017
    # e.g. client.get_historical_klines(symbol='BTCUSDT', '1h', "1 Dec, 2017", "1 Jan, 2018")
    starttime = '1 day ago UTC'  # to start for 1 day ago
    interval = '5m'
    bars = client.get_historical_klines(symbol, interval, starttime)
    pprint.pprint(bars)
    
    for line in bars:        # Keep only first 5 columns, "date" "open" "high" "low" "close"
        del line[5:]

    df = pd.DataFrame(bars, columns=['date', 'open', 'high', 'low', 'close']) #  2 dimensional tabular data
    return df


def bollinger_trade_logic():
    symbol_df = get_data_frame()

def main():
    bollinger_trade_logic()

As a second step, define main()macd_trade_logic() and get_data_frame(). We need historical data to start the Bollinger calculations. The function get_data_frame() uses the python-binance API get_historical_klines() to get the historical data for the given interval (5min) and start time (one day ago). Note that the interval and start time can be changed to any valid interval and start time (see comments or python-binance documentation for more details). Finally, use the pandas DataFrame() to generate the data frame for the first five columns (date, open, high, low, and close).

Step 3: Trading Logic

Calculate SMA for a period of 20, Standard deviation (SD), upper and lower band.

def bollinger_trade_logic():
    symbol_df = get_data_frame()
    period = 20
    # small time Moving average. calculate 20 moving average using Pandas over close price
    symbol_df['sma'] = symbol_df['close'].rolling(period).mean()
    # Get standard deviation
    symbol_df['std'] = symbol_df['close'].rolling(period).std()

    # Calculate Upper Bollinger band
    symbol_df['upper'] = symbol_df['sma']  + (2 * symbol_df['std'])
    # Calculate Lower Bollinger band
    symbol_df['lower'] = symbol_df['sma']  - (2 * symbol_df['std'])

The SMA is calculated using rolling() and mean() functions and SD using std() of Pandas data frame. As described, upper and lower are calculated using the mentioned formula above.

Step 4: Prepare Buy and Sell

The buy point is when close values are lesser than lower band values, while the sell point is when close values are greater than the upper band values. To compare the ‘close’, ‘upper’, and ‘lower’ columns, np.where() function can be used. The np. where(), function can be thought of as an if-else condition used in Python.

However, directly comparing these columns of Pandas data frame is not possible as some values can be of string type such as NaN (not a number), while others can be of float type. To compare the numeric values we need to convert to a common type such as float.

    # prepare buy and sell signals. The lists prepared are still panda data frames with float nos
    close_list = pd.to_numeric(symbol_df['close'], downcast='float')
    upper_list = pd.to_numeric(symbol_df['upper'], downcast='float')
    lower_list = pd.to_numeric(symbol_df['lower'], downcast='float')

    symbol_df['buy'] = np.where(close_list < lower_list,   symbol_df['close'], np.NaN )
    symbol_df['sell'] = np.where(close_list > upper_list,   symbol_df['close'], np.NaN )

To get a common Pandas data frame type ‘float’,  the function to_numeric() can be used.

Step 5: File Writing

At this stage, it would be a good idea to see all the columns output to a text file. We can use the regular file open and write functions to write to a file.

  with open('output.txt', 'w') as f:
        f.write(
                symbol_df.to_string()
               )

When you run the application, you will see that the output.txt has a date, open, high, low, close, upper, lower, buy, and sell columns. You can observe that the date column is in Unix timestamp (ms) and not in a human-readable format. This can be changed to a human-readable format using the Pandas to_datetime() function.

    # To print in human-readable date and time (from timestamp)
    symbol_df.set_index('date', inplace=True)
    symbol_df.index = pd.to_datetime(symbol_df.index, unit='ms')

    with open('output.txt', 'w') as f:
        f.write(symbol_df.to_string())

Step 6: Plot

We can now visually interpret all the important symbol-related information. This can be done by plotting the graph using Matplotlib and making a call to plot_graph() from bollinger_trade_logic().

def plot_graph(df):
    df=df.astype(float)
    df[['close', 'sma','upper', 'lower']].plot()
    plt.xlabel('Date',fontsize=18)
    plt.ylabel('Close price',fontsize=18)
    x_axis = df.index
    plt.fill_between(x_axis, df['lower'], df['upper'], color='grey',alpha=0.30)

    plt.scatter(df.index,df['buy'], color='purple',label='Buy',  marker='^', alpha = 1) # purple = buy
    plt.scatter(df.index,df['sell'], color='red',label='Sell',  marker='v', alpha = 1)  # red = sell


    plt.show()

Call the above function from bollinger_trade_logic().

   plot_graph(symbol_df)     # can comment this line if not needed

Step 7: Buy or Sell

Lastly, trading i.e the actual buying or selling of the crypto must be implemented.

def buy_or_sell(df):

    buy_list  = pd.to_numeric(df['buy'], downcast='float')
    sell_list = pd.to_numeric(df['sell'], downcast='float')

    for i in range(len(buy_list)):
        # get current price of the symbol
        current_price = client.get_symbol_ticker(symbol =symbol)
        if float(current_price['price']) >= sell_list[i]:  # sell order
            print("sell sell sell...")
            sell_order = client.order_market_sell(symbol=symbol, quantity=0.01)
            print(sell_order)
        elif float(current_price['price']) <= buy_list[i]:  # buy order
            print("buy buy buy...")
            buy_order = client.order_market_buy(symbol=symbol, quantity=0.001)
            print(buy_order)
        else:
            print("...do nothing...")

Prepare a buy and sell list. Compare with the current market price of the symbol. Accordingly, place a Binance buy/sell order if the price is lesser/greater than the price in the buy/sell list.

The above function can be called from the bollinger_trade_logic() at the end.

buy_or_sell(symbol_df)

Full Code

Here’s the full code of this bot for copy&paste:

# Author: Yogesh K for finxter.com
# python script: trading with Bollinger bands for Binance


import os
from binance.client import Client
import pprint
import pandas as pd     # needs pip install
import numpy as np
import matplotlib.pyplot as plt   # needs pip install

def get_data_frame():
    # valid intervals - 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M
    # request historical candle (or klines) data using timestamp from above, interval either every min, hr, day or month
    # starttime = '30 minutes ago UTC' for last 30 mins time
    # e.g. client.get_historical_klines(symbol='ETHUSDTUSDT', '1m', starttime)
    # starttime = '1 Dec, 2017', '1 Jan, 2018'  for last month of 2017
    # e.g. client.get_historical_klines(symbol='BTCUSDT', '1h', "1 Dec, 2017", "1 Jan, 2018")
    starttime = '1 day ago UTC'  # to start for 1 day ago
    interval = '5m'
    bars = client.get_historical_klines(symbol, interval, starttime)
    pprint.pprint(bars)
    
    for line in bars:        # Keep only first 5 columns, "date" "open" "high" "low" "close"
        del line[5:]

    df = pd.DataFrame(bars, columns=['date', 'open', 'high', 'low', 'close']) #  2 dimensional tabular data
    return df

def plot_graph(df):
    df=df.astype(float)
    df[['close', 'sma','upper', 'lower']].plot()
    plt.xlabel('Date',fontsize=18)
    plt.ylabel('Close price',fontsize=18)
    x_axis = df.index
    plt.fill_between(x_axis, df['lower'], df['upper'], color='grey',alpha=0.30)

    plt.scatter(df.index,df['buy'], color='purple',label='Buy',  marker='^', alpha = 1) # purple = buy
    plt.scatter(df.index,df['sell'], color='red',label='Sell',  marker='v', alpha = 1)  # red = sell


    plt.show()


def buy_or_sell(df):

    buy_list  = pd.to_numeric(df['buy'], downcast='float')
    sell_list = pd.to_numeric(df['sell'], downcast='float')

    for i in range(len(buy_list)):
         # get current price of the symbol
        current_price = client.get_symbol_ticker(symbol =symbol)
        if float(current_price['price']) >= sell_list[i]:  # sell order
            print("sell sell sell...")
            sell_order = client.order_market_sell(symbol=symbol, quantity=0.01)
            print(sell_order)
        elif float(current_price['price']) <= buy_list[i]:  # buy order
            print("buy buy buy...")
            buy_order = client.order_market_buy(symbol=symbol, quantity=0.001)
            print(buy_order)
        else:
            print("...do nothing...")




def bollinger_trade_logic():
    symbol_df = get_data_frame()
    period = 20
    # small time Moving average. calculate 20 moving average using Pandas over close price
    symbol_df['sma'] = symbol_df['close'].rolling(period).mean()
    # Get standard deviation
    symbol_df['std'] = symbol_df['close'].rolling(period).std()

    # Calculate Upper Bollinger band
    symbol_df['upper'] = symbol_df['sma']  + (2 * symbol_df['std'])
    # Calculate Lower Bollinger band
    symbol_df['lower'] = symbol_df['sma']  - (2 * symbol_df['std'])

    # To print in human readable date and time (from timestamp)
    symbol_df.set_index('date', inplace=True)
    symbol_df.index = pd.to_datetime(symbol_df.index, unit='ms') # index set to first column = date_and_time
 
    # prepare buy and sell signals. The lists prepared are still panda dataframes with float nos
    close_list = pd.to_numeric(symbol_df['close'], downcast='float')
    upper_list = pd.to_numeric(symbol_df['upper'], downcast='float')
    lower_list = pd.to_numeric(symbol_df['lower'], downcast='float')

    symbol_df['buy'] = np.where(close_list < lower_list,   symbol_df['close'], np.NaN )
    symbol_df['sell'] = np.where(close_list > upper_list,   symbol_df['close'], np.NaN )

    with open('output.txt', 'w') as f:
        f.write(
                symbol_df.to_string()
               )
    
#    plot_graph(symbol_df)

    buy_or_sell(symbol_df)

    




def main():
    bollinger_trade_logic()


if __name__ == "__main__":

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

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

    pprint.pprint(client.get_account())

    symbol = 'BTCUSDT'   # Change symbol here e.g. BTCUSDT, BNBBTC, ETHUSDT, NEOBTC
    main()

Conclusion

We saw what Bollinger bands are and how they can be used for crypto trading. The bot with the Bollinger algorithm was implemented. You can run the application and see the buy/sell profits. The buy_or_sell() logic depends on the user requirements and is not one size fits all. You can always implement the logic which gives you maximum profit during the trade.

Where to Go From Here

Cryptocurrency trading is a highly sought-after skill in the 21st century. Freelancers who excel in crypto trading are paid up to $300 per hour. If you want to learn the ins and outs of trading, check out our full course on the Finxter Computer Science academy: