目录
股票回测是目前相较之下较具科学精神的方法,虽然说过去历史资料并不能代表未来,但回测结果至少让我们可以检验自己的策略于过往的绩效表现如何,借以提供此策略或许有效的证据。而如果每次要尝试新的策略就得把相同的架构再重写一遍,相当费时且修改上会相当麻烦,于是今天我们尝试把回测固定的流程模组化,这样未来只需要专注在思考新策略即可!
本文章使用 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
天设为可调整的项目,用以比较日期变化对报酬率的影响。
量化分析是未来投资的趋势,世界知名的避险基金都有采用此方法,将资料萃取成可用的参数作为策略制定,挖掘超额报酬,若读者有兴趣可以前往我们的官方网站,里面有提供更多财务、交易等财金资料,来帮助您制作更好的交易策略!