Table of Contents
股票回測是目前相較之下較具科學精神的方法,雖然說過去歷史資料並不能代表未來,但回測結果至少讓我們可以檢驗自己的策略於過往的績效表現如何,藉以提供此策略或許有效的證據。而如果每次要嘗試新的策略就得把相同的架構再重寫一遍,相當費時且修改上會相當麻煩,於是今天我們嘗試把回測固定的流程模組化,這樣未來只需要專注在思考新策略即可!
本文章使用 Mac OS 並以 Jupyter Notebook 作為編輯器
import tejapi
import pandas as pd
import numpy as np
Note: tejapi 安裝以命令提示字元 (Windows)/終端機 (Mac),輸入 pip install tejapi
tejapi.ApiConfig.api_key = 'Your Key'
tejapi.ApiConfig.ignoretz = True
Step 1. 思考回測時會用到哪些儲存變數
構想大致流程所需的欄位
Step 2. 點首先設定一個類別class
叫backtest
class backtest():
Step 3. 以下的def
都是在這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 = []
當然資料庫也要放在初始值裡面,證券名稱是我們要找的target_list
# 資料庫
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. 策略制定,這裡我們挑選兩個常見策略,以下針對重點介紹,完整程式碼請見此連結
突破近20日最高價和最高量進行買進,突破近10日最低價和最低量則賣出
def price_volume(self, specific=None):
# 策略更新
self.signal_data = pd.DataFrame()
self.action_data = pd.DataFrame()
股票池裡每一檔股票都要產生交易訊號
for i in self.target_list:
target_data = self.data[self.data['證券代碼'] == i]
計算近10、20天最高最低價量
rolling_max = target_data[['收盤價(元)', '成交量(千股)']].rolling(20).max()
rolling_min = target_data[['收盤價(元)', '成交量(千股)']].rolling(10).min()
產生交易訊號
#收盤價、成交量同時突破近20天高點買入
stock_data['買入訊號判斷'] = np.where((stock_data['收盤價(元)'] == stock_data['收盤價(max)']) & (stock_data['成交量(千股)'] == stock_data['成交量(max)']), -1, 0)
#收盤價、成交量同時突破近10天低點賣出
stock_data['賣出訊號判斷'] = np.where((stock_data['收盤價(元)'] == stock_data['收盤價(min)']) & (stock_data['成交量(千股)'] == stock_data['成交量(min)']), 1, 0)
remain_stock = self.signal_data.iloc[:-1,8:10].sum().sum()
self.signal_data.iloc[-1,9] = 0
self.signal_data.iloc[-1,8] = 0
if remain_stock < 0:
self.signal_data.iloc[-1,9] = -remain_stock
else:
self.signal_data.iloc[-1,8] = -remain_stock
第一天持有至最後一天賣出
def buy_hold(self, specific=None):
# 策略更新
self.signal_data = pd.DataFrame()
self.action_data = pd.DataFrame()
股票池裡每一檔股票都要產生交易訊號
for i in self.target_list:
target_data = self.data[self.data['證券代碼'] == i]
第一天買入,最後一天賣出
target_data['買入訊號判斷'] = 0
target_data['賣出訊號判斷'] = 0
# 第一天買入,最後一天賣出
target_data.iloc[0, 4] = -1
target_data.iloc[-1,5] = 1
Step 5. 執行回測計算
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)]
計算獲利
self.protfolio_profit.append(sum(action_data['交易'].tolist()))
計算成本
self.protfolio_cost.append(-action_data[action_data['買入訊號判斷'] < 0]['交易'].sum())
Step 6. 計算最終報酬率
def ROI(self):
# 報酬率
return_profit = sum(self.protfolio_profit) /
sum(self.protfolio_cost)
return return_profit
策略的優劣通常和大盤來做比較,如果忙了老半天獲利輸給單純持有大盤,則代表策略或許有進步的空間
Step 1. 設定market
為backtest
類別,這裡我們使用調整後指數 Y9997
market = backtest(['Y9997'])
Step 2. 選擇策略
market.buy_hold()
Step 2. 進行回測
market.run()
Step 3. 檢視報酬率
market.ROI()
這裡我們想試試 MSCI 做為自己的投資組合,MSCI 指數是指摩根士丹利資本國際公司所編製的股價股數,也稱摩根指數、大摩指數,我們可以查到2020 MSCI 持有的股票名單,這裡使用 TWN/AIDXS
交易資料庫來搜尋指數成分股。
data_MSCI = tejapi.get('TWN/AIDXS',
coid = 'MSCI',
mdate= '2019-12-31',
opts={'columns':['coid','mdate','key3']},
chinese_column_name=True)
篩選出成分股,並把獨有的證券代號提出轉為list
MSCI_list = data_MSCI['成份股'].unique().tolist()
最後把中文刪除只留下代碼
MSCI_list = [coid[:4] for coid in MSCI_list]
由於模組化的效果,我們不用再寫一遍回測流程,只需要輕鬆的改變target_list
就可以馬上得到我們要的結果
Step 1. 設定msci
為backtest
類別,並選擇MSCI_list
股票池
msci = backtest(MSCI_list)
Step 2. 選擇策略
msci.price_volume()
Step 3. 進行回測
msci.run()
Step 4. 檢視報酬率
msci.ROI()
2020台股屬於大多頭,事後來看是這段期間的持有的策略報酬率較好,但時間只有一年也無法說明持有策略總是優於價量策略。本文章主要介紹如何架構一套簡易回測系統,讀者可在策略上自行調整,例如可以試著將價量的參數10
、20
天設為可調整的項目,用以比較日期變化對報酬率的影響。
量化分析是未來投資的趨勢,世界知名的避險基金都有採用此方法,將資料萃取成可用的參數作為策略制定,挖掘超額報酬,若讀者有興趣可以前往我們的官方網站,裡面有提供更多財務、交易等財金資料,來幫助您製作更好的交易策略!
電子報訂閱