# 简介
RQPAttr 是由 Ricequant 开发的绩效分析引擎,支持权益类 Brinson 行业归因、因子归因。使用 RQPAttr,您可以对您的策略进行进一步的分析,通过分析策略的收益来源,对策略进行评估与修正。
# API
performance_attribute( model, daily_weights, daily_return=None, benchmark_info=None)
# 参数
参数 | 类型 | 说明 |
---|---|---|
model | str OR str list | 模型类型,可选以下几种的混合: "equity/brinson"、"equity/factor" 、"equity/factor_v2" |
daily_weights | pd.Series | 每日每个合约的权重, index 为 ['date', 'order_book', 'asset_type'] , 值为 weight, 其中 asset_type 字段支持 'stock' 和 'cash' |
daily_return | pd.Series | 每日的总收益率, 其中 index 为 'date', 值为收益率,收益率的开始时间应是权重的开始时间的下一个交易日, 结束时间应是权重结束时间的下一个交易日 |
benchmark_info | dict | 基准,无基准信息输入则以上证 300 作为基准。基准支持 4 种类型, 如下所示: 1. {'type': 'index', 'name': '上证 300', 'detail': '000300.XSHG'} 2. {'type': 'mixed_index', 'name': '20% 上证 300 + 80% 上证综指', 'detail': {'000300.XSHG': 0.2, '000008.XSHG': 0.8}} 3. {'type': 'yield_rate', 'name': '1 年期国债', 'detail': 'YIELD1Y'} 4. {'type': 'cash', 'name': "零收益现金", 'detail': 0.0} 其中 name 为可选字段 |
注:daily_weights 的 date 每一项都向后取一个交易日即为 daily_return 的日期
# 其他可选参数
参数 | 类型 | 说明 |
---|---|---|
leverage_ratio | pd.Series OR float | 杠杆率, 组合收益率当日的杠杆率 |
standard | str | brinson 行业归因标准,可选:'sws', 'citics' |
special_assets_return | pd.Series | index 为 date, order_book_id,value return, 其中收益率为真实组合收益率 |
# 返回
dict
returns_decomposition
:list
,混合资产 Brinson 归因attribution:dict
,根据选择的模型,返回以下相应结果"equity/brinson"
:brinson 归因"equity/factor"
:factor_attribution
:各因子对投资组合与基准组合收益贡献factor_exposure
:投资组合与基准组合对因子的风险暴露sensitivity
:敏感度分析
"equity/factor_v2"
:factor_attribution
:各因子对投资组合与基准组合收益贡献factor_exposure
:投资组合与基准组合对因子的风险暴露sensitivity
:敏感度分析
excel_report
:dict
,收益趋势
# 归因模型说明
# 1. 混合资产 Brinson 归因
对于包含多种标的的混合型投资组合,米筐采用无交互作用项,BHB 形式的 Brinson 模型对投资组合的收益进行归因,以反映投资策略在各类资产中的配置能力和标的选择能力。部分归因项说明、归因结构图示例见下表及下图,其中:
- 第一步归因中,投资组合的收益可分为交易收益和持仓收益两部分。交易收益产生于交易执行时选择合适价格点位,进行低买高卖所产生的收益,通常来源于交易人员或者交易算法的交易能力,而与策略本身的投资能力无关。因此通过分解交易收益和持仓收益,能够分别评估账户的交易执行能力和策略投资能力。
- 第二步归因中,投资组合持仓收益可分解为“主动收益”和“基准持仓收益”;
- 第三步归因中,计算各类资产对主动收益的贡献;
- 第四步归因中,对各类资产的主动收益进行 Brinson 归因,计算其配置收益和选择收益。其中配置能力反映投资策略对于资产收益整体变动的判断能力,而选择收益反映投资策略对于各资产类别中优质资产的挑选能力;
- 对于仅有投资组合或基准组合配置的资产,其主动收益均计入配置收益,表示投资组合配置基准以外的资产,或不配置基准中的特定资产所带来的超额收益均为配置收益;
- 归因中使用联接算法,保证归因项之和等于累积持仓主动收益
归因项 | 说明 |
---|---|
交易收益 | 指交易成交价和日终价格之间的价差,以及交易费用所产生的收益 |
持仓收益 | 指日终价格变化所带来的收益 |
主动收益 | 指投资组合相对于基准的收益差 |
配置收益 | 指投资组合相对于基准超配/低配某一类资产所产生的超额收益 |
选择收益 | 指在各类资产中,投资组合相对于基准选择不同标的所产生的超额收益 |
# 2. 股票行业 Brinson 归因
股票行业归因采用 BF 版本,无交互作用项的 Brinson 模型,对股票的行业收益进行“配置-选股”分解。其中:
- 配置收益表示投资策略基于行业走势调整行业权重所带来主动收益;
- 选股收益表示投资策略在行业内挑选优质个股所带的主动收益;
- 现金作为一个特殊版块,与行业并列,现金板块的主动收益反映策略的大盘择时收益;
- 对于仅有投资组合或基准组合配置的行业,其主动收益均计入配置收益,表示投资组合配置基准以外的行业,或不配置基准中的特定行业所带来的超额收益均为配置收益
BF 的归因项形式如下:
其中为资产总数;和分别为投资组合和基准组合中资产 i 的权重;和分别为投资组合和基准组合中资产 i 的收益。考虑基准收益为各类资产的加权平均:
因为 ,有:
# 3. 因子归因
多因子风险模型认为股票组合收益有四个收益来源:风格偏好、行业偏好、市场联动、以及特异收益,其中:
- 风格偏好:风格为影响股票收益的共同风格因素(包括市值、盈利性、成长性、动量等)。例如,若投资组合偏好于持有小市值股票,同时小市值股票表现显著优于大市值股票,则小市值的风格偏好能为投资组合带来超额收益;
- 行业偏好:例如,若投资组合偏好于持有“医药生物”行业,同时该行业总体优于其它行业的表现,此时“医药生物”的行业偏好能为投资组合带来超额收益;
- 市场联动:市场联动表示股票市场整体涨落对投资组合的收益影响。对于任意两个满仓组合,其获得的市场收益及承担的市场整体波动风险相同;而对于“股票+现金”的组合,随着组合中的股票仓位比例的增加,市场联动对组合收益影响越大;
- 特异收益:特异收益表示影响个股收益的特殊因素(例如上市公司管理层变动,政府政策优惠等)
因子归因模型从基于上述四类因素对股票组合业绩进行分解,评估投资策略的主要收益及风险来源。
在股票因子归因部分,对股票主动收益按如下形式进行因子分解:
其中 为投资组合主动收益, 为因子(风格+行业+市场联动)数目; 为投资组合对因子 的主动暴露度; 为因子 的因子收益; 为投资组合主动残余收益。
基于 MSCI Barra 提出的 X-Sigma-Rho 归因模型,因子主动风险归因表达式为:
由上述表达式可知,因子对投资组合主动风险贡献取决于以下因素:
- 投资组合的因子主动暴露度
- 因子收益波动率
- 因子收益和主动收益的相关性
上述三项中,因子主动暴露度是投资组合主动押注的部分,反映的是投资组合的因子风险暴露偏离基准的程度,即其风格/行业偏好;因子收益波动率反映市场风格转换/行业轮动变化所带来的被动风险;相关性部分反映的则是投资组合收益和因子表现的风险联动程度。
# 使用范例
该范例所使用的组合数据来自于通过 rqalpha-plus 回测框架回测后所保存的回测结果。
import os
import pandas as pd
import numpy as np
import rqdatac
import pickle
from rqalpha.mod.rqalpha_mod_sys_analyser.plot import *
from rqdatac import *
import rqpattr
from rqpattr.api import performance_attribute
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
rqdatac.init()
#读取回测后保存的回测结果
path = os.getcwd()+'\\bts\\bts'
files = '000906.XSHG_factor1_liquidity_v2_asfc.pkl'
result = pd.read_pickle(os.path.join(path, files))
print('result')
#out:
# {'sys_analyser': {'summary': {'strategy_name': 'strategy',
# 'start_date': '2020-06-10',
# 'end_date': '2022-06-30',
# 'strategy_file': 'strategy.py',
# 'run_type': 'BACKTEST',
# 'starting_cash': 'STOCK:1000000000.0',
# 'STOCK': 1000000000.0,
# 'benchmark': '000906.XSHG',
# 'benchmark_symbol': '中证800',
# 'alpha': 0.04161847252000668,
# 'beta': 0.9689441949400958,
# 'sharpe': 0.42678333868827145,
# 'excess_sharpe': 0.3582160902725003,
# 'information_ratio': 0.3453132024893898,
# 'downside_risk': 0.1655438441069776,
# 'tracking_error': 0.12271422135189096,
# 'sortino': 0.5852185528297059,
# 'volatility': 0.22699885420999039,
# 'excess_volatility': 0.007730269334000854,
# 'excess_annual_volatility': 0.12271422135189096,
# 'max_drawdown': 0.3449066882507446,
# 'excess_max_drawdown': 0.16459229054214705,
# 'excess_returns': 0.07486020583373909,
# 'excess_annual_returns': 0.03712968826159724,
# 'var': 0.023450724628239612,
# 'total_value': 1203328714.4768782,
# 'cash': 8210.926878128295,
# 'total_returns': 0.20332871447687806,
# 'annualized_returns': 0.09798110622545697,
# 'unit_net_value': 1.203328714476878,
# 'units': 1000000000.0,
# 'benchmark_total_returns': 0.11694720785066992,
# 'benchmark_annualized_returns': 0.05744300314969153,
# 'excess_cum_returns': 0.08638150662620814,
# 'max_drawdown_duration': IndexRange(start=305, end=498, start_date=datetime.date(2021, 9, 7), end_date=datetime.date(2022, 6, 30)),
# 'max_drawdown_duration_start_date': '2021-09-07',
# 'max_drawdown_duration_end_date': '2022-06-30',
# 'max_drawdown_duration_days': 296,
# 'turnover': 19.746910020902284,
# 'avg_daily_turnover': 0.03878575014506964,
# 'excess_max_drawdown_duration': IndexRange(start=69, end=277, start_date=datetime.date(2020, 9, 17), end_date=datetime.date(2021, 7, 29)),
# 'excess_max_drawdown_duration_start_date': '2020-09-17',
# 'excess_max_drawdown_duration_end_date': '2021-07-29',
# 'excess_max_drawdown_duration_days': 315,
# 'weekly_alpha': 0.0407174667550259,
# 'weekly_beta': 0.9442199165766891,
# 'weekly_sharpe': 0.4328984550718106,
# 'weekly_sortino': 0.6233499038107769,
# 'weekly_information_ratio': 0.38383642721331795,
# 'weekly_tracking_error': 0.015232196824837712,
# 'weekly_max_drawdown': 0.2874243182734171},
# 'trades': datetime trading_datetime order_book_id \
# datetime
# 2020-06-10 15:00:00 2020-06-10 15:00:00 2020-06-10 15:00:00 000671.XSHE
# 2020-06-10 15:00:00 2020-06-10 15:00:00 2020-06-10 15:00:00 603806.XSHG
# 2020-06-10 15:00:00 2020-06-10 15:00:00 2020-06-10 15:00:00 002773.XSHE
# 2020-06-10 15:00:00 2020-06-10 15:00:00 2020-06-10 15:00:00 000988.XSHE
# 2020-06-10 15:00:00 2020-06-10 15:00:00 2020-06-10 15:00:00 601811.XSHG
# ... ... ... ...
# 2022-06-13 15:00:00 2022-06-13 15:00:00 2022-06-13 15:00:00 511220.XSHG
# 2022-06-16 15:00:00 2022-06-16 15:00:00 2022-06-16 15:00:00 511220.XSHG
# 2022-06-23 15:00:00 2022-06-23 15:00:00 2022-06-23 15:00:00 511220.XSHG
# 2022-06-29 15:00:00 2022-06-29 15:00:00 2022-06-29 15:00:00 511220.XSHG
# 2022-06-30 15:00:00 2022-06-30 15:00:00 2022-06-30 15:00:00 511220.XSHG
# symbol side position_effect exec_id tax \
# datetime
# 2020-06-10 15:00:00 阳光城 BUY OPEN 16710930284727 0.0
# 2020-06-10 15:00:00 福斯特 BUY OPEN 16710930284728 0.0
# 2020-06-10 15:00:00 康弘药业 BUY OPEN 16710930284729 0.0
# 2020-06-10 15:00:00 华工科技 BUY OPEN 16710930284730 0.0
# 2020-06-10 15:00:00 新华文轩 BUY OPEN 16710930284731 0.0
# ... ... ... ... ... ...
# 2022-06-13 15:00:00 海富通城投ETF BUY OPEN 16710930286411 0.0
# 2022-06-16 15:00:00 海富通城投ETF BUY OPEN 16710930286412 0.0
# 2022-06-23 15:00:00 海富通城投ETF BUY OPEN 16710930286413 0.0
# 2022-06-29 15:00:00 海富通城投ETF BUY OPEN 16710930286414 0.0
# 2022-06-30 15:00:00 海富通城投ETF BUY OPEN 16710930286415 0.0
# commission last_quantity last_price order_id \
# datetime
# 2020-06-10 15:00:00 54331.39440 26711600 6.780 16710930364951
# 2020-06-10 15:00:00 38711.06820 3062100 42.140 16710930364963
# 2020-06-10 15:00:00 28030.80870 2358900 39.610 16710930364972
# 2020-06-10 15:00:00 24600.08340 3723900 22.020 16710930364985
# 2020-06-10 15:00:00 22208.87760 7243600 10.220 16710930365008
# ... ... ... ... ...
# 2022-06-13 15:00:00 11.89320 400 99.110 16710930376692
# 2022-06-16 15:00:00 178.43040 6000 99.128 16710930376694
# 2022-06-23 15:00:00 5.00000 100 99.259 16710930376696
# 2022-06-29 15:00:00 1844.92842 62600 98.239 16710930376698
# 2022-06-30 15:00:00 8.84205 300 98.245 16710930376700
# transaction_cost
# datetime
# 2020-06-10 15:00:00 54331.39440
# 2020-06-10 15:00:00 38711.06820
# 2020-06-10 15:00:00 28030.80870
# 2020-06-10 15:00:00 24600.08340
# 2020-06-10 15:00:00 22208.87760
# ... ...
# 2022-06-13 15:00:00 11.89320
# 2022-06-16 15:00:00 178.43040
# 2022-06-23 15:00:00 5.00000
# 2022-06-29 15:00:00 1844.92842
# 2022-06-30 15:00:00 8.84205
# [1689 rows x 13 columns],
# 'portfolio': cash total_value market_value unit_net_value \
# date
# 2020-06-10 153.0189 9.997001e+08 9.996999e+08 0.999700
# 2020-06-11 153.0189 9.918064e+08 9.918062e+08 0.991806
# 2020-06-12 1984.0438 9.998441e+08 9.998421e+08 0.999844
# 2020-06-15 1984.0438 9.891545e+08 9.891525e+08 0.989154
# 2020-06-16 1984.0438 1.006052e+09 1.006050e+09 1.006052
# ... ... ... ... ...
# 2022-06-24 161.1973 1.187876e+09 1.187876e+09 1.187876
# 2022-06-27 161.1973 1.196714e+09 1.196679e+09 1.196714
# 2022-06-28 161.1973 1.205983e+09 1.205948e+09 1.205983
# 2022-06-29 2893.2689 1.168880e+09 1.168843e+09 1.168880
# 2022-06-30 8210.9269 1.203329e+09 1.203321e+09 1.203329
# units static_unit_net_value
# date
# 2020-06-10 1.000000e+09 1.0000
# 2020-06-11 1.000000e+09 0.9997
# 2020-06-12 1.000000e+09 0.9918
# 2020-06-15 1.000000e+09 0.9998
# 2020-06-16 1.000000e+09 0.9892
# ... ... ...
# 2022-06-24 1.000000e+09 1.1671
# 2022-06-27 1.000000e+09 1.1879
# 2022-06-28 1.000000e+09 1.1967
# 2022-06-29 1.000000e+09 1.2060
# 2022-06-30 1.000000e+09 1.1689
# [499 rows x 6 columns],
# 'benchmark_portfolio': unit_net_value
# date
# 2020-06-10 0.998862
# 2020-06-11 0.989584
# 2020-06-12 0.991023
# 2020-06-15 0.982023
# 2020-06-16 0.997713
# ... ...
# 2022-06-24 1.096298
# 2022-06-27 1.107309
# 2022-06-28 1.119947
# 2022-06-29 1.101584
# 2022-06-30 1.116947
# [499 rows x 1 columns],
# 'stock_account': cash transaction_cost market_value total_value
# date
# 2020-06-10 153.0189 299909.9811 9.996999e+08 9.997001e+08
# 2020-06-11 153.0189 0.0000 9.918062e+08 9.918064e+08
# 2020-06-12 1984.0438 1853.5725 9.998421e+08 9.998441e+08
# 2020-06-15 1984.0438 0.0000 9.891525e+08 9.891545e+08
# 2020-06-16 1984.0438 0.0000 1.006050e+09 1.006052e+09
# ... ... ... ... ...
# 2022-06-24 161.1973 0.0000 1.187876e+09 1.187876e+09
# 2022-06-27 161.1973 0.0000 1.196679e+09 1.196714e+09
# 2022-06-28 161.1973 0.0000 1.205948e+09 1.205983e+09
# 2022-06-29 2893.2689 1844.9284 1.168843e+09 1.168880e+09
# 2022-06-30 8210.9269 8.8421 1.203321e+09 1.203329e+09
# [499 rows x 4 columns],
# 'stock_positions': order_book_id symbol quantity last_price avg_price \
# date
# 2020-06-10 000671.XSHE 阳光城 26711600.0 6.78 6.780
# 2020-06-10 603806.XSHG 福斯特 3062100.0 42.14 42.140
# 2020-06-10 002773.XSHE 康弘药业 2358900.0 39.61 39.610
# 2020-06-10 000988.XSHE 华工科技 3723900.0 22.02 22.020
# 2020-06-10 601811.XSHG 新华文轩 7243600.0 10.22 10.220
# ... ... ... ... ... ...
# 2022-06-30 002460.XSHE 赣锋锂业 38100.0 148.70 141.450
# 2022-06-30 600316.XSHG 洪都航空 101100.0 30.24 31.603
# 2022-06-30 601117.XSHG 中国化学 300600.0 9.41 9.644
# 2022-06-30 600079.XSHG 人福医药 149200.0 16.00 18.070
# 2022-06-30 600588.XSHG 用友网络 42500.0 21.71 29.880
# market_value
# date
# 2020-06-10 181104648.0
# 2020-06-10 129036894.0
# 2020-06-10 93436029.0
# 2020-06-10 82000278.0
# 2020-06-10 74029592.0
# ... ...
# 2022-06-30 5665470.0
# 2022-06-30 3057264.0
# 2022-06-30 2828646.0
# 2022-06-30 2387200.0
# 2022-06-30 922675.0
# [19797 rows x 6 columns],
# 'yearly_risk_free_rates': {2020: 0.020009000000000002, 2021: 0, 2022: 0}}}
获取回测结果中持仓及权益数据
positions = result['sys_analyser']['stock_positions']
portfolio = result['sys_analyser']['portfolio']
positions = positions.join(portfolio['total_value'])
positions['asset_type'] = 'stock'
positions = positions.reset_index()
positions = positions.set_index(['date','order_book_id','asset_type'])
daily_return = portfolio['unit_net_value'].pct_change().dropna()
weights = positions['market_value']/positions['total_value']
print('daily_return')
# out:
# date
# 2020-06-11 -0.007896
# 2020-06-12 0.008104
# 2020-06-15 -0.010692
# 2020-06-16 0.017083
# 2020-06-17 0.005524
# ...
# 2022-06-24 0.017760
# 2022-06-27 0.007440
# 2022-06-28 0.007745
# 2022-06-29 -0.030766
# 2022-06-30 0.029472
# Name: unit_net_value, Length: 498, dtype: float64
print('weights')
# out:
# date order_book_id asset_type
# 2020-06-10 000671.XSHE stock 0.181159
# 603806.XSHG stock 0.129076
# 002773.XSHE stock 0.093464
# 000988.XSHE stock 0.082025
# 601811.XSHG stock 0.074052
# ...
# 2022-06-30 002460.XSHE stock 0.004708
# 600316.XSHG stock 0.002541
# 601117.XSHG stock 0.002351
# 600079.XSHG stock 0.001984
# 600588.XSHG stock 0.000767
# Length: 19797, dtype: float64
传入 rqpattr 计算引擎,对策略进行归因
result = rqpattr.performance_attribute(model="equity/factor_v2", daily_weights=weights, daily_return=daily_return,
benchmark_info={'type': 'index', 'name': '中证800', 'detail': '000906.XSHG'})
混合资产 Brinson 归因
result['returns_decomposition']
#out:
# [{'factor': '总收益',
# 'value': 0.20369010703210916,
# 'children': [{'factor': '交易收益',
# 'value': -0.025843778212483993,
# 'children': None},
# {'factor': '杠杆收益', 'value': 0.0, 'children': None},
# {'factor': '持仓收益',
# 'value': 0.2295338852445934,
# 'children': [{'factor': '主动收益',
# 'value': 0.06632185697753212,
# 'children': [{'factor': '可转债收益',
# 'value': 0.0,
# 'children': [{'factor': '可转债配置收益', 'value': 0.0, 'children': None},
# {'factor': '可转债选择收益', 'value': 0.0, 'children': None}]},
# {'factor': '基金收益',
# 'value': 0.0,
# 'children': [{'factor': '基金配置收益', 'value': 0.0, 'children': None},
# {'factor': '基金选择收益', 'value': 0.0, 'children': None}]},
# {'factor': '指数收益',
# 'value': 0.0,
# 'children': [{'factor': '指数配置收益', 'value': 0.0, 'children': None},
# {'factor': '指数选择收益', 'value': 0.0, 'children': None}]},
# {'factor': '期权收益',
# 'value': 0.0,
# 'children': [{'factor': '期权配置收益', 'value': 0.0, 'children': None},
# {'factor': '期权选择收益', 'value': 0.0, 'children': None}]},
# {'factor': '期货收益',
# 'value': 0.0,
# 'children': [{'factor': '期货配置收益', 'value': 0.0, 'children': None},
# {'factor': '期货选择收益', 'value': 0.0, 'children': None}]},
# {'factor': '港股收益',
# 'value': 0.0,
# 'children': [{'factor': '港股配置收益', 'value': 0.0, 'children': None},
# {'factor': '港股选择收益', 'value': 0.0, 'children': None}]},
# {'factor': '现金收益',
# 'value': 0.0,
# 'children': [{'factor': '现金配置收益', 'value': 0.0, 'children': None},
# {'factor': '现金选择收益', 'value': 0.0, 'children': None}]},
# {'factor': '股票收益',
# 'value': 0.06632185697753212,
# 'children': [{'factor': '股票配置收益',
# 'value': 3.054056964001931e-06,
# 'children': None},
# {'factor': '股票选择收益',
# 'value': 0.06631880292056812,
# 'children': None}]},
# {'factor': '非标资产收益',
# 'value': 0.0,
# 'children': [{'factor': '非标资产配置收益', 'value': 0.0, 'children': None},
# {'factor': '非标资产选择收益', 'value': 0.0, 'children': None}]}]},
# {'factor': '基准持仓收益',
# 'value': 0.1632120282670611,
# 'children': [{'factor': '基准收益',
# 'value': 0.1182192041711989,
# 'children': None},
# {'factor': '穿透效应',
# 'value': 0.04499282409586221,
# 'children': None}]}]}]}]
展示组合风格因子主动暴露及主动收益
style_factor_attr = pd.DataFrame(
result['attribution']['factor_attribution'][0]['factors'])
plt.figure()
style_factor_attr.set_index('factor')['active_exposure'].plot(kind='bar',figsize=(15,7),legend='active_exposure',fontsize=15)
plt.figure()
(style_factor_attr.set_index('factor')['active_return']*100).plot(kind='bar', figsize=(15, 7),legend='active_return',fontsize=15)