量能回测实战

photo by Leio McLaren on Unsplash

本文重点概要

· 文章难度:★★☆☆☆

· 以成交量增长为筛选指标

· 阅读建议:本文以成交量增长作为买卖指标,以视觉化的方式观察交易讯号以及买卖点,接著再利用函数计算出报酬率,如果对回测或是技术指标不了解的读者,欢迎先行阅读【量化分析(二)】- 技术分析简介与回测,详细理解回测的执行流程。

前言

近几年动能交易很常出现在股票策略的讨论当中,我们在股票市场通常很常听到谈论价量关系,价是量的先行指标等等,而本文想进而探讨当成交量放大时以做为进场策略之回测效应,本方法并没有像其他技术指标一样有一定之准则,于是在设计上比较较为弹性,在程式设计上能使读者们自行更改,详细参数设定上会在后面程式码中提及如何修改。而本文采用以下策略进行回测:

1. 进场成交量是前”4”日平均的2.5倍做为进入准则

2. 出场设在成交量低于”5”日平均的0.75倍

编辑环境及模组需求

本文使用 Window10 并以 Spyder(anaconda31) 作为编辑器

###汇入套件 ##########数据分析三宝 import pandas as pd import matplotlib.pyplot as plt import numpy as np #################TEJ import tejapi tejapi.ApiConfig.api_key = 'Your Key' tejapi.ApiConfig.ignoretz = True

资料库使用

证券交易资料表:上市(柜)未调整股价(日),资料代码为(TWN/EWPRCD)。

资料导入

coid="2618" #### 可自行更换股票代码 start="2018-01-01" ### 自行更换起始时间 end= "2022-02-17"  ### 自行更换结束时间 opts={'columns': ['coid', 'mdate', 'volume', 'close_adj','close_d','open_d']}  ###本文使用除权息调整收盘价 ############# fly=tejapi.get('TWN/EWPRCD',coid=coid,                  mdate={'gt':start,'lt':end},                 paginate=True,                 chinese_column_name=True,                 opts=opts )

选择现在最热门的族群航空,时间范围选取于2018年1月1日至 2022年2月16日,并把图绘出来借以观察股价情况。

fly.set_index("日期",drop=True,inplace=True) # 设定index plt.figure(facecolor='white',figsize=(12,8)) plt.plot(fly['收盘价'], label='收盘价') plt.title("飞机起飞表",fontsize=25) plt.xticks(rotation=45) plt.xlabel('日期',fontsize=25) plt.ylabel('股价',fontsize=25) plt.grid() plt.show()
飞机起飞表
飞机起飞表

资料处理

step1 计算指标

def voltrade(df,p,q,r):     df =df.copy()     df["当日交易量"]=df["成交量(千股)"].rolling(p).mean()      df["前五日总量"]=df["成交量(千股)"].rolling(q).sum()     df[str(r)+"日均线"]=df["收盘价-除权息"].rolling(r).mean()     ####扣除掉当日之平均     df["前几日平均"]=(df["前五日总量"]-df["当日交易量"])/(q-p)     return df

此段能得到前4天的平均交易量(扣除当日),以及5MA(皆能自行更改,改动q, r即可)

如q设5,能得到前四日平均交易量,如r设5,得到近5日平均股价

r=5 stock=voltrade(fly, 1, 5, r) # 本文选定策略参数 stock
股票资讯
股票资讯

Step 4. 找出买卖讯号与视觉化买卖点(详见完整程式码)

def buysell(company,a,b):     company =company.copy()     buy=[]     sell=[]     hold=0     for i in range(len(company)):         if  company["当日交易量"][i] > company["前几日平均"][i]*a :             sell.append(np.nan)             if hold !=1:                 buy.append(company["收盘价-除权息"][i])                   hold = 1             else:                  buy.append(np.nan)         elif company["当日交易量"][i]<company["前几日平均"][i]*b :             buy.append(np.nan)             if hold !=0:                 sell.append(company["收盘价-除权息"][i])                 hold = 0             else:                 sell.append(np.nan)         else:             buy.append(np.nan)             sell.append(np.nan)     a=(buy,sell)     company['Buy_Signal_Price']=a[0]     company['Sell_Signal_Price']=a[1]     company["买卖股数1"]=company['Buy_Signal_Price'].apply(lambda x : 1000 if x >0 else 0)     company["买卖股数2"]=company['Sell_Signal_Price'].apply(lambda x : -1000 if x >0 else 0  )     company["买卖股数"]=company["买卖股数1"]+ company["买卖股数2"]     return company

此段设计为当日成交量大于前四日平均2.5倍时进场,在成交量小于前四日平均0.75倍时出场,系数皆能自行调整。

vol=buysell(stock,2.5,0.75) plot(vol)
买卖交易讯号(一)
买卖交易讯号(一)

我们能看到这方法中,股票前三年中皆在盘整进出增加了许多摩擦成本,到2021年开始飙升但此方法因为条件过于简易而造成了上涨但是提早出场的情形,因此在条件上本文多设了五日均线的设定,成交量大量且突破五日均线下买入,成交量缩小且跌破五日均线时卖出,结果如下图。

买卖交易讯号(二)
买卖交易讯号(二)

本文使用成交量为进出指标,于是把股价与成交量画再一起,本文提供一起画与分开画的方法,斟酌于自己喜欢哪种~~

pvtwo(volma) #分开绘制 pvsame(volma)#合并绘制
图(四)
图(四)
图(五)
图(五)

计算报酬

MACD指标回测实战里,我们有详细介绍考虑手续费、现金部位计算报酬率的方式,因为流程固定,本文以下方函数包装。

def target_return(data, principal):     data=data.copy()     #计算成本     data['手续费'] = data['收盘价-除权息']* abs(data['买卖股数'])*0.001425     data['手续费'] = np.where((data['手续费']>0)&(data['手续费'] <20), 20, data['手续费'])     data['证交税'] = np.where(data['买卖股数']<0, data['收盘价-除权息']* abs(data['买卖股数'])*0.003, 0)     data['摩擦成本'] = (data['手续费'] + data['证交税']).apply(np.floor)     #计算资产价值     data['股票价值'] = data['买卖股数'].cumsum() * data['收盘价-除权息']     data['现金价值'] = principal - data['摩擦成本'] + (data['收盘价-除权息']* -data['买卖股数']).cumsum()      data['资产价值'] = data['股票价值'] + data['现金价值']     #计算报酬率     data['当日价值变动(%)'] = (data['资产价值']/data['资产价值'].shift(1) - 1)*100     data['累计报酬(%)'] = (data['资产价值']/principal - 1)*100     return data

本文单纯考量一般股票买卖,并无考虑融券融资情形,所以没有计算保证金的流程,只要将买卖股数的资料整理好,并决定期初的现金部位,即可算出交易成本以及报酬率。本文选择投入16000元为期初一张股票之价格。

volreturn1 = target_return( volma, principal = 16000)##有加入均线之策略 volreturn2 = target_return( vol, principal = 16000)##并未加入均线策略
图(五)
图(五)

加入买入持有策略,与本文策略进行比较。

keep=vol.copy() keep["买卖股数"]=0 keep["买卖股数"][0]=1000 keep["买卖股数"][len(keep)-1]=-1000 volreturn3 = target_return( keep, principal = 16000)

报酬绩效比较

Step 1. 累计报酬比较(详见完整程式码)

图(六)
图(六)

Step 2. 累计报酬比较(详见完整程式码)

图(七)
图(七)

结论

我们能看到如果只使用成交量做为进出条件,年化报酬率只有8%与持有相当,加入条件MA后年化报酬率来到了在17.31,Sharp ratio 也是里面最高的,但是缺点也很明显在于2018~2020年上报酬率为负数,也就是在于盘整的股票上,效果并不是那么好,甚至有可能会变成负报酬率,或许在进场的条件上能设定的较为严格,减少多余的进出,还有两个特殊情形要提醒此方法的使用:

  1. 面临到被处置股票也无法使用此方法,因为处置时成交量势必下降。

2. 在进行指数调整时,如富时指数,msci等,成交量容易放大导致买卖点判断错误。

来到2021年后,长荣航开始起涨,俗话都说股价是用钱砸出来的,成交量放大才能推升股价,因此此策略能在这段找到不错的进场点位,而因为短进短出的特性,也能让资金使用上更具备效率。

再之后也会介绍使用TEJ资料库寻找成交量放大的股票,借由程式的筛选,能让寻找标的时候更便利,也能试试看搭配此方法一起使用喔!所以欢迎对各种交易回测有兴趣的读者,选购TEJ E-Shop的相关方案,用高品质的资料库,建构出适合自己的交易策略。

完整程式码

延伸阅读

相关连结

返回总览页