复制链接
克隆策略

    {"description":"实验创建于2022/7/22","graph":{"edges":[{"to_node_id":"-783:instruments","from_node_id":"-530:data"},{"to_node_id":"-783:options_data","from_node_id":"-688:data_1"}],"nodes":[{"node_id":"-530","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2016-06-02","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2022-06-23","type":"Literal","bound_global_parameter":null},{"name":"market","value":"CN_FUND","type":"Literal","bound_global_parameter":null},{"name":"instrument_list","value":"513100.HOF","type":"Literal","bound_global_parameter":null},{"name":"max_count","value":0,"type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"rolling_conf","node_id":"-530"}],"output_ports":[{"name":"data","node_id":"-530"}],"cacheable":false,"seq_num":1,"comment":"","comment_collapsed":true},{"node_id":"-688","module_id":"BigQuantSpace.cached.cached-v3","parameters":[{"name":"run","value":"# Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端\n\"\"\"\ndef bigquant_run(input_1, input_2, input_3):\n # 示例代码如下。在这里编写您的代码\n df = pd.DataFrame({'data': [1, 2, 3]})\n data_1 = DataSource.write_df(df)\n data_2 = DataSource.write_pickle(df)\n return Outputs(data_1=data_1, data_2=data_2, data_3=None)\n\"\"\"\n\ndef bigquant_run(input_1, input_2, input_3, spacing, cdays, divided, holding):\n # 示例代码如下。在这里编写您的代码\n data_1 = DataSource.write_pickle({'spacing':spacing, 'cdays':cdays, 'divided':divided, 'holding':holding})\n return Outputs(data_1=data_1, data_2=None, data_3=None)\n","type":"Literal","bound_global_parameter":null},{"name":"post_run","value":"# 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。\ndef bigquant_run(outputs):\n return outputs\n","type":"Literal","bound_global_parameter":null},{"name":"input_ports","value":"","type":"Literal","bound_global_parameter":null},{"name":"params","value":"{'spacing':0.16, 'cdays':165, 'divided':10, 'holding':0.1}","type":"Literal","bound_global_parameter":null},{"name":"output_ports","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-688"},{"name":"input_2","node_id":"-688"},{"name":"input_3","node_id":"-688"}],"output_ports":[{"name":"data_1","node_id":"-688"},{"name":"data_2","node_id":"-688"},{"name":"data_3","node_id":"-688"}],"cacheable":false,"seq_num":2,"comment":"","comment_collapsed":true},{"node_id":"-783","module_id":"BigQuantSpace.hftrade.hftrade-v2","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 context.capital_base = 1000000\n context.ins = context.instruments #从传入参数中获取需要交易的合约\n context.set_universe(context.ins) #设置需要处理的合约\n context.last_grid = {}\n context.reset_net = {}\n context.grid_change_last = {}\n context.center = {}\n context.order_num = {}\n context.first_order_flag = {}\n context.spacing = {}\n context.cdays = {}\n context.divided = {}\n context.holding = {}\n \"\"\"\n context.spacing = context.options['data'].read()['spacing']\n context.cdays = context.options['data'].read()['cdays']\n context.holding = context.options['data'].read()['holding']\n \"\"\"\n \n for sid in context.instruments:\n context.spacing[sid] = context.options['data'].read()['spacing']\n context.cdays[sid] = context.options['data'].read()['cdays']\n context.divided[sid] = context.options['data'].read()['divided']\n context.holding[sid] = context.options['data'].read()['holding']\n context.last_grid[sid] = 0\n context.reset_net[sid] = True\n context.grid_change_last[sid] = [0,0]\n context.first_order_flag[sid] = True\n \n context.set_stock_t1(0)\n context.set_commission(PerOrder(buy_cost=0, sell_cost=0, min_cost=0))","type":"Literal","bound_global_parameter":null},{"name":"before_trading_start","value":"# 交易引擎:每个单位时间开盘前调用一次。\ndef bigquant_run(context, data):\n # 盘前处理,订阅行情等\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"handle_tick","value":"# 交易引擎:tick数据处理函数,每个tick执行一次\ndef bigquant_run(context, tick):\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"handle_data","value":"# 交易引擎:bar数据处理函数,每个时间单位执行一次\ndef bigquant_run(context, data):\n context.subscribe(context.ins) #注册合约\n\n # 记录上一次交易时网格范围的变化情况(例如从4区到5区,记为4,5)\n for sid in context.instruments:\n if context.reset_net[sid]:\n context.center[sid] = data.history(sid, [\"close\"], context.cdays[sid], \"1d\").mean()[0]\n context.reset_net[sid] = False\n print(str(data.current_dt) + \" center: \" + str(context.center))\n \n #计算当天可交易仓位\n for sid in context.instruments:\n weight = 1/len(context.instruments)\n price = data.current(sid, \"close\")\n cash = context.capital_base * weight\n order_num = int(cash/price/1000)*1000\n context.order_num[sid] = order_num\n \n cur_date = data.current_dt\n \n for sid in context.instruments:\n # 分别获取持仓\n position = context.get_position(sid)\n \n if position.current_qty == 0 and context.first_order_flag[sid] == True:\n print(\"triggered!\")\n order_num = int(context.order_num[sid] * context.holding[sid]/100)*100\n rv = context.order(sid, order_num, price, order_type=OrderType.MARKET)\n context.first_order_flag[sid] = False\n \n # 获取当前价格\n price = data.current(sid, \"close\")\n \n # 设置网格和当前价格所处的网格区域\n band = np.arange(1-context.spacing[sid], 1+context.spacing[sid], context.spacing[sid]/10)* context.center[sid]\n grid = pd.cut([price], band, labels=np.arange(1,len(band)))[0]\n weight = 1/len(context.instruments)/len(band)\n \n if np.isnan(grid):\n print(str(cur_date) + \" \"+ sid + \" 价格波动超过网格范围,可适当调节网格宽度和数量\")\n context.reset_net[sid] = True\n\n if context.last_grid[sid] < grid:\n # 记录新旧格子范围(按照大小排序)\n grid_change_new = [context.last_grid[sid],grid]\n if context.last_grid[sid] == 0:\n context.last_grid[sid] = grid\n return\n if context.last_grid[sid] != 0:\n if grid_change_new != context.grid_change_last[sid]:\n # 更新前一次的数据\n context.last_grid[sid] = grid\n context.grid_change_last[sid] = grid_change_new\n # 如果有仓位\n if position.current_qty != 0:\n order_num = int(context.order_num[sid] / context.divided[sid]/100)*100\n if position.current_qty >= order_num:\n rv = context.order(sid, -order_num, price, order_type=OrderType.MARKET)\n else:\n rv = context.order(sid, -position.current_qty, price, order_type=OrderType.MARKET)\n \n \n # 如果新网格小于前一天的网格,开仓\n if context.last_grid[sid] > grid:\n # 记录新旧格子范围(按照大小排序)\n grid_change_new = [grid,context.last_grid[sid]]\n #print(sid + \" \" + str(context.grid_change_last[sid]) + \" \"+ str([context.last_grid[sid], grid]))\n if context.last_grid[sid] == 0:\n context.last_grid[sid] = grid\n return\n if context.last_grid[sid] != 0:\n # 如果前一次开仓是4-5,这一次是5-4,算是没有突破,不成交\n if grid_change_new != context.grid_change_last[sid]:\n # 更新前一次的数据\n context.last_grid[sid] = grid\n context.grid_change_last[sid] = grid_change_new\n order_num = int(context.order_num[sid] / context.divided[sid]/100)*100\n if context.portfolio.cash > price * order_num:\n rv = context.order(sid, order_num, price, order_type=OrderType.MARKET)","type":"Literal","bound_global_parameter":null},{"name":"handle_trade","value":"# 交易引擎:成交回报处理函数,每个成交发生时执行一次\ndef bigquant_run(context, trade):\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"handle_order","value":"# 交易引擎:委托回报处理函数,每个委托变化时执行一次\ndef bigquant_run(context, order):\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"after_trading","value":"# 交易引擎:盘后处理函数,每日盘后执行一次\ndef bigquant_run(context, data):\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"capital_base","value":1000000,"type":"Literal","bound_global_parameter":null},{"name":"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":"before_start_days","value":"0","type":"Literal","bound_global_parameter":null},{"name":"volume_limit","value":1,"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":"benchmark","value":"000001.HIX","type":"Literal","bound_global_parameter":null},{"name":"plot_charts","value":"False","type":"Literal","bound_global_parameter":null},{"name":"disable_cache","value":"True","type":"Literal","bound_global_parameter":null},{"name":"replay_bdb","value":"False","type":"Literal","bound_global_parameter":null},{"name":"show_debug_info","value":"False","type":"Literal","bound_global_parameter":null},{"name":"backtest_only","value":"False","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-783"},{"name":"options_data","node_id":"-783"},{"name":"history_ds","node_id":"-783"},{"name":"benchmark_ds","node_id":"-783"}],"output_ports":[{"name":"raw_perf","node_id":"-783"}],"cacheable":false,"seq_num":3,"comment":"","comment_collapsed":true},{"node_id":"-2365","module_id":"BigQuantSpace.hyper_parameter_search.hyper_parameter_search-v1","parameters":[{"name":"param_grid_builder","value":"\"\"\"\ndef bigquant_run():\n param_grid = {}\n\n # 在这里设置需要调优的参数备选\n # param_grid['m3.features'] = ['close_1/close_0', 'close_2/close_0\\nclose_3/close_0']\n # param_grid['m6.number_of_trees'] = [5, 10, 20]\n\n return param_grid\n\"\"\"\n\ndef bigquant_run():\n param_grid = {}\n \n spacing = np.arange(0.1,0.5,0.02)\n cdays = np.arange(30,200,5)\n divided = np.array([10])\n holding = np.array([0.1])\n \n param_grid['m2.params'] = [\"{'spacing':%s,'cdays':%s,'divided':%s,'holding':%s}\"%(i,j,k,l) for i in spacing for j in cdays for k in divided for l in holding]\n param_grid['m1.instrument_list'] = ['513100.HOF']\n return param_grid","type":"Literal","bound_global_parameter":null},{"name":"scoring","value":"def bigquant_run(result):\n score = {'score':result.get('m3').read_raw_perf()['sharpe'].tail(1)[0], 'return':result.get('m3').read_raw_perf()['algorithm_period_return'].tail(1)[0], 'max_drawdown':result.get('m3').read_raw_perf()['max_drawdown'].tail(1)[0]}\n return score\n","type":"Literal","bound_global_parameter":null},{"name":"search_algorithm","value":"网格搜索","type":"Literal","bound_global_parameter":null},{"name":"search_iterations","value":"1000","type":"Literal","bound_global_parameter":null},{"name":"random_state","value":"","type":"Literal","bound_global_parameter":null},{"name":"workers","value":"6","type":"Literal","bound_global_parameter":null},{"name":"worker_distributed_run","value":"False","type":"Literal","bound_global_parameter":null},{"name":"worker_silent","value":"True","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":"-2365"},{"name":"input_1","node_id":"-2365"},{"name":"input_2","node_id":"-2365"},{"name":"input_3","node_id":"-2365"}],"output_ports":[{"name":"result","node_id":"-2365"}],"cacheable":false,"seq_num":4,"comment":"","comment_collapsed":true}],"node_layout":"<node_postions><node_position Node='-530' Position='184,134,200,200'/><node_position Node='-688' Position='609,135,200,200'/><node_position Node='-783' Position='369,292,200,200'/><node_position Node='-2365' Position='176,-4,200,200'/></node_postions>"},"nodes_readonly":false,"studio_version":"v2"}
    In [ ]:
    # 本代码由可视化策略环境自动生成 2022年8月19日 13:41
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    """
    def m2_run_bigquant_run(input_1, input_2, input_3):
        # 示例代码如下。在这里编写您的代码
        df = pd.DataFrame({'data': [1, 2, 3]})
        data_1 = DataSource.write_df(df)
        data_2 = DataSource.write_pickle(df)
        return Outputs(data_1=data_1, data_2=data_2, data_3=None)
    """
    
    def m2_run_bigquant_run(input_1, input_2, input_3, spacing, cdays, divided, holding):
        # 示例代码如下。在这里编写您的代码
        data_1 = DataSource.write_pickle({'spacing':spacing, 'cdays':cdays, 'divided':divided, 'holding':holding})
        return Outputs(data_1=data_1, data_2=None, data_3=None)
    
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m2_post_run_bigquant_run(outputs):
        return outputs
    
    # 交易引擎:初始化函数,只执行一次
    def m3_initialize_bigquant_run(context):
        context.capital_base = 1000000
        context.ins = context.instruments #从传入参数中获取需要交易的合约
        context.set_universe(context.ins) #设置需要处理的合约
        context.last_grid = {}
        context.reset_net = {}
        context.grid_change_last = {}
        context.center = {}
        context.order_num = {}
        context.first_order_flag = {}
        context.spacing = {}
        context.cdays = {}
        context.divided = {}
        context.holding = {}
        """
        context.spacing = context.options['data'].read()['spacing']
        context.cdays = context.options['data'].read()['cdays']
        context.holding = context.options['data'].read()['holding']
        """
        
        for sid in context.instruments:
            context.spacing[sid] = context.options['data'].read()['spacing']
            context.cdays[sid] = context.options['data'].read()['cdays']
            context.divided[sid] = context.options['data'].read()['divided']
            context.holding[sid] = context.options['data'].read()['holding']
            context.last_grid[sid] = 0
            context.reset_net[sid] = True
            context.grid_change_last[sid] = [0,0]
            context.first_order_flag[sid] = True
        
        context.set_stock_t1(0)
        context.set_commission(PerOrder(buy_cost=0, sell_cost=0, min_cost=0))
    # 交易引擎:每个单位时间开盘前调用一次。
    def m3_before_trading_start_bigquant_run(context, data):
        # 盘前处理,订阅行情等
        pass
    
    # 交易引擎:tick数据处理函数,每个tick执行一次
    def m3_handle_tick_bigquant_run(context, tick):
        pass
    
    # 交易引擎:bar数据处理函数,每个时间单位执行一次
    def m3_handle_data_bigquant_run(context, data):
        context.subscribe(context.ins) #注册合约
    
        # 记录上一次交易时网格范围的变化情况(例如从4区到5区,记为4,5)
        for sid in context.instruments:
            if context.reset_net[sid]:
                context.center[sid] = data.history(sid, ["close"], context.cdays[sid], "1d").mean()[0]
                context.reset_net[sid] = False
                print(str(data.current_dt) + " center: " + str(context.center))
            
        #计算当天可交易仓位
        for sid in context.instruments:
            weight = 1/len(context.instruments)
            price = data.current(sid, "close")
            cash = context.capital_base * weight
            order_num = int(cash/price/1000)*1000
            context.order_num[sid] = order_num
        
        cur_date = data.current_dt
        
        for sid in context.instruments:
            # 分别获取持仓
            position = context.get_position(sid)
            
            if position.current_qty == 0 and context.first_order_flag[sid] == True:
                print("triggered!")
                order_num = int(context.order_num[sid] * context.holding[sid]/100)*100
                rv = context.order(sid, order_num, price, order_type=OrderType.MARKET)
                context.first_order_flag[sid] = False
           
            # 获取当前价格
            price = data.current(sid, "close")
                
            # 设置网格和当前价格所处的网格区域
            band = np.arange(1-context.spacing[sid], 1+context.spacing[sid], context.spacing[sid]/10)* context.center[sid]
            grid = pd.cut([price], band, labels=np.arange(1,len(band)))[0]
            weight = 1/len(context.instruments)/len(band)
            
            if np.isnan(grid):
                print(str(cur_date) + " "+ sid + " 价格波动超过网格范围,可适当调节网格宽度和数量")
                context.reset_net[sid] = True
    
            if context.last_grid[sid] < grid:
                # 记录新旧格子范围(按照大小排序)
                grid_change_new = [context.last_grid[sid],grid]
                if context.last_grid[sid] == 0:
                    context.last_grid[sid] = grid
                    return
                if context.last_grid[sid] != 0:
                    if grid_change_new != context.grid_change_last[sid]:
                        # 更新前一次的数据
                        context.last_grid[sid] = grid
                        context.grid_change_last[sid] = grid_change_new
                        # 如果有仓位
                        if position.current_qty != 0:
                            order_num = int(context.order_num[sid] / context.divided[sid]/100)*100
                            if position.current_qty >= order_num:
                                rv = context.order(sid, -order_num, price, order_type=OrderType.MARKET)
                            else:
                                rv = context.order(sid, -position.current_qty, price, order_type=OrderType.MARKET)
                      
                            
            # 如果新网格小于前一天的网格,开仓
            if context.last_grid[sid] > grid:
                # 记录新旧格子范围(按照大小排序)
                grid_change_new = [grid,context.last_grid[sid]]
                #print(sid + " " + str(context.grid_change_last[sid]) + " "+ str([context.last_grid[sid], grid]))
                if context.last_grid[sid] == 0:
                    context.last_grid[sid] = grid
                    return
                if context.last_grid[sid] != 0:
                    # 如果前一次开仓是4-5,这一次是5-4,算是没有突破,不成交
                    if grid_change_new != context.grid_change_last[sid]:
                        # 更新前一次的数据
                        context.last_grid[sid] = grid
                        context.grid_change_last[sid] = grid_change_new
                        order_num = int(context.order_num[sid] / context.divided[sid]/100)*100
                        if context.portfolio.cash > price * order_num:
                            rv = context.order(sid, order_num, price, order_type=OrderType.MARKET)
    # 交易引擎:成交回报处理函数,每个成交发生时执行一次
    def m3_handle_trade_bigquant_run(context, trade):
        pass
    
    # 交易引擎:委托回报处理函数,每个委托变化时执行一次
    def m3_handle_order_bigquant_run(context, order):
        pass
    
    # 交易引擎:盘后处理函数,每日盘后执行一次
    def m3_after_trading_bigquant_run(context, data):
        pass
    
    
    g = T.Graph({
    
        'm1': 'M.instruments.v2',
        'm1.start_date': '2016-06-02',
        'm1.end_date': '2022-06-23',
        'm1.market': 'CN_FUND',
        'm1.instrument_list': '513100.HOF',
        'm1.max_count': 0,
        'm1.m_cached': False,
    
        'm2': 'M.cached.v3',
        'm2.run': m2_run_bigquant_run,
        'm2.post_run': m2_post_run_bigquant_run,
        'm2.input_ports': '',
        'm2.params': '{\'spacing\':0.16, \'cdays\':165, \'divided\':10, \'holding\':0.1}',
        'm2.output_ports': '',
        'm2.m_cached': False,
    
        'm3': 'M.hftrade.v2',
        'm3.instruments': T.Graph.OutputPort('m1.data'),
        'm3.options_data': T.Graph.OutputPort('m2.data_1'),
        'm3.start_date': '',
        'm3.end_date': '',
        'm3.initialize': m3_initialize_bigquant_run,
        'm3.before_trading_start': m3_before_trading_start_bigquant_run,
        'm3.handle_tick': m3_handle_tick_bigquant_run,
        'm3.handle_data': m3_handle_data_bigquant_run,
        'm3.handle_trade': m3_handle_trade_bigquant_run,
        'm3.handle_order': m3_handle_order_bigquant_run,
        'm3.after_trading': m3_after_trading_bigquant_run,
        'm3.capital_base': 1000000,
        'm3.frequency': 'daily',
        'm3.price_type': '真实价格',
        'm3.product_type': '自动',
        'm3.before_start_days': '0',
        'm3.volume_limit': 1,
        'm3.order_price_field_buy': 'open',
        'm3.order_price_field_sell': 'close',
        'm3.benchmark': '000001.HIX',
        'm3.plot_charts': False,
        'm3.disable_cache': True,
        'm3.replay_bdb': False,
        'm3.show_debug_info': False,
        'm3.backtest_only': False,
    })
    
    # g.run({})
    
    
    """
    def m4_param_grid_builder_bigquant_run():
        param_grid = {}
    
        # 在这里设置需要调优的参数备选
        # param_grid['m3.features'] = ['close_1/close_0', 'close_2/close_0\nclose_3/close_0']
        # param_grid['m6.number_of_trees'] = [5, 10, 20]
    
        return param_grid
    """
    
    def m4_param_grid_builder_bigquant_run():
        param_grid = {}
        
        spacing = np.arange(0.1,0.5,0.02)
        cdays = np.arange(30,200,5)
        divided = np.array([10])
        holding = np.array([0.1])
        
        param_grid['m2.params'] = ["{'spacing':%s,'cdays':%s,'divided':%s,'holding':%s}"%(i,j,k,l) for i in spacing for j in cdays for k in divided for l in holding]
        param_grid['m1.instrument_list'] = ['513100.HOF']
        return param_grid
    def m4_scoring_bigquant_run(result):
        score = {'score':result.get('m3').read_raw_perf()['sharpe'].tail(1)[0], 'return':result.get('m3').read_raw_perf()['algorithm_period_return'].tail(1)[0], 'max_drawdown':result.get('m3').read_raw_perf()['max_drawdown'].tail(1)[0]}
        return score
    
    
    m4 = M.hyper_parameter_search.v1(
        param_grid_builder=m4_param_grid_builder_bigquant_run,
        scoring=m4_scoring_bigquant_run,
        search_algorithm='网格搜索',
        search_iterations=1000,
        workers=6,
        worker_distributed_run=False,
        worker_silent=True,
        run_now=True,
        bq_graph=g
    )
    
    In [3]:
    data = {'instrument':[item['m1.instrument_list'] for item in m4.result.cv_results_['params']],
            'spacing':[item['m2.params'].split(',')[0].split(':')[1] for item in m4.result.cv_results_['params']],
            'cdays':[item['m2.params'].split(',')[1].split(':')[1] for item in m4.result.cv_results_['params']],
            'divided':[item['m2.params'].split(',')[2].split(':')[1] for item in m4.result.cv_results_['params']],
            'holding':[item['m2.params'].split(',')[3].split(':')[1].split('}')[0] for item in m4.result.cv_results_['params']],
            'sharpe':m4.result.cv_results_['split0_test_score'],
            'return':m4.result.cv_results_['split0_test_return'],
            'max_drawdown':m4.result.cv_results_['split0_test_max_drawdown']}
    
    df = pd.DataFrame(data)
    
    df.to_csv('res.csv',sep=',', index=False, header=True)
    
    In [4]:
    df.head()
    
    Out[4]:
    instrument spacing cdays divided holding sharpe return max_drawdown
    0 513100.HOF 0.3 1 10 0.1 -0.117693 0.073134 -0.101592
    1 513100.HOF 0.3 1 10 0.2 -0.110558 0.075470 -0.101389
    2 513100.HOF 0.3 1 10 0.30000000000000004 -0.084223 0.084014 -0.100651
    3 513100.HOF 0.3 1 10 0.4 -0.057434 0.092558 -0.099924
    4 513100.HOF 0.3 1 20 0.1 -0.502704 0.037582 -0.054568
    In [11]:
    import pandas as pd
    df = pd.read_csv("res.csv")
    
    In [12]:
    df_calen = DataSource("trading_days").read()
    
    df_days = df_calen[(df_calen['country_code']=='CN') & (df_calen['date']>='2016-06-02') & (df_calen['date']<='2022-06-23')]
    
    In [13]:
    df['yearly_return'] = df['return'].apply(lambda x: pow(1+x, 242/df_days.shape[0])-1)
    
    In [14]:
    df['ratio_return_drawdown'] = df['yearly_return'] / abs(df['max_drawdown'])
    
    In [15]:
    df.iloc[df.groupby('instrument')['return'].idxmax()]
    
    Out[15]:
    instrument spacing cdays divided holding sharpe return max_drawdown yearly_return ratio_return_drawdown
    129 513100.HOF 0.16 165 10 0.1 0.786333 0.955216 -0.157914 0.116537 0.737978
    In [ ]: