Python trading bot from scratch (using Binance API), part 2 / Habr

Python trading bot from scratch (using Binance API), part 2 / Habr

IMPORTANTLY: this article has been created exclusively for educational purposes. Trading on the stock exchange is associated with a big risk and can lead to significant losses.

The first part of the article is available at exiled.

Introduction (and reminders)

In the previous part of the article, we analyzed how to write the simplest trading bot on python using the Binance API. The strategy we used was based on a static price corridor on the ETHUSDT pair. Such a strategy is very rarely used in practice and in our case served only as an example, but it illustrated important techniques when trading with real strategies.

Let us briefly recall the main steps of the strategy from the previous part of the article:

  1. Request the last price of the asset on the exchange (without division into BID or ASK in the simplest case);

  2. If we have gone beyond the upper limit of the corridor: close the long and open the short;

  3. If we have gone beyond the lower limit of the corridor: close the short and open the long.

Now, using the algorithm above, we can recall the general structure of the language implementation of this strategy python:

# imports and functions are omitted here as they stay exactly the same 

# flags to track the exchanges to avoid duplicated entries
in_short = False
in_long = False

# main loop
while True:
    # get latest price for the symbol
    latest_price = client.futures_symbol_ticker(symbol=symbol)['price']
    latest_price = float(latest_price)
    
    # check the condition (these conditions could be substitued by any other)
    if "<condintion>":
        # if we are in long position, close it
        if in_long:
            long_close(symbol=symbol, quantity=1)
            in_long = False
        # if we are not in short position, open it
        if not in_short:
            short_open(symbol=symbol, quantity=1)
            in_short = True
    elif "<other_contition>":
        # if we are in long position, close it
        if not in_long:
            long_open(symbol=symbol, quantity=1)
            in_long = True
        # if we are in short position, close it
        if in_short:
            short_close(symbol=symbol, quantity=1)
            in_short = False

    # 1 second sleep
    time.sleep(1)

That is, if you replace the conditions <condition> and <other_condition> for some real-world conditions, the code above can be the basis for a full-fledged trading strategy.

Basic (statistical) strategies

Moving on to real single-asset trading strategies (multi-asset trading deserves a separate serious conversations) I would like to highlight three main directions in statistical strategies (statistical strategies will be called those strategies that are based only on data about the previous price and trade volumes and do not take into account “external” factors affecting the market):

  1. Moving average crossover: the strategy considers two moving averages with different periods and goes long when the line with a shorter period crosses the line with a longer period from the bottom up and goes short, conversely, when the line with a shorter period crosses the line with a longer period from the top down.

  2. Dynamic price corridor (channel breakout): the strategy considers one moving average and a price corridor, the upper limit of which corresponds to the maximum of the price for the previous n candles, and the lower limit to the minimum of the previous n candles.

  3. Bollinger bands (Bollinger bands): the strategy also builds a price corridor, but by different rules

    The upper limit of the corridor = moving average + 2 standard deviations of the price;

    The lower limit of the corridor = moving average – 2 standard deviations of the price.

    The rules for strategy entry and exit are defined as follows:

    A short is opened when the price crosses the upper Bollinger line (from bottom to top). The short is closed at the crossing of the moving average price (from top to bottom).

    When the price crosses the lower Bollinger line (from top to bottom), a long is opened. Long is closed at the crossing of the moving average price (from bottom to top).

Bollinger lines

In this article, we will analyze Bollinger lines in detail (the implementation of other strategies, after mastering Bollinger lines, should not be difficult).

Let’s start with the definition:

Bollinger lines (bands). (Bollinger bands) – a tool for technical analysis of financial markets, reflecting current deviations in the price of a share, commodity or currency.

The main assumption of the strategy is that if the price deviates significantly from the moving average, it will revert back. The advantage of Bollinger lines is the simplicity of their calculation. Let’s imagine that we have data on the closing prices of 5-minute candles of some asset (see the table below), then we can calculate the Bollinger lines using the following formulas:

The table presents the closing prices and the values ​​of the moving averages and deviations, as well as the values ​​of the Bollinger lines:

Closing time

Closing price

Moving average with a period of 3 candles (15 minutes)

Standard deviation with a period of 3 candles (15 minutes)

Bollinger upper bound

Bollinger upper bound

12:00

100.0

NaN

NaN

NaN

NaN

12:05 p.m

100.1

NaN

NaN

NaN

NaN

12:10

100.2

100.1

0.1

100.3

99.9

12:15 p.m

100.1

100.133333

0.57735

100.248803

100.017863

12:25 p.m

100.3

100.2

0.1

100.4

100.0

12:30 p.m

100.5

100.3

0.2

100.7

99.9

12:35 p.m

100.2

100.333333

0.152753

100.638838

100.027828

12:40 p.m

100.0

100.233333

0.251661

100.736656

99.730011

12:45 p.m

99.5

99.9

0.360555

100.621110

99.178890

on Figure 1 Below are the Bollinger Bands (blue and purple) and the Moving Average (orange). In addition to the Bollinger lines, entry and exit points for trades (according to the strategy) are presented. They are marked with vertical lines. The vertical dotted line corresponds to the moment of entry into the agreement, and the vertical solid line to the moment of exit from the agreement. The number next to the signature at the bottom of the deal represents its income (loss) in percentage.

Fig. 1. Bollinger lines and moving average, as well as the opening and closing moments of trades according to the strategy

Python implementation

We can now use the theory above to implement the strategy in language python. The essential difference between the current strategy and the strategy of the static price corridor in the first part of the article is that now we want to receive the price of the asset only at the moment of closing the candle, that is, every 5 minutes (constant 5 минут can be easily changed).

Calculation of Bollinger lines

The code below shows how you can calculate Bollinger Bands for 5-minute intervals with a 3-candle (15-minute) period:

import numpy as np
import time
from datetime import datetime
from binance.client import Client

# list to keep all closing prices
prices = []

# lists to keep all stats needed
moving_average_values = []
bollinger_band_high_values = []
bollinger_band_low_values = []

# flags to track the exchanges to avoid duplicated entries
in_short = False
in_long = False

# period of Bollinger bands
period = 3

# main loop
while True:
    # if current candlestick closed
    if datetime.now().minute % 5 == 0:
        # get latest price for the symbol
        latest_price = client.futures_symbol_ticker(symbol=symbol)['price']
        latest_price = float(latest_price)
        prices.append(latest_price)
    
        # print latest price with current time
        latest_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        print(latest_time, latest_price)

        # calculate moving average and deviation
        ma = np.mean(prices[-period:])
        moving_average_values.append(ma)

        std = np.std(prices[-period:], ddof=1)

        # calculate Bollinger bands
        bb_high = ma + 2 * std
        bb_low = ma - 2 * std

        bollinger_band_high_values.append(bb_high)
        bollinger_band_low_values.append(bb_low)

Rules of entry (exit) to transactions

Once we have the values ​​of the Bollinger lines and the moving average, we can move on to the implementation of the strategy itself. To begin with, let’s analyze the terms of entering into the agreement:

# check if current closing price is higher than the upper band
# but previous closing price is lower than the upper band
# this indicates that the crossing occured on current candlestick
if prices[-2] < bollinger_band_high_values[-2] and prices[-1] > bollinger_band_high_values[-1]:
    # if we are not in short position, open it
    if not in_short:
        short_open(symbol=symbol, quantity=1)
        in_short = True

# check if current closing price is lower than the lower band
# but previous closing price is higher than the lower band
# this indicates that the crossing occured on current candlestick
if prices[-1] < bollinger_band_low_values[-1] and prices[-2] > bollinger_band_low_values[-2]:
    # if we are not in long position, open it
    if not in_long:
        long_open(symbol=symbol, quantity=1)
        in_long = True

Now let’s analyze the conditions for exiting the agreement:

# check if current closing price is lower than the moving average
# but previous closing price is higher than the moving average
# this indicates that the crossing occured on current candlestick
if prices[-2] > moving_average_values[-2] and prices[-1] < moving_average_values[-1]:
    if in_short:
        short_close(symbol=symbol, quantity=1)
        in_short = False

# vice versa
if prices[-2] < moving_average_values[-2] and prices[-1] > moving_average_values[-1]:
    if in_long:
        long_close(symbol=symbol, quantity=1)
        in_long = False

In the end, let’s put all the pieces together and get a complete strategy!

import numpy as np
import time
from datetime import datetime
from binance.client import Client

# exchange operations:
def short_open(symbol, quantity):
    client.futures_create_order(symbol=symbol, 
                                side="SELL", 
                                type="MARKET", 
                                quantity=quantity)
    
def short_close(symbol, quantity):
    client.futures_create_order(symbol=symbol, 
                                side="BUY", 
                                type="MARKET", 
                                quantity=quantity)
    
def long_open(symbol, quantity):
    client.futures_create_order(symbol=symbol, 
                                side="BUY", 
                                type="MARKET", 
                                quantity=quantity)

def long_close(symbol, quantity):
    client.futures_create_order(symbol=symbol, 
                                side="SELL", 
                                type="MARKET", 
                                quantity=quantity)

# initialize Client with your API keys
api_key = "<your_api_key>"
api_secret = "<your_private_api_key>"
client = Client(api_key,  api_secret)

# traded symbol
symbol = "ETHUSDT"

# list to keep all closing prices
prices = []

# lists to keep all stats needed
moving_average_values = []
bollinger_band_high_values = []
bollinger_band_low_values = []

# flags to track the exchanges to avoid duplicated entries
in_short = False
in_long = False

# period of Bollinger bands
period = 3

# main loop
while True:
    # if current candlestick closed
    if datetime.now().minute % 5 == 0:
        # get latest price for the symbol
        latest_price = client.futures_symbol_ticker(symbol=symbol)['price']
        latest_price = float(latest_price)
        prices.append(latest_price)
    
        # print latest price with current time
        latest_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        print(latest_time, latest_price)

        # calculate moving average and deviation
        ma = np.mean(prices[-period:])
        moving_average_values.append(ma)

        std = np.std(prices[-period:], ddof=1)

        # calculate Bollinger bands
        bb_high = ma + 2 * std
        bb_low = ma - 2 * std

        bollinger_band_high_values.append(bb_high)
        bollinger_band_low_values.append(bb_low)

        # descion of the strategy
        # short exchanges
        if prices[-2] < bollinger_band_high_values[-2] and prices[-1] > bollinger_band_high_values[-1]:
            if not in_short:
                short_open(symbol=symbol, quantity=1)
                in_short = True

        if prices[-2] > moving_average_values[-2] and prices[-1] < moving_average_values[-1]:
            if in_short:
                short_close(symbol=symbol, quantity=1)
                in_short = False

        # long conditions
        if prices[-1] < bollinger_band_low_values[-1] and prices[-2] > bollinger_band_low_values[-2]:
            if not in_long:
                long_open(symbol=symbol, quantity=1)
                in_long = True
        
        if prices[-2] < moving_average_values[-2] and prices[-1] > moving_average_values[-1]:
            if in_long:
                long_close(symbol=symbol, quantity=1)
                in_long = False

    time.sleep(1)

Thanks for reading!

Related posts