量化交易:星期几是这个股票的‘好日子’
第26节 星期几是这个股票的‘好日子’
作者: 阿布
阿布量化版权所有 未经允许 禁止转载
abu量化系统github地址 (欢迎+star)
备注:不熟悉编程的用户可忽略代码具体实现,可直接使用界面ui操作
上一节讲解量化交易中跨市场低频统计套利的示例,本节将示例一个与周期相关的短线择时策略,本节的内容是为《量化交易之路》中的一个小节做的完整策略实例补充。
很多刚接触交易的人总喜欢把交易看成一种有固定收入的工作,比如他们有自己的规矩,周五一定要把所有股票都卖了,安安心心过周末,周一看情况一切良好再把股票买回来。
还有一些人有着很奇怪的癖好认为周三是他的幸运日,在周三买入他选中的股票,有些人每个月第一个周五发工资,市场就是由这些各种各样的人组成的。
某一个股票上的活跃用户在一段短时间内变化并不大,也就是说这些习惯周五卖周一买的人会反复在一支股票上交易,普通投资者普遍的投资方式是针对一支股票不断的进行买卖,他们不会长期持有这支股票,但也不会远离这支股票很长时间,我认为有两点促成了以上事实。
-
贪欲:贪欲在这中间起到了很大的作用,当一个人第一次买入一支股票并且持有到有一定利润的时候,他选择卖出这支股票,因为他认为涨的已经很多了,该适当的回调了,之后股价的走势只有两种可能:第一按照他的预期下跌,这样的话他可能选择跌到某种程度再次进场买入这支股票;第二就是继续上涨,这种情况下他会选择不断‘诅咒’这支股票,直到有一天股价上涨到让他无法忍受,从此由‘黑转粉’。
-
时间成本与懒惰:一个人类的时间和精力都是有限的,它无法获取市场中所有股票的信息,每次获取熟悉一支股票的时间成本在他看来也是非常巨大,他反复的盯着自己最频繁买卖的那几支股票。
1. 美股周期短线分析
下面先获取沙盒数据中美股一年的数据,做为短线分析示例:
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
下面先一个一个观察每一个股票的周期涨跌概率,可以发现:
- 特斯拉在周四上涨的概率最大59%
- 诺亚财富也在周四上涨的概率最大65%
- 百度在周五上涨概率达到60%
- 苹果在周三上涨概率达到56%
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,即虽然特斯拉在周四有大概率的上涨可能,如果使用第一种算法,那么由于涨幅比例不符合要求,在具体策略中将不会发出买入信号。
备注:
- 上面的计算中0.618是可以在具体策略中通过参数传递
- 无论是上面使用的55%胜率还是0.618都是以制造非均衡概率优势为目的
- 在实际策略编写中根据交易量需求,以及市场交易目标数量等等确定具体使用上面那一种算法, 或两个并行生效
下面看看百度上涨概率超过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,即如果策略中使用第一种阀值计算方式将满足买入信号发出,如果第二种就不满足。
2. 日胜率均值回复策略
实盘中使用的symbol数量会远远多于本例中使用沙盒数据的数量,策略可以要求两种阀值都满足,且加入更多的非均衡条件构造最终的非均衡结果,但是由于本例沙盒数据量少,所以下面编写策略时采用两种阀值计算方式满足一种即可,策略大概原理如下:
策略的性质属于:均值回复
- 默认以40天为周期(8周)结合涨跌阀值计算周几适合买入
- 回测运行中每一个月重新计算一次上述的周几适合买入
- 在策略日任务中买入信号为:昨天下跌,今天开盘也下跌,且明天是计算出来的上涨概率大的'周几'
具体策略编写如下所示:
备注:不熟悉编程的用户可忽略代码具体实现,也可以直接使用界面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)
3. 各个市场回测日胜率均值回复策略
上面的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}]
def run_loo_back(choice_symbols, start, end):
abu_result_tuple, _ = abu.run_loop_back(read_cash,
buy_factors,
sell_factors,
start=start,
end=end,
choice_symbols=choice_symbols, n_process_pick=1)
ABuProgress.clear_output()
AbuMetricsBase.show_general(*abu_result_tuple, returns_cmp=True, only_info=True)
return abu_result_tuple
# 开始进行美股沙盒数据回测,沙盒数据中美股只有从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量化文档目录章节
- 择时策略的开发
- 择时策略的优化
- 滑点策略与交易手续费
- 多支股票择时回测与仓位管理
- 选股策略的开发
- 回测结果的度量
- 寻找策略最优参数和评分
- A股市场的回测
- 港股市场的回测
- 比特币,莱特币的回测
- 期货市场的回测
- 机器学习与比特币示例
- 量化技术分析应用
- 量化相关性分析应用
- 量化交易和搜索引擎
- UMP主裁交易决策
- UMP边裁交易决策
- 自定义裁判决策交易
- 数据源
- A股全市场回测
- A股UMP决策
- 美股全市场回测
- 美股UMP决策
abu量化系统文档教程持续更新中,请关注公众号中的更新提醒。
更多关于abu量化系统请关注微信公众号: abu_quant