量化投资:第8节 A股市场的回测

0
回复
5641
查看
[复制链接]

24

主题

15

回帖

227

积分

中级会员

积分
227
来源: 2019-11-20 19:22:28 显示全部楼层 |阅读模式
之前的小节回测示例都是使用美股,本节示例A股市场的回测。
买入因子,卖出因子等依然使用相同的设置,如下所示:
  1. <span style="background-color: white;"># 设置初始资金数
  2. read_cash = 1000000

  3. # 买入因子依然延用向上突破因子
  4. buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
  5.                {'xd': 42, 'class': AbuFactorBuyBreak}]

  6. # 卖出因子继续使用上一节使用的因子
  7. sell_factors = [
  8.     {'stop_loss_n': 1.0, 'stop_win_n': 3.0,
  9.      'class': AbuFactorAtrNStop},
  10.     {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
  11.     {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}</span>
复制代码



1. A股市场的回测示例
择时股票池使用沙盒缓存数据中的如下股票:
A股市场:
  • 科大讯飞(002230)
  • 乐视网(300104)
  • 东方财富(300059)
  • 中国中车(601766)
  • 同仁堂(600085),
  • 招商银行(600036)
  • 山西汾酒(600809)
  • 万科A(000002)
  • 比亚迪(002594)
  • 万达电影(002739)
  • 上证指数(sh000001)
代码如下所示:



  1. <span style="background-color: white;"># 择时股票池
  2. choice_symbols = ['002230', '300104', '300059', '601766', '600085', '600036', '600809', '000002', '002594', '002739']</span>
复制代码

  1. <span style="background-color: white;"># 使用run_loop_back运行策略
  2. abu_result_tuple, kl_pd_manger = abu.run_loop_back(read_cash,
  3.                                                    buy_factors,
  4.                                                    sell_factors,
  5.                                                    n_folds=6,
  6.                                                    choice_symbols=choice_symbols)</span>
复制代码

  1. <span style="background-color: white;">AbuMetricsBase.show_general(*abu_result_tuple, only_show_returns=True)</span>
复制代码


  1. <span style="background-color: white;">买入后卖出的交易数量:169
  2. 买入后尚未卖出的交易数量:1
  3. 胜率:55.6213%
  4. 平均获利期望:17.4736%
  5. 平均亏损期望:-6.6848%
  6. 盈亏比:3.5702
  7. 策略收益: 164.6985%
  8. 基准收益: 75.7668%
  9. 策略年化收益: 41.1746%
  10. 基准年化收益: 18.9417%
  11. 策略买入成交比例:88.2353%
  12. 策略资金利用率比例:34.8566%
  13. 策略共执行1008个交易日</span>
复制代码
112001.png


上面的回测结果虽然可以正常运行,但是很多交易细节还是使用的默认设置中的美股交易模式,因为默认的设置EMarketTargetType.E_MARKET_TARGET_US是美股,它会影响到一年多少个交易日等等交易细节,基准标尺等问题,如注意观察上面使用使用show_general显示的最终收益对比图,可以发现策略收益对比的是纳斯达克指数,并不是A股大盘。
正确的做法是首先将abupy量化环境设置为A股,代码如下所示:

  1. <span style="background-color: white;">abupy.env.g_market_target = EMarketTargetType.E_MARKET_TARGET_CN</span>
复制代码

  1. <span style="background-color: white;">abu_result_tuple, kl_pd_manger = abu.run_loop_back(read_cash,
  2.                                                    buy_factors,
  3.                                                    sell_factors,
  4.                                                    n_folds=6,
  5.                                                    choice_symbols=choice_symbols)</span>
复制代码

  1. <span style="background-color: white;">AbuMetricsBase.show_general(*abu_result_tuple, only_show_returns=True)</span>
复制代码

  1. <span style="background-color: white;">买入后卖出的交易数量:252
  2. 买入后尚未卖出的交易数量:2
  3. 胜率:43.6508%
  4. 平均获利期望:17.5752%
  5. 平均亏损期望:-6.9084%
  6. 盈亏比:2.1424
  7. 策略收益: 114.8238%
  8. 基准收益: 20.6036%
  9. 策略年化收益: 19.8870%
  10. 基准年化收益: 3.5685%
  11. 策略买入成交比例:83.0709%
  12. 策略资金利用率比例:38.6073%
  13. 策略共执行1455个交易日</span>
复制代码
112002.png


2. 涨跌停的特殊处理
下面主要讲解一下A股市场中比较特殊的地方:涨停,跌停,首先看一下下面这笔交易:



  1. <span style="background-color: white;">orders_pd = abu_result_tuple.orders_pd
  2. view_orders = orders_pd[(orders_pd['symbol'] == '601766') & (orders_pd['buy_date'] == 20150417)]
  3. view_orders</span>
复制代码
112003.png

从交易单来看似乎一切ok,虽然最终交易亏损了,下面使用plot_candle_from_order可视化view_orders,plot_candle_from_order标记出了买入和卖出点

  1. <span style="background-color: white;">trade_df = ABuMarketDrawing.plot_candle_from_order(view_orders)</span>
复制代码
112004.png


ABuMarketDrawing.plot_candle_from_order返回的trade_df是这笔交易的持股周期内的金融时间序列,如下所示,看第一条数据
2015-04-17交易日即为买入交易日,可以发现close,high,low的价格都是一样的,这代表了在集合竞价阶段股票已经涨停,但在我们回测中默认使用的
滑点买入类依然认为可以买入。
备注:滑点类相关内容请阅读:滑点策略与交易手续费

  1. <span style="background-color: white;">print('买入价格为:{}'.format(view_orders.ix[0].buy_price))
  2. trade_df.head()</span>
复制代码

  1. <span style="background-color: white;">买入价格为:35.61</span>
复制代码


112007.png
和买入类似,注意下面这笔交易:

  1. <span style="background-color: white;">view_orders = orders_pd[(orders_pd['symbol'] == '000002') & (orders_pd['sell_date'] == 20160704)]
  2. trade_df = ABuMarketDrawing.plot_candle_from_order(view_orders.ix[0])
  3. print('卖出价格为:{}'.format(view_orders.ix[0].sell_price))
  4. trade_df.tail()</span>
复制代码


112008.png
  1. <span style="background-color: white;">卖出价格为:21.27</span>
复制代码
112010.png


上面ABuMarketDrawing.plot_candle_from_order返回的trade_df是这笔交易的持股周期内的金融时间序列,看第最后一条数据
2016-07-04交易日即为卖出交易日,可以发现close,high,low的价格都是一样的,这代表了在集合竞价阶段股票已经跌停,但在我们回测中默认使用的
滑点卖出类依然认为可以卖出。
类似的情况还有下面这种虽然并不是在集合竞价阶段股票涨停,但是涨停下依然使用当天最高最低均价买入,买入价格为:1.175显然也不合适,同理在非集合竞价跌停的情况下以当天的平均价格卖出也不合适。
备注:默认滑点类使用均价买入卖出,详情阅读AbuSlippageSellBase,AbuSlippageBuyBase

  1. <span style="background-color: white;">view_orders = orders_pd[(orders_pd['symbol'] == '300059') & (orders_pd['buy_date'] == 20120222)]
  2. trade_df = ABuMarketDrawing.plot_candle_from_order(view_orders.ix[0])
  3. print('买入价格为:{}'.format(view_orders.ix[0].buy_price))
  4. trade_df.head(1)</span>
复制代码
112012.png



  1. <span style="background-color: white;">买入价格为:1.175</span>
复制代码
112014.png
为解决上述问题abupy中有针对A股涨停和跌停的特殊装饰器封装在滑点模块中:
具体实现原理不在这里展开,效果为:
  • 针对非集合竞价阶段的涨停,滑点买入价格以高概率在接近涨停的价格买入
  • 针对非集合竞价阶段的跌停,滑点卖出价格以高概率在接近跌停的价格卖出
  • 集合竞价阶段的涨停根据设置中的买入成功概率进行买入决策
  • 集合竞价阶段的跌停根据设置中的卖出成功概率进行卖出决策
具体实现请阅读源代码AbuSlippageSellBase,AbuSlippageBuyBase。
下面的代码即将上述4个针对A股涨停和跌停的特殊设置打开:

  1. <span style="background-color: white;">from abupy import slippage
  2. # 开启针对非集合竞价阶段的涨停,滑点买入价格以高概率在接近涨停的价格买入
  3. slippage.sbb.g_enable_limit_up = True
  4. # 将集合竞价阶段的涨停买入成功概率设置为0,如果设置为0.2即20%概率成功买入
  5. slippage.sbb.g_pre_limit_up_rate = 0
  6. # 开启针对非集合竞价阶段的跌停,滑点卖出价格以高概率在接近跌停的价格卖出
  7. slippage.ssb.g_enable_limit_down = True
  8. # 将集合竞价阶段的跌停卖出成功概率设置为0, 如果设置为0.2即20%概率成功卖出
  9. slippage.ssb.g_pre_limit_down_rate = 0</span>
复制代码
其它的回测因子等设置都不变,重新使用abu.run_loop_back进行回测,代码如下:
  1. <span style="background-color: white;">abu_result_slippage, kl_pd_manger = abu.run_loop_back(read_cash,
  2.                                                    buy_factors,
  3.                                                    sell_factors,
  4.                                                    n_folds=6,
  5.                                                    choice_symbols=choice_symbols)</span>
复制代码
  1. <span style="background-color: white;">AbuMetricsBase.show_general(*abu_result_slippage, only_show_returns=True)</span>
复制代码

  1. <span style="background-color: white;">买入后卖出的交易数量:249
  2. 买入后尚未卖出的交易数量:2
  3. 胜率:42.9719%
  4. 平均获利期望:16.4189%
  5. 平均亏损期望:-6.8735%
  6. 盈亏比:1.8920
  7. 策略收益: 89.4554%
  8. 基准收益: 20.6036%
  9. 策略年化收益: 15.4933%
  10. 基准年化收益: 3.5685%
  11. 策略买入成交比例:82.4701%
  12. 策略资金利用率比例:39.7315%
  13. 策略共执行1455个交易日</span>
复制代码

112015.png


上面的度量显示买入后卖出的交易数量:249,之前没开启涨跌停时是252,即可知道有三笔交易由于开启了涨跌停没有进行买入,但是怎么能知道那些交易发生了变化呢?
备注:读者可尝试使用 ABU量化系统使用文档-第九节 港股市场的回测 中讲解的AbuSDBreak对上面A股交易进行回测,度量结果。
3. 对多组交易结果进行分析
AbuOrderPdProxy是abupy中内置的针对交易单对象进行并集,交集,差集等交易单分析使用的工具,通过EOrderSameRule使用不同的判断为是否相同使用的交易单规则,更多实现详情请阅读AbuOrderPdProxy源代码,下面示例使用:

  1. <span style="background-color: white;">from abupy import AbuOrderPdProxy, EOrderSameRule

  2. orders_pd_slippage = abu_result_slippage.orders_pd
  3. # 通过orders_pd构造AbuOrderPdProxy
  4. proxy = AbuOrderPdProxy(orders_pd)
  5. with proxy.proxy_work(abu_result_slippage.orders_pd) as (order, order_slippage):
  6.     print('order == order_slippage: {}'.format(order == order_slippage))
  7.     print('order > order_slippage: {}'.format(order > order_slippage))
  8.     diff_a = order - order_slippage
  9.     diff_b = order_slippage - order</span>
复制代码
  1. <span style="background-color: white;">order == order_slippage: False
  2. order > order_slippage: True</span>
复制代码

下面对比一下两个差集的第一个数据,可以发现不同点是买入价格:
  • 未使用涨跌停控制的diff_a的交易依然是使用1.17的价格买入股票
  • 使用涨跌停控制的diff_b的交易使用接近涨停价格1.23的价格买入股票
备注:读者可以输出diff_a,diff_b自行一个一个对比一下看看,这里不再详对



  1. <span style="background-color: white;">diff_a.head(1)</span>
复制代码
112016.png



  1. <span style="background-color: white;">diff_b.head(1)</span>
复制代码
112017.png




下面通过EOrderSameRule.ORDER_SAME_BD做为AbuOrderPdProxy的参数,这样构造的AbuOrderPdProxy即切换了对交易单相同的规则:

  1. <span style="background-color: white;">class EOrderSameRule(Enum):
  2.     """对order_pd中对order判断为是否相同使用的规则"""

  3.     """order有相同的symbol和买入日期就认为是相同"""
  4.     ORDER_SAME_BD = 0
  5.     """order有相同的symbol, 买入日期,和卖出日期,即不考虑价格,只要日期相同就相同"""
  6.     ORDER_SAME_BSD = 1
  7.     """order有相同的symbol, 买入日期,相同的买入价格,即单子买入时刻都相同"""
  8.     ORDER_SAME_BDP = 2
  9.     """order有相同的symbol, 买入日期, 买入价格, 并且相同的卖出日期和价格才认为是相同,即买入卖出时刻都相同"""
  10.     ORDER_SAME_BSPD = 3</span>
复制代码
使用相同的symbol和买入日期就认为是相同的规则,结果如下:
  1. <span style="background-color: white;">proxy = AbuOrderPdProxy(orders_pd, EOrderSameRule.ORDER_SAME_BD)
  2. with proxy.proxy_work(abu_result_slippage.orders_pd) as (order, order_slippage):
  3.     diff_c = order - order_slippage
  4. diff_c</span>
复制代码
112018.png

  1. <span style="background-color: white;">ABuSymbolPd.make_kl_df('601766', start='20150417', end='20150417')</span>
复制代码

112019.png


  1. <span style="background-color: white;">ABuSymbolPd.make_kl_df('300104', start='20131009', end='20131009')</span>
复制代码
112020.png




可以看到diff_c中展示的三个交易单都是在集合竞价阶段已涨停了的股票交易,在开启了涨跌停控制后这三笔交易都没有进行买入。
如上述使用的AbuOrderPdProxy等工具,abupy不仅仅提供了对交易进行回测的功能,有许多分析,统计,机器学习,以及可视化工具在项目中可以帮助你分析策略,分析回测结果,以及为产生新的策略产生基础阀值等功能,在之后的教程中将陆续讲解使用以及示例。








回复

使用道具 举报

您需要登录后才可以回帖 登录 | 免费注册
关注微信