策略分享

102a-AI策略-代码交易

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

策略介绍

  • 102 中我们使用了 仓位分配 模块生成目标仓位,在 BigTrader 中 K 线处理中根据输入数据的目标仓位买卖
  • 在旧版的 StockRanker 策略中,是在 BigTrader 中通过编写代码处理交易逻辑,这里复现这个策略逻辑
  • 主要逻辑和 102 一致,这里尝试考虑更多细节,但实现较为复杂,在大部分情况下没有显著收益。
  • 如果对比差别不大,推荐用 102 作为模版,策略逻辑更加清晰,实现更加简洁和易于扩展

策略实现

策略结构

  • 将 StockRanker 预测分数数据 直接 传入到 BigTrader

BigTrader 初始化函数

  • stock_count = 3 设置持股数量
  • context.stock_weights 计算出的股票权重/仓位
  • context.max_cash_per_instrument 每只股票最大资金比例
  • context.options["hold_days"] = 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 = 3
    # 每只的股票的权重, 如下的权重分配会使得靠前的股票分配多一点的资金, [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

BigTrader K线处理函数

  • context.data 里加载了 BigTrader 的第一个数据输入,即 StockRanker 的预测结果,读入后是一个 panda DataFrame
  • 资金分配,持有天数是 5 天,则规划每个交易日买和卖 1/5 的权益。并尝试尽可能的提高资金利用率。
  • 卖出:对于持仓股票,按 StockRanker 的打分,优先卖出分数靠后的股票
  • 买入:按 StockRanker 的打分,优先买入分数靠前的股票
# 回测引擎:每日数据处理函数, 每天执行一次
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)

策略代码

102 中策略基础上,去掉 仓位分配 模块,使用旧版 StockRanker 策略的交易逻辑。

https://bigquant.com/codeshare/2d969ebf-6cff-4917-bec0-10c27139f861

\

{link}