扑克投资家 2017-10-15
作者: 阿布
阿布量化版权所有 未经允许 禁止转载
abu量化系统github地址 (您的star是我的动力!)
本节界面操作视频教程地址
备注:不熟悉编程的用户可忽略代码具体实现,可直接使用界面ui操作
上一节讲解量化交易中跨市场低频统计套利的示例,本节将示例一个与周期相关的短线择时策略,本节的内容是为《量化交易之路》中的一个小节做的完整策略实例补充。
很多刚接触交易的人总喜欢把交易看成一种有固定收入的工作,比如他们有自己的规矩,周五一定要把所有股票都卖了,安安心心过周末,周一看情况一切良好再把股票买回来。
还有一些人有着很奇怪的癖好认为周三是他的幸运日,在周三买入他选中的股票,有些人每个月第一个周五发工资,市场就是由这些各种各样的人组成的。
某一个股票上的活跃用户在一段短时间内变化并不大,也就是说这些习惯周五卖周一买的人会反复在一支股票上交易,普通投资者普遍的投资方式是针对一支股票不断的进行买卖,他们不会长期持有这支股票,但也不会远离这支股票很长时间,我认为有两点促成了以上事实。
贪欲:贪欲在这中间起到了很大的作用,当一个人第一次买入一支股票并且持有到有一定利润的时候,他选择卖出这支股票,因为他认为涨的已经很多了,该适当的回调了,之后股价的走势只有两种可能:第一按照他的预期下跌,这样的话他可能选择跌到某种程度再次进场买入这支股票;第二就是继续上涨,这种情况下他会选择不断‘诅咒’这支股票,直到有一天股价上涨到让他无法忍受,从此由‘黑转粉’。
时间成本与懒惰:一个人类的时间和精力都是有限的,它无法获取市场中所有股票的信息,每次获取熟悉一支股票的时间成本在他看来也是非常巨大,他反复的盯着自己最频繁买卖的那几支股票。
下面先获取沙盒数据中美股一年的数据,做为短线分析示例:
us_choice_symbols = ['usTSLA', 'usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usWUBA', 'usVIPS'] kl_dict = {us_symbol[2:]: ABuSymbolPd.make_kl_df(us_symbol, start='2014-07-26', end='2015-07-26') for us_symbol in us_choice_symbols}
从日振幅涨跌幅比来看,只有BIDU和WUBA能勉强有短线套利的空间(值 > 1.8), 但是由于沙盒数据中只有这些symbol,所以暂时忽略这个特证,之后做非沙盒数据全市场周期短线分析时再使用这个值。
ABuKLUtil.wave_change_rate(kl_dict)
TSLA日振幅涨跌幅比:1.778420 NOAH日振幅涨跌幅比:1.733710 SFUN日振幅涨跌幅比:1.784097 BIDU日振幅涨跌幅比:1.812175 AAPL日振幅涨跌幅比:1.664462 GOOG日振幅涨跌幅比:1.573070 WUBA日振幅涨跌幅比:1.913431 VIPS日振幅涨跌幅比:1.568457
下面先一个一个观察每一个股票的周期涨跌概率,可以发现:
ABuKLUtil.date_week_win(kl_dict)
假如择时策略中需要找到每一个股票上涨概率超过55%的交易日,做为策略买入的日子,比如下面示例找特斯拉超过55%的交易日:
tl_dw = ABuKLUtil.date_week_win(kl_dict['TSLA']) tl_dw_vd = tl_dw[tl_dw.win > 0.55] tl_dw_vd
可以看到上面的结果就是符号要求的交易日,但是如果虽然周四的胜率很很高,但是周四的上涨比例很低呢,如果上涨比例很低,会造成盈亏比很低,造成最终交易依然亏损,下面使用date_week_mean看看上面各个美股每个交易日的涨跌比例,如下:
ABuKLUtil.date_week_mean(kl_dict)
看看特斯拉满足胜率要求的交易日中的涨跌幅比例,如下:
tl_dwm = ABuKLUtil.date_week_mean(kl_dict['TSLA']) tl_dwm.loc[tl_dw_vd.index]
可以看到周四的涨跌平均值是0.54,在具体策略编写中可以使用如下两种阀值计算方式,确定周四的涨幅比例是否高于下面两种算法:
abs(tl_dwm.sum()).values[0] / 0.618, abs(tl_dwm._p_change).mean() / 0.618
(0.73209375244226405, 0.45709539602153293)
可以看到第一种算法的值计算为0.73,第二种为0.45,0.54虽然大于0.45但是小于0.73,即虽然特斯拉在周四有大概率的上涨可能,如果使用第一种算法,那么由于涨幅比例不符合要求,在具体策略中将不会发出买入信号。
备注:
下面看看百度上涨概率超过55%的交易日:
bd_dw = ABuKLUtil.date_week_win(kl_dict['BIDU']) bd_dw_vd = bd_dw[bd_dw.win > 0.55] bd_dw_vd
看看百度满足胜率要求的交易日中的涨跌幅比例,以及两种阀值计算,如下:
bd_dwm = ABuKLUtil.date_week_mean(kl_dict['BIDU']) print(abs(bd_dwm.sum()).values[0] / 0.618, abs(bd_dwm._p_change).mean() / 0.618) bd_dwm.loc[bd_dw_vd.index]
0.171504714657 0.2820732168894443
结果看到第一种算法的值计算为0.17,第二种为0.28,0.25虽然大于0.17但是小于0.28,即如果策略中使用第一种阀值计算方式将满足买入信号发出,如果第二种就不满足。
实盘中使用的symbol数量会远远多于本例中使用沙盒数据的数量,策略可以要求两种阀值都满足,且加入更多的非均衡条件构造最终的非均衡结果,但是由于本例沙盒数据量少,所以下面编写策略时采用两种阀值计算方式满足一种即可,策略大概原理如下:
策略的性质属于:均值回复
具体策略编写如下所示:
备注:不熟悉编程的用户可忽略代码具体实现,也可以直接使用界面ui操作本节ui界面操作教程视频地址
class AbuFactorBuyWD(AbuFactorBuyTD, BuyCallMixin): def _init_self(self, **kwargs): """ kwargs中可选参数:buy_dw: 代表周期胜率阀值,默认0.55即55% kwargs中可选参数:buy_dwm: 代表涨幅比例阀值系数,默认0.618 kwargs中可选参数:dw_period: 代表分析dw,dwm所使用的交易周期,默认40天周期(8周) """ self.buy_dw = kwargs.pop('buy_dw', 0.55) self.buy_dwm = kwargs.pop('buy_dwm', 0.618) self.dw_period = kwargs.pop('dw_period', 40) # combine_kl_pd中包含择时金融时间数据与择时之前一年的金融时间数据, 先取出择时开始之前的周期数据 last_kl = self.combine_kl_pd.loc[:self.kl_pd.index[0]] if last_kl.shape[0] > self.dw_period: last_kl = last_kl[-self.dw_period:] # 开始计算周几买,_make_buy_date把结果被放在self.buy_date_week序列中 self._make_buy_date(last_kl) def fit_month(self, today): """月任务,每一个重新取之前一年的金融时间序列数据,重新计算一遍'周几买'""" end_ind = self.combine_kl_pd[self.combine_kl_pd.date == today.date].key.values[0] start_ind = end_ind - self.dw_period if end_ind - self.dw_period > 0 else 0 # 根据当前的交易日,切片过去的一年金融时间序列 last_kl = self.combine_kl_pd.iloc[start_ind:end_ind] # 重新计算一遍'周几买' self._make_buy_date(last_kl) def fit_day(self, today): """日任务:昨天下跌,今天开盘也下跌,根据今天是周几,在不在序列self.buy_date_week中决定今天买不买""" if self.yesterday.p_change < 0 and today.open < self.yesterday.close \ and int(today.date_week) in self.buy_date_week: # 由于没有用到今天的收盘价格等,可以直接使用buy_today return self.buy_today() return None # noinspection PyProtectedMember def _make_buy_date(self, last_kl): self.buy_date_week = [] # 计算周期内,周期的胜率 last_dw = ABuKLUtil.date_week_win(last_kl) # 摘取大于阀值self.buy_dw的'周几',buy_dw默认0.55 last_dw_vd = last_dw[last_dw.win >= self.buy_dw] """ eg: last_dw_vd 0 1 win date_week 周四 3 5 0.62 周五 2 6 0.75 """ if len(last_dw_vd) > 0: # 如果胜率有符合要求的,使用周几平均涨幅计算date_week_mean last_dwm = ABuKLUtil.date_week_mean(last_kl) # 摘取满足胜率的last_dw_vd last_dwm_vd = last_dwm.loc[last_dw_vd.index] """ eg: last_dwm_vd _p_change date_week 周四 1.55 周五 1.12 """ # 阀值计算方式1 dwm1 = abs(last_dwm.sum()).values[0] / self.buy_dwm # 阀值计算方式2 dwm2 = abs(last_dwm._p_change).mean() / self.buy_dwm # 如果symbol多可以使用&的关系 dm_effect = (last_dwm_vd._p_change > dwm1) | (last_dwm_vd._p_change > dwm2) buy_date_loc = last_dwm_vd[dm_effect].index """ eg: buy_date_loc Index(['周四', '周五'], dtype='object', name='date_week') """ if len(buy_date_loc) > 0: # 如果涨跌幅阀值也满足,tolist,eg:['周一', '周二', '周三', '周四', '周五'] dw_index = last_dw.index.tolist() # 如果是一周5个交易日的就是4,如果是比特币等7天交易日的就是6 max_ind = len(dw_index) - 1 for bdl in buy_date_loc: sell_ind = dw_index.index(bdl) buy_ind = sell_ind - 1 if sell_ind > 0 else max_ind self.buy_date_week.append(buy_ind)
上面的AbuFactorBuyWD即完成了整个策略的编写,下面开始进行回测,如下所示:
# 初始化资金 read_cash = 1000000 # 买入策略AbuFactorBuyWD,参数都使用默认的 buy_factors = [{'class': AbuFactorBuyWD}] # 卖出策略使用AbuFactorSellNDay,sell_n=1即只持有一天,is_sell_today=True, 持有一天后当天卖出 sell_factors = [{'class': AbuFactorSellNDay, 'sell_n': 1, 'is_sell_today': True}] # 开始进行美股沙盒数据回测,沙盒数据中美股只有从13年7月到16年7月的数据,其它市场会多一些 abu_result_tuple = run_loo_back(us_choice_symbols, '2013-07-26', '2016-07-26')
买入后卖出的交易数量:146 买入后尚未卖出的交易数量:2 胜率:53.4247% 平均获利期望:1.9614% 平均亏损期望:-1.9299% 盈亏比:1.1534 所有交易收益比例和:0.2369 所有交易总盈亏和:24655.9100
可以看到上面的回测中胜率超过了50%,从下面的交易单中可以看到所有交易都只持有了一天,如下:
abu_result_tuple.orders_pd.filter( ['symbol', 'buy_date', 'sell_date', 'keep_days', 'profit'])[:7]
上面的策略中计算'周几'上涨概率最大的交易周期默认为40天周期(8周),这个周期长度不能太长也不能太短,因为某一个股票上的活跃用户只是在一段短时间内变化不大,但是一个市场中的参与者随着时间的流逝,也在慢慢不断变化,不断新老交替,就像我们人类,每7年我们就是一个全新的自己,所有细胞血液都将完全更新一遍。
下面使用这个策略对比特币,莱特币进行回测,如下所示:
_ = run_loo_back(['btc', 'ltc'], '2013-07-26', '2017-07-26')
买入后卖出的交易数量:24 买入后尚未卖出的交易数量:0 胜率:58.3333% 平均获利期望:2.8335% 平均亏损期望:-2.6652% 盈亏比:1.8846 所有交易收益比例和:0.1302 所有交易总盈亏和:18321.1800
下面使用这个沙盒中A股市场symbol进行回测,如下所示:
cn_choice_symbols = ['002230', '300104', '300059', '601766', '600085', '600036', '600809', '000002', '002594'] _ = run_loo_back(cn_choice_symbols, '2013-07-26', '2017-07-26')
买入后卖出的交易数量:112 买入后尚未卖出的交易数量:1 胜率:55.3571% 平均获利期望:1.8545% 平均亏损期望:-2.0521% 盈亏比:1.0527 所有交易收益比例和:0.1237 所有交易总盈亏和:6612.0000
小结:
上面的回测交易由于使用沙盒数据,数据量少,所以实际上的回测效果一般,即如果一个策略有56%的胜率,那么一天只执行10次交易,你的胜率有各种可能,不一定达到56%,但是如果你能一天执行10000次以上,那么你的胜率如果不是56%,不管是更多或者更少,都代表你计算胜率的方式有问题。
所以针对上面这个策略,确定你拥有交易概率优势,只要一天内可以从不同市场、不同股票、不同时段内找到足够多的交易机会,执行足够多的次数,那你最后一定是盈利的,统计套利的核心思想就是这个,不只是要单纯追求胜率,更应该关注大数定律,寻找多元化的交易机会,最终达成理想的胜率。
在之后的章节中会示例不使用沙盒数据,在各个市场中通过这个策略寻找交易进行进行回测,请关注公众号中的代码示例更新
更多阿布量化量化技术文章
更多关于abu量化系统请关注微信公众号: abu_quant