BigQuant使用文档

150-AI选股策略

由qxiao创建,最终由qxiao 被浏览 63 用户

策略介绍

本策略通过选择多维度的因子,使用AI算法来预测股票的未来表现并进行排序。这里使用算法StockRanker,BigQuant 平台开发的一种先进的机器学习算法,专门用于量化选股排序学习,通过在多个因子/特征的数据上训练,旨在从大量股票中识别并排序那些未来表现可能最优异的股票。

策略思想

策略基于以下几个核心思想:

  1. 特征选择:输入对股票价格有显著影响的多维度因子,包括估值、成长、财务、杠杆、市场及技术指标方面的因子
  2. 预测目标:预测未来 5 日收益率
  3. 数据抽取和处理:抽取和处理数据
  4. 模型训练:应用StockRanker算法,训练模型来预测股票未来上涨概率。StockRanker返回一个相对分数(score),分数越大,预测未来涨幅越大。注意此 score 绝对值没有意义。
  5. 回测与交易:设置调仓周期,根据仓位目标,发出交易信号

策略实现&代码

在BigQuant平台可以非常容易的将量化+AI结合起来。如下是策略代码,可以克隆策略进入 AIStudio 运行和调优.

https://bigquant.com/codesharev3/b014c802-f7eb-464c-bf62-3735c4807dc2

\

策略理解

该可视化策略主要由两个部分组成,左半边抽取训练数据集,用于算法模型训练;右半边抽取预测数据集,用于模型预测结果,最后进行回测交易。

\

基础选股模块

  • m1、m15代码列表模块实现选股操作,m1抽取2018-01-01到2020-12-31的数据用于训练数据集,m15抽取2021-01-01到2021-12-31的数据用于预测数据集
    • 交易市场:选择中国A股市场
    • m1、m15的输出连接到m3、m5、m16的第一个输入

因子特征选择

预测目标

  • m4输入特征列表模块实现数据标注,添加了 label 数据列
  • label 列的计算
    • m_lead(close, 5) / m_lead(open, 1) 未来第5天的收盘价除以明天的开盘价,即对应的是明天开盘买入,5个交易日后收盘卖出
    • c_quantile_cont(_future_return, 0.01)c_quantile_cont(_future_return, 0.99) 计算未来收益的 1% 和 99% 分位值
    • clip(_future_return, _future_return_1pct, _future_return_99pct) 根据分位值裁剪数据,即裁剪极值
    • all_cbins(_clipped_return, 20) AS _label 将数据等分为 20 组,即离散化
    • if( m_lead(high, 1) = m_lead(low, 1), NULL, _label) as label 过滤掉一字涨停的情况

数据抽取

  • m3、m5在抽取训练数据集的因子特征、标签label,用于算法模型训练
  • m16在抽取预测数据集的因子特征,用于获得预测结果
  • 开始日期、结束日期一般不需要指定,使用代码列表里的日期。历史数据向前取数 90天,用于部分算子需要更多历史数据

\

数据连接和缺失值处理

  • m8 DAI数据连接模块实现训练数据集的因子特征和标签label的合并连接
    • 连接方式:inner内连接,基于两个表中的date、instrument列进行连接,只保留在两个表中都匹配的记录
  • m12和m16对数据进行缺失值处理

模型训练

  • m7 StockRanker 训练用于模型训练,输入数据中的label作为预测目标,其他列(除date instrument 外)作为特征输入。
    • StockRanker算法原理,可以参考论文 From RankNet to LambdaRank to LambdaMART: An Overview

    • 学习算法:排序,相关技术请搜索 learning to rank

    • 叶节点数量:30。StockRanker会构建多棵二叉树,这里表示每棵树的最大叶节点数量。一般情况下,这个值越大,在训练集上拟合越好,也可能在训练数据上过度拟合

    • 每叶节点最小样本数:1000。这里表示每个叶节点最小需要1000个数据样本支持。一般情况下,这个值越大,泛化性能越好

    • 树的数量:20。StockRanker会训练多棵树,后面的树不断拟合前面训练的残差,以期逐步减少训练损失,最后收敛。一般情况下,这个值越大,在训练集上拟合越好,也可能在训练数据上过度拟合

    • 学习率:0.1。机器学习学习率。经验参数。

    • 特征值离散化数量:对于每个特征,StockRanker会将其分组离散化。一般情况下,这个值越小,泛化行越好,但也可能学习不够

    • 特征列采样率和数据行采样率,在训练每棵树时,使用多少比例的特征(列)和数据样本(行),为1表示每次都适应全部数据。小于1,表示随机抽样一部分。一般情况下,这个值越小,泛化行越好,但也可能学习不够

    • NDCG base,请搜索 NDCG。对应下面的1部分,这个值越大,表示顺序的影响越小。

\

模型预测

  • m13 StockRanker 预测用于模型预测,基于训练出来的模型,对输入的预测数据集进行目标预测
  • 模型预测股票未来上涨概率,StockRanker返回一个相对分数(score),分数越大,预测未来涨幅越大(此 score 绝对值没有意义)。

\

回测交易

  • m7 BigTrader 回测和交易,每5日调仓,开盘买入,收盘卖出。

  • 初始化函数:主要代码

    # 交易引擎:初始化函数, 只执行一次
    def bigquant_run(context):
        import math
        import numpy as np
    
        from bigtrader.finance.commission import PerOrder
    
        # 系统已经设置了默认的交易手续费和滑点, 要修改手续费可使用如下函数
        context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
        # 预测数据, 通过 options 传入进来, 使用 read_df 函数, 加载到内存 (DataFrame)
        # 设置买入的股票数量, 这里买入预测股票列表排名靠前的5只
        stock_count = 5
        # 每只的股票的权重, 如下的权重分配会使得靠前的股票分配多一点的资金, [0.339160, 0.213986, 0.169580, ..]
        context.stock_weights = np.array(
            [1 / math.log(i + 2) for i in range(0, stock_count)]
        )
        context.stock_weights = context.stock_weights / context.stock_weights.sum()
    
        # 设置每只股票占用的最大资金比例
        context.max_cash_per_instrument = 0.2
        context.options["hold_days"] = 5
    
  • K线处理函数:主要代码

    # 回测引擎:每日数据处理函数, 每天执行一次
    def bigquant_run(context, data):
        # 按日期过滤得到今日的预测数据
        ranker_prediction = context.data[
            context.data.date == data.current_dt.strftime("%Y-%m-%d")
        ]
    
        # 1. 资金分配
        # 平均持仓时间是hold_days, 每日都将买入股票, 每日预期使用 1/hold_days 的资金
        # 实际操作中, 会存在一定的买入误差, 所以在前hold_days天, 等量使用资金;之后, 尽量使用剩余资金(这里设置最多用等量的1.5倍)
        is_staging = (
            context.trading_day_index < context.options["hold_days"]
        )  # 是否在建仓期间(前 hold_days 天)
        cash_avg = context.portfolio.portfolio_value / context.options["hold_days"]
        cash_for_buy = min(context.portfolio.cash, (1 if is_staging else 1.5) * cash_avg)
        cash_for_sell = cash_avg - (context.portfolio.cash - cash_for_buy)
        positions = {
            e: p.amount * p.last_sale_price for e, p in context.portfolio.positions.items()
        }
    
        # 2. 生成卖出订单:hold_days天之后才开始卖出;对持仓的股票, 按机器学习算法预测的排序末位淘汰
        if not is_staging and cash_for_sell > 0:
            equities = {e: e for e, p in context.portfolio.positions.items()}
            instruments = list(
                reversed(
                    list(
                        ranker_prediction.instrument[
                            ranker_prediction.instrument.apply(lambda x: x in equities)
                        ]
                    )
                )
            )
    
            for instrument in instruments:
                context.order_target(instrument, 0)
                cash_for_sell -= positions[instrument]
                if cash_for_sell <= 0:
                    break
    
        # 3. 生成买入订单:按机器学习算法预测的排序, 买入前面的stock_count只股票
        buy_cash_weights = context.stock_weights
        buy_instruments = list(ranker_prediction.instrument[: len(buy_cash_weights)])
        max_cash_per_instrument = (
            context.portfolio.portfolio_value * context.max_cash_per_instrument
        )
        for i, instrument in enumerate(buy_instruments):
            cash = cash_for_buy * buy_cash_weights[i]
            if cash > max_cash_per_instrument - positions.get(instrument, 0):
                # 确保股票持仓量不会超过每次股票最大的占用资金量
                cash = max_cash_per_instrument - positions.get(instrument, 0)
            if cash > 0:
                context.order_value(instrument, cash)
    
    

    更多关于 回测的说明可以参考 预备知识回测部分


\

文档

AI StockRanker耍单票策略
{link}