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
andpython-binance
API. - If you want to know how to set up the development environment, set up a
binance
account orbinance-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
- You can keep separate loops to buy and sell and keep looping until at least one buy and one sell occurs and then break.
- 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.
- 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.
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: