克隆策略

基于持仓的标签示例:

根据以下要求进行基金筛选,筛选逻辑作为“消费”标签保存

1、过往4个报告期的平均规模 > 10亿;

2、过往4个报告期的股票市值占净资产的比例 > 50%;

3、最新报告期在消费类行业上的标准化配置比例大于40%,其中消费类行业指申万一级行业分类中的食品饮料、家用电器、休闲服务和农林牧渔。标准化配置指:a、全持仓维度下,在消费类行业上的配置市值和股票配置市值的比值;b、季报前十大维度下,在消费类行业上的配置市值和前十大股票配置市值的比值

4、过往8个报告期,有5期以上在消费类行业上的标准化配置比例大于40%

基于业绩的标签示例:

以消费标签为范围,以T为时间周期参数计算标签内基金在T日内的相对排名序列,选取相对排名在a%分位至b%分位的基金

回测示例:

以以上两个标签为选基标准,等权构建(或以相对排名加权)构建基金组合,进行回测。

    {"description":"实验创建于2021/11/2","graph":{"edges":[{"to_node_id":"-484:input_data","from_node_id":"-466:data"},{"to_node_id":"-1086:input","from_node_id":"-466:data"},{"to_node_id":"-484:features","from_node_id":"-472:data"},{"to_node_id":"-150:data1","from_node_id":"-484:data"},{"to_node_id":"-145:data1","from_node_id":"-493:data"},{"to_node_id":"-125:input_1","from_node_id":"-500:data"},{"to_node_id":"-888:features","from_node_id":"-883:data"},{"to_node_id":"-1626:input_data","from_node_id":"-888:data"},{"to_node_id":"-718:options_data","from_node_id":"-1626:data"},{"to_node_id":"-888:input_data","from_node_id":"-133:data"},{"to_node_id":"-133:data1","from_node_id":"-145:data"},{"to_node_id":"-133:data2","from_node_id":"-150:data"},{"to_node_id":"-133:data3","from_node_id":"-125:data_1"},{"to_node_id":"-718:history_ds","from_node_id":"-1086:history_data"},{"to_node_id":"-718:instruments","from_node_id":"-1086:instrument_list"},{"to_node_id":"-718:trading_calendar","from_node_id":"-1086:calendar"}],"nodes":[{"node_id":"-466","module_id":"BigQuantSpace.use_datasource.use_datasource-v1","parameters":[{"name":"datasource_id","value":"nav_CN_FUND","type":"Literal","bound_global_parameter":null},{"name":"start_date","value":"2015-01-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-466"},{"name":"features","node_id":"-466"}],"output_ports":[{"name":"data","node_id":"-466"}],"cacheable":true,"seq_num":1,"comment":"基金净值数据,每天","comment_collapsed":false},{"node_id":"-472","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"mom = nav_acc / nav_acc.shift(60) -1 \nrk = rank(mom)","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"-472"}],"output_ports":[{"name":"data","node_id":"-472"}],"cacheable":true,"seq_num":2,"comment":"基金净值收益率及排名特征","comment_collapsed":false},{"node_id":"-484","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":"-484"},{"name":"features","node_id":"-484"}],"output_ports":[{"name":"data","node_id":"-484"}],"cacheable":true,"seq_num":4,"comment":"","comment_collapsed":true},{"node_id":"-493","module_id":"BigQuantSpace.use_datasource.use_datasource-v1","parameters":[{"name":"datasource_id","value":"share_CN_FUND","type":"Literal","bound_global_parameter":null},{"name":"start_date","value":"2015-01-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-493"},{"name":"features","node_id":"-493"}],"output_ports":[{"name":"data","node_id":"-493"}],"cacheable":true,"seq_num":3,"comment":"基金规模数据,每季度","comment_collapsed":false},{"node_id":"-500","module_id":"BigQuantSpace.use_datasource.use_datasource-v1","parameters":[{"name":"datasource_id","value":"portfolio_CN_FUND","type":"Literal","bound_global_parameter":null},{"name":"start_date","value":"2015-01-01","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-500"},{"name":"features","node_id":"-500"}],"output_ports":[{"name":"data","node_id":"-500"}],"cacheable":true,"seq_num":5,"comment":"基金持仓数据","comment_collapsed":false},{"node_id":"-718","module_id":"BigQuantSpace.trade.trade-v4","parameters":[{"name":"start_date","value":"2017-04-17","type":"Literal","bound_global_parameter":null},{"name":"end_date","value":"","type":"Literal","bound_global_parameter":null},{"name":"initialize","value":"# 回测引擎:初始化函数,只执行一次\ndef bigquant_run(context):\n df = context.options['data'].read()\n context.indicator_data = df[['date','instrument']].groupby('date').apply(lambda x:x.instrument.to_list())\n\n context.set_commission(PerOrder(buy_cost=0.0001, sell_cost=0.0001, min_cost=5))\n\n print('====', context.indicator_data)\n ","type":"Literal","bound_global_parameter":null},{"name":"handle_data","value":"# 回测引擎:每日数据处理函数,每天执行一次\ndef bigquant_run(context, data):\n \n # 当前的日期\n date = data.current_dt.strftime('%Y-%m-%d')\n \n try:\n cur_data = context.indicator_data[date.replace('-', '')]\n except:\n return \n # 根据日期获取调仓需要买入的股票的列表\n stock_to_buy = list(cur_data)\n # 通过positions对象,使用列表生成式的方法获取目前持仓的股票列表\n stock_hold_now = [equity.symbol for equity in context.portfolio.positions]\n # 继续持有的股票:调仓时,如果买入的股票已经存在于目前的持仓里,那么应继续持有\n no_need_to_sell = [i for i in stock_hold_now if i in stock_to_buy]\n # 需要卖出的股票\n stock_to_sell = [i for i in stock_hold_now if i not in no_need_to_sell]\n \n # 卖出\n for stock in stock_to_sell:\n # 如果该股票停牌,则没法成交。因此需要用can_trade方法检查下该股票的状态\n # 如果返回真值,则可以正常下单,否则会出错\n # 因为stock是字符串格式,我们用symbol方法将其转化成平台可以接受的形式:Equity格式\n\n if data.can_trade(context.symbol(stock)):\n try:\n context.order_target_percent(context.symbol(stock), 0)\n except ValueError as e:\n print('*****', date, e)\n # 如果当天没有买入的股票,就返回\n if len(stock_to_buy) == 0:\n return\n\n # 等权重买入 \n weight = 1 / len(stock_to_buy)\n \n # 买入\n for stock in stock_to_buy:\n if data.can_trade(context.symbol(stock)):\n # 下单使得某只股票的持仓权重达到weight,因为\n # weight大于0,因此是等权重买入\n try:\n context.order_target_percent(context.symbol(stock), weight)\n except ValueError as e:\n print('*****', date, e)\n ","type":"Literal","bound_global_parameter":null},{"name":"prepare","value":"# 回测引擎:准备数据,只执行一次\ndef bigquant_run(context):\n pass\n","type":"Literal","bound_global_parameter":null},{"name":"before_trading_start","value":"","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.SHA","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"instruments","node_id":"-718"},{"name":"options_data","node_id":"-718"},{"name":"history_ds","node_id":"-718"},{"name":"benchmark_ds","node_id":"-718"},{"name":"trading_calendar","node_id":"-718"}],"output_ports":[{"name":"raw_perf","node_id":"-718"}],"cacheable":false,"seq_num":21,"comment":"","comment_collapsed":true},{"node_id":"-883","module_id":"BigQuantSpace.input_features.input_features-v1","parameters":[{"name":"features","value":"size= share*nav\nstock_mkv_div_total_asset = stock_mkv_sum / size \ntop10_stock_mkv_div_total_asset = top10_stock_mkv_sum / size \n\ncomsumption_stock_mkv = 农林牧渔 + 家用电器 + 食品饮料 + 休闲服务 + 化工\ncomsumption_stock_div_stock_mkv = comsumption_stock_mkv / stock_mkv_sum\n\nc0 = mean(size, 4) > 1000000000\nc1 = ts_min(stock_mkv_div_total_asset, 4) > 0.5\nc2 = comsumption_stock_div_stock_mkv > 0.4\nc3 = sum(np.where(comsumption_stock_div_stock_mkv>0.4, 1, 0), 8) >=5\n\n ","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"features_ds","node_id":"-883"}],"output_ports":[{"name":"data","node_id":"-883"}],"cacheable":true,"seq_num":24,"comment":"计算选基特征","comment_collapsed":false},{"node_id":"-888","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":"-888"},{"name":"features","node_id":"-888"}],"output_ports":[{"name":"data","node_id":"-888"}],"cacheable":true,"seq_num":25,"comment":"构建选基条件","comment_collapsed":false},{"node_id":"-1626","module_id":"BigQuantSpace.filter.filter-v3","parameters":[{"name":"expr","value":"c0&c1&c2&c3&(rk>0.01)&(rk<0.99)","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":"-1626"}],"output_ports":[{"name":"data","node_id":"-1626"},{"name":"left_data","node_id":"-1626"}],"cacheable":true,"seq_num":26,"comment":"","comment_collapsed":true},{"node_id":"-133","module_id":"BigQuantSpace.joinx.joinx-v2","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":"-133"},{"name":"data2","node_id":"-133"},{"name":"data3","node_id":"-133"},{"name":"data4","node_id":"-133"}],"output_ports":[{"name":"data","node_id":"-133"}],"cacheable":true,"seq_num":6,"comment":"","comment_collapsed":true},{"node_id":"-145","module_id":"BigQuantSpace.resample.resample-v1","parameters":[{"name":"group","value":"instrument","type":"Literal","bound_global_parameter":null},{"name":"sessions","value":"Q","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"data1","node_id":"-145"}],"output_ports":[{"name":"data","node_id":"-145"}],"cacheable":true,"seq_num":9,"comment":"","comment_collapsed":true},{"node_id":"-150","module_id":"BigQuantSpace.resample.resample-v1","parameters":[{"name":"group","value":"instrument","type":"Literal","bound_global_parameter":null},{"name":"sessions","value":"Q","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"data1","node_id":"-150"}],"output_ports":[{"name":"data","node_id":"-150"}],"cacheable":true,"seq_num":7,"comment":"","comment_collapsed":true},{"node_id":"-125","module_id":"BigQuantSpace.add_fund_industry_pos_factor.add_fund_industry_pos_factor-v2","parameters":[],"input_ports":[{"name":"input_1","node_id":"-125"}],"output_ports":[{"name":"data_1","node_id":"-125"}],"cacheable":true,"seq_num":8,"comment":"","comment_collapsed":true},{"node_id":"-1086","module_id":"BigQuantSpace.trade_data_generation.trade_data_generation-v1","parameters":[{"name":"category","value":"CN_FUND","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"input","node_id":"-1086"}],"output_ports":[{"name":"history_data","node_id":"-1086"},{"name":"instrument_list","node_id":"-1086"},{"name":"calendar","node_id":"-1086"}],"cacheable":true,"seq_num":10,"comment":"","comment_collapsed":true}],"node_layout":"<node_postions><node_position Node='-466' Position='19.861114501953125,80.27932739257812,200,200'/><node_position Node='-472' Position='389,95,200,200'/><node_position Node='-484' Position='275,238,200,200'/><node_position Node='-493' Position='-65,239,200,200'/><node_position Node='-500' Position='639,202,200,200'/><node_position Node='-718' Position='159.557861328125,691.9529418945312,200,200'/><node_position Node='-883' Position='517,382,200,200'/><node_position Node='-888' Position='380,461,200,200'/><node_position Node='-1626' Position='379.8140869140625,534.6743774414062,200,200'/><node_position Node='-133' Position='102,387,200,200'/><node_position Node='-145' Position='-64,310,200,200'/><node_position Node='-150' Position='275,288,200,200'/><node_position Node='-125' Position='640,279,200,200'/><node_position Node='-1086' Position='39.79011535644531,497.6975402832031,200,200'/></node_postions>"},"nodes_readonly":false,"studio_version":"v2"}
    In [2]:
    # 本代码由可视化策略环境自动生成 2021年11月26日 10:51
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # 回测引擎:初始化函数,只执行一次
    def m21_initialize_bigquant_run(context):
        df = context.options['data'].read()
        context.indicator_data = df[['date','instrument']].groupby('date').apply(lambda x:x.instrument.to_list())
    
        context.set_commission(PerOrder(buy_cost=0.0001, sell_cost=0.0001, min_cost=5))
    
        print('====', context.indicator_data)
        
    # 回测引擎:每日数据处理函数,每天执行一次
    def m21_handle_data_bigquant_run(context, data):
         
        # 当前的日期
        date = data.current_dt.strftime('%Y-%m-%d')
        
        try:
            cur_data = context.indicator_data[date.replace('-', '')]
        except:
            return 
        # 根据日期获取调仓需要买入的股票的列表
        stock_to_buy = list(cur_data)
        # 通过positions对象,使用列表生成式的方法获取目前持仓的股票列表
        stock_hold_now = [equity.symbol for equity in context.portfolio.positions]
        # 继续持有的股票:调仓时,如果买入的股票已经存在于目前的持仓里,那么应继续持有
        no_need_to_sell = [i for i in stock_hold_now if i in stock_to_buy]
        # 需要卖出的股票
        stock_to_sell = [i for i in stock_hold_now if i not in no_need_to_sell]
      
        # 卖出
        for stock in stock_to_sell:
            # 如果该股票停牌,则没法成交。因此需要用can_trade方法检查下该股票的状态
            # 如果返回真值,则可以正常下单,否则会出错
            # 因为stock是字符串格式,我们用symbol方法将其转化成平台可以接受的形式:Equity格式
    
            if data.can_trade(context.symbol(stock)):
                try:
                    context.order_target_percent(context.symbol(stock), 0)
                except ValueError as e:
                    print('*****', date, e)
        # 如果当天没有买入的股票,就返回
        if len(stock_to_buy) == 0:
            return
    
        # 等权重买入 
        weight =  1 / len(stock_to_buy)
        
        # 买入
        for stock in stock_to_buy:
            if data.can_trade(context.symbol(stock)):
                # 下单使得某只股票的持仓权重达到weight,因为
                # weight大于0,因此是等权重买入
                try:
                    context.order_target_percent(context.symbol(stock), weight)
                except ValueError as e:
                    print('*****',  date, e)
      
    # 回测引擎:准备数据,只执行一次
    def m21_prepare_bigquant_run(context):
        pass
    
    
    m1 = M.use_datasource.v1(
        datasource_id='nav_CN_FUND',
        start_date='2015-01-01',
        end_date=''
    )
    
    m10 = M.trade_data_generation.v1(
        input=m1.data,
        category='CN_FUND'
    )
    
    m2 = M.input_features.v1(
        features="""mom = nav_acc / nav_acc.shift(60) -1 
    rk = rank(mom)"""
    )
    
    m4 = M.derived_feature_extractor.v3(
        input_data=m1.data,
        features=m2.data,
        date_col='date',
        instrument_col='instrument',
        drop_na=False,
        remove_extra_columns=False,
        user_functions={}
    )
    
    m7 = M.resample.v1(
        data1=m4.data,
        group='instrument',
        sessions='Q'
    )
    
    m3 = M.use_datasource.v1(
        datasource_id='share_CN_FUND',
        start_date='2015-01-01',
        end_date=''
    )
    
    m9 = M.resample.v1(
        data1=m3.data,
        group='instrument',
        sessions='Q'
    )
    
    m5 = M.use_datasource.v1(
        datasource_id='portfolio_CN_FUND',
        start_date='2015-01-01',
        end_date=''
    )
    
    m8 = M.add_fund_industry_pos_factor.v2(
        input_1=m5.data
    )
    
    m6 = M.joinx.v2(
        data1=m9.data,
        data2=m7.data,
        data3=m8.data_1,
        on='date,instrument',
        how='inner',
        sort=False
    )
    
    m24 = M.input_features.v1(
        features="""size= share*nav
    stock_mkv_div_total_asset =  stock_mkv_sum / size 
    top10_stock_mkv_div_total_asset = top10_stock_mkv_sum / size 
    
    comsumption_stock_mkv  = 农林牧渔 + 家用电器 + 食品饮料 + 休闲服务 + 化工
    comsumption_stock_div_stock_mkv = comsumption_stock_mkv / stock_mkv_sum
    
    c0 = mean(size, 4) > 1000000000
    c1 = ts_min(stock_mkv_div_total_asset, 4) > 0.5
    c2 = comsumption_stock_div_stock_mkv > 0.4
    c3 = sum(np.where(comsumption_stock_div_stock_mkv>0.4, 1, 0), 8) >=5
    
     """
    )
    
    m25 = M.derived_feature_extractor.v3(
        input_data=m6.data,
        features=m24.data,
        date_col='date',
        instrument_col='instrument',
        drop_na=False,
        remove_extra_columns=False,
        user_functions={}
    )
    
    m26 = M.filter.v3(
        input_data=m25.data,
        expr='c0&c1&c2&c3&(rk>0.01)&(rk<0.99)',
        output_left_data=False
    )
    
    m21 = M.trade.v4(
        instruments=m10.instrument_list,
        options_data=m26.data,
        history_ds=m10.history_data,
        trading_calendar=m10.calendar,
        start_date='2017-04-17',
        end_date='',
        initialize=m21_initialize_bigquant_run,
        handle_data=m21_handle_data_bigquant_run,
        prepare=m21_prepare_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.SHA'
    )
    
    ==== date
    2016-12-31                                         [159928.ZOF]
    2017-03-31                              [110011.OF, 159928.ZOF]
    2017-06-30                                          [110011.OF]
    2017-09-30                              [110011.OF, 161725.ZOF]
    2017-12-31                 [159928.ZOF, 161725.ZOF, 162605.ZOF]
    2018-03-31                              [110011.OF, 161725.ZOF]
    2018-06-30    [110011.OF, 159928.ZOF, 160222.ZOF, 161725.ZOF...
    2018-09-30    [001076.OF, 002851.OF, 110011.OF, 159928.ZOF, ...
    2018-12-31    [001076.OF, 159928.ZOF, 161725.ZOF, 162605.ZOF...
    2019-03-31    [001076.OF, 110011.OF, 160222.ZOF, 161725.ZOF,...
    2019-06-30    [001076.OF, 001927.OF, 110011.OF, 159928.ZOF, ...
    2019-09-30    [001076.OF, 001373.OF, 001927.OF, 004986.OF, 1...
    2019-12-31    [001044.OF, 005450.OF, 159928.ZOF, 160222.ZOF,...
    2020-03-31    [000603.OF, 001044.OF, 001076.OF, 001373.OF, 0...
    2020-06-30    [000974.OF, 001076.OF, 001373.OF, 005450.OF, 1...
    2020-09-30    [000628.OF, 000974.OF, 001044.OF, 001076.OF, 0...
    2020-12-31    [001044.OF, 001076.OF, 001349.OF, 001373.OF, 0...
    2021-03-31    [000529.OF, 000692.OF, 001076.OF, 001373.OF, 0...
    2021-06-30    [000529.OF, 001076.OF, 001349.OF, 001631.OF, 0...
    2021-09-30     [159905.ZOF, 159928.ZOF, 512690.HOF, 515650.HOF]
    dtype: object
    
    • 收益率137.25%
    • 年化收益率21.41%
    • 基准收益率40.92%
    • 阿尔法0.13
    • 贝塔0.98
    • 夏普比率0.84
    • 胜率0.61
    • 盈亏比2.45
    • 收益波动率22.81%
    • 信息比率0.06
    • 最大回撤29.07%
    bigcharts-data-start/{"__type":"tabs","__id":"bigchart-aea61ee2f5ca4087a5f65e5de916f90c"}/bigcharts-data-end

    从交易详情里可看到选出的基金示例有:

    2020-12-18 159928.ZOF 消费ETF

    2020-12-18 160222.ZOF 食品LOF

    2020-12-18 162605.ZOF

    2020-12-18 005450.OF 华夏稳胜混合

    2020-12-18 519915.OF 富国消费主题混合

    2020-12-18 001076.OF

    2020-12-18 001373.OF

    2020-12-18 110011.OF 易方达优质精选混合

    2020-12-18 159905.ZOF

    2020-12-18 160910.ZOF

    2020-12-18 161725.ZOF

    2020-12-18 000974.OF 安信消费医药股票

    2020-12-18 001044.OF

    2020-12-18 001632.OF 天弘中证食品饮料

    2020-12-18 002621.OF

    2020-12-18 003634.OF 嘉实农业产业股票