克隆策略
In [7]:
1
Out[7]:
1

TabNet在量化选股中的应用

TabNet: Attentive Interpretable Tabular Learning

基于Tabnet模型的量化选股方案。抽取了98个量价因子,2010到2018年为数据训练TabNet模型,并将模型的预测结果应用在2018到2021年9月的数据上进行了回测。

TabNet核心参数

  • input_dim: 输入的特征数
  • n_steps: 决策的步数,通常为{3 ~ 10}
  • n_d: 预测阶段的特征数,通常为{8 ~ 64}
  • n_a: Attentive阶段的特征数,通常为{8 ~ 64}
  • gamma: Attentive中注意力更新的比例,通常为{1.0 ~ 2.0}
  • momentum: BN层的动量,通常为{0.0 ~ 1.0}

    {"description":"实验创建于2021/8/27","graph":{"edges":[{"to_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-15:instruments","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-8:data"},{"to_node_id":"-106:instruments","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-8:data"},{"to_node_id":"-276:input_1","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-15:data"},{"to_node_id":"-106:features","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-113:features","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-122:features","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-129:features","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-243:features","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-251:features","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-266:input_2","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-288:features","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-293:features","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-298:input_2","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24:data"},{"to_node_id":"-243:input_data","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-53:data"},{"to_node_id":"-122:instruments","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-62:data"},{"to_node_id":"-141:instruments","from_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-62:data"},{"to_node_id":"-113:input_data","from_node_id":"-106:data"},{"to_node_id":"-266:input_1","from_node_id":"-113:data"},{"to_node_id":"-129:input_data","from_node_id":"-122:data"},{"to_node_id":"-298:input_1","from_node_id":"-129:data"},{"to_node_id":"-2431:input_2","from_node_id":"-129:data"},{"to_node_id":"-436:input_1","from_node_id":"-243:data"},{"to_node_id":"-557:input_data","from_node_id":"-251:data"},{"to_node_id":"-544:training_data","from_node_id":"-436:data_1"},{"to_node_id":"-544:validation_data","from_node_id":"-436:data_2"},{"to_node_id":"-288:input_data","from_node_id":"-266:data"},{"to_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-53:data2","from_node_id":"-288:data"},{"to_node_id":"-251:input_data","from_node_id":"-293:data"},{"to_node_id":"-293:input_data","from_node_id":"-298:data"},{"to_node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-53:data1","from_node_id":"-276:data"},{"to_node_id":"-557:trained_model","from_node_id":"-544:data"},{"to_node_id":"-2431:input_1","from_node_id":"-557:data"},{"to_node_id":"-141:options_data","from_node_id":"-2431:data_1"}],"nodes":[{"node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-8","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2017-01-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2019-12-31","type":"Literal","bound_global_parameter":null},{"name":"market","value":"CN_STOCK_A","type":"Literal","bound_global_parameter":null},{"name":"instrument_list","value":"","type":"Literal","bound_global_parameter":null},{"name":"max_count","value":"0","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"rolling_conf","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-8"}],"output_ports":[{"name":"data","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-8"}],"cacheable":true,"seq_num":1,"comment":"","comment_collapsed":true},{"node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-15","module_id":"BigQuantSpace.advanced_auto_labeler.advanced_auto_labeler-v2","parameters":[{"name":"label_expr","value":"# #号开始的表示注释\n# 0. 每行一个,顺序执行,从第二个开始,可以使用label字段\n# 1. 可用数据字段见 https://bigquant.com/docs/data_history_data.html\n# 添加benchmark_前缀,可使用对应的benchmark数据\n# 2. 可用操作符和函数见 `表达式引擎 <https://bigquant.com/docs/big_expr.html>`_\n\n# 计算收益:5日收盘价(作为卖出价格)除以明日开盘价(作为买入价格)\nshift(close, -5) / shift(open, -1)-1\n\n# 极值处理:用1%和99%分位的值做clip\nclip(label, all_quantile(label, 0.01), all_quantile(label, 0.99))\n\n# 过滤掉一字涨停的情况 (设置label为NaN,在后续处理和训练中会忽略NaN的label)\nwhere(shift(high, -1) == shift(low, -1), NaN, label)\n","type":"Literal","bound_global_parameter":null},{"name":"start_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"benchmark","value":"000300.SHA","type":"Literal","bound_global_parameter":null},{"name":"drop_na_label","value":"True","type":"Literal","bound_global_parameter":null},{"name":"cast_label_int","value":"False","type":"Literal","bound_global_parameter":null},{"name":"user_functions","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-15"}],"output_ports":[{"name":"data","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-15"}],"cacheable":true,"seq_num":2,"comment":"","comment_collapsed":true},{"node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"close_0\nopen_0\nhigh_0\nlow_0 \namount_0\nturn_0 \nreturn_0\n \nclose_1\nopen_1\nhigh_1\nlow_1\nreturn_1\namount_1\nturn_1\n \nclose_2\nopen_2\nhigh_2\nlow_2\namount_2\nturn_2\nreturn_2\n \nclose_3\nopen_3\nhigh_3\nlow_3\namount_3\nturn_3\nreturn_3\n \nclose_4\nopen_4\nhigh_4\nlow_4\namount_4\nturn_4\nreturn_4\n \nmean(close_0, 5)\nmean(low_0, 5)\nmean(open_0, 5)\nmean(high_0, 5)\nmean(turn_0, 5)\nmean(amount_0, 5)\nmean(return_0, 5)\n \nts_max(close_0, 5)\nts_max(low_0, 5)\nts_max(open_0, 5)\nts_max(high_0, 5)\nts_max(turn_0, 5)\nts_max(amount_0, 5)\nts_max(return_0, 5)\n \nts_min(close_0, 5)\nts_min(low_0, 5)\nts_min(open_0, 5)\nts_min(high_0, 5)\nts_min(turn_0, 5)\nts_min(amount_0, 5)\nts_min(return_0, 5) \n \nstd(close_0, 5)\nstd(low_0, 5)\nstd(open_0, 5)\nstd(high_0, 5)\nstd(turn_0, 5)\nstd(amount_0, 5)\nstd(return_0, 5)\n \nts_rank(close_0, 5)\nts_rank(low_0, 5)\nts_rank(open_0, 5)\nts_rank(high_0, 5)\nts_rank(turn_0, 5)\nts_rank(amount_0, 5)\nts_rank(return_0, 5)\n \ndecay_linear(close_0, 5)\ndecay_linear(low_0, 5)\ndecay_linear(open_0, 5)\ndecay_linear(high_0, 5)\ndecay_linear(turn_0, 5)\ndecay_linear(amount_0, 5)\ndecay_linear(return_0, 5)\n \ncorrelation(volume_0, return_0, 5)\ncorrelation(volume_0, high_0, 5)\ncorrelation(volume_0, low_0, 5)\ncorrelation(volume_0, close_0, 5)\ncorrelation(volume_0, open_0, 5)\ncorrelation(volume_0, turn_0, 5)\n \ncorrelation(return_0, high_0, 5)\ncorrelation(return_0, low_0, 5)\ncorrelation(return_0, close_0, 5)\ncorrelation(return_0, open_0, 5)\ncorrelation(return_0, turn_0, 5)\n \ncorrelation(high_0, low_0, 5)\ncorrelation(high_0, close_0, 5)\ncorrelation(high_0, open_0, 5)\ncorrelation(high_0, turn_0, 5)\n \ncorrelation(low_0, close_0, 5)\ncorrelation(low_0, open_0, 5)\ncorrelation(low_0, turn_0, 5)\n \ncorrelation(close_0, open_0, 5)\ncorrelation(close_0, turn_0, 5)\n\ncorrelation(open_0, turn_0, 5)","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24"}],"output_ports":[{"name":"data","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-24"}],"cacheable":true,"seq_num":3,"comment":"","comment_collapsed":true},{"node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-53","module_id":"BigQuantSpace.join.join-v3","parameters":[{"name":"on","value":"date,instrument","type":"Literal","bound_global_parameter":null},{"name":"how","value":"inner","type":"Literal","bound_global_parameter":null},{"name":"sort","value":"False","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"data1","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-53"},{"name":"data2","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-53"}],"output_ports":[{"name":"data","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-53"}],"cacheable":true,"seq_num":4,"comment":"","comment_collapsed":true},{"node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-62","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2020-01-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2020-12-01","type":"Literal","bound_global_parameter":null},{"name":"market","value":"CN_STOCK_A","type":"Literal","bound_global_parameter":null},{"name":"instrument_list","value":"","type":"Literal","bound_global_parameter":null},{"name":"max_count","value":"0","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"rolling_conf","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-62"}],"output_ports":[{"name":"data","node_id":"287d2cb0-f53c-4101-bdf8-104b137c8601-62"}],"cacheable":true,"seq_num":5,"comment":"预测数据,用于回测和模拟","comment_collapsed":true},{"node_id":"-106","module_id":"BigQuantSpace.general_feature_extractor.general_feature_extractor-v7","parameters":[{"name":"start_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"before_start_days","value":"10","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-106"},{"name":"features","node_id":"-106"}],"output_ports":[{"name":"data","node_id":"-106"}],"cacheable":true,"seq_num":6,"comment":"","comment_collapsed":true},{"node_id":"-113","module_id":"BigQuantSpace.derived_feature_extractor.derived_feature_extractor-v3","parameters":[{"name":"date_col","value":"date","type":"Literal","bound_global_parameter":null},{"name":"instrument_col","value":"instrument","type":"Literal","bound_global_parameter":null},{"name":"drop_na","value":"True","type":"Literal","bound_global_parameter":null},{"name":"remove_extra_columns","value":"False","type":"Literal","bound_global_parameter":null},{"name":"user_functions","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-113"},{"name":"features","node_id":"-113"}],"output_ports":[{"name":"data","node_id":"-113"}],"cacheable":true,"seq_num":7,"comment":"","comment_collapsed":true},{"node_id":"-122","module_id":"BigQuantSpace.general_feature_extractor.general_feature_extractor-v7","parameters":[{"name":"start_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"before_start_days","value":"10","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-122"},{"name":"features","node_id":"-122"}],"output_ports":[{"name":"data","node_id":"-122"}],"cacheable":true,"seq_num":8,"comment":"","comment_collapsed":true},{"node_id":"-129","module_id":"BigQuantSpace.derived_feature_extractor.derived_feature_extractor-v3","parameters":[{"name":"date_col","value":"date","type":"Literal","bound_global_parameter":null},{"name":"instrument_col","value":"instrument","type":"Literal","bound_global_parameter":null},{"name":"drop_na","value":"True","type":"Literal","bound_global_parameter":null},{"name":"remove_extra_columns","value":"False","type":"Literal","bound_global_parameter":null},{"name":"user_functions","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-129"},{"name":"features","node_id":"-129"}],"output_ports":[{"name":"data","node_id":"-129"}],"cacheable":true,"seq_num":9,"comment":"","comment_collapsed":true},{"node_id":"-243","module_id":"BigQuantSpace.dl_convert_to_bin.dl_convert_to_bin-v2","parameters":[{"name":"window_size","value":1,"type":"Literal","bound_global_parameter":null},{"name":"feature_clip","value":"3","type":"Literal","bound_global_parameter":null},{"name":"flatten","value":"True","type":"Literal","bound_global_parameter":null},{"name":"window_along_col","value":"instrument","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-243"},{"name":"features","node_id":"-243"}],"output_ports":[{"name":"data","node_id":"-243"}],"cacheable":true,"seq_num":10,"comment":"","comment_collapsed":true},{"node_id":"-251","module_id":"BigQuantSpace.dl_convert_to_bin.dl_convert_to_bin-v2","parameters":[{"name":"window_size","value":1,"type":"Literal","bound_global_parameter":null},{"name":"feature_clip","value":"3","type":"Literal","bound_global_parameter":null},{"name":"flatten","value":"True","type":"Literal","bound_global_parameter":null},{"name":"window_along_col","value":"instrument","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-251"},{"name":"features","node_id":"-251"}],"output_ports":[{"name":"data","node_id":"-251"}],"cacheable":true,"seq_num":11,"comment":"","comment_collapsed":true},{"node_id":"-436","module_id":"BigQuantSpace.cached.cached-v3","parameters":[{"name":"run","value":"# Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端\ndef bigquant_run(input_1, input_2, input_3):\n # 示例代码如下。在这里编写您的代码\n from sklearn.model_selection import train_test_split\n data = input_1.read()\n x_train, x_val, y_train, y_val = train_test_split(data[\"x\"], data['y'], shuffle=False, random_state=2021)\n data_1 = DataSource.write_pickle({'x': x_train, 'y': y_train.reshape(-1, 1)})\n data_2 = DataSource.write_pickle({'x': x_val, 'y': y_val.reshape(-1, 1)})\n return Outputs(data_1=data_1, data_2=data_2, data_3=None)","type":"Literal","bound_global_parameter":null},{"name":"post_run","value":"# 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。\ndef bigquant_run(outputs):\n return outputs\n","type":"Literal","bound_global_parameter":null},{"name":"input_ports","value":"","type":"Literal","bound_global_parameter":null},{"name":"params","value":"{}","type":"Literal","bound_global_parameter":null},{"name":"output_ports","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-436"},{"name":"input_2","node_id":"-436"},{"name":"input_3","node_id":"-436"}],"output_ports":[{"name":"data_1","node_id":"-436"},{"name":"data_2","node_id":"-436"},{"name":"data_3","node_id":"-436"}],"cacheable":true,"seq_num":12,"comment":"","comment_collapsed":true},{"node_id":"-266","module_id":"BigQuantSpace.standardlize.standardlize-v8","parameters":[{"name":"columns_input","value":"[]","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-266"},{"name":"input_2","node_id":"-266"}],"output_ports":[{"name":"data","node_id":"-266"}],"cacheable":true,"seq_num":13,"comment":"","comment_collapsed":true},{"node_id":"-288","module_id":"BigQuantSpace.fillnan.fillnan-v1","parameters":[{"name":"fill_value","value":"0.0","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-288"},{"name":"features","node_id":"-288"}],"output_ports":[{"name":"data","node_id":"-288"}],"cacheable":true,"seq_num":14,"comment":"","comment_collapsed":true},{"node_id":"-293","module_id":"BigQuantSpace.fillnan.fillnan-v1","parameters":[{"name":"fill_value","value":"0.0","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-293"},{"name":"features","node_id":"-293"}],"output_ports":[{"name":"data","node_id":"-293"}],"cacheable":true,"seq_num":15,"comment":"","comment_collapsed":true},{"node_id":"-298","module_id":"BigQuantSpace.standardlize.standardlize-v8","parameters":[{"name":"columns_input","value":"[]","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-298"},{"name":"input_2","node_id":"-298"}],"output_ports":[{"name":"data","node_id":"-298"}],"cacheable":true,"seq_num":16,"comment":"","comment_collapsed":true},{"node_id":"-276","module_id":"BigQuantSpace.standardlize.standardlize-v8","parameters":[{"name":"columns_input","value":"label","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-276"},{"name":"input_2","node_id":"-276"}],"output_ports":[{"name":"data","node_id":"-276"}],"cacheable":true,"seq_num":17,"comment":"","comment_collapsed":true},{"node_id":"-544","module_id":"BigQuantSpace.dl_models_tabnet_train.dl_models_tabnet_train-v1","parameters":[{"name":"input_dim","value":"98","type":"Literal","bound_global_parameter":null},{"name":"n_steps","value":"3","type":"Literal","bound_global_parameter":null},{"name":"n_d","value":"16","type":"Literal","bound_global_parameter":null},{"name":"n_a","value":"16","type":"Literal","bound_global_parameter":null},{"name":"gamma","value":"1.3","type":"Literal","bound_global_parameter":null},{"name":"momentum","value":"0.01","type":"Literal","bound_global_parameter":null},{"name":"batch_size","value":"20480","type":"Literal","bound_global_parameter":null},{"name":"virtual_batch_size","value":"1280","type":"Literal","bound_global_parameter":null},{"name":"epochs","value":"30","type":"Literal","bound_global_parameter":null},{"name":"num_workers","value":"4","type":"Literal","bound_global_parameter":null},{"name":"device_name","value":"auto:自动调用GPU","type":"Literal","bound_global_parameter":null},{"name":"verbose","value":"1:输出进度条记录","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"training_data","node_id":"-544"},{"name":"validation_data","node_id":"-544"}],"output_ports":[{"name":"data","node_id":"-544"}],"cacheable":true,"seq_num":18,"comment":"","comment_collapsed":true},{"node_id":"-557","module_id":"BigQuantSpace.dl_models_tabnet_predict.dl_models_tabnet_predict-v1","parameters":[],"input_ports":[{"name":"trained_model","node_id":"-557"},{"name":"input_data","node_id":"-557"}],"output_ports":[{"name":"data","node_id":"-557"}],"cacheable":false,"seq_num":19,"comment":"","comment_collapsed":true},{"node_id":"-2431","module_id":"BigQuantSpace.cached.cached-v3","parameters":[{"name":"run","value":"# Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端\ndef bigquant_run(input_1, input_2, input_3):\n # 示例代码如下。在这里编写您的代码\n pred_label = input_1.read_pickle()\n df = input_2.read_df()\n df = pd.DataFrame({'pred_label':pred_label[:,0], 'instrument':df.instrument, 'date':df.date})\n df.sort_values(['date','pred_label'],inplace=True, ascending=[True,False])\n return Outputs(data_1=DataSource.write_df(df), data_2=None, data_3=None)\n","type":"Literal","bound_global_parameter":null},{"name":"post_run","value":"# 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。\ndef bigquant_run(outputs):\n return outputs\n","type":"Literal","bound_global_parameter":null},{"name":"input_ports","value":"","type":"Literal","bound_global_parameter":null},{"name":"params","value":"{}","type":"Literal","bound_global_parameter":null},{"name":"output_ports","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-2431"},{"name":"input_2","node_id":"-2431"},{"name":"input_3","node_id":"-2431"}],"output_ports":[{"name":"data_1","node_id":"-2431"},{"name":"data_2","node_id":"-2431"},{"name":"data_3","node_id":"-2431"}],"cacheable":true,"seq_num":20,"comment":"","comment_collapsed":true},{"node_id":"-141","module_id":"BigQuantSpace.trade.trade-v4","parameters":[{"name":"start_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"initialize","value":"# 回测引擎:初始化函数,只执行一次\ndef bigquant_run(context):\n # 加载预测数据\n context.ranker_prediction = context.options['data'].read_df()\n\n # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数\n context.set_commission(PerOrder(buy_cost=0.001, sell_cost=0.001, min_cost=5))\n # 预测数据,通过options传入进来,使用 read_df 函数,加载到内存 (DataFrame)\n # 设置买入的股票数量,这里买入预测股票列表排名靠前的5只\n stock_count = 20\n # 每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[0.339160, 0.213986, 0.169580, ..]\n context.stock_weights = T.norm([1 / math.log(i + 2) for i in range(0, stock_count)])\n # 设置每只股票占用的最大资金比例\n context.max_cash_per_instrument = 0.2\n context.options['hold_days'] = 5\n","type":"Literal","bound_global_parameter":null},{"name":"handle_data","value":"# 回测引擎:每日数据处理函数,每天执行一次\ndef bigquant_run(context, data):\n # 按日期过滤得到今日的预测数据\n ranker_prediction = context.ranker_prediction[\n context.ranker_prediction.date == data.current_dt.strftime('%Y-%m-%d')]\n\n # 1. 资金分配\n # 平均持仓时间是hold_days,每日都将买入股票,每日预期使用 1/hold_days 的资金\n # 实际操作中,会存在一定的买入误差,所以在前hold_days天,等量使用资金;之后,尽量使用剩余资金(这里设置最多用等量的1.5倍)\n is_staging = context.trading_day_index < context.options['hold_days'] # 是否在建仓期间(前 hold_days 天)\n cash_avg = context.portfolio.portfolio_value / context.options['hold_days']\n cash_for_buy = min(context.portfolio.cash, (1 if is_staging else 1.5) * cash_avg)\n cash_for_sell = cash_avg - (context.portfolio.cash - cash_for_buy)\n positions = {e.symbol: p.amount * p.last_sale_price\n for e, p in context.perf_tracker.position_tracker.positions.items()}\n\n # 2. 生成卖出订单:hold_days天之后才开始卖出;对持仓的股票,按机器学习算法预测的排序末位淘汰\n if not is_staging and cash_for_sell > 0:\n equities = {e.symbol: e for e, p in context.perf_tracker.position_tracker.positions.items()}\n instruments = list(reversed(list(ranker_prediction.instrument[ranker_prediction.instrument.apply(\n lambda x: x in equities and not context.has_unfinished_sell_order(equities[x]))])))\n # print('rank order for sell %s' % instruments)\n for instrument in instruments:\n context.order_target(context.symbol(instrument), 0)\n cash_for_sell -= positions[instrument]\n if cash_for_sell <= 0:\n break\n\n # 3. 生成买入订单:按机器学习算法预测的排序,买入前面的stock_count只股票\n buy_cash_weights = context.stock_weights\n buy_instruments = list(ranker_prediction.instrument[:len(buy_cash_weights)])\n max_cash_per_instrument = context.portfolio.portfolio_value * context.max_cash_per_instrument\n for i, instrument in enumerate(buy_instruments):\n cash = cash_for_buy * buy_cash_weights[i]\n if cash > max_cash_per_instrument - positions.get(instrument, 0):\n # 确保股票持仓量不会超过每次股票最大的占用资金量\n cash = max_cash_per_instrument - positions.get(instrument, 0)\n if cash > 0:\n context.order_value(context.symbol(instrument), cash)\n","type":"Literal","bound_global_parameter":null},{"name":"prepare","value":"# 回测引擎:准备数据,只执行一次\ndef bigquant_run(context):\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"before_trading_start","value":"","type":"Literal","bound_global_parameter":null},{"name":"volume_limit","value":0.025,"type":"Literal","bound_global_parameter":null},{"name":"order_price_field_buy","value":"open","type":"Literal","bound_global_parameter":null},{"name":"order_price_field_sell","value":"close","type":"Literal","bound_global_parameter":null},{"name":"capital_base","value":1000000,"type":"Literal","bound_global_parameter":null},{"name":"auto_cancel_non_tradable_orders","value":"True","type":"Literal","bound_global_parameter":null},{"name":"data_frequency","value":"daily","type":"Literal","bound_global_parameter":null},{"name":"price_type","value":"后复权","type":"Literal","bound_global_parameter":null},{"name":"product_type","value":"股票","type":"Literal","bound_global_parameter":null},{"name":"plot_charts","value":"True","type":"Literal","bound_global_parameter":null},{"name":"backtest_only","value":"False","type":"Literal","bound_global_parameter":null},{"name":"benchmark","value":"000300.SHA","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-141"},{"name":"options_data","node_id":"-141"},{"name":"history_ds","node_id":"-141"},{"name":"benchmark_ds","node_id":"-141"},{"name":"trading_calendar","node_id":"-141"}],"output_ports":[{"name":"raw_perf","node_id":"-141"}],"cacheable":false,"seq_num":21,"comment":"","comment_collapsed":true},{"node_id":"-261","module_id":"BigQuantSpace.hyper_rolling_train.hyper_rolling_train-v1","parameters":[{"name":"run","value":"def bigquant_run(\n bq_graph,\n inputs,\n trading_days_market='CN', # 使用那个市场的交易日历, TODO\n train_instruments_mid='m1', # 训练数据 证券代码列表 模块id\n test_instruments_mid='m5', # 测试数据 证券代码列表 模块id\n predict_mid='m20', # 预测 模块id\n trade_mid='m21', # 回测 模块id\n start_date='2014-01-01', # 数据开始日期\n end_date=T.live_run_param('trading_date', '2021-10-08'), # 数据结束日期\n train_update_days=250, # 更新周期,按交易日计算,每多少天更新一次\n train_update_days_for_live=None, #模拟实盘模式下的更新周期,按交易日计算,每多少天更新一次。如果需要在模拟实盘阶段使用不同的模型更新周期,可以设置这个参数\n train_data_min_days=250, # 最小数据天数,按交易日计算,所以第一个滚动的结束日期是 从开始日期到开始日期+最小数据天数\n train_data_max_days=750, # 最大数据天数,按交易日计算,0,表示没有限制,否则每一个滚动的开始日期=max(此滚动的结束日期-最大数据天数, 开始日期\n rolling_count_for_live=1, #实盘模式下滚动次数,模拟实盘模式下,取最后多少次滚动。一般在模拟实盘模式下,只用到最后一次滚动训练的模型,这里可以设置为1;如果你的滚动训练数据时间段很短,以至于期间可能没有训练数据,这里可以设置大一点。0表示没有限制\n):\n def merge_datasources(input_1):\n df_list = [ds[0].read_df().set_index('date').loc[ds[1]:].reset_index() for ds in input_1]\n df = pd.concat(df_list)\n instrument_data = {\n 'start_date': df['date'].min().strftime('%Y-%m-%d'),\n 'end_date': df['date'].max().strftime('%Y-%m-%d'),\n 'instruments': list(set(df['instrument'])),\n }\n return Outputs(data=DataSource.write_df(df), instrument_data=DataSource.write_pickle(instrument_data))\n\n def gen_rolling_dates(trading_days_market, start_date, end_date, train_update_days, train_update_days_for_live, train_data_min_days, train_data_max_days, rolling_count_for_live):\n # 是否实盘模式\n tdays = list(D.trading_days(market=trading_days_market, start_date=start_date, end_date=end_date)['date'])\n is_live_run = T.live_run_param('trading_date', None) is not None\n\n if is_live_run and train_update_days_for_live:\n train_update_days = train_update_days_for_live\n\n rollings = []\n train_end_date = train_data_min_days\n while train_end_date < len(tdays):\n if train_data_max_days is not None and train_data_max_days > 0:\n train_start_date = max(train_end_date - train_data_max_days, 0)\n else:\n train_start_date = 0\n rollings.append({\n 'train_start_date': tdays[train_start_date].strftime('%Y-%m-%d'),\n 'train_end_date': tdays[train_end_date - 1].strftime('%Y-%m-%d'),\n 'test_start_date': tdays[train_end_date].strftime('%Y-%m-%d'),\n 'test_end_date': tdays[min(train_end_date + train_update_days, len(tdays)) - 1].strftime('%Y-%m-%d'),\n })\n train_end_date += train_update_days\n \n \n if not rollings:\n raise Exception('没有滚动需要执行,请检查配置')\n\n if is_live_run and rolling_count_for_live:\n rollings = rollings[-rolling_count_for_live:]\n\n return rollings\n\n g = bq_graph\n\n rolling_dates = gen_rolling_dates(\n trading_days_market, start_date, end_date, train_update_days, train_update_days_for_live, train_data_min_days, train_data_max_days, rolling_count_for_live)\n \n print('=========:', len(rolling_dates), rolling_dates)\n \n # 训练和预测\n results = []\n for rolling in rolling_dates:\n parameters = {}\n # 先禁用回测\n parameters[trade_mid + '.__enabled__'] = False\n parameters[train_instruments_mid + '.start_date'] = rolling['train_start_date']\n parameters[train_instruments_mid + '.end_date'] = rolling['train_end_date']\n parameters[test_instruments_mid + '.start_date'] = rolling['test_start_date']\n parameters[test_instruments_mid + '.end_date'] = rolling['test_end_date']\n # print('------ rolling_train:', parameters)\n results.append(g.run(parameters))\n \n print('++++++++:', len( results), results)\n\n\n # 合并预测结果并回测\n mx = M.cached.v3(run=merge_datasources, input_1=[[result[predict_mid].data_1, result[test_instruments_mid].data.read_pickle()['start_date']] for result in results])\n \n parameters = {}\n parameters['*.__enabled__'] = False\n parameters[trade_mid + '.__enabled__'] = True\n parameters[trade_mid + '.instruments'] = mx.instrument_data\n parameters[trade_mid + '.options_data'] = mx.data\n\n trade = g.run(parameters)\n\n return {'rollings': results, 'trade': trade}\n","type":"Literal","bound_global_parameter":null},{"name":"run_now","value":"True","type":"Literal","bound_global_parameter":null},{"name":"bq_graph","value":"True","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"bq_graph_port","node_id":"-261"},{"name":"input_1","node_id":"-261"},{"name":"input_2","node_id":"-261"},{"name":"input_3","node_id":"-261"}],"output_ports":[{"name":"result","node_id":"-261"}],"cacheable":false,"seq_num":23,"comment":"","comment_collapsed":true}],"node_layout":"<node_postions><node_position Node='287d2cb0-f53c-4101-bdf8-104b137c8601-8' Position='274,-1,200,200'/><node_position Node='287d2cb0-f53c-4101-bdf8-104b137c8601-15' Position='21,167,200,200'/><node_position Node='287d2cb0-f53c-4101-bdf8-104b137c8601-24' Position='716,-109,200,200'/><node_position Node='287d2cb0-f53c-4101-bdf8-104b137c8601-53' Position='275,444,200,200'/><node_position Node='287d2cb0-f53c-4101-bdf8-104b137c8601-62' Position='1125,0,200,200'/><node_position Node='-106' Position='436,108,200,200'/><node_position Node='-113' Position='430,172,200,200'/><node_position Node='-122' Position='1135,144,200,200'/><node_position Node='-129' Position='1135,237,200,200'/><node_position Node='-243' Position='283,532,200,200'/><node_position Node='-251' Position='1121,557,200,200'/><node_position Node='-436' Position='290,622.5196533203125,200,200'/><node_position Node='-266' Position='436,251,200,200'/><node_position Node='-288' Position='433,319,200,200'/><node_position Node='-293' Position='1127,452,200,200'/><node_position Node='-298' Position='1130,337,200,200'/><node_position Node='-276' Position='19,243,200,200'/><node_position Node='-544' Position='291,701,200,200'/><node_position Node='-557' Position='628,752,200,200'/><node_position Node='-2431' Position='567,848,200,200'/><node_position Node='-141' Position='471,939,200,200'/><node_position Node='-261' Position='-233.19655990600586,-113.9471664428711,200,200'/></node_postions>"},"nodes_readonly":false,"studio_version":"v2"}
    In [8]:
    # 本代码由可视化策略环境自动生成 2021年10月11日 13:40
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    def m12_run_bigquant_run(input_1, input_2, input_3):
        # 示例代码如下。在这里编写您的代码
        from sklearn.model_selection import train_test_split
        data = input_1.read()
        x_train, x_val, y_train, y_val = train_test_split(data["x"], data['y'], shuffle=False, random_state=2021)
        data_1 = DataSource.write_pickle({'x': x_train, 'y': y_train.reshape(-1, 1)})
        data_2 = DataSource.write_pickle({'x': x_val, 'y': y_val.reshape(-1, 1)})
        return Outputs(data_1=data_1, data_2=data_2, data_3=None)
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m12_post_run_bigquant_run(outputs):
        return outputs
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    def m20_run_bigquant_run(input_1, input_2, input_3):
        # 示例代码如下。在这里编写您的代码
        pred_label = input_1.read_pickle()
        df = input_2.read_df()
        df = pd.DataFrame({'pred_label':pred_label[:,0], 'instrument':df.instrument, 'date':df.date})
        df.sort_values(['date','pred_label'],inplace=True, ascending=[True,False])
        return Outputs(data_1=DataSource.write_df(df), data_2=None, data_3=None)
    
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m20_post_run_bigquant_run(outputs):
        return outputs
    
    # 回测引擎:初始化函数,只执行一次
    def m21_initialize_bigquant_run(context):
        # 加载预测数据
        context.ranker_prediction = context.options['data'].read_df()
    
        # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
        context.set_commission(PerOrder(buy_cost=0.001, sell_cost=0.001, min_cost=5))
        # 预测数据,通过options传入进来,使用 read_df 函数,加载到内存 (DataFrame)
        # 设置买入的股票数量,这里买入预测股票列表排名靠前的5只
        stock_count = 20
        # 每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[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
    
    # 回测引擎:每日数据处理函数,每天执行一次
    def m21_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 m21_prepare_bigquant_run(context):
        pass
    
    
    g = T.Graph({
    
        'm1': 'M.instruments.v2',
        'm1.start_date': '2017-01-01',
        'm1.end_date': '2019-12-31',
        'm1.market': 'CN_STOCK_A',
        'm1.instrument_list': '',
        'm1.max_count': 0,
    
        'm2': 'M.advanced_auto_labeler.v2',
        'm2.instruments': T.Graph.OutputPort('m1.data'),
        'm2.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
    
    # 极值处理:用1%和99%分位的值做clip
    clip(label, all_quantile(label, 0.01), all_quantile(label, 0.99))
    
    # 过滤掉一字涨停的情况 (设置label为NaN,在后续处理和训练中会忽略NaN的label)
    where(shift(high, -1) == shift(low, -1), NaN, label)
    """,
        'm2.start_date': '',
        'm2.end_date': '',
        'm2.benchmark': '000300.SHA',
        'm2.drop_na_label': True,
        'm2.cast_label_int': False,
    
        'm17': 'M.standardlize.v8',
        'm17.input_1': T.Graph.OutputPort('m2.data'),
        'm17.columns_input': 'label',
    
        'm3': 'M.input_features.v1',
        'm3.features': """close_0
    open_0
    high_0
    low_0 
    amount_0
    turn_0 
    return_0
     
    close_1
    open_1
    high_1
    low_1
    return_1
    amount_1
    turn_1
     
    close_2
    open_2
    high_2
    low_2
    amount_2
    turn_2
    return_2
     
    close_3
    open_3
    high_3
    low_3
    amount_3
    turn_3
    return_3
     
    close_4
    open_4
    high_4
    low_4
    amount_4
    turn_4
    return_4
     
    mean(close_0, 5)
    mean(low_0, 5)
    mean(open_0, 5)
    mean(high_0, 5)
    mean(turn_0, 5)
    mean(amount_0, 5)
    mean(return_0, 5)
     
    ts_max(close_0, 5)
    ts_max(low_0, 5)
    ts_max(open_0, 5)
    ts_max(high_0, 5)
    ts_max(turn_0, 5)
    ts_max(amount_0, 5)
    ts_max(return_0, 5)
     
    ts_min(close_0, 5)
    ts_min(low_0, 5)
    ts_min(open_0, 5)
    ts_min(high_0, 5)
    ts_min(turn_0, 5)
    ts_min(amount_0, 5)
    ts_min(return_0, 5) 
     
    std(close_0, 5)
    std(low_0, 5)
    std(open_0, 5)
    std(high_0, 5)
    std(turn_0, 5)
    std(amount_0, 5)
    std(return_0, 5)
     
    ts_rank(close_0, 5)
    ts_rank(low_0, 5)
    ts_rank(open_0, 5)
    ts_rank(high_0, 5)
    ts_rank(turn_0, 5)
    ts_rank(amount_0, 5)
    ts_rank(return_0, 5)
     
    decay_linear(close_0, 5)
    decay_linear(low_0, 5)
    decay_linear(open_0, 5)
    decay_linear(high_0, 5)
    decay_linear(turn_0, 5)
    decay_linear(amount_0, 5)
    decay_linear(return_0, 5)
     
    correlation(volume_0, return_0, 5)
    correlation(volume_0, high_0, 5)
    correlation(volume_0, low_0, 5)
    correlation(volume_0, close_0, 5)
    correlation(volume_0, open_0, 5)
    correlation(volume_0, turn_0, 5)
      
    correlation(return_0, high_0, 5)
    correlation(return_0, low_0, 5)
    correlation(return_0, close_0, 5)
    correlation(return_0, open_0, 5)
    correlation(return_0, turn_0, 5)
     
    correlation(high_0, low_0, 5)
    correlation(high_0, close_0, 5)
    correlation(high_0, open_0, 5)
    correlation(high_0, turn_0, 5)
     
    correlation(low_0, close_0, 5)
    correlation(low_0, open_0, 5)
    correlation(low_0, turn_0, 5)
     
    correlation(close_0, open_0, 5)
    correlation(close_0, turn_0, 5)
    
    correlation(open_0, turn_0, 5)""",
    
        'm6': 'M.general_feature_extractor.v7',
        'm6.instruments': T.Graph.OutputPort('m1.data'),
        'm6.features': T.Graph.OutputPort('m3.data'),
        'm6.start_date': '',
        'm6.end_date': '',
        'm6.before_start_days': 10,
    
        'm7': 'M.derived_feature_extractor.v3',
        'm7.input_data': T.Graph.OutputPort('m6.data'),
        'm7.features': T.Graph.OutputPort('m3.data'),
        'm7.date_col': 'date',
        'm7.instrument_col': 'instrument',
        'm7.drop_na': True,
        'm7.remove_extra_columns': False,
    
        'm13': 'M.standardlize.v8',
        'm13.input_1': T.Graph.OutputPort('m7.data'),
        'm13.input_2': T.Graph.OutputPort('m3.data'),
        'm13.columns_input': '[]',
    
        'm14': 'M.fillnan.v1',
        'm14.input_data': T.Graph.OutputPort('m13.data'),
        'm14.features': T.Graph.OutputPort('m3.data'),
        'm14.fill_value': '0.0',
    
        'm4': 'M.join.v3',
        'm4.data1': T.Graph.OutputPort('m17.data'),
        'm4.data2': T.Graph.OutputPort('m14.data'),
        'm4.on': 'date,instrument',
        'm4.how': 'inner',
        'm4.sort': False,
    
        'm10': 'M.dl_convert_to_bin.v2',
        'm10.input_data': T.Graph.OutputPort('m4.data'),
        'm10.features': T.Graph.OutputPort('m3.data'),
        'm10.window_size': 1,
        'm10.feature_clip': 3,
        'm10.flatten': True,
        'm10.window_along_col': 'instrument',
    
        'm12': 'M.cached.v3',
        'm12.input_1': T.Graph.OutputPort('m10.data'),
        'm12.run': m12_run_bigquant_run,
        'm12.post_run': m12_post_run_bigquant_run,
        'm12.input_ports': '',
        'm12.params': '{}',
        'm12.output_ports': '',
    
        'm18': 'M.dl_models_tabnet_train.v1',
        'm18.training_data': T.Graph.OutputPort('m12.data_1'),
        'm18.validation_data': T.Graph.OutputPort('m12.data_2'),
        'm18.input_dim': 98,
        'm18.n_steps': 3,
        'm18.n_d': 16,
        'm18.n_a': 16,
        'm18.gamma': 1.3,
        'm18.momentum': 0.01,
        'm18.batch_size': 20480,
        'm18.virtual_batch_size': 1280,
        'm18.epochs': 30,
        'm18.num_workers': 4,
        'm18.device_name': 'auto:自动调用GPU',
        'm18.verbose': '1:输出进度条记录',
    
        'm5': 'M.instruments.v2',
        'm5.start_date': '2020-01-01',
        'm5.end_date': '2020-12-01',
        'm5.market': 'CN_STOCK_A',
        'm5.instrument_list': '',
        'm5.max_count': 0,
    
        'm8': 'M.general_feature_extractor.v7',
        'm8.instruments': T.Graph.OutputPort('m5.data'),
        'm8.features': T.Graph.OutputPort('m3.data'),
        'm8.start_date': '',
        'm8.end_date': '',
        'm8.before_start_days': 10,
    
        'm9': 'M.derived_feature_extractor.v3',
        'm9.input_data': T.Graph.OutputPort('m8.data'),
        'm9.features': T.Graph.OutputPort('m3.data'),
        'm9.date_col': 'date',
        'm9.instrument_col': 'instrument',
        'm9.drop_na': True,
        'm9.remove_extra_columns': False,
    
        'm16': 'M.standardlize.v8',
        'm16.input_1': T.Graph.OutputPort('m9.data'),
        'm16.input_2': T.Graph.OutputPort('m3.data'),
        'm16.columns_input': '[]',
    
        'm15': 'M.fillnan.v1',
        'm15.input_data': T.Graph.OutputPort('m16.data'),
        'm15.features': T.Graph.OutputPort('m3.data'),
        'm15.fill_value': '0.0',
    
        'm11': 'M.dl_convert_to_bin.v2',
        'm11.input_data': T.Graph.OutputPort('m15.data'),
        'm11.features': T.Graph.OutputPort('m3.data'),
        'm11.window_size': 1,
        'm11.feature_clip': 3,
        'm11.flatten': True,
        'm11.window_along_col': 'instrument',
    
        'm19': 'M.dl_models_tabnet_predict.v1',
        'm19.trained_model': T.Graph.OutputPort('m18.data'),
        'm19.input_data': T.Graph.OutputPort('m11.data'),
        'm19.m_cached': False,
    
        'm20': 'M.cached.v3',
        'm20.input_1': T.Graph.OutputPort('m19.data'),
        'm20.input_2': T.Graph.OutputPort('m9.data'),
        'm20.run': m20_run_bigquant_run,
        'm20.post_run': m20_post_run_bigquant_run,
        'm20.input_ports': '',
        'm20.params': '{}',
        'm20.output_ports': '',
    
        'm21': 'M.trade.v4',
        'm21.instruments': T.Graph.OutputPort('m5.data'),
        'm21.options_data': T.Graph.OutputPort('m20.data_1'),
        'm21.start_date': '',
        'm21.end_date': '',
        'm21.initialize': m21_initialize_bigquant_run,
        'm21.handle_data': m21_handle_data_bigquant_run,
        'm21.prepare': m21_prepare_bigquant_run,
        'm21.volume_limit': 0.025,
        'm21.order_price_field_buy': 'open',
        'm21.order_price_field_sell': 'close',
        'm21.capital_base': 1000000,
        'm21.auto_cancel_non_tradable_orders': True,
        'm21.data_frequency': 'daily',
        'm21.price_type': '后复权',
        'm21.product_type': '股票',
        'm21.plot_charts': True,
        'm21.backtest_only': False,
        'm21.benchmark': '000300.SHA',
    })
    
    # g.run({})
    
    
    def m23_run_bigquant_run(
        bq_graph,
        inputs,
        trading_days_market='CN', # 使用那个市场的交易日历, TODO
        train_instruments_mid='m1', # 训练数据 证券代码列表 模块id
        test_instruments_mid='m5', # 测试数据 证券代码列表 模块id
        predict_mid='m20', # 预测 模块id
        trade_mid='m21', # 回测 模块id
        start_date='2014-01-01', # 数据开始日期
        end_date=T.live_run_param('trading_date', '2021-10-08'), # 数据结束日期
        train_update_days=250, # 更新周期,按交易日计算,每多少天更新一次
        train_update_days_for_live=None, #模拟实盘模式下的更新周期,按交易日计算,每多少天更新一次。如果需要在模拟实盘阶段使用不同的模型更新周期,可以设置这个参数
        train_data_min_days=250, # 最小数据天数,按交易日计算,所以第一个滚动的结束日期是 从开始日期到开始日期+最小数据天数
        train_data_max_days=750, # 最大数据天数,按交易日计算,0,表示没有限制,否则每一个滚动的开始日期=max(此滚动的结束日期-最大数据天数, 开始日期
        rolling_count_for_live=1, #实盘模式下滚动次数,模拟实盘模式下,取最后多少次滚动。一般在模拟实盘模式下,只用到最后一次滚动训练的模型,这里可以设置为1;如果你的滚动训练数据时间段很短,以至于期间可能没有训练数据,这里可以设置大一点。0表示没有限制
    ):
        def merge_datasources(input_1):
            df_list = [ds[0].read_df().set_index('date').loc[ds[1]:].reset_index() for ds in input_1]
            df = pd.concat(df_list)
            instrument_data = {
                'start_date': df['date'].min().strftime('%Y-%m-%d'),
                'end_date': df['date'].max().strftime('%Y-%m-%d'),
                'instruments': list(set(df['instrument'])),
            }
            return Outputs(data=DataSource.write_df(df), instrument_data=DataSource.write_pickle(instrument_data))
    
        def gen_rolling_dates(trading_days_market, start_date, end_date, train_update_days, train_update_days_for_live, train_data_min_days, train_data_max_days, rolling_count_for_live):
            # 是否实盘模式
            tdays = list(D.trading_days(market=trading_days_market, start_date=start_date, end_date=end_date)['date'])
            is_live_run = T.live_run_param('trading_date', None) is not None
    
            if is_live_run and train_update_days_for_live:
                train_update_days = train_update_days_for_live
    
            rollings = []
            train_end_date = train_data_min_days
            while train_end_date < len(tdays):
                if train_data_max_days is not None and train_data_max_days > 0:
                    train_start_date = max(train_end_date - train_data_max_days, 0)
                else:
                    train_start_date = 0
                rollings.append({
                    'train_start_date': tdays[train_start_date].strftime('%Y-%m-%d'),
                    'train_end_date': tdays[train_end_date - 1].strftime('%Y-%m-%d'),
                    'test_start_date': tdays[train_end_date].strftime('%Y-%m-%d'),
                    'test_end_date': tdays[min(train_end_date + train_update_days, len(tdays)) - 1].strftime('%Y-%m-%d'),
                })
                train_end_date += train_update_days
           
            
            if not rollings:
                raise Exception('没有滚动需要执行,请检查配置')
    
            if is_live_run and rolling_count_for_live:
                rollings = rollings[-rolling_count_for_live:]
    
            return rollings
    
        g = bq_graph
    
        rolling_dates = gen_rolling_dates(
            trading_days_market, start_date, end_date, train_update_days, train_update_days_for_live, train_data_min_days, train_data_max_days, rolling_count_for_live)
        
        print('=========:', len(rolling_dates), rolling_dates)
        
        # 训练和预测
        results = []
        for rolling in rolling_dates:
            parameters = {}
            # 先禁用回测
            parameters[trade_mid + '.__enabled__'] = False
            parameters[train_instruments_mid + '.start_date'] = rolling['train_start_date']
            parameters[train_instruments_mid + '.end_date'] = rolling['train_end_date']
            parameters[test_instruments_mid + '.start_date'] = rolling['test_start_date']
            parameters[test_instruments_mid + '.end_date'] = rolling['test_end_date']
            # print('------ rolling_train:', parameters)
            results.append(g.run(parameters))
            
        print('++++++++:', len( results),  results)
    
    
        # 合并预测结果并回测
        mx = M.cached.v3(run=merge_datasources, input_1=[[result[predict_mid].data_1, result[test_instruments_mid].data.read_pickle()['start_date']] for result in results])
        
        parameters = {}
        parameters['*.__enabled__'] = False
        parameters[trade_mid + '.__enabled__'] = True
        parameters[trade_mid + '.instruments'] = mx.instrument_data
        parameters[trade_mid + '.options_data'] = mx.data
    
        trade = g.run(parameters)
    
        return {'rollings': results, 'trade': trade}
    
    
    m23 = M.hyper_rolling_train.v1(
        run=m23_run_bigquant_run,
        run_now=True,
        bq_graph=g
    )
    
    =========: 7 [{'train_start_date': '2014-01-02', 'train_end_date': '2015-01-09', 'test_start_date': '2015-01-12', 'test_end_date': '2016-01-18'}, {'train_start_date': '2014-01-02', 'train_end_date': '2016-01-18', 'test_start_date': '2016-01-19', 'test_end_date': '2017-01-25'}, {'train_start_date': '2014-01-02', 'train_end_date': '2017-01-25', 'test_start_date': '2017-01-26', 'test_end_date': '2018-02-01'}, {'train_start_date': '2015-01-12', 'train_end_date': '2018-02-01', 'test_start_date': '2018-02-02', 'test_end_date': '2019-02-19'}, {'train_start_date': '2016-01-19', 'train_end_date': '2019-02-19', 'test_start_date': '2019-02-20', 'test_end_date': '2020-02-28'}, {'train_start_date': '2017-01-26', 'train_end_date': '2020-02-28', 'test_start_date': '2020-03-02', 'test_end_date': '2021-03-10'}, {'train_start_date': '2018-02-02', 'train_end_date': '2021-03-10', 'test_start_date': '2021-03-11', 'test_end_date': '2021-10-08'}]
    
    Device used : cuda
    
    epoch 0  | loss: 1.17906 | val_0_mse: 0.99688 |  0:00:17s
    epoch 1  | loss: 1.00874 | val_0_mse: 0.99498 |  0:00:33s
    epoch 2  | loss: 0.99906 | val_0_mse: 0.9946  |  0:00:51s
    epoch 3  | loss: 0.99718 | val_0_mse: 0.99401 |  0:01:10s
    epoch 4  | loss: 0.99666 | val_0_mse: 0.99366 |  0:01:25s
    epoch 5  | loss: 0.99597 | val_0_mse: 0.99377 |  0:01:42s
    epoch 6  | loss: 0.99577 | val_0_mse: 0.99272 |  0:02:01s
    epoch 7  | loss: 0.99535 | val_0_mse: 0.99275 |  0:02:19s
    epoch 8  | loss: 0.99486 | val_0_mse: 0.99194 |  0:02:33s
    epoch 9  | loss: 0.99444 | val_0_mse: 0.9917  |  0:02:51s
    epoch 10 | loss: 0.99448 | val_0_mse: 0.99168 |  0:03:08s
    epoch 11 | loss: 0.99425 | val_0_mse: 0.99233 |  0:03:25s
    epoch 12 | loss: 0.9938  | val_0_mse: 0.99154 |  0:03:40s
    epoch 13 | loss: 0.99384 | val_0_mse: 0.99177 |  0:03:58s
    epoch 14 | loss: 0.99405 | val_0_mse: 0.99114 |  0:04:15s
    epoch 15 | loss: 0.99376 | val_0_mse: 0.99148 |  0:04:31s
    epoch 16 | loss: 0.99353 | val_0_mse: 0.99104 |  0:04:45s
    epoch 17 | loss: 0.99333 | val_0_mse: 0.99094 |  0:04:59s
    epoch 18 | loss: 0.99323 | val_0_mse: 0.9907  |  0:05:12s
    epoch 19 | loss: 0.99288 | val_0_mse: 0.99079 |  0:05:27s
    epoch 20 | loss: 0.99238 | val_0_mse: 0.99019 |  0:05:40s
    epoch 21 | loss: 0.99249 | val_0_mse: 0.99033 |  0:05:54s
    epoch 22 | loss: 0.99211 | val_0_mse: 0.99015 |  0:06:08s
    epoch 23 | loss: 0.99232 | val_0_mse: 0.99088 |  0:06:23s
    epoch 24 | loss: 0.99224 | val_0_mse: 0.98994 |  0:06:41s
    epoch 25 | loss: 0.99227 | val_0_mse: 0.99055 |  0:06:58s
    epoch 26 | loss: 0.99263 | val_0_mse: 0.99047 |  0:07:11s
    epoch 27 | loss: 0.99223 | val_0_mse: 0.99031 |  0:07:27s
    epoch 28 | loss: 0.99193 | val_0_mse: 0.98976 |  0:07:44s
    epoch 29 | loss: 0.99164 | val_0_mse: 0.98961 |  0:07:58s
    Stop training because you reached max_epochs = 30 with best_epoch = 29 and best_val_0_mse = 0.98961
    Best weights from best epoch are automatically used!
    
    Device used : cuda
    
    Device used : cuda
    
    epoch 0  | loss: 1.0803  | val_0_mse: 0.98986 |  0:00:26s
    epoch 1  | loss: 0.99706 | val_0_mse: 0.98807 |  0:00:55s
    epoch 2  | loss: 0.9948  | val_0_mse: 0.98618 |  0:01:22s
    epoch 3  | loss: 0.99374 | val_0_mse: 0.98583 |  0:01:54s
    epoch 4  | loss: 0.99328 | val_0_mse: 0.98562 |  0:02:22s
    epoch 5  | loss: 0.99273 | val_0_mse: 0.9848  |  0:02:55s
    epoch 6  | loss: 0.99237 | val_0_mse: 0.98496 |  0:03:22s
    epoch 7  | loss: 0.99212 | val_0_mse: 0.9846  |  0:03:54s
    epoch 8  | loss: 0.99153 | val_0_mse: 0.98402 |  0:04:22s
    epoch 9  | loss: 0.99097 | val_0_mse: 0.98377 |  0:04:53s
    epoch 10 | loss: 0.99068 | val_0_mse: 0.98378 |  0:05:19s
    epoch 11 | loss: 0.99063 | val_0_mse: 0.98357 |  0:05:50s
    epoch 12 | loss: 0.99015 | val_0_mse: 0.98343 |  0:06:17s
    epoch 13 | loss: 0.98995 | val_0_mse: 0.98285 |  0:06:47s
    epoch 14 | loss: 0.98937 | val_0_mse: 0.98239 |  0:07:17s
    epoch 15 | loss: 0.98929 | val_0_mse: 0.98301 |  0:07:49s
    epoch 16 | loss: 0.98901 | val_0_mse: 0.98217 |  0:08:20s
    epoch 17 | loss: 0.98893 | val_0_mse: 0.98186 |  0:08:50s
    epoch 18 | loss: 0.98869 | val_0_mse: 0.9818  |  0:09:24s
    epoch 19 | loss: 0.988   | val_0_mse: 0.98131 |  0:09:54s
    epoch 20 | loss: 0.98788 | val_0_mse: 0.98164 |  0:10:28s
    epoch 21 | loss: 0.98771 | val_0_mse: 0.98265 |  0:10:56s
    epoch 22 | loss: 0.98764 | val_0_mse: 0.98131 |  0:11:28s
    epoch 23 | loss: 0.9873  | val_0_mse: 0.98834 |  0:11:59s
    epoch 24 | loss: 0.98718 | val_0_mse: 0.98039 |  0:12:30s
    epoch 25 | loss: 0.98649 | val_0_mse: 0.98046 |  0:13:02s
    epoch 26 | loss: 0.98668 | val_0_mse: 0.97998 |  0:13:29s
    epoch 27 | loss: 0.98667 | val_0_mse: 0.97994 |  0:13:59s
    epoch 28 | loss: 0.98658 | val_0_mse: 0.98017 |  0:14:30s
    epoch 29 | loss: 0.98621 | val_0_mse: 0.97969 |  0:14:55s
    Stop training because you reached max_epochs = 30 with best_epoch = 29 and best_val_0_mse = 0.97969
    Best weights from best epoch are automatically used!
    
    Device used : cuda
    
    Device used : cuda
    
    epoch 0  | loss: 1.05855 | val_0_mse: 0.99177 |  0:00:37s
    epoch 1  | loss: 0.99538 | val_0_mse: 0.98973 |  0:01:22s
    epoch 2  | loss: 0.99429 | val_0_mse: 0.98913 |  0:02:08s
    epoch 3  | loss: 0.99356 | val_0_mse: 0.98898 |  0:02:53s
    epoch 4  | loss: 0.99314 | val_0_mse: 0.98853 |  0:03:41s
    epoch 5  | loss: 0.99241 | val_0_mse: 0.98773 |  0:04:26s
    epoch 6  | loss: 0.99157 | val_0_mse: 0.98643 |  0:05:11s
    epoch 7  | loss: 0.99089 | val_0_mse: 0.98658 |  0:05:58s
    epoch 8  | loss: 0.99066 | val_0_mse: 0.98682 |  0:06:42s
    epoch 9  | loss: 0.99043 | val_0_mse: 0.98616 |  0:07:27s
    epoch 10 | loss: 0.99002 | val_0_mse: 0.9854  |  0:08:21s
    epoch 11 | loss: 0.98968 | val_0_mse: 0.98501 |  0:09:11s
    epoch 12 | loss: 0.98925 | val_0_mse: 0.98518 |  0:09:55s
    epoch 13 | loss: 0.98874 | val_0_mse: 0.98441 |  0:10:44s
    epoch 14 | loss: 0.9887  | val_0_mse: 0.98474 |  0:11:28s
    epoch 15 | loss: 0.98891 | val_0_mse: 0.98424 |  0:12:12s
    epoch 16 | loss: 0.98831 | val_0_mse: 0.98413 |  0:13:02s
    epoch 17 | loss: 0.98819 | val_0_mse: 0.98393 |  0:13:48s
    epoch 18 | loss: 0.98782 | val_0_mse: 0.98383 |  0:14:36s
    epoch 19 | loss: 0.98752 | val_0_mse: 0.98372 |  0:15:23s
    epoch 20 | loss: 0.98728 | val_0_mse: 0.98282 |  0:16:10s
    epoch 21 | loss: 0.98705 | val_0_mse: 0.98278 |  0:17:00s
    epoch 22 | loss: 0.98693 | val_0_mse: 0.98339 |  0:17:45s
    epoch 23 | loss: 0.98705 | val_0_mse: 0.98273 |  0:18:33s
    epoch 24 | loss: 0.98688 | val_0_mse: 0.98311 |  0:19:17s
    epoch 25 | loss: 0.98655 | val_0_mse: 0.98431 |  0:20:06s
    epoch 26 | loss: 0.98586 | val_0_mse: 0.98203 |  0:20:47s
    epoch 27 | loss: 0.9857  | val_0_mse: 0.98155 |  0:21:25s
    epoch 28 | loss: 0.98523 | val_0_mse: 0.98121 |  0:22:03s
    epoch 29 | loss: 0.98505 | val_0_mse: 0.9818  |  0:22:44s
    Stop training because you reached max_epochs = 30 with best_epoch = 28 and best_val_0_mse = 0.98121
    Best weights from best epoch are automatically used!
    
    Device used : cuda
    
    Device used : cuda
    
    epoch 0  | loss: 1.04969 | val_0_mse: 0.99811 |  0:00:50s
    epoch 1  | loss: 0.99651 | val_0_mse: 0.99577 |  0:01:42s
    epoch 2  | loss: 0.99527 | val_0_mse: 0.99502 |  0:02:29s
    epoch 3  | loss: 0.99396 | val_0_mse: 0.99405 |  0:03:19s
    epoch 4  | loss: 0.99356 | val_0_mse: 0.99387 |  0:04:11s
    epoch 5  | loss: 0.99313 | val_0_mse: 0.99318 |  0:05:00s
    epoch 6  | loss: 0.99259 | val_0_mse: 0.99291 |  0:05:48s
    epoch 7  | loss: 0.99224 | val_0_mse: 0.99246 |  0:06:39s
    epoch 8  | loss: 0.99204 | val_0_mse: 0.99345 |  0:07:25s
    epoch 9  | loss: 0.99227 | val_0_mse: 0.99258 |  0:08:13s
    epoch 10 | loss: 0.99175 | val_0_mse: 0.99136 |  0:09:02s
    epoch 11 | loss: 0.99122 | val_0_mse: 0.99135 |  0:09:49s
    epoch 12 | loss: 0.99118 | val_0_mse: 0.9912  |  0:10:41s
    epoch 13 | loss: 0.99086 | val_0_mse: 0.99081 |  0:11:29s
    epoch 14 | loss: 0.99069 | val_0_mse: 0.99124 |  0:12:20s
    epoch 15 | loss: 0.99153 | val_0_mse: 0.9919  |  0:13:12s
    epoch 16 | loss: 0.99153 | val_0_mse: 0.99168 |  0:14:00s
    epoch 17 | loss: 0.99084 | val_0_mse: 0.99116 |  0:14:53s
    epoch 18 | loss: 0.9907  | val_0_mse: 0.9916  |  0:15:43s
    epoch 19 | loss: 0.99106 | val_0_mse: 0.99151 |  0:16:37s
    epoch 20 | loss: 0.99083 | val_0_mse: 0.99209 |  0:17:22s
    epoch 21 | loss: 0.99084 | val_0_mse: 0.99176 |  0:18:13s
    epoch 22 | loss: 0.99111 | val_0_mse: 0.99107 |  0:19:01s
    epoch 23 | loss: 0.99047 | val_0_mse: 0.99082 |  0:19:54s
    
    Early stopping occurred at epoch 23 with best_epoch = 13 and best_val_0_mse = 0.99081
    Best weights from best epoch are automatically used!
    
    Device used : cuda
    
    Device used : cuda
    
    epoch 0  | loss: 1.04281 | val_0_mse: 0.99568 |  0:00:46s
    epoch 1  | loss: 0.99669 | val_0_mse: 0.99515 |  0:01:33s
    epoch 2  | loss: 0.99539 | val_0_mse: 0.99323 |  0:02:28s
    epoch 3  | loss: 0.99453 | val_0_mse: 0.99295 |  0:03:23s
    epoch 4  | loss: 0.99421 | val_0_mse: 0.99241 |  0:04:22s
    epoch 5  | loss: 0.99355 | val_0_mse: 0.99202 |  0:05:22s
    epoch 6  | loss: 0.99322 | val_0_mse: 0.99253 |  0:06:19s
    epoch 7  | loss: 0.99297 | val_0_mse: 0.99216 |  0:07:17s
    epoch 8  | loss: 0.9927  | val_0_mse: 0.99172 |  0:08:15s
    epoch 9  | loss: 0.99247 | val_0_mse: 0.99039 |  0:09:18s
    epoch 10 | loss: 0.99199 | val_0_mse: 0.99012 |  0:10:17s
    epoch 11 | loss: 0.99144 | val_0_mse: 0.99082 |  0:11:13s
    epoch 12 | loss: 0.99159 | val_0_mse: 0.99064 |  0:12:19s
    epoch 13 | loss: 0.99199 | val_0_mse: 0.99109 |  0:13:22s
    epoch 14 | loss: 0.99191 | val_0_mse: 0.99061 |  0:14:27s
    epoch 15 | loss: 0.99168 | val_0_mse: 0.98989 |  0:15:35s
    epoch 16 | loss: 0.991   | val_0_mse: 0.9896  |  0:16:36s
    epoch 17 | loss: 0.99037 | val_0_mse: 0.98912 |  0:17:34s
    epoch 18 | loss: 0.98994 | val_0_mse: 0.98838 |  0:18:36s
    epoch 19 | loss: 0.9896  | val_0_mse: 0.98826 |  0:19:36s
    epoch 20 | loss: 0.98947 | val_0_mse: 0.9879  |  0:20:40s
    epoch 21 | loss: 0.98909 | val_0_mse: 0.98827 |  0:21:43s
    epoch 22 | loss: 0.98892 | val_0_mse: 0.98814 |  0:22:45s
    epoch 23 | loss: 0.98905 | val_0_mse: 0.98819 |  0:23:49s
    epoch 24 | loss: 0.98862 | val_0_mse: 0.98727 |  0:24:51s
    epoch 25 | loss: 0.98824 | val_0_mse: 0.98687 |  0:25:52s
    epoch 26 | loss: 0.98831 | val_0_mse: 0.98759 |  0:26:46s
    epoch 27 | loss: 0.98884 | val_0_mse: 0.98809 |  0:27:36s
    epoch 28 | loss: 0.98919 | val_0_mse: 0.98771 |  0:28:26s
    epoch 29 | loss: 0.98838 | val_0_mse: 0.98716 |  0:29:23s
    Stop training because you reached max_epochs = 30 with best_epoch = 25 and best_val_0_mse = 0.98687
    Best weights from best epoch are automatically used!
    
    Device used : cuda
    
    Device used : cuda
    
    epoch 0  | loss: 1.04277 | val_0_mse: 0.99359 |  0:01:05s
    epoch 1  | loss: 0.9953  | val_0_mse: 0.99244 |  0:02:14s
    epoch 2  | loss: 0.99455 | val_0_mse: 0.9922  |  0:03:21s
    epoch 3  | loss: 0.99364 | val_0_mse: 0.99165 |  0:04:24s
    epoch 4  | loss: 0.99351 | val_0_mse: 0.99219 |  0:05:34s
    epoch 5  | loss: 0.99337 | val_0_mse: 0.99177 |  0:06:48s
    epoch 6  | loss: 0.99259 | val_0_mse: 0.99085 |  0:08:00s
    epoch 7  | loss: 0.99296 | val_0_mse: 0.99171 |  0:09:09s
    epoch 8  | loss: 0.993   | val_0_mse: 0.99106 |  0:10:15s
    epoch 9  | loss: 0.9925  | val_0_mse: 0.99109 |  0:11:27s
    epoch 10 | loss: 0.99201 | val_0_mse: 0.99028 |  0:12:35s
    epoch 11 | loss: 0.99158 | val_0_mse: 0.98988 |  0:13:41s
    epoch 12 | loss: 0.99181 | val_0_mse: 0.99084 |  0:14:51s
    epoch 13 | loss: 0.99258 | val_0_mse: 0.99085 |  0:15:59s
    epoch 14 | loss: 0.99196 | val_0_mse: 0.9902  |  0:17:05s
    epoch 15 | loss: 0.99152 | val_0_mse: 0.98986 |  0:18:16s
    epoch 16 | loss: 0.99118 | val_0_mse: 0.98973 |  0:19:19s
    epoch 17 | loss: 0.99099 | val_0_mse: 0.9899  |  0:20:26s
    epoch 18 | loss: 0.99111 | val_0_mse: 0.99037 |  0:21:30s
    epoch 19 | loss: 0.99097 | val_0_mse: 0.98957 |  0:22:37s
    epoch 20 | loss: 0.99095 | val_0_mse: 0.99017 |  0:23:42s
    epoch 21 | loss: 0.99115 | val_0_mse: 0.98952 |  0:24:46s
    epoch 22 | loss: 0.99106 | val_0_mse: 0.98986 |  0:25:46s
    epoch 23 | loss: 0.99078 | val_0_mse: 0.99022 |  0:26:38s
    epoch 24 | loss: 0.99164 | val_0_mse: 0.98978 |  0:27:31s
    epoch 25 | loss: 0.99126 | val_0_mse: 0.98969 |  0:28:42s
    epoch 26 | loss: 0.99093 | val_0_mse: 0.98943 |  0:29:50s
    epoch 27 | loss: 0.99103 | val_0_mse: 0.99027 |  0:30:59s
    epoch 28 | loss: 0.99161 | val_0_mse: 0.98995 |  0:32:08s
    epoch 29 | loss: 0.99151 | val_0_mse: 0.99026 |  0:33:17s
    Stop training because you reached max_epochs = 30 with best_epoch = 26 and best_val_0_mse = 0.98943
    Best weights from best epoch are automatically used!
    
    Device used : cuda
    
    Device used : cuda
    
    epoch 0  | loss: 1.03356 | val_0_mse: 0.99151 |  0:01:12s
    epoch 1  | loss: 0.99349 | val_0_mse: 0.99174 |  0:02:27s
    epoch 2  | loss: 0.99309 | val_0_mse: 0.99197 |  0:03:43s
    epoch 3  | loss: 0.99297 | val_0_mse: 0.99182 |  0:04:57s
    epoch 4  | loss: 0.99276 | val_0_mse: 0.99115 |  0:06:10s
    epoch 5  | loss: 0.99221 | val_0_mse: 0.99065 |  0:07:22s
    epoch 6  | loss: 0.99182 | val_0_mse: 0.99067 |  0:08:42s
    epoch 7  | loss: 0.99149 | val_0_mse: 0.99052 |  0:09:55s
    epoch 8  | loss: 0.99162 | val_0_mse: 0.99051 |  0:11:07s
    epoch 9  | loss: 0.99122 | val_0_mse: 0.99046 |  0:12:25s
    epoch 10 | loss: 0.99126 | val_0_mse: 0.99043 |  0:13:38s
    epoch 11 | loss: 0.99128 | val_0_mse: 0.99032 |  0:14:53s
    epoch 12 | loss: 0.99149 | val_0_mse: 0.99083 |  0:16:07s
    epoch 13 | loss: 0.99174 | val_0_mse: 0.99063 |  0:17:23s
    epoch 14 | loss: 0.99139 | val_0_mse: 0.99121 |  0:18:34s
    epoch 15 | loss: 0.99137 | val_0_mse: 0.99019 |  0:19:43s
    epoch 16 | loss: 0.99148 | val_0_mse: 0.99059 |  0:20:59s
    epoch 17 | loss: 0.99157 | val_0_mse: 0.99068 |  0:22:02s
    epoch 18 | loss: 0.99168 | val_0_mse: 0.99063 |  0:23:04s
    epoch 19 | loss: 0.99182 | val_0_mse: 0.99073 |  0:24:06s
    epoch 20 | loss: 0.99181 | val_0_mse: 0.99063 |  0:25:22s
    epoch 21 | loss: 0.99191 | val_0_mse: 0.99161 |  0:26:31s
    epoch 22 | loss: 0.99167 | val_0_mse: 0.99044 |  0:27:43s
    epoch 23 | loss: 0.9916  | val_0_mse: 0.99065 |  0:28:59s
    epoch 24 | loss: 0.99184 | val_0_mse: 0.99042 |  0:30:12s
    epoch 25 | loss: 0.99189 | val_0_mse: 0.99097 |  0:31:30s
    
    Early stopping occurred at epoch 25 with best_epoch = 15 and best_val_0_mse = 0.99019
    Best weights from best epoch are automatically used!
    
    Device used : cuda
    
    ++++++++: 7 [{'m1': {'version': 'v3', 'data': DataSource(68241c363eb24493ad10071c3ac32243T)}, 'm2': {'version': 'v3', 'cast_label_int': False, 'data': DataSource(07687271c4434de190c8a7e939cf6b44T), 'plot_label_counts': <bound method plot_label_counts of {...}>}, 'm17': {'version': 'v3', 'data': DataSource(3b813c49cb56448a83011000121dd77eT)}, 'm3': {'version': 'v3', 'data': DataSource(d959f1c34b7c48ebafc615a565b36afaT)}, 'm6': {'version': 'v3', 'data': DataSource(aed725224fb8446ea7ae455a9bcdaa18T)}, 'm7': {'version': 'v3', 'data': DataSource(05c6ea69f48a4bdd99e267290fd1177cT)}, 'm13': {'version': 'v3', 'data': DataSource(0ab434fa4ac044f68eebea4c2f1e6fa2T)}, 'm14': {'version': 'v3', 'data': DataSource(7cc1b59c2f614d129f7227c6b8855490T)}, 'm4': {'version': 'v3', 'data': DataSource(487b8ca11e7d48f0b3821cab692f0adaT), 'rows': 566116}, 'm10': {'version': 'v3', 'data': DataSource(4417778d5cb04c7393a71c44e4b9a78cT)}, 'm12': {'version': 'v3', 'data_1': DataSource(b3549480699749c8b04fb5ad5d08c1cdT), 'data_2': DataSource(f078da3883994ac2a992815d846e8159T), 'data_3': None}, 'm18': {'version': 'v3', 'data': DataSource(0e785dd8f73a4df5b0a6b7add3d6df09T)}, 'm5': {'version': 'v3', 'data': DataSource(55dfc437f42c4344a1cbaaaa93554a85T)}, 'm8': {'version': 'v3', 'data': DataSource(8c00872ffb114f59a6944fc98fe4dc50T)}, 'm9': {'version': 'v3', 'data': DataSource(2b7564f990e34d9c8a6b7afe5d57bcf4T)}, 'm16': {'version': 'v3', 'data': DataSource(0851cbc826d44770b02bf7458aebc807T)}, 'm15': {'version': 'v3', 'data': DataSource(144e22288ba04da4b08a3b75b0f5238bT)}, 'm11': {'version': 'v3', 'data': DataSource(afc4efab90164a1d91d90a4ce81ec54dT)}, 'm19': {'version': 'v3', 'data': DataSource(4bb5392733a44b3d9fa5660906c8c46fT)}, 'm20': {'version': 'v3', 'data_1': DataSource(a83d2cefc5f74f399bf9fd7dbbc904a7T), 'data_2': None, 'data_3': None}}, {'m1': {'version': 'v3', 'data': DataSource(6764f54e10af4db3ae79934152ca0f4aT)}, 'm2': {'version': 'v3', 'cast_label_int': False, 'data': DataSource(dc215323d944416485bcedcdbc5e22a3T), 'plot_label_counts': <bound method plot_label_counts of {...}>}, 'm17': {'version': 'v3', 'data': DataSource(9834c15003784c5dbe935d10cadeafc7T)}, 'm3': {'version': 'v3', 'data': DataSource(d959f1c34b7c48ebafc615a565b36afaT)}, 'm6': {'version': 'v3', 'data': DataSource(dd96e8ddefa449a5aca032726f30a591T)}, 'm7': {'version': 'v3', 'data': DataSource(0d2a60a0ad654ced8e45b8dfcb66ee22T)}, 'm13': {'version': 'v3', 'data': DataSource(c0b454ad5c6c487cab6e0ecfea1b7004T)}, 'm14': {'version': 'v3', 'data': DataSource(0dd7cba56ec04f4da074b0fdb9fbdb5eT)}, 'm4': {'version': 'v3', 'data': DataSource(4975e14163804fa0b708b2384fc5594dT), 'rows': 1141540}, 'm10': {'version': 'v3', 'data': DataSource(d4c2485dd5de41959c7e6cbea17ce1ceT)}, 'm12': {'version': 'v3', 'data_1': DataSource(e1a78946399f4b30abd82cb1841ca619T), 'data_2': DataSource(2be23289368b4835b9cab5861e4d54a0T), 'data_3': None}, 'm18': {'version': 'v3', 'data': DataSource(9ff6a613e1154e0e809563652f7a2f67T)}, 'm5': {'version': 'v3', 'data': DataSource(8fc2f2637aa64d0aa40358baab1ffc81T)}, 'm8': {'version': 'v3', 'data': DataSource(f23f304bd7ac4c2e96df4adf4c39d410T)}, 'm9': {'version': 'v3', 'data': DataSource(0a78723f5d3547eb9d0c02d84eb9f277T)}, 'm16': {'version': 'v3', 'data': DataSource(4c9784372a164c0c82765d60d6f3b5f2T)}, 'm15': {'version': 'v3', 'data': DataSource(3bc7660a50064d1eb6a13247460c627bT)}, 'm11': {'version': 'v3', 'data': DataSource(889dee009d744733b3d16e2b8c1359d8T)}, 'm19': {'version': 'v3', 'data': DataSource(077c44be68474f2f9f8cb79687b3d040T)}, 'm20': {'version': 'v3', 'data_1': DataSource(427cee2a3db9408c9b5eaf0f4c283fb4T), 'data_2': None, 'data_3': None}}, {'m1': {'version': 'v3', 'data': DataSource(c5214ee8cc7242b1a8820afe89d2b9eaT)}, 'm2': {'version': 'v3', 'cast_label_int': False, 'data': DataSource(5a55a1d2d2fd4805bbf98d84b1c70fd7T), 'plot_label_counts': <bound method plot_label_counts of {...}>}, 'm17': {'version': 'v3', 'data': DataSource(184382a45e52447ba677e8c5c83a9c42T)}, 'm3': {'version': 'v3', 'data': DataSource(d959f1c34b7c48ebafc615a565b36afaT)}, 'm6': {'version': 'v3', 'data': DataSource(49e5f9ab814145ccb9521c4029f0d641T)}, 'm7': {'version': 'v3', 'data': DataSource(a270d6cf1e1f463c89323d1ab47a4cc2T)}, 'm13': {'version': 'v3', 'data': DataSource(67d0038e409140e49466421794c089fbT)}, 'm14': {'version': 'v3', 'data': DataSource(40430d19efb14794be1921bdcabf9b0dT)}, 'm4': {'version': 'v3', 'data': DataSource(446063f5c0c64b9783018d8342ab2e08T), 'rows': 1797897}, 'm10': {'version': 'v3', 'data': DataSource(4e5336dcb6344f399017c1b7010e14c9T)}, 'm12': {'version': 'v3', 'data_1': DataSource(0604f63da4764d66838b16e9d8bad16cT), 'data_2': DataSource(d5c6c87eb306461da8d6d668d425b0c2T), 'data_3': None}, 'm18': {'version': 'v3', 'data': DataSource(11c2b95b99b24492b3adefcc1b16c7fcT)}, 'm5': {'version': 'v3', 'data': DataSource(d8d57f3bdcb549bb9723902250a74236T)}, 'm8': {'version': 'v3', 'data': DataSource(8d7c292f90414c5bbdd714473d2cc230T)}, 'm9': {'version': 'v3', 'data': DataSource(a4d58ed1d48b4138b6e8a1925bb8eca7T)}, 'm16': {'version': 'v3', 'data': DataSource(bc59bc0b0d564f0c85e62e53a9069fa9T)}, 'm15': {'version': 'v3', 'data': DataSource(bfd711bdbafc42dc85db9c474e4fd823T)}, 'm11': {'version': 'v3', 'data': DataSource(ac8e200cae8742128bfa1ef75f4cbc74T)}, 'm19': {'version': 'v3', 'data': DataSource(53c5afd8d892493fb705bec27a7670b9T)}, 'm20': {'version': 'v3', 'data_1': DataSource(b96c346559074f9c96559c39109937bcT), 'data_2': None, 'data_3': None}}, {'m1': {'version': 'v3', 'data': DataSource(be58a1d632914170b97ecaf845954443T)}, 'm2': {'version': 'v3', 'cast_label_int': False, 'data': DataSource(10749d1cb3ee4747bb33f3d51e91c360T), 'plot_label_counts': <bound method plot_label_counts of {...}>}, 'm17': {'version': 'v3', 'data': DataSource(4479649d148242e3b08187b4902ebe30T)}, 'm3': {'version': 'v3', 'data': DataSource(d959f1c34b7c48ebafc615a565b36afaT)}, 'm6': {'version': 'v3', 'data': DataSource(d08172a096564eb3a6a1954e911f7c01T)}, 'm7': {'version': 'v3', 'data': DataSource(f68bcfc6f800442a9379a2408f70fbabT)}, 'm13': {'version': 'v3', 'data': DataSource(424f93b8da8f49c0a934f1325468e9deT)}, 'm14': {'version': 'v3', 'data': DataSource(c504229778504fcea2b5d13b015bf376T)}, 'm4': {'version': 'v3', 'data': DataSource(2c3e4402a134465eada1674f723b5828T), 'rows': 1981161}, 'm10': {'version': 'v3', 'data': DataSource(85560df344194c78a366e34a3e1166d1T)}, 'm12': {'version': 'v3', 'data_1': DataSource(8baf0f9cf2024bc79e49cfe42b9b1674T), 'data_2': DataSource(733c282718484f00b1a1c98a698521dbT), 'data_3': None}, 'm18': {'version': 'v3', 'data': DataSource(da9400e858e24413a7ef1836da1051e8T)}, 'm5': {'version': 'v3', 'data': DataSource(47cffbea080d4efeb799ca0c85494591T)}, 'm8': {'version': 'v3', 'data': DataSource(7ef6d342eb05495a869d64a20368b12fT)}, 'm9': {'version': 'v3', 'data': DataSource(0081046a65274befbd8808386e84cf07T)}, 'm16': {'version': 'v3', 'data': DataSource(f854361cb2394624b01ebc7e34a0a426T)}, 'm15': {'version': 'v3', 'data': DataSource(668dceab15e3407eb8067bf199a10341T)}, 'm11': {'version': 'v3', 'data': DataSource(392af57f1f154cbd94ad906bb8cfd554T)}, 'm19': {'version': 'v3', 'data': DataSource(8f3c39c78e0f42cabfc53115adfcd34bT)}, 'm20': {'version': 'v3', 'data_1': DataSource(cdeec7bee11344208c72c166efa41447T), 'data_2': None, 'data_3': None}}, {'m1': {'version': 'v3', 'data': DataSource(949bd164c7f446189b53c4b84adc6a3dT)}, 'm2': {'version': 'v3', 'cast_label_int': False, 'data': DataSource(2dad080ace644086843a0bfef2173195T), 'plot_label_counts': <bound method plot_label_counts of {...}>}, 'm17': {'version': 'v3', 'data': DataSource(1dd0c69aaee347509614a0901fd27163T)}, 'm3': {'version': 'v3', 'data': DataSource(d959f1c34b7c48ebafc615a565b36afaT)}, 'm6': {'version': 'v3', 'data': DataSource(af9b87add46b4df499e305b1d318e3feT)}, 'm7': {'version': 'v3', 'data': DataSource(d235251865f546c2bb4aaec7e754f844T)}, 'm13': {'version': 'v3', 'data': DataSource(fdd15e81316b4942aa78e9290df1d70eT)}, 'm14': {'version': 'v3', 'data': DataSource(e966c94f7e6e4f98bbfd26a43f4008deT)}, 'm4': {'version': 'v3', 'data': DataSource(323150161df04097a47cbd685aeedc68T), 'rows': 2248763}, 'm10': {'version': 'v3', 'data': DataSource(c4fbafe9dc53444394bf38974845a4c1T)}, 'm12': {'version': 'v3', 'data_1': DataSource(db79f7cf8b5d42f38608fe10c375eb77T), 'data_2': DataSource(e81fc157c2404c5da8481c0c22a648e5T), 'data_3': None}, 'm18': {'version': 'v3', 'data': DataSource(3c831e5c55b04dfeacaeefe15237e047T)}, 'm5': {'version': 'v3', 'data': DataSource(d173fe56f32049dc852031782df6b9ecT)}, 'm8': {'version': 'v3', 'data': DataSource(6d850e68d4404d88a805cb4aed1f0562T)}, 'm9': {'version': 'v3', 'data': DataSource(4d69d6e8ffce48e7bd127e0ebbd51f8aT)}, 'm16': {'version': 'v3', 'data': DataSource(0f1e1c1a798846dd8af14c58b498b489T)}, 'm15': {'version': 'v3', 'data': DataSource(f64336fd81184579a0da257a1665aca2T)}, 'm11': {'version': 'v3', 'data': DataSource(009fb83ce1db4c58931d024fb7e0c354T)}, 'm19': {'version': 'v3', 'data': DataSource(c1794cd0ecc14541ac61dc721ef690feT)}, 'm20': {'version': 'v3', 'data_1': DataSource(a02325a94a194fed85df5658212e3323T), 'data_2': None, 'data_3': None}}, {'m1': {'version': 'v3', 'data': DataSource(efcffce5551846c191742f9df88ac35aT)}, 'm2': {'version': 'v3', 'cast_label_int': False, 'data': DataSource(52b6fe1b1c374706b8789edfd27c95c3T), 'plot_label_counts': <bound method plot_label_counts of {...}>}, 'm17': {'version': 'v3', 'data': DataSource(b6caa0dc0b424db09194ba197035a422T)}, 'm3': {'version': 'v3', 'data': DataSource(d959f1c34b7c48ebafc615a565b36afaT)}, 'm6': {'version': 'v3', 'data': DataSource(bdaf158b26ce4cdd8d0e5b56107ab552T)}, 'm7': {'version': 'v3', 'data': DataSource(021635067f0440afaf92dcb10d6ded79T)}, 'm13': {'version': 'v3', 'data': DataSource(ef24dded70f34273837d4731e12c019dT)}, 'm14': {'version': 'v3', 'data': DataSource(e4c3c2c0f9434f5e89b37c852a301aaeT)}, 'm4': {'version': 'v3', 'data': DataSource(e7e4b3c9f35b457bb9d61d587e81eff7T), 'rows': 2496821}, 'm10': {'version': 'v3', 'data': DataSource(a0358fcac82c4c9c9fab413a2c802f84T)}, 'm12': {'version': 'v3', 'data_1': DataSource(ee415aeedc2844a19133adc786c064f6T), 'data_2': DataSource(7289e73797dd4e19889daea531c34c95T), 'data_3': None}, 'm18': {'version': 'v3', 'data': DataSource(18b307e3a3b74e91a6c70962ae16fa0bT)}, 'm5': {'version': 'v3', 'data': DataSource(eda90ea93b4d494e92e6a561e888b92dT)}, 'm8': {'version': 'v3', 'data': DataSource(d8d52f0a0c1042c5b46aa3791d185fbcT)}, 'm9': {'version': 'v3', 'data': DataSource(311c0868124f47a6b2b04d1fa6841d5cT)}, 'm16': {'version': 'v3', 'data': DataSource(c2d4c402839844c599b783725e8edaaeT)}, 'm15': {'version': 'v3', 'data': DataSource(40aa467f69d149668fcb3c426d3819a8T)}, 'm11': {'version': 'v3', 'data': DataSource(b08f70790d304c6d825d74b41853187fT)}, 'm19': {'version': 'v3', 'data': DataSource(956b268c1c7b452eb23c864aad4efbaaT)}, 'm20': {'version': 'v3', 'data_1': DataSource(7781f514ba184b12bbeb810ac1232eddT), 'data_2': None, 'data_3': None}}, {'m1': {'version': 'v3', 'data': DataSource(65c31817e9f8478b82d74b7b555918a2T)}, 'm2': {'version': 'v3', 'cast_label_int': False, 'data': DataSource(af2305ba05b44d62991dfb713a52222eT), 'plot_label_counts': <bound method plot_label_counts of {...}>}, 'm17': {'version': 'v3', 'data': DataSource(4d9862db5ff74c2888ec5beedf6ba7bfT)}, 'm3': {'version': 'v3', 'data': DataSource(d959f1c34b7c48ebafc615a565b36afaT)}, 'm6': {'version': 'v3', 'data': DataSource(d0c9b9a2ffde4c598a50562ea1f91b8fT)}, 'm7': {'version': 'v3', 'data': DataSource(8c0b2aec1e2d47cbb9e26107217b8913T)}, 'm13': {'version': 'v3', 'data': DataSource(ed9eaf7e985d4633892f8037c6d9c67fT)}, 'm14': {'version': 'v3', 'data': DataSource(5893545664f14b5e90a3b289644c3578T)}, 'm4': {'version': 'v3', 'data': DataSource(2fc213398d1a493698eca41b451c1317T), 'rows': 2713676}, 'm10': {'version': 'v3', 'data': DataSource(5c5793a069af46e286fd979eb9248a25T)}, 'm12': {'version': 'v3', 'data_1': DataSource(6ac295b77ed145b0ba293779f3a622f8T), 'data_2': DataSource(5f390f00a9b54c5199b52bb392a301d2T), 'data_3': None}, 'm18': {'version': 'v3', 'data': DataSource(f63a72dbbd8047389d65fdea0b706581T)}, 'm5': {'version': 'v3', 'data': DataSource(825828a4aff24dacaa7f865a9a575a00T)}, 'm8': {'version': 'v3', 'data': DataSource(fd4d1fa600e342caabef8066a6322d14T)}, 'm9': {'version': 'v3', 'data': DataSource(2eff61c64b9b475c8357f5d595815469T)}, 'm16': {'version': 'v3', 'data': DataSource(aeecc098b835403e8ce552ea7b3256abT)}, 'm15': {'version': 'v3', 'data': DataSource(431c731855564f4090efaee9523e5d68T)}, 'm11': {'version': 'v3', 'data': DataSource(de15534726b944c1a7e2862467472208T)}, 'm19': {'version': 'v3', 'data': DataSource(cd185363b9494ccf80ef0298a867c141T)}, 'm20': {'version': 'v3', 'data_1': DataSource(886a826a37004f52a1f3058e0e452598T), 'data_2': None, 'data_3': None}}]
    
    • 收益率387.81%
    • 年化收益率27.57%
    • 基准收益率39.0%
    • 阿尔法0.25
    • 贝塔0.9
    • 夏普比率0.83
    • 胜率0.56
    • 盈亏比1.02
    • 收益波动率31.92%
    • 信息比率0.06
    • 最大回撤55.22%
    bigcharts-data-start/{"__type":"tabs","__id":"bigchart-1eb4e82158a046928183665543e3b620"}/bigcharts-data-end