复制链接
克隆策略

成交单分析功能

版本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","graph":{"edges":[{"to_node_id":"-6539:backtest_ds","from_node_id":"-1259:raw_perf"},{"to_node_id":"-1259:instruments","from_node_id":"-129:instruments_ds"},{"to_node_id":"-1259:options_data","from_node_id":"-129:trading_records_ds"}],"nodes":[{"node_id":"-1259","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 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))","type":"Literal","bound_global_parameter":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","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":"# 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。\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":"close","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":"-1259"},{"name":"options_data","node_id":"-1259"},{"name":"history_ds","node_id":"-1259"},{"name":"benchmark_ds","node_id":"-1259"},{"name":"trading_calendar","node_id":"-1259"}],"output_ports":[{"name":"raw_perf","node_id":"-1259"}],"cacheable":false,"seq_num":2,"comment":"","comment_collapsed":true},{"node_id":"-6539","module_id":"BigQuantSpace.N_days_performance_statistics.N_days_performance_statistics-v5","parameters":[{"name":"N","value":"5","type":"Literal","bound_global_parameter":null}],"input_ports":[{"name":"backtest_ds","node_id":"-6539"}],"output_ports":[{"name":"evaluation_of_perf_indicator","node_id":"-6539"},{"name":"analysis_of_stage_return_rat","node_id":"-6539"}],"cacheable":true,"seq_num":5,"comment":"","comment_collapsed":true},{"node_id":"-129","module_id":"BigQuantSpace.order_record_input.order_record_input-v5","parameters":[{"name":"trading_records_filename","value":"模板策略/股票/常用工具/交割单模板.csv","type":"Literal","bound_global_parameter":null},{"name":"date_col","value":"成交日期","type":"Literal","bound_global_parameter":null},{"name":"instrument_col","value":"证券代码","type":"Literal","bound_global_parameter":null},{"name":"side_col","value":"买卖标志","type":"Literal","bound_global_parameter":null},{"name":"trade_price_col","value":"成交价格","type":"Literal","bound_global_parameter":null},{"name":"trade_volume_col","value":"成交数量","type":"Literal","bound_global_parameter":null}],"input_ports":[],"output_ports":[{"name":"instruments_ds","node_id":"-129"},{"name":"trading_records_ds","node_id":"-129"}],"cacheable":false,"seq_num":4,"comment":"","comment_collapsed":true}],"node_layout":"<node_postions><node_position Node='-1259' Position='224,293,200,200'/><node_position Node='-6539' Position='-195,500,200,200'/><node_position Node='-129' Position='239,105,200,200'/></node_postions>"},"nodes_readonly":false,"studio_version":"v2"}
    In [2]:
    # 本代码由可视化策略环境自动生成 2021年12月31日 15:44
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    # 回测引擎:初始化函数,只执行一次
    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_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_before_trading_start_bigquant_run(context, data):
        pass
    
    
    m4 = 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=m4.instruments_ds,
        options_data=m4.trading_records_ds,
        start_date='',
        end_date='',
        initialize=m2_initialize_bigquant_run,
        handle_data=m2_handle_data_bigquant_run,
        prepare=m2_prepare_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='000300.HIX'
    )
    
    m5 = M.N_days_performance_statistics.v5(
        backtest_ds=m2.raw_perf,
        N=5
    )
    
    • 收益率-8.91%
    • 年化收益率-40.71%
    • 基准收益率-5.03%
    • 阿尔法-0.19
    • 贝塔0.96
    • 夏普比率-1.5
    • 胜率0.65
    • 盈亏比1.83
    • 收益波动率33.17%
    • 信息比率-0.05
    • 最大回撤17.78%
    bigcharts-data-start/{"__type":"tabs","__id":"bigchart-f2c2b592a90e4f579add214dda625100"}/bigcharts-data-end
                         最近5日策略绩效统计
    return_ratio          -0.064695
    annual_return_ratio   -0.965641
    beta                   2.327051
    alpha                 -0.918770
    sharp_ratio           -8.866525
    sortino               -9.296394
    return_volatility      0.375235
    max_drawdown          -0.086538
    

    Pyfolio收益分析

    In [14]:
    m2.pyfolio_full_tear_sheet()
    
    Entire data start date: 2018-07-02
    Entire data end date: 2018-08-31
    
    
    Backtest Months: 2
    
    Performance statistics Backtest
    cum_returns_final -0.08
    annual_return -0.39
    annual_volatility 0.28
    sharpe_ratio -1.65
    calmar_ratio -2.57
    stability_of_timeseries 0.40
    max_drawdown -0.15
    omega_ratio 0.76
    sortino_ratio -2.15
    skew -0.09
    kurtosis -0.37
    tail_ratio 0.85
    common_sense_ratio 0.52
    information_ratio -0.11
    alpha -0.35
    beta 0.95
    Worst Drawdown Periods net drawdown in % peak date valley date recovery date duration
    0 15.22 2018-07-26 2018-08-31 NaT NaN
    1 4.61 2018-07-13 2018-07-19 2018-07-24 8
    2 0.93 2018-07-03 2018-07-05 2018-07-09 5
    3 0.00 2018-07-02 2018-07-02 2018-07-02 1
    4 0.00 2018-07-02 2018-07-02 2018-07-02 1
    
    [-0.037 -0.096]
    
    Top 10 long positions of all time max
    Equity(46 [000820.SZA]) 12.84%
    Equity(6 [002122.SZA]) 12.21%
    Equity(49 [002124.SZA]) 8.81%
    Equity(22 [002247.SZA]) 8.67%
    Equity(113 [002600.SZA]) 7.64%
    Equity(74 [600734.SHA]) 6.07%
    Equity(99 [600532.SHA]) 5.94%
    Equity(76 [002604.SZA]) 5.79%
    Equity(100 [603555.SHA]) 4.93%
    Equity(78 [601111.SHA]) 4.83%

    risk_analyze风险分析

    In [15]:
    m2.risk_analyze()