复制链接
克隆策略

期货策略-基于协整的跨期套利

交易规则

  • 使用9月、10月螺纹钢合约价格的价差,取时间窗口为30天,得到价差的移动平均值和标准差,定义:
  • 上轨 = 价差移动平均值 + 标准差
  • 下轨 = 价差移动平均值 - 标准差
  • 指标 = (价差 - 价差移动平均值)/价差标准差

策略构建步骤

确定股票池和回测时间

通过证券代码列表输入回测的起止日期

确定买卖原则

  • 指标小于等于-1时,价差突破下轨,买入RB1809,卖出RB1810
  • 指标大于等于1时,价差突破上轨,卖出RB1809,买入RB1810

回测

通过 trade 模块中的初始化函数定义交易手续费和滑点;

通过 trade 模块中的主函数(handle函数)查看每日的买卖交易信号,按照买卖原则执行相应的买入/卖出/调仓操作。

策略详情

    {"description":"实验创建于2021/12/4","graph":{"edges":[{"to_node_id":"-81:instruments","from_node_id":"-72:data"},{"to_node_id":"-162:input_2","from_node_id":"-81:data"},{"to_node_id":"-118:instruments","from_node_id":"-109:data"},{"to_node_id":"-162:input_1","from_node_id":"-118:data"},{"to_node_id":"-185:options_data","from_node_id":"-162:data_1"},{"to_node_id":"-185:instruments","from_node_id":"-207:data"}],"nodes":[{"node_id":"-72","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2021-06-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2021-12-1","type":"Literal","bound_global_parameter":null},{"name":"market","value":"CN_FUTURE","type":"Literal","bound_global_parameter":null},{"name":"instrument_list","value":"C2205.DCE","type":"Literal","bound_global_parameter":null},{"name":"max_count","value":0,"type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"rolling_conf","node_id":"-72"}],"output_ports":[{"name":"data","node_id":"-72"}],"cacheable":true,"seq_num":1,"comment":"","comment_collapsed":true},{"node_id":"-81","module_id":"BigQuantSpace.use_datasource.use_datasource-v1","parameters":[{"name":"datasource_id","value":"bar1d_CN_FUTURE","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}],"input_ports":[{"name":"instruments","node_id":"-81"},{"name":"features","node_id":"-81"}],"output_ports":[{"name":"data","node_id":"-81"}],"cacheable":true,"seq_num":2,"comment":"","comment_collapsed":true},{"node_id":"-109","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2021-06-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2021-12-1","type":"Literal","bound_global_parameter":null},{"name":"market","value":"CN_FUTURE","type":"Literal","bound_global_parameter":null},{"name":"instrument_list","value":"C2201.DCE\n","type":"Literal","bound_global_parameter":null},{"name":"max_count","value":0,"type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"rolling_conf","node_id":"-109"}],"output_ports":[{"name":"data","node_id":"-109"}],"cacheable":true,"seq_num":6,"comment":"","comment_collapsed":true},{"node_id":"-118","module_id":"BigQuantSpace.use_datasource.use_datasource-v1","parameters":[{"name":"datasource_id","value":"bar1d_CN_FUTURE","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}],"input_ports":[{"name":"instruments","node_id":"-118"},{"name":"features","node_id":"-118"}],"output_ports":[{"name":"data","node_id":"-118"}],"cacheable":true,"seq_num":7,"comment":"","comment_collapsed":true},{"node_id":"-162","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 df_C2201 = input_1.read()\n df_C2205 = input_2.read()\n df_C2201=df_C2201.set_index(df_C2201['date'])\n df_C2205=df_C2205.set_index(df_C2205['date'])\n df_sp = df_C2201['close'] - df_C2205['close']\n a=df_C2201[['close','open']]\n a.rename(columns={'close':'C2201_close'}, inplace = True)\n b=df_C2205[['close','open']]\n b.rename(columns={'close':'C2205_close'}, inplace = True)\n data=pd.concat([a,b],axis=1)\n data = data[['C2201_close','C2205_close']]\n data['p'] = df_sp\n data['mean_20'] = data['p'].rolling(20).mean()\n data.dropna(inplace=True)\n data['std'] = np.std(data['p'])\n data['sig'] = (data['p'] - data['mean_20'])/data['std']\n data['up_line'] = data['mean_20'] + data['std']\n data['low_line'] = data['mean_20'] - data['std']\n data['date'] = data.index\n data_1 = DataSource.write_df(data)\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":"{}","type":"Literal","bound_global_parameter":null},{"name":"output_ports","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-162"},{"name":"input_2","node_id":"-162"},{"name":"input_3","node_id":"-162"}],"output_ports":[{"name":"data_1","node_id":"-162"},{"name":"data_2","node_id":"-162"},{"name":"data_3","node_id":"-162"}],"cacheable":true,"seq_num":8,"comment":"","comment_collapsed":true},{"node_id":"-185","module_id":"BigQuantSpace.hftrade.hftrade-v2","parameters":[{"name":"start_date","value":"2021-06-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2021-12-1","type":"Literal","bound_global_parameter":null},{"name":"initialize","value":"# 交易引擎:初始化函数,只执行一次\ndef bigquant_run(context):\n # 加载预测数据\n context.all_data = context.options[\"data\"].read()\n context.order_num = 1\n context.set_slippage_value(volume_limit=1)\n","type":"Literal","bound_global_parameter":null},{"name":"before_trading_start","value":"# 交易引擎:每个单位时间开盘前调用一次。\ndef bigquant_run(context, data):\n #订阅合约\n context.subscribe([context.instruments[0],context.instruments[1]])\n context.set_slippage_value(volume_limit=1)","type":"Literal","bound_global_parameter":null},{"name":"handle_tick","value":"# 交易引擎:tick数据处理函数,每个tick执行一次\ndef bigquant_run(context, data):\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"handle_data","value":"#平仓\ndef close_orders(context,data):\n #获取当前时间\n cur_date = data.current_dt.strftime('%Y-%m-%d')\n\n for ins in context.instruments:\n # 分别获取多头持仓和空头持仓\n position_long = context.get_position(ins, Direction.LONG)\n position_short = context.get_position(ins, Direction.SHORT)\n price = data.current(ins,\"close\")\n if(position_long.current_qty != 0):\n context.sell_close(ins, position_long.avail_qty, price, order_type=OrderType.MARKET)\n\n if(position_short.current_qty != 0):\n context.buy_close(ins, position_short.avail_qty, price, order_type=OrderType.MARKET)\n \n# 交易引擎:bar数据处理函数,每个时间单位执行一次\ndef bigquant_run(context, data): \n today = data.current_dt.strftime('%Y-%m-%d') # 当前交易日期\n all_data = context.all_data[context.all_data.date == today]\n if len(all_data) ==0:#过滤没有指标的数据\n return\n up_line = all_data['up_line'].iloc[0]\n low_line = all_data['low_line'].iloc[0]\n sig = all_data['sig'].iloc[0]\n sp = all_data['p'].iloc[0]\n instrument_2201 = context.future_symbol(context.instruments[0]) # 交易标的\n instrument_2205 = context.future_symbol(context.instruments[1]) # 交易标的\n \n #正向开仓 \n if sig >= 1 and sp > up_line:\n #先平掉所有持仓\n close_orders(context,data)\n #做多\n price = data.current(context.instruments[0],\"close\")\n context.buy_open(context.instruments[0], context.order_num, price, order_type=OrderType.MARKET)\n #做空\n price = data.current(context.instruments[1],\"close\")\n context.sell_open(context.instruments[1], context.order_num, price, order_type=OrderType.MARKET)\n \n #反向开仓 \n elif sig <= -1 and sp < low_line:\n #先平掉所有持仓\n close_orders(context,data)\n #做多\n price = data.current(context.instruments[1],\"close\")\n context.buy_open(context.instruments[1], context.order_num, price, order_type=OrderType.MARKET)\n #做空\n price = data.current(context.instruments[0],\"close\")\n context.sell_open(context.instruments[0], context.order_num, price, order_type=OrderType.MARKET)\n ","type":"Literal","bound_global_parameter":null},{"name":"handle_trade","value":"# 交易引擎:成交回报处理函数,每个成交发生时执行一次\ndef bigquant_run(context, data):\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"handle_order","value":"# 交易引擎:委托回报处理函数,每个委托变化时执行一次\ndef bigquant_run(context, data):\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":"8000","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":"benchmark","value":"000300.HIX","type":"Literal","bound_global_parameter":null},{"name":"plot_charts","value":"True","type":"Literal","bound_global_parameter":null},{"name":"disable_cache","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":"-185"},{"name":"options_data","node_id":"-185"},{"name":"history_ds","node_id":"-185"},{"name":"benchmark_ds","node_id":"-185"}],"output_ports":[{"name":"raw_perf","node_id":"-185"}],"cacheable":false,"seq_num":3,"comment":"","comment_collapsed":true},{"node_id":"-207","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2021-06-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2021-12-1","type":"Literal","bound_global_parameter":null},{"name":"market","value":"CN_FUTURE","type":"Literal","bound_global_parameter":null},{"name":"instrument_list","value":"C2201.DCE\nC2205.DCE\n","type":"Literal","bound_global_parameter":null},{"name":"max_count","value":0,"type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"rolling_conf","node_id":"-207"}],"output_ports":[{"name":"data","node_id":"-207"}],"cacheable":true,"seq_num":5,"comment":"","comment_collapsed":true}],"node_layout":"<node_postions><node_position Node='-72' Position='348.8028869628906,125.15370178222656,200,200'/><node_position Node='-81' Position='331,223,200,200'/><node_position Node='-109' Position='36,130,200,200'/><node_position Node='-118' Position='34,220,200,200'/><node_position Node='-162' Position='189,333,200,200'/><node_position Node='-185' Position='52,510,200,200'/><node_position Node='-207' Position='-81,409,200,200'/></node_postions>"},"nodes_readonly":false,"studio_version":"v2"}
    In [1]:
    # 本代码由可视化策略环境自动生成 2021年12月10日 15:17
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    def m8_run_bigquant_run(input_1, input_2, input_3):
        # 示例代码如下。在这里编写您的代码
        df_C2201 = input_1.read()
        df_C2205 = input_2.read()
        df_C2201=df_C2201.set_index(df_C2201['date'])
        df_C2205=df_C2205.set_index(df_C2205['date'])
        df_sp = df_C2201['close'] - df_C2205['close']
        a=df_C2201[['close','open']]
        a.rename(columns={'close':'C2201_close'}, inplace = True)
        b=df_C2205[['close','open']]
        b.rename(columns={'close':'C2205_close'}, inplace = True)
        data=pd.concat([a,b],axis=1)
        data = data[['C2201_close','C2205_close']]
        data['p'] = df_sp
        data['mean_20'] = data['p'].rolling(20).mean()
        data.dropna(inplace=True)
        data['std'] = np.std(data['p'])
        data['sig'] = (data['p'] - data['mean_20'])/data['std']
        data['up_line'] = data['mean_20'] + data['std']
        data['low_line'] = data['mean_20'] - data['std']
        data['date'] = data.index
        data_1 = DataSource.write_df(data)
        return Outputs(data_1=data_1, data_2=None, data_3=None)
    
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m8_post_run_bigquant_run(outputs):
        return outputs
    
    # 交易引擎:初始化函数,只执行一次
    def m3_initialize_bigquant_run(context):
        # 加载预测数据
        context.all_data = context.options["data"].read()
        context.order_num = 1
        context.set_slippage_value(volume_limit=1)
    
    # 交易引擎:每个单位时间开盘前调用一次。
    def m3_before_trading_start_bigquant_run(context, data):
        #订阅合约
        context.subscribe([context.instruments[0],context.instruments[1]])
        context.set_slippage_value(volume_limit=1)
    # 交易引擎:tick数据处理函数,每个tick执行一次
    def m3_handle_tick_bigquant_run(context, data):
        pass
    
    #平仓
    def close_orders(context,data):
        #获取当前时间
        cur_date = data.current_dt.strftime('%Y-%m-%d')
    
        for ins in context.instruments:
            # 分别获取多头持仓和空头持仓
            position_long = context.get_position(ins, Direction.LONG)
            position_short = context.get_position(ins, Direction.SHORT)
            price = data.current(ins,"close")
            if(position_long.current_qty != 0):
                context.sell_close(ins, position_long.avail_qty, price, order_type=OrderType.MARKET)
    
            if(position_short.current_qty != 0):
                context.buy_close(ins, position_short.avail_qty, price, order_type=OrderType.MARKET)
                 
    # 交易引擎:bar数据处理函数,每个时间单位执行一次
    def m3_handle_data_bigquant_run(context, data): 
        today = data.current_dt.strftime('%Y-%m-%d') # 当前交易日期
        all_data = context.all_data[context.all_data.date == today]
        if len(all_data) ==0:#过滤没有指标的数据
            return
        up_line = all_data['up_line'].iloc[0]
        low_line = all_data['low_line'].iloc[0]
        sig = all_data['sig'].iloc[0]
        sp = all_data['p'].iloc[0]
        instrument_2201 = context.future_symbol(context.instruments[0]) # 交易标的
        instrument_2205 = context.future_symbol(context.instruments[1]) # 交易标的
        
        #正向开仓    
        if sig >= 1 and sp > up_line:
            #先平掉所有持仓
            close_orders(context,data)
            #做多
            price = data.current(context.instruments[0],"close")
            context.buy_open(context.instruments[0], context.order_num, price, order_type=OrderType.MARKET)
            #做空
            price = data.current(context.instruments[1],"close")
            context.sell_open(context.instruments[1], context.order_num, price, order_type=OrderType.MARKET)
            
        #反向开仓    
        elif sig <= -1 and sp < low_line:
            #先平掉所有持仓
            close_orders(context,data)
            #做多
            price = data.current(context.instruments[1],"close")
            context.buy_open(context.instruments[1], context.order_num, price, order_type=OrderType.MARKET)
            #做空
            price = data.current(context.instruments[0],"close")
            context.sell_open(context.instruments[0], context.order_num, price, order_type=OrderType.MARKET)
                
    # 交易引擎:成交回报处理函数,每个成交发生时执行一次
    def m3_handle_trade_bigquant_run(context, data):
        pass
    
    # 交易引擎:委托回报处理函数,每个委托变化时执行一次
    def m3_handle_order_bigquant_run(context, data):
        pass
    
    # 交易引擎:盘后处理函数,每日盘后执行一次
    def m3_after_trading_bigquant_run(context, data):
        pass
    
    
    m1 = M.instruments.v2(
        start_date='2021-06-01',
        end_date='2021-12-1',
        market='CN_FUTURE',
        instrument_list='C2205.DCE',
        max_count=0
    )
    
    m2 = M.use_datasource.v1(
        instruments=m1.data,
        datasource_id='bar1d_CN_FUTURE',
        start_date='',
        end_date=''
    )
    
    m6 = M.instruments.v2(
        start_date='2021-06-01',
        end_date='2021-12-1',
        market='CN_FUTURE',
        instrument_list="""C2201.DCE
    """,
        max_count=0
    )
    
    m7 = M.use_datasource.v1(
        instruments=m6.data,
        datasource_id='bar1d_CN_FUTURE',
        start_date='',
        end_date=''
    )
    
    m8 = M.cached.v3(
        input_1=m7.data,
        input_2=m2.data,
        run=m8_run_bigquant_run,
        post_run=m8_post_run_bigquant_run,
        input_ports='',
        params='{}',
        output_ports=''
    )
    
    m5 = M.instruments.v2(
        start_date='2021-06-01',
        end_date='2021-12-1',
        market='CN_FUTURE',
        instrument_list="""C2201.DCE
    C2205.DCE
    """,
        max_count=0
    )
    
    m3 = M.hftrade.v2(
        instruments=m5.data,
        options_data=m8.data_1,
        start_date='2021-06-01',
        end_date='2021-12-1',
        initialize=m3_initialize_bigquant_run,
        before_trading_start=m3_before_trading_start_bigquant_run,
        handle_tick=m3_handle_tick_bigquant_run,
        handle_data=m3_handle_data_bigquant_run,
        handle_trade=m3_handle_trade_bigquant_run,
        handle_order=m3_handle_order_bigquant_run,
        after_trading=m3_after_trading_bigquant_run,
        capital_base=8000,
        frequency='daily',
        price_type='真实价格',
        product_type='期货',
        before_start_days='0',
        benchmark='000300.HIX',
        plot_charts=True,
        disable_cache=False,
        show_debug_info=False,
        backtest_only=False
    )
    
    • 收益率0.27%
    • 年化收益率0.54%
    • 基准收益率-9.32%
    • 阿尔法-0.03
    • 贝塔-0.05
    • 夏普比率-0.13
    • 胜率0.27
    • 盈亏比1.09
    • 收益波动率12.38%
    • 最大回撤5.22%
    bigcharts-data-start/{"__type":"tabs","__id":"bigchart-c37fcde2fd9a4e9fa6a107fa9967a4bf"}/bigcharts-data-end
    In [ ]: