第7节 寻找策略最优参数和评分
2017年10月13日
第9节 港股市场的回测
2017年10月13日

第8节 A股市场的回测

作者: 阿布

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

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

本节ipython notebook

之前的小节回测示例都是使用美股,本节示例A股市场的回测。
买入因子,卖出因子等依然使用相同的设置,如下所示:

# 设置初始资金数
read_cash = 1000000

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

# 卖出因子继续使用上一节使用的因子
sell_factors = [
    {'stop_loss_n': 1.0, 'stop_win_n': 3.0,
     'class': AbuFactorAtrNStop},
    {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
    {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
]

1. A股市场的回测示例

择时股票池使用沙盒缓存数据中的如下股票:

A股市场:

  • 科大讯飞(002230)
  • 乐视网(300104)
  • 东方财富(300059)
  • 中国中车(601766)
  • 同仁堂(600085),
  • 招商银行(600036)
  • 山西汾酒(600809)
  • 万科A(000002)
  • 比亚迪(002594)
  • 万达电影(002739)
  • 上证指数(sh000001)

代码如下所示:

# 择时股票池
choice_symbols = ['002230', '300104', '300059', '601766', '600085', '600036', '600809', '000002', '002594', '002739']
# 使用run_loop_back运行策略
abu_result_tuple, kl_pd_manger = abu.run_loop_back(read_cash,
                                                   buy_factors,
                                                   sell_factors,
                                                   n_folds=6,
                                                   choice_symbols=choice_symbols)
AbuMetricsBase.show_general(*abu_result_tuple, only_show_returns=True)
买入后卖出的交易数量:169
买入后尚未卖出的交易数量:1
胜率:55.6213%
平均获利期望:17.4736%
平均亏损期望:-6.6848%
盈亏比:3.5702
策略收益: 164.6985%
基准收益: 75.7668%
策略年化收益: 41.1746%
基准年化收益: 18.9417%
策略买入成交比例:88.2353%
策略资金利用率比例:34.8566%
策略共执行1008个交易日

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

正确的做法是首先将abupy量化环境设置为A股,代码如下所示:

abupy.env.g_market_target = EMarketTargetType.E_MARKET_TARGET_CN
abu_result_tuple, kl_pd_manger = abu.run_loop_back(read_cash,
                                                   buy_factors,
                                                   sell_factors,
                                                   n_folds=6,
                                                   choice_symbols=choice_symbols)
AbuMetricsBase.show_general(*abu_result_tuple, only_show_returns=True)
买入后卖出的交易数量:252
买入后尚未卖出的交易数量:2
胜率:43.6508%
平均获利期望:17.5752%
平均亏损期望:-6.9084%
盈亏比:2.1424
策略收益: 114.8238%
基准收益: 20.6036%
策略年化收益: 19.8870%
基准年化收益: 3.5685%
策略买入成交比例:83.0709%
策略资金利用率比例:38.6073%
策略共执行1455个交易日

2. 涨跌停的特殊处理

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

orders_pd = abu_result_tuple.orders_pd
view_orders = orders_pd[(orders_pd['symbol'] == '601766') & (orders_pd['buy_date'] == 20150417)]
view_orders

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

trade_df = ABuMarketDrawing.plot_candle_from_order(view_orders)

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

备注:滑点类相关内容请阅读:滑点策略与交易手续费

print('买入价格为:{}'.format(view_orders.ix[0].buy_price))
trade_df.head()
买入价格为:35.61

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

view_orders = orders_pd[(orders_pd['symbol'] == '000002') & (orders_pd['sell_date'] == 20160704)]
trade_df = ABuMarketDrawing.plot_candle_from_order(view_orders.ix[0])
print('卖出价格为:{}'.format(view_orders.ix[0].sell_price))
trade_df.tail()

卖出价格为:21.27

上面ABuMarketDrawing.plot_candle_from_order返回的trade_df是这笔交易的持股周期内的金融时间序列,看第最后一条数据
2016-07-04交易日即为卖出交易日,可以发现close,high,low的价格都是一样的,这代表了在集合竞价阶段股票已经跌停,但在我们回测中默认使用的
滑点卖出类依然认为可以卖出。

类似的情况还有下面这种虽然并不是在集合竞价阶段股票涨停,但是涨停下依然使用当天最高最低均价买入,买入价格为:1.175显然也不合适,同理在非集合竞价跌停的情况下以当天的平均价格卖出也不合适。

备注:默认滑点类使用均价买入卖出,详情阅读AbuSlippageSellBase,AbuSlippageBuyBase

view_orders = orders_pd[(orders_pd['symbol'] == '300059') & (orders_pd['buy_date'] == 20120222)]
trade_df = ABuMarketDrawing.plot_candle_from_order(view_orders.ix[0])
print('买入价格为:{}'.format(view_orders.ix[0].buy_price))
trade_df.head(1)

买入价格为:1.175

png

为解决上述问题abupy中有针对A股涨停和跌停的特殊装饰器封装在滑点模块中:

具体实现原理不在这里展开,效果为:

  1. 针对非集合竞价阶段的涨停,滑点买入价格以高概率在接近涨停的价格买入
  2. 针对非集合竞价阶段的跌停,滑点卖出价格以高概率在接近跌停的价格卖出
  3. 集合竞价阶段的涨停根据设置中的买入成功概率进行买入决策
  4. 集合竞价阶段的跌停根据设置中的卖出成功概率进行卖出决策

具体实现请阅读源代码AbuSlippageSellBase,AbuSlippageBuyBase。

下面的代码即将上述4个针对A股涨停和跌停的特殊设置打开:

from abupy import slippage
# 开启针对非集合竞价阶段的涨停,滑点买入价格以高概率在接近涨停的价格买入
slippage.sbb.g_enable_limit_up = True
# 将集合竞价阶段的涨停买入成功概率设置为0,如果设置为0.2即20%概率成功买入
slippage.sbb.g_pre_limit_up_rate = 0
# 开启针对非集合竞价阶段的跌停,滑点卖出价格以高概率在接近跌停的价格卖出
slippage.ssb.g_enable_limit_down = True
# 将集合竞价阶段的跌停卖出成功概率设置为0, 如果设置为0.2即20%概率成功卖出
slippage.ssb.g_pre_limit_down_rate = 0

其它的回测因子等设置都不变,重新使用abu.run_loop_back进行回测,代码如下:

abu_result_slippage, kl_pd_manger = abu.run_loop_back(read_cash,
                                                   buy_factors,
                                                   sell_factors,
                                                   n_folds=6,
                                                   choice_symbols=choice_symbols)
AbuMetricsBase.show_general(*abu_result_slippage, only_show_returns=True)
买入后卖出的交易数量:249
买入后尚未卖出的交易数量:2
胜率:42.9719%
平均获利期望:16.4189%
平均亏损期望:-6.8735%
盈亏比:1.8920
策略收益: 89.4554%
基准收益: 20.6036%
策略年化收益: 15.4933%
基准年化收益: 3.5685%
策略买入成交比例:82.4701%
策略资金利用率比例:39.7315%
策略共执行1455个交易日

png

上面的度量显示买入后卖出的交易数量:249,之前没开启涨跌停时是252,即可知道有三笔交易由于开启了涨跌停没有进行买入,但是怎么能知道那些交易发生了变化呢?

备注:读者可尝试使用 ABU量化系统使用文档-第九节 港股市场的回测 中讲解的AbuSDBreak对上面A股交易进行回测,度量结果。

3. 对多组交易结果进行分析

AbuOrderPdProxy是abupy中内置的针对交易单对象进行并集,交集,差集等交易单分析使用的工具,通过EOrderSameRule使用不同的判断为是否相同使用的交易单规则,更多实现详情请阅读AbuOrderPdProxy源代码,下面示例使用:

from abupy import AbuOrderPdProxy, EOrderSameRule

orders_pd_slippage = abu_result_slippage.orders_pd
# 通过orders_pd构造AbuOrderPdProxy
proxy = AbuOrderPdProxy(orders_pd)
with proxy.proxy_work(abu_result_slippage.orders_pd) as (order, order_slippage):
    print('order == order_slippage: {}'.format(order == order_slippage))
    print('order > order_slippage: {}'.format(order > order_slippage))
    diff_a = order - order_slippage
    diff_b = order_slippage - order
order == order_slippage: False
order > order_slippage: True

下面对比一下两个差集的第一个数据,可以发现不同点是买入价格:

  • 未使用涨跌停控制的diff_a的交易依然是使用1.17的价格买入股票
  • 使用涨跌停控制的diff_b的交易使用接近涨停价格1.23的价格买入股票

备注:读者可以输出diff_a,diff_b自行一个一个对比一下看看,这里不再详对

diff_a.head(1)

png

diff_b.head(1)

png

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

class EOrderSameRule(Enum):
    """对order_pd中对order判断为是否相同使用的规则"""

    """order有相同的symbol和买入日期就认为是相同"""
    ORDER_SAME_BD = 0
    """order有相同的symbol, 买入日期,和卖出日期,即不考虑价格,只要日期相同就相同"""
    ORDER_SAME_BSD = 1
    """order有相同的symbol, 买入日期,相同的买入价格,即单子买入时刻都相同"""
    ORDER_SAME_BDP = 2
    """order有相同的symbol, 买入日期, 买入价格, 并且相同的卖出日期和价格才认为是相同,即买入卖出时刻都相同"""
    ORDER_SAME_BSPD = 3

使用相同的symbol和买入日期就认为是相同的规则,结果如下:

proxy = AbuOrderPdProxy(orders_pd, EOrderSameRule.ORDER_SAME_BD)
with proxy.proxy_work(abu_result_slippage.orders_pd) as (order, order_slippage):
    diff_c = order - order_slippage
diff_c

png

ABuSymbolPd.make_kl_df('601766', start='20150417', end='20150417')

png

ABuSymbolPd.make_kl_df('300104', start='20131009', end='20131009')

png

可以看到diff_c中展示的三个交易单都是在集合竞价阶段已涨停了的股票交易,在开启了涨跌停控制后这三笔交易都没有进行买入。

如上述使用的AbuOrderPdProxy等工具,abupy不仅仅提供了对交易进行回测的功能,有许多分析,统计,机器学习,以及可视化工具在项目中可以帮助你分析策略,分析回测结果,以及为产生新的策略产生基础阀值等功能,在之后的教程中将陆续讲解使用以及示例。

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

1 评论

  1. 林柏杨说道:

    股票池的数量超过一个就会出现问题,请问如何解决,谢谢!
    from abupy import AbuFactorBuyBreak
    from abupy import AbuFactorAtrNStop
    from abupy import AbuFactorPreAtrNStop
    from abupy import AbuFactorCloseAtrNStop
    from abupy import abu
    from abupy import AbuMetricsBase
    from abupy import EMarketTargetType
    import abupy
    abupy.env.g_market_target = EMarketTargetType.E_MARKET_TARGET_CN
    read_cash=1000000
    stock_pickers=None
    buy_factors=[{'xd':60,'class':AbuFactorBuyBreak},{'xd':42,'class':AbuFactorBuyBreak}]
    sell_factors=[{'stop_loss_n':1.0,'stop_win_n':3.0,'class':AbuFactorAtrNStop},{'class':AbuFactorPreAtrNStop,'pre_atr_n':1.5},{'class':AbuFactorCloseAtrNStop,'close_atr_n':1.5}]
    choice_symbols = []
    abu_result_tuple,kl_pd_manger=abu.run_loop_back(read_cash,buy_factors,sell_factors,stock_pickers,choice_symbols=None,n_folds=2)

    AbuMetricsBase.show_general(*abu_result_tuple, only_show_returns=True)

    Traceback (most recent call last):
    File "", line 1, in
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\spawn.py", line 105, in spawn_main
    exitcode = _main(fd)
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\spawn.py", line 114, in _main
    prepare(preparation_data)
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\spawn.py", line 225, in prepare
    _fixup_main_from_path(data['init_main_from_path'])
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\spawn.py", line 277, in _fixup_main_from_path
    run_name="__mp_main__")
    File "C:\ProgramData\Anaconda3\lib\runpy.py", line 263, in run_path
    pkg_name=pkg_name, script_name=fname)
    File "C:\ProgramData\Anaconda3\lib\runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
    File "C:\ProgramData\Anaconda3\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
    File "C:\Users\Administrator\PycharmProjects\untitled3\test.py", line 15, in
    abu_result_tuple,kl_pd_manger=abu.run_loop_back(read_cash,buy_factors,sell_factors,stock_pickers,choice_symbols=None,n_folds=2)
    File "C:\ProgramData\Anaconda3\lib\site-packages\abupy\CoreBu\ABu.py", line 110, in run_loop_back
    n_process_pick_time=n_process_pick)
    File "C:\ProgramData\Anaconda3\lib\site-packages\abupy\AlphaBu\ABuPickTimeMaster.py", line 78, in do_symbols_with_same_factors_process
    for choice_symbols in process_symbols)
    File "C:\ProgramData\Anaconda3\lib\site-packages\abupy\CoreBu\ABuParallel.py", line 91, in __call__
    future_result = pool.submit(jb[0], *jb[1], **jb[2])
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\process.py", line 454, in submit
    self._start_queue_management_thread()
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\process.py", line 415, in _start_queue_management_thread
    self._adjust_process_count()
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\process.py", line 434, in _adjust_process_count
    p.start()
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 33, in __init__
    prep_data = spawn.get_preparation_data(process_obj._name)
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\spawn.py", line 143, in get_preparation_data
    _check_not_importing_main()
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\spawn.py", line 136, in _check_not_importing_main
    is not going to be frozen to produce an executable.''')
    RuntimeError:
    An attempt has been made to start a new process before the
    current process has finished its bootstrapping phase.

    This probably means that you are not using fork to start your
    child processes and you have forgotten to use the proper idiom
    in the main module:

    if __name__ == '__main__':
    freeze_support()
    ...

    The "freeze_support()" line can be omitted if the program
    is not going to be frozen to produce an executable.
    pid:158400 done!
    pid:158388 gen kl_pd complete:81.83%
    pid:158416 gen kl_pd complete:92.36%
    pid:158408 gen kl_pd complete:80.21%
    exception calling callback for
    Traceback (most recent call last):
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 297, in _invoke_callbacks
    callback(self)
    File "C:\ProgramData\Anaconda3\lib\site-packages\abupy\CoreBu\ABuParallel.py", line 77, in when_done
    result.append(r.result())
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 398, in result
    return self.__get_result()
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 357, in __get_result
    raise self._exception
    concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending.
    exception calling callback for
    Traceback (most recent call last):
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 297, in _invoke_callbacks
    callback(self)
    File "C:\ProgramData\Anaconda3\lib\site-packages\abupy\CoreBu\ABuParallel.py", line 77, in when_done
    result.append(r.result())
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 398, in result
    return self.__get_result()
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 357, in __get_result
    raise self._exception
    concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending.
    exception calling callback for
    Traceback (most recent call last):
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 297, in _invoke_callbacks
    callback(self)
    File "C:\ProgramData\Anaconda3\lib\site-packages\abupy\CoreBu\ABuParallel.py", line 77, in when_done
    result.append(r.result())
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 398, in result
    return self.__get_result()
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 357, in __get_result
    raise self._exception
    concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending.
    exception calling callback for
    Traceback (most recent call last):
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 297, in _invoke_callbacks
    callback(self)
    File "C:\ProgramData\Anaconda3\lib\site-packages\abupy\CoreBu\ABuParallel.py", line 77, in when_done
    result.append(r.result())
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 398, in result
    return self.__get_result()
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 357, in __get_result
    raise self._exception
    concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending.
    exception calling callback for
    Traceback (most recent call last):
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 297, in _invoke_callbacks
    callback(self)
    File "C:\ProgramData\Anaconda3\lib\site-packages\abupy\CoreBu\ABuParallel.py", line 77, in when_done
    result.append(r.result())
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 398, in result
    return self.__get_result()
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\_base.py", line 357, in __get_result
    raise self._exception
    concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending.
    Exception in thread Thread-1:
    Traceback (most recent call last):
    File "C:\ProgramData\Anaconda3\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
    File "C:\ProgramData\Anaconda3\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\process.py", line 295, in _queue_management_worker
    shutdown_worker()
    File "C:\ProgramData\Anaconda3\lib\concurrent\futures\process.py", line 253, in shutdown_worker
    call_queue.put_nowait(None)
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\queues.py", line 129, in put_nowait
    return self.put(obj, False)
    File "C:\ProgramData\Anaconda3\lib\multiprocessing\queues.py", line 83, in put
    raise Full
    queue.Full

    metrics input is invalid or zero order gen!
    metrics input is invalid or zero order gen!

发表评论

电子邮件地址不会被公开。 必填项已用*标注