关于能模拟交易策略每天信号为空的问题

实盘交易
prepare
模拟交易
策略分享
标签: #<Tag:0x00007fcc122ced78> #<Tag:0x00007fcc122cebc0> #<Tag:0x00007fcc122ce968> #<Tag:0x00007fcc122ce828>

(ypf007) #1
克隆策略
In [1]:
# 基础参数配置
class conf:
    start_date = '2010-01-01'
    end_date='2017-05-01'
    # split_date 之前的数据用于训练,之后的数据用作效果评估
    split_date = '2012-01-01'
    # D.instruments: https://bigquant.com/docs/data_instruments.html
    instruments = D.instruments(start_date, end_date)

    # 机器学习目标标注函数
    # 如下标注函数等价于 min(max((持有期间的收益 * 100), -20), 20) + 20 (后面的M.fast_auto_labeler会做取整操作)
    # 说明:max/min这里将标注分数限定在区间[-20, 20],+20将分数变为非负数 (StockRanker要求标注分数非负整数)
    label_expr = ['return * 100', 'where(label > {0}, {0}, where(label < -{0}, -{0}, label)) + {0}'.format(20)]
    # 持有天数,用于计算label_expr中的return值(收益)
    hold_days = 5

    # 特征 https://bigquant.com/docs/data_features.html,你可以通过表达式构造任何特征
    features = [
        'close_5/close_0',  # 5日收益
        'close_10/close_0',  # 10日收益
        'close_20/close_0',  # 20日收益
        'avg_amount_0/avg_amount_5',  # 当日/5日平均交易额
        'avg_amount_5/avg_amount_20',  # 5日/20日平均交易额
        'rank_avg_amount_0/rank_avg_amount_5',  # 当日/5日平均交易额排名
        'rank_avg_amount_5/rank_avg_amount_10',  # 5日/10日平均交易额排名
        'rank_return_0',  # 当日收益
        'rank_return_5',  # 5日收益
        'rank_return_10',  # 10日收益
    ]
# 给数据做标注:给每一行数据(样本)打分,一般分数越高表示越好
m1 = M.fast_auto_labeler.v8(
    instruments=conf.instruments, start_date=conf.start_date, end_date=conf.end_date,
    label_expr=conf.label_expr, hold_days=conf.hold_days,
    benchmark='000300.SHA', sell_at='open', buy_at='open')
# 计算特征数据
m2 = M.general_feature_extractor.v5(
    instruments=conf.instruments, start_date=conf.start_date, end_date=conf.end_date,
    features=conf.features)
# 数据预处理:缺失数据处理,数据规范化,T.get_stock_ranker_default_transforms为StockRanker模型做数据预处理
m3 = M.transform.v2(
    data=m2.data, transforms=T.get_stock_ranker_default_transforms(),
    drop_null=True, astype='int32', except_columns=['date', 'instrument'],
    clip_lower=0, clip_upper=200000000)
# 合并标注和特征数据
m4 = M.join.v2(data1=m1.data, data2=m3.data, on=['date', 'instrument'], sort=True)

# 训练数据集
m5_training = M.filter.v2(data=m4.data, expr='date < "%s"' % conf.split_date)
# 评估数据集
m5_evaluation = M.filter.v2(data=m4.data, expr='"%s" <= date' % conf.split_date)
# StockRanker机器学习训练
m6 = M.stock_ranker_train.v3(training_ds=m5_training.data, features=conf.features)
# 对评估集做预测
m7 = M.stock_ranker_predict.v2(model_id=m6.model_id, data=m5_evaluation.data)


## 量化回测 https://bigquant.com/docs/strategy_backtest.html
# 回测引擎:初始化函数,只执行一次
def initialize(context):
    ds_id='55853b28236411e88c0a0242ac110059'   #此id为databacktest3.csv的数据文件
    context.new_df=DataSource(id=ds_id).read_df()
    # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    # 预测数据,通过options传入进来,使用 read_df 函数,加载到内存 (DataFrame)
    context.ranker_prediction = context.options['ranker_prediction'].read_df()
    # 设置买入的股票数量,这里买入预测股票列表排名靠前的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.date={}

# 回测引擎:每日数据处理函数,每天执行一次
def handle_data(context, data):
    databacktest=context.new_df
    # 按日期过滤得到今日的预测数据
    ranker_prediction = context.ranker_prediction[context.ranker_prediction.date == data.current_dt.strftime('%Y-%m-%d')]
    current_dt = 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.symbol: p.amount * p.last_sale_price         for e, p in context.perf_tracker.position_tracker.positions.items()}
    equities = {e.symbol: e for e, p in context.perf_tracker.position_tracker.positions.items()}
    buy_dates = {}
    for e in equities:
        if e in context.date:
            buy_dates[e] = context.date[e]
    
  # 2. 生成卖出订单:hold_days天之后才开始卖出;对持仓的股票,按StockRanker预测的排序末位淘汰
    if databacktest['direction'].values[databacktest.date==current_dt]==-1:    # LSTM择时卖
        instruments = list(reversed(list(ranker_prediction.instrument[ranker_prediction.instrument.apply(
                lambda x: x in equities and not context.has_unfinished_sell_order(equities[x]))])))
        for instrument in instruments:
            if context.trading_calendar.session_distance(pd.Timestamp(context.date[instrument]), pd.Timestamp(current_dt))>=5:
                context.order_target(context.symbol(instrument), 0)
    
    if not is_staging and cash_for_sell > 0:
        instruments = list(reversed(list(ranker_prediction.instrument[ranker_prediction.instrument.apply(
                lambda x: x in equities and not context.has_unfinished_sell_order(equities[x]))])))
        # print('rank order for sell %s' % instruments)
        for instrument in instruments:
            context.order_target(context.symbol(instrument), 0)
            cash_for_sell -= positions[instrument]
            if cash_for_sell <= 0:
                break

    # 3. 生成买入订单:按StockRanker预测的排序,买入前面的stock_count只股票
    if databacktest['direction'].values[databacktest.date==current_dt]==1:    # LSTM择时买
        buy_dt = data.current_dt.strftime('%Y-%m-%d')
        context.date=buy_dt
        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)
                buy_dates[instrument] = current_dt
            
    context.date = buy_dates
# 调用回测引擎
m8 = M.trade.v2(
    instruments=m7.instruments,
    start_date=m7.start_date,
    end_date=m7.end_date,
    initialize=initialize,
    handle_data=handle_data,
    order_price_field_buy='open',       # 表示 开盘 时买入
    order_price_field_sell='close',     # 表示 收盘 前卖出
    capital_base=100000,               # 初始资金
    benchmark='000300.SHA',             # 比较基准,不影响回测结果
    # 通过 options 参数传递预测数据和参数给回测引擎
    options={'ranker_prediction': m7.predictions, 'hold_days': conf.hold_days},
    m_cached=False
) 
[2018-03-17 12:27:51.135001] INFO: bigquant: fast_auto_labeler.v8 开始运行..
[2018-03-17 12:27:51.144058] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.172695] INFO: bigquant: fast_auto_labeler.v8 运行完成[0.037717s].
[2018-03-17 12:27:51.346424] INFO: bigquant: general_feature_extractor.v5 开始运行..
[2018-03-17 12:27:51.350067] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.351290] INFO: bigquant: general_feature_extractor.v5 运行完成[0.004887s].
[2018-03-17 12:27:51.370283] INFO: bigquant: transform.v2 开始运行..
[2018-03-17 12:27:51.376974] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.378317] INFO: bigquant: transform.v2 运行完成[0.008032s].
[2018-03-17 12:27:51.396028] INFO: bigquant: join.v2 开始运行..
[2018-03-17 12:27:51.399877] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.401251] INFO: bigquant: join.v2 运行完成[0.005218s].
[2018-03-17 12:27:51.416425] INFO: bigquant: filter.v2 开始运行..
[2018-03-17 12:27:51.421210] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.422513] INFO: bigquant: filter.v2 运行完成[0.006131s].
[2018-03-17 12:27:51.431821] INFO: bigquant: filter.v2 开始运行..
[2018-03-17 12:27:51.435923] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.437053] INFO: bigquant: filter.v2 运行完成[0.005245s].
[2018-03-17 12:27:51.459470] INFO: bigquant: stock_ranker_train.v3 开始运行..
[2018-03-17 12:27:51.462788] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.464030] INFO: bigquant: stock_ranker_train.v3 运行完成[0.004582s].
[2018-03-17 12:27:51.478826] INFO: bigquant: stock_ranker_predict.v2 开始运行..
[2018-03-17 12:27:51.488837] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.490075] INFO: bigquant: stock_ranker_predict.v2 运行完成[0.011249s].
[2018-03-17 12:27:51.563460] INFO: bigquant: backtest.v7 开始运行..
[2018-03-17 12:27:51.720899] INFO: algo: set price type:backward_adjusted
[2018-03-17 12:29:49.128445] INFO: Performance: Simulated 1285 trading days out of 1285.
[2018-03-17 12:29:49.130128] INFO: Performance: first open: 2012-01-04 01:30:00+00:00
[2018-03-17 12:29:49.131479] INFO: Performance: last close: 2017-04-20 07:00:00+00:00
[注意] 有 111 笔卖出是在多天内完成的。当日卖出股票超过了当日股票交易的2.5%会出现这种情况。
  • 收益率6815.23%
  • 年化收益率129.51%
  • 基准收益率47.57%
  • 阿尔法1.24
  • 贝塔0.46
  • 夏普比率5.8
  • 胜率0.733
  • 盈亏比1.492
  • 收益波动率21.62%
  • 信息比率5.34
  • 最大回撤11.34%
[2018-03-17 12:29:56.128415] INFO: bigquant: backtest.v7 运行完成[124.564958s].

(小Q) #2

您好,看了你的代码,该代码模拟交易会存在一些问题。

因为每天模拟交易的机制就是运行该策略时,系统会将M.trade函数里面的参数替换为当日交易日期,因此有些策略如果要用到最新数据,那么需要prepare函数来更新该数据,否则当天运行没有结果。

本策略就是这样,虽然M.trade的开始日期和结束日期默认是最新日期,但预测结果并没有对当天的股票进行预测,因此在handle_data函数里,ranker_prediction为空的dataframe,因此没有交易信号啦。

修改如下,您再看看:

克隆策略
In [1]:
# 基础参数配置
class conf:
    start_date = '2010-01-01'
    end_date='2017-05-01'
    # split_date 之前的数据用于训练,之后的数据用作效果评估
    split_date = '2012-01-01'
    # D.instruments: https://bigquant.com/docs/data_instruments.html
    instruments = D.instruments(start_date, end_date)

    # 机器学习目标标注函数
    # 如下标注函数等价于 min(max((持有期间的收益 * 100), -20), 20) + 20 (后面的M.fast_auto_labeler会做取整操作)
    # 说明:max/min这里将标注分数限定在区间[-20, 20],+20将分数变为非负数 (StockRanker要求标注分数非负整数)
    label_expr = ['return * 100', 'where(label > {0}, {0}, where(label < -{0}, -{0}, label)) + {0}'.format(20)]
    # 持有天数,用于计算label_expr中的return值(收益)
    hold_days = 5

    # 特征 https://bigquant.com/docs/data_features.html,你可以通过表达式构造任何特征
    features = [
        'close_5/close_0',  # 5日收益
        'close_10/close_0',  # 10日收益
        'close_20/close_0',  # 20日收益
        'avg_amount_0/avg_amount_5',  # 当日/5日平均交易额
        'avg_amount_5/avg_amount_20',  # 5日/20日平均交易额
        'rank_avg_amount_0/rank_avg_amount_5',  # 当日/5日平均交易额排名
        'rank_avg_amount_5/rank_avg_amount_10',  # 5日/10日平均交易额排名
        'rank_return_0',  # 当日收益
        'rank_return_5',  # 5日收益
        'rank_return_10',  # 10日收益
    ]
# 给数据做标注:给每一行数据(样本)打分,一般分数越高表示越好
m1 = M.fast_auto_labeler.v8(
    instruments=conf.instruments, start_date=conf.start_date, end_date=conf.end_date,
    label_expr=conf.label_expr, hold_days=conf.hold_days,
    benchmark='000300.SHA', sell_at='open', buy_at='open')
# 计算特征数据
m2 = M.general_feature_extractor.v5(
    instruments=conf.instruments, start_date=conf.start_date, end_date=conf.end_date,
    features=conf.features)
# 数据预处理:缺失数据处理,数据规范化,T.get_stock_ranker_default_transforms为StockRanker模型做数据预处理
m3 = M.transform.v2(
    data=m2.data, transforms=T.get_stock_ranker_default_transforms(),
    drop_null=True, astype='int32', except_columns=['date', 'instrument'],
    clip_lower=0, clip_upper=200000000)
# 合并标注和特征数据
m4 = M.join.v2(data1=m1.data, data2=m3.data, on=['date', 'instrument'], sort=True)

# 训练数据集
m5_training = M.filter.v2(data=m4.data, expr='date < "%s"' % conf.split_date)
# 评估数据集
m5_evaluation = M.filter.v2(data=m4.data, expr='"%s" <= date' % conf.split_date)
# StockRanker机器学习训练
m6 = M.stock_ranker_train.v3(training_ds=m5_training.data, features=conf.features)
# 对评估集做预测
m7 = M.stock_ranker_predict.v2(model_id=m6.model_id, data=m5_evaluation.data)
[2018-03-17 13:09:40.285203] INFO: bigquant: fast_auto_labeler.v8 开始运行..
[2018-03-17 13:09:40.297631] INFO: bigquant: 命中缓存
[2018-03-17 13:09:40.323282] INFO: bigquant: fast_auto_labeler.v8 运行完成[0.0381s].
[2018-03-17 13:09:40.603460] INFO: bigquant: general_feature_extractor.v5 开始运行..
[2018-03-17 13:09:40.613905] INFO: bigquant: 命中缓存
[2018-03-17 13:09:40.616025] INFO: bigquant: general_feature_extractor.v5 运行完成[0.012602s].
[2018-03-17 13:09:40.636067] INFO: bigquant: transform.v2 开始运行..
[2018-03-17 13:09:40.640441] INFO: bigquant: 命中缓存
[2018-03-17 13:09:40.641916] INFO: bigquant: transform.v2 运行完成[0.005859s].
[2018-03-17 13:09:40.657057] INFO: bigquant: join.v2 开始运行..
[2018-03-17 13:09:40.660732] INFO: bigquant: 命中缓存
[2018-03-17 13:09:40.662693] INFO: bigquant: join.v2 运行完成[0.005695s].
[2018-03-17 13:09:40.678838] INFO: bigquant: filter.v2 开始运行..
[2018-03-17 13:09:40.682422] INFO: bigquant: 命中缓存
[2018-03-17 13:09:40.683897] INFO: bigquant: filter.v2 运行完成[0.005082s].
[2018-03-17 13:09:40.694649] INFO: bigquant: filter.v2 开始运行..
[2018-03-17 13:09:40.709209] INFO: bigquant: 命中缓存
[2018-03-17 13:09:40.710851] INFO: bigquant: filter.v2 运行完成[0.016252s].
[2018-03-17 13:09:40.764806] INFO: bigquant: stock_ranker_train.v3 开始运行..
[2018-03-17 13:09:40.787026] INFO: bigquant: 命中缓存
[2018-03-17 13:09:40.788996] INFO: bigquant: stock_ranker_train.v3 运行完成[0.024219s].
[2018-03-17 13:09:40.820131] INFO: bigquant: stock_ranker_predict.v2 开始运行..
[2018-03-17 13:09:40.833359] INFO: bigquant: 命中缓存
[2018-03-17 13:09:40.835085] INFO: bigquant: stock_ranker_predict.v2 运行完成[0.014987s].
In [1]:
## 量化回测 https://bigquant.com/docs/module_trade.html
# 回测引擎:准备数据,只执行一次
def prepare(context):
    # context.start_date / end_date,回测的时候,为trader传入参数;在实盘运行的时候,由系统替换为实盘日期
    instruments = D.instruments()
    n1 = M.general_feature_extractor.v5(
        instruments=instruments,
        start_date=context.start_date, end_date=context.end_date,
        model_id=context.options['model_id'])
    n2 = M.transform.v2(
        data=n1.data, transforms=T.get_stock_ranker_default_transforms(),
        drop_null=True, astype='int32', except_columns=['date', 'instrument'],
        clip_lower=0, clip_upper=200000000)
    # 加载训练出来的模型进行预测
    n3 = M.stock_ranker_predict.v2(model_id=context.options['model_id'], data=n2.data)
    context.instruments = n3.instruments
    context.options['ranker_predictions'] = n3.predictions



## 量化回测 https://bigquant.com/docs/strategy_backtest.html
# 回测引擎:初始化函数,只执行一次
def initialize(context):
    ds_id='55853b28236411e88c0a0242ac110059'   #此id为databacktest3.csv的数据文件
    context.new_df=DataSource(id=ds_id).read_df()
    # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    # 将预测的结果的datasource转换成dataframe
    context.ranker_prediction = context.options['ranker_prediction'].read_df()
    # 设置买入的股票数量,这里买入预测股票列表排名靠前的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.date={}

# 回测引擎:每日数据处理函数,每天执行一次
def handle_data(context, data):
    databacktest=context.new_df
    # 按日期过滤得到今日的预测数据
    ranker_prediction = context.ranker_prediction[context.ranker_prediction.date == data.current_dt.strftime('%Y-%m-%d')]
    current_dt = 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.symbol: p.amount * p.last_sale_price for e, p in context.perf_tracker.position_tracker.positions.items()}
    equities = {e.symbol: e for e, p in context.perf_tracker.position_tracker.positions.items()}
    buy_dates = {}
    for e in equities:
        if e in context.date:
            buy_dates[e] = context.date[e]
    
  # 2. 生成卖出订单:hold_days天之后才开始卖出;对持仓的股票,按StockRanker预测的排序末位淘汰
    if databacktest['direction'].values[databacktest.date==current_dt]==-1:    # LSTM择时卖
        instruments = list(reversed(list(ranker_prediction.instrument[ranker_prediction.instrument.apply(
                lambda x: x in equities and not context.has_unfinished_sell_order(equities[x]))])))
        for instrument in instruments:
            if context.trading_calendar.session_distance(pd.Timestamp(context.date[instrument]), pd.Timestamp(current_dt))>=5:
                context.order_target(context.symbol(instrument), 0)
    
    if not is_staging and cash_for_sell > 0:
        instruments = list(reversed(list(ranker_prediction.instrument[ranker_prediction.instrument.apply(
                lambda x: x in equities and not context.has_unfinished_sell_order(equities[x]))])))
        # print('rank order for sell %s' % instruments)
        for instrument in instruments:
            context.order_target(context.symbol(instrument), 0)
            cash_for_sell -= positions[instrument]
            if cash_for_sell <= 0:
                break

    # 3. 生成买入订单:按StockRanker预测的排序,买入前面的stock_count只股票
    if databacktest['direction'].values[databacktest.date==current_dt]==1:    # LSTM择时买
        buy_dt = data.current_dt.strftime('%Y-%m-%d')
        context.date=buy_dt
        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)
                buy_dates[instrument] = current_dt
            
    context.date = buy_dates
# 调用回测引擎
m8 = M.trade.v2(
    instruments=m7.instruments,
    start_date=m7.start_date,
    end_date=m7.end_date,
    prepare=prepare,
    initialize=initialize,
    handle_data=handle_data,
    order_price_field_buy='open',       # 表示 开盘 时买入
    order_price_field_sell='close',     # 表示 收盘 前卖出
    capital_base=100000,               # 初始资金
    benchmark='000300.SHA',             # 比较基准,不影响回测结果
    # 通过 options 参数传递预测数据和参数给回测引擎
    options={'hold_days': conf.hold_days, 'model_id': m6.model_id},
    m_cached=False
) 
[2018-03-17 12:27:51.135001] INFO: bigquant: fast_auto_labeler.v8 开始运行..
[2018-03-17 12:27:51.144058] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.172695] INFO: bigquant: fast_auto_labeler.v8 运行完成[0.037717s].
[2018-03-17 12:27:51.346424] INFO: bigquant: general_feature_extractor.v5 开始运行..
[2018-03-17 12:27:51.350067] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.351290] INFO: bigquant: general_feature_extractor.v5 运行完成[0.004887s].
[2018-03-17 12:27:51.370283] INFO: bigquant: transform.v2 开始运行..
[2018-03-17 12:27:51.376974] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.378317] INFO: bigquant: transform.v2 运行完成[0.008032s].
[2018-03-17 12:27:51.396028] INFO: bigquant: join.v2 开始运行..
[2018-03-17 12:27:51.399877] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.401251] INFO: bigquant: join.v2 运行完成[0.005218s].
[2018-03-17 12:27:51.416425] INFO: bigquant: filter.v2 开始运行..
[2018-03-17 12:27:51.421210] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.422513] INFO: bigquant: filter.v2 运行完成[0.006131s].
[2018-03-17 12:27:51.431821] INFO: bigquant: filter.v2 开始运行..
[2018-03-17 12:27:51.435923] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.437053] INFO: bigquant: filter.v2 运行完成[0.005245s].
[2018-03-17 12:27:51.459470] INFO: bigquant: stock_ranker_train.v3 开始运行..
[2018-03-17 12:27:51.462788] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.464030] INFO: bigquant: stock_ranker_train.v3 运行完成[0.004582s].
[2018-03-17 12:27:51.478826] INFO: bigquant: stock_ranker_predict.v2 开始运行..
[2018-03-17 12:27:51.488837] INFO: bigquant: 命中缓存
[2018-03-17 12:27:51.490075] INFO: bigquant: stock_ranker_predict.v2 运行完成[0.011249s].
[2018-03-17 12:27:51.563460] INFO: bigquant: backtest.v7 开始运行..
[2018-03-17 12:27:51.720899] INFO: algo: set price type:backward_adjusted
[2018-03-17 12:29:49.128445] INFO: Performance: Simulated 1285 trading days out of 1285.
[2018-03-17 12:29:49.130128] INFO: Performance: first open: 2012-01-04 01:30:00+00:00
[2018-03-17 12:29:49.131479] INFO: Performance: last close: 2017-04-20 07:00:00+00:00
[注意] 有 111 笔卖出是在多天内完成的。当日卖出股票超过了当日股票交易的2.5%会出现这种情况。
  • 收益率6815.23%
  • 年化收益率129.51%
  • 基准收益率47.57%
  • 阿尔法1.24
  • 贝塔0.46
  • 夏普比率5.8
  • 胜率0.733
  • 盈亏比1.492
  • 收益波动率21.62%
  • 信息比率5.34
  • 最大回撤11.34%
[2018-03-17 12:29:56.128415] INFO: bigquant: backtest.v7 运行完成[124.564958s].


(ahxdct) #3

模块版本升级后,现在还需要自行编写代码解决这个问题吗?


(iQuant) #4

您现在是遇到这个问题了吗?您用的是哪个版本的模块呢?您可以描述一下问题,我们来帮您看一下。


(ahxdct) #5

本代码由可视化策略环境自动生成 2019年1月18日 14:35

本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。

回测引擎:每日数据处理函数,每天执行一次

def m19_handle_data_bigquant_run(context, data):
# 按日期过滤得到今日的预测数据
ranker_prediction = context.ranker_prediction[
context.ranker_prediction.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.symbol: p.amount * p.last_sale_price
             for e, p in context.perf_tracker.position_tracker.positions.items()}

# 2. 生成卖出订单:hold_days天之后才开始卖出;对持仓的股票,按机器学习算法预测的排序末位淘汰
if not is_staging and cash_for_sell > 0:
    equities = {e.symbol: e for e, p in context.perf_tracker.position_tracker.positions.items()}
    instruments = list(reversed(list(ranker_prediction.instrument[ranker_prediction.instrument.apply(
            lambda x: x in equities and not context.has_unfinished_sell_order(equities[x]))])))
    # print('rank order for sell %s' % instruments)
    for instrument in instruments:
        context.order_target(context.symbol(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(context.symbol(instrument), cash)

回测引擎:准备数据,只执行一次

def m19_prepare_bigquant_run(context):
pass

回测引擎:初始化函数,只执行一次

def m19_initialize_bigquant_run(context):
# 加载预测数据
context.ranker_prediction = context.options[‘data’].read_df()

# 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
# 预测数据,通过options传入进来,使用 read_df 函数,加载到内存 (DataFrame)
# 设置买入的股票数量,这里买入预测股票列表排名靠前的5只
stock_count = 4
# 每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[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.25
context.options['hold_days'] = 5

m1 = M.instruments.v2(
start_date=‘2014-01-01’,
end_date=‘2018-06-01’,
market=‘CN_STOCK_A’,
instrument_list=’’,
max_count=0
)

m2 = M.advanced_auto_labeler.v2(
instruments=m1.data,
label_expr="""# #号开始的表示注释

0. 每行一个,顺序执行,从第二个开始,可以使用label字段

1. 可用数据字段见 {{web_host_url}}docs/data_history_data.html

添加benchmark_前缀,可使用对应的benchmark数据

2. 可用操作符和函数见 表达式引擎 <{{web_host_url}}docs/big_expr.html>_

计算收益:5日收盘价(作为卖出价格)除以明日开盘价(作为买入价格)

shift(close, -5) / shift(open, -1)

极值处理:用1%和99%分位的值做clip

clip(label, all_quantile(label, 0.01), all_quantile(label, 0.99))

将分数映射到分类,这里使用20个分类

all_wbins(label, 1000)

过滤掉一字涨停的情况 (设置label为NaN,在后续处理和训练中会忽略NaN的label)

where(shift(high, -1) == shift(low, -1), NaN, label)
“”",
start_date=’’,
end_date=’’,
benchmark=‘000300.SHA’,
drop_na_label=True,
cast_label_int=True
)

m3 = M.input_features.v1(
features="""# #号开始的表示注释

多个特征,每行一个,可以包含基础特征和衍生特征

ta_mfi_14_0
ta_adx_14_0
delta(ta_cci_14_0,1)
correlation(avg_turn_5,return_5,5)
ta_willr_14_0
where(ta_macd_macd_12_26_9_0>0,swing_volatility_5_0,swing_volatility_5_0*-1)
ta_aroon_up_14_0*ta_aroon_down_14_0
correlation(daily_return_0,daily_return_2,2)"""
)

m15 = M.general_feature_extractor.v7(
instruments=m1.data,
features=m3.data,
start_date=’’,
end_date=’’,
before_start_days=10
)

m4 = M.chinaa_stock_filter.v1(
input_data=m15.data,
index_constituent_cond=[‘全部’],
board_cond=[‘全部’],
industry_cond=[‘全部’],
st_cond=[‘正常’],
output_left_data=False
)

m16 = M.derived_feature_extractor.v3(
input_data=m4.data,
features=m3.data,
date_col=‘date’,
instrument_col=‘instrument’,
drop_na=False,
remove_extra_columns=False
)

m7 = M.join.v3(
data1=m2.data,
data2=m16.data,
on=‘date,instrument’,
how=‘inner’,
sort=False
)

m13 = M.dropnan.v1(
input_data=m7.data
)

m6 = M.stock_ranker_train.v5(
training_ds=m13.data,
features=m3.data,
learning_algorithm=‘排序’,
number_of_leaves=30,
minimum_docs_per_leaf=1000,
number_of_trees=20,
learning_rate=0.1,
max_bins=1023,
feature_fraction=1,
m_lazy_run=False
)

m9 = M.instruments.v2(
start_date=T.live_run_param(‘trading_date’, ‘2018-06-01’),
end_date=T.live_run_param(‘trading_date’, ‘2018-12-31’),
market=‘CN_STOCK_A’,
instrument_list=’’,
max_count=0
)

m17 = M.general_feature_extractor.v7(
instruments=m9.data,
features=m3.data,
start_date=’’,
end_date=’’,
before_start_days=20
)

m5 = M.chinaa_stock_filter.v1(
input_data=m17.data,
index_constituent_cond=[‘全部’],
board_cond=[‘全部’],
industry_cond=[‘全部’],
st_cond=[‘正常’],
output_left_data=False
)

m18 = M.derived_feature_extractor.v3(
input_data=m5.data,
features=m3.data,
date_col=‘date’,
instrument_col=‘instrument’,
drop_na=False,
remove_extra_columns=False
)

m14 = M.dropnan.v1(
input_data=m18.data
)

m8 = M.stock_ranker_predict.v5(
model=m6.model,
data=m14.data,
m_lazy_run=False
)

m19 = M.trade.v4(
instruments=m9.data,
options_data=m8.predictions,
start_date=’’,
end_date=’’,
handle_data=m19_handle_data_bigquant_run,
prepare=m19_prepare_bigquant_run,
initialize=m19_initialize_bigquant_run,
volume_limit=0.025,
order_price_field_buy=‘open’,
order_price_field_sell=‘close’,
capital_base=1000000,
auto_cancel_non_tradable_orders=True,
data_frequency=‘daily’,
price_type=‘后复权’,
product_type=‘股票’,
plot_charts=True,
backtest_only=False,
benchmark=‘000300.SHA’
)


(ahxdct) #6

代码如上,运行正常,没有输出。


(copen) #7

是什么时候开始模拟实盘的呢?每天都没有输出吗?您可以看一下策略日志,看运行是否有报错,若没有报错说明您策略运行正常,可以尝试向前取一定天数数据,参加下图: