基金策略回测样例

策略分享
基金
etf
多市场
场内基金
标签: #<Tag:0x00007f5b89d70910> #<Tag:0x00007f5b89d707a8> #<Tag:0x00007f5b89d70668> #<Tag:0x00007f5b89d70528> #<Tag:0x00007f5b89d703c0>

(xgl891) #1

本文分享了一个简单的基于双均线的基金策略,主要是使用平台的回测引擎做一个基金的回测,给大家分享一些关于基金回测的tips。

策略基本思路

策略思想是:当短期均线上穿长期均线时,形成金叉,此时买入基金。当短期均线下穿长期均线时,形成死叉,此时卖出基金。研究表明,双均线系统虽然简单,但只要严格执行,也能长期盈利。
具体可参考 学院-金叉死叉策略

基金回测

  • 证券代码列表中的交易市场需要选择 CN_FUND.

  • 由于平台现在的基础特征抽取模块不支持对基金的特征抽取,因此需要连入一个自定义模块来读取基础数据,然后可以使用衍生特征抽取来计算自己想要的因子。自定义模块的代码可参考下面策略里的m2。

  • 现在平台回测模块的回测产品类型暂时只支持股票,期货,和期权。所以如果要做基金回测的话,需要自己输入回测历史数据,即 将历史数据连接到回测模块的第三个输入接口那里。

  • 关于基金的代码表示:证券代码列表中交易市场选择CN_FUND后,如果想要某只特定的基金数据,可以在股票代码列表里输入相应的基金代码,如果不知道自己想要的基金的代码表示,可以在basic_info_CN_FUND表内读取相关基本信息进行查询,例如:
    1
    可以看到这个表里有instrument, display_name, name, list_date, delist_date, type 几个字段。
    如果想知道基金名为 科技ETF的基金代码,可以输入如下代码查看:
    2

基金策略

克隆策略

    {"Description":"实验创建于2018/10/17","Summary":"","Graph":{"EdgesInternal":[{"DestinationInputPortId":"-314:features","SourceOutputPortId":"-24:data"},{"DestinationInputPortId":"-432:input_data","SourceOutputPortId":"-314:data"},{"DestinationInputPortId":"-314:input_data","SourceOutputPortId":"-418:data_1"},{"DestinationInputPortId":"-112:options_data","SourceOutputPortId":"-432:data"},{"DestinationInputPortId":"-112:history_ds","SourceOutputPortId":"-432:data"},{"DestinationInputPortId":"-418:input_1","SourceOutputPortId":"-357:data"},{"DestinationInputPortId":"-112:instruments","SourceOutputPortId":"-357:data"}],"ModuleNodes":[{"Id":"-24","ModuleId":"BigQuantSpace.input_features.input_features-v1","ModuleParameters":[{"Name":"features","Value":"\n# #号开始的表示注释\n# 多个特征,每行一个,可以包含基础特征和衍生特征\n\nmean(close,5)\nmean(close,10)\n","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"features_ds","NodeId":"-24"}],"OutputPortsInternal":[{"Name":"data","NodeId":"-24","OutputType":null}],"UsePreviousResults":false,"moduleIdForCode":3,"IsPartOfPartialRun":null,"Comment":"定义指标","CommentCollapsed":false},{"Id":"-314","ModuleId":"BigQuantSpace.derived_feature_extractor.derived_feature_extractor-v3","ModuleParameters":[{"Name":"date_col","Value":"date","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"instrument_col","Value":"instrument","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"drop_na","Value":"False","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"remove_extra_columns","Value":"False","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"user_functions","Value":"{}","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_data","NodeId":"-314"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"features","NodeId":"-314"}],"OutputPortsInternal":[{"Name":"data","NodeId":"-314","OutputType":null}],"UsePreviousResults":true,"moduleIdForCode":5,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true},{"Id":"-418","ModuleId":"BigQuantSpace.cached.cached-v3","ModuleParameters":[{"Name":"run","Value":"# Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端\ndef bigquant_run(input_1, start_date,end_date):\n # 示例代码如下。在这里编写您的代码\n ins=input_1.read_pickle()['instruments']\n start_date=input_1.read_pickle()['start_date']\n end_date=input_1.read_pickle()['end_date']\n df=DataSource('bar1d_CN_FUND').read(ins,start_date=start_date,end_date=end_date)\n data_1 = DataSource.write_df(df)\n return Outputs(data_1=data_1, data_2=None, data_3=None)\n","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"post_run","Value":"# 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。\ndef bigquant_run(outputs):\n return outputs\n","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"input_ports","Value":"","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"params","Value":"{'start':'2018-08-29'}","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"output_ports","Value":"","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_1","NodeId":"-418"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_2","NodeId":"-418"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_3","NodeId":"-418"}],"OutputPortsInternal":[{"Name":"data_1","NodeId":"-418","OutputType":null},{"Name":"data_2","NodeId":"-418","OutputType":null},{"Name":"data_3","NodeId":"-418","OutputType":null}],"UsePreviousResults":true,"moduleIdForCode":2,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true},{"Id":"-432","ModuleId":"BigQuantSpace.dropnan.dropnan-v1","ModuleParameters":[],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_data","NodeId":"-432"}],"OutputPortsInternal":[{"Name":"data","NodeId":"-432","OutputType":null}],"UsePreviousResults":true,"moduleIdForCode":6,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true},{"Id":"-357","ModuleId":"BigQuantSpace.instruments.instruments-v2","ModuleParameters":[{"Name":"start_date","Value":"2013-01-16","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"end_date","Value":"2018-01-16","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"market","Value":"CN_FUND","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"instrument_list","Value":"510330.HOF","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"max_count","Value":0,"ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"rolling_conf","NodeId":"-357"}],"OutputPortsInternal":[{"Name":"data","NodeId":"-357","OutputType":null}],"UsePreviousResults":true,"moduleIdForCode":1,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true},{"Id":"-112","ModuleId":"BigQuantSpace.trade.trade-v4","ModuleParameters":[{"Name":"start_date","Value":"2013-01-29","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"end_date","Value":"","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"initialize","Value":"# 回测引擎:初始化函数,只执行一次\ndef bigquant_run(context):\n\n # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数\n context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))\n","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"handle_data","Value":"# 回测引擎:每日数据处理函数,每天执行一次\ndef bigquant_run(context, data):\n # 获取今日的日期\n today = data.current_dt.strftime('%Y-%m-%d') \n data_today=context.data[context.data['date']==today]\n sid = context.symbol(context.instruments[0]) \n price = data.current(sid, 'price')\n cash = context.portfolio.cash \n #cur_position = context.portfolio.positions[sid].amount\n\n short_avg=np.array(data_today['mean(close,5)'])[0]\n long_avg=np.array(data_today['mean(close,10)'])[0]\n \n \n if short_avg>long_avg :\n context.order(sid,int(cash/price/100)*100)\n if short_avg<long_avg :\n context.order_target_percent(sid, 0)","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"prepare","Value":"# 回测引擎:准备数据,只执行一次\ndef bigquant_run(context):\n # 加载预测数据\n \n df = context.options['data'].read_df()\n context.data=df\n pass","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"before_trading_start","Value":"# 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。\ndef bigquant_run(context, data):\n pass","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"volume_limit","Value":0.025,"ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"order_price_field_buy","Value":"open","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"order_price_field_sell","Value":"close","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"capital_base","Value":1000000,"ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"auto_cancel_non_tradable_orders","Value":"True","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"data_frequency","Value":"daily","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"price_type","Value":"后复权","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"product_type","Value":"股票","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"plot_charts","Value":"True","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"backtest_only","Value":"False","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"benchmark","Value":"","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"instruments","NodeId":"-112"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"options_data","NodeId":"-112"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"history_ds","NodeId":"-112"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"benchmark_ds","NodeId":"-112"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"trading_calendar","NodeId":"-112"}],"OutputPortsInternal":[{"Name":"raw_perf","NodeId":"-112","OutputType":null}],"UsePreviousResults":false,"moduleIdForCode":4,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true}],"SerializedClientData":"<?xml version='1.0' encoding='utf-16'?><DataV1 xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><Meta /><NodePositions><NodePosition Node='-24' Position='504,328,200,200'/><NodePosition Node='-314' Position='388,433,200,200'/><NodePosition Node='-418' Position='150,321,200,200'/><NodePosition Node='-432' Position='388,509,200,200'/><NodePosition Node='-357' Position='-87,188,200,200'/><NodePosition Node='-112' Position='203,653,200,200'/></NodePositions><NodeGroups /></DataV1>"},"IsDraft":true,"ParentExperimentId":null,"WebService":{"IsWebServiceExperiment":false,"Inputs":[],"Outputs":[],"Parameters":[{"Name":"交易日期","Value":"","ParameterDefinition":{"Name":"交易日期","FriendlyName":"交易日期","DefaultValue":"","ParameterType":"String","HasDefaultValue":true,"IsOptional":true,"ParameterRules":[],"HasRules":false,"MarkupType":0,"CredentialDescriptor":null}}],"WebServiceGroupId":null,"SerializedClientData":"<?xml version='1.0' encoding='utf-16'?><DataV1 xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><Meta /><NodePositions></NodePositions><NodeGroups /></DataV1>"},"DisableNodesUpdate":false,"Category":"user","Tags":[],"IsPartialRun":true}
    In [49]:
    # 本代码由可视化策略环境自动生成 2019年9月4日 14:47
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
    def m2_run_bigquant_run(input_1, start_date,end_date):
        # 示例代码如下。在这里编写您的代码
        ins=input_1.read_pickle()['instruments']
        start_date=input_1.read_pickle()['start_date']
        end_date=input_1.read_pickle()['end_date']
        df=DataSource('bar1d_CN_FUND').read(ins,start_date=start_date,end_date=end_date)
        data_1 = DataSource.write_df(df)
        return Outputs(data_1=data_1, data_2=None, data_3=None)
    
    # 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
    def m2_post_run_bigquant_run(outputs):
        return outputs
    
    # 回测引擎:初始化函数,只执行一次
    def m4_initialize_bigquant_run(context):
    
        # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
        context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    
    # 回测引擎:每日数据处理函数,每天执行一次
    def m4_handle_data_bigquant_run(context, data):
        # 获取今日的日期
        today = data.current_dt.strftime('%Y-%m-%d')  
        data_today=context.data[context.data['date']==today]
        sid = context.symbol(context.instruments[0]) 
        price = data.current(sid, 'price')
        cash = context.portfolio.cash 
        #cur_position = context.portfolio.positions[sid].amount
    
        short_avg=np.array(data_today['mean(close,5)'])[0]
        long_avg=np.array(data_today['mean(close,10)'])[0]
        
        
        if short_avg>long_avg :
            context.order(sid,int(cash/price/100)*100)
        if short_avg<long_avg :
            context.order_target_percent(sid, 0)
    # 回测引擎:准备数据,只执行一次
    def m4_prepare_bigquant_run(context):
        # 加载预测数据
        
        df = context.options['data'].read_df()
        context.data=df
        pass
    # 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。
    def m4_before_trading_start_bigquant_run(context, data):
        pass
    
    m3 = M.input_features.v1(
        features="""
    # #号开始的表示注释
    # 多个特征,每行一个,可以包含基础特征和衍生特征
    
    mean(close,5)
    mean(close,10)
    """,
        m_cached=False
    )
    
    m1 = M.instruments.v2(
        start_date='2013-01-16',
        end_date='2018-01-16',
        market='CN_FUND',
        instrument_list='510330.HOF',
        max_count=0
    )
    
    m2 = M.cached.v3(
        input_1=m1.data,
        run=m2_run_bigquant_run,
        post_run=m2_post_run_bigquant_run,
        input_ports='',
        params='{\'start\':\'2018-08-29\'}',
        output_ports=''
    )
    
    m5 = M.derived_feature_extractor.v3(
        input_data=m2.data_1,
        features=m3.data,
        date_col='date',
        instrument_col='instrument',
        drop_na=False,
        remove_extra_columns=False,
        user_functions={}
    )
    
    m6 = M.dropnan.v1(
        input_data=m5.data
    )
    
    m4 = M.trade.v4(
        instruments=m1.data,
        options_data=m6.data,
        history_ds=m6.data,
        start_date='2013-01-29',
        end_date='',
        initialize=m4_initialize_bigquant_run,
        handle_data=m4_handle_data_bigquant_run,
        prepare=m4_prepare_bigquant_run,
        before_trading_start=m4_before_trading_start_bigquant_run,
        volume_limit=0.025,
        order_price_field_buy='open',
        order_price_field_sell='close',
        capital_base=1000000,
        auto_cancel_non_tradable_orders=True,
        data_frequency='daily',
        price_type='后复权',
        product_type='股票',
        plot_charts=True,
        backtest_only=False,
        benchmark=''
    )
    
    • 收益率63.87%
    • 年化收益率10.84%
    • 基准收益率60.58%
    • 阿尔法0.04
    • 贝塔0.51
    • 夏普比率0.51
    • 胜率0.56
    • 盈亏比1.84
    • 收益波动率17.13%
    • 信息比率-0.0
    • 最大回撤23.84%
    bigcharts-data-start/{"__type":"tabs","__id":"bigchart-a87e0cf644aa4e8885806313f352d3ee"}/bigcharts-data-end

    可以看到,采用一个简单的双均线策略,比单独购买该基金并一直持有,策略收益更高,策略波动更小。