【宽客学院】成交单分析

slippage
指定价格
标签: #<Tag:0x00007fc05f7ae168> #<Tag:0x00007fc05f7ae028>

(iQuant) #1

如果我们手上有一份成交单是否可以对其进行模拟回测并分析呢?本文就为大家介绍一下成交单分析功能。

克隆策略

成交单分析功能

版本v1.0

以"交割单模板.csv"为例,展示了成交单分析功能, 具体的csv文件格式可以参考帖子'交割单模板.csv'

1、成交单csv数据读取

输入:交割单模板.csv 指定:

  • 成交日期列的列名,本例的csv文件中日期列表头为"成交日期"
  • 证券代码列名,本例的csv文件中证券代码列表头为"证券代码"
  • 买卖标志列名,本例的csv文件中买卖标志列表头为"买卖标志"
  • 成交价格列名,本例的csv文件中成交价格列表头为"成交价格"
  • 成交数量列名,本例的csv文件中成交价格列表头为"成交数量"

此模块会将每日的交易记录读取至DataFrame,并将信号时间前移1天来实现模拟当日成交,输出股票代码列表、起止时间和前移1日后的交易信号数据(包括日期、代码、买卖方向、成交价格和数量)。

2、回测

  • 在初始化函数中,通过设置固定滑点模型实现按照指定价格交易,设置手续费
  • 在主函数中,首先获取当日的交易信号数据trade_data,然后针对每行交易信号数据执行买卖动作:

    context.order(context.symbol(trade_data['instrument'].iloc[i]), volume, limit_price=trade_data['trade_price'].iloc[i])

    即实现了对指定的一行交易记录的股票trade_data['instrument'].iloc[i],使用指定的价格trade_data['trade_price'].iloc[i],执行买入/卖出volume股的操作

  • 同时设置成交率限制为0,以保证所有下单能够成交。

3、下单检查

  • 我们通过回测结果中的交易详情可以看到每笔单子的下单时间、股票代码、买卖方向、成交数量、成交价和佣金。可以检查这些记录与成交单记录的csv文件的一致性。

4、 结果分析

  • 通过回测结果可以看到绩效曲线、成交记录和策略的阿尔法、夏普比率等统计指标;
  • 从画布左侧模块列表的“共享模块”列表中拖入 “最近N日绩效评估”、“Brinson绩效评估” 和 “每日持仓分析” 三个模块。运行后可以查看相关的阶段绩效、Brinson收益归因和每日持仓股票/行业等信息。
  • 通过调用m2.pyfolio_full_tear_sheet()查看收益分析,通过m2.risk_analyze()查看风险分析

    {"Description":"实验创建于2018/10/24","Summary":"","Graph":{"EdgesInternal":[{"DestinationInputPortId":"-6539:input_1","SourceOutputPortId":"-1259:raw_perf"},{"DestinationInputPortId":"-6542:input_1","SourceOutputPortId":"-1259:raw_perf"},{"DestinationInputPortId":"-6544:input_1","SourceOutputPortId":"-1259:raw_perf"},{"DestinationInputPortId":"-1259:instruments","SourceOutputPortId":"-41:data_1"},{"DestinationInputPortId":"-1259:options_data","SourceOutputPortId":"-41:data_2"}],"ModuleNodes":[{"Id":"-1259","ModuleId":"BigQuantSpace.trade.trade-v4","ModuleParameters":[{"Name":"start_date","Value":"","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"end_date","Value":"","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"handle_data","Value":"# 回测引擎:每日数据处理函数,每天执行一次\ndef bigquant_run(context, data):\n # 按日期过滤得到今日收盘后的下单股票\n trade_data = context.trade_data[context.trade_data.date == data.current_dt.strftime('%Y-%m-%d')]\n\n for i in range(0, len(trade_data)):\n volume = trade_data['trade_volume'].iloc[i]\n if trade_data['side'].iloc[i] in ['BUY', '配售中签', '新股入帐']:\n pass\n elif trade_data['side'].iloc[i] == 'SELL':\n volume = -volume\n else:\n print('警告:未知的买卖标志 %s' % (trade_data['side'].iloc[i]))\n try:\n context.order(context.symbol(trade_data['instrument'].iloc[i]), volume, limit_price=trade_data['trade_price'].iloc[i])\n except Exception as e:\n #print('警告:%s' % (e))\n pass","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"prepare","Value":"# 回测引擎:准备数据,只执行一次\ndef bigquant_run(context):\n pass\n","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"initialize","Value":"# 回测引擎:初始化函数,只执行一次\ndef bigquant_run(context):\n context.trade_data = context.options['data'].read_df()\n context.set_long_only()\n # 设置按制定价格交易\n from zipline.finance.slippage import SlippageModel\n class FixedPriceSlippage(SlippageModel):\n def process_order(self, data, order, bar_volume=0, trigger_check_price=0):\n if order.limit is None:\n price_field = self._price_field_buy if order.amount > 0 else self._price_field_sell\n price = data.current(order.asset, price_field)\n else:\n price = order.limit\n return (price, order.amount)\n fix_slippage = FixedPriceSlippage()\n fix_slippage._price_field_buy = 'low'\n fix_slippage._price_field_sell = 'high'\n fix_slippage = FixedPriceSlippage(price_field_buy='low', price_field_sell='high')\n context.set_slippage(us_equities=fix_slippage)\n context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"before_trading_start","Value":"# 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。\ndef bigquant_run(context, data):\n pass\n","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"volume_limit","Value":"0","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":"-1259"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"options_data","NodeId":"-1259"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"history_ds","NodeId":"-1259"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"benchmark_ds","NodeId":"-1259"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"trading_calendar","NodeId":"-1259"}],"OutputPortsInternal":[{"Name":"raw_perf","NodeId":"-1259","OutputType":null}],"UsePreviousResults":false,"moduleIdForCode":2,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true},{"Id":"-6539","ModuleId":"BigQuantSpace.N_days_performance_statistics.N_days_performance_statistics-v5","ModuleParameters":[{"Name":"N","Value":"5","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_1","NodeId":"-6539"}],"OutputPortsInternal":[],"UsePreviousResults":true,"moduleIdForCode":5,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true},{"Id":"-6542","ModuleId":"BigQuantSpace.daily_position_analysis.daily_position_analysis-v6","ModuleParameters":[],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_1","NodeId":"-6542"}],"OutputPortsInternal":[],"UsePreviousResults":true,"moduleIdForCode":6,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true},{"Id":"-6544","ModuleId":"BigQuantSpace.Brinson.Brinson-v3","ModuleParameters":[{"Name":"benchmark","Value":"000300.HIX","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_1","NodeId":"-6544"}],"OutputPortsInternal":[],"UsePreviousResults":true,"moduleIdForCode":7,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true},{"Id":"-41","ModuleId":"BigQuantSpace.order_record_input.order_record_input-v5","ModuleParameters":[{"Name":"trading_records_filename","Value":"模板策略/股票/常用工具/.交割单模板.csv","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"date_col","Value":"成交日期","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"instrument_col","Value":"证券代码","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"side_col","Value":"买卖标志","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"trade_price_col","Value":"成交价格","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"trade_volume_col","Value":"成交数量","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[],"OutputPortsInternal":[{"Name":"data_1","NodeId":"-41","OutputType":null},{"Name":"data_2","NodeId":"-41","OutputType":null}],"UsePreviousResults":false,"moduleIdForCode":3,"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='-1259' Position='224,293,200,200'/><NodePosition Node='-6539' Position='-149,515,200,200'/><NodePosition Node='-6542' Position='598,532,200,200'/><NodePosition Node='-6544' Position='208,518,200,200'/><NodePosition Node='-41' Position='285,97,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 [1]:
    # 本代码由可视化策略环境自动生成 2019年2月19日 10:21
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # 回测引擎:每日数据处理函数,每天执行一次
    def m2_handle_data_bigquant_run(context, data):
        # 按日期过滤得到今日收盘后的下单股票
        trade_data = context.trade_data[context.trade_data.date == data.current_dt.strftime('%Y-%m-%d')]
    
        for i in range(0, len(trade_data)):
            volume = trade_data['trade_volume'].iloc[i]
            if trade_data['side'].iloc[i] in ['BUY', '配售中签', '新股入帐']:
                pass
            elif trade_data['side'].iloc[i] == 'SELL':
                volume = -volume
            else:
                print('警告:未知的买卖标志 %s' % (trade_data['side'].iloc[i]))
            try:
                context.order(context.symbol(trade_data['instrument'].iloc[i]), volume, limit_price=trade_data['trade_price'].iloc[i])
            except Exception as e:
                #print('警告:%s' % (e))
                pass
    # 回测引擎:准备数据,只执行一次
    def m2_prepare_bigquant_run(context):
        pass
    
    # 回测引擎:初始化函数,只执行一次
    def m2_initialize_bigquant_run(context):
        context.trade_data = context.options['data'].read_df()
        context.set_long_only()
        # 设置按制定价格交易
        from zipline.finance.slippage import SlippageModel
        class FixedPriceSlippage(SlippageModel):
            def process_order(self, data, order, bar_volume=0, trigger_check_price=0):
                if order.limit is None:
                    price_field = self._price_field_buy if order.amount > 0 else self._price_field_sell
                    price = data.current(order.asset, price_field)
                else:
                    price = order.limit
                return (price, order.amount)
        fix_slippage = FixedPriceSlippage()
        fix_slippage._price_field_buy = 'low'
        fix_slippage._price_field_sell = 'high'
        fix_slippage = FixedPriceSlippage(price_field_buy='low', price_field_sell='high')
        context.set_slippage(us_equities=fix_slippage)
        context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    # 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。
    def m2_before_trading_start_bigquant_run(context, data):
        pass
    
    
    m3 = M.order_record_input.v5(
        trading_records_filename='模板策略/股票/常用工具/.交割单模板.csv',
        date_col='成交日期',
        instrument_col='证券代码',
        side_col='买卖标志',
        trade_price_col='成交价格',
        trade_volume_col='成交数量',
        m_cached=False
    )
    
    m2 = M.trade.v4(
        instruments=m3.data_1,
        options_data=m3.data_2,
        start_date='',
        end_date='',
        handle_data=m2_handle_data_bigquant_run,
        prepare=m2_prepare_bigquant_run,
        initialize=m2_initialize_bigquant_run,
        before_trading_start=m2_before_trading_start_bigquant_run,
        volume_limit=0,
        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=''
    )
    
    m5 = M.N_days_performance_statistics.v5(
        input_1=m2.raw_perf,
        N=5
    )
    
    m6 = M.daily_position_analysis.v6(
        input_1=m2.raw_perf
    )
    
    m7 = M.Brinson.v3(
        input_1=m2.raw_perf,
        benchmark='000300.HIX'
    )
    
    [2018-12-17 18:47:26.799168] INFO: bigquant: order_record_input.v5 开始运行..
    [2018-12-17 18:47:26.992989] INFO: bigquant: order_record_input.v5 运行完成[0.193857s].
    [2018-12-17 18:47:27.020289] INFO: bigquant: backtest.v8 开始运行..
    [2018-12-17 18:47:27.025611] INFO: bigquant: biglearning backtest:V8.1.6
    [2018-12-17 18:47:27.026488] INFO: bigquant: product_type:stock by specified
    [2018-12-17 18:47:32.039356] INFO: bigquant: 读取股票行情完成:31309
    [2018-12-17 18:47:32.304209] INFO: algo: TradingAlgorithm V1.3.7
    [2018-12-17 18:47:32.845423] INFO: algo: trading transform...
    [2018-12-17 18:47:33.521910] INFO: Performance: Simulated 45 trading days out of 45.
    [2018-12-17 18:47:33.523523] INFO: Performance: first open: 2018-07-02 09:30:00+00:00
    [2018-12-17 18:47:33.524368] INFO: Performance: last close: 2018-08-31 15:00:00+00:00
    
    • 收益率-9.57%
    • 年化收益率-43.07%
    • 基准收益率-5.03%
    • 阿尔法-0.26
    • 贝塔0.95
    • 夏普比率-1.64
    • 胜率0.64
    • 盈亏比1.83
    • 收益波动率32.86%
    • 信息比率-0.06
    • 最大回撤18.24%
    [2018-12-17 18:47:34.189451] INFO: bigquant: backtest.v8 运行完成[7.169147s].
    [2018-12-17 18:47:34.211287] INFO: bigquant: N_days_performance_statistics.v5 开始运行..
                         最近5日策略绩效统计
    alpha                 -2.011489
    annual_return_ratio   -0.965446
    beta                   2.657091
    max_drawdown          -0.085927
    return_ratio          -0.064590
    return_volatility      0.376869
    sharp_ratio           -8.811834
    sortino               -9.201681
    
    [2018-12-17 18:47:34.312561] INFO: bigquant: N_days_performance_statistics.v5 运行完成[0.101468s].
    [2018-12-17 18:47:34.319578] INFO: bigquant: daily_position_analysis.v6 开始运行..
    
    [2018-12-17 18:48:29.749109] INFO: bigquant: daily_position_analysis.v6 运行完成[55.429519s].
    [2018-12-17 18:48:29.766593] INFO: bigquant: Brinson.v3 开始运行..
    
    [2018-12-17 18:48:48.474730] INFO: bigquant: Brinson.v3 运行完成[18.708123s].
    

    Pyfolio收益分析

    In [2]:
    m2.pyfolio_full_tear_sheet()
    
    WARNING (theano.tensor.blas): Using NumPy C-API based implementation for BLAS functions.
    
    Entire data start date: 2018-07-02
    Entire data end date: 2018-08-31
    
    
    Backtest Months: 2
    
    Performance statistics Backtest
    cum_returns_final -0.10
    annual_return -0.43
    annual_volatility 0.33
    sharpe_ratio -1.55
    calmar_ratio -2.36
    stability_of_timeseries 0.33
    max_drawdown -0.18
    omega_ratio 0.78
    sortino_ratio -1.96
    skew -0.28
    kurtosis -0.70
    tail_ratio 0.90
    common_sense_ratio 0.51
    information_ratio -0.10
    alpha -0.38
    beta 1.09
    Worst Drawdown Periods net drawdown in % peak date valley date recovery date duration
    0 18.24 2018-07-26 2018-08-31 NaT NaN
    1 4.54 2018-07-18 2018-07-19 2018-07-25 6
    2 2.08 2018-07-03 2018-07-06 2018-07-10 6
    3 0.52 2018-07-10 2018-07-11 2018-07-12 3
    4 0.20 2018-07-13 2018-07-16 2018-07-17 3
    [-0.043 -0.122]
    
    Stress Events mean min max
    New Normal -0.20% -4.54% 3.47%
    Top 10 long positions of all time max
    Equity(28 [600029.SHA]) 14.39%
    Equity(53 [002260.SZA]) 13.37%
    Equity(104 [000506.SZA]) 13.15%
    Equity(18 [000820.SZA]) 12.85%
    Equity(23 [300216.SZA]) 11.82%
    Equity(77 [000735.SZA]) 11.60%
    Equity(39 [002124.SZA]) 11.27%
    Equity(85 [002002.SZA]) 10.67%
    Equity(76 [002122.SZA]) 10.26%
    Equity(97 [002247.SZA]) 8.60%
    Top 10 short positions of all time max
    Top 10 positions of all time max
    Equity(28 [600029.SHA]) 14.39%
    Equity(53 [002260.SZA]) 13.37%
    Equity(104 [000506.SZA]) 13.15%
    Equity(18 [000820.SZA]) 12.85%
    Equity(23 [300216.SZA]) 11.82%
    Equity(77 [000735.SZA]) 11.60%
    Equity(39 [002124.SZA]) 11.27%
    Equity(85 [002002.SZA]) 10.67%
    Equity(76 [002122.SZA]) 10.26%
    Equity(97 [002247.SZA]) 8.60%
    All positions ever held max
    Equity(28 [600029.SHA]) 14.39%
    Equity(53 [002260.SZA]) 13.37%
    Equity(104 [000506.SZA]) 13.15%
    Equity(18 [000820.SZA]) 12.85%
    Equity(23 [300216.SZA]) 11.82%
    Equity(77 [000735.SZA]) 11.60%
    Equity(39 [002124.SZA]) 11.27%
    Equity(85 [002002.SZA]) 10.67%
    Equity(76 [002122.SZA]) 10.26%
    Equity(97 [002247.SZA]) 8.60%
    Equity(98 [002530.SZA]) 8.45%
    Equity(3 [603010.SHA]) 7.76%
    Equity(33 [002600.SZA]) 7.40%
    Equity(19 [002711.SZA]) 7.36%
    Equity(73 [300138.SZA]) 7.07%
    Equity(70 [002584.SZA]) 6.94%
    Equity(111 [600870.SHA]) 6.75%
    Equity(4 [600532.SHA]) 5.71%
    Equity(35 [601111.SHA]) 5.31%
    Equity(112 [002604.SZA]) 5.02%
    Equity(17 [603555.SHA]) 4.84%
    Equity(52 [600734.SHA]) 4.78%
    Equity(11 [002611.SZA]) 4.72%
    Equity(89 [002178.SZA]) 4.56%
    Equity(26 [603108.SHA]) 4.39%
    Equity(78 [600234.SHA]) 4.35%
    Equity(101 [002164.SZA]) 4.00%
    Equity(50 [300162.SZA]) 4.00%
    Equity(5 [002872.SZA]) 3.92%
    Equity(54 [600695.SHA]) 3.79%
    Equity(114 [300008.SZA]) 3.66%
    Equity(99 [002418.SZA]) 3.52%
    Equity(93 [002515.SZA]) 3.44%
    Equity(84 [603021.SHA]) 3.36%
    Equity(48 [002451.SZA]) 3.21%
    Equity(72 [300266.SZA]) 3.15%
    Equity(59 [603456.SHA]) 3.11%
    Equity(37 [002035.SZA]) 3.08%
    Equity(106 [002840.SZA]) 3.05%
    Equity(83 [300089.SZA]) 3.04%
    Equity(92 [000035.SZA]) 3.03%
    Equity(9 [300004.SZA]) 2.93%
    Equity(14 [002879.SZA]) 2.78%
    Equity(32 [300317.SZA]) 2.67%
    Equity(60 [600556.SHA]) 2.63%
    Equity(15 [300279.SZA]) 2.61%
    Equity(57 [002219.SZA]) 1.66%
    Equity(75 [603699.SHA]) 0.84%
    Equity(16 [600393.SHA]) 0.68%
    Equity(87 [600634.SHA]) 0.59%
    Equity(40 [603085.SHA]) 0.51%

    risk_analyze风险分析

    In [3]:
    m2.risk_analyze()