Backtrader 量化回测实践(6)——量化回测评价工具Quantstats
1.概述
Quantstats是用于量化金融分析和投资组合优化的Python库。该库提供了各种工具,可从不同来源获得金融数据,进行技术和基本分析,并创建和测试投资策略。还可以使用可视化工具来分析股票和投资组合。
Quantstats是一款简单易用的定量金融分析工具,因此它将是本研究的首选库。
用BT进行回测后,内置的Analysis分析指标,可以作为回测结果的考量指标,但是没有对于结合市场指标评价回测结果。例如策略年化收益率5%,但是大盘市场走势,大盘指数年化收益率6%,说明策略并不理想。
1964年,威廉·夏普在《金融学期刊》上发表的论文里,首次把金融资产的收益拆成两部分。和市场一起波动的部分叫贝塔收益,不和市场一起波动的部分就叫阿尔法收益。
阿尔法收益,指的是跑赢大盘的超额收益。战胜市场平均水平获得的收益。主要承担的是非系统性风险,也就是证券组合带来的风险。
贝塔收益,就是跟随整个市场波动获取的平均收益。风险主要来自于市场的系统性风险。
Quantstats可以实现市场参考,设定一个基准策略,对比量化策略,在A股通常把基准设定为沪深300指数的收益率。
安装简单,不做赘述。
2. QuantStats主要功能
在GitHub上介绍:
主要有三个功能:
QuantStats is comprised of 3 main modules:
quantstats.stats - for calculating various performance metrics, like Sharpe ratio, Win rate, Volatility, etc.
quantstats.plots - for visualizing performance, drawdowns, rolling statistics, monthly returns, etc.
quantstats.reports - for generating metrics reports, batch plotting, and creating tear sheets that can be saved as an HTML file.
- 1.统计功能,计算各类性能指标,夏普率,胜率,波动率等;
- 2.绘图功能,可视化性能指标,最大回撤,滚动统计,月度回报率等;
- 3.报告功能,生成度量报告,批量绘图,创建HTML报告;
(1)统计功能
统计指标很丰富。
str_stats = ''
for k in dir(qs.stats) :
if k[:1] != '_':
str_stats += k + ','
print(str_stats)
统计指标:
adjusted_sortino,autocorr_penalty,avg_loss,avg_return,avg_win,best,cagr,calmar,common_sense_ratio,comp,compare,compsum,conditional_value_at_risk,consecutive_losses,consecutive_wins,cpc_index,cvar,distribution,drawdown_details,expected_return,expected_shortfall,exposure,gain_to_pain_ratio,geometric_mean,ghpr,greeks,implied_volatility,information_ratio,kelly_criterion,kurtosis,max_drawdown,monthly_returns,omega,outlier_loss_ratio,outlier_win_ratio,outliers,payoff_ratio,pct_rank,probabilistic_adjusted_sortino_ratio,probabilistic_ratio,probabilistic_sharpe_ratio,probabilistic_sortino_ratio,profit_factor,profit_ratio,r2,r_squared,rar,recovery_factor,remove_outliers,risk_of_ruin,risk_return_ratio,rolling_greeks,rolling_sharpe,rolling_sortino,rolling_volatility,ror,serenity_index,sharpe,skew,smart_sharpe,smart_sortino,sortino,tail_ratio,to_drawdown_series,treynor_ratio,ulcer_index,ulcer_performance_index,upi,value_at_risk,var,volatility,warn,win_loss_ratio,win_rate,worst
可以逐一测试。
(2)报告功能
You can create 7 different report tearsheets:
qs.reports.metrics(mode='basic|full", ...) - shows basic/full metrics
qs.reports.plots(mode='basic|full", ...) - shows basic/full plots
qs.reports.basic(...) - shows basic metrics and plots
qs.reports.full(...) - shows full metrics and plots
qs.reports.html(...) - generates a complete report as html
Let's create an html tearsheet
主要报告功能说明:
qs.reports.metrics(mode='basic|full", …) - 展现基础/所有指标
qs.reports.plots(mode='basic|full", …) - 展现基础/所有绘图
qs.reports.basic(…) - 展现基础指标和绘图
qs.reports.full(…) - 展现所有指标和绘图
qs.reports.html(…) - 生成html完整报告
(3)绘图功能
str_stats = ''
for k in dir(qs.plots) :
if k[:1] != '_':
str_stats += k + ','
print(str_stats)
绘图功能:
daily_returns,distribution,drawdown,drawdowns_periods,earnings,histogram,log_returns,monthly_heatmap,monthly_returns,plotly,returns,rolling_beta,rolling_sharpe,rolling_sortino,rolling_volatility,snapshot,to_plotly,warnings,yearly_returns,
3.测试准备
(1)数据说明
- 收益率用收盘价,pandas的方法:pct_change计算。
- quantstats需要的策略数据格式为Series,值是收益率,索引是日期,必须为datetime格式。
- 基准数据用沪深300指标。
- 无风险收益率,假设按三年期存款利率3%。
(2)环境版本
后面有的功能会报错,所以列示出需要的环境版本。
QuantStats 0.0.62
pandas 2.0.3
matplotlib 3.2.2
(3)数据准备
if __name__ == "__main__":
st_data = get_code('000858.SZ')
pd.to_datetime(st_data.index , format='%Y-%m-%d')
st_ret = st_data['close'].pct_change()
# quantstats 要求列名是returns
st_ret.name = 'returns'
# 第一天是空值,设置为0
st_ret.fillna(0.0, inplace=True)
# 沪深300指数作为参考
bk_index = get_index('000300.SH')
pd.to_datetime(bk_index.index , format='%Y-%m-%d')
# 计算每日涨幅,作为基准
#bk_ret['returns'] = bk_index['close'].pct_change()
bk_ret = bk_index['close'].pct_change()
# quantstats 要求列名是returns
bk_ret.name = 'returns'
# 第一天是空值,设置为0
bk_ret.fillna(0.0, inplace=True)
4. QuantStats统计功能
QuantStats指标丰富,包括:收益率、年化收益率、最大回撤、最大回撤比率、收益风险比、盈亏比、收益曲线、收益分布、信息比率、夏普比率,α、β收益等。
(1)sharpe夏普率
print('sharpe: ', qs.stats.sharpe(returns=st_ret))
sharpe: 0.4976597575061821
(2)win_loss_ratio盈亏比
print('win_loss_ratio: ', qs.stats.win_loss_ratio(returns=st_ret))
win_loss_ratio: 1.0713852747410746
(3)α β收益率
alphabeta = qs.stats.greeks(st_ret, bk_ret)
print('alpha',alphabeta.alpha)
print('beta',alphabeta.beta)
alpha 0.0997169249792272
beta 0.9832893493513557
(3) volatility波动率
print('volatility: ', qs.stats.volatility(returns=st_ret))
volatility: 0.4219360391808521
5. QuantStats报告功能
(1)metrics
qs.reports.metrics(returns=st_ret, benchmark=bk_ret,mode='full',rf=0.03, prepare_returns=False)
股票的指标作为策略指标吗,沪深300指标作为参考指标:
Benchmark Strategy
------------------------- ----------- ----------
Start Period 2000-01-04 2000-01-04
End Period 2020-12-30 2020-12-30
Risk-Free Rate 3.0% 3.0%
Time in Market 76.0% 76.0%
Cumulative Return 420.32% 4,253.26%
CAGR﹪ 5.57% 13.21%
Sharpe 0.35 0.63
Prob. Sharpe Ratio 49.46% 85.19%
Smart Sharpe 0.34 0.61
Sortino 0.49 0.93
Smart Sortino 0.47 0.91
Sortino/√2 0.34 0.66
Smart Sortino/√2 0.33 0.64
Omega 1.14 1.14
Max Drawdown -72.3% -76.45%
Longest DD Days 4824 3523
Volatility (ann.) 23.7% 36.1%
R^2 0.42 0.42
Information Ratio 0.03 0.03
Calmar 0.08 0.17
Skew -0.37 -0.27
Kurtosis 6.73 9.72
Expected Daily % 0.03% 0.08%
Expected Monthly % 0.67% 1.53%
Expected Yearly % 8.17% 19.68%
Kelly Criterion 5.81% 5.14%
Risk of Ruin 0.0% 0.0%
Daily Value-at-Risk -2.41% -3.64%
Expected Shortfall (cVaR) -2.41% -3.64%
Max Consecutive Wins 12 9
Max Consecutive Losses 10 11
Gain/Pain Ratio 0.1 0.16
Gain/Pain (1M) 0.46 0.79
Payoff Ratio 0.98 1.05
Profit Factor 1.1 1.16
Common Sense Ratio 1.17 1.44
CPC Index 0.58 0.62
Tail Ratio 1.06 1.24
Outlier Win Ratio 7.84 4.84
Outlier Loss Ratio 4.66 3.14
MTD 3.09% 12.94%
3M 11.37% 30.56%
6M 24.43% 71.27%
YTD 24.83% 115.68%
1Y 27.14% 122.39%
3Y (ann.) 4.17% 34.46%
5Y (ann.) 8.05% 41.98%
10Y (ann.) 3.6% 15.77%
All-time (ann.) 5.57% 13.21%
Best Day 9.34% 10.02%
Worst Day -11.56% -28.44%
Best Month 27.93% 57.21%
Worst Month -25.85% -33.15%
Best Year 161.55% 214.74%
Worst Year -65.95% -70.67%
Avg. Drawdown -4.56% -7.22%
Avg. Drawdown Days 99 75
Recovery Factor 3.05 6.62
Ulcer Index 0.37 0.34
Serenity Index 0.13 0.52
Avg. Up Month 7.1% 11.74%
Avg. Down Month -7.12% -9.38%
Win Days % 53.36% 51.52%
Win Month % 57.98% 58.51%
Win Quarter % 54.69% 64.06%
Win Year % 56.25% 68.75%
Beta - 0.98
Alpha - 0.15
Correlation - 64.53%
Treynor Ratio - 4324.16%
(2)basic
qs.reports.basic(returns=st_ret, benchmark=bk_ret,rf=0.03, prepare_returns=False)
输出结果:
Performance Metrics
Benchmark Strategy
------------------ ----------- ----------
Start Period 2005-01-05 2005-01-05
End Period 2020-12-30 2020-12-30
Risk-Free Rate 3.0% 3.0%
Time in Market 100.0% 99.0%
Cumulative Return 420.32% 4,253.26%
CAGR﹪ 7.38% 17.69%
Sharpe 0.43 0.75
Prob. Sharpe Ratio 60.11% 90.55%
Sortino 0.61 1.1
Sortino/√2 0.43 0.78
Omega 1.14 1.14
Max Drawdown -72.3% -76.45%
Longest DD Days 4824 3523
Gain/Pain Ratio 0.1 0.16
Gain/Pain (1M) 0.46 0.79
Payoff Ratio 0.98 1.05
Profit Factor 1.1 1.16
Common Sense Ratio 1.17 1.39
CPC Index 0.58 0.62
Tail Ratio 1.06 1.2
Outlier Win Ratio 5.39 3.29
Outlier Loss Ratio 5.12 3.45
MTD 3.09% 12.94%
3M 11.37% 30.56%
6M 24.43% 71.27%
YTD 24.83% 115.68%
1Y 27.14% 122.39%
3Y (ann.) 4.17% 34.46%
5Y (ann.) 8.05% 41.98%
10Y (ann.) 3.6% 15.77%
All-time (ann.) 7.38% 17.69%
Avg. Drawdown -4.56% -7.22%
Avg. Drawdown Days 99 75
Recovery Factor 3.05 6.62
Ulcer Index 0.43 0.39
Serenity Index 0.13 0.48
输出图示:
(3)plots
基础信息绘图:
qs.reports.plots(returns=st_ret, benchmark=bk_ret,mode='basic',rf=0.03, prepare_returns=False)
全部信息绘图,报错:
qs.reports.plots(returns=st_ret, benchmark=bk_ret,mode='full',rf=0.03, prepare_returns=False)
ValueError: Multi-dimensional indexing (e.g.
obj[:, None]
) is no longer supported. Convert to a numpy array before indexing instead.
如果有人知道错误原因,不吝赐教!!!
(4)full 和 html
由于full和html都使用全量信息绘图,所以和plots的full模式下,都报同样的错误。
qs.reports.full(st_ret, benchmark=bk_ret,rf=0.03)
qs.reports.html(returns=st_ret, benchmark=bk_ret,output='returns.html')
6. QuantStats绘图功能
绘图方法:
daily_returns,distribution,drawdown,drawdowns_periods,earnings,histogram,log_returns,monthly_heatmap,monthly_returns,plotly,returns,rolling_beta,rolling_sharpe,rolling_sortino,rolling_volatility,snapshot,to_plotly,warnings,yearly_returns
(1)daily_returns
qs.plots.daily_returns(returns=st_ret, benchmark=bk_ret)
(2)drawdowns_periods
阶段最大回撤
qs.plots.drawdowns_periods(returns=st_ret)
(3)snapshot
qs.plots.snapshot(returns=st_ret, benchmark=bk_ret)
(4)returns
qs.plots.returns(returns=st_ret, benchmark=bk_ret)
(5)yearly_returns
qs.plots.yearly_returns(returns=st_ret, benchmark=bk_ret)
(6)distribution
qs.plots.distribution(returns=st_ret)
(7)所有测试
测试了所有绘图方法,部分的方法有报错,都在注释中记录了。
大家可以测试,如有处理方法,问题原因,望告知。
#qs.plots.daily_returns(returns=st_ret, benchmark=bk_ret)
#qs.plots.distribution(returns=st_ret)
#qs.plots.drawdown(returns=st_ret)
#qs.plots.drawdowns_periods(returns=st_ret)
#qs.plots.earnings(returns=st_ret) # ValueError: Multi-dimensional indexing (e.g. `obj[:, None]`) is no longer supported. Convert to a numpy array before indexing instead.
#qs.plots.histogram(returns=st_data, benchmark=bk_index) # TypeError: '>=' not supported between instances of 'datetime.date' and 'int'
#qs.plots.log_returns(returns=st_ret, benchmark=bk_ret)
#qs.plots.monthly_heatmap(returns=st_ret, benchmark=bk_ret)
#qs.plots.monthly_returns(returns=st_ret)
#qs.plots.plotly(returns=st_ret) #TypeError: 'module' object is not callable
#qs.plots.returns(returns=st_ret, benchmark=bk_ret)
#qs.plots.rolling_beta(returns=st_ret, benchmark=bk_ret)
#qs.plots.rolling_sharpe(returns=st_ret, benchmark=bk_ret)
#qs.plots.rolling_sortino(returns=st_ret, benchmark=bk_ret)
#qs.plots.rolling_volatility(returns=st_ret, benchmark=bk_ret)
#qs.plots.snapshot(returns=st_ret, benchmark=bk_ret)
#qs.plots.to_plotly(returns=st_ret, benchmark=bk_ret) # TypeError: to_plotly() got an unexpected keyword argument 'returns'
#qs.plots.warnings(returns=st_ret, benchmark=bk_ret) # TypeError: 'module' object is not callable
#qs.plots.yearly_returns(returns=st_ret, benchmark=bk_ret)
7. 常用指标说明
1. 无风险利率(Risk-Free Rate)
无风险利率表示投资者在一定时间内能够期望从无任何风险的投资中获得的利率。现实中,通常使用伦敦同业拆放利率(London Inter bank Offered Rate,LIBOR)或美国国债利率作为无风险利率。因为一般认为金融机构倒闭的机会很低,财政有问题的银行会被禁止参与同业拆放,因此LIBOR是没有风险的。政府可以发行主权货币应付到期的债务,因此国债不可能违约。不过,此说法并不适用于欧元。欧元区国家无权发行货币,因此发生了欧洲主权债务危机
2. Cumulative Return & 复合年均增长率(cagr:Compound Annual Growth Rate)
Cumulative Return:累计收益= (Ending Value/Beginning Value)
cagr计算方法为:总增长率百分比的n方根,n相等于有关时期内的年数。CAGR = (Ending Value/Beginning Value)^(1/# of years)-1
3. 总杠杆(gross leverage)
总杠杆率是敞口绝对值的总和除以净资产(Net asset value,简称NAV),不考虑多空抵消和对冲。1亿美元标普500股票的长仓和1亿美元的标普500期货空仓总杠杆率为200%。这一指标反映的是总体投资活动水平,是交易对手风险和流动性风险的关注对象之一
4. 最大回撤(Max Drawdown)
描述策略可能出现的最糟糕的情况,是指在某一段时期内产品净值从最高点开始回落到最低点的幅度。具体计算方法为:max(1 - 策略当日价值 / 当日之前产品最高净值)
举例说明:2010年7月20日初始净值1;恰逢2010年10月美国推出QE2全球股市大涨,该基金净值增长到1.8;其后国内股市剧烈震荡,截止2011年4月25,该基金净值为0.98.假设投资者在最高峰时期认购,半年后在最低潮时期赎回,亏损45.5%。这就是最大回撤率给高位追买的投资者的指示意义
5. 波动率(Volatility)
是金融资产价格的波动程度,是对资产收益率不确定性的衡量,用于反映金融资产的风险水平。波动率越高,金融资产价格的波动越剧烈,资产收益率的不确定性就越强;波动率越低,金融资产价格的波动越平缓,资产收益率的确定性就越强。具体计算方法为:策略每日收益的年化标准差
6. 胜率(win rate)
胜率的定义是赢钱次数除以总买入次数
举例说明:投入十次,七次盈利,三次亏损,胜率就是70%
7. 胜负比(win loss ratio)
胜负比也称为“成功率”。赢/亏比率是赢的交易总数与输的交易数之比。它不考虑赢或输了多少钱,只考虑输赢次数
8. 盈亏比(risk return ratio)
也叫赔率。指的是每次交易的盈利和亏损的比例,代表投资的风险收益比。投资系统的盈亏比=一段时间所有投资盈利单的盈利之和/相同时间段所有亏损单的亏损之和。投资的收益盈亏比是3是指平均来讲赚3块钱,要付出1块的止损。或者说冒一块钱的风险的一项投资获利了3块。长期来看,投资盈亏比,才是直接反映投资者综合水平的的一个量化指标
长期稳定赚大钱的成功者的系统,都是高盈亏比的系统。只有高盈亏比系统,才能成为成功者,才能算稳定盈利
举例说明:平均每次盈利30%,平均每次亏损10%,盈亏比就是3倍
9. 阿尔法(alpha、α值)
投资者获得与市场波动无关的回报,也叫超额收益。 比如投资者获得了15%的回报,其基准获得了10%的回报,那么Alpha或者价值增值的部分就是5%
10. 贝塔(beta、β值)
反映了策略对大盘变化的敏感性。例如一个策略的Beta为1.5,则大盘涨1%的时候,策略可能涨1.5%,反之亦然。具体计算方法为:策略每日收益与参考标准每日收益的协方差/参考标准每日收益的方差。
Beta coefficient = Covariance(Re, Rm)/Variance(Rm)
其中:Re为单一股票的回报, Rm为整体市场的回报,Covariance为股票收益相对整体市场收益的变化情况, Variance为市场数据远离平均值的幅度
11. 夏普比率(sharpe ratio)
夏普比率描述了资产收益对投资者所承担风险的补偿程度。当以一个相同基准来比较两种资产之时,夏普比率较高的资产在相同风险下收益更好;或者说,如果收益相同的话,夏普比率较高的资产风险较低。但是,像其他任何数学模型一样,它依赖于数据的正确性
sharpe ratio = (Rp - Rf)/σp
其中Rp为投资组合的收益,Rf为无风险资产的收益,σp为投资组合超额收益的标准方差
12. 信息比例(information Ratio)
信息比率与夏普比率相似,主要区别在于夏普比率使用无风险收益(比如美国国债)作为基准,而信息比率使用风险指数作为基准(如标准普尔500指数)
具体计算方法为:(策略累积收益 - 基准累积收益)/策略与基准每日收益差值的标准差。【意义】:信息比率数值越大,业绩表现越好。它以马克维茨的均异模型为基础,可以衡量基金的均异特性,表示单位主动风险所带来的超额收益
13. 索提比例(sortino ratio)
与夏普比率类似,所不同的是它区分了波动的好坏,因此在计算波动率时它所采用的不是标准差,而是下行标准差。这其中的隐含条件是投资组合的上涨(正回报率)符合投资人的需求,不应计入风险调整。具体计算方法为:(策略收益-无风险利率)/策略下行波动率。【适用范围】:因为索提诺比率使用的是下行偏差来考虑风险,那么所有的下行偏差局限性也会出现在索提诺比率中。也就是必须要有足够多的“不良”观测,才能计算一个有效的索提诺比率。sortino 比率数值越大,业绩表现越好。
14. 卡玛比率(calmar ratio)
卡玛比率=超额收益/最大回撤(风险)
卡玛比率和卡玛比率的唯一不同之处就是分母不同,夏普比率使用标准差作为风险,卡玛比率使用最大回撤作为风险,本质上都是衡量基金的风险-回报关系
15. Omega比率(omega ratio)
omega比率实际上考虑了收益的整个分布信息,因此包括了所有高阶矩的信息。在临界收益率等于均值的时候,Omega比率等于1。在相同的临界收益率下,对于不同的投资选择,Omega比率越大越好。适用范围:在收益率不服从正态分布的时候,Omega是非常好的替代。【意义】:Omega比率值越高,投资绩效也就越好
16. Tail 比率(tail ratio)
日收益分布的95分位值/5分位值。【使用范围】:均值回归策略,这类型策略的最大风险在于左侧的尾部风险。单次的大额回撤需要很长的时间才能够恢复。因此 tail_ratio 很适合用来刻画这类策略面临的风险。【意义】:tail 比率越大越好,可以理解成衡量最好情况与最坏情况下的收益表现的指标。例如:tail_ratio = 0.25,5分位的亏损是95分位收益的四倍。 这样的策略在发生大额亏损的情况下很难在短时间内恢复
17. Common sense比率(common sense ratio)
(日收益分布的95分位值/5分位值) *(总盈利/总亏损)。【使用范围】:均值回归策略,趋势追踪策略。【意义】:大于1时,策略盈利;小于1时,策略亏损
18. skew值(Skewness)
亦称歪度,在概率论和统计学中衡量实数随机变量概率分布的不对称性。偏度的值可以为正,可以为负或者甚至是无法定义。在数量上,偏度为负(负偏态;左偏)就意味着在概率密度函数左侧的尾部比右侧的长,绝大多数的值(不一定包括中位数在内)位于平均值的右侧。偏度为正(正偏态;右偏)就意味着在概率密度函数右侧的尾部比左侧的长,绝大多数的值(不一定包括中位数)位于平均值的左侧。偏度为零就表示数值相对均匀地分布在平均值的两侧,但不一定意味着其为对称分布
19. 峰度(kurtosis)
大于0表示收益的分布与正态分布相比较为陡峭
20. 决定系数(coefficient of determination, R2)
累计对数收益对时间t的回归的R^2。【意义】:R平方值是趋势线拟合程度的指标,它的数值大小可以反映趋势线的估计值与对应的实际数据之间的拟合程度,拟合程度越高,趋势线的可靠性就越高。R平方值是取值范围在0~1之间的数值,也称为决定系数,最常用于评价回归模型优劣程度的指标
指标说明原文链接