复制链接
克隆策略

期货日线布林带策略

版本 v1.0

目录

  • ### 布林带策略的交易规则

  • ### 策略构建步骤

  • ### 策略的实现

正文

一、布林带策略的交易规则

  • 相关定义如下:

    中轨 = N时间段的close价格简单移动平均线

    上轨 = 中轨 + K × N时间段的标准差

    下轨 = 中轨 − K × N时间段的标准差

    当前价格相对位置 percent_b = (close - lower)/(upper - lower)

    本例中N取20,K取2

  • 当percent_b ≥ 1时,以第二日开盘价平空开多;

  • 当percent_b ≤ 0时,以第二日开盘价平多卖空;

二、策略构建步骤

1、确定期货合约和回测时间

  • 通过证券代码列表输入要回测的期货合约,以及回测的起止日期

2、确定买卖条件信号

  • 在输入特征列表中通过表达式引擎定义 buy_condition=where((close-(mean(close,20) - 2*std(close,20))) / (4*std(close,20))>=1,1,0),实现买入信号。
  • 在输入特征列表中通过表达式引擎定义 sell_condition=where((close-(mean(close,20)- 2*std(close,20))) / (4*std(close,20))<=0,1,0),实现卖出信号。
  • 通过自定义Python模块获取合约基础数据,通过衍生特征抽取模块实现买卖条件指标 buy_condition 和 sell_condition 数据的抽取。
  • 通过缺失数据处理模块删去有缺失值的数据。

3、确定买卖原则

  • 如果当日 buy_condition > 0,执行平空开多操作;
  • 如果当日 sell_condition > 0,执行平多开空操作。

4、模拟回测

  • 通过 trade 模块中的初始化函数定义交易手续费、滑点、杠杆比例和是否逐日结算;
  • 通过 trade 模块中的准备函数定义 context.buy_condition 和 context.sell_condition 变量来获取并存放每日买卖交易信号;
  • 通过 trade 模块中的主函数(handle函数)查看每日的交易信号,按照买卖原则执行相应的交易操作。

三、策略的实现

可视化策略实现如下:

    {"description":"实验创建于2018/10/16","graph":{"edges":[{"to_node_id":"-1442:instruments","from_node_id":"-25:data"},{"to_node_id":"-1483:input_1","from_node_id":"-25:data"},{"to_node_id":"-1473:features","from_node_id":"-1468:data"},{"to_node_id":"-1632:input_data","from_node_id":"-1473:data"},{"to_node_id":"-1473:input_data","from_node_id":"-1483:data_1"},{"to_node_id":"-1442:options_data","from_node_id":"-1632:data"}],"nodes":[{"node_id":"-25","module_id":"BigQuantSpace.instruments.instruments-v2","parameters":[{"name":"start_date","value":"2017-11-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"2018-05-01","type":"Literal","bound_global_parameter":null},{"name":"market","value":"CN_FUTURE","type":"Literal","bound_global_parameter":null},{"name":"instrument_list","value":"RU1809.SHF","type":"Literal","bound_global_parameter":null},{"name":"max_count","value":0,"type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"rolling_conf","node_id":"-25"}],"output_ports":[{"name":"data","node_id":"-25"}],"cacheable":true,"seq_num":2,"comment":"","comment_collapsed":true},{"node_id":"-1442","module_id":"BigQuantSpace.trade.trade-v4","parameters":[{"name":"start_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"initialize","value":"# 回测引擎:初始化函数,只执行一次\ndef bigquant_run(context):\n # 设置是否是结算模式\n context.set_need_settle(False)\n # 设置最大杠杆\n context.set_max_leverage(1, 'fill_amap')","type":"Literal","bound_global_parameter":null},{"name":"handle_data","value":"# 回测引擎:每日数据处理函数,每天执行一次\ndef bigquant_run(context, data):\n # 获取当日指标数据\n today = data.current_dt.strftime('%Y-%m-%d') # 当前交易日期\n buy_condition=context.buy_condition[today]\n sell_condition=context.sell_condition[today]\n \n instrument = context.future_symbol(context.instruments[0]) # 交易标的\n curr_po=context.portfolio.positions[instrument] # 组合持仓\n curr_position = curr_po.amount # 持仓数量\n \n\n # 交易逻辑\n if buy_condition>0 and data.can_trade(instrument): # 开多,下单数量20手\n order_target(instrument, 20)\n print(today,'平空开多')\n \n elif sell_condition>0 and data.can_trade(instrument):# 开空,下单数量20手\n order_target(instrument, -20)\n print(today,'平多开空')","type":"Literal","bound_global_parameter":null},{"name":"prepare","value":"# 回测引擎:准备数据,只执行一次\ndef bigquant_run(context):\n df = context.options['data'].read_df()\n df['date']=df['date'].apply(lambda x:x.strftime('%Y-%m-%d'))\n df.set_index('date',inplace=True)\n context.buy_condition=df['buy_condition']\n context.sell_condition=df['sell_condition']\n","type":"Literal","bound_global_parameter":null},{"name":"before_trading_start","value":"# 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。\ndef bigquant_run(context, data):\n pass\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":"open","type":"Literal","bound_global_parameter":null},{"name":"capital_base","value":"1000000","type":"Literal","bound_global_parameter":null},{"name":"auto_cancel_non_tradable_orders","value":"True","type":"Literal","bound_global_parameter":null},{"name":"data_frequency","value":"daily","type":"Literal","bound_global_parameter":null},{"name":"price_type","value":"后复权","type":"Literal","bound_global_parameter":null},{"name":"product_type","value":"期货","type":"Literal","bound_global_parameter":null},{"name":"plot_charts","value":"True","type":"Literal","bound_global_parameter":null},{"name":"backtest_only","value":"False","type":"Literal","bound_global_parameter":null},{"name":"benchmark","value":"000300.HIX","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-1442"},{"name":"options_data","node_id":"-1442"},{"name":"history_ds","node_id":"-1442"},{"name":"benchmark_ds","node_id":"-1442"},{"name":"trading_calendar","node_id":"-1442"}],"output_ports":[{"name":"raw_perf","node_id":"-1442"}],"cacheable":false,"seq_num":3,"comment":"","comment_collapsed":true},{"node_id":"-1468","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"\n# #号开始的表示注释\n# 多个特征,每行一个,可以包含基础特征和衍生特征\nbuy_condition=where((close-(mean(close,20)-2*std(close,20)))/(4*std(close,20))>=1,1,0)\nsell_condition=where((close-(mean(close,20)-2*std(close,20)))/(4*std(close,20))<=0,1,0)\n\n","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"-1468"}],"output_ports":[{"name":"data","node_id":"-1468"}],"cacheable":true,"seq_num":5,"comment":"","comment_collapsed":true},{"node_id":"-1473","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":"False","type":"Literal","bound_global_parameter":null},{"name":"remove_extra_columns","value":"False","type":"Literal","bound_global_parameter":null},{"name":"user_functions","value":"{}","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_data","node_id":"-1473"},{"name":"features","node_id":"-1473"}],"output_ports":[{"name":"data","node_id":"-1473"}],"cacheable":true,"seq_num":6,"comment":"","comment_collapsed":true},{"node_id":"-1483","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,before_days):\n # 示例代码如下。在这里编写您的代码\n start_date=(pd.to_datetime(input_1.read_pickle()['start_date']) - datetime.timedelta(days=before_days)).strftime('%Y-%m-%d')\n end_date=input_1.read_pickle()['end_date']\n instruments=input_1.read_pickle()['instruments']\n fields=['open','high','low','close']\n df = DataSource('bar1d_CN_FUTURE').read(instruments,start_date,end_date,fields)\n df['adjust_factor']=1.0\n data_1 = DataSource.write_df(df)\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":"{'before_days':60}","type":"Literal","bound_global_parameter":null},{"name":"output_ports","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input_1","node_id":"-1483"},{"name":"input_2","node_id":"-1483"},{"name":"input_3","node_id":"-1483"}],"output_ports":[{"name":"data_1","node_id":"-1483"},{"name":"data_2","node_id":"-1483"},{"name":"data_3","node_id":"-1483"}],"cacheable":true,"seq_num":4,"comment":"","comment_collapsed":true},{"node_id":"-1632","module_id":"BigQuantSpace.dropnan.dropnan-v1","parameters":[],"input_ports":[{"name":"input_data","node_id":"-1632"}],"output_ports":[{"name":"data","node_id":"-1632"}],"cacheable":true,"seq_num":7,"comment":"","comment_collapsed":true}],"node_layout":"<node_postions><node_position Node='-25' Position='-240,16,200,200'/><node_position Node='-1442' Position='210,480.2281494140625,200,200'/><node_position Node='-1468' Position='259,30,200,200'/><node_position Node='-1473' Position='213,237,200,200'/><node_position Node='-1483' Position='-17,124,200,200'/><node_position Node='-1632' Position='151,342,200,200'/></node_postions>"},"nodes_readonly":false,"studio_version":"v2"}
    In [1]:
    # 本代码由可视化策略环境自动生成 2021年12月6日 22:50
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    def m4_run_bigquant_run(input_1, input_2, input_3,before_days):
        # 示例代码如下。在这里编写您的代码
        start_date=(pd.to_datetime(input_1.read_pickle()['start_date']) - datetime.timedelta(days=before_days)).strftime('%Y-%m-%d')
        end_date=input_1.read_pickle()['end_date']
        instruments=input_1.read_pickle()['instruments']
        fields=['open','high','low','close']
        df = DataSource('bar1d_CN_FUTURE').read(instruments,start_date,end_date,fields)
        df['adjust_factor']=1.0
        data_1 = DataSource.write_df(df)
        return Outputs(data_1=data_1, data_2=None, data_3=None)
    
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m4_post_run_bigquant_run(outputs):
        return outputs
    
    # 回测引擎:初始化函数,只执行一次
    def m3_initialize_bigquant_run(context):
       # 设置是否是结算模式
        context.set_need_settle(False)
        # 设置最大杠杆
        context.set_max_leverage(1, 'fill_amap')
    # 回测引擎:每日数据处理函数,每天执行一次
    def m3_handle_data_bigquant_run(context, data):
        # 获取当日指标数据
        today = data.current_dt.strftime('%Y-%m-%d') # 当前交易日期
        buy_condition=context.buy_condition[today]
        sell_condition=context.sell_condition[today]
        
        instrument = context.future_symbol(context.instruments[0]) # 交易标的
        curr_po=context.portfolio.positions[instrument] # 组合持仓
        curr_position = curr_po.amount  # 持仓数量
             
    
        # 交易逻辑
        if buy_condition>0 and data.can_trade(instrument): # 开多,下单数量20手
            order_target(instrument,  20)
            print(today,'平空开多')
                
        elif  sell_condition>0 and data.can_trade(instrument):# 开空,下单数量20手
            order_target(instrument, -20)
            print(today,'平多开空')
    # 回测引擎:准备数据,只执行一次
    def m3_prepare_bigquant_run(context):
        df = context.options['data'].read_df()
        df['date']=df['date'].apply(lambda x:x.strftime('%Y-%m-%d'))
        df.set_index('date',inplace=True)
        context.buy_condition=df['buy_condition']
        context.sell_condition=df['sell_condition']
    
    # 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。
    def m3_before_trading_start_bigquant_run(context, data):
        pass
    
    
    m2 = M.instruments.v2(
        start_date='2017-11-01',
        end_date='2018-05-01',
        market='CN_FUTURE',
        instrument_list='RU1809.SHF',
        max_count=0
    )
    
    m4 = M.cached.v3(
        input_1=m2.data,
        run=m4_run_bigquant_run,
        post_run=m4_post_run_bigquant_run,
        input_ports='',
        params='{\'before_days\':60}',
        output_ports=''
    )
    
    m5 = M.input_features.v1(
        features="""
    # #号开始的表示注释
    # 多个特征,每行一个,可以包含基础特征和衍生特征
    buy_condition=where((close-(mean(close,20)-2*std(close,20)))/(4*std(close,20))>=1,1,0)
    sell_condition=where((close-(mean(close,20)-2*std(close,20)))/(4*std(close,20))<=0,1,0)
    
    """
    )
    
    m6 = M.derived_feature_extractor.v3(
        input_data=m4.data_1,
        features=m5.data,
        date_col='date',
        instrument_col='instrument',
        drop_na=False,
        remove_extra_columns=False,
        user_functions={}
    )
    
    m7 = M.dropnan.v1(
        input_data=m6.data
    )
    
    m3 = M.trade.v4(
        instruments=m2.data,
        options_data=m7.data,
        start_date='',
        end_date='',
        initialize=m3_initialize_bigquant_run,
        handle_data=m3_handle_data_bigquant_run,
        prepare=m3_prepare_bigquant_run,
        before_trading_start=m3_before_trading_start_bigquant_run,
        volume_limit=0,
        order_price_field_buy='open',
        order_price_field_sell='open',
        capital_base=1000000,
        auto_cancel_non_tradable_orders=True,
        data_frequency='daily',
        price_type='后复权',
        product_type='期货',
        plot_charts=True,
        backtest_only=False,
        benchmark='000300.HIX'
    )
    
    [2018-11-01 09:45:52.420306] INFO: bigquant: instruments.v2 开始运行..
    [2018-11-01 09:45:52.470055] INFO: bigquant: 命中缓存
    [2018-11-01 09:45:52.471265] INFO: bigquant: instruments.v2 运行完成[0.050996s].
    [2018-11-01 09:45:52.515310] INFO: bigquant: cached.v3 开始运行..
    [2018-11-01 09:45:52.543582] INFO: bigquant: 命中缓存
    [2018-11-01 09:45:52.545121] INFO: bigquant: cached.v3 运行完成[0.029804s].
    [2018-11-01 09:45:52.558287] INFO: bigquant: input_features.v1 开始运行..
    [2018-11-01 09:45:52.565886] INFO: bigquant: 命中缓存
    [2018-11-01 09:45:52.566904] INFO: bigquant: input_features.v1 运行完成[0.008612s].
    [2018-11-01 09:45:52.651570] INFO: bigquant: derived_feature_extractor.v3 开始运行..
    [2018-11-01 09:45:52.656884] INFO: bigquant: 命中缓存
    [2018-11-01 09:45:52.658042] INFO: bigquant: derived_feature_extractor.v3 运行完成[0.006485s].
    [2018-11-01 09:45:52.661909] INFO: bigquant: dropnan.v1 开始运行..
    [2018-11-01 09:45:52.692403] INFO: bigquant: 命中缓存
    [2018-11-01 09:45:52.693535] INFO: bigquant: dropnan.v1 运行完成[0.031599s].
    [2018-11-01 09:45:52.769417] INFO: bigquant: backtest.v8 开始运行..
    [2018-11-01 09:45:52.772220] INFO: bigquant: biglearning backtest:V8.1.3
    [2018-11-01 09:45:52.933973] INFO: bigquant: product_type:future by specified
    [2018-11-01 09:45:56.948960] INFO: algo: TradingAlgorithm V1.3.3
    2017-11-02 平空开多
    2017-11-06 平空开多
    2017-11-07 平空开多
    2017-11-13 平空开多
    2017-12-04 平空开多
    2018-01-23 平多开空
    2018-01-24 平多开空
    2018-01-30 平多开空
    2018-01-31 平多开空
    2018-02-01 平多开空
    2018-02-06 平多开空
    2018-03-20 平多开空
    2018-03-21 平多开空
    2018-03-22 平多开空
    2018-03-23 平多开空
    2018-03-26 平多开空
    2018-03-27 平多开空
    [2018-11-01 09:45:57.470970] INFO: Performance: Simulated 120 trading days out of 120.
    [2018-11-01 09:45:57.472119] INFO: Performance: first open: 2017-10-31 21:00:00+00:00
    [2018-11-01 09:45:57.472902] INFO: Performance: last close: 2018-04-27 15:00:00+00:00
    
    • 收益率41.86%
    • 年化收益率108.41%
    • 基准收益率-19.76%
    • 阿尔法1.01
    • 贝塔0.25
    • 夏普比率1.44
    • 胜率0.0
    • 盈亏比0.0
    • 收益波动率62.25%
    • 信息比率0.13
    • 最大回撤23.82%
    [2018-11-01 09:45:58.065853] INFO: bigquant: backtest.v8 运行完成[5.296408s].