一次运行评价多个因子的效果


(sensezeng) #1

通常执行一个因子的训练,回测等,需要几分钟。
而同时输入多个因子,就变成了多因子策略。
每次都修改因子再回测很麻烦。
所以,简单修改了生成的代码,把因子放在features里面,原有代码封装成函数。用for循环函数。

克隆策略
In [ ]:
# 本代码由可视化策略环境自动生成 2018年4月16日 14:42
# 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。


features = ['list_days_0',
            'sh_holder_avg_pct_0',
            'pe_ttm_0']

m1 = M.instruments.v2(
    start_date='2016-01-01',
    end_date='2017-09-30',
    market='CN_STOCK_A',
    instrument_list='',
    max_count=0
)

m2 = M.advanced_auto_labeler.v2(
    instruments=m1.data,
    label_expr="""# #号开始的表示注释
# 0. 每行一个,顺序执行,从第二个开始,可以使用label字段
# 1. 可用数据字段见 https://bigquant.com/docs/data_history_data.html
#   添加benchmark_前缀,可使用对应的benchmark数据
# 2. 可用操作符和函数见 `表达式引擎 <https://bigquant.com/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, 20)

# 过滤掉一字涨停的情况 (设置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
)

def run_single(feature):
    m3 = M.input_features.v1(
        features=feature
    )

    m4 = M.general_feature_extractor.v6(
        instruments=m1.data,
        features=m3.data,
        start_date='',
        end_date='',
        before_start_days=20
    )

    m5 = M.derived_feature_extractor.v2(
        input_data=m4.data,
        features=m3.data,
        date_col='date',
        instrument_col='instrument'
    )

    m7 = M.join.v3(
        data1=m2.data,
        data2=m5.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', '2017-10-01'),
        end_date=T.live_run_param('trading_date', '2018-04-10'),
        market='CN_STOCK_A',
        instrument_list='',
        max_count=0
    )

    m10 = M.general_feature_extractor.v6(
        instruments=m9.data,
        features=m3.data,
        start_date='',
        end_date='',
        before_start_days=20
    )

    m11 = M.derived_feature_extractor.v2(
        input_data=m10.data,
        features=m3.data,
        date_col='date',
        instrument_col='instrument'
    )

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

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

    # 回测引擎:每日数据处理函数,每天执行一次
    def m12_handle_data_bigquant_run(context, data):
        # 按日期过滤得到今日的预测数据
        ranker_prediction = context.ranker_prediction[
            context.ranker_prediction.date == data.current_dt.strftime('%Y-%m-%d')]

        #----------------------------止赢模块START------------------------
        positions = {e.symbol: p.cost_basis  for e, p in context.portfolio.positions.items()}
        # 新建当日止赢股票列表是为了handle_data 策略逻辑部分不再对该股票进行判断
        current_stopwin_stock = [] 
        if len(positions) > 0:
            for i in positions.keys():
                stock_cost = positions[i] 
                stock_market_price = data.current(context.symbol(i), 'price') 
                percent = (stock_market_price - stock_cost)/stock_cost
                if percent > 0.4:   
                    context.order_target_percent(context.symbol(i),0)     
                    #current_stopwin_stock.append(i)
                    print('股票:',i,'出现止盈状况')
                    print(percent)
                if percent < -0.13:
                    context.order_target_percent(context.symbol(i),0)     
                    #current_stoploss_stock.append(i)    
                    print('股票:',i,'出现止损状况')
                    print(percent)                
        #---------------------------止赢模块END---------------------------    

        # 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)
        cash_for_sell = 0
        if context.portfolio.cash > cash_avg:
            cash_for_sell = 0
        else:
            cash_for_sell = cash_avg - context.portfolio.cash
        positions = {e.symbol: p.amount * p.last_sale_price
                     for e, p in context.perf_tracker.position_tracker.positions.items()}

        # 2. 生成卖出订单:hold_days天之后才开始卖出;对持仓的股票,按机器学习算法预测的排序末位淘汰
        print (cash_for_sell)
        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 m12_prepare_bigquant_run(context):
        pass

    # 回测引擎:初始化函数,只执行一次
    def m12_initialize_bigquant_run(context):
        # 加载预测数据
        context.ranker_prediction = context.options['data'].read_df()

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

    m12 = M.trade.v3(
        instruments=m9.data,
        options_data=m8.predictions,
        start_date='',
        end_date='',
        handle_data=m12_handle_data_bigquant_run,
        prepare=m12_prepare_bigquant_run,
        initialize=m12_initialize_bigquant_run,
        volume_limit=0.025,
        order_price_field_buy='open',
        order_price_field_sell='close',
        capital_base=1000000,
        benchmark='000300.SHA',
        auto_cancel_non_tradable_orders=True,
        data_frequency='daily',
        price_type='前复权',
        plot_charts=True,
        backtest_only=False,
        amount_integer=False    
    )
    
    print ('Finish ',feature)
    
for feature in features:
    run_single(feature)
[2018-04-17 07:58:23.966277] INFO: bigquant: instruments.v2 开始运行..
[2018-04-17 07:58:23.970805] INFO: bigquant: 命中缓存
[2018-04-17 07:58:23.972581] INFO: bigquant: instruments.v2 运行完成[0.006375s].
[2018-04-17 07:58:23.985353] INFO: bigquant: advanced_auto_labeler.v2 开始运行..
[2018-04-17 07:58:23.989841] INFO: bigquant: 命中缓存
[2018-04-17 07:58:23.991955] INFO: bigquant: advanced_auto_labeler.v2 运行完成[0.006619s].
[2018-04-17 07:58:24.005206] INFO: bigquant: input_features.v1 开始运行..
[2018-04-17 07:58:24.009589] INFO: bigquant: 命中缓存
[2018-04-17 07:58:24.011505] INFO: bigquant: input_features.v1 运行完成[0.006328s].
[2018-04-17 07:58:24.027078] INFO: bigquant: general_feature_extractor.v6 开始运行..
[2018-04-17 07:58:35.103018] INFO: 基础特征抽取: 年份 2015, 特征行数=35294
[2018-04-17 07:58:40.419276] INFO: 基础特征抽取: 年份 2016, 特征行数=641546
[2018-04-17 07:58:52.203621] INFO: 基础特征抽取: 年份 2017, 特征行数=549801
[2018-04-17 07:58:52.230277] INFO: 基础特征抽取: 总行数: 1226641
[2018-04-17 07:58:52.242638] INFO: bigquant: general_feature_extractor.v6 运行完成[28.21557s].
[2018-04-17 07:58:52.254878] INFO: bigquant: derived_feature_extractor.v2 开始运行..
[2018-04-17 07:58:52.830245] INFO: derived_feature_extractor: /y_2015, 35294
[2018-04-17 07:58:53.120844] INFO: derived_feature_extractor: /y_2016, 641546
[2018-04-17 07:58:54.082930] INFO: derived_feature_extractor: /y_2017, 549801
[2018-04-17 07:58:54.460463] INFO: bigquant: derived_feature_extractor.v2 运行完成[2.205564s].
[2018-04-17 07:58:54.472181] INFO: bigquant: join.v3 开始运行..
[2018-04-17 07:58:55.982300] INFO: join: /y_2015, 行数=0/35294, 耗时=0.970349s
[2018-04-17 07:58:58.373009] INFO: join: /y_2016, 行数=637424/641546, 耗时=2.387734s
[2018-04-17 07:59:00.332740] INFO: join: /y_2017, 行数=529183/549801, 耗时=1.887056s
[2018-04-17 07:59:00.452680] INFO: join: 最终行数: 1166607
[2018-04-17 07:59:00.460526] INFO: bigquant: join.v3 运行完成[5.988329s].
[2018-04-17 07:59:00.475800] INFO: bigquant: dropnan.v1 开始运行..
[2018-04-17 07:59:00.565016] INFO: dropnan: /y_2015, 0/0
[2018-04-17 07:59:01.520864] INFO: dropnan: /y_2016, 637424/637424
[2018-04-17 07:59:02.304578] INFO: dropnan: /y_2017, 529183/529183
[2018-04-17 07:59:02.330303] INFO: dropnan: 行数: 1166607/1166607
[2018-04-17 07:59:02.357794] INFO: bigquant: dropnan.v1 运行完成[1.88197s].
[2018-04-17 07:59:02.371844] INFO: bigquant: stock_ranker_train.v5 开始运行..
[2018-04-17 07:59:03.172122] INFO: df2bin: prepare bins ..
[2018-04-17 07:59:03.230909] INFO: df2bin: prepare data: training ..
[2018-04-17 07:59:03.377363] INFO: df2bin: sort ..
[2018-04-17 07:59:18.609729] INFO: stock_ranker_train: 232b0f18 准备训练: 1166607 行数
[2018-04-17 08:01:03.141984] INFO: bigquant: stock_ranker_train.v5 运行完成[120.770116s].
[2018-04-17 08:01:03.151762] INFO: bigquant: instruments.v2 开始运行..
[2018-04-17 08:01:03.159263] INFO: bigquant: 命中缓存
[2018-04-17 08:01:03.160763] INFO: bigquant: instruments.v2 运行完成[0.009018s].
[2018-04-17 08:01:03.176059] INFO: bigquant: general_feature_extractor.v6 开始运行..
[2018-04-17 08:01:12.939049] INFO: 基础特征抽取: 年份 2017, 特征行数=240677
[2018-04-17 08:01:14.921501] INFO: 基础特征抽取: 年份 2018, 特征行数=207158
[2018-04-17 08:01:14.943516] INFO: 基础特征抽取: 总行数: 447835
[2018-04-17 08:01:14.952995] INFO: bigquant: general_feature_extractor.v6 运行完成[11.776981s].
[2018-04-17 08:01:14.965309] INFO: bigquant: derived_feature_extractor.v2 开始运行..
[2018-04-17 08:01:15.222849] INFO: derived_feature_extractor: /y_2017, 240677
[2018-04-17 08:01:15.443670] INFO: derived_feature_extractor: /y_2018, 207158
[2018-04-17 08:01:15.727888] INFO: bigquant: derived_feature_extractor.v2 运行完成[0.762521s].
[2018-04-17 08:01:15.752494] INFO: bigquant: dropnan.v1 开始运行..
[2018-04-17 08:01:16.123798] INFO: dropnan: /y_2017, 240677/240677
[2018-04-17 08:01:16.422742] INFO: dropnan: /y_2018, 207158/207158
[2018-04-17 08:01:16.440133] INFO: dropnan: 行数: 447835/447835
[2018-04-17 08:01:16.453391] INFO: bigquant: dropnan.v1 运行完成[0.700897s].
[2018-04-17 08:01:16.478397] INFO: bigquant: stock_ranker_predict.v5 开始运行..
[2018-04-17 08:01:16.845414] INFO: df2bin: prepare data: prediction ..
[2018-04-17 08:01:22.929460] INFO: stock_ranker_predict: 准备预测: 447835 行
[2018-04-17 08:01:26.190650] INFO: bigquant: stock_ranker_predict.v5 运行完成[9.712263s].
[2018-04-17 08:01:26.327475] INFO: bigquant: backtest.v7 开始运行..
[2018-04-17 08:01:26.553410] INFO: algo: set price type:forward_adjusted
0
0
0
0
0
189694.682124
4770.61885548
116024.138741
0
90878.8079776
0
109137.65039
0
198274.897717
0
股票: 603079.SHA 出现止损状况
-0.149110305356
140214.249502
0
77293.123318
6824.25747703
3808.44305823
11757.7869765
0
105799.568491
83647.8754107
5533.21699576
4606.88920258
120773.469454
183496.083001
0
股票: 603813.SHA 出现止损状况
-0.202150001473
股票: 603055.SHA 出现止损状况
-0.146807669343
股票: 603106.SHA 出现止损状况
-0.189444380379
168073.66589
[2018-04-17 08:02:04.591714] INFO: Blotter: 2017-11-20 cancel order Equity(926 [603378.SHA]) 
股票: 603157.SHA 出现止损状况
-0.130611786544
0
0
0
股票: 603363.SHA 出现止损状况
-0.164355069603
0
股票: 300706.SZA 出现止损状况
-0.149139916031
0
62965.5784207
0
0
150085.090891
0
75289.0981558
股票: 300707.SZA 出现止损状况
-0.148549206163
股票: 300705.SZA 出现止损状况
-0.142071221678
0
0
163317.122045
0
143136.077796
0
166314.716472
0
167231.044996
0
46874.2532235
0
157504.267421
0
120937.29783
0
159603.29865
0
162583.126745
0
179324.855553
0
178193.216255
[2018-04-17 08:02:05.304951] INFO: Blotter: 2018-01-08 cancel order Equity(508 [002864.SZA]) 
0
138538.301927
0
177246.160291
0
175042.714695
0
181675.466846
0
184132.10796
0
144199.410273
0
174960.320044
405.904846335
0
21981.9284811
0
股票: 600576.SHA 出现止损状况
-0.149073884846
股票: 603711.SHA 出现止损状况
-0.131207340865
166001.406667
股票: 002915.SZA 出现止损状况
-0.137948276438
0
股票: 002915.SZA 出现止损状况
-0.2241315673
149896.67993
股票: 603890.SHA 出现止损状况
-0.164684784457
股票: 600573.SHA 出现止损状况
-0.160812665159
股票: 002915.SZA 出现止损状况
-0.301805711823
0
0
0
68021.7302378
0
99036.98958
股票: 603477.SHA 出现止损状况
-0.145048945185
15841.4952578
0
80559.0722475
0
127566.346206
6313.62365857
0
110046.842368
20181.3967218
0
104006.681159
1415.43359836
0
90679.7088797
0
79169.6020291
0
155751.63704
0
36710.6901364
0
40981.7783632
0
151768.571007
0
94539.6271159
3969.56266968
65397.8410148
2493.98552279
19556.313695
50457.519716
5844.46535048
0
[2018-04-17 08:02:06.577243] INFO: Performance: Simulated 124 trading days out of 124.
[2018-04-17 08:02:06.578631] INFO: Performance: first open: 2017-10-09 01:30:00+00:00
[2018-04-17 08:02:06.579977] INFO: Performance: last close: 2018-04-10 07:00:00+00:00
[注意] 有 8 笔卖出是在多天内完成的。当日卖出股票超过了当日股票交易的2.5%会出现这种情况。
  • 收益率-9.67%
  • 年化收益率-18.68%
  • 基准收益率2.36%
  • 阿尔法-0.23
  • 贝塔0.72
  • 夏普比率-0.65
  • 胜率0.437
  • 盈亏比1.09
  • 收益波动率35.59%
  • 信息比率-0.7
  • 最大回撤30.1%
[2018-04-17 08:02:08.050157] INFO: bigquant: backtest.v7 运行完成[41.722677s].
Finish  list_days_0
[2018-04-17 08:02:08.110962] INFO: bigquant: input_features.v1 开始运行..
[2018-04-17 08:02:08.117077] INFO: bigquant: input_features.v1 运行完成[0.006124s].
[2018-04-17 08:02:08.149536] INFO: bigquant: general_feature_extractor.v6 开始运行..
[2018-04-17 08:02:14.488097] INFO: 基础特征抽取: 年份 2015, 特征行数=35294
[2018-04-17 08:02:17.098211] INFO: 基础特征抽取: 年份 2016, 特征行数=641546
[2018-04-17 08:02:23.847228] INFO: 基础特征抽取: 年份 2017, 特征行数=549801
[2018-04-17 08:02:23.874904] INFO: 基础特征抽取: 总行数: 1226641
[2018-04-17 08:02:23.891532] INFO: bigquant: general_feature_extractor.v6 运行完成[15.741971s].
[2018-04-17 08:02:23.908518] INFO: bigquant: derived_feature_extractor.v2 开始运行..
[2018-04-17 08:02:24.509626] INFO: derived_feature_extractor: /y_2015, 35294
[2018-04-17 08:02:25.037970] INFO: derived_feature_extractor: /y_2016, 641546
[2018-04-17 08:02:25.608950] INFO: derived_feature_extractor: /y_2017, 549801
[2018-04-17 08:02:25.935603] INFO: bigquant: derived_feature_extractor.v2 运行完成[2.027065s].
[2018-04-17 08:02:25.949643] INFO: bigquant: join.v3 开始运行..
[2018-04-17 08:02:26.935342] INFO: join: /y_2015, 行数=0/35294, 耗时=0.634496s
[2018-04-17 08:02:28.603682] INFO: join: /y_2016, 行数=637424/641546, 耗时=1.66493s