第4节 多支股票择时回测与仓位管理
2017年2月3日
第6节 回测结果的度量
2017年10月11日

第5节 选股策略的开发

作者: 阿布

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

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

本节ipython notebook

在第一节即说过:

  • 在对的时间,遇见对的人(股票),是一种幸福
  • 在对的时间,遇见错的人(股票),是一种悲伤
  • 在错的时间,遇见对的人(股票),是一声叹息
  • 在错的时间,遇见错的人(股票),是一种无奈

之前的节讲的都是择时(什么时候投资), 本节将讲解选股。

1. 选股因子的编写

与择时小节类似,实现示例在中abu量化系统实现一个选股策略。

如下代码AbuPickRegressAngMinMax为选股因子,它的作用是将股票前期走势进行线性拟合计算一个角度,参数为选股条件,将选股条件作用于角度后进行股票的筛选。

from abupy import AbuPickStockBase, ps, ABuRegUtil

class AbuPickRegressAngMinMax(AbuPickStockBase):
    """拟合角度选股因子示例类"""
    def _init_self(self, **kwargs):
        """通过kwargs设置拟合角度边际条件,配置因子参数"""

        # 暂时与base保持一致不使用kwargs.pop('a', default)方式
        # fit_pick中 ang > threshold_ang_min, 默认负无穷,即默认所有都符合
        self.threshold_ang_min = -np.inf
        if 'threshold_ang_min' in kwargs:
            # 设置最小角度阀值
            self.threshold_ang_min = kwargs['threshold_ang_min']

        # fit_pick中 ang < threshold_ang_max, 默认正无穷,即默认所有都符合
        self.threshold_ang_max = np.inf
        if 'threshold_ang_max' in kwargs:
            # 设置最大角度阀值
            self.threshold_ang_max = kwargs['threshold_ang_max']

    @ps.reversed_result
    def fit_pick(self, kl_pd, target_symbol):
        """开始根据自定义拟合角度边际参数进行选股"""
        # 计算走势角度
        ang = ABuRegUtil.calc_regress_deg(kl_pd.close, show=False)
        # 根据参数进行角度条件判断
        if self.threshold_ang_min < ang < self.threshold_ang_max:
            return True
        return False

    def fit_first_choice(self, pick_worker, choice_symbols, *args, **kwargs):
        raise NotImplementedError('AbuPickRegressAng fit_first_choice unsupported now!')

上面编写的AbuPickRegressAngMinMax即为一个完整的选股策略:

  1. 选股策略必须继承自AbuPickStockBase
  2. 选股策略必须实现fit_pick,即完成通过选股阶段金融时间序列对股票决策是否选中
  3. 选股策略必须实现fit_first_choice, 但是可以raise NotImplementedError,fit_first_choice后面的章节示例
  4. fit_pick上的装饰器ps.reversed_result稍后讲解

选股模块主要功能依托AbuPickStockWorker,其类似择时模块中的AbuPickTimeWorker,其通过init_stock_pickers()函数将所有选股因子实例化,然后在之后的fit()操作中,遍历所有选股因子,使用选股因子的fit_pick()函数,保留所有选股因子的fit_pick()都返回True的股票,只要有一个选股因子的fit_pick结果是False就将股票剔除。

详细代码请查阅 AbuPickStockWorker源代码。

本节只讲简单讲解选股使用示例,比如只想选取符合上升走势的股票:

from abupy import AbuPickStockWorker
from abupy import AbuBenchmark, AbuCapital, AbuKLManager

# 选股条件threshold_ang_min=0.0, 即要求股票走势为向上上升趋势
stock_pickers = [{'class': AbuPickRegressAngMinMax,
                  'threshold_ang_min': 0.0, 'reversed': False}]

# 从这几个股票里进行选股,只是为了演示方便
# 一般的选股都会是数量比较多的情况比如全市场股票
choice_symbols = ['usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG',
                  'usTSLA', 'usWUBA', 'usVIPS']

benchmark = AbuBenchmark()
capital = AbuCapital(1000000, benchmark)
kl_pd_manger = AbuKLManager(benchmark, capital)
stock_pick = AbuPickStockWorker(capital, benchmark, kl_pd_manger,
                                choice_symbols=choice_symbols,
                                stock_pickers=stock_pickers)
stock_pick.fit()
# 打印最后的选股结果
stock_pick.choice_symbols
['usSFUN', 'usBIDU', 'usTSLA', 'usWUBA', 'usVIPS']

上面的实现方式和第一节中讲解择时回测的使用时一样通过分解流程方式一步一步实现使用AbuPickStockWorker进行选股,目的是为了更清晰的说明内部操作流程,编码过程会显的有些复杂臃肿。

实际上在编写完成一个策略后只需要abu.run_loop_back()函数即可以完成回测,在后面的小节中会进行讲解。

上面选股的结果将noah剔除,因为它在回测之前的选股周期内趋势为下降趋势,如下图所示:

# 从kl_pd_manger缓存中获取选股走势数据,
# 注意get_pick_stock_kl_pd()为选股数据,get_pick_time_kl_pd()为择时
kl_pd_noah = kl_pd_manger.get_pick_stock_kl_pd('usNOAH')
# 绘制并计算角度
deg = ABuRegUtil.calc_regress_deg(kl_pd_noah.close)
print('noah 选股周期内角度={}'.format(round(deg, 3)))

noah 选股周期内角度=-9.289

注意上面的选股数据要使用择时回测数据之前的一段时间数据,在AbuPickStockBase中定义了xd,min_xd选股周期获取参数,
在AbuKLManager中通过get_pick_stock_kl_pd()函数配合xd,min_xd参数获取选股周期数据

更多详情请阅读AbuPickStockBase源代码与AbuKLManager源代码

上述选股代码实现在ABuPickStockExecute.do_pick_stock_work()中进行了封装,即讲AbuPickStockWorker及一些零散操作进行封装。

更多详情请阅读ABuPickStockExecute,使用示例如下所示:

eg:继续使用AbuPickRegressAngMinMax做为选股因子,如下定义threshold_ang_min=0.0, threshold_ang_max=10.0,即只选取上升趋势且上升角度小于10度的股票,下面示例使用ABuPickStockExecute.do_pick_stock_work()函数

from abupy import ABuPickStockExecute

stock_pickers = [{'class': AbuPickRegressAngMinMax,
                  'threshold_ang_min': 0.0, 'threshold_ang_max': 10.0,
                  'reversed': False}]

ABuPickStockExecute.do_pick_stock_work(choice_symbols, benchmark,
                                       capital, stock_pickers)
['usSFUN', 'usBIDU']

可以看到结果sfun和baidu都符合,下面代码验证一下:

kl_pd_sfun = kl_pd_manger.get_pick_stock_kl_pd('usSFUN')
print('sfun 选股周期内角度={:.3f}'.format(ABuRegUtil.calc_regress_deg(kl_pd_sfun.close)))

sfun 选股周期内角度=8.230
kl_pd_baidu = kl_pd_manger.get_pick_stock_kl_pd('usBIDU')
print('bidu 选股周期内角度={:.3f}'.format(ABuRegUtil.calc_regress_deg(kl_pd_baidu.close)))

bidu 选股周期内角度=9.644

上面结果显示两支股票在选股周期中的价格趋势拟合角度都在0-10之间。

假设修改需求想要选取周期内趋势角度在0度-10度之外的所有股票,可以这样编写代码:

# 和上面的代码唯一的区别就是reversed=True
stock_pickers = [{'class': AbuPickRegressAngMinMax,
                  'threshold_ang_min': 0.0, 'threshold_ang_max': 10.0,
                  'reversed': True}]

ABuPickStockExecute.do_pick_stock_work(choice_symbols, benchmark,
                                       capital, stock_pickers)
['usNOAH', 'usTSLA', 'usWUBA', 'usVIPS']

可以看到结果与之前相反除了baidu和sfun之外所有股票都被选上,由于在AbuPickStockBase中定义函数reversed_result(),它的作用就是定义结果是否反转。

具体实现请阅读AbuPickStockBase中reversed_result装饰器的实现。

使用实际很简单,在每个具体的选股因子上的fit_pick()函数上根据需要选择是否安上装饰器,如下编写价格选股因子所示:

class AbuPickStockPriceMinMax(AbuPickStockBase):
    """价格选股因子示例类"""
    def _init_self(self, **kwargs):
        """通过kwargs设置选股价格边际条件,配置因子参数"""

        # 暂时与base保持一致不使用kwargs.pop('a', default)方式
        # fit_pick中选择 > 最小(threshold_price_min), 默认负无穷,即默认所有都符合
        self.threshold_price_min = -np.inf
        if 'threshold_price_min' in kwargs:
            # 最小价格阀值
            self.threshold_price_min = kwargs['threshold_price_min']

        # fit_pick中选择 < 最大(threshold_price_max), 默认正无穷,即默认所有都符合
        self.threshold_price_max = np.inf
        if 'threshold_price_max' in kwargs:
            # 最大价格阀值
            self.threshold_price_max = kwargs['threshold_price_max']

    @ps.reversed_result
    def fit_pick(self, kl_pd, target_symbol):
        """开始根据自定义价格边际参数进行选股"""
        if kl_pd.close.max() < self.threshold_price_max and kl_pd.close.min() > self.threshold_price_min:
            # kl_pd.close的最大价格 < 最大价格阀值 且 kl_pd.close的最小价格 > 最小价格阀值
            return True
        return False

    def fit_first_choice(self, pick_worker, choice_symbols, *args, **kwargs):
        raise NotImplementedError('AbuPickStockPriceMinMax fit_first_choice unsupported now!')

备注:本节所编写的选股示例代码以内置在abupy项目代码中可通过如下方式直接导入:

from abupy import AbuPickStockPriceMinMax
from abupy import AbuPickRegressAngMinMax

2. 多个选股因子并行执行

  1. ABuPickRegressAngMinMax: threshold_ang_min=0.0, 即要求股票走势为向上,上升趋势
  2. ABuPickStockPriceMinMax threshold_price_min=50.0, 即要求股票在选股周期内股价最小值要大于50.0

继续使用ABuPickStockExecute,使上面两个选股因子同时生效,结果符合的只有BIDU及TSLA,代码如下所示:

from abupy import AbuPickStockPriceMinMax


# 选股list使用两个不同的选股因子组合,并行同时生效
stock_pickers = [{'class': AbuPickRegressAngMinMax,
                  'threshold_ang_min': 0.0, 'reversed': False},
                 {'class': AbuPickStockPriceMinMax,
                  'threshold_price_min': 50.0,
                  'reversed': False}]

%time ABuPickStockExecute.do_pick_stock_work(choice_symbols, benchmark, capital, stock_pickers)
['usBIDU', 'usTSLA']

3. 使用并行来提升选股运行效率

与并行择时实现方式类似,选股使用AbuPickStockMaster并行执行多个进程来提升选股效率。

具体代码请查询AbuPickStockMaster,下面为使用示例, 使用do_pick_stock_with_process()函数执行默认n_process_pick_stock=8,即默认同时运行8个进程。

# 选股list使用两个不同的选股因子组合,并行同时生效
stock_pickers = [{'class': AbuPickRegressAngMinMax,
                  'threshold_ang_min': 0.0, 'reversed': False},
                 {'class': AbuPickStockPriceMinMax,
                  'threshold_price_min': 50.0,
                  'reversed': False}]

cs = AbuPickStockMaster.do_pick_stock_with_process(capital, benchmark, stock_pickers, choice_symbols)
pid:6614 pick stocks complete:100.0%
pid:6615 pick stocks complete:100.0%
pid:6616 pick stocks complete:100.0%
pid:6617 pick stocks complete:100.0%
pid:6618 pick stocks complete:100.0%
pid:6619 pick stocks complete:100.0%
pid:6620 pick stocks complete:100.0%
pid:6621 pick stocks complete:100.0%
pid:6617 done!
pid:6618 done!
pid:6615 done!
pid:6614 done!
pid:6616 done!
pid:6620 done!
pid:6619 done!
pid:6621 done!

从上面输出可以看到选股阶段启动了8个进程,因为股票数量太少,进程启动销毁也需要一定时间,所以上例整体效率并没有提高。

更多示例请阅读《量化交易之路》书中的示例以及对应的ipython代码或者python代码

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

发表评论

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