历史文档

【历史文档】策略示例-AI模板策略交易逻辑解读

由clearyf创建,最终由small_q 被浏览 1230 用户

更新

本文内容对应旧版平台与旧版资源,其内容不再适合最新版平台,请查看新版平台的使用说明

新版量化开发IDE(AIStudio):

https://bigquant.com/wiki/doc/aistudio-aiide-NzAjgKapzW

新版模版策略:

https://bigquant.com/wiki/doc/demos-ecdRvuM1TU

新版数据平台:

https://bigquant.com/data/home

https://bigquant.com/wiki/doc/dai-PLSbc1SbZX

新版表达式算子:

https://bigquant.com/wiki/doc/dai-sql-Rceb2JQBdS

新版因子平台:

https://bigquant.com/wiki/doc/bigalpha-EOVmVtJMS5

\

导语

StockRanker算法是基于梯度树模型的排序算法,原理可以参考《 list wise learning to rank》,本文详细讲解StockRanker算法AI模板策略的回测模块中策略的构建过程。

我们在完成模型预测,获取StockRanker模型的预测集数据结果后,就可以将预测数据作为外部数据传递给回测模块并构建策略。 回测模块通常需要输入回测的起止时间和标的范围、历史行情数据、外部数据和基准数据。其中,历史行情数据和基准数据不是必需传入的,模块默认从平台数据库获取指定时间范围的标的历史数据,基准数据默认从平台数据库获取指定的基准指数历史数据。因此模板策略可视化界面中只需将证券代码列表模块和预测模块的输出连线到回测模块的指定输入接口

初始化函数

初始化函数中通常加载预测数据、设置策略所需全局变量和手续费率以及滑点。 首先我们通过 context.options['data'].read_df() 把预测数据读入并赋值给全局变量context.ranker_prediction,这里我们使用context.ranker_prediction在初始化函数中定义变量,那么主函数中也能够通过context.ranker_prediction调取到预测数据,实现了变量定义的全局化。

    # 加载预测数据
    context.ranker_prediction = context.options['data'].read_df()

相同的道理,我们可以通过context对象定义其它所需的全局变量,例如股票的权重context.stock_weights、每只股票占用的最大资金比例context.max_cash_per_instrument 以及资金周转天数context.options['hold_days']

    # 设置买入的股票数量,这里买入预测股票列表排名靠前的5只
    stock_count = 5
    # 每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[0.339160, 0.213986, 0.169580, ..]
    context.stock_weights = T.norm([1 / math.log(i + 2) for i in range(0, stock_count)])
    # 设置每只股票占用的最大资金比例
    context.max_cash_per_instrument = 0.2
    context.options['hold_days'] = 5

设置手续费率,买入费率0.0003,卖出费率0.0013,最低成本5元(不足5元按5元计算)。

    # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))

数据准备函数

数据准备函数中通常计算策略中用到的指标信号,例如大盘风控信号。由于本例默认的策略中没有涉及指标计算,因此此模块代码直接为pass。

盘前处理函数

盘前处理函数中通常进行订单管理,例如买卖单的查询/撤销。由于本例默认的策略中没有涉及订单管理,因此为空。

主函数

每日主函数交易逻辑中:

  • 第一步,从全局变量context.ranker_prediction中获取日期为当日(即data.current_dt.strftime('%Y-%m-%d'))的股票预测排序。
    # 按日期过滤得到今日的预测数据
    ranker_prediction = context.ranker_prediction[
        context.ranker_prediction.date == data.current_dt.strftime('%Y-%m-%d')]
  • 第二步,分配每日买入股票的资金cash_for_buy和需要卖出的股票市值cash_for_sell。 根据资金周转天数hold_days,我们的理论每日轮仓资金为cash_avg =总资产/hold_days, 在建仓期hold_days天内,每日尽量使用等额资金买入股票,即买入cash_avg 。 建仓期后为保证每日轮动cash_avg 资金,需要每天卖出一定量的股票以保证可用现金>=cash_avg。 由于每日轮仓逻辑中会完整卖掉一只股票的所有市值,可能出现某只股票市值超出cash_avg很多,这时候卖出这只股票所得现金中超出cash_avg的那部分会闲置,为了提高资金使用效率我们设置可用于买入股票的资金为1.5倍的cash_avg和现金的最小值,这样可以使得剩余可用现金尽量少,提高了资金的利用效率。
    # 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.symbol: p.amount * p.last_sale_price
                 for e, p in context.portfilio.positions.items()}
  • 第三步,如果过了建仓期,每日执行卖出逻辑,即卖出持仓中排名靠后的股票直到卖出的总市值达到cash_for_sell。
    # 2. 生成卖出订单:hold_days天之后才开始卖出;对持仓的股票,按机器学习算法预测的排序末位淘汰
    if not is_staging and cash_for_sell > 0:
        equities = {e.symbol: e for e, p in context.portfilio.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(context.symbol(instrument), 0)
            cash_for_sell -= positions[instrument]
            if cash_for_sell <= 0:
                break
  • 第四步, 按机器学习算法预测的排序,买入前面的stock_count只股票直到买入的总市值达到cash_for_buy 。
    # 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(context.symbol(instrument), cash)

结语:使用AI预测结果构建策略的过程有很多种方式,本文以StockRanker算法AI模板策略为例详细解释了利用排序算法的预测结果进行策略构建。在自定义买入卖出策略教程中我们将会看到,只需在模板策略的基础上进行少量修改就可以实现策略交易逻辑的定制化开发。

\

标签

StockRanker算法排序算法回测模块
评论
  • 这里有个问题,这个模型的购买时间时早上开盘,卖出时间是下午收盘。意味着当天卖出回收的现金不能用于购买今天购买的股票,而是用于购买明天的股票。这个算法中的“资金分配模块”不符合实际
  • 通常机构里是买卖按 algo 同时进行。所以大概率是能实现以 VWAP 结算。不知道 BigQuant 里有没有可以用 VWAP 来 mark pnl 的
  • 应该只是回测是定在这个时间把。如果自己实盘,可以改为其他时间买卖啊
{link}