阿布量化机器学习

量化交易:星期几是这个股票的‘好日子’

2017-09-30  本文已影响164人  8cec6606d7bf

第26节 星期几是这个股票的‘好日子’

作者: 阿布

阿布量化版权所有 未经允许 禁止转载

abu量化系统github地址 (欢迎+star)

本节界面操作视频教程地址

备注:不熟悉编程的用户可忽略代码具体实现,可直接使用界面ui操作

上一节讲解量化交易中跨市场低频统计套利的示例,本节将示例一个与周期相关的短线择时策略,本节的内容是为《量化交易之路》中的一个小节做的完整策略实例补充。

很多刚接触交易的人总喜欢把交易看成一种有固定收入的工作,比如他们有自己的规矩,周五一定要把所有股票都卖了,安安心心过周末,周一看情况一切良好再把股票买回来。

还有一些人有着很奇怪的癖好认为周三是他的幸运日,在周三买入他选中的股票,有些人每个月第一个周五发工资,市场就是由这些各种各样的人组成的。

某一个股票上的活跃用户在一段短时间内变化并不大,也就是说这些习惯周五卖周一买的人会反复在一支股票上交易,普通投资者普遍的投资方式是针对一支股票不断的进行买卖,他们不会长期持有这支股票,但也不会远离这支股票很长时间,我认为有两点促成了以上事实。

  1. 贪欲:贪欲在这中间起到了很大的作用,当一个人第一次买入一支股票并且持有到有一定利润的时候,他选择卖出这支股票,因为他认为涨的已经很多了,该适当的回调了,之后股价的走势只有两种可能:第一按照他的预期下跌,这样的话他可能选择跌到某种程度再次进场买入这支股票;第二就是继续上涨,这种情况下他会选择不断‘诅咒’这支股票,直到有一天股价上涨到让他无法忍受,从此由‘黑转粉’。

  2. 时间成本与懒惰:一个人类的时间和精力都是有限的,它无法获取市场中所有股票的信息,每次获取熟悉一支股票的时间成本在他看来也是非常巨大,他反复的盯着自己最频繁买卖的那几支股票。

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

下面先一个一个观察每一个股票的周期涨跌概率,可以发现:

  1. 特斯拉在周四上涨的概率最大59%
  2. 诺亚财富也在周四上涨的概率最大65%
  3. 百度在周五上涨概率达到60%
  4. 苹果在周三上涨概率达到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,即虽然特斯拉在周四有大概率的上涨可能,如果使用第一种算法,那么由于涨幅比例不符合要求,在具体策略中将不会发出买入信号。

备注:

  1. 上面的计算中0.618是可以在具体策略中通过参数传递
  2. 无论是上面使用的55%胜率还是0.618都是以制造非均衡概率优势为目的
  3. 在实际策略编写中根据交易量需求,以及市场交易目标数量等等确定具体使用上面那一种算法, 或两个并行生效

下面看看百度上涨概率超过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数量会远远多于本例中使用沙盒数据的数量,策略可以要求两种阀值都满足,且加入更多的非均衡条件构造最终的非均衡结果,但是由于本例沙盒数据量少,所以下面编写策略时采用两种阀值计算方式满足一种即可,策略大概原理如下:

策略的性质属于:均值回复

  1. 默认以40天为周期(8周)结合涨跌阀值计算周几适合买入
  2. 回测运行中每一个月重新计算一次上述的周几适合买入
  3. 在策略日任务中买入信号为:昨天下跌,今天开盘也下跌,且明天是计算出来的上涨概率大的'周几'

具体策略编写如下所示:

备注:不熟悉编程的用户可忽略代码具体实现,也可以直接使用界面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量化文档目录章节

  1. 择时策略的开发
  2. 择时策略的优化
  3. 滑点策略与交易手续费
  4. 多支股票择时回测与仓位管理
  5. 选股策略的开发
  6. 回测结果的度量
  7. 寻找策略最优参数和评分
  8. A股市场的回测
  9. 港股市场的回测
  10. 比特币,莱特币的回测
  11. 期货市场的回测
  12. 机器学习与比特币示例
  13. 量化技术分析应用
  14. 量化相关性分析应用
  15. 量化交易和搜索引擎
  16. UMP主裁交易决策
  17. UMP边裁交易决策
  18. 自定义裁判决策交易
  19. 数据源
  20. A股全市场回测
  21. A股UMP决策
  22. 美股全市场回测
  23. 美股UMP决策

abu量化系统文档教程持续更新中,请关注公众号中的更新提醒。

更多阿布量化量化技术文章

更多关于abu量化系统请关注微信公众号: abu_quant

上一篇下一篇

猜你喜欢

热点阅读