克隆策略

    {"description":"实验创建于2020/2/21","graph":{"edges":[{"to_node_id":"-41:instruments","from_node_id":"-31:data"},{"to_node_id":"-4208:instruments","from_node_id":"-31:data"},{"to_node_id":"-12255:input_2","from_node_id":"-31:data"},{"to_node_id":"-52:data1","from_node_id":"-41:data"},{"to_node_id":"-198:input_data","from_node_id":"-52:data"},{"to_node_id":"-3887:input_1","from_node_id":"-102:data"},{"to_node_id":"-3725:input_3","from_node_id":"-102:data"},{"to_node_id":"-3954:input_1","from_node_id":"-102:data"},{"to_node_id":"-508:input_3","from_node_id":"-102:data"},{"to_node_id":"-1628:instruments","from_node_id":"-102:data"},{"to_node_id":"-12259:input_2","from_node_id":"-102:data"},{"to_node_id":"-1702:instruments","from_node_id":"-102:data"},{"to_node_id":"-184:data2","from_node_id":"-116:predictions"},{"to_node_id":"-4571:predictions","from_node_id":"-184:data"},{"to_node_id":"-116:predict_ds","from_node_id":"-188:data"},{"to_node_id":"-116:training_ds","from_node_id":"-198:data"},{"to_node_id":"-116:test_ds","from_node_id":"-198:data"},{"to_node_id":"-924:input_1","from_node_id":"-2503:data"},{"to_node_id":"-1720:input_1","from_node_id":"-2503:data"},{"to_node_id":"-1720:input_2","from_node_id":"-22272:data"},{"to_node_id":"-1962:input_2","from_node_id":"-22276:data"},{"to_node_id":"-1673:input_1","from_node_id":"-22276:data"},{"to_node_id":"-4228:input_2","from_node_id":"-924:data_1"},{"to_node_id":"-1648:input_2","from_node_id":"-924:data_1"},{"to_node_id":"-1962:input_1","from_node_id":"-1720:data_1"},{"to_node_id":"-676:features","from_node_id":"-1673:data_1"},{"to_node_id":"-1221:features","from_node_id":"-1673:data_1"},{"to_node_id":"-1227:input_2","from_node_id":"-1673:data_1"},{"to_node_id":"-682:input_2","from_node_id":"-1673:data_1"},{"to_node_id":"-116:features","from_node_id":"-1673:data_1"},{"to_node_id":"-4208:features","from_node_id":"-1962:data_1"},{"to_node_id":"-4215:features","from_node_id":"-1962:data_1"},{"to_node_id":"-1628:features","from_node_id":"-1962:data_1"},{"to_node_id":"-1635:features","from_node_id":"-1962:data_1"},{"to_node_id":"-673:input_2","from_node_id":"-4550:data"},{"to_node_id":"-2431:input_1","from_node_id":"-1540:data"},{"to_node_id":"-184:data1","from_node_id":"-2431:data_1"},{"to_node_id":"-989:input_data","from_node_id":"-3887:data_1"},{"to_node_id":"-1540:input_data","from_node_id":"-3725:data_2"},{"to_node_id":"-2431:input_2","from_node_id":"-3725:data_3"},{"to_node_id":"-2638:input_1","from_node_id":"-2186:data"},{"to_node_id":"-3954:input_2","from_node_id":"-2186:data"},{"to_node_id":"-3887:input_2","from_node_id":"-18645:data"},{"to_node_id":"-3964:data1","from_node_id":"-989:data"},{"to_node_id":"-4215:input_data","from_node_id":"-4208:data"},{"to_node_id":"-12255:input_1","from_node_id":"-4215:data"},{"to_node_id":"-676:input_data","from_node_id":"-4228:data"},{"to_node_id":"-1635:input_data","from_node_id":"-1628:data"},{"to_node_id":"-12259:input_1","from_node_id":"-1635:data"},{"to_node_id":"-1221:input_data","from_node_id":"-1648:data"},{"to_node_id":"-4228:input_1","from_node_id":"-12255:data"},{"to_node_id":"-1648:input_1","from_node_id":"-12259:data"},{"to_node_id":"-1945:cleaning_feature_list","from_node_id":"-2638:data_1"},{"to_node_id":"-3960:input_data","from_node_id":"-3954:data_1"},{"to_node_id":"-508:input_1","from_node_id":"-3960:data"},{"to_node_id":"-3004:input_ds","from_node_id":"-3964:data"},{"to_node_id":"-3964:data2","from_node_id":"-610:data"},{"to_node_id":"-1945:feature_data1","from_node_id":"-508:data_1"},{"to_node_id":"-1945:feature_data2","from_node_id":"-508:data_2"},{"to_node_id":"-3725:input_2","from_node_id":"-3004:sorted_data"},{"to_node_id":"-3725:input_1","from_node_id":"-673:data_2"},{"to_node_id":"-682:input_1","from_node_id":"-676:data"},{"to_node_id":"-52:data2","from_node_id":"-682:data"},{"to_node_id":"-1227:input_1","from_node_id":"-1221:data"},{"to_node_id":"-188:input_data","from_node_id":"-1227:data"},{"to_node_id":"-610:input_data_1","from_node_id":"-1945:cleaned_data1"},{"to_node_id":"-610:input_data_2","from_node_id":"-1945:cleaned_data2"},{"to_node_id":"-1540:trained_model","from_node_id":"-5858:data"},{"to_node_id":"-1702:options_data","from_node_id":"-4571:data"}],"nodes":[{"node_id":"-31","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2020-10-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2020-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":"-31"}],"output_ports":[{"name":"data","node_id":"-31"}],"cacheable":true,"seq_num":4,"comment":"训练集","comment_collapsed":false},{"node_id":"-41","module_id":"BigQuantSpace.advanced_auto_labeler.advanced_auto_labeler-v2","parameters":[{"name":"label_expr","value":"# #号开始的表示注释\n# 0. 每行一个,顺序执行,从第二个开始,可以使用label字段\n# 1. 可用数据字段见 https://bigquant.com/docs/develop/datasource/deprecated/history_data.html\n# 添加benchmark_前缀,可使用对应的benchmark数据\n# 2. 可用操作符和函数见 `表达式引擎 <https://bigquant.com/docs/develop/bigexpr/usage.html>`_\n\n# 计算收益:5日收盘价(作为卖出价格)除以明日开盘价(作为买入价格)\nshift(close, -1) / shift(close, 0)\n#shift(close, -5) / shift(open, -1)\n#shift(close, -2) / shift(close, -1)\n#shift(close, -2) / shift(open, -2)\n\n# 极值处理:用1%和99%分位的值做clip\nclip(label, all_quantile(label, 0.01), all_quantile(label, 0.99))\n\n# 将分数映射到分类,这里使用20个分类\nall_wbins(label, 20)\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":"True","type":"Literal","bound_global_parameter":null},{"name":"user_functions","value":"{}","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-41"}],"output_ports":[{"name":"data","node_id":"-41"}],"cacheable":true,"seq_num":5,"comment":"","comment_collapsed":true},{"node_id":"-52","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":"-52"},{"name":"data2","node_id":"-52"}],"output_ports":[{"name":"data","node_id":"-52"}],"cacheable":true,"seq_num":6,"comment":"绑定 标签","comment_collapsed":false},{"node_id":"-102","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2021-07-01","type":"Literal","bound_global_parameter":"交易日期"},{"name":"end_date","value":"2021-08-31","type":"Literal","bound_global_parameter":"交易日期"},{"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":"-102"}],"output_ports":[{"name":"data","node_id":"-102"}],"cacheable":true,"seq_num":15,"comment":"预测集","comment_collapsed":false},{"node_id":"-116","module_id":"BigQuantSpace.stock_ranker.stock_ranker-v2","parameters":[{"name":"learning_algorithm","value":"排序","type":"Literal","bound_global_parameter":null},{"name":"number_of_leaves","value":30,"type":"Literal","bound_global_parameter":null},{"name":"minimum_docs_per_leaf","value":1000,"type":"Literal","bound_global_parameter":null},{"name":"number_of_trees","value":"30","type":"Literal","bound_global_parameter":null},{"name":"learning_rate","value":0.1,"type":"Literal","bound_global_parameter":null},{"name":"max_bins","value":1023,"type":"Literal","bound_global_parameter":null},{"name":"feature_fraction","value":1,"type":"Literal","bound_global_parameter":null},{"name":"data_row_fraction","value":1,"type":"Literal","bound_global_parameter":null},{"name":"ndcg_discount_base","value":1,"type":"Literal","bound_global_parameter":null},{"name":"slim_data","value":"True","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"training_ds","node_id":"-116"},{"name":"features","node_id":"-116"},{"name":"base_model","node_id":"-116"},{"name":"test_ds","node_id":"-116"},{"name":"input_model","node_id":"-116"},{"name":"predict_ds","node_id":"-116"}],"output_ports":[{"name":"model","node_id":"-116"},{"name":"predictions","node_id":"-116"}],"cacheable":true,"seq_num":16,"comment":"","comment_collapsed":true},{"node_id":"-160","module_id":"BigQuantSpace.hyper_rolling_train.hyper_rolling_train-v1","parameters":[{"name":"run","value":"#have_benchmark_factor = False # 是否有大盘风控因子模块:无\nhave_benchmark_factor = True # 是否有大盘风控因子模块:有\n\ntrain_update_days = 120 # 更新周期,按交易日计算,每多少天更新一次,代表每次滚动测试集长度\ntrain_data_min_days = 60 # 最小数据天数,按交易日计算,代表滚动训练集最小长度,即 首次 滚动训练集长度,所以 第一个滚动的日期区间 是从 开始日期 到 开始日期+最小数据天数\ntrain_data_max_days = 60 # 最大数据天数,按交易日计算,代表滚动训练集最大长度,即 滚动几次后 的滚动训练集长度,0,表示没有限制,否则 每一个滚动的开始日期 = max(此滚动的结束日期-最大数据天数, 开始日期\n\nstart_date = '2021-01-01' # 数据开始日期\ntdays = list(D.trading_days(end_date = start_date)['date']) # 结束于 数据开始日期 的 交易日历\n\nimport time \n\ndef bigquant_run(\n bq_graph,\n inputs,\n trading_days_market='CN', # 使用那个市场的交易日历, TODO\n train_instruments_mid='m4', # 训练集 证券代码列表模块id\n test_instruments_mid='m15', # 测试集 证券代码列表模块id\n# predict_mid='m12' if have_benchmark_factor else 'm16', # 预测模块id\n predict_mid='m19' if have_benchmark_factor else 'm16', # 预测模块id,预测模块 为连接 回测模块 options_data输入端即第二输入端的模块\n trade_mid='m56', # 回测模块id\n# start_date='2021-01-01', # 数据开始日期\n start_date=tdays[len(tdays)-train_data_min_days], # 数据开始日期,即 首次滚动训练 开始日期,预留 首次滚动 训练集 \n end_date=T.live_run_param('trading_date', '2021-05-31'), # 数据结束日期,绑定实盘参数“交易日期”\n# train_update_days=250, # 更新周期,按交易日计算,每多少天更新一次\n# train_data_min_days=250, # 最小数据天数,按交易日计算,所以第一个滚动的结束日期是 从开始日期到开始日期+最小数据天数\n# train_data_max_days=250, # 最大数据天数,按交易日计算,0,表示没有限制,否则每一个滚动的开始日期=max(此滚动的结束日期-最大数据天数, 开始日期\n# train_update_days=250, # 更新周期,按交易日计算,每多少天更新一次\n# train_data_min_days=250*3, # 最小数据天数,按交易日计算,所以第一个滚动的结束日期是 从开始日期到开始日期+最小数据天数\n# train_data_max_days=250*5, # 最大数据天数,按交易日计算,0,表示没有限制,否则每一个滚动的开始日期=max(此滚动的结束日期-最大数据天数, 开始日期\n# train_update_days=120, # 更新周期,按交易日计算,每多少天更新一次\n# train_data_min_days=120, # 最小数据天数,按交易日计算,所以第一个滚动的结束日期是 从开始日期到开始日期+最小数据天数\n# train_data_max_days=120, # 最大数据天数,按交易日计算,0,表示没有限制,否则每一个滚动的开始日期=max(此滚动的结束日期-最大数据天数, 开始日期\n# train_update_days=120, # 更新周期,按交易日计算,每多少天更新一次,代表每次滚动测试集长度\n# train_data_min_days=60, # 最小数据天数,按交易日计算,代表滚动训练集最小长度,即首次滚动训练集长度,所以 第一个滚动的日期区间 是从 开始日期 到 开始日期+最小数据天数\n# train_data_max_days=60, # 最大数据天数,按交易日计算,代表滚动训练集最大长度,即滚动几次后的滚动训练集长度,0,表示没有限制,否则 每一个滚动的开始日期 = max(此滚动的结束日期-最大数据天数, 开始日期\n train_update_days=train_update_days, # 更新周期,按交易日计算,每多少天更新一次,代表每次滚动测试集长度\n train_data_min_days=train_data_min_days, # 最小数据天数,按交易日计算,代表 滚动训练集最小长度,即 首次滚动训练集长度,所以 第一个滚动的日期区间 是从 开始日期 到 开始日期+最小数据天数\n train_data_max_days=train_data_max_days, # 最大数据天数,按交易日计算,代表 滚动训练集最大长度,即 滚动几次后的滚动训练集长度,0,表示没有限制,否则 每一个滚动的开始日期 = max(此滚动的结束日期-最大数据天数, 开始日期\n train_update_days_for_live=train_update_days, # 模拟实盘模式下的更新周期,按交易日计算,每多少天更新一次。如果需要在模拟实盘阶段使用不同的模型更新周期,可以设置这个参数。赋值为None的时候,实盘更新周期与回测更新周期一致。\n rolling_count_for_live=1, # 实盘模式下滚动次数,模拟实盘模式下,取最后多少次滚动。一般在模拟实盘模式下,只用到最后一次滚动训练的模型,这里可以设置为1;如果你的滚动训练数据时间段很短,以至于期间可能没有训练数据,这里可以设置大一点。0表示没有限制\n):\n \n # 合并 滚动预测结果 函数\n # 输入端:\n # input_1:滚动预测结果列表,为 每次滚动运行对应的 [预测模块结果,预测集开始日期] 组成的列表\n # 输出端:\n # data:携带策略参数的合并预测结果字典\n # instrument_data:合并证券代码数据字典\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 \n # 合并 滚动预测结果列表\n # 循环读取 输入端 input_1 滚动预测结果列表,合并生成其中 预测模块结果列表\n df_list = []\n for ds in input_1: # ds 为 input_1 成员列表,即 每次滚动运行对应的 [预测模块结果,预测集开始日期]\n ds0 = ds[0].read() # ds0 为 每次滚动运行对应的 预测模块结果(即输出端),数据帧 类型\n ds0['pred'] = ds0['pred'].set_index('date').loc[ds[1]:].reset_index() # ds1 为 每次滚动运行对应的 预测集开始日期 \n df_list.append(ds0)\n# print('df_list:\\n',df_list)\n\n # 合并预测结果数据帧:由 合并的预测模块结果列表 纵向合并 形成\n# df = pd.concat(df_list)\n df = pd.concat([dfl['pred'] for dfl in df_list])\n \n # 合并证券代码数据字典:按 证券代码列表模块 输出端 字典结构 组装生成 合并证券代码数据字典\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\n # 携带策略参数的合并预测结果字典:按 策略参数配置模块 输出端 字典结构 组装生成 携带参数的预测结果字典\n df_dict = {'pred': df, # df 为 合并预测结果数据帧\n 'stock_count': df_list[0]['stock_count'],\n 'hold_days': df_list[0]['hold_days'],\n 'min_cash_per_instrument': df_list[0]['min_cash_per_instrument'],\n 'stop_loss': df_list[0]['stop_loss'], \n 'target_up': df_list[0]['target_up'],\n 'target_up_draw_down': df_list[0]['target_up_draw_down']\n }\n# return Outputs(data=DataSource.write_df(df), instrument_data=DataSource.write_pickle(instrument_data))\n return Outputs(data=DataSource.write_pickle(df_dict), instrument_data=DataSource.write_pickle(instrument_data))\n\n # 生成 滚动日期区间字典列表 函数\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 # 起止日期之间的交易日历列表\n tdays = list(D.trading_days(market=trading_days_market, start_date=start_date, end_date=end_date)['date'])\n \n # 检测是否实盘模式\n is_live_run = T.live_run_param('trading_date', None) is not None\n\n # 实盘模式数据更新天数处理\n if is_live_run and train_update_days_for_live:\n train_update_days = train_update_days_for_live\n\n # 生成 滚动日期区间字典列表\n rollings = []\n train_end_date = train_data_min_days\n \n while train_end_date < len(tdays):\n \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 \n # 滚动日期区间字典列表 追加 滚动日期区间字典\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 \n train_end_date += train_update_days\n\n if not rollings:\n raise Exception('没有滚动需要执行,请检查配置')\n\n # 实盘模式滚动次数处理:取最后 rolling_count_for_live 次滚动\n if is_live_run and rolling_count_for_live:\n rollings = rollings[-rolling_count_for_live:]\n\n return rollings\n\n # bq_graph 是 可视化图 对象,总结了 可视化的所有模块 和 每个模块涉及的参数\n g = bq_graph\n\n # 调用 gen_rolling_dates 函数 生成 滚动日期区间字典列表\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 # 滚动运行:仅 滚动训练和预测,禁用 回测 模块,即 回测 模块 不参与 滚动运行\n results = [] # 滚动运行结果列表\n # 遍历 滚动日期区间字典列表,逐次 滚动运行\n for rolling in rolling_dates:\n \n # 生成 本次滚动训练 参数字典\n \n parameters = {}\n \n # 先禁用 回测模块\n parameters[trade_mid + '.__enabled__'] = False\n \n # 生成 本次滚动 训练 参数:训练集和测试集 起止日期\n # 模拟实盘模式下,在一个更新周期内,训练集 --> 起止日期不变,测试集 --> 开始日期不变,结束日期保持最新\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 \n # 打印 本次滚动 训练 参数字典\n# print('------ rolling_train:', parameters)\n# print(time.ctime(), '------ 滚动训练参数字典:', parameters, '\\n')\n print(time.strftime(\"%Y-%m-%d %H:%M:%S\",time.localtime()), '滚动训练参数字典:\\n', parameters, '\\n')\n \n # 执行 本次滚动运行,并记录 本次滚动运行结果字典\n # 按 本次滚动 训练 参数字典 parameters 运行 可视化图 对象 g,本次滚动运行结果字典 计入 result\n # 本次滚动运行结果字典 result 为 可视化图 对象 g 的 组成模块 及其 运行结果 组成的 字典\n # results.append(g.run(parameters))\n result = g.run(parameters) # 记录 本次滚动运行结果字典\n \n # 生成 滚动运行结果字典列表\n if not result is None: # result 非空\n results.append(result) # 本次滚动运行结果字典 计入 滚动运行结果字典列表\n else:\n raise Exception('------ 本次滚动运行结果为空,请检查配置!') \n \n print('\\n滚动运行结果字典列表 rolling_train_results 长度:', len(results), '\\n') \n# print('------ 滚动运行结果字典列表 rolling_train_results 长度:', len(results)) \n# print('------ rolling_train_results:', results) \n\n # 调用 合并函数 merge_datasources 函数,合并 滚动预测结果,生成 合并函数 模块对象 mx\n # 合并函数 输入端 input_1 基于 滚动运行结果列表 results 构建\n if not have_benchmark_factor: # 无 大盘风控因子模块,预测模块 对应 输出端 为 predictions\n mx = M.cached.v3(run = merge_datasources, \n input_1 = [[result[predict_mid].predictions, result[test_instruments_mid].data.read_pickle()['start_date']] \n for result in results])\n else: # 有 大盘风控因子模块,预测模块 对应 输出端 为 data\n mx = M.cached.v3(run = merge_datasources, \n input_1 = [[result[predict_mid].data, result[test_instruments_mid].data.read_pickle()['start_date']] \n# input_1 = [[result[predict_mid].data.read()['pred'], result[test_instruments_mid].data.read_pickle()['start_date']] \n for result in results])\n \n # 根据 合并函数 模块对象 mx 生成 回测模块 参数字典\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 # 回测:按 回测模块 参数字典 parameters 运行 可视化图 对象 g,回测 结果 计入 trade\n trade = g.run(parameters)\n\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":"-160"},{"name":"input_1","node_id":"-160"},{"name":"input_2","node_id":"-160"},{"name":"input_3","node_id":"-160"}],"output_ports":[{"name":"result","node_id":"-160"}],"cacheable":false,"seq_num":24,"comment":"","comment_collapsed":true},{"node_id":"-184","module_id":"BigQuantSpace.join.join-v3","parameters":[{"name":"on","value":"date","type":"Literal","bound_global_parameter":null},{"name":"how","value":"right","type":"Literal","bound_global_parameter":null},{"name":"sort","value":"False","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"data1","node_id":"-184"},{"name":"data2","node_id":"-184"}],"output_ports":[{"name":"data","node_id":"-184"}],"cacheable":true,"seq_num":12,"comment":"","comment_collapsed":true},{"node_id":"-188","module_id":"BigQuantSpace.chinaa_stock_filter.chinaa_stock_filter-v1","parameters":[{"name":"index_constituent_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E4%B8%8A%E8%AF%8150%22%2C%22displayValue%22%3A%22%E4%B8%8A%E8%AF%8150%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%B2%AA%E6%B7%B1300%22%2C%22displayValue%22%3A%22%E6%B2%AA%E6%B7%B1300%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%B8%AD%E8%AF%81500%22%2C%22displayValue%22%3A%22%E4%B8%AD%E8%AF%81500%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%B8%AD%E8%AF%81800%22%2C%22displayValue%22%3A%22%E4%B8%AD%E8%AF%81800%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%B8%8A%E8%AF%81180%22%2C%22displayValue%22%3A%22%E4%B8%8A%E8%AF%81180%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%B8%AD%E8%AF%81100%22%2C%22displayValue%22%3A%22%E4%B8%AD%E8%AF%81100%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%B7%B1%E8%AF%81100%22%2C%22displayValue%22%3A%22%E6%B7%B1%E8%AF%81100%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"board_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E4%B8%8A%E8%AF%81%E4%B8%BB%E6%9D%BF%22%2C%22displayValue%22%3A%22%E4%B8%8A%E8%AF%81%E4%B8%BB%E6%9D%BF%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%B7%B1%E8%AF%81%E4%B8%BB%E6%9D%BF%22%2C%22displayValue%22%3A%22%E6%B7%B1%E8%AF%81%E4%B8%BB%E6%9D%BF%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%88%9B%E4%B8%9A%E6%9D%BF%22%2C%22displayValue%22%3A%22%E5%88%9B%E4%B8%9A%E6%9D%BF%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%A7%91%E5%88%9B%E6%9D%BF%22%2C%22displayValue%22%3A%22%E7%A7%91%E5%88%9B%E6%9D%BF%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"industry_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E4%BA%A4%E9%80%9A%E8%BF%90%E8%BE%93%22%2C%22displayValue%22%3A%22%E4%BA%A4%E9%80%9A%E8%BF%90%E8%BE%93%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%BC%91%E9%97%B2%E6%9C%8D%E5%8A%A1%22%2C%22displayValue%22%3A%22%E4%BC%91%E9%97%B2%E6%9C%8D%E5%8A%A1%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%BC%A0%E5%AA%92%2F%E4%BF%A1%E6%81%AF%E6%9C%8D%E5%8A%A1%22%2C%22displayValue%22%3A%22%E4%BC%A0%E5%AA%92%2F%E4%BF%A1%E6%81%AF%E6%9C%8D%E5%8A%A1%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%85%AC%E7%94%A8%E4%BA%8B%E4%B8%9A%22%2C%22displayValue%22%3A%22%E5%85%AC%E7%94%A8%E4%BA%8B%E4%B8%9A%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%86%9C%E6%9E%97%E7%89%A7%E6%B8%94%22%2C%22displayValue%22%3A%22%E5%86%9C%E6%9E%97%E7%89%A7%E6%B8%94%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%8C%96%E5%B7%A5%22%2C%22displayValue%22%3A%22%E5%8C%96%E5%B7%A5%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%8C%BB%E8%8D%AF%E7%94%9F%E7%89%A9%22%2C%22displayValue%22%3A%22%E5%8C%BB%E8%8D%AF%E7%94%9F%E7%89%A9%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%95%86%E4%B8%9A%E8%B4%B8%E6%98%93%22%2C%22displayValue%22%3A%22%E5%95%86%E4%B8%9A%E8%B4%B8%E6%98%93%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%9B%BD%E9%98%B2%E5%86%9B%E5%B7%A5%22%2C%22displayValue%22%3A%22%E5%9B%BD%E9%98%B2%E5%86%9B%E5%B7%A5%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%AE%B6%E7%94%A8%E7%94%B5%E5%99%A8%22%2C%22displayValue%22%3A%22%E5%AE%B6%E7%94%A8%E7%94%B5%E5%99%A8%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%BB%BA%E7%AD%91%E6%9D%90%E6%96%99%2F%E5%BB%BA%E7%AD%91%E5%BB%BA%E6%9D%90%22%2C%22displayValue%22%3A%22%E5%BB%BA%E7%AD%91%E6%9D%90%E6%96%99%2F%E5%BB%BA%E7%AD%91%E5%BB%BA%E6%9D%90%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%BB%BA%E7%AD%91%E8%A3%85%E9%A5%B0%22%2C%22displayValue%22%3A%22%E5%BB%BA%E7%AD%91%E8%A3%85%E9%A5%B0%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%88%BF%E5%9C%B0%E4%BA%A7%22%2C%22displayValue%22%3A%22%E6%88%BF%E5%9C%B0%E4%BA%A7%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%9C%89%E8%89%B2%E9%87%91%E5%B1%9E%22%2C%22displayValue%22%3A%22%E6%9C%89%E8%89%B2%E9%87%91%E5%B1%9E%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%9C%BA%E6%A2%B0%E8%AE%BE%E5%A4%87%22%2C%22displayValue%22%3A%22%E6%9C%BA%E6%A2%B0%E8%AE%BE%E5%A4%87%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%B1%BD%E8%BD%A6%2F%E4%BA%A4%E8%BF%90%E8%AE%BE%E5%A4%87%22%2C%22displayValue%22%3A%22%E6%B1%BD%E8%BD%A6%2F%E4%BA%A4%E8%BF%90%E8%AE%BE%E5%A4%87%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%94%B5%E5%AD%90%22%2C%22displayValue%22%3A%22%E7%94%B5%E5%AD%90%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%94%B5%E6%B0%94%E8%AE%BE%E5%A4%87%22%2C%22displayValue%22%3A%22%E7%94%B5%E6%B0%94%E8%AE%BE%E5%A4%87%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%BA%BA%E7%BB%87%E6%9C%8D%E8%A3%85%22%2C%22displayValue%22%3A%22%E7%BA%BA%E7%BB%87%E6%9C%8D%E8%A3%85%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%BB%BC%E5%90%88%22%2C%22displayValue%22%3A%22%E7%BB%BC%E5%90%88%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E8%AE%A1%E7%AE%97%E6%9C%BA%22%2C%22displayValue%22%3A%22%E8%AE%A1%E7%AE%97%E6%9C%BA%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E8%BD%BB%E5%B7%A5%E5%88%B6%E9%80%A0%22%2C%22displayValue%22%3A%22%E8%BD%BB%E5%B7%A5%E5%88%B6%E9%80%A0%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%80%9A%E4%BF%A1%22%2C%22displayValue%22%3A%22%E9%80%9A%E4%BF%A1%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%87%87%E6%8E%98%22%2C%22displayValue%22%3A%22%E9%87%87%E6%8E%98%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%92%A2%E9%93%81%22%2C%22displayValue%22%3A%22%E9%92%A2%E9%93%81%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%93%B6%E8%A1%8C%22%2C%22displayValue%22%3A%22%E9%93%B6%E8%A1%8C%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%9D%9E%E9%93%B6%E9%87%91%E8%9E%8D%22%2C%22displayValue%22%3A%22%E9%9D%9E%E9%93%B6%E9%87%91%E8%9E%8D%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%A3%9F%E5%93%81%E9%A5%AE%E6%96%99%22%2C%22displayValue%22%3A%22%E9%A3%9F%E5%93%81%E9%A5%AE%E6%96%99%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"st_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E6%AD%A3%E5%B8%B8%22%2C%22displayValue%22%3A%22%E6%AD%A3%E5%B8%B8%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22ST%22%2C%22displayValue%22%3A%22ST%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22*ST%22%2C%22displayValue%22%3A%22*ST%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%9A%82%E5%81%9C%E4%B8%8A%E5%B8%82%22%2C%22displayValue%22%3A%22%E6%9A%82%E5%81%9C%E4%B8%8A%E5%B8%82%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"delist_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E9%80%80%E5%B8%82%22%2C%22displayValue%22%3A%22%E9%80%80%E5%B8%82%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%9D%9E%E9%80%80%E5%B8%82%22%2C%22displayValue%22%3A%22%E9%9D%9E%E9%80%80%E5%B8%82%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"output_left_data","value":"False","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-188"}],"output_ports":[{"name":"data","node_id":"-188"},{"name":"left_data","node_id":"-188"}],"cacheable":true,"seq_num":11,"comment":"","comment_collapsed":true},{"node_id":"-198","module_id":"BigQuantSpace.chinaa_stock_filter.chinaa_stock_filter-v1","parameters":[{"name":"index_constituent_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E4%B8%8A%E8%AF%8150%22%2C%22displayValue%22%3A%22%E4%B8%8A%E8%AF%8150%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%B2%AA%E6%B7%B1300%22%2C%22displayValue%22%3A%22%E6%B2%AA%E6%B7%B1300%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%B8%AD%E8%AF%81500%22%2C%22displayValue%22%3A%22%E4%B8%AD%E8%AF%81500%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%B8%AD%E8%AF%81800%22%2C%22displayValue%22%3A%22%E4%B8%AD%E8%AF%81800%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%B8%8A%E8%AF%81180%22%2C%22displayValue%22%3A%22%E4%B8%8A%E8%AF%81180%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%B8%AD%E8%AF%81100%22%2C%22displayValue%22%3A%22%E4%B8%AD%E8%AF%81100%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%B7%B1%E8%AF%81100%22%2C%22displayValue%22%3A%22%E6%B7%B1%E8%AF%81100%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"board_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E4%B8%8A%E8%AF%81%E4%B8%BB%E6%9D%BF%22%2C%22displayValue%22%3A%22%E4%B8%8A%E8%AF%81%E4%B8%BB%E6%9D%BF%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%B7%B1%E8%AF%81%E4%B8%BB%E6%9D%BF%22%2C%22displayValue%22%3A%22%E6%B7%B1%E8%AF%81%E4%B8%BB%E6%9D%BF%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%88%9B%E4%B8%9A%E6%9D%BF%22%2C%22displayValue%22%3A%22%E5%88%9B%E4%B8%9A%E6%9D%BF%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%A7%91%E5%88%9B%E6%9D%BF%22%2C%22displayValue%22%3A%22%E7%A7%91%E5%88%9B%E6%9D%BF%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"industry_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E4%BA%A4%E9%80%9A%E8%BF%90%E8%BE%93%22%2C%22displayValue%22%3A%22%E4%BA%A4%E9%80%9A%E8%BF%90%E8%BE%93%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%BC%91%E9%97%B2%E6%9C%8D%E5%8A%A1%22%2C%22displayValue%22%3A%22%E4%BC%91%E9%97%B2%E6%9C%8D%E5%8A%A1%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E4%BC%A0%E5%AA%92%2F%E4%BF%A1%E6%81%AF%E6%9C%8D%E5%8A%A1%22%2C%22displayValue%22%3A%22%E4%BC%A0%E5%AA%92%2F%E4%BF%A1%E6%81%AF%E6%9C%8D%E5%8A%A1%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%85%AC%E7%94%A8%E4%BA%8B%E4%B8%9A%22%2C%22displayValue%22%3A%22%E5%85%AC%E7%94%A8%E4%BA%8B%E4%B8%9A%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%86%9C%E6%9E%97%E7%89%A7%E6%B8%94%22%2C%22displayValue%22%3A%22%E5%86%9C%E6%9E%97%E7%89%A7%E6%B8%94%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%8C%96%E5%B7%A5%22%2C%22displayValue%22%3A%22%E5%8C%96%E5%B7%A5%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%8C%BB%E8%8D%AF%E7%94%9F%E7%89%A9%22%2C%22displayValue%22%3A%22%E5%8C%BB%E8%8D%AF%E7%94%9F%E7%89%A9%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%95%86%E4%B8%9A%E8%B4%B8%E6%98%93%22%2C%22displayValue%22%3A%22%E5%95%86%E4%B8%9A%E8%B4%B8%E6%98%93%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%9B%BD%E9%98%B2%E5%86%9B%E5%B7%A5%22%2C%22displayValue%22%3A%22%E5%9B%BD%E9%98%B2%E5%86%9B%E5%B7%A5%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%AE%B6%E7%94%A8%E7%94%B5%E5%99%A8%22%2C%22displayValue%22%3A%22%E5%AE%B6%E7%94%A8%E7%94%B5%E5%99%A8%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%BB%BA%E7%AD%91%E6%9D%90%E6%96%99%2F%E5%BB%BA%E7%AD%91%E5%BB%BA%E6%9D%90%22%2C%22displayValue%22%3A%22%E5%BB%BA%E7%AD%91%E6%9D%90%E6%96%99%2F%E5%BB%BA%E7%AD%91%E5%BB%BA%E6%9D%90%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E5%BB%BA%E7%AD%91%E8%A3%85%E9%A5%B0%22%2C%22displayValue%22%3A%22%E5%BB%BA%E7%AD%91%E8%A3%85%E9%A5%B0%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%88%BF%E5%9C%B0%E4%BA%A7%22%2C%22displayValue%22%3A%22%E6%88%BF%E5%9C%B0%E4%BA%A7%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%9C%89%E8%89%B2%E9%87%91%E5%B1%9E%22%2C%22displayValue%22%3A%22%E6%9C%89%E8%89%B2%E9%87%91%E5%B1%9E%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%9C%BA%E6%A2%B0%E8%AE%BE%E5%A4%87%22%2C%22displayValue%22%3A%22%E6%9C%BA%E6%A2%B0%E8%AE%BE%E5%A4%87%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%B1%BD%E8%BD%A6%2F%E4%BA%A4%E8%BF%90%E8%AE%BE%E5%A4%87%22%2C%22displayValue%22%3A%22%E6%B1%BD%E8%BD%A6%2F%E4%BA%A4%E8%BF%90%E8%AE%BE%E5%A4%87%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%94%B5%E5%AD%90%22%2C%22displayValue%22%3A%22%E7%94%B5%E5%AD%90%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%94%B5%E6%B0%94%E8%AE%BE%E5%A4%87%22%2C%22displayValue%22%3A%22%E7%94%B5%E6%B0%94%E8%AE%BE%E5%A4%87%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%BA%BA%E7%BB%87%E6%9C%8D%E8%A3%85%22%2C%22displayValue%22%3A%22%E7%BA%BA%E7%BB%87%E6%9C%8D%E8%A3%85%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E7%BB%BC%E5%90%88%22%2C%22displayValue%22%3A%22%E7%BB%BC%E5%90%88%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E8%AE%A1%E7%AE%97%E6%9C%BA%22%2C%22displayValue%22%3A%22%E8%AE%A1%E7%AE%97%E6%9C%BA%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E8%BD%BB%E5%B7%A5%E5%88%B6%E9%80%A0%22%2C%22displayValue%22%3A%22%E8%BD%BB%E5%B7%A5%E5%88%B6%E9%80%A0%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%80%9A%E4%BF%A1%22%2C%22displayValue%22%3A%22%E9%80%9A%E4%BF%A1%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%87%87%E6%8E%98%22%2C%22displayValue%22%3A%22%E9%87%87%E6%8E%98%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%92%A2%E9%93%81%22%2C%22displayValue%22%3A%22%E9%92%A2%E9%93%81%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%93%B6%E8%A1%8C%22%2C%22displayValue%22%3A%22%E9%93%B6%E8%A1%8C%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%9D%9E%E9%93%B6%E9%87%91%E8%9E%8D%22%2C%22displayValue%22%3A%22%E9%9D%9E%E9%93%B6%E9%87%91%E8%9E%8D%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%A3%9F%E5%93%81%E9%A5%AE%E6%96%99%22%2C%22displayValue%22%3A%22%E9%A3%9F%E5%93%81%E9%A5%AE%E6%96%99%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"st_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E6%AD%A3%E5%B8%B8%22%2C%22displayValue%22%3A%22%E6%AD%A3%E5%B8%B8%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22ST%22%2C%22displayValue%22%3A%22ST%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22*ST%22%2C%22displayValue%22%3A%22*ST%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E6%9A%82%E5%81%9C%E4%B8%8A%E5%B8%82%22%2C%22displayValue%22%3A%22%E6%9A%82%E5%81%9C%E4%B8%8A%E5%B8%82%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"delist_cond","value":"%7B%22enumItems%22%3A%5B%7B%22value%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22displayValue%22%3A%22%E5%85%A8%E9%83%A8%22%2C%22selected%22%3Atrue%7D%2C%7B%22value%22%3A%22%E9%80%80%E5%B8%82%22%2C%22displayValue%22%3A%22%E9%80%80%E5%B8%82%22%2C%22selected%22%3Afalse%7D%2C%7B%22value%22%3A%22%E9%9D%9E%E9%80%80%E5%B8%82%22%2C%22displayValue%22%3A%22%E9%9D%9E%E9%80%80%E5%B8%82%22%2C%22selected%22%3Afalse%7D%5D%7D","type":"Literal","bound_global_parameter":null},{"name":"output_left_data","value":"False","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-198"}],"output_ports":[{"name":"data","node_id":"-198"},{"name":"left_data","node_id":"-198"}],"cacheable":true,"seq_num":14,"comment":"","comment_collapsed":true},{"node_id":"-2503","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"\n# #号开始的表示注释,注释需单独一行\n# 多个特征,每行一个,可以包含基础特征和衍生特征,特征须为本平台特征\nopen=open_0\nclose=close_0\nhigh=high_0\nlow=low_0\nvolume=volume_0\namount= amount_0\nvwap=amount / volume\nret=close_0 / close_1 - 1\ndtm=where(open<=delay(open,1),0,max((high-open),(open-delay(open,1))))\ndbm=where(open>=delay(open,1),0,max((open-low),(open-delay(open,1))))\ntr=max(max(high-low,abs(high-delay(close,1))),abs(low-delay(close,1)))\nhd=high-delay(high,1)\nld=delay(low,1)-low \n","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"-2503"}],"output_ports":[{"name":"data","node_id":"-2503"}],"cacheable":true,"seq_num":33,"comment":"基本因子","comment_collapsed":false},{"node_id":"-22272","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"# #号开始的表示注释\n# 多个特征,每行一个,可以包含基础特征和衍生特征\nindustry_sw_level1_0\nmarket_cap_float_0\n#market_cap_0","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"-22272"}],"output_ports":[{"name":"data","node_id":"-22272"}],"cacheable":true,"seq_num":37,"comment":"中性化因子列表","comment_collapsed":false},{"node_id":"-22276","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"# #号开始的表示注释,注释需单独一行\n# 多个特征,每行一个,可以包含基础特征和衍生特征,特征须为本平台特征\nalpha_001=-1*correlation(rank(delta(log(volume),1)),rank(((close-open)/open)),6)\nalpha_002=-1*delta((((close-low)-(high-close))/(high-low)),1) \nalpha_003=sum(where(close==delay(close,1),0,close)-where(close>delay(close,1),min(low,delay(close,1)),max(high,delay(close,1))),6)\n#alpha_004=where(((sum(close,8)/8)+std(close,8))<(sum(close,2)/2),-1,where((sum(close,2)/2)<((sum(close,8)/8)-std(close,8)),1,where((volume/mean(volume,20))>=1,1,-1)))\n#alpha_005=-1*ts_max(correlation(ts_rank(volume,5),ts_rank(high,5),5),3)\n#alpha_006=-1*rank(sign(delta((((open*0.85)+(high*0.15))),4)))\n#alpha_007=((rank(max((vwap-close),3))+rank(min((vwap-close),3)))*rank(delta(volume,3)))\n#alpha_008=-1*rank(delta(((((high+low)/2)*0.2)+(vwap*0.8)),4))\n\n","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"-22276"}],"output_ports":[{"name":"data","node_id":"-22276"}],"cacheable":true,"seq_num":42,"comment":"国泰君安短周期选股因子","comment_collapsed":false},{"node_id":"-924","module_id":"BigQuantSpace.features_short.features_short-v1","parameters":[],"input_ports":[{"name":"input_1","node_id":"-924"}],"output_ports":[{"name":"data_1","node_id":"-924"}],"cacheable":true,"seq_num":43,"comment":"基本因子","comment_collapsed":false},{"node_id":"-1720","module_id":"BigQuantSpace.features_append.features_append-v1","parameters":[],"input_ports":[{"name":"input_1","node_id":"-1720"},{"name":"input_2","node_id":"-1720"}],"output_ports":[{"name":"data_1","node_id":"-1720"}],"cacheable":true,"seq_num":44,"comment":"","comment_collapsed":true},{"node_id":"-1673","module_id":"BigQuantSpace.features_short.features_short-v1","parameters":[],"input_ports":[{"name":"input_1","node_id":"-1673"}],"output_ports":[{"name":"data_1","node_id":"-1673"}],"cacheable":true,"seq_num":45,"comment":"国泰君安短周期选股因子","comment_collapsed":false},{"node_id":"-1962","module_id":"BigQuantSpace.features_append.features_append-v1","parameters":[],"input_ports":[{"name":"input_1","node_id":"-1962"},{"name":"input_2","node_id":"-1962"}],"output_ports":[{"name":"data_1","node_id":"-1962"}],"cacheable":true,"seq_num":46,"comment":"","comment_collapsed":true},{"node_id":"-4550","module_id":"BigQuantSpace.dl_layer_input.dl_layer_input-v1","parameters":[{"name":"shape","value":"10,12","type":"Literal","bound_global_parameter":null},{"name":"batch_shape","value":"","type":"Literal","bound_global_parameter":null},{"name":"dtype","value":"float32","type":"Literal","bound_global_parameter":null},{"name":"sparse","value":"False","type":"Literal","bound_global_parameter":null},{"name":"name","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"inputs","node_id":"-4550"}],"output_ports":[{"name":"data","node_id":"-4550"}],"cacheable":false,"seq_num":18,"comment":"","comment_collapsed":true},{"node_id":"-1540","module_id":"BigQuantSpace.dl_model_predict.dl_model_predict-v1","parameters":[{"name":"batch_size","value":"32","type":"Literal","bound_global_parameter":null},{"name":"n_gpus","value":0,"type":"Literal","bound_global_parameter":null},{"name":"verbose","value":"2:每个epoch输出一行记录","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"trained_model","node_id":"-1540"},{"name":"input_data","node_id":"-1540"}],"output_ports":[{"name":"data","node_id":"-1540"}],"cacheable":true,"seq_num":21,"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 \n pred_label = input_1.read_pickle() # 大盘因子预测标签\n df = input_2.read_df() # 大盘因子测试集,含实际标签\n# window = 7 # m18.data.shape[1]\n# df = df.head(len(df) - window)\n \n df = pd.DataFrame({'pred_label':pred_label[:,0], 'date':df.date, 'label':df.label})\n df.sort_values(['date','pred_label'],inplace=True, ascending=[True,False])\n \n return Outputs(data_1=DataSource.write_df(df), data_2=None, data_3=None)\n\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":22,"comment":"绑定 大盘因子测试集 预测标签 和 实际标签","comment_collapsed":false},{"node_id":"-3887","module_id":"BigQuantSpace.index_feature_extract.index_feature_extract-v3","parameters":[{"name":"before_days","value":"6000","type":"Literal","bound_global_parameter":null},{"name":"index","value":"000300.HIX","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-3887"},{"name":"input_2","node_id":"-3887"}],"output_ports":[{"name":"data_1","node_id":"-3887"},{"name":"data_2","node_id":"-3887"}],"cacheable":true,"seq_num":28,"comment":"大盘标签","comment_collapsed":false},{"node_id":"-3725","module_id":"BigQuantSpace.cached.cached-v3","parameters":[{"name":"run","value":"# Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端\n# 输入端:\n # input_1:接 输入层Input\n # input_2:基于 测试集代码列表 抽取的 指数特征(大盘因子)数据集\n # input_3:接 测试集代码列表\n# 输出端:\n # data_1:大盘因子滑动窗口训练集,数组类型字典\n # data_2:大盘因子滑动窗口测试集,数组类型字典\n # data_3:大盘因子测试集,丢弃第一个 前补 window,完整保留原始列 \n \ndef bigquant_run(input_1, input_2, input_3):\n # 示例代码如下。在这里编写您的代码\n\n #window = 7 # m18.data.shape[1] # 每个input的长度\n #window = input_1.shape[1] # 每个input的长度,对应 LSTM time_steps\n window = input_1 # 每个input的长度,对应 LSTM time_steps\n \n # 指数特征(大盘因子)数据集:基于 测试集 代码列表 时间区间,但多取了部分天数用于 训练集 \n df = input_2.read_df() \n \n # 取 测试集开始日期 为 大盘因子测试集开始日期 \n start_date = input_3.read()['start_date'] \n \n # 大盘因子名称去除前缀'bm_'\n cols = df.columns\n df.columns = [(col.partition('bm_')[2] if col.startswith('b') else col) for col in cols ]\n \n # 大盘因子数据集 去除 ['date','instrument','label'] 三列\n df_x = df[df.columns.difference(['date','instrument','label'] )] \n\n train_df = df.query(\"date<@start_date\") # 大盘因子训练集\n test_df = df.query(\"date>=@start_date\") # 大盘因子测试集\n# train_df = df[df['date'] < start_date] # 大盘因子 训练集\n# test_df = df[df['date'] >= start_date] # 大盘因子 预测集\n\n# print('\\n')\n print('\\n大盘因子预处理模块:标准化、滑动窗口、滑动窗口型大盘因子数据集 拆分为 训练集和测试集')\n print('大盘因子预处理前训练集train_df长度:',len(train_df))\n print('大盘因子预处理前测试集test_df长度:',len(test_df))\n \n test_df = pd.concat([train_df.tail(window),test_df]) # 大盘因子测试集 向前补一个 window\n \n# if len(train_df) < train_len:\n# print('大盘因子数据集长度不足!')\n# print('请调增‘指数特征抽取模块’的‘行情数据向前抽取天数’,或调减‘输入层Input’ shape 长度参数,或调减 train_length 参数!')\n# return\n \n train_x = train_df[train_df.columns.difference(['date','instrument','label'] )] # 大盘因子训练集 x,去除 ['date','instrument','label'] 三列\n train_y = train_df['label'] # 大盘因子训练集 y,仅 ['label'] 列\n test_x = test_df[test_df.columns.difference(['date','instrument','label'] )] # 大盘因子测试集 x,去除 ['date','instrument','label'] 三列\n \n# print('大盘因子测试集test_x长度:',len(test_x))\n \n # 数据处理:标准化\n from sklearn.preprocessing import scale\n train_x = scale(train_x) \n test_x = scale(test_x)\n \n # 数据处理:窗口化,生成 滑动窗口数据,基于前一个 滑动窗口(window) 训练和预测 \n train_x = [(train_x[i:i+window]) for i in range(len(train_x)-window)] # x 元素形状:(window,len(fields))\n train_y = [train_y[window+i:window+i+1].values[0] for i in range(len(train_y)-window)]\n \n test_x = [(test_x[i:i+window]) for i in range(len(test_x)-window)] # x 元素形状:(window,len(fields))\n\n# train_x = [scale(train_x[i:i+window]) for i in range(len(train_x)-window)] # x 元素形状:(window,len(fields))\n# test_x = [scale(test_x[i:i+window]) for i in range(len(test_x)-window)] # x 元素形状:(window,len(fields))\n# train_x = [scale(train_x[i:i+window]) for i in range(len(train_x)-window-1)] # x 元素形状:(window,len(fields))\n# train_y = [train_y[window+i:window+i+1].values[0] for i in range(len(train_y)-window-1)]\n# train_x = [scale(train_x[i:i+window+1]) for i in range(len(train_x)-window)] # x 元素形状:(window,len(fields))\n# train_y = [train_y[window+i-1:window+i].values[0] for i in range(len(train_y)-window)]\n# train_y = [train_y[i:i+1].values[0] for i in range(len(train_y)-window)]\n# train_x = [scale(train_x[i:i+window]) for i in range(len(train_x))] # x 元素形状:(window,len(fields))\n# train_y = [train_y[i] for i in range(len(train_y)-window)]\n# train_y = [train_y[i] for i in range(len(train_y))]\n# test_x = [scale(df_x[i:i+window]) for i in range(len(df_x)-len(test_df),len(df_x)-window)] # x 元素形状:(window,len(fields))\n# test_x = [scale(df_x[i+1-window:i+1]) for i in range(len(df_x)-len(test_df),len(df_x))] # x 元素形状:(window,len(fields))\n# test_x = [scale(test_x[i:i+window]) for i in range(len(test_x))] # x 元素形状:(window,len(fields))\n\n # LSTM 训练和预测模块接受 数组类型字典 的输入\n train = {'x':np.array(train_x),'y':np.array(train_y)}\n test = {'x':np.array(test_x)}\n\n print('标准化及滑动窗口后大盘因子训练集train_x长度:',len(train_x))\n print('标准化及滑动窗口后大盘因子训练集train_y长度:',len(train_y))\n print('标准化及滑动窗口后大盘因子测试集test_x长度:',len(test_x), '\\n')\n# print('标准化及滑动窗口后大盘因子测试集test长度:',len(test['x']))\n \n data_1 = DataSource.write_pickle(train) # 大盘因子滑动窗口训练集,数组类型字典\n data_2 = DataSource.write_pickle(test) # 大盘因子滑动窗口测试集,数组类型字典\n data_3 = DataSource.write_df(test_df[window:]) # 大盘因子测试集,丢弃第一个 前补 window,完整保留原始列 \n# data_3 = DataSource.write_df(test_df) # 大盘因子测试集,完整保留原始列 \n return Outputs(data_1=data_1, data_2=data_2, data_3=data_3, data_4=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":"data_1,data_2,data_3,data_4","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-3725"},{"name":"input_2","node_id":"-3725"},{"name":"input_3","node_id":"-3725"}],"output_ports":[{"name":"data_1","node_id":"-3725"},{"name":"data_2","node_id":"-3725"},{"name":"data_3","node_id":"-3725"}],"cacheable":true,"seq_num":30,"comment":"标准化、滑动窗口、滑动窗口型大盘因子数据集 拆分为 训练集和测试集","comment_collapsed":false},{"node_id":"-2186","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"#开盘价/收盘价,最高价/收盘价,最低价/收盘价,收盘价日涨幅,交易量日涨幅,持仓量日涨幅,收盘价5日平均/收盘价,收盘价10日平均/收盘价,收盘价20日平均/收盘价,交易量5日平均/交易量,交易量10日平均/交易量,交易量20日平均/交易量,持仓量5日平均/持仓量,持仓量10日平均/持仓量,持仓量20日平均/持仓量,收盘价14日RSI指数\n# 20210831-1:169%\n# 20210831-1-ep800:176%\n# 20210831-1-ep1000:156%\n\nbm_close= close\nbm_open= open\nbm_high= high\nbm_low= low\nbm_amount= amount\nbm_volume= volume\n\nbm_amplitude= (high-low)/shift(close, 1)\nbm_max_amplitude_5= ts_max(bm_amplitude, 5)\nbm_min_amplitude_5= ts_min(bm_amplitude, 5)\n\n#bm_ret_5=close / shift(open, 5) - 1\n#bm_ret_10=close / shift(open, 10) - 1\n#bm_ret_20=close / shift(open, 20) - 1\nbm_mean_close_5= mean(close,5)\n#bm_mean_close_10= mean(close,10)\n#bm_mean_close_20= mean(close,20)\nbm_high_5= ts_max(high, 5)\nbm_low_5= ts_min(low, 5)\nbm_amount_5= sum(amount,5)\nbm_mean_amount_5= mean(amount,5)\n#bm_mean_amount_10= mean(amount,10)\nbm_volume_5= sum(volume,5)\nbm_mean_volume_5= mean(volume,5)\n#bm_mean_volume_10= mean(volume,10)\n\nbm_open_close= open / close\nbm_high_close= high / close\nbm_low_close= low / close\nbm_close_delta= close / shift(close, 1)\nbm_volume_delta= volume / shift(volume, 1)\nbm_mean_close_5_close= mean(close,5) / close\n#bm_mean_close_10_close= mean(close,10) / close\n#bm_mean_close_20_close= mean(close,20) / close\nbm_mean_volume_5_volume= mean(volume,5) / volume\n#bm_mean_volume_10_volume= mean(volume,10) / volume\n#bm_mean_volume_20_volume= mean(volume,20) / volume\n\nbm_rsi_6= ta_rsi(close, 6)\n#bm_rsi_14= ta_rsi(close, 14)\n#bm_cci_1= ta_cci(high, low, close, 1)\nbm_cci_5= ta_cci(high, low, close, 5)\n#bm_bias_1= ta_bias(close, 1)\nbm_bias_5= ta_bias(close, 5)\n#bm_bias_10= ta_bias(close, 10)\nbm_mom_1= ta_mom(close,1)\n#bm_mom_5= ta_mom(close,5)\n#bm_roc_1= ta_roc(close,1)\nbm_roc_5= ta_roc(close,5)\n#bm_willr_5= ta_willr(close, 5)\nbm_diff_close_sma= close /((ta_sma(close,30) + ta_sma(close,72)) / 2) -1\nbm_ma_long_5= ta_ma(close, 5, derive='long')\nbm_ma_short_5= ta_ma(close, 5, derive='short')\n#bm_ma_long_10= ta_ma(close, 10, derive='long')\n#bm_ma_short_10= ta_ma(close, 10, derive='short')\n#bm_ma_long_20= ta_ma(close, 20, derive='long')\n#bm_ma_short_20= ta_ma(close, 20, derive='short')\n\n#avg_mf_net_amount_5\n#rank_avg_mf_net_amount_5\n#rank_avg_mf_net_amount_0\n#mf_net_amount_xl_0\n#mf_net_amount_l_0\n#mf_net_amount_m_0\n#mf_net_amount_s_0\n#mf_net_amount_main_0\n#mf_net_pct_xl_0\n#mf_net_pct_l_0\n#mf_net_pct_m_0\n#mf_net_pct_s_0\n#mf_net_pct_main_0\n\n","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"-2186"}],"output_ports":[{"name":"data","node_id":"-2186"}],"cacheable":true,"seq_num":32,"comment":"大盘因子 bm_*","comment_collapsed":false},{"node_id":"-18645","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"#bm_label=shift(close, -5) / shift(close, 0) - 1 # 大盘因子标签\nbm_label=shift(close, -1) / shift(close, 0) - 1 # 大盘因子标签\n#bm_label=shift(close, -5) / shift(open, -1) - 1 # 大盘因子标签\n#bm_label=where(shift(close, -5) / shift(open, -1) - 1 < -0.002, 0, 1 )\n#bm_label=where(shift(close, -5) / shift(open, -1) - 1 < -0.002, 1, 0 )\n#bm_label=where(shift(close, -5) / shift(open, -1) - 1 < -0.002, -1, where(shift(close, -5) / shift(open, -1) - 1 > 0.01, 1, 0))\n#bm_label=where(shift(close, -5) / shift(open, -1) - 1 < -0.002, 2, where(shift(close, -5) / shift(open, -1) - 1 > 0.01, 1, 0))\n#bm_label=shift(close, -2) / shift(close, -1)-1\n#bm_label=shift(close, -2) / shift(open, -2)-1\n#bm_label=shift(close, -3) / shift(open, -1)-1\n#bm_label=shift(close, -4) / shift(open, -1)-1\n#bm_label=shift(close, -6) / shift(open, -1)-1\n","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"-18645"}],"output_ports":[{"name":"data","node_id":"-18645"}],"cacheable":true,"seq_num":34,"comment":"大盘标签 bm_label","comment_collapsed":false},{"node_id":"-989","module_id":"BigQuantSpace.dropnan.dropnan-v2","parameters":[],"input_ports":[{"name":"input_data","node_id":"-989"},{"name":"features","node_id":"-989"}],"output_ports":[{"name":"data","node_id":"-989"}],"cacheable":true,"seq_num":17,"comment":"","comment_collapsed":true},{"node_id":"-4208","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":"500","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-4208"},{"name":"features","node_id":"-4208"}],"output_ports":[{"name":"data","node_id":"-4208"}],"cacheable":true,"seq_num":29,"comment":"","comment_collapsed":true},{"node_id":"-4215","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":"True","type":"Literal","bound_global_parameter":null},{"name":"user_functions","value":"{}","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-4215"},{"name":"features","node_id":"-4215"}],"output_ports":[{"name":"data","node_id":"-4215"}],"cacheable":true,"seq_num":31,"comment":"","comment_collapsed":true},{"node_id":"-4228","module_id":"BigQuantSpace.delete_columns.delete_columns-v2","parameters":[{"name":"cols","value":"[]","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-4228"},{"name":"input_2","node_id":"-4228"}],"output_ports":[{"name":"data","node_id":"-4228"}],"cacheable":true,"seq_num":41,"comment":"","comment_collapsed":true},{"node_id":"-1628","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":"500","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-1628"},{"name":"features","node_id":"-1628"}],"output_ports":[{"name":"data","node_id":"-1628"}],"cacheable":true,"seq_num":48,"comment":"","comment_collapsed":true},{"node_id":"-1635","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":"True","type":"Literal","bound_global_parameter":null},{"name":"user_functions","value":"{}","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-1635"},{"name":"features","node_id":"-1635"}],"output_ports":[{"name":"data","node_id":"-1635"}],"cacheable":true,"seq_num":49,"comment":"","comment_collapsed":true},{"node_id":"-1648","module_id":"BigQuantSpace.delete_columns.delete_columns-v2","parameters":[{"name":"cols","value":"[]","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-1648"},{"name":"input_2","node_id":"-1648"}],"output_ports":[{"name":"data","node_id":"-1648"}],"cacheable":true,"seq_num":51,"comment":"","comment_collapsed":true},{"node_id":"-12255","module_id":"BigQuantSpace.del_data_before_startdate.del_data_before_startdate-v5","parameters":[],"input_ports":[{"name":"input_1","node_id":"-12255"},{"name":"input_2","node_id":"-12255"}],"output_ports":[{"name":"data","node_id":"-12255"}],"cacheable":true,"seq_num":50,"comment":"","comment_collapsed":true},{"node_id":"-12259","module_id":"BigQuantSpace.del_data_before_startdate.del_data_before_startdate-v5","parameters":[],"input_ports":[{"name":"input_1","node_id":"-12259"},{"name":"input_2","node_id":"-12259"}],"output_ports":[{"name":"data","node_id":"-12259"}],"cacheable":true,"seq_num":54,"comment":"","comment_collapsed":true},{"node_id":"-2638","module_id":"BigQuantSpace.features_short.features_short-v1","parameters":[],"input_ports":[{"name":"input_1","node_id":"-2638"}],"output_ports":[{"name":"data_1","node_id":"-2638"}],"cacheable":true,"seq_num":57,"comment":"大盘因子","comment_collapsed":false},{"node_id":"-3954","module_id":"BigQuantSpace.index_feature_extract.index_feature_extract-v3","parameters":[{"name":"before_days","value":"6000","type":"Literal","bound_global_parameter":null},{"name":"index","value":"000300.HIX","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-3954"},{"name":"input_2","node_id":"-3954"}],"output_ports":[{"name":"data_1","node_id":"-3954"},{"name":"data_2","node_id":"-3954"}],"cacheable":true,"seq_num":62,"comment":"大盘因子","comment_collapsed":false},{"node_id":"-3960","module_id":"BigQuantSpace.dropnan.dropnan-v2","parameters":[],"input_ports":[{"name":"input_data","node_id":"-3960"},{"name":"features","node_id":"-3960"}],"output_ports":[{"name":"data","node_id":"-3960"}],"cacheable":true,"seq_num":63,"comment":"","comment_collapsed":true},{"node_id":"-3964","module_id":"BigQuantSpace.join.join-v3","parameters":[{"name":"on","value":"date","type":"Literal","bound_global_parameter":null},{"name":"how","value":"inner","type":"Literal","bound_global_parameter":null},{"name":"sort","value":"True","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"data1","node_id":"-3964"},{"name":"data2","node_id":"-3964"}],"output_ports":[{"name":"data","node_id":"-3964"}],"cacheable":true,"seq_num":64,"comment":"绑定 大盘标签","comment_collapsed":false},{"node_id":"-610","module_id":"BigQuantSpace.concat.concat-v3","parameters":[],"input_ports":[{"name":"input_data_1","node_id":"-610"},{"name":"input_data_2","node_id":"-610"},{"name":"input_data_3","node_id":"-610"}],"output_ports":[{"name":"data","node_id":"-610"}],"cacheable":true,"seq_num":61,"comment":"大盘因子 训练集 预测集 合并","comment_collapsed":false},{"node_id":"-508","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 \n df = input_1.read() # 取 大盘因子 数据集\n \n start_date = input_3.read()['start_date'] # 取 预测集 开始日期\n \n df1 = df[(df['date'] < start_date)] # 大盘因子 训练集\n df2 = df[(df['date'] >= start_date)] # 大盘因子 预测集\n \n print('大盘因子训练集、预测集分割模块:')\n print(\"大盘因子训练集长度:\",len(df1))\n print(\"大盘因子预测集长度:\",len(df2))\n print('\\n')\n \n data_1 = DataSource.write_df(df1)\n data_2 = DataSource.write_df(df2)\n return Outputs(data_1=data_1, data_2=data_2, 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":"-508"},{"name":"input_2","node_id":"-508"},{"name":"input_3","node_id":"-508"}],"output_ports":[{"name":"data_1","node_id":"-508"},{"name":"data_2","node_id":"-508"},{"name":"data_3","node_id":"-508"}],"cacheable":true,"seq_num":9,"comment":"大盘因子 训练集 预测集 分割","comment_collapsed":false},{"node_id":"-3004","module_id":"BigQuantSpace.sort.sort-v4","parameters":[{"name":"sort_by","value":"date","type":"Literal","bound_global_parameter":null},{"name":"group_by","value":"","type":"Literal","bound_global_parameter":null},{"name":"keep_columns","value":"--","type":"Literal","bound_global_parameter":null},{"name":"ascending","value":"True","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_ds","node_id":"-3004"},{"name":"sort_by_ds","node_id":"-3004"}],"output_ports":[{"name":"sorted_data","node_id":"-3004"}],"cacheable":true,"seq_num":65,"comment":"","comment_collapsed":true},{"node_id":"-673","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 window = input_2.shape[1]\n return Outputs(data_1=None, data_2=window, 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":"-673"},{"name":"input_2","node_id":"-673"},{"name":"input_3","node_id":"-673"}],"output_ports":[{"name":"data_1","node_id":"-673"},{"name":"data_2","node_id":"-673"},{"name":"data_3","node_id":"-673"}],"cacheable":false,"seq_num":8,"comment":"获取 window","comment_collapsed":false},{"node_id":"-676","module_id":"BigQuantSpace.winsorize.winsorize-v6","parameters":[{"name":"columns_input","value":"","type":"Literal","bound_global_parameter":null},{"name":"median_deviate","value":3,"type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-676"},{"name":"features","node_id":"-676"}],"output_ports":[{"name":"data","node_id":"-676"}],"cacheable":true,"seq_num":10,"comment":"","comment_collapsed":true},{"node_id":"-682","module_id":"BigQuantSpace.standardlize.standardlize-v8","parameters":[{"name":"columns_input","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-682"},{"name":"input_2","node_id":"-682"}],"output_ports":[{"name":"data","node_id":"-682"}],"cacheable":true,"seq_num":36,"comment":"","comment_collapsed":true},{"node_id":"-1221","module_id":"BigQuantSpace.winsorize.winsorize-v6","parameters":[{"name":"columns_input","value":"","type":"Literal","bound_global_parameter":null},{"name":"median_deviate","value":3,"type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-1221"},{"name":"features","node_id":"-1221"}],"output_ports":[{"name":"data","node_id":"-1221"}],"cacheable":true,"seq_num":47,"comment":"","comment_collapsed":true},{"node_id":"-1227","module_id":"BigQuantSpace.standardlize.standardlize-v8","parameters":[{"name":"columns_input","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-1227"},{"name":"input_2","node_id":"-1227"}],"output_ports":[{"name":"data","node_id":"-1227"}],"cacheable":true,"seq_num":55,"comment":"","comment_collapsed":true},{"node_id":"-1945","module_id":"BigQuantSpace.dataclean.dataclean-v40","parameters":[{"name":"nan_method","value":"中位数填充","type":"Literal","bound_global_parameter":null},{"name":"outlier_method","value":"MAD法","type":"Literal","bound_global_parameter":null},{"name":"n_components","value":"12","type":"Literal","bound_global_parameter":null},{"name":"grp_len","value":10,"type":"Literal","bound_global_parameter":null},{"name":"if_nan","value":"False","type":"Literal","bound_global_parameter":null},{"name":"if_inf","value":"False","type":"Literal","bound_global_parameter":null},{"name":"if_outlier","value":"False","type":"Literal","bound_global_parameter":null},{"name":"if_neutralize","value":"False","type":"Literal","bound_global_parameter":null},{"name":"neutral_feature_remained","value":"False","type":"Literal","bound_global_parameter":null},{"name":"if_standardize","value":"False","type":"Literal","bound_global_parameter":null},{"name":"if_pca","value":"True","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"feature_data1","node_id":"-1945"},{"name":"feature_data2","node_id":"-1945"},{"name":"cleaning_feature_list","node_id":"-1945"},{"name":"neutral_feature_list","node_id":"-1945"}],"output_ports":[{"name":"cleaned_data1","node_id":"-1945"},{"name":"cleaned_data2","node_id":"-1945"},{"name":"cleaned_factors","node_id":"-1945"}],"cacheable":true,"seq_num":38,"comment":"PCA","comment_collapsed":false},{"node_id":"-1702","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 print('\\n初始化函数。。。。。。')\n \n # 加载预测数据\n \n # 预测数据,通过 options.options['data'] 传入进来,使用 read_df 函数,加载到内存 (DataFrame)\n# context.ranker_prediction = context.options['data'].read_df()\n \n # 带策略参数的预测结果数据字典,通过 options.options['data'] 传入进来\n pred_with_para_dict = context.options['data'].read()\n# print('\\n带策略参数的预测结果数据字典:\\n',pred_with_para_dict)\n dict_slice = lambda adict, start, end: dict((k, adict[k]) for k in list(adict.keys())[start:end])\n import json\n print('\\n策略参数字典:\\n',json.dumps(dict_slice(pred_with_para_dict,1,len(pred_with_para_dict)),indent = 1,separators = (',',': ')))\n\n # 预测结果数据\n context.ranker_prediction = pred_with_para_dict['pred']\n print('\\n预测结果数据(即 pred 关键字 对应 DataFrame):\\n',context.ranker_prediction)\n \n # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数\n context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))\n \n # 设置持仓股票数量\n# context.stock_count = 5\n# context.stock_count = 2\n context.stock_count = pred_with_para_dict['stock_count']\n\n# # 每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[0.339160, 0.213986, 0.169580, ..]\n# context.stock_weights = T.norm([1 / math.log(i + 2) for i in range(0, context.stock_count)])\n \n # 设置每只股票占用的最大、最小资金比例\n context.max_cash_per_instrument = 1 / context.stock_count # 0.2\n# context.min_cash_per_instrument = 0.02\n context.min_cash_per_instrument = pred_with_para_dict['min_cash_per_instrument']\n \n # 设置持仓/换仓周期(交易日)\n# context.hold_days = 3\n# context.hold_days = 30\n context.hold_days = pred_with_para_dict['hold_days']\n\n # 设置交易逻辑运行间隔周期(交易日)\n# context.run_trade_module_per_days = 1\n context.run_trade_module_per_days = pred_with_para_dict['run_trade_module_per_days'] # 交易逻辑运行间隔周期(交易日)\n \n # 设置止损止盈幅度\n context.stop_loss = pred_with_para_dict['stop_loss'] # 止损\n# context.stop_loss = 0.05 # 止损\n# context.stop_loss = 0.03 # 止损\n# context.stop_win = 0.3 # 止盈\n \n # 设置目标涨幅和每个目标涨幅的回撤幅度\n# context.target_up = 0.05 # 目标涨幅(即初始止盈幅度)\n# context.target_up = 0.10 # 目标涨幅(即初始止盈幅度)\n# context.target_up = 0.30 # 目标涨幅(即初始止盈幅度)\n# context.target_up_draw_down = 0.3 # 每个目标涨幅回撤幅度\n context.target_up = pred_with_para_dict['target_up'] # 目标涨幅(即初始止盈幅度)\n context.target_up_draw_down = pred_with_para_dict['target_up_draw_down'] # 每个目标涨幅回撤幅度\n# context.target_up_draw_down = 0.5 # 每个目标涨幅回撤幅度 \n\n # 设置大盘风控下跌幅度\n context.bm_riskctrl_down = pred_with_para_dict['bm_riskctrl_down'] # 大盘风控下跌幅度\n \n # 设置仓位控制用平均市盈率上下限,即满仓市盈率和空仓市盈率\n context.min_pe = pred_with_para_dict['min_pe'] # 满仓市盈率,仓位控制用平均市盈率下限\n context.max_pe = pred_with_para_dict['max_pe'] # 空仓市盈率,仓位控制用平均市盈率上限\n \n # 设置根据大盘预测涨跌幅调整仓位比例倍数\n context.bm_pred_label_times = pred_with_para_dict['bm_pred_label_times'] # 根据大盘预测涨跌幅调整仓位比例倍数\n \n \n ","type":"Literal","bound_global_parameter":null},{"name":"handle_data","value":"\n# 回测引擎:每个单位时间(即k线对应时间周期,如分钟线、日线、周线等)数据处理函数,每个单位时间结束后调用一次\ndef bigquant_run(context, data):\n \n print('\\n数据准备函数。。。。。。')\n \n # 加载 大盘风控数据(大盘因子预测值) 和 仓位控制数据(市场平均市盈率)\n# df = context.options['data'].read_df()\n df = context.options['data'].read()['pred']\n\n#################################### 大盘风控模块开始 ########################################################### \n #在数据准备函数中一次性计算每日的大盘风控条件相比于在handle中每日计算风控条件可以提高回测速度\n #df['pred_label'] = df['pred_label'].fillna(0)\n # 将大盘预测值做分箱处理,如果预测值<-0.01,取为-1。\n# df['direction'] = pd.cut(df['pred_label'],bins=[-float('inf'),-0.0025,0.0025,float('inf')],labels=[-1,0,1])\n# pred_label_bins = [-float('inf'),-0.02,-0.01,-0.0025,0.0025,0.01,0.02,0.04,float('inf')]\n# pred_label_bin_labels = [-1, -0.3, -0.15, 0, 0.1, 0.2, 0.3, 1]\n# pred_label_bins = [-float('inf'),-0.05,-0.0025,0.0025,0.05,float('inf')]\n## pred_label_bins = [-float('inf'),-0.05,-0.0025,0.02,0.1,float('inf')] # 非对称分箱\n# pred_label_bins = [-float('inf'),-0.05,-0.01,0.01,0.05,float('inf')]\n## pred_label_bin_labels = [-1, -0.2, 0, 0.5, 1]\n## df['direction'] = pd.cut(df['pred_label'],bins=pred_label_bins,labels=pred_label_bin_labels)\n## context.dataprediction = df[['date','direction']]\n \n bm_riskctrl_down = context.options['data'].read()['bm_riskctrl_down'] # 大盘风控下跌幅度\n# bm_riskctrl_down = context.pred_with_para_dict['bm_riskctrl_down'] # 大盘风控下跌幅度\n df['direction'] = np.where(df['pred_label'] < -bm_riskctrl_down, -1, 0)\n# df['direction'] = np.where(df['pred_label'] < -0.02, -1, 0)\n# df['direction'] = np.where(df['pred_label'] < -0.02, -1, 1*df['pred_label'] )\n# df['direction'] = np.where(df['pred_label'] < -0.002, -1, np.where(df['pred_label'] > 0.01, 25*df['pred_label'], 0))\n# df['direction'] = np.where(df['pred_label'] < -0.02, -1, np.where(df['pred_label'] > 0.01, 20*df['pred_label'], 10*df['pred_label']))\n# df['direction'] = np.where(df['pred_label'] < -0.005, -1, np.where(df['pred_label'] > 0.01, 25*df['pred_label'], 15*df['pred_label']))\n context.bm_direction_and_label = df[['date','direction','pred_label','label']].drop_duplicates(['date'])\n \n print('\\n大盘预测数据:\\n',context.bm_direction_and_label)\n# print(pd.DataFrame(dict(list(context.bm_direction_and_pred_label.groupby(['date'],as_index=False)))).reset_index())\n# print(pd.DataFrame(context.bm_direction_and_pred_label.groupby(['date'],as_index=False)).describe().reset_index())\n \n#################################### 大盘风控模块结束 ########################################################### \n \n############################### ST、退市整理、涨跌停等处理开始 ################################# \n # 获取ST状态、涨跌停状态、上市天数等状态信息\n context.status_df = D.features(instruments = context.instruments, start_date = context.start_date, end_date = context.end_date, \n fields=['st_status_0','price_limit_status_0','price_limit_status_1','list_days_0','return_0','amount_0','amount_1'])\n context.status_df['delta_amount'] = (context.status_df['amount_0']-context.status_df['amount_1'])/context.status_df['amount_1']\n # 获取股票代码对应的股票名称\n# context.status_df = D.history_data(instruments =context.instruments,start_date = context.start_date, end_date = context.end_date, \n# fields=['name','st_status','price_limit_status'])\n context.name_df = DataSource('instruments_CN_STOCK_A').read(instruments = context.instruments, start_date=context.start_date, end_date=context.end_date, fields=['name'])\n \n # 设置按['date','instrument']多级索引\n context.status_df = context.status_df.set_index(['date','instrument'])\n context.name_df = context.name_df.set_index(['date','instrument'])\n # 索引按['date','instrument'] lexsort 排序\n context.status_df = context.status_df.sort_index(level=['date','instrument'])\n context.name_df = context.name_df.sort_index(level=['date','instrument'])\n ############################### ST、退市整理、涨跌停等处理结束 ################################# \n \n ############################### 仓位管理模块开始 ################################# \n # 采用基于市场平均市盈率的仓位控制策略:根据市场平均市盈率所处区间设置相应仓位比重\n # 市场平均市盈率 <30, 投入 100% 资金,即满仓\n # 市场平均市盈率 30~100,投入 5%~90% 资金\n # 市场平均市盈率 >100, 投入 0% 资金,即清仓\n # 设置分箱规则:2005 以来全市场平均 市盈率最小 18,最大 108,将市场平均市盈率在在此范围内分箱\n# pe_bins = [-float('inf'), 30, 35, 40, 45, 50, 60, 70, 80, 85, 90, 100, float('inf')]\n# # 设置分箱标签,以对应仓位比重为分箱标签\n# pe_bin_labels = [1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.05, 0]\n# # 将市场平均市盈率分箱,获得对应分箱的仓位比重,并存入全局变量\n# context.position_ratio = df.groupby('date').apply(lambda x: pd.cut(x['avg_pe'], bins=pe_bins, labels=pe_bin_labels))\n# context.position_ratio = pd.DataFrame(context.position_ratio)\n# context.position_ratio.columns = ['position_ratio']\n# context.position_ratio = context.position_ratio.reset_index().drop('level_1',axis=1).drop_duplicates().set_index('date')\n \n# min_pe = 30\n# max_pe = 100\n# min_pe = context.pred_with_para_dict['min_pe'] # 满仓市盈率,仓位控制用平均市盈率下限\n# max_pe = context.pred_with_para_dict['max_pe'] # 空仓市盈率,仓位控制用平均市盈率上限\n# min_pe = df['avg_pe'].min() \n# max_pe = df['avg_pe'].max() \n# df['avg_pe'] = df['avg_pe'].clip(lower=min_pe,upper=max_pe) # 截取 min_pe 和 max_pe 之间的 avg_pe\n \n if 'avg_pe'in df.columns:\n \n min_pe = context.options['data'].read()['min_pe'] # 满仓市盈率,仓位控制用平均市盈率下限\n max_pe = context.options['data'].read()['max_pe'] # 空仓市盈率,仓位控制用平均市盈率上限\n \n # 生成仓位比重数据,平均市盈率越高,仓位越低\n context.position_ratio = df.groupby('date').apply(lambda x:1-(x['avg_pe']-min_pe)/(max_pe-min_pe)) \n \n context.position_ratio = pd.DataFrame(context.position_ratio)\n context.position_ratio.columns = ['position_ratio']\n context.position_ratio = context.position_ratio.reset_index().drop('level_1',axis=1).drop_duplicates().set_index('date')\n \n print('\\n平均市盈率数据:')\n print(df[['date','avg_pe']])\n print(\"\\nmax_avg_pe:\",df['avg_pe'].max())\n print(\"min_avg_pe:\",df['avg_pe'].min())\n print('\\n仓位控制数据:')\n print(context.position_ratio)\n \n else:\n \n context.position_ratio = df.groupby('date').apply(lambda x:1) \n \n context.position_ratio = pd.DataFrame(context.position_ratio)\n context.position_ratio.columns = ['position_ratio']\n context.position_ratio = context.position_ratio.reset_index().drop_duplicates().set_index('date')\n \n print('\\n仓位控制数据:')\n print(context.position_ratio) \n \n############################### 仓位管理模块结束 ################################# \n \n \n \n print('\\n交易/回测模块主函数。。。。。。')\n \n#---------------------START:大盘风控模块(含建仓期)--------------------------\n current_stoploss_stock = [] \n today_date = data.current_dt.strftime('%Y-%m-%d')\n \n # 获取当前持仓情况\n positions_all = [equity.symbol for equity in context.portfolio.positions]\n \n # 获取当前大盘预测情况\n #ds_id=context.predataid\n #today_prediction = DataSource(id=ds_id).read_pickle()\n bm_direction_and_label = context.bm_direction_and_label\n today_bm_direction = bm_direction_and_label[bm_direction_and_label.date == today_date].direction.values[0]\n today_bm_pred_label = bm_direction_and_label[bm_direction_and_label.date == today_date].pred_label.values[0]\n today_bm_label = bm_direction_and_label[bm_direction_and_label.date == today_date].label.values[0]\n \n print(today_date,'次日大盘方向预测:',today_bm_direction)\n print(today_date,'次日大盘涨跌幅预测:',today_bm_pred_label)\n print(today_date,'次日大盘涨跌幅实际:',today_bm_label)\n \n # 满足空仓条件\n if today_bm_direction == -1: # < 0 \t\n if len(positions_all)>0:\n # 全部卖出后返回\n for i in positions_all:\n# 当日不能交易股票,不代表次日也不能交易,不应排除!! \n# if data.can_trade(context.symbol(i)): \n context.order_target_percent(context.symbol(i), 0)\n current_stoploss_stock.append(i)\n print('日期:', today_date, '股票:', i, '风控清仓!')\n print('风控执行',today_date)\n return # 运行风控后当日结束,不再执行后续的买卖订单\n#------------------------END:大盘风控模块(含建仓期)---------------------------\n\n#------------------------------------------止损模块START--------------------------------------------\n from userlib import mylib\n \n # 测试变量 current_stoploss_stock 是否存在,若不存在则赋初值为空列表\n try:\n current_stoploss_stock = current_stoploss_stock \n except NameError:\n current_stoploss_stock = [] \n \n # 获取当日日期\n today = data.current_dt \n \n # 获取当前持仓情况\n equities = {e.symbol: p for e, p in context.portfolio.positions.items() if p.amount>0}\n \n # 新建当日止损股票列表是为了 handle_data 策略逻辑部分不再对该股票进行判断\n #current_stoploss_stock = [] \n \n if len(equities) > 0:# 如果有持仓\n for i in equities.keys():\n\n# 当日不能交易股票,不代表次日也不能交易,不应排除!! \n# if data.can_trade(context.symbol(i)):\n \n # 持仓成本\n stock_cost = equities[i].cost_basis \n # 最新市场价格\n stock_market_price = data.current(context.symbol(i), 'price') \n last_sale_date = equities[i].last_sale_date # 上次交易日期\n delta_days = today - last_sale_date \n hold_days = delta_days.days # 持仓天数\n # 建仓以来的最高价\n highest_price_since_buy = data.history(context.symbol(i), 'high', hold_days, '1d').max()\n \n # # 按建仓以来最高价下跌指定幅度确定止损位置\n # stoploss_line = highest_price_since_buy * (1 - context.stop_loss)\n\n # 确定止损位置\n #stoploss_line = stock_cost * (1 - context.stop_loss) \n stoploss_line = mylib.move_stop_win_loss_price(buy_price = stock_cost,\n current_price = stock_market_price,\n high_price = highest_price_since_buy,\n prime_stop_loss = context.stop_loss,\n target_up = context.target_up,\n target_up_draw_down = context.target_up_draw_down) \n record('止损位置', stoploss_line)\n \n # 如果已经止损,则跳过,防止卖空\n if i in current_stoploss_stock:\n continue\n # 如果价格下穿止损位置\n if stock_market_price < stoploss_line:\n #context.order_target(context.symbol(i), 0) \n context.order_target_percent(context.symbol(i), 0)\n current_stoploss_stock.append(i)\n print('日期:', today, '股票:', i, '出现止损状况','成本价:',stock_cost,'最新价:',stock_market_price)\n # # 如果价格上穿止盈位置\n # if stock_market_price > stock_cost * (1 + context.stop_win):\n # context.order_target(context.symbol(i), 0) \n # print('日期:', today, '股票:', i, '出现止盈状况')\n#-------------------------------------------止损模块END---------------------- \n\n#-------------------------------------------周期控制模块BEGIN---------------------- \n # 交易逻辑运行周期控制\n # 按 context.run_trade_module_per_days 记录的 交易逻辑运行间隔周期 控制 交易模块 的运行\n # 每隔 context.run_trade_module_per_days 天 运行 一次 交易模块\n if context.trading_day_index % context.run_trade_module_per_days != 0: \n return\n#-------------------------------------------周期控制模块END---------------------- \n\n#-------------------------------------------交易模块BEGIN---------------------- \n today = data.current_dt.strftime('%Y-%m-%d')\n today_dt = data.current_dt\n \n # 按日期过滤得到今日的预测数据\n ranker_prediction = context.ranker_prediction[context.ranker_prediction.date == today]\n\n stock_count = context.stock_count # 持仓股票数量\n \n #---------------------------处理需要买入的股票列表---------------------- \n \n # 需要买入的股票:排名前 context.stock_count 的股票\n #stock_to_buy = list(ranker_prediction.instrument[:stock_count])\n #stock_to_buy = list(ranker_prediction.instrument[:3 * stock_count]) # 因存在剔除情况,故多取一点 \n stock_to_buy = list(ranker_prediction.instrument[: stock_count + 10 ]) # 因存在剔除情况,故多取一点 \n print(\"日期:\",today,\"原始stock_to_buy:\",stock_to_buy)\n #print(\"日期:\",today,\"止损:\",current_stoploss_stock) \n \n # 需要买入的股票:去除当天不能交易股票\n# 当日不能交易股票,不代表次日也不能交易,不应排除!! \n# stock_to_buy = [i for i in stock_to_buy if data.can_trade(context.symbol(i))]\n# print(\"日期:\",today,\"去除当天不能交易股票后stock_to_buy:\",stock_to_buy)\n \n # 需要买入的股票:去除已止损股票,防止当天卖出后又买入\n stock_to_buy = [i for i in stock_to_buy if i not in current_stoploss_stock]\n print(\"日期:\",today,\"去除止损股票后stock_to_buy:\",stock_to_buy)\n \n# # 需要买入的股票:去除多取的股票\n# stock_to_buy = stock_to_buy[:stock_count]\n# print(\"日期:\",today,\"去除多取股票后stock_to_buy:\",stock_to_buy)\n \n #---------------------------结束处理需要买入的股票列表----------------------\n \n # 通过positions对象,使用列表生成式的方法获取目前持仓的股票列表\n stock_hold_now = [equity.symbol for equity in context.portfolio.positions]\n print(\"日期:\",today,\"持仓暨原始stock_to_sell:\",stock_hold_now)\n \n #---------------------------处理需要继续持有的股票列表----------------------\n \n # 继续持有的股票:截至当天 持仓时间 不足 context.hold_days 指定 持仓周期 的股票继续持有,注意股票买入当天盘后计入持仓但持仓时间为 0 。\n no_need_to_sell = [i for i in stock_hold_now if (today_dt - equities[i].last_sale_date).days + 1 < context.hold_days]\n print(\"日期:\",today,\"持仓不卖股--持仓时间不足股:\",no_need_to_sell)\n \n# # 继续持有的股票:当天排名靠后的股票不应继续持有,如后 context.stock_count 名\n# no_need_to_sell = [i for i in no_need_to_sell if i not in list(ranker_prediction.instrument[-2 * context.stock_count:])]\n# print(\"日期:\",today,\"持仓不卖股--剔除排名靠后股后:\",no_need_to_sell)\n# # 继续持有的股票:非当天要买入的股票即非当天排名靠前的股票不应继续持有\n# no_need_to_sell = [i for i in no_need_to_sell if i not in stock_to_buy]\n# print(\"日期:\",today,\"持仓不卖股--剔除非排名靠前股后:\",no_need_to_sell)\n \n # 继续持有的股票:如果当天要买入的前 stock_count 只股票已经存在于目前的持仓里,那么应继续持有\n # no_need_to_sell = no_need_to_sell + [i for i in stock_hold_now if i in stock_to_buy]\n no_need_to_sell = list(set(no_need_to_sell + [i for i in stock_hold_now if i in stock_to_buy[:stock_count]]))\n print(\"日期:\",today,\"持仓不卖股--追加要买入股后:\",no_need_to_sell)\n\n# 当日不能交易股票,不代表次日也不能交易,不应排除!! \n# # 继续持有的股票:当天不能交易的股票被动继续持有\n# no_need_to_sell = no_need_to_sell + [i for i in stock_hold_now if not data.can_trade(context.symbol(i))]\n# no_need_to_sell = list(set(no_need_to_sell + [i for i in stock_hold_now if not data.can_trade(context.symbol(i))]))\n# print(\"日期:\",today,\"持仓不卖股--追加不能交易股后:\",no_need_to_sell)\n \n # 继续持有的股票:剔除止损股票,止损股票不再继续持有\n no_need_to_sell = [i for i in no_need_to_sell if i not in current_stoploss_stock]\n print(\"日期:\",today,\"持仓不卖股--剔除止损股后:\",no_need_to_sell)\n \n #---------------------------结束处理需要继续持有的股票列表----------------------\n \n #---------------------------处理需要卖出的股票列表----------------------\n \n # 需要卖出的股票:去除持仓不卖股\n stock_to_sell = [i for i in stock_hold_now if i not in no_need_to_sell]\n print(\"日期:\",today,\"去除持仓不卖股后stock_to_sell:\",stock_to_sell)\n \n # 需要卖出的股票:去除已止损股票,防止卖空\n stock_to_sell = [i for i in stock_to_sell if i not in current_stoploss_stock]\n print(\"日期:\",today,\"已止损股:\",current_stoploss_stock)\n print(\"日期:\",today,\"去除止损股后stock_to_sell:\",stock_to_sell)\n \n #---------------------------结束处理需要卖出的股票列表----------------------\n \n #---------------------------继续处理需要买入、卖出的股票列表---------------------- \n \n # 需要买入的股票:去除不卖的股票,持仓不卖的股票不买\n stock_to_buy = [i for i in stock_to_buy if i not in no_need_to_sell]\n print(\"日期:\",today,\"去除持仓不卖股后stock_to_buy:\",stock_to_buy)\n \n #------------------------涨跌停状态、ST状态、上市首日股、“退”字股等异常处理模块开始-------------------------\n if True:\n # context.status_df、context.name_df 均为 ['date','instrument'] 二级索引\n # 获取当日涨跌停状态、ST状态数据\n df_status = context.status_df.loc[(today,slice(None)),:]\n # 当日涨停股,涨跌停状态:1表示跌停,2表示未涨跌停,3则表示涨停\n stock_limit_up = df_status[df_status.price_limit_status_0==3].index.get_level_values(1).tolist()\n # 当日一字板涨停股\n stock_limit_up_yzb = [i for i in stock_limit_up if data.current(context.symbol(i),'high')==data.current(context.symbol(i),'low')] \n #print(\"日期:\",today,\"涨停股:\",stock_limit_up)\n# # 当日ST股,ST状态:0:正常股票,1:ST,2:*ST,11:暂停上市\n# stock_st = df_status[df_status.st_status_0!=0].index.get_level_values(1).tolist()\n # 当日*ST股和暂停上市股,ST状态:0:正常股票,1:ST,2:*ST,11:暂停上市\n stock_st = df_status[df_status.st_status_0 > 1].index.get_level_values(1).tolist()\n #print(\"日期:\",today,\"ST股:\",stock_st)\n # 当日上市首日股\n stock_list_1st_day = df_status[df_status.list_days_0==0].index.get_level_values(1).tolist()\n # 获取当日股票名称数据\n stock_name = context.name_df.loc[(today,slice(None)),:]\n\n # 去除*ST、暂停上市股票,不买入*ST、暂停上市股票\n# print(\"日期:\",today,\"ST不买:\", [i for i in stock_to_buy if i in stock_st])\n# stock_to_buy = [i for i in stock_to_buy if i not in stock_st] \n# print(\"日期:\",today,\"去除ST股不买后stock_to_buy:\", stock_to_buy)\n print(\"日期:\",today,\"*ST股、暂停上市股不买:\", [i for i in stock_to_buy if i in stock_st])\n stock_to_buy = [i for i in stock_to_buy if i not in stock_st] \n print(\"日期:\",today,\"去除*ST股、暂停上市股不买后stock_to_buy:\", stock_to_buy)\n \n # 去除退市整理期股票,不买入退市整理期股票\n print(\"日期:\",today,\"'退'字股不买:\", [i for i in stock_to_buy if '退' in stock_name.loc[(today,i),:].name.values[0]])\n stock_to_buy = [i for i in stock_to_buy if '退' not in stock_name.loc[(today,i),:].name.values[0]] \n print(\"日期:\",today,\"去除'退'字股不买后stock_to_buy:\", stock_to_buy)\n\n# # 去除涨停股票,涨停不买\n# print(\"日期:\",today,\"涨停不买:\", [i for i in stock_to_buy if i in stock_limit_up])\n# stock_to_buy = [i for i in stock_to_buy if i not in stock_limit_up]\n \n# # 去除一字涨停股票,一字涨停不买(因为次日大概率继续一字涨停?)\n# print(\"日期:\",today,\"一字涨停不买:\", [i for i in stock_to_buy if i in stock_limit_up_yzb])\n# stock_to_buy = [i for i in stock_to_buy if i not in stock_limit_up_yzb]\n# print(\"日期:\",today,\"去除一字涨停股不买后stock_to_buy:\", stock_to_buy)\n \n # 去除上市首日股票,上市首日不买(因为次日大概率一字涨停)\n print(\"日期:\",today,\"上市首日不买:\", [i for i in stock_to_buy if i in stock_list_1st_day])\n stock_to_buy = [i for i in stock_to_buy if i not in stock_list_1st_day]\n print(\"日期:\",today,\"去除上市首日股不买后stock_to_buy:\", stock_to_buy)\n \n # 去除涨停股票,涨停不卖\n print(\"日期:\",today,\"涨停不卖:\", [i for i in stock_to_sell if i in stock_limit_up])\n stock_to_sell = [i for i in stock_to_sell if i not in stock_limit_up]\n print(\"日期:\",today,\"去除涨停不卖股后stock_to_sell:\", stock_to_sell)\n \n# # 去除一字涨停股票,一字涨停不卖\n# print(\"日期:\",today,\"一字涨停不卖:\", [i for i in stock_to_sell if i in stock_limit_up_yzb])\n# stock_to_sell = [i for i in stock_to_sell if i not in stock_limit_up_yzb]\n# print(\"日期:\",today,\"去除一字涨停不卖股后stock_to_sell:\", stock_to_sell)\n \n #------------------------涨跌停状态、ST状态、上市首日股、“退”字股等异常处理模块结束-------------------------\n \n # 记录 全局 买入股票列表,供 次日 盘前处理函数 处理 停牌股票买入订单\n context.stock_to_buy = stock_to_buy\n \n #------------------------持仓股票数量控制模块开始-------------------------\n\n# # 固定数量补仓\n# stock_to_buy = stock_to_buy[:stock_count] \n \n # 卖几补几,维持固定数量持仓\n stock_to_buy = stock_to_buy[: (stock_count - len(stock_hold_now) + len(current_stoploss_stock) + len(stock_to_sell))] \n print(\"日期:\",today,\"持仓股票数量控制后stock_to_buy:\", stock_to_buy)\n \n #------------------------持仓股票数量控制模块结束-------------------------\n \n # 根据持仓股票数量控制情况 修正 全局 买入股票列表\n context.stock_to_buy = context.stock_to_buy[len(stock_to_buy):]\n \n print(\"日期:\",today,\"最终stock_to_buy:\",stock_to_buy)\n print(\"日期:\",today,\"最终stock_to_sell:\",stock_to_sell)\n \n #---------------------------结束继续处理需要买入、卖出的股票列表---------------------- \n\n# print('账户现金:',context.portfolio.cash)\n \n # 卖出\n for stock in stock_to_sell:\n # 如果该股票停牌,则没法成交。因此需要用can_trade方法检查下该股票的状态\n # 如果返回真值,则可以正常下单,否则会出错\n # 因为stock是字符串格式,我们用symbol方法将其转化成平台可以接受的形式:Equity格式\n# if data.can_trade(context.symbol(stock)):\n# 当日不能交易股票,不代表次日也不能交易,不应排除!! \n # order_target_percent是平台的一个下单接口,表明下单使得该股票的权重为0,\n # 即卖出全部股票,可参考回测文档\n context.order_target_percent(context.symbol(stock), 0)\n print(\"日期:\",today,\"卖出:\",stock)\n \n # 如果当天没有买入的股票,就返回\n if len(stock_to_buy) == 0:\n return\n \n #-------------------------------------------仓位控制模块 BEGIN ---------------------- \n # 获取今日 目标仓位比\n # position_ratio = context.position_ratio.loc[today,'position_ratio'][0]\n # position_ratio = 0.8\n position_ratio = context.position_ratio.loc[today,'position_ratio'] \n print(\"日期:\",today,\"目标仓位比例:\", position_ratio)\n\n# # 视乎大盘择时预测结果调增仓位\n# position_ratio = position_ratio + 0.2 if today_prediction > 0 else position_ratio\n# # 仓位比例最高为 1\n# position_ratio = position_ratio if position_ratio <= 1 else 1\n \n # 视乎大盘择时预测结果调整仓位\n position_ratio = position_ratio + context.bm_pred_label_times * (today_bm_pred_label if not np.isnan(today_bm_pred_label) else 0)\n print('今日大盘择时仓位调整比例:',context.bm_pred_label_times * (today_bm_pred_label if not np.isnan(today_bm_pred_label) else 0))\n \n # 仓位比例最低为 0, 最高为 1\n position_ratio = max(0,position_ratio)\n position_ratio = min(1,position_ratio)\n \n # portfolio_value: 账户总价值(包括持仓市值+现金)\n # positions_value: 持仓市值\n \n# # 结合目标仓位比、已持仓比例,计算今日 买入仓位比例\n# position_ratio_had = context.portfolio.positions_value / context.portfolio.portfolio_value # 现已持仓比例\n# position_ratio_to_buy = position_ratio - position_ratio_had # 调减现已持仓比例\n# #position_ratio_to_buy = position_ratio\n# print(\"日期:\",today,\"目标仓位比例:\", position_ratio)\n# print(\"日期:\",today,\"已持仓比例:\", position_ratio_had)\n# print(\"日期:\",today,\"买入仓位比例:\", position_ratio_to_buy)\n \n # 按 继续持仓清单 no_need_to_sell 估算 继续持仓比例\n position_value_no_need_to_sell = 0\n for stock in no_need_to_sell:\n equity = context.portfolio.positions[context.symbol(stock)]\n # 继续持仓市值\n position_value_no_need_to_sell += equity.amount * equity.last_sale_price \n # 继续持仓比例\n position_ratio_no_need_to_sell = position_value_no_need_to_sell / context.portfolio.portfolio_value \n \n # 结合目标仓位比例、继续持仓比例,计算 买入仓位比例\n position_ratio_to_buy = position_ratio - position_ratio_no_need_to_sell # 调减继续持仓比例\n \n print(\"日期:\",today,\"持仓市值:\", context.portfolio.positions_value)\n print(\"日期:\",today,\"组合价值:\", context.portfolio.portfolio_value)\n print(\"日期:\",today,\"大盘择时仓位调整后目标仓位比例:\", position_ratio)\n print(\"日期:\",today,\"继续持仓市值:\", position_value_no_need_to_sell)\n print(\"日期:\",today,\"继续持仓比例:\", position_ratio_no_need_to_sell)\n print(\"日期:\",today,\"买入仓位比例:\", position_ratio_to_buy)\n \n # 如果当天 买入仓位比例 非正(即无需买入),返回\n if position_ratio_to_buy <= 0:\n print(\"日期:\",today,\"仓位控制,无需买入!\")\n return\n \n # 如果当天 买入仓位比例 较小,如不超过 单股最低资金比例 context.min_cash_per_instrument ,则调整 stock_to_buy 为仅买入 1 只股票,防止单股仓位比例过低\n if position_ratio_to_buy <= context.min_cash_per_instrument:\n stock_to_buy = stock_to_buy[:1]\n print(\"日期:\",today,\"买入仓位比例低于单股最低资金比例 %s ,调整后stock_to_buy:\" % context.min_cash_per_instrument,stock_to_buy)\n #-------------------------------------------仓位控制模块 END ---------------------- \n \n #-------------------------------------------权重控制模块 BEGIN ---------------------- \n # 计算今日购买权重\n # 等权重买入 \n# weights = position_ratio_to_buy / len(stock_to_buy) \n # 非等权重买入 \n # 每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[0.339160, 0.213986, 0.169580, ..]\n weights = T.norm([1 / math.log(i + 2) for i in range(0, len(stock_to_buy))])\n weights = pd.Series(weights)\n weights = position_ratio_to_buy * weights \n #-------------------------------------------权重控制模块 END ---------------------- \n \n # 买入\n for stock in stock_to_buy:\n# if data.can_trade(context.symbol(stock)):\n# 当日不能交易股票,不代表次日也不能交易,不应排除!! \n # 计算股票 stock 对应的购买权重\n weight = weights if isinstance(weights,float) else weights[stock_to_buy.index(stock)] \n \n # 购买权重考虑每只股票占用最大资金比例 context.max_cash_per_instrument 的约束\n weight = min(weight,context.max_cash_per_instrument)\n \n # 下单使得某只股票的持仓权重达到 weight\n context.order_target_percent(context.symbol(stock), weight)\n print(\"日期:\",today,\"买入:\",stock,\"买入持仓权重:\",weight)\n\n#-------------------------------------------交易模块END---------------------- \n\n \n \n \n","type":"Literal","bound_global_parameter":null},{"name":"prepare","value":"# 回测引擎:准备数据,回测情景仅第一天执行一次,模拟实盘情景每天运行一次\ndef bigquant_run(context):\n \n pass","type":"Literal","bound_global_parameter":null},{"name":"before_trading_start","value":"# 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。\ndef bigquant_run(context, data):\n \n print('\\n盘前处理函数。。。。。。')\n \n # 对即将撮合的订单进行二次过滤\n \n # 获取当日日期\n today = data.current_dt.strftime('%Y-%m-%d')\n \n # 在开盘前对当天停牌且在购买名单内的股票进行取消交易\n # 并将原计划用于交易该股票的钱分摊到 全局买入股票列表 context.stock_to_buy 中的下一只股票进行购买\n for open_orders in context.get_open_orders().values():\n for order in open_orders:\n sid = order.sid\n price = data.current(sid, 'price')\n symbol = sid.symbol\n if not data.can_trade(sid): # 股票停牌\n context.cancel_order(order)\n print('日期:', today, '停牌取消买单:{},数量为:{}'.format(symbol, order.amount))\n context._in_before_trading_start = False\n plan_amount = order.amount * price\n alternative_symbol = [i for i in context.stock_to_buy if i != symbol and data.can_trade(i)]\n# alternative_symbol = [i for i in instruments if i != symbol]\n context.order_target_value(context.symbol(alternative_symbol[0]), plan_amount) \n print('日期:', today, '更新为买单:{},预计成交金额为:{}'.format(alternative_symbol[0], plan_amount))\n \n####################################################################################################################### \n if False:\n # 周期控制\n if context.trading_day_index % context.hold_days != 0: # 按 context.hold_days 记录的持仓天(交易日)数换仓\n return\n\n # 获取当日日期(因盘前主要处理前一交易日盘后订单,故当日日期取前一交易日日期。)\n today = data.current_dt.strftime('%Y-%m-%d')\n print('当前交易日:',today)\n trading_days = D.trading_days(start_date=context.start_date, end_date=context.end_date) # 获取相应交易日历\n today_loc = trading_days[trading_days['date']==today].index[0] # 当前交易日对应标签索引\n today_iloc = trading_days.index.get_loc(today_loc) # 当前交易日在交易日历里的对应位置索引\n if today_iloc == 0: # 首个交易日不做盘前处理\n return\n today = trading_days.at[trading_days[trading_days['date']==today].index[0] - 1 ,'date'].strftime('%Y-%m-%d') # 取前一交易日日期\n print('前一交易日:',today)\n \n # 获取当日涨跌停状态、ST状态数据\n df_price_limit_status = context.status_df.loc[(today,slice(None)),:]\n # 获取当日股票名称数据\n df_name = context.name_df.loc[(today,slice(None)),:]\n\n # 得到当前未完成订单\n for orders in get_open_orders().values():\n # 循环,撤销订单\n for _order in orders:\n ins = str(_order.sid.symbol)\n #print(_order.amount,\n # ins,df_price_limit_status[df_price_limit_status.instrument==ins].price_limit_status_0.values[0],\n # df_name[df_name.instrument==ins].name.values[0])\n try:\n df_ins = df_price_limit_status.loc[(today,ins),:]\n # 如果当日涨停,则取消卖单\n #if df_price_limit_status[df_price_limit_status.instrument==ins].price_limit_status_0.values[0]>2 and _order.amount<0:\n if df_ins.price_limit_status_0.values[0]>2 and _order.amount<0:\n cancel_order(_order)\n print(today,'尾盘涨停取消卖单',ins) \n # # 如果当日量价均上涨,则取消卖单\n # if df_ins.return_0.values[0]>1.05 and df_ins.delta_amount.values[0]>0.05 and _order.amount<0:\n # cancel_order(_order)\n # print(today,'正在上涨的股票取消卖单',ins) \n\n # 如果当日为退市整理期股,则取消买单\n #if '退' in df_price_limit_status[df_price_limit_status.instrument==ins].name.values[0] and _order.amount>0:\n # cancel_order(_order)\n # print(today,'退市整理期取消买单',ins) \n if '退' in df_name.loc[(today,ins),:].name.values[0] and _order.amount>0:\n cancel_order(_order)\n print(today,'退市整理期取消买单',ins) \n\n # 如果当日为ST股,则取消买单\n if df_ins.st_status_0.values[0]!=0 and _order.amount>0:\n cancel_order(_order)\n print(today,'ST或暂停上市股取消买单',ins) \n except:\n continue\n","type":"Literal","bound_global_parameter":null},{"name":"volume_limit","value":"0","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":"100000","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":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-1702"},{"name":"options_data","node_id":"-1702"},{"name":"history_ds","node_id":"-1702"},{"name":"benchmark_ds","node_id":"-1702"},{"name":"trading_calendar","node_id":"-1702"}],"output_ports":[{"name":"raw_perf","node_id":"-1702"}],"cacheable":false,"seq_num":56,"comment":"","comment_collapsed":true},{"node_id":"-5858","module_id":"BigQuantSpace.model_read.model_read-v1","parameters":[{"name":"filedir","value":"/home/bigquant/work/userlib/","type":"Literal","bound_global_parameter":null},{"name":"filename","value":"bm_model20210701前","type":"Literal","bound_global_parameter":null}],"input_ports":[],"output_ports":[{"name":"data","node_id":"-5858"}],"cacheable":false,"seq_num":1,"comment":"","comment_collapsed":true},{"node_id":"-4571","module_id":"BigQuantSpace.trade_para_config.trade_para_config-v9","parameters":[{"name":"run_trade_module_per_days","value":1,"type":"Literal","bound_global_parameter":null},{"name":"hold_days","value":1,"type":"Literal","bound_global_parameter":null},{"name":"stock_count","value":1,"type":"Literal","bound_global_parameter":null},{"name":"min_cash_per_instrument","value":0.1,"type":"Literal","bound_global_parameter":null},{"name":"stop_loss","value":0.05,"type":"Literal","bound_global_parameter":null},{"name":"target_up","value":0.3,"type":"Literal","bound_global_parameter":null},{"name":"target_up_draw_down","value":0.3,"type":"Literal","bound_global_parameter":null},{"name":"bm_riskctrl_down","value":0.03,"type":"Literal","bound_global_parameter":null},{"name":"min_pe","value":30,"type":"Literal","bound_global_parameter":null},{"name":"max_pe","value":80,"type":"Literal","bound_global_parameter":null},{"name":"bm_pred_label_times","value":20,"type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"predictions","node_id":"-4571"}],"output_ports":[{"name":"data","node_id":"-4571"}],"cacheable":false,"seq_num":19,"comment":"","comment_collapsed":true}],"node_layout":"<node_postions><node_position Node='-31' Position='256,-171,200,200'/><node_position Node='-41' Position='239,244,200,200'/><node_position Node='-52' Position='281,364,200,200'/><node_position Node='-102' Position='443,-171,200,200'/><node_position Node='-116' Position='270,533,200,200'/><node_position Node='-160' Position='-259,879,200,200'/><node_position Node='-184' Position='1,720,200,200'/><node_position Node='-188' Position='514,437,200,200'/><node_position Node='-198' Position='276,436,200,200'/><node_position Node='-2503' Position='71,-396,200,200'/><node_position Node='-22272' Position='363,-394,200,200'/><node_position Node='-22276' Position='650,-387,200,200'/><node_position Node='-924' Position='72,-300,200,200'/><node_position Node='-1720' Position='356,-312,200,200'/><node_position Node='-1673' Position='646,-305,200,200'/><node_position Node='-1962' Position='355,-253,200,200'/><node_position Node='-4550' Position='-604,-12,200,200'/><node_position Node='-1540' Position='-345,527,200,200'/><node_position Node='-2431' Position='-271,597,200,200'/><node_position Node='-3887' Position='-324,-3,200,200'/><node_position Node='-3725' Position='-317,335,200,200'/><node_position Node='-2186' Position='-11,-172,200,200'/><node_position Node='-18645' Position='-317,-125,200,200'/><node_position Node='-989' Position='-321,61,200,200'/><node_position Node='-4208' Position='285,-78,200,200'/><node_position Node='-4215' Position='285,-34,200,200'/><node_position Node='-4228' Position='284,55,200,200'/><node_position Node='-1628' Position='581,-74,200,200'/><node_position Node='-1635' Position='581,-29,200,200'/><node_position Node='-1648' Position='582,67,200,200'/><node_position Node='-12255' Position='286,13,200,200'/><node_position Node='-12259' Position='581,21,200,200'/><node_position Node='-2638' Position='-7,-103,200,200'/><node_position Node='-3954' Position='-5,-2,200,200'/><node_position Node='-3960' Position='0,57,200,200'/><node_position Node='-3964' Position='-314,156,200,200'/><node_position Node='-610' Position='1,243,200,200'/><node_position Node='-508' Position='1,105,200,200'/><node_position Node='-3004' Position='-314,253,200,200'/><node_position Node='-673' Position='-602,80,200,200'/><node_position Node='-676' Position='288,104,200,200'/><node_position Node='-682' Position='288,155,200,200'/><node_position Node='-1221' Position='583,112,200,200'/><node_position Node='-1227' Position='583,157,200,200'/><node_position Node='-1945' Position='0,171,200,200'/><node_position Node='-1702' Position='-22,879,200,200'/><node_position Node='-5858' Position='-476,468,200,200'/><node_position Node='-4571' Position='9,790,200,200'/></node_postions>"},"nodes_readonly":false,"studio_version":"v2"}
    In [18]:
    # 本代码由可视化策略环境自动生成 2021年9月8日 03:33
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    def m8_run_bigquant_run(input_1, input_2, input_3):
        # 示例代码如下。在这里编写您的代码
        window = input_2.shape[1]
        return Outputs(data_1=None, data_2=window, data_3=None)
    
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m8_post_run_bigquant_run(outputs):
        return outputs
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    def m9_run_bigquant_run(input_1, input_2, input_3):
        # 示例代码如下。在这里编写您的代码
        
        df = input_1.read() # 取 大盘因子 数据集
        
        start_date = input_3.read()['start_date'] # 取 预测集 开始日期
        
        df1 = df[(df['date'] < start_date)]  # 大盘因子 训练集
        df2 = df[(df['date'] >= start_date)] # 大盘因子 预测集
        
        print('大盘因子训练集、预测集分割模块:')
        print("大盘因子训练集长度:",len(df1))
        print("大盘因子预测集长度:",len(df2))
        print('\n')
        
        data_1 = DataSource.write_df(df1)
        data_2 = DataSource.write_df(df2)
        return Outputs(data_1=data_1, data_2=data_2, data_3=None)
    
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m9_post_run_bigquant_run(outputs):
        return outputs
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    # 输入端:
      # input_1:接 输入层Input
      # input_2:基于 测试集代码列表 抽取的 指数特征(大盘因子)数据集
      # input_3:接 测试集代码列表
    # 输出端:
      # data_1:大盘因子滑动窗口训练集,数组类型字典
      # data_2:大盘因子滑动窗口测试集,数组类型字典
      # data_3:大盘因子测试集,丢弃第一个 前补 window,完整保留原始列   
        
    def m30_run_bigquant_run(input_1, input_2, input_3):
        # 示例代码如下。在这里编写您的代码
    
        #window = 7 # m18.data.shape[1] # 每个input的长度
        #window = input_1.shape[1] # 每个input的长度,对应 LSTM time_steps
        window = input_1 # 每个input的长度,对应 LSTM time_steps
        
        # 指数特征(大盘因子)数据集:基于 测试集 代码列表 时间区间,但多取了部分天数用于 训练集 
        df = input_2.read_df() 
        
        # 取 测试集开始日期 为 大盘因子测试集开始日期 
        start_date = input_3.read()['start_date'] 
        
        # 大盘因子名称去除前缀'bm_'
        cols = df.columns
        df.columns = [(col.partition('bm_')[2] if col.startswith('b') else col) for col in cols ]
        
        # 大盘因子数据集 去除 ['date','instrument','label'] 三列
        df_x = df[df.columns.difference(['date','instrument','label'] )] 
    
        train_df = df.query("date<@start_date") # 大盘因子训练集
        test_df = df.query("date>=@start_date") # 大盘因子测试集
    #    train_df = df[df['date'] < start_date]  # 大盘因子 训练集
    #    test_df = df[df['date'] >= start_date] # 大盘因子 预测集
    
    #    print('\n')
        print('\n大盘因子预处理模块:标准化、滑动窗口、滑动窗口型大盘因子数据集 拆分为 训练集和测试集')
        print('大盘因子预处理前训练集train_df长度:',len(train_df))
        print('大盘因子预处理前测试集test_df长度:',len(test_df))
        
        test_df = pd.concat([train_df.tail(window),test_df]) # 大盘因子测试集 向前补一个 window
        
    #    if len(train_df) < train_len:
    #        print('大盘因子数据集长度不足!')
    #        print('请调增‘指数特征抽取模块’的‘行情数据向前抽取天数’,或调减‘输入层Input’ shape 长度参数,或调减 train_length 参数!')
    #        return
        
        train_x = train_df[train_df.columns.difference(['date','instrument','label'] )] # 大盘因子训练集 x,去除 ['date','instrument','label'] 三列
        train_y = train_df['label'] # 大盘因子训练集 y,仅 ['label'] 列
        test_x = test_df[test_df.columns.difference(['date','instrument','label'] )] # 大盘因子测试集 x,去除 ['date','instrument','label'] 三列
        
    #    print('大盘因子测试集test_x长度:',len(test_x))
        
        # 数据处理:标准化
        from sklearn.preprocessing import scale
        train_x = scale(train_x) 
        test_x = scale(test_x)
        
        # 数据处理:窗口化,生成 滑动窗口数据,基于前一个 滑动窗口(window) 训练和预测   
        train_x = [(train_x[i:i+window]) for i in range(len(train_x)-window)]  # x 元素形状:(window,len(fields))
        train_y = [train_y[window+i:window+i+1].values[0] for i in range(len(train_y)-window)]
        
        test_x = [(test_x[i:i+window]) for i in range(len(test_x)-window)]  # x 元素形状:(window,len(fields))
    
    #    train_x = [scale(train_x[i:i+window]) for i in range(len(train_x)-window)]  # x 元素形状:(window,len(fields))
    #    test_x = [scale(test_x[i:i+window]) for i in range(len(test_x)-window)]  # x 元素形状:(window,len(fields))
    #    train_x = [scale(train_x[i:i+window]) for i in range(len(train_x)-window-1)]  # x 元素形状:(window,len(fields))
    #    train_y = [train_y[window+i:window+i+1].values[0] for i in range(len(train_y)-window-1)]
    #    train_x = [scale(train_x[i:i+window+1]) for i in range(len(train_x)-window)]  # x 元素形状:(window,len(fields))
    #    train_y = [train_y[window+i-1:window+i].values[0] for i in range(len(train_y)-window)]
    #    train_y = [train_y[i:i+1].values[0] for i in range(len(train_y)-window)]
    #    train_x = [scale(train_x[i:i+window]) for i in range(len(train_x))]  # x 元素形状:(window,len(fields))
    #    train_y = [train_y[i] for i in range(len(train_y)-window)]
    #    train_y = [train_y[i] for i in range(len(train_y))]
    #    test_x = [scale(df_x[i:i+window]) for i in range(len(df_x)-len(test_df),len(df_x)-window)]  # x 元素形状:(window,len(fields))
    #    test_x = [scale(df_x[i+1-window:i+1]) for i in range(len(df_x)-len(test_df),len(df_x))]  # x 元素形状:(window,len(fields))
    #    test_x = [scale(test_x[i:i+window]) for i in range(len(test_x))]  # x 元素形状:(window,len(fields))
    
        # LSTM 训练和预测模块接受 数组类型字典 的输入
        train = {'x':np.array(train_x),'y':np.array(train_y)}
        test  = {'x':np.array(test_x)}
    
        print('标准化及滑动窗口后大盘因子训练集train_x长度:',len(train_x))
        print('标准化及滑动窗口后大盘因子训练集train_y长度:',len(train_y))
        print('标准化及滑动窗口后大盘因子测试集test_x长度:',len(test_x), '\n')
    #    print('标准化及滑动窗口后大盘因子测试集test长度:',len(test['x']))
        
        data_1 = DataSource.write_pickle(train) # 大盘因子滑动窗口训练集,数组类型字典
        data_2 = DataSource.write_pickle(test)  # 大盘因子滑动窗口测试集,数组类型字典
        data_3 = DataSource.write_df(test_df[window:]) # 大盘因子测试集,丢弃第一个 前补 window,完整保留原始列   
    #    data_3 = DataSource.write_df(test_df) # 大盘因子测试集,完整保留原始列   
        return Outputs(data_1=data_1, data_2=data_2, data_3=data_3, data_4=None)
    
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m30_post_run_bigquant_run(outputs):
        return outputs
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    def m22_run_bigquant_run(input_1, input_2, input_3):
        # 示例代码如下。在这里编写您的代码
        
        pred_label = input_1.read_pickle() # 大盘因子预测标签
        df = input_2.read_df() # 大盘因子测试集,含实际标签
    #    window = 7 # m18.data.shape[1]
    #    df = df.head(len(df) - window)
        
        df = pd.DataFrame({'pred_label':pred_label[:,0], 'date':df.date, 'label':df.label})
        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 m22_post_run_bigquant_run(outputs):
        return outputs
    
    # 回测引擎:初始化函数,只执行一次
    def m56_initialize_bigquant_run(context):
        
        print('\n初始化函数。。。。。。')
        
        # 加载预测数据
        
        # 预测数据,通过 options.options['data'] 传入进来,使用 read_df 函数,加载到内存 (DataFrame)
    #    context.ranker_prediction = context.options['data'].read_df()
        
        # 带策略参数的预测结果数据字典,通过 options.options['data'] 传入进来
        pred_with_para_dict = context.options['data'].read()
    #    print('\n带策略参数的预测结果数据字典:\n',pred_with_para_dict)
        dict_slice = lambda adict, start, end: dict((k, adict[k]) for k in list(adict.keys())[start:end])
        import json
        print('\n策略参数字典:\n',json.dumps(dict_slice(pred_with_para_dict,1,len(pred_with_para_dict)),indent = 1,separators = (',',':    ')))
    
        # 预测结果数据
        context.ranker_prediction = pred_with_para_dict['pred']
        print('\n预测结果数据(即 pred 关键字 对应 DataFrame):\n',context.ranker_prediction)
                                   
        # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
        context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
        
        # 设置持仓股票数量
    #    context.stock_count = 5
    #    context.stock_count = 2
        context.stock_count = pred_with_para_dict['stock_count']
    
    #    # 每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[0.339160, 0.213986, 0.169580, ..]
    #    context.stock_weights = T.norm([1 / math.log(i + 2) for i in range(0, context.stock_count)])
        
        # 设置每只股票占用的最大、最小资金比例
        context.max_cash_per_instrument = 1 / context.stock_count  # 0.2
    #    context.min_cash_per_instrument = 0.02
        context.min_cash_per_instrument = pred_with_para_dict['min_cash_per_instrument']
        
        # 设置持仓/换仓周期(交易日)
    #    context.hold_days = 3
    #    context.hold_days = 30
        context.hold_days = pred_with_para_dict['hold_days']
    
        # 设置交易逻辑运行间隔周期(交易日)
    #    context.run_trade_module_per_days = 1
        context.run_trade_module_per_days = pred_with_para_dict['run_trade_module_per_days'] # 交易逻辑运行间隔周期(交易日)
        
        # 设置止损止盈幅度
        context.stop_loss = pred_with_para_dict['stop_loss']  # 止损
    #    context.stop_loss = 0.05  # 止损
    #    context.stop_loss = 0.03 # 止损
    #    context.stop_win  = 0.3   # 止盈
        
        # 设置目标涨幅和每个目标涨幅的回撤幅度
    #    context.target_up = 0.05 # 目标涨幅(即初始止盈幅度)
    #    context.target_up = 0.10 # 目标涨幅(即初始止盈幅度)
    #    context.target_up = 0.30 # 目标涨幅(即初始止盈幅度)
    #    context.target_up_draw_down = 0.3 # 每个目标涨幅回撤幅度
        context.target_up = pred_with_para_dict['target_up'] # 目标涨幅(即初始止盈幅度)
        context.target_up_draw_down = pred_with_para_dict['target_up_draw_down'] # 每个目标涨幅回撤幅度
    #    context.target_up_draw_down = 0.5 # 每个目标涨幅回撤幅度    
    
        # 设置大盘风控下跌幅度
        context.bm_riskctrl_down = pred_with_para_dict['bm_riskctrl_down'] # 大盘风控下跌幅度
        
        # 设置仓位控制用平均市盈率上下限,即满仓市盈率和空仓市盈率
        context.min_pe = pred_with_para_dict['min_pe'] # 满仓市盈率,仓位控制用平均市盈率下限
        context.max_pe = pred_with_para_dict['max_pe'] # 空仓市盈率,仓位控制用平均市盈率上限
        
        # 设置根据大盘预测涨跌幅调整仓位比例倍数
        context.bm_pred_label_times = pred_with_para_dict['bm_pred_label_times'] # 根据大盘预测涨跌幅调整仓位比例倍数
       
       
        
    
    # 回测引擎:每个单位时间(即k线对应时间周期,如分钟线、日线、周线等)数据处理函数,每个单位时间结束后调用一次
    def m56_handle_data_bigquant_run(context, data):
        
        print('\n数据准备函数。。。。。。')
        
        # 加载 大盘风控数据(大盘因子预测值) 和 仓位控制数据(市场平均市盈率)
    #    df = context.options['data'].read_df()
        df = context.options['data'].read()['pred']
    
    #################################### 大盘风控模块开始 ###########################################################   
        #在数据准备函数中一次性计算每日的大盘风控条件相比于在handle中每日计算风控条件可以提高回测速度
        #df['pred_label'] = df['pred_label'].fillna(0)
        # 将大盘预测值做分箱处理,如果预测值<-0.01,取为-1。
    #    df['direction'] = pd.cut(df['pred_label'],bins=[-float('inf'),-0.0025,0.0025,float('inf')],labels=[-1,0,1])
    #    pred_label_bins = [-float('inf'),-0.02,-0.01,-0.0025,0.0025,0.01,0.02,0.04,float('inf')]
    #    pred_label_bin_labels =       [-1,   -0.3, -0.15,    0,     0.1, 0.2, 0.3,  1]
    #    pred_label_bins = [-float('inf'),-0.05,-0.0025,0.0025,0.05,float('inf')]
    ##    pred_label_bins = [-float('inf'),-0.05,-0.0025,0.02,0.1,float('inf')]  # 非对称分箱
    #    pred_label_bins = [-float('inf'),-0.05,-0.01,0.01,0.05,float('inf')]
    ##    pred_label_bin_labels =       [-1,   -0.2,    0,     0.5,  1]
    ##    df['direction'] = pd.cut(df['pred_label'],bins=pred_label_bins,labels=pred_label_bin_labels)
    ##    context.dataprediction = df[['date','direction']]
        
        bm_riskctrl_down = context.options['data'].read()['bm_riskctrl_down'] # 大盘风控下跌幅度
    #    bm_riskctrl_down = context.pred_with_para_dict['bm_riskctrl_down'] # 大盘风控下跌幅度
        df['direction'] = np.where(df['pred_label'] < -bm_riskctrl_down, -1, 0)
    #    df['direction'] = np.where(df['pred_label'] < -0.02, -1, 0)
    #    df['direction'] = np.where(df['pred_label'] < -0.02, -1, 1*df['pred_label'] )
    #    df['direction'] = np.where(df['pred_label'] < -0.002, -1, np.where(df['pred_label'] > 0.01, 25*df['pred_label'], 0))
    #    df['direction'] = np.where(df['pred_label'] < -0.02, -1, np.where(df['pred_label'] > 0.01, 20*df['pred_label'], 10*df['pred_label']))
    #    df['direction'] = np.where(df['pred_label'] < -0.005, -1, np.where(df['pred_label'] > 0.01, 25*df['pred_label'], 15*df['pred_label']))
        context.bm_direction_and_label = df[['date','direction','pred_label','label']].drop_duplicates(['date'])
        
        print('\n大盘预测数据:\n',context.bm_direction_and_label)
    #    print(pd.DataFrame(dict(list(context.bm_direction_and_pred_label.groupby(['date'],as_index=False)))).reset_index())
    #    print(pd.DataFrame(context.bm_direction_and_pred_label.groupby(['date'],as_index=False)).describe().reset_index())
            
    #################################### 大盘风控模块结束 ###########################################################   
        
    ############################### ST、退市整理、涨跌停等处理开始 #################################    
        # 获取ST状态、涨跌停状态、上市天数等状态信息
        context.status_df = D.features(instruments = context.instruments, start_date = context.start_date, end_date = context.end_date, 
                               fields=['st_status_0','price_limit_status_0','price_limit_status_1','list_days_0','return_0','amount_0','amount_1'])
        context.status_df['delta_amount'] = (context.status_df['amount_0']-context.status_df['amount_1'])/context.status_df['amount_1']
        # 获取股票代码对应的股票名称
    #    context.status_df = D.history_data(instruments =context.instruments,start_date = context.start_date, end_date = context.end_date, 
    #                           fields=['name','st_status','price_limit_status'])
        context.name_df = DataSource('instruments_CN_STOCK_A').read(instruments = context.instruments, start_date=context.start_date, end_date=context.end_date, fields=['name'])
        
        # 设置按['date','instrument']多级索引
        context.status_df = context.status_df.set_index(['date','instrument'])
        context.name_df = context.name_df.set_index(['date','instrument'])
        # 索引按['date','instrument'] lexsort 排序
        context.status_df = context.status_df.sort_index(level=['date','instrument'])
        context.name_df = context.name_df.sort_index(level=['date','instrument'])
     ############################### ST、退市整理、涨跌停等处理结束 #################################    
     
     ############################### 仓位管理模块开始 #################################    
        # 采用基于市场平均市盈率的仓位控制策略:根据市场平均市盈率所处区间设置相应仓位比重
        # 市场平均市盈率 <30,   投入 100%   资金,即满仓
        # 市场平均市盈率 30~100,投入 5%~90% 资金
        # 市场平均市盈率 >100,  投入   0%   资金,即清仓
        # 设置分箱规则:2005 以来全市场平均  市盈率最小 18,最大 108,将市场平均市盈率在在此范围内分箱
    #    pe_bins = [-float('inf'), 30,  35,  40,  45,  50,  60,  70,  80,  85,  90,  100, float('inf')]
    #    # 设置分箱标签,以对应仓位比重为分箱标签
    #    pe_bin_labels =         [1,  0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.05,  0]
    #    # 将市场平均市盈率分箱,获得对应分箱的仓位比重,并存入全局变量
    #    context.position_ratio = df.groupby('date').apply(lambda x: pd.cut(x['avg_pe'], bins=pe_bins, labels=pe_bin_labels))
    #    context.position_ratio = pd.DataFrame(context.position_ratio)
    #    context.position_ratio.columns = ['position_ratio']
    #    context.position_ratio = context.position_ratio.reset_index().drop('level_1',axis=1).drop_duplicates().set_index('date')
        
    #    min_pe = 30
    #    max_pe = 100
    #    min_pe = context.pred_with_para_dict['min_pe'] # 满仓市盈率,仓位控制用平均市盈率下限
    #    max_pe = context.pred_with_para_dict['max_pe'] # 空仓市盈率,仓位控制用平均市盈率上限
    #    min_pe = df['avg_pe'].min() 
    #    max_pe = df['avg_pe'].max() 
    #    df['avg_pe'] = df['avg_pe'].clip(lower=min_pe,upper=max_pe) # 截取 min_pe 和 max_pe 之间的 avg_pe
        
        if 'avg_pe'in df.columns:
        
            min_pe = context.options['data'].read()['min_pe'] # 满仓市盈率,仓位控制用平均市盈率下限
            max_pe = context.options['data'].read()['max_pe'] # 空仓市盈率,仓位控制用平均市盈率上限
           
            # 生成仓位比重数据,平均市盈率越高,仓位越低
            context.position_ratio = df.groupby('date').apply(lambda x:1-(x['avg_pe']-min_pe)/(max_pe-min_pe))   
        
            context.position_ratio = pd.DataFrame(context.position_ratio)
            context.position_ratio.columns = ['position_ratio']
            context.position_ratio = context.position_ratio.reset_index().drop('level_1',axis=1).drop_duplicates().set_index('date')
        
            print('\n平均市盈率数据:')
            print(df[['date','avg_pe']])
            print("\nmax_avg_pe:",df['avg_pe'].max())
            print("min_avg_pe:",df['avg_pe'].min())
            print('\n仓位控制数据:')
            print(context.position_ratio)
            
        else:
            
            context.position_ratio = df.groupby('date').apply(lambda x:1)   
            
            context.position_ratio = pd.DataFrame(context.position_ratio)
            context.position_ratio.columns = ['position_ratio']
            context.position_ratio = context.position_ratio.reset_index().drop_duplicates().set_index('date')
        
            print('\n仓位控制数据:')
            print(context.position_ratio)        
        
    ############################### 仓位管理模块结束 #################################    
        
        
        
        print('\n交易/回测模块主函数。。。。。。')
        
    #---------------------START:大盘风控模块(含建仓期)--------------------------
        current_stoploss_stock = [] 
        today_date = data.current_dt.strftime('%Y-%m-%d')
        
        # 获取当前持仓情况
        positions_all = [equity.symbol for equity in context.portfolio.positions]
        
        # 获取当前大盘预测情况
        #ds_id=context.predataid
        #today_prediction = DataSource(id=ds_id).read_pickle()
        bm_direction_and_label = context.bm_direction_and_label
        today_bm_direction = bm_direction_and_label[bm_direction_and_label.date == today_date].direction.values[0]
        today_bm_pred_label = bm_direction_and_label[bm_direction_and_label.date == today_date].pred_label.values[0]
        today_bm_label = bm_direction_and_label[bm_direction_and_label.date == today_date].label.values[0]
        
        print(today_date,'次日大盘方向预测:',today_bm_direction)
        print(today_date,'次日大盘涨跌幅预测:',today_bm_pred_label)
        print(today_date,'次日大盘涨跌幅实际:',today_bm_label)
        
        # 满足空仓条件
        if today_bm_direction == -1: # < 0 	
            if len(positions_all)>0:
                # 全部卖出后返回
                for i in positions_all:
    # 当日不能交易股票,不代表次日也不能交易,不应排除!! 
    #                if data.can_trade(context.symbol(i)): 
                        context.order_target_percent(context.symbol(i), 0)
                        current_stoploss_stock.append(i)
                        print('日期:', today_date, '股票:', i, '风控清仓!')
            print('风控执行',today_date)
            return  # 运行风控后当日结束,不再执行后续的买卖订单
    #------------------------END:大盘风控模块(含建仓期)---------------------------
    
    #------------------------------------------止损模块START--------------------------------------------
        from userlib import mylib
        
        # 测试变量 current_stoploss_stock 是否存在,若不存在则赋初值为空列表
        try:
            current_stoploss_stock = current_stoploss_stock  
        except NameError:
            current_stoploss_stock = [] 
        
        # 获取当日日期
        today = data.current_dt  
        
        # 获取当前持仓情况
        equities = {e.symbol: p for e, p in context.portfolio.positions.items() if p.amount>0}
        
        # 新建当日止损股票列表是为了 handle_data 策略逻辑部分不再对该股票进行判断
        #current_stoploss_stock = [] 
        
        if len(equities) > 0:# 如果有持仓
            for i in equities.keys():
    
    # 当日不能交易股票,不代表次日也不能交易,不应排除!! 
    #            if data.can_trade(context.symbol(i)):
                    
                    # 持仓成本
                    stock_cost = equities[i].cost_basis 
                    # 最新市场价格
                    stock_market_price = data.current(context.symbol(i), 'price')  
                    last_sale_date = equities[i].last_sale_date   # 上次交易日期
                    delta_days = today - last_sale_date  
                    hold_days = delta_days.days # 持仓天数
                    # 建仓以来的最高价
                    highest_price_since_buy = data.history(context.symbol(i), 'high', hold_days, '1d').max()
                
                #    # 按建仓以来最高价下跌指定幅度确定止损位置
                #    stoploss_line = highest_price_since_buy * (1 - context.stop_loss)
    
                    # 确定止损位置
                    #stoploss_line = stock_cost * (1 - context.stop_loss)           
                    stoploss_line = mylib.move_stop_win_loss_price(buy_price = stock_cost,
                                                                   current_price = stock_market_price,
                                                                   high_price = highest_price_since_buy,
                                                                   prime_stop_loss = context.stop_loss,
                                                                   target_up = context.target_up,
                                                                   target_up_draw_down = context.target_up_draw_down)           
                    record('止损位置', stoploss_line)
                    
                    # 如果已经止损,则跳过,防止卖空
                    if i in current_stoploss_stock:
                        continue
                    # 如果价格下穿止损位置
                    if stock_market_price < stoploss_line:
                        #context.order_target(context.symbol(i), 0)     
                        context.order_target_percent(context.symbol(i), 0)
                        current_stoploss_stock.append(i)
                        print('日期:', today, '股票:', i, '出现止损状况','成本价:',stock_cost,'最新价:',stock_market_price)
                 #   # 如果价格上穿止盈位置
                 #   if stock_market_price > stock_cost * (1 + context.stop_win):
                 #       context.order_target(context.symbol(i), 0)     
                 #       print('日期:', today, '股票:', i, '出现止盈状况')
    #-------------------------------------------止损模块END----------------------    
    
    #-------------------------------------------周期控制模块BEGIN----------------------    
        # 交易逻辑运行周期控制
        # 按 context.run_trade_module_per_days 记录的 交易逻辑运行间隔周期 控制 交易模块 的运行
        # 每隔 context.run_trade_module_per_days 天 运行 一次 交易模块
        if context.trading_day_index % context.run_trade_module_per_days != 0: 
            return
    #-------------------------------------------周期控制模块END----------------------    
    
    #-------------------------------------------交易模块BEGIN----------------------     
        today = data.current_dt.strftime('%Y-%m-%d')
        today_dt = data.current_dt
          
        # 按日期过滤得到今日的预测数据
        ranker_prediction = context.ranker_prediction[context.ranker_prediction.date == today]
    
        stock_count = context.stock_count # 持仓股票数量
        
        #---------------------------处理需要买入的股票列表---------------------- 
        
        # 需要买入的股票:排名前 context.stock_count 的股票
        #stock_to_buy = list(ranker_prediction.instrument[:stock_count])
        #stock_to_buy = list(ranker_prediction.instrument[:3 * stock_count])  # 因存在剔除情况,故多取一点   
        stock_to_buy = list(ranker_prediction.instrument[: stock_count + 10 ])  # 因存在剔除情况,故多取一点   
        print("日期:",today,"原始stock_to_buy:",stock_to_buy)
        #print("日期:",today,"止损:",current_stoploss_stock)    
        
        # 需要买入的股票:去除当天不能交易股票
    # 当日不能交易股票,不代表次日也不能交易,不应排除!! 
    #    stock_to_buy = [i for i in stock_to_buy if data.can_trade(context.symbol(i))]
    #    print("日期:",today,"去除当天不能交易股票后stock_to_buy:",stock_to_buy)
        
        # 需要买入的股票:去除已止损股票,防止当天卖出后又买入
        stock_to_buy = [i for i in stock_to_buy if i not in current_stoploss_stock]
        print("日期:",today,"去除止损股票后stock_to_buy:",stock_to_buy)
        
    #    # 需要买入的股票:去除多取的股票
    #    stock_to_buy = stock_to_buy[:stock_count]
    #    print("日期:",today,"去除多取股票后stock_to_buy:",stock_to_buy)
        
        #---------------------------结束处理需要买入的股票列表----------------------
        
        # 通过positions对象,使用列表生成式的方法获取目前持仓的股票列表
        stock_hold_now = [equity.symbol for equity in context.portfolio.positions]
        print("日期:",today,"持仓暨原始stock_to_sell:",stock_hold_now)
        
        #---------------------------处理需要继续持有的股票列表----------------------
        
        # 继续持有的股票:截至当天 持仓时间 不足 context.hold_days 指定 持仓周期 的股票继续持有,注意股票买入当天盘后计入持仓但持仓时间为 0 。
        no_need_to_sell = [i for i in stock_hold_now if (today_dt - equities[i].last_sale_date).days + 1 < context.hold_days]
        print("日期:",today,"持仓不卖股--持仓时间不足股:",no_need_to_sell)
        
    #    # 继续持有的股票:当天排名靠后的股票不应继续持有,如后 context.stock_count 名
    #    no_need_to_sell = [i for i in no_need_to_sell if i not in list(ranker_prediction.instrument[-2 * context.stock_count:])]
    #    print("日期:",today,"持仓不卖股--剔除排名靠后股后:",no_need_to_sell)
    #    # 继续持有的股票:非当天要买入的股票即非当天排名靠前的股票不应继续持有
    #    no_need_to_sell = [i for i in no_need_to_sell if i not in stock_to_buy]
    #    print("日期:",today,"持仓不卖股--剔除非排名靠前股后:",no_need_to_sell)
        
        # 继续持有的股票:如果当天要买入的前 stock_count 只股票已经存在于目前的持仓里,那么应继续持有
        # no_need_to_sell = no_need_to_sell + [i for i in stock_hold_now if i in stock_to_buy]
        no_need_to_sell = list(set(no_need_to_sell + [i for i in stock_hold_now if i in stock_to_buy[:stock_count]]))
        print("日期:",today,"持仓不卖股--追加要买入股后:",no_need_to_sell)
    
    # 当日不能交易股票,不代表次日也不能交易,不应排除!!    
    #    # 继续持有的股票:当天不能交易的股票被动继续持有
    #    no_need_to_sell = no_need_to_sell + [i for i in stock_hold_now if not data.can_trade(context.symbol(i))]
    #    no_need_to_sell = list(set(no_need_to_sell + [i for i in stock_hold_now if not data.can_trade(context.symbol(i))]))
    #    print("日期:",today,"持仓不卖股--追加不能交易股后:",no_need_to_sell)
        
        # 继续持有的股票:剔除止损股票,止损股票不再继续持有
        no_need_to_sell = [i for i in no_need_to_sell if i not in current_stoploss_stock]
        print("日期:",today,"持仓不卖股--剔除止损股后:",no_need_to_sell)
        
        #---------------------------结束处理需要继续持有的股票列表----------------------
        
        #---------------------------处理需要卖出的股票列表----------------------
        
        # 需要卖出的股票:去除持仓不卖股
        stock_to_sell = [i for i in stock_hold_now if i not in no_need_to_sell]
        print("日期:",today,"去除持仓不卖股后stock_to_sell:",stock_to_sell)
        
        # 需要卖出的股票:去除已止损股票,防止卖空
        stock_to_sell = [i for i in stock_to_sell if i not in current_stoploss_stock]
        print("日期:",today,"已止损股:",current_stoploss_stock)
        print("日期:",today,"去除止损股后stock_to_sell:",stock_to_sell)
        
        #---------------------------结束处理需要卖出的股票列表----------------------
        
        #---------------------------继续处理需要买入、卖出的股票列表---------------------- 
         
        # 需要买入的股票:去除不卖的股票,持仓不卖的股票不买
        stock_to_buy = [i for i in stock_to_buy if i not in no_need_to_sell]
        print("日期:",today,"去除持仓不卖股后stock_to_buy:",stock_to_buy)
        
        #------------------------涨跌停状态、ST状态、上市首日股、“退”字股等异常处理模块开始-------------------------
        if True:
            # context.status_df、context.name_df 均为 ['date','instrument'] 二级索引
            # 获取当日涨跌停状态、ST状态数据
            df_status = context.status_df.loc[(today,slice(None)),:]
            # 当日涨停股,涨跌停状态:1表示跌停,2表示未涨跌停,3则表示涨停
            stock_limit_up = df_status[df_status.price_limit_status_0==3].index.get_level_values(1).tolist()
            # 当日一字板涨停股
            stock_limit_up_yzb = [i for i in stock_limit_up if data.current(context.symbol(i),'high')==data.current(context.symbol(i),'low')] 
            #print("日期:",today,"涨停股:",stock_limit_up)
    #        # 当日ST股,ST状态:0:正常股票,1:ST,2:*ST,11:暂停上市
    #        stock_st = df_status[df_status.st_status_0!=0].index.get_level_values(1).tolist()
            # 当日*ST股和暂停上市股,ST状态:0:正常股票,1:ST,2:*ST,11:暂停上市
            stock_st = df_status[df_status.st_status_0 > 1].index.get_level_values(1).tolist()
            #print("日期:",today,"ST股:",stock_st)
            # 当日上市首日股
            stock_list_1st_day = df_status[df_status.list_days_0==0].index.get_level_values(1).tolist()
            # 获取当日股票名称数据
            stock_name = context.name_df.loc[(today,slice(None)),:]
    
            # 去除*ST、暂停上市股票,不买入*ST、暂停上市股票
    #        print("日期:",today,"ST不买:", [i for i in stock_to_buy if i in stock_st])
    #        stock_to_buy = [i for i in stock_to_buy if  i not in stock_st]    
    #        print("日期:",today,"去除ST股不买后stock_to_buy:", stock_to_buy)
            print("日期:",today,"*ST股、暂停上市股不买:", [i for i in stock_to_buy if i in stock_st])
            stock_to_buy = [i for i in stock_to_buy if  i not in stock_st]    
            print("日期:",today,"去除*ST股、暂停上市股不买后stock_to_buy:", stock_to_buy)
            
            # 去除退市整理期股票,不买入退市整理期股票
            print("日期:",today,"'退'字股不买:", [i for i in stock_to_buy if '退' in stock_name.loc[(today,i),:].name.values[0]])
            stock_to_buy = [i for i in stock_to_buy if '退' not in stock_name.loc[(today,i),:].name.values[0]]    
            print("日期:",today,"去除'退'字股不买后stock_to_buy:", stock_to_buy)
    
    #        # 去除涨停股票,涨停不买
    #        print("日期:",today,"涨停不买:", [i for i in stock_to_buy if i in stock_limit_up])
    #        stock_to_buy = [i for i in stock_to_buy if i not in stock_limit_up]
            
    #        # 去除一字涨停股票,一字涨停不买(因为次日大概率继续一字涨停?)
    #        print("日期:",today,"一字涨停不买:", [i for i in stock_to_buy if i in stock_limit_up_yzb])
    #        stock_to_buy = [i for i in stock_to_buy if i not in stock_limit_up_yzb]
    #        print("日期:",today,"去除一字涨停股不买后stock_to_buy:", stock_to_buy)
            
            # 去除上市首日股票,上市首日不买(因为次日大概率一字涨停)
            print("日期:",today,"上市首日不买:", [i for i in stock_to_buy if i in stock_list_1st_day])
            stock_to_buy = [i for i in stock_to_buy if i not in stock_list_1st_day]
            print("日期:",today,"去除上市首日股不买后stock_to_buy:", stock_to_buy)
            
            # 去除涨停股票,涨停不卖
            print("日期:",today,"涨停不卖:", [i for i in stock_to_sell if i in stock_limit_up])
            stock_to_sell = [i for i in stock_to_sell if i not in stock_limit_up]
            print("日期:",today,"去除涨停不卖股后stock_to_sell:", stock_to_sell)
            
    #        # 去除一字涨停股票,一字涨停不卖
    #        print("日期:",today,"一字涨停不卖:", [i for i in stock_to_sell if i in stock_limit_up_yzb])
    #        stock_to_sell = [i for i in stock_to_sell if i not in stock_limit_up_yzb]
    #        print("日期:",today,"去除一字涨停不卖股后stock_to_sell:", stock_to_sell)
        
        #------------------------涨跌停状态、ST状态、上市首日股、“退”字股等异常处理模块结束-------------------------
        
        # 记录 全局 买入股票列表,供 次日 盘前处理函数 处理 停牌股票买入订单
        context.stock_to_buy = stock_to_buy
        
        #------------------------持仓股票数量控制模块开始-------------------------
    
    #   # 固定数量补仓
    #     stock_to_buy = stock_to_buy[:stock_count]  
        
        # 卖几补几,维持固定数量持仓
        stock_to_buy = stock_to_buy[: (stock_count - len(stock_hold_now) + len(current_stoploss_stock) + len(stock_to_sell))]  
        print("日期:",today,"持仓股票数量控制后stock_to_buy:", stock_to_buy)
        
        #------------------------持仓股票数量控制模块结束-------------------------
        
        # 根据持仓股票数量控制情况 修正 全局 买入股票列表
        context.stock_to_buy = context.stock_to_buy[len(stock_to_buy):]
        
        print("日期:",today,"最终stock_to_buy:",stock_to_buy)
        print("日期:",today,"最终stock_to_sell:",stock_to_sell)
        
        #---------------------------结束继续处理需要买入、卖出的股票列表---------------------- 
    
    #    print('账户现金:',context.portfolio.cash)
        
        # 卖出
        for stock in stock_to_sell:
            # 如果该股票停牌,则没法成交。因此需要用can_trade方法检查下该股票的状态
            # 如果返回真值,则可以正常下单,否则会出错
            # 因为stock是字符串格式,我们用symbol方法将其转化成平台可以接受的形式:Equity格式
    #        if data.can_trade(context.symbol(stock)):
    # 当日不能交易股票,不代表次日也不能交易,不应排除!!            
                # order_target_percent是平台的一个下单接口,表明下单使得该股票的权重为0,
                #   即卖出全部股票,可参考回测文档
                context.order_target_percent(context.symbol(stock), 0)
                print("日期:",today,"卖出:",stock)
        
        # 如果当天没有买入的股票,就返回
        if len(stock_to_buy) == 0:
            return
        
        #-------------------------------------------仓位控制模块 BEGIN ----------------------    
        # 获取今日 目标仓位比
        # position_ratio = context.position_ratio.loc[today,'position_ratio'][0]
        # position_ratio = 0.8
        position_ratio = context.position_ratio.loc[today,'position_ratio'] 
        print("日期:",today,"目标仓位比例:", position_ratio)
    
    #    # 视乎大盘择时预测结果调增仓位
    #    position_ratio = position_ratio + 0.2 if today_prediction > 0 else position_ratio
    #    # 仓位比例最高为 1
    #    position_ratio = position_ratio if position_ratio <= 1 else 1
        
        # 视乎大盘择时预测结果调整仓位
        position_ratio = position_ratio + context.bm_pred_label_times  * (today_bm_pred_label if not np.isnan(today_bm_pred_label) else 0)
        print('今日大盘择时仓位调整比例:',context.bm_pred_label_times  * (today_bm_pred_label if not np.isnan(today_bm_pred_label) else 0))
        
        # 仓位比例最低为 0, 最高为 1
        position_ratio = max(0,position_ratio)
        position_ratio = min(1,position_ratio)
       
        # portfolio_value:  账户总价值(包括持仓市值+现金)
        # positions_value:  持仓市值
        
    #    # 结合目标仓位比、已持仓比例,计算今日 买入仓位比例
    #    position_ratio_had = context.portfolio.positions_value / context.portfolio.portfolio_value  # 现已持仓比例
    #    position_ratio_to_buy = position_ratio - position_ratio_had # 调减现已持仓比例
    #    #position_ratio_to_buy = position_ratio
    #    print("日期:",today,"目标仓位比例:", position_ratio)
    #    print("日期:",today,"已持仓比例:", position_ratio_had)
    #    print("日期:",today,"买入仓位比例:", position_ratio_to_buy)
        
        # 按 继续持仓清单 no_need_to_sell 估算 继续持仓比例
        position_value_no_need_to_sell = 0
        for stock in no_need_to_sell:
            equity = context.portfolio.positions[context.symbol(stock)]
            # 继续持仓市值
            position_value_no_need_to_sell += equity.amount * equity.last_sale_price  
        # 继续持仓比例
        position_ratio_no_need_to_sell = position_value_no_need_to_sell / context.portfolio.portfolio_value  
        
        # 结合目标仓位比例、继续持仓比例,计算 买入仓位比例
        position_ratio_to_buy = position_ratio - position_ratio_no_need_to_sell # 调减继续持仓比例
       
        print("日期:",today,"持仓市值:", context.portfolio.positions_value)
        print("日期:",today,"组合价值:", context.portfolio.portfolio_value)
        print("日期:",today,"大盘择时仓位调整后目标仓位比例:", position_ratio)
        print("日期:",today,"继续持仓市值:", position_value_no_need_to_sell)
        print("日期:",today,"继续持仓比例:", position_ratio_no_need_to_sell)
        print("日期:",today,"买入仓位比例:", position_ratio_to_buy)
        
        # 如果当天 买入仓位比例 非正(即无需买入),返回
        if position_ratio_to_buy <= 0:
            print("日期:",today,"仓位控制,无需买入!")
            return
        
        # 如果当天 买入仓位比例 较小,如不超过 单股最低资金比例 context.min_cash_per_instrument ,则调整 stock_to_buy 为仅买入 1 只股票,防止单股仓位比例过低
        if position_ratio_to_buy <= context.min_cash_per_instrument:
            stock_to_buy = stock_to_buy[:1]
            print("日期:",today,"买入仓位比例低于单股最低资金比例 %s ,调整后stock_to_buy:" % context.min_cash_per_instrument,stock_to_buy)
        #-------------------------------------------仓位控制模块 END ----------------------    
        
        #-------------------------------------------权重控制模块 BEGIN ----------------------    
        # 计算今日购买权重
        # 等权重买入 
    #    weights =  position_ratio_to_buy / len(stock_to_buy)  
        # 非等权重买入 
        # 每只的股票的权重,如下的权重分配会使得靠前的股票分配多一点的资金,[0.339160, 0.213986, 0.169580, ..]
        weights = T.norm([1 / math.log(i + 2) for i in range(0, len(stock_to_buy))])
        weights = pd.Series(weights)
        weights = position_ratio_to_buy * weights  
        #-------------------------------------------权重控制模块 END ----------------------    
        
        # 买入
        for stock in stock_to_buy:
    #        if data.can_trade(context.symbol(stock)):
    # 当日不能交易股票,不代表次日也不能交易,不应排除!!                        
                # 计算股票 stock 对应的购买权重
                weight = weights if isinstance(weights,float) else weights[stock_to_buy.index(stock)] 
                
                # 购买权重考虑每只股票占用最大资金比例 context.max_cash_per_instrument 的约束
                weight = min(weight,context.max_cash_per_instrument)
                
                # 下单使得某只股票的持仓权重达到 weight
                context.order_target_percent(context.symbol(stock), weight)
                print("日期:",today,"买入:",stock,"买入持仓权重:",weight)
    
    #-------------------------------------------交易模块END----------------------     
    
        
        
        
    
    # 回测引擎:准备数据,回测情景仅第一天执行一次,模拟实盘情景每天运行一次
    def m56_prepare_bigquant_run(context):
        
        pass
    # 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。
    def m56_before_trading_start_bigquant_run(context, data):
        
        print('\n盘前处理函数。。。。。。')
        
        # 对即将撮合的订单进行二次过滤
        
        # 获取当日日期
        today = data.current_dt.strftime('%Y-%m-%d')
        
        # 在开盘前对当天停牌且在购买名单内的股票进行取消交易
        # 并将原计划用于交易该股票的钱分摊到 全局买入股票列表 context.stock_to_buy 中的下一只股票进行购买
        for open_orders in context.get_open_orders().values():
            for order in open_orders:
                sid = order.sid
                price = data.current(sid, 'price')
                symbol = sid.symbol
                if not data.can_trade(sid):  # 股票停牌
                    context.cancel_order(order)
                    print('日期:', today, '停牌取消买单:{},数量为:{}'.format(symbol, order.amount))
                    context._in_before_trading_start = False
                    plan_amount = order.amount * price
                    alternative_symbol = [i for i in context.stock_to_buy if i != symbol and data.can_trade(i)]
    #                alternative_symbol = [i for i in instruments if i != symbol]
                    context.order_target_value(context.symbol(alternative_symbol[0]), plan_amount)    
                    print('日期:', today, '更新为买单:{},预计成交金额为:{}'.format(alternative_symbol[0], plan_amount))
        
    #######################################################################################################################    
        if False:
            # 周期控制
            if context.trading_day_index % context.hold_days != 0: # 按 context.hold_days 记录的持仓天(交易日)数换仓
                return
    
            # 获取当日日期(因盘前主要处理前一交易日盘后订单,故当日日期取前一交易日日期。)
            today = data.current_dt.strftime('%Y-%m-%d')
            print('当前交易日:',today)
            trading_days = D.trading_days(start_date=context.start_date, end_date=context.end_date)  # 获取相应交易日历
            today_loc = trading_days[trading_days['date']==today].index[0] # 当前交易日对应标签索引
            today_iloc = trading_days.index.get_loc(today_loc)  # 当前交易日在交易日历里的对应位置索引
            if today_iloc == 0:  # 首个交易日不做盘前处理
                return
            today = trading_days.at[trading_days[trading_days['date']==today].index[0] - 1 ,'date'].strftime('%Y-%m-%d') # 取前一交易日日期
            print('前一交易日:',today)
            
            # 获取当日涨跌停状态、ST状态数据
            df_price_limit_status = context.status_df.loc[(today,slice(None)),:]
            # 获取当日股票名称数据
            df_name = context.name_df.loc[(today,slice(None)),:]
    
            # 得到当前未完成订单
            for orders in get_open_orders().values():
               # 循环,撤销订单
                for _order in orders:
                    ins = str(_order.sid.symbol)
                    #print(_order.amount,
                    #      ins,df_price_limit_status[df_price_limit_status.instrument==ins].price_limit_status_0.values[0],
                    #      df_name[df_name.instrument==ins].name.values[0])
                    try:
                        df_ins = df_price_limit_status.loc[(today,ins),:]
                        # 如果当日涨停,则取消卖单
                        #if  df_price_limit_status[df_price_limit_status.instrument==ins].price_limit_status_0.values[0]>2 and _order.amount<0:
                        if  df_ins.price_limit_status_0.values[0]>2 and _order.amount<0:
                            cancel_order(_order)
                            print(today,'尾盘涨停取消卖单',ins) 
        #                # 如果当日量价均上涨,则取消卖单
        #                if  df_ins.return_0.values[0]>1.05 and df_ins.delta_amount.values[0]>0.05 and _order.amount<0:
        #                    cancel_order(_order)
        #                    print(today,'正在上涨的股票取消卖单',ins) 
    
                        # 如果当日为退市整理期股,则取消买单
                        #if  '退' in df_price_limit_status[df_price_limit_status.instrument==ins].name.values[0] and _order.amount>0:
                        #    cancel_order(_order)
                        #    print(today,'退市整理期取消买单',ins) 
                        if  '退' in df_name.loc[(today,ins),:].name.values[0] and _order.amount>0:
                            cancel_order(_order)
                            print(today,'退市整理期取消买单',ins) 
    
                        # 如果当日为ST股,则取消买单
                        if  df_ins.st_status_0.values[0]!=0 and _order.amount>0:
                            cancel_order(_order)
                            print(today,'ST或暂停上市股取消买单',ins) 
                    except:
                        continue
    
    
    g = T.Graph({
    
        'm4': 'M.instruments.v2',
        'm4.start_date': '2020-10-01',
        'm4.end_date': '2020-12-31',
        'm4.market': 'CN_STOCK_A',
        'm4.instrument_list': '',
        'm4.max_count': 0,
    
        'm5': 'M.advanced_auto_labeler.v2',
        'm5.instruments': T.Graph.OutputPort('m4.data'),
        'm5.label_expr': """# #号开始的表示注释
    # 0. 每行一个,顺序执行,从第二个开始,可以使用label字段
    # 1. 可用数据字段见 https://bigquant.com/docs/develop/datasource/deprecated/history_data.html
    #   添加benchmark_前缀,可使用对应的benchmark数据
    # 2. 可用操作符和函数见 `表达式引擎 <https://bigquant.com/docs/develop/bigexpr/usage.html>`_
    
    # 计算收益:5日收盘价(作为卖出价格)除以明日开盘价(作为买入价格)
    shift(close, -1) / shift(close, 0)
    #shift(close, -5) / shift(open, -1)
    #shift(close, -2) / shift(close, -1)
    #shift(close, -2) / shift(open, -2)
    
    # 极值处理:用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)
    """,
        'm5.start_date': '',
        'm5.end_date': '',
        'm5.benchmark': '000300.SHA',
        'm5.drop_na_label': True,
        'm5.cast_label_int': True,
        'm5.user_functions': {},
    
        'm15': 'M.instruments.v2',
        'm15.start_date': T.live_run_param('trading_date', '2021-07-01'),
        'm15.end_date': T.live_run_param('trading_date', '2021-08-31'),
        'm15.market': 'CN_STOCK_A',
        'm15.instrument_list': '',
        'm15.max_count': 0,
    
        'm33': 'M.input_features.v1',
        'm33.features': """
    # #号开始的表示注释,注释需单独一行
    # 多个特征,每行一个,可以包含基础特征和衍生特征,特征须为本平台特征
    open=open_0
    close=close_0
    high=high_0
    low=low_0
    volume=volume_0
    amount= amount_0
    vwap=amount / volume
    ret=close_0 / close_1 - 1
    dtm=where(open<=delay(open,1),0,max((high-open),(open-delay(open,1))))
    dbm=where(open>=delay(open,1),0,max((open-low),(open-delay(open,1))))
    tr=max(max(high-low,abs(high-delay(close,1))),abs(low-delay(close,1)))
    hd=high-delay(high,1)
    ld=delay(low,1)-low 
    """,
    
        'm43': 'M.features_short.v1',
        'm43.input_1': T.Graph.OutputPort('m33.data'),
    
        'm37': 'M.input_features.v1',
        'm37.features': """# #号开始的表示注释
    # 多个特征,每行一个,可以包含基础特征和衍生特征
    industry_sw_level1_0
    market_cap_float_0
    #market_cap_0""",
    
        'm44': 'M.features_append.v1',
        'm44.input_1': T.Graph.OutputPort('m33.data'),
        'm44.input_2': T.Graph.OutputPort('m37.data'),
    
        'm42': 'M.input_features.v1',
        'm42.features': """# #号开始的表示注释,注释需单独一行
    # 多个特征,每行一个,可以包含基础特征和衍生特征,特征须为本平台特征
    alpha_001=-1*correlation(rank(delta(log(volume),1)),rank(((close-open)/open)),6)
    alpha_002=-1*delta((((close-low)-(high-close))/(high-low)),1)            
    alpha_003=sum(where(close==delay(close,1),0,close)-where(close>delay(close,1),min(low,delay(close,1)),max(high,delay(close,1))),6)
    #alpha_004=where(((sum(close,8)/8)+std(close,8))<(sum(close,2)/2),-1,where((sum(close,2)/2)<((sum(close,8)/8)-std(close,8)),1,where((volume/mean(volume,20))>=1,1,-1)))
    #alpha_005=-1*ts_max(correlation(ts_rank(volume,5),ts_rank(high,5),5),3)
    #alpha_006=-1*rank(sign(delta((((open*0.85)+(high*0.15))),4)))
    #alpha_007=((rank(max((vwap-close),3))+rank(min((vwap-close),3)))*rank(delta(volume,3)))
    #alpha_008=-1*rank(delta(((((high+low)/2)*0.2)+(vwap*0.8)),4))
    
    """,
    
        'm46': 'M.features_append.v1',
        'm46.input_1': T.Graph.OutputPort('m44.data_1'),
        'm46.input_2': T.Graph.OutputPort('m42.data'),
    
        'm29': 'M.general_feature_extractor.v7',
        'm29.instruments': T.Graph.OutputPort('m4.data'),
        'm29.features': T.Graph.OutputPort('m46.data_1'),
        'm29.start_date': '',
        'm29.end_date': '',
        'm29.before_start_days': 500,
    
        'm31': 'M.derived_feature_extractor.v3',
        'm31.input_data': T.Graph.OutputPort('m29.data'),
        'm31.features': T.Graph.OutputPort('m46.data_1'),
        'm31.date_col': 'date',
        'm31.instrument_col': 'instrument',
        'm31.drop_na': True,
        'm31.remove_extra_columns': True,
        'm31.user_functions': {},
    
        'm50': 'M.del_data_before_startdate.v5',
        'm50.input_1': T.Graph.OutputPort('m31.data'),
        'm50.input_2': T.Graph.OutputPort('m4.data'),
    
        'm41': 'M.delete_columns.v2',
        'm41.input_1': T.Graph.OutputPort('m50.data'),
        'm41.input_2': T.Graph.OutputPort('m43.data_1'),
        'm41.cols': [],
    
        'm48': 'M.general_feature_extractor.v7',
        'm48.instruments': T.Graph.OutputPort('m15.data'),
        'm48.features': T.Graph.OutputPort('m46.data_1'),
        'm48.start_date': '',
        'm48.end_date': '',
        'm48.before_start_days': 500,
    
        'm49': 'M.derived_feature_extractor.v3',
        'm49.input_data': T.Graph.OutputPort('m48.data'),
        'm49.features': T.Graph.OutputPort('m46.data_1'),
        'm49.date_col': 'date',
        'm49.instrument_col': 'instrument',
        'm49.drop_na': True,
        'm49.remove_extra_columns': True,
        'm49.user_functions': {},
    
        'm54': 'M.del_data_before_startdate.v5',
        'm54.input_1': T.Graph.OutputPort('m49.data'),
        'm54.input_2': T.Graph.OutputPort('m15.data'),
    
        'm51': 'M.delete_columns.v2',
        'm51.input_1': T.Graph.OutputPort('m54.data'),
        'm51.input_2': T.Graph.OutputPort('m43.data_1'),
        'm51.cols': [],
    
        'm45': 'M.features_short.v1',
        'm45.input_1': T.Graph.OutputPort('m42.data'),
    
        'm10': 'M.winsorize.v6',
        'm10.input_data': T.Graph.OutputPort('m41.data'),
        'm10.features': T.Graph.OutputPort('m45.data_1'),
        'm10.columns_input': '',
        'm10.median_deviate': 3,
    
        'm47': 'M.winsorize.v6',
        'm47.input_data': T.Graph.OutputPort('m51.data'),
        'm47.features': T.Graph.OutputPort('m45.data_1'),
        'm47.columns_input': '',
        'm47.median_deviate': 3,
    
        'm55': 'M.standardlize.v8',
        'm55.input_1': T.Graph.OutputPort('m47.data'),
        'm55.input_2': T.Graph.OutputPort('m45.data_1'),
        'm55.columns_input': '',
    
        'm11': 'M.chinaa_stock_filter.v1',
        'm11.input_data': T.Graph.OutputPort('m55.data'),
        'm11.index_constituent_cond': ['全部'],
        'm11.board_cond': ['全部'],
        'm11.industry_cond': ['全部'],
        'm11.st_cond': ['全部'],
        'm11.delist_cond': ['全部'],
        'm11.output_left_data': False,
    
        'm36': 'M.standardlize.v8',
        'm36.input_1': T.Graph.OutputPort('m10.data'),
        'm36.input_2': T.Graph.OutputPort('m45.data_1'),
        'm36.columns_input': '',
    
        'm6': 'M.join.v3',
        'm6.data1': T.Graph.OutputPort('m5.data'),
        'm6.data2': T.Graph.OutputPort('m36.data'),
        'm6.on': 'date,instrument',
        'm6.how': 'inner',
        'm6.sort': False,
    
        'm14': 'M.chinaa_stock_filter.v1',
        'm14.input_data': T.Graph.OutputPort('m6.data'),
        'm14.index_constituent_cond': ['全部'],
        'm14.board_cond': ['全部'],
        'm14.industry_cond': ['全部'],
        'm14.st_cond': ['全部'],
        'm14.delist_cond': ['全部'],
        'm14.output_left_data': False,
    
        'm16': 'M.stock_ranker.v2',
        'm16.training_ds': T.Graph.OutputPort('m14.data'),
        'm16.features': T.Graph.OutputPort('m45.data_1'),
        'm16.test_ds': T.Graph.OutputPort('m14.data'),
        'm16.predict_ds': T.Graph.OutputPort('m11.data'),
        'm16.learning_algorithm': '排序',
        'm16.number_of_leaves': 30,
        'm16.minimum_docs_per_leaf': 1000,
        'm16.number_of_trees': 30,
        'm16.learning_rate': 0.1,
        'm16.max_bins': 1023,
        'm16.feature_fraction': 1,
        'm16.data_row_fraction': 1,
        'm16.ndcg_discount_base': 1,
        'm16.slim_data': True,
    
        'm18': 'M.dl_layer_input.v1',
        'm18.shape': '10,12',
        'm18.batch_shape': '',
        'm18.dtype': 'float32',
        'm18.sparse': False,
        'm18.name': '',
    
        'm8': 'M.cached.v3',
        'm8.input_2': T.Graph.OutputPort('m18.data'),
        'm8.run': m8_run_bigquant_run,
        'm8.post_run': m8_post_run_bigquant_run,
        'm8.input_ports': '',
        'm8.params': '{}',
        'm8.output_ports': '',
        'm8.m_cached': False,
    
        'm32': 'M.input_features.v1',
        'm32.features': """#开盘价/收盘价,最高价/收盘价,最低价/收盘价,收盘价日涨幅,交易量日涨幅,持仓量日涨幅,收盘价5日平均/收盘价,收盘价10日平均/收盘价,收盘价20日平均/收盘价,交易量5日平均/交易量,交易量10日平均/交易量,交易量20日平均/交易量,持仓量5日平均/持仓量,持仓量10日平均/持仓量,持仓量20日平均/持仓量,收盘价14日RSI指数
    # 20210831-1:169%
    # 20210831-1-ep800:176%
    # 20210831-1-ep1000:156%
    
    bm_close= close
    bm_open= open
    bm_high= high
    bm_low= low
    bm_amount= amount
    bm_volume= volume
    
    bm_amplitude= (high-low)/shift(close, 1)
    bm_max_amplitude_5= ts_max(bm_amplitude, 5)
    bm_min_amplitude_5= ts_min(bm_amplitude, 5)
    
    #bm_ret_5=close / shift(open, 5) - 1
    #bm_ret_10=close / shift(open, 10) - 1
    #bm_ret_20=close / shift(open, 20) - 1
    bm_mean_close_5= mean(close,5)
    #bm_mean_close_10= mean(close,10)
    #bm_mean_close_20= mean(close,20)
    bm_high_5= ts_max(high, 5)
    bm_low_5= ts_min(low, 5)
    bm_amount_5= sum(amount,5)
    bm_mean_amount_5= mean(amount,5)
    #bm_mean_amount_10= mean(amount,10)
    bm_volume_5= sum(volume,5)
    bm_mean_volume_5= mean(volume,5)
    #bm_mean_volume_10= mean(volume,10)
    
    bm_open_close= open / close
    bm_high_close= high / close
    bm_low_close= low / close
    bm_close_delta= close / shift(close, 1)
    bm_volume_delta= volume / shift(volume, 1)
    bm_mean_close_5_close= mean(close,5) / close
    #bm_mean_close_10_close= mean(close,10) / close
    #bm_mean_close_20_close= mean(close,20) / close
    bm_mean_volume_5_volume= mean(volume,5) / volume
    #bm_mean_volume_10_volume= mean(volume,10) / volume
    #bm_mean_volume_20_volume= mean(volume,20) / volume
    
    bm_rsi_6= ta_rsi(close, 6)
    #bm_rsi_14= ta_rsi(close, 14)
    #bm_cci_1= ta_cci(high, low, close, 1)
    bm_cci_5= ta_cci(high, low, close, 5)
    #bm_bias_1= ta_bias(close, 1)
    bm_bias_5= ta_bias(close, 5)
    #bm_bias_10= ta_bias(close, 10)
    bm_mom_1= ta_mom(close,1)
    #bm_mom_5= ta_mom(close,5)
    #bm_roc_1= ta_roc(close,1)
    bm_roc_5= ta_roc(close,5)
    #bm_willr_5= ta_willr(close, 5)
    bm_diff_close_sma= close /((ta_sma(close,30) + ta_sma(close,72)) / 2) -1
    bm_ma_long_5= ta_ma(close, 5, derive='long')
    bm_ma_short_5= ta_ma(close, 5, derive='short')
    #bm_ma_long_10= ta_ma(close, 10, derive='long')
    #bm_ma_short_10= ta_ma(close, 10, derive='short')
    #bm_ma_long_20= ta_ma(close, 20, derive='long')
    #bm_ma_short_20= ta_ma(close, 20, derive='short')
    
    #avg_mf_net_amount_5
    #rank_avg_mf_net_amount_5
    #rank_avg_mf_net_amount_0
    #mf_net_amount_xl_0
    #mf_net_amount_l_0
    #mf_net_amount_m_0
    #mf_net_amount_s_0
    #mf_net_amount_main_0
    #mf_net_pct_xl_0
    #mf_net_pct_l_0
    #mf_net_pct_m_0
    #mf_net_pct_s_0
    #mf_net_pct_main_0
    
    """,
    
        'm57': 'M.features_short.v1',
        'm57.input_1': T.Graph.OutputPort('m32.data'),
    
        'm62': 'M.index_feature_extract.v3',
        'm62.input_1': T.Graph.OutputPort('m15.data'),
        'm62.input_2': T.Graph.OutputPort('m32.data'),
        'm62.before_days': 6000,
        'm62.index': '000300.HIX',
    
        'm63': 'M.dropnan.v2',
        'm63.input_data': T.Graph.OutputPort('m62.data_1'),
    
        'm9': 'M.cached.v3',
        'm9.input_1': T.Graph.OutputPort('m63.data'),
        'm9.input_3': T.Graph.OutputPort('m15.data'),
        'm9.run': m9_run_bigquant_run,
        'm9.post_run': m9_post_run_bigquant_run,
        'm9.input_ports': '',
        'm9.params': '{}',
        'm9.output_ports': '',
    
        'm38': 'M.dataclean.v40',
        'm38.feature_data1': T.Graph.OutputPort('m9.data_1'),
        'm38.feature_data2': T.Graph.OutputPort('m9.data_2'),
        'm38.cleaning_feature_list': T.Graph.OutputPort('m57.data_1'),
        'm38.nan_method': '中位数填充',
        'm38.outlier_method': 'MAD法',
        'm38.n_components': 12,
        'm38.grp_len': 10,
        'm38.if_nan': False,
        'm38.if_inf': False,
        'm38.if_outlier': False,
        'm38.if_neutralize': False,
        'm38.neutral_feature_remained': False,
        'm38.if_standardize': False,
        'm38.if_pca': True,
    
        'm61': 'M.concat.v3',
        'm61.input_data_1': T.Graph.OutputPort('m38.cleaned_data1'),
        'm61.input_data_2': T.Graph.OutputPort('m38.cleaned_data2'),
    
        'm34': 'M.input_features.v1',
        'm34.features': """#bm_label=shift(close, -5) / shift(close, 0) - 1 # 大盘因子标签
    bm_label=shift(close, -1) / shift(close, 0) - 1 # 大盘因子标签
    #bm_label=shift(close, -5) / shift(open, -1) - 1 # 大盘因子标签
    #bm_label=where(shift(close, -5) / shift(open, -1) - 1 < -0.002, 0, 1 )
    #bm_label=where(shift(close, -5) / shift(open, -1) - 1 < -0.002, 1, 0 )
    #bm_label=where(shift(close, -5) / shift(open, -1) - 1 < -0.002, -1, where(shift(close, -5) / shift(open, -1) - 1 > 0.01, 1, 0))
    #bm_label=where(shift(close, -5) / shift(open, -1) - 1 < -0.002, 2, where(shift(close, -5) / shift(open, -1) - 1 > 0.01, 1, 0))
    #bm_label=shift(close, -2) / shift(close, -1)-1
    #bm_label=shift(close, -2) / shift(open, -2)-1
    #bm_label=shift(close, -3) / shift(open, -1)-1
    #bm_label=shift(close, -4) / shift(open, -1)-1
    #bm_label=shift(close, -6) / shift(open, -1)-1
    """,
    
        'm28': 'M.index_feature_extract.v3',
        'm28.input_1': T.Graph.OutputPort('m15.data'),
        'm28.input_2': T.Graph.OutputPort('m34.data'),
        'm28.before_days': 6000,
        'm28.index': '000300.HIX',
    
        'm17': 'M.dropnan.v2',
        'm17.input_data': T.Graph.OutputPort('m28.data_1'),
    
        'm64': 'M.join.v3',
        'm64.data1': T.Graph.OutputPort('m17.data'),
        'm64.data2': T.Graph.OutputPort('m61.data'),
        'm64.on': 'date',
        'm64.how': 'inner',
        'm64.sort': True,
    
        'm65': 'M.sort.v4',
        'm65.input_ds': T.Graph.OutputPort('m64.data'),
        'm65.sort_by': 'date',
        'm65.group_by': '',
        'm65.keep_columns': '--',
        'm65.ascending': True,
    
        'm30': 'M.cached.v3',
        'm30.input_1': T.Graph.OutputPort('m8.data_2'),
        'm30.input_2': T.Graph.OutputPort('m65.sorted_data'),
        'm30.input_3': T.Graph.OutputPort('m15.data'),
        'm30.run': m30_run_bigquant_run,
        'm30.post_run': m30_post_run_bigquant_run,
        'm30.input_ports': '',
        'm30.params': '',
        'm30.output_ports': 'data_1,data_2,data_3,data_4',
    
        'm1': 'M.model_read.v1',
        'm1.filedir': '/home/bigquant/work/userlib/',
        'm1.filename': 'bm_model20210701前',
        'm1.m_cached': False,
    
        'm21': 'M.dl_model_predict.v1',
        'm21.trained_model': T.Graph.OutputPort('m1.data'),
        'm21.input_data': T.Graph.OutputPort('m30.data_2'),
        'm21.batch_size': 32,
        'm21.n_gpus': 0,
        'm21.verbose': '2:每个epoch输出一行记录',
    
        'm22': 'M.cached.v3',
        'm22.input_1': T.Graph.OutputPort('m21.data'),
        'm22.input_2': T.Graph.OutputPort('m30.data_3'),
        'm22.run': m22_run_bigquant_run,
        'm22.post_run': m22_post_run_bigquant_run,
        'm22.input_ports': '',
        'm22.params': '{}',
        'm22.output_ports': '',
    
        'm12': 'M.join.v3',
        'm12.data1': T.Graph.OutputPort('m22.data_1'),
        'm12.data2': T.Graph.OutputPort('m16.predictions'),
        'm12.on': 'date',
        'm12.how': 'right',
        'm12.sort': False,
    
        'm19': 'M.trade_para_config.v9',
        'm19.predictions': T.Graph.OutputPort('m12.data'),
        'm19.run_trade_module_per_days': 1,
        'm19.hold_days': 1,
        'm19.stock_count': 1,
        'm19.min_cash_per_instrument': 0.1,
        'm19.stop_loss': 0.05,
        'm19.target_up': 0.3,
        'm19.target_up_draw_down': 0.3,
        'm19.bm_riskctrl_down': 0.03,
        'm19.min_pe': 30,
        'm19.max_pe': 80,
        'm19.bm_pred_label_times': 20,
        'm19.m_cached': False,
    
        'm56': 'M.trade.v4',
        'm56.instruments': T.Graph.OutputPort('m15.data'),
        'm56.options_data': T.Graph.OutputPort('m19.data'),
        'm56.start_date': '',
        'm56.end_date': '',
        'm56.initialize': m56_initialize_bigquant_run,
        'm56.handle_data': m56_handle_data_bigquant_run,
        'm56.prepare': m56_prepare_bigquant_run,
        'm56.before_trading_start': m56_before_trading_start_bigquant_run,
        'm56.volume_limit': 0,
        'm56.order_price_field_buy': 'open',
        'm56.order_price_field_sell': 'close',
        'm56.capital_base': 100000,
        'm56.auto_cancel_non_tradable_orders': True,
        'm56.data_frequency': 'daily',
        'm56.price_type': '真实价格',
        'm56.product_type': '股票',
        'm56.plot_charts': True,
        'm56.backtest_only': False,
        'm56.benchmark': '',
    })
    
    # g.run({})
    
    
    #have_benchmark_factor = False  # 是否有大盘风控因子模块:无
    have_benchmark_factor = True  # 是否有大盘风控因子模块:有
    
    train_update_days = 120 # 更新周期,按交易日计算,每多少天更新一次,代表每次滚动测试集长度
    train_data_min_days = 60 # 最小数据天数,按交易日计算,代表滚动训练集最小长度,即 首次 滚动训练集长度,所以 第一个滚动的日期区间 是从 开始日期 到 开始日期+最小数据天数
    train_data_max_days = 60 # 最大数据天数,按交易日计算,代表滚动训练集最大长度,即 滚动几次后 的滚动训练集长度,0,表示没有限制,否则 每一个滚动的开始日期 = max(此滚动的结束日期-最大数据天数, 开始日期
    
    start_date = '2021-01-01' # 数据开始日期
    tdays = list(D.trading_days(end_date = start_date)['date']) # 结束于 数据开始日期 的 交易日历
    
    import time 
    
    def m24_run_bigquant_run(
        bq_graph,
        inputs,
        trading_days_market='CN', # 使用那个市场的交易日历, TODO
        train_instruments_mid='m4', # 训练集 证券代码列表模块id
        test_instruments_mid='m15', # 测试集 证券代码列表模块id
    #    predict_mid='m12' if have_benchmark_factor else 'm16', # 预测模块id
        predict_mid='m19' if have_benchmark_factor else 'm16', # 预测模块id,预测模块 为连接 回测模块 options_data输入端即第二输入端的模块
        trade_mid='m56', # 回测模块id
    #    start_date='2021-01-01', # 数据开始日期
        start_date=tdays[len(tdays)-train_data_min_days], # 数据开始日期,即 首次滚动训练 开始日期,预留 首次滚动 训练集  
        end_date=T.live_run_param('trading_date', '2021-05-31'), # 数据结束日期,绑定实盘参数“交易日期”
    #    train_update_days=250, # 更新周期,按交易日计算,每多少天更新一次
    #    train_data_min_days=250, # 最小数据天数,按交易日计算,所以第一个滚动的结束日期是 从开始日期到开始日期+最小数据天数
    #    train_data_max_days=250, # 最大数据天数,按交易日计算,0,表示没有限制,否则每一个滚动的开始日期=max(此滚动的结束日期-最大数据天数, 开始日期
    #    train_update_days=250, # 更新周期,按交易日计算,每多少天更新一次
    #    train_data_min_days=250*3, # 最小数据天数,按交易日计算,所以第一个滚动的结束日期是 从开始日期到开始日期+最小数据天数
    #    train_data_max_days=250*5, # 最大数据天数,按交易日计算,0,表示没有限制,否则每一个滚动的开始日期=max(此滚动的结束日期-最大数据天数, 开始日期
    #    train_update_days=120, # 更新周期,按交易日计算,每多少天更新一次
    #    train_data_min_days=120, # 最小数据天数,按交易日计算,所以第一个滚动的结束日期是 从开始日期到开始日期+最小数据天数
    #    train_data_max_days=120, # 最大数据天数,按交易日计算,0,表示没有限制,否则每一个滚动的开始日期=max(此滚动的结束日期-最大数据天数, 开始日期
    #    train_update_days=120, # 更新周期,按交易日计算,每多少天更新一次,代表每次滚动测试集长度
    #    train_data_min_days=60, # 最小数据天数,按交易日计算,代表滚动训练集最小长度,即首次滚动训练集长度,所以 第一个滚动的日期区间 是从 开始日期 到 开始日期+最小数据天数
    #    train_data_max_days=60, # 最大数据天数,按交易日计算,代表滚动训练集最大长度,即滚动几次后的滚动训练集长度,0,表示没有限制,否则 每一个滚动的开始日期 = max(此滚动的结束日期-最大数据天数, 开始日期
        train_update_days=train_update_days, # 更新周期,按交易日计算,每多少天更新一次,代表每次滚动测试集长度
        train_data_min_days=train_data_min_days, # 最小数据天数,按交易日计算,代表 滚动训练集最小长度,即 首次滚动训练集长度,所以 第一个滚动的日期区间 是从 开始日期 到 开始日期+最小数据天数
        train_data_max_days=train_data_max_days, # 最大数据天数,按交易日计算,代表 滚动训练集最大长度,即 滚动几次后的滚动训练集长度,0,表示没有限制,否则 每一个滚动的开始日期 = max(此滚动的结束日期-最大数据天数, 开始日期
        train_update_days_for_live=train_update_days, # 模拟实盘模式下的更新周期,按交易日计算,每多少天更新一次。如果需要在模拟实盘阶段使用不同的模型更新周期,可以设置这个参数。赋值为None的时候,实盘更新周期与回测更新周期一致。
        rolling_count_for_live=1, # 实盘模式下滚动次数,模拟实盘模式下,取最后多少次滚动。一般在模拟实盘模式下,只用到最后一次滚动训练的模型,这里可以设置为1;如果你的滚动训练数据时间段很短,以至于期间可能没有训练数据,这里可以设置大一点。0表示没有限制
    ):
        
        # 合并 滚动预测结果 函数
        # 输入端:
            # input_1:滚动预测结果列表,为 每次滚动运行对应的 [预测模块结果,预测集开始日期] 组成的列表
        # 输出端:
            # data:携带策略参数的合并预测结果字典
            # instrument_data:合并证券代码数据字典
        def merge_datasources(input_1):
    #        df_list = [ds[0].read_df().set_index('date').loc[ds[1]:].reset_index() for ds in input_1]
            
            # 合并 滚动预测结果列表
            # 循环读取 输入端 input_1 滚动预测结果列表,合并生成其中 预测模块结果列表
            df_list = []
            for ds in input_1: # ds 为 input_1 成员列表,即 每次滚动运行对应的 [预测模块结果,预测集开始日期]
                ds0 = ds[0].read() # ds0 为 每次滚动运行对应的 预测模块结果(即输出端),数据帧 类型
                ds0['pred'] = ds0['pred'].set_index('date').loc[ds[1]:].reset_index() # ds1 为 每次滚动运行对应的 预测集开始日期       
                df_list.append(ds0)
    #        print('df_list:\n',df_list)
    
            # 合并预测结果数据帧:由 合并的预测模块结果列表 纵向合并 形成
    #        df = pd.concat(df_list)
            df = pd.concat([dfl['pred'] for dfl in 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'])),
            }
    
            # 携带策略参数的合并预测结果字典:按 策略参数配置模块 输出端 字典结构 组装生成 携带参数的预测结果字典
            df_dict = {'pred': df, # df 为 合并预测结果数据帧
                       'stock_count': df_list[0]['stock_count'],
                       'hold_days': df_list[0]['hold_days'],
                       'min_cash_per_instrument': df_list[0]['min_cash_per_instrument'],
                       'stop_loss': df_list[0]['stop_loss'], 
                       'target_up': df_list[0]['target_up'],
                       'target_up_draw_down': df_list[0]['target_up_draw_down']
                      }
    #        return Outputs(data=DataSource.write_df(df), instrument_data=DataSource.write_pickle(instrument_data))
            return Outputs(data=DataSource.write_pickle(df_dict), 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('没有滚动需要执行,请检查配置')
    
            # 实盘模式滚动次数处理:取最后 rolling_count_for_live 次滚动
            if is_live_run and rolling_count_for_live:
                rollings = rollings[-rolling_count_for_live:]
    
            return rollings
    
        # bq_graph 是 可视化图 对象,总结了 可视化的所有模块 和 每个模块涉及的参数
        g = bq_graph
    
        # 调用 gen_rolling_dates 函数 生成 滚动日期区间字典列表
        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)
    
        # 滚动运行:仅 滚动训练和预测,禁用 回测 模块,即 回测 模块 不参与 滚动运行
        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)
    #        print(time.ctime(), '------ 滚动训练参数字典:', parameters, '\n')
            print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()), '滚动训练参数字典:\n', parameters, '\n')
            
            # 执行 本次滚动运行,并记录 本次滚动运行结果字典
            # 按 本次滚动 训练 参数字典 parameters 运行 可视化图 对象 g,本次滚动运行结果字典 计入 result
            # 本次滚动运行结果字典 result 为 可视化图 对象 g 的 组成模块 及其 运行结果 组成的 字典
            # results.append(g.run(parameters))
            result = g.run(parameters) # 记录 本次滚动运行结果字典
            
            # 生成 滚动运行结果字典列表
            if not result is None: # result 非空
                results.append(result) # 本次滚动运行结果字典 计入  滚动运行结果字典列表
            else:
                raise Exception('------ 本次滚动运行结果为空,请检查配置!') 
                
        print('\n滚动运行结果字典列表 rolling_train_results 长度:', len(results), '\n')     
    #    print('------ 滚动运行结果字典列表 rolling_train_results 长度:', len(results))     
    #    print('------ rolling_train_results:', results)     
    
        # 调用 合并函数 merge_datasources 函数,合并 滚动预测结果,生成 合并函数 模块对象 mx
        # 合并函数 输入端 input_1 基于 滚动运行结果列表 results 构建
        if not have_benchmark_factor: # 无 大盘风控因子模块,预测模块 对应 输出端 为 predictions
            mx = M.cached.v3(run = merge_datasources, 
                             input_1 = [[result[predict_mid].predictions, result[test_instruments_mid].data.read_pickle()['start_date']] 
                                        for result in results])
        else: # 有 大盘风控因子模块,预测模块 对应 输出端 为 data
            mx = M.cached.v3(run = merge_datasources, 
                             input_1 = [[result[predict_mid].data, result[test_instruments_mid].data.read_pickle()['start_date']] 
    #                         input_1 = [[result[predict_mid].data.read()['pred'], result[test_instruments_mid].data.read_pickle()['start_date']] 
                                        for result in results])
        
        # 根据 合并函数 模块对象 mx 生成 回测模块 参数字典
        parameters = {}
        parameters['*.__enabled__'] = False # 禁用 全部 模块
        parameters[trade_mid + '.__enabled__'] = True # 启用 回测模块
        parameters[trade_mid + '.instruments'] = mx.instrument_data # 合并证券代码数据字典
        parameters[trade_mid + '.options_data'] = mx.data # 携带策略参数的合并预测结果字典
        
        # 回测:按 回测模块 参数字典 parameters 运行 可视化图 对象 g,回测 结果 计入 trade
        trade = g.run(parameters)
    
        # 返回 滚动运行结果列表 和 回测结果
        return {'rollings': results, 'trade': trade}
    
    
    m24 = M.hyper_rolling_train.v1(
        run=m24_run_bigquant_run,
        run_now=True,
        bq_graph=g
    )
    
    2021-09-08 03:23:11 滚动训练参数字典:
     {'m56.__enabled__': False, 'm4.start_date': '2020-10-09', 'm4.end_date': '2020-12-31', 'm15.start_date': '2021-01-04', 'm15.end_date': '2021-05-31'} 
    
    
    3/3 - 1s
    DataSource(e6052c2b15784c75a37fc49a6fad9a37T)
    
    bigcharts-data-start/{"__type":"tabs","__id":"bigchart-494dd05593384b7c98f0ff2019c08348"}/bigcharts-data-end
    策略参数配置模块----策略参数字典:
     {
      "run_trade_module_per_days" : 1,
      "hold_days" : 1,
      "stock_count" : 1,
      "min_cash_per_instrument" : 0.1,
      "stop_loss" : 0.05,
      "target_up" : 0.3,
      "target_up_draw_down" : 0.3,
      "bm_riskctrl_down" : 0.03,
      "min_pe" : 30,
      "max_pe" : 80,
      "bm_pred_label_times" : 20
    }
    
    滚动运行结果字典列表 rolling_train_results 长度: 1 
    
    
    初始化函数。。。。。。
    
    策略参数字典:
     {
     "stock_count":    1,
     "hold_days":    1,
     "min_cash_per_instrument":    0.1,
     "stop_loss":    0.05,
     "target_up":    0.3,
     "target_up_draw_down":    0.3
    }
    
    预测结果数据(即 pred 关键字 对应 DataFrame):
                  date  instrument     score  position  pred_label     label
    0      2021-01-04  688111.SHA  1.703383         1     0.00449  0.019133
    1      2021-01-04  000895.SZA  1.664948         2     0.00449  0.019133
    2      2021-01-04  300124.SZA  1.664948         3     0.00449  0.019133
    3      2021-01-04  601888.SHA  1.664948         4     0.00449  0.019133
    4      2021-01-04  600519.SHA  1.634272         5     0.00449  0.019133
    ...           ...         ...       ...       ...         ...       ...
    404661 2021-05-31  000415.SZA -0.259438      4273         NaN       NaN
    404662 2021-05-31  002550.SZA -0.278906      4274         NaN       NaN
    404663 2021-05-31  002640.SZA -0.278906      4275         NaN       NaN
    404664 2021-05-31  002827.SZA -0.278906      4276         NaN       NaN
    404665 2021-05-31  300608.SZA -0.315068      4277         NaN       NaN
    
    [404666 rows x 6 columns]
    
    ---------------------------------------------------------------------------
    KeyError                                  Traceback (most recent call last)
    <ipython-input-18-669998e82074> in <module>
       1479 
       1480 
    -> 1481 m24 = M.hyper_rolling_train.v1(
       1482     run=m24_run_bigquant_run,
       1483     run_now=True,
    
    <ipython-input-18-669998e82074> in m24_run_bigquant_run(bq_graph, inputs, trading_days_market, train_instruments_mid, test_instruments_mid, predict_mid, trade_mid, start_date, end_date, train_update_days, train_data_min_days, train_data_max_days, train_update_days_for_live, rolling_count_for_live)
       1473 
       1474     # 回测:按 回测模块 参数字典 parameters 运行 可视化图 对象 g,回测 结果 计入 trade
    -> 1475     trade = g.run(parameters)
       1476 
       1477     # 返回 滚动运行结果列表 和 回测结果
    
    <ipython-input-18-669998e82074> in m56_initialize_bigquant_run(context)
        197     # 设置交易逻辑运行间隔周期(交易日)
        198 #    context.run_trade_module_per_days = 1
    --> 199     context.run_trade_module_per_days = pred_with_para_dict['run_trade_module_per_days'] # 交易逻辑运行间隔周期(交易日)
        200 
        201     # 设置止损止盈幅度
    
    KeyError: 'run_trade_module_per_days'