Quantitative technology of data - Part III understanding backtrade

Posted by leocon on Wed, 09 Feb 2022 16:13:08 +0100

Interested children, especially children's shoes, especially in the application of big data and artificial intelligence in quantification, can pay attention to my official account. If the script code is not clear, please see the official account number:

datahomex:

Through the introduction of the previous two articles in this series, we must have a basic understanding of procedural trading, but it is only procedural trading, not quantitative trading.

Today, datajun introduces you an open-source quantitative framework based on python, backtrader, which opens our formal learning path of quantitative trading.

1, Know backtrader

1.1 INTRODUCTION

backtrader official website:

https://www.backtrader.com/

Take a look at the introduction of backtrader on the official website:

A feature-rich Python framework for backtesting and trading

backtrader allows you to focus on writing reusable trading strategies, indicators and analyzers instead of having to spend time building infrastructure.

backtrader is an open source framework based on python for backtesting and trading. It enables users to focus on the development of trading strategies and indicators without caring about the infrastructure of the system.

In short, backtrader helps us set up the overall framework of quantitative trading and reserves rich interfaces for developers to use. It can support all links of quantitative trading, including data loading, policy development, policy backtracking and real-time trading.

 

1.2 components

backtrader core components:

1. Back test system (Cerebro):

  • It is necessary to set systematic parameters, such as initial capital, commission, trading position size, etc;

  • It is necessary to inject custom developed transaction strategies and test data sets;

2. Strategy:

The core part of quantitative trading development needs to develop buy and sell signals and design trading strategies;

3. Data Feed: the component of the test data set loaded into the back test system;

The above three are the core modules of backtrader in quantitative development. There are also some auxiliary components, such as plotting, built-in Indicators, Analyzers, LiveTrading, etc.

The functions and instructions of these components are described in detail on the official website. Of course, we will also introduce them one by one in the following series of articles.

 

2, Hello Algotrading!

We first use an introductory example given on the official website to appreciate the charm of the backtrade framework, execute the following script, and finally generate the performance of the trading strategy on the test data set.

2.1 interpretation of procedures

from datetime import datetime
import backtrader as bt

# Create a subclass of Strategy to define the indicators and logic

# The first step is to develop a custom Strategy, which needs to inherit the Strategy class of backtrader
class SmaCross(bt.Strategy):
    # list of parameters which are configurable for the strategy
    params = dict(
        pfast=10,  # period for the fast moving average
        pslow=30   # period for the slow moving average
    )
    # General log printing function, which can print orders and transaction records. It is optional and optional
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
        
	# The initialization function initializes the calculation of attributes and indicators. It is only executed once during the operation of the whole back test system
    def __init__(self):
        sma1 = bt.ind.SMA(period=self.p.pfast)  # fast moving average
        sma2 = bt.ind.SMA(period=self.p.pslow)  # slow moving average
        self.crossover = bt.ind.CrossOver(sma1, sma2)  # crossover signal
	
	# Order status message notification function
	def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return
        # Check whether the order is completed
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))
            else:
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))
	
	# Each trading day will be called in turn
    def next(self):
        if not self.position:  # not in the market
            if self.crossover > 0:  # if fast crosses slow to the upside
                self.buy()  # enter long

        elif self.crossover < 0:  # in the market & cross to the downside
            self.close()  # close long position
            
# Create a data feed
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
datapath = os.path.join(modpath,'603186.csv')
data = bt.feeds.GenericCSVData(
        dataname=datapath,
        fromdate=datetime.datetime(2010, 1, 1),
        todate=datetime.datetime(2020, 4, 12),
        dtformat='%Y%m%d',
        datetime=2,
        open=3,
        high=4,
        low=5,
        close=6,
        volume=10,
        reverse=True
    )
    
# The back test system is instantiated and translated into brain by Cerebro. The back test system is the control center of the whole backtrader
cerebro = bt.Cerebro()  # create a "Cerebro" engine instance
# Initial capital 10000
cerebro.broker.setcash(10000)
# commission
cerebro.broker.setcommission(commission=0.002)
cerebro.adddata(data)  # Add the data feed
cerebro.addstrategy(SmaCross)  # Add the trading strategy
cerebro.run()  # run it all
cerebro.plot()  # and plot it with a single command

Steps for using the backtrader framework:

1. The first step is to develop your own trading strategy class

class SmaCross(bt.Strategy)

The custom transaction strategy class needs to inherit bt.Strategy class. bt.Strategy class provides many methods for us to rewrite. Here are some of the most important functions:

init() function: initialization function. The back test process is only executed once. It is recommended to implement the one-time static indicators required by the strategy in this function, such as the long-term and short-term moving average indicators in the above example;

next() function: the core function, which will be called once every trading day. It is necessary to realize the trading order logic according to the trading signal of the day. For example, when the short-term moving average line crosses the long-term moving average line, buy and go long; Otherwise, sell and close the position.

Description of transaction function in Code:

self.buy(): buy long

self.sell(): sell short

In addition, common transaction functions:

self.close(): warehouse clearing

self.cancel(): cancel the order

notify_order function: order message notification function, which prints the order information of each transaction, including transaction price, transaction amount, commission, etc.

2. The second step is to prepare the back test data set

modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
datapath = os.path.join(modpath,'orcl.txt')
data = bt.feeds.YahooFinanceCSVData(dataname= datapath,
                                 fromdate=datetime(2000, 1, 1),
                                 todate=datetime(2000, 12, 31))

With the strategy, we need the back test data set to test the effectiveness of our strategy. The backtrader framework has strict format requirements for the back test data set,

The default needs to include:

datetime, open, high, low, close, volume

The datafeeds data module in the backtrader supports many types of data forms, including third-party api, real-time data (Yahoo, VisualChart, Sierra Chart, Interactive Brokers, Yingtou, OANDA, Quandl), local txt, CSV files, pandas dataframe, incluxdb, MT4CSV, etc. the most commonly used third-party api may be the local file or dataframe.

If you have studied dbms (relational database), you can understand datafeeds as two-dimensional tables. If there is no database foundation, you can imagine it as excel tables. Each row represents the trading information of the stock on a trading day, and each column can be regarded as a line in the backtrader.

 

Many concepts of datafeeds in backtrader are still difficult to understand. We will explain them in detail later.

3. Create a back test system instance (straight white point can also be said to create a brain)

cerebro = bt.Cerebro()  # create a "Cerebro" engine instance

After the back test instance is created, the transaction strategy and back test data are injected into the back test instance. You can also set the initial capital, commission and other information. Finally, the execution of the back test instance will print the transaction details of the whole transaction strategy on the test data set.

# Initial capital 10000
cerebro.broker.setcash(10000)
# commission
cerebro.broker.setcommission(commission=0.002)
# Back test data set
cerebro.adddata(data)  # Add the data feed
# Trading policy rate
cerebro.addstrategy(SmaCross)  # Add the trading strategy
2019-06-12, BUY EXECUTED, Price: 29.61, Cost: 2961.00, Comm 0.00
2019-10-10, SELL EXECUTED, Price: 39.79, Cost: 2961.00, Comm 0.00
2019-10-28, BUY EXECUTED, Price: 46.99, Cost: 4699.00, Comm 0.00
2019-11-20, SELL EXECUTED, Price: 43.00, Cost: 4699.00, Comm 0.00
2019-12-20, BUY EXECUTED, Price: 40.86, Cost: 4086.00, Comm 0.00
2019-12-24, SELL EXECUTED, Price: 38.64, Cost: 4086.00, Comm 0.00
2020-01-10, BUY EXECUTED, Price: 43.69, Cost: 4369.00, Comm 0.00
2020-03-23, SELL EXECUTED, Price: 49.00, Cost: 4369.00, Comm 0.00

2.2 interpretation of results

 

This simple trading strategy performed well, and the overall profit was unexpected. However, this is not the focus of our concern. Our focus is to introduce how to look at this figure:

1. The top part is the current cash and assets of the account. The red line represents the cash value and the blue line represents the total assets. You can see that the total cash of the total association is 10928

2. Below the K line, the red line is sma10, the blue line is sma30, and the red line is the closing price of the day

3. The red inverted triangle indicates selling short and the green triangle indicates buying long

4. The bottom part is the buying and selling signal line. When the value is greater than 0, it means that the short-term moving average uploads the long-term moving average to buy; When it is less than 0, it is sold.

 

3, Develop a custom policy

3.1 implement the entry strategy of the previous period

In the last issue, dataking developed a simple entry strategy. Let's use the backtrader framework to test it.

Buying signal: sma10 is worn on sma5. At that time, we obtained K-line data at the level of 5 minutes;

The selling signal satisfies any of the following conditions:

1) sma5 and sma10;

2) Current closing price > 1.01 * buying price and current (sma5-sma10) < previous period (sma5-sma10) * 0.95;

3) Current closing price < 0.97 * buying price;

# Implement the previous entry strategy
class SmaCross(bt.Strategy):
    # list of parameters which are configurable for the strategy
    params = dict(
        pfast=10,  # period for the fast moving average
        pslow=30   # period for the slow moving average
    )
​
    # General log printing function, which can print orders and transaction records. It is optional and optional
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
​
    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log('BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))
                self.buyprice = order.executed.price # Record cost price
            else:
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))
        return super().notify_order(order)
​
    def __init__(self):
        self.dataclose = self.datas[0].close
        self.sma1 = bt.ind.SMA(period=self.p.pfast)  # fast moving average
        self.sma2 = bt.ind.SMA(period=self.p.pslow)  # slow moving average 
        self.dif = self.sma1 - self.sma2
        self.crossover = bt.ind.CrossOver(self.sma1, self.sma2)  # crossover signal
        self.buyprice = None # Purchase cost price
​
    def next(self):
        if not self.position:  # not in the market
            if self.crossover > 0:  # if fast crosses slow to the upside
                self.buy()  # enter long
        else:
            condition = (self.dataclose[0] - self.buyprice) / self.dataclose[0]
            if self.crossover < 0 or condition < 0.97 or (condition>1.01 and self.dif[0] < self.dif[-1] * 0.95) :  
                # in the market & cross to the downside
                self.close(volume=False)  # close long position

 

3.2 loading custom datasets

In [Article 1 coming children's shoes], the data master obtained the K-line data of 5 minutes level by calling the / market/history/kline interface and made local persistent storage. This time, we will use this part of data to test the above simple strategy back and forth. The specific code is as follows:

# Load data into model
from sqlalchemy import create_engine
import pandas as pd
import time
engine = create_engine('mysql://root:123456@127.0.0.1/stock?charset=utf8mb4') 
conn = engine.connect()
sql=("select * from huobi_daily")
df = pd.read_sql(sql,engine)
df['date'] = df['id'].apply(lambda x:time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(x)))
df['date'] = pd.to_datetime(df['date'])
df.index = df.date
data = bt.feeds.PandasData(dataname=df)
cerebro.adddata(data) 

 

3.3 operation results

 

Summary:

Today, we have completed the development of a complete quantitative trading program. From the back test, the effect of our strategy in the previous period is very general in the short-term trading of several currencies. Of course, this is reasonable. After all, the strategy is too simple. Later, we will focus on the development of custom strategies, which may be combined with complex technologies such as statistical model, machine learning and deep learning, However, the whole framework and process are the same.

In addition, the backtrader framework introduced today will continue to be used later, and more functions and details will be explained at that time. I hope you can go further and further on the road of quantification with the data gentleman.

Topics: Machine Learning