【Application(5)】Modular Backtesting System
Use TEJ API to construct our own backtest system.
Preface
Trade Backtest is a more scientific approach to test strategy nowadays. Although the past cannot represent the future. The result of backtest can provide us some information about the strategy. However, if we have to run different strategy, we need to build a new system. This way will waste our much time and it is inefficient. Therefore, we try to modular the system and focus on developing the strategy.
The Editing Environment and Modules Required
We use Windows OS and Jupyter Notebook in this article.
import tejapi
import pandas as pd
import numpy as np
tejapi.ApiConfig.api_key = 'Your Key'
tejapi.ApiConfig.ignoretz = True
Note: Remember to replace ‘Your key’ with the one you applied for. The last line indicates that we ignore the time zone.
The highlights of the article
- Pre-planning of Building System
- Modular System
Building System
Step 1. Thinking about what tables or variables we might need.
- Stock Pool
- Raw Data
- Trade Strategy
- Trade Signal
- Backtesting
- Sell Variable
- Buy Variable
Consider the columns required for the process.
Step 2. Creating class
name backtest
class backtest():
Step 3. All of the def
is created under this class
.
def __init__(self, target_list):
# 設定初始值
# 股票池
self.target_list = target_list# 訊號表
self.signal_data = pd.DataFrame()
# 交易表
self.action_data = pd.DataFrame()
# 獲利數
self.protfolio_profit = []
# 成本數
self.protfolio_cost = []
Raw also need to put it into this class. target_list
is our stock pool.
# 資料庫
self.data = tejapi.get('TWN/APRCD',
coid = target_list,
mdate={'gte':'2020-01-01', 'lte':'2020-12-31'},
opts={'columns':['coid','mdate','close_d',
'volume']},
chinese_column_name=True,
paginate=True).reset_index(drop=True)
Step 4. Making Strategy, here we choose common strategies. We only explain specific point. To see full code, you can click this link.
- Price and Volume Strategy
We will buy the stock if the price and volume break through the highest price and volume in the past 20 days.
def price_volume(self, specific=None):
# 策略更新
self.signal_data = pd.DataFrame()
self.action_data = pd.DataFrame()
Generate transaction signals for each stock.
# 股票池每一檔股票都跑一次訊號產生
for i in self.target_list:
target_data = self.data[self.data['證券代碼'] == i]
Calculate the highest and lowest price and volume in past 10 and 20 days.
rolling_max = target_data[['收盤價(元)', '成交量(千股)']].rolling(20).max()rolling_min = target_data[['收盤價(元)', '成交量(千股)']].rolling(10).min()
Buy
stock_data['買入訊號判斷'] = np.where((stock_data['收盤價(元)'] == stock_data['收盤價(max)']) & (stock_data['成交量(千股)'] == stock_data['成交量(max)']), -1, 0)
Sell
stock_data['賣出訊號判斷'] = np.where((stock_data['收盤價(元)'] == stock_data['收盤價(min)']) & (stock_data['成交量(千股)'] == stock_data['成交量(min)']), 1, 0)
Offset on last day
remain_stock = self.signal_data.iloc[:-1,8:10].sum().sum()self.signal_data.iloc[-1,9] = 0
self.signal_data.iloc[-1,8] = 0if remain_stock < 0:
self.signal_data.iloc[-1,9] = -remain_stock
else:
self.signal_data.iloc[-1,8] = -remain_stock
- Buy and Hold strategy
def buy_hold(self, specific=None):
# 策略更新
self.signal_data = pd.DataFrame()
self.action_data = pd.DataFrame()
Generate transaction signals for each stock.
# 股票池每一檔股票都跑一次訊號產生
for i in self.target_list:
target_data = self.data[self.data['證券代碼'] == i]
Buy on first day and Sell on last day
target_data['買入訊號判斷'] = 0
target_data['賣出訊號判斷'] = 0
# 第一天買入,最後一天賣出
target_data.iloc[0, 4] = -1
target_data.iloc[-1,5] = 1
Step 5. Processing Backtest
def run(self, specific=None):
# 做出交易紀錄表
trade_data = pd.DataFrame(index= self.data['年月日'], columns=self.target_list).fillna(0.0)
action_data = self.signal_data[(self.signal_data['買入訊號判斷'] != 0) | (self.signal_data['賣出訊號判斷'] != 0)]
Calculate Profit
# 計算個股總獲利
self.protfolio_profit.append(sum(action_data['交易'].tolist()))
Calculate Cost
# 計算個股總成本
self.protfolio_cost.append(-action_data[action_data['買入訊號判斷'] < 0]['交易'].sum())
Step 6. Calculate Return on Investment
def ROI(self):
# 報酬率
return_profit = sum(self.protfolio_profit) /
sum(self.protfolio_cost)return return_profit
Review the performance of the market
We usually compare our strategy to market return. That is because we want beat the market to obtain excess profits.
Step 1. Creating class
name market
, here we use ‘Y9997’ to represent market index.
market = backtest(['Y9997'])
Step 2. Chooses the strategy
market.buy_hold()
Step 2. Backtesting
market.run()
Step 3. Review the performance
market.ROI()
Custom our own portfolio
Now we want to use MSCI to be our portfolio. MSCI is an acronym for Morgan Stanley Capital International. It is an investment research firm that provides stock indexes, portfolio risk and performance analytics. Code of Database is TWN/AIDXS
, and we can find MSCI portfolio in 2020.
data_MSCI = tejapi.get('TWN/AIDXS',
coid = 'MSCI',
mdate= '2019-12-31',
opts={'columns':['coid','mdate','key3']},
chinese_column_name=True)
Filter the data and save it to the list
.
MSCI_list = data_MSCI['成份股'].unique().tolist()
Delete the Chinese name.
MSCI_list = [coid[:4] for coid in MSCI_list]
Due to the effect of module, we do not need to rewrite the process of backtest. Just need to change target_list
to get the result we want.
Step 1. Creating class
name msci
, target_list
is MSCI_list
.
msci = backtest(MSCI_list)
Step 2. Chooses the strategy
msci.price_volume()
Step 3. Backtesting
msci.run()
Step 4. Review the performance
msci.ROI()
Conclusion
There will be a strong bull market in Taiwan’s stock market in 2020. It seems like buy and hold is better than price_volume strategy. But the period just only one year, we cannot jump to conclusion that which is better one. Today’s article describes how to build a simple backtest system. Reader can try to adjust the structure. For example, turn rolling days 10
、20
into adjustable parameters, in order to compare the compact of date change on the rate of return.
Quantitative Analysis is the trend of investment. Worldwide famous hedge fund adopt this method to make trade strategy. If you have interest on this topic, you can go to our official website. It provides various financial data to help you make better strategy.
Source Code
Extended Reading
- 【Application(1)】The investing strategy of Benjamin Graham
- 【Quant(4)】Trading Strategy by Keeping an Eye on Big Players
Related Link
You could give us encouragement by …
We will share financial database applications every week.
If you think today’s article is good, you can click on the applause icononce.
If you think it is awesome, you can hold the applause icon until 50 times.
If you have any feedback, please feel free to leave a comment below.