Python量化交易 您所在的位置:网站首页 量化交易图形分析 Python量化交易

Python量化交易

2024-07-17 07:37| 来源: 网络整理| 查看: 265

使用matplotlib和mplfinance生成专业的数据可视化仪表盘(下篇) 投资结果的可视化(下篇)图表的布局规划及格式设定图表布局格式设定 表头和回测结果摘要信息表1:绘制收益率曲线图1,绘制投资收益率以及基准收益率2,添加买卖区间3,使用箭头标记最大回撤区间 表2:绘制对数比例的收益率曲线图表3:绘制收益额柱状图表4:绘制盈利能力指数变动图(阿尔法系数/夏普率)表5:绘制风险系数变动图(波动率/贝塔系数)表6:历史回撤区间(潜水图——回撤比例图)表7:月度收益热力图表8:年度收益率柱状图表9:月度收益率直方图 总结及完整代码

投资结果的可视化(下篇)

在上一篇文章《投资组合的评价和可视化(上)》中,我们了解了几种常见的投资组合评价指标的计算方法,并且通过一个实例,一步步根据这个投资组合在过去十年的模拟交易结果,计算出了它的各项指标,接下来,我们将一步步实现所有指标的可视化。

在这篇文章里,我们使用matplotlib和mplfinance来实现可视化。我们使用前一篇文章里计算完成的数据,把他们组合显示在一张图表中(如下图): 在这里插入图片描述

看上去图表比较复杂,但是我们会一步步将它们实现。图表中的原始数据可以在这里下载,原始数据包含一个大小盘轮动策略在过去十年里的模拟交易结果。在上一篇文章中,我们在原始数据的基础上一步步计算了投资组合的所有相关评价指标,包括:

收益率和年化收益率:整个模拟交易历史上的投资收益率、年化收益率和当日收益率存储在looped_value的rtn, annual_rtn, pct_change三列中月度收益率:投资组合在模拟交易历史上每个月的月度收益率存储在一个名为montly_return_df的DataFrame中波动率Volatility:这里我们以250个交易日为单位,滚动计算每一天的波动率,这个数字保存在looped_value的volatility列中,同时,计算出十年间的滚动波动率的平均值:0.211最大回撤Max Drawdwon:我们通过遍历十年间的资产总额,统计出所有的回撤情况,存储在一个名为dd_df的DataFrame中,并按回撤深度从大到小排序,知道最大回撤位20%。夏普率Sharp Ratio:反映投资组合每承担一份风险,可以获取多少超额收益,我们滚动计算了十年间每一天的250日夏普率,存储在looped_value的sharp列中,并计算出十年间滚动夏普率的平均值:0.94,说明承担的风险稍大于获取的超额收益。卡尔玛比率 Calmar Ratio:反映投资组合每承受一份回撤,能够获取多少超额收益,我们滚动计算了十年间每一天的滚动卡尔玛比率,存储在looped_value的sharp列中,并计算出十年间滚动卡尔玛率的平均值:2.07,说明相对于收益来说,回撤幅度可以接受。贝塔系数:体现投资组合随市场波动的情况,我们同样计算了滚动贝塔系数,存储在beta列中,同时计算出其平均值:0.62,说明投资组合的总体风险(波动率)小于市场平均水平。阿尔法系数:揭示投资组合获取超越市场因素带来的超市场收益,滚动阿尔法系数存储在looped_value的alpha列中,平均值为27%,这是超过市场因素的“内生”收益率。

下面,我们就来一步步把上面的信息通过matplotlib可视化出来。不过,本文所有代码都是基于上一篇文章的结果,因此,还没有读上一篇文章的同学,需要复制完整代码以确保上述的计算都已经完成。

图表的布局规划及格式设定

由于需要显示的内容非常多,一张简单的图表是无法表现所有内容的,因此我们需要一张组合图表。在Matplotlib中,一张图表被称为一个figure,在一个figure上可以放置若干个绘图区域Axes,每个绘图区域内都有自己独立的数据、网格、标题等等元素,我们要在一个figure上有计划第放置多个Axes来制作组合图表。

我们首先需要加载matplotlib模块:

import matplotlib.pyplot as plt import matplotlib.dates as mdates from pandas.plotting import register_matplotlib_converters register_matplotlib_converters() 图表布局

我们希望整张图表既能够展现所有的内容,但是又主次分明,因此需要对图表的格式做出一个大致的规划: 首先,图表应该表现三大类内容:

回测结果的文字总结,用文字的形式展示出关键信息,如回报率、夏普率等等历史过程曲线图,以交易历史日期为X轴,以不同的图表展示各种历史过程曲线,例如收益率、波动率、回撤水平等,这部分图表又包含两部分: 2.1. 收益率曲线,由于收益率是回测结果的核心信息,因此这部分图表要占据最大的版面,占据视觉重心位置,而且以三种不同的方式展现 2.2. 回测评价指标曲线,包括风险敞口评价指标如beta、波动率,以及盈利能力评价指标如alpha等收益率统计图表,通过热力图、直方图等多种形式展示历史收益率的水平。

根据上面的规划,我们可以按下面的格式在一个figure上创建九张图表,并调整它们的位置。留出最大的一张表,位于视觉重心处,用于显示最重要的收益率曲线。

chart_width = 0.88 ## 图表布局规划 fig = plt.figure(figsize=(12, 15), facecolor=(0.82, 0.83, 0.85)) ax1 = fig.add_axes([0.05, 0.67, 0.88, 0.20]) ax2 = fig.add_axes([0.05, 0.57, 0.88, 0.08], sharex=ax1) ax3 = fig.add_axes([0.05, 0.49, 0.88, 0.06], sharex=ax1) ax4 = fig.add_axes([0.05, 0.41, 0.88, 0.06], sharex=ax1) ax5 = fig.add_axes([0.05, 0.33, 0.88, 0.06], sharex=ax1) ax6 = fig.add_axes([0.05, 0.25, 0.88, 0.06], sharex=ax1) ax7 = fig.add_axes([0.05, 0.04, 0.35, 0.16]) ax8 = fig.add_axes([0.45, 0.04, 0.15, 0.16]) ax9 = fig.add_axes([0.64, 0.04, 0.29, 0.16])

使用plt.show()或者fig.show()即可看到图表的外观如下。

同学们可以自行微调add_axes()里列表中的四个数字,来调整每个表的位置大小,这四个数字分别代表: [图表左下角X坐标,图表左下角Y坐标,图表的宽度,图表的高度] 每个数字都是一个小数,单位为整个figure的高度或宽度,例如,[0.64, 0.04, 0.29, 0.16]表示图> 表的左下角位于figure宽度的64%处、高度的4%处,宽度为figure的29%,高度为figure的16%

在这里插入图片描述

格式设定

为了图表的美观起见,我们还可以调整一下各个图表的格式,例如,前六张图表可以共享一个X轴,因此图表的X轴坐标便可以隐藏,另外,我们希望在图表左边放置名称或说明,那么Y轴坐标就可以放在右边,等等。

这部分代码可以放置在最后。

# 设置所有图表的基本格式: for ax in [ax1, ax2, ax3, ax4, ax5, ax6]: ax.yaxis.tick_right() ax.xaxis.set_ticklabels([]) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.spines['bottom'].set_visible(False) ax.spines['left'].set_visible(False) ax.grid(True) # 设置图表的格式 years = mdates.YearLocator() # every year months = mdates.MonthLocator() # every month weekdays = mdates.WeekdayLocator() # every weekday years_fmt = mdates.DateFormatter('%Y') month_fmt_none = mdates.DateFormatter('') month_fmt_l = mdates.DateFormatter('%y/%m') month_fmt_s = mdates.DateFormatter('%m') # 调整主图表的日期格式 major_locator = years major_formatter = years_fmt minor_locator = months minor_formatter = month_fmt_none # 前五个主表的时间轴共享,因此只需要设置最下方表的时间轴即可 ax6.xaxis.set_major_locator(major_locator) ax6.xaxis.set_major_formatter(major_formatter) ax6.xaxis.set_minor_locator(minor_locator) ax6.xaxis.set_minor_formatter(minor_formatter) for ax in [ax1, ax2, ax3, ax4, ax5]: plt.setp(ax.get_xticklabels(), visible=False) 表头和回测结果摘要信息

图表的格式设置完毕后,我们可以先把所有文字信息输出到图表上的空白处。 在这里,我们需要计算出需要显示的所有数据:

# 持股数量变动量,当持股数量发生变动时,判断产生买卖行为 change = (looped_value[stock_holdings] - looped_value[stock_holdings].shift(1)).sum(1) # 计算回测记录第一天的回测结果和参考指数价格,以此计算后续的收益率曲线 start_point = looped_value['value'].iloc[0] ref_start = looped_value['benchmark'].iloc[0] # 计算回测结果的每日回报率 ret = looped_value['value'] - looped_value['value'].shift(1) position = 1 - (looped_value['cash'] / looped_value['value']) beta = looped_value['beta'] alpha = looped_value['alpha'] volatility = looped_value['volatility'] sharp = looped_value['sharp'] underwater = looped_value['underwater'] drawdowns = dd_df # 回测结果和参考指数的总体回报率曲线 return_rate = (looped_value.value - start_point) / start_point * 100 ref_rate = (looped_value.benchmark - ref_start) / ref_start * 100 # 将benchmark的起始资产总额调整到与回测资金初始值一致,一遍生成可以比较的benchmark资金曲线 # 这个资金曲线用于显示"以对数比例显示的资金变化曲线"图 adjusted_bench_start = looped_value.benchmark / ref_start * start_point

使用fig.suptitle()设置图表的大标题 而其他所有的文字都可以通过fig.text()来输出。fig.text()的头两个参数是文字的坐标,需要注意,文字的坐标是以文字块左下角的位置来定义的,因此如果文字中包含换行符,第一行文字就会被“顶到”上面去。

请注意,下面的代码中使用了fontname='pingfang HK'以使用中文字体,否则中文会显示为乱码

## 2,显示投资回测摘要信息 title_asset_pool = '沪深300/创业板指 大小盘轮动策略' fig.suptitle(f'回测交易结果: {title_asset_pool} - 业绩基准: 沪深300指数', fontsize=14, fontweight=10, fontname='pingfang HK') # 投资回测结果的评价指标全部被打印在图表上,所有的指标按照表格形式打印 # 为了实现表格效果,指标的标签和值分成两列打印,每一列的打印位置相同 fig.text(0.07, 0.955, f'回测期长: {total_years:3.1f} 年, ' f' 从: {looped_value.index[0].date()} 起至 {looped_value.index[-1].date()}止', fontname='pingfang HK') fig.text(0.21, 0.90, f'交易操作汇总:\n\n' f'投资总金额:\n' f'期末总资产:', ha='right', fontname='pingfang HK') fig.text(0.23, 0.90, f'{op_counts.buy.sum()} 次买入 \n' f'{op_counts.sell.sum()} 次卖出\n' f'¥{total_invest:13,.2f}\n' f'¥{final_value:13,.2f}', fontname='pingfang HK') fig.text(0.50, 0.90, f'总投资收益率:\n' f'平均年化收益率:\n' f'基准投资收益率:\n' f'基准投资平均年化收益率:\n' f'最大回撤:\n', ha='right', fontname='pingfang HK') fig.text(0.52, 0.90, f'{total_return:.2%} \n' f'{annual_return: .2%} \n' f'{ref_return:.2%} \n' f'{ref_annual_rtn:.2%}\n' f'{mdd:.1%} \n' f' 底部日期 {mdd_date.date()}', fontname='pingfang HK') fig.text(0.82, 0.90, f'alpha / 阿尔法系数:\n' f'Beta / 贝塔系数:\n' f'Sharp ratio / 夏普率:\n' f'Calmar ratio / 卡尔玛比率:\n' f'250-日滚动波动率:', ha='right', fontname='pingfang HK') fig.text(0.84, 0.90, f'{avg_alpha:.3f} \n' f'{avg_beta:.3f} \n' f'{avg_sharp:.3f} \n' f'{avg_calmar:.3f} \n' f'{avg_volatility:.3f}', fontname='pingfang HK')

如果输入的文字正常,显示效果应该如下图所示:

在这里插入图片描述

有的朋友在运行上述代码时,可能会遇到错误说使用的中文字体不存在,因而中文显示为乱码,这里给出一个解决方案供大家参考:

为了显示系统中有哪些中文字体,可以先导入matplotlib的FontManager类,调用这个类的ttflist属性,就可以看到系统中已经存在的所有可以被matplotlib使用的字体了,选择其中的中文字体即可(中文字体名称中一般都带有拼音,或者含有TC、SC之类的关键字:

>>> from matplotlib.font_manager import FontManager >>> fm = FontManager() >>> fm.ttflist Out: [, , ... , ... , ]

清单中的字体可能会比较多,也有多种中文字体,比如上面例子中的PingFang HK就是中文字体,将字体名称PingFang HK用于font就可以了:font_name='PingFang HK'

接下来进入重头戏,各个图表的绘制。由于每张图表数据区别甚大,因此我们分别来看。

表1:绘制收益率曲线图

收益率曲线图是整张图表的视觉重心,也是一张颇为复杂的复合图表,我们希望它能体现出最重要的关键信息,因此信息集成度比较高。

这张图表由三部分组成:

收益率曲线:包括基准收益率曲线和投资收益率曲线,为了体现基准收益率的变化,基准收益率曲线还被填充为两种颜色:正收益区间填充绿色,负收益区间填充红色,投资收益率就只是一条深红色曲线,在偏绿的背景下最为显眼。交易持股指示:用于指示整个交易过程中什么时候买入、什么时候卖出,或者什么时候持股、什么时候空仓最大回撤区间:使用箭头指出最大回撤区间和回撤比例

上面三部分我们逐步绘制。

1,绘制投资收益率以及基准收益率

收益率曲线用最简单的axex.plot()函数即可实现,传入必要的参数如颜色color,线形linestyle、透明度alpha等即可:

# 3。1, 绘制回测结果的收益率曲线图 ax1.set_title('总收益率、基准收益率和交易历史', fontname='pingfang HK') ax1.plot(looped_value.index, ref_rate, linestyle='-', color=(0.4, 0.6, 0.8), alpha=0.85, label='Benchmark') ax1.plot(looped_value.index, return_rate, linestyle='-', color=(0.8, 0.2, 0.0), alpha=0.85, label='Return') ax1.set_ylabel('收益率', fontname='pingfang HK') ax1.yaxis.set_major_formatter(mtick.PercentFormatter())

用axes.fill_between()在参考收益率的正负区间分别填充红色和绿色,使它更显眼。

# 填充参考收益率的正负区间,绿色填充正收益率,红色填充负收益率 ax1.fill_between(looped_value.index, 0, ref_rate, where=ref_rate >= 0, facecolor=(0.4, 0.6, 0.2), alpha=0.35) ax1.fill_between(looped_value.index, 0, ref_rate, where=ref_rate 0: # 用不同深浅的绿色填充多头区间, 0 < long_short < 1 if long_short > 1: long_short = 1 ax1.axvspan(first, second, facecolor=((1 - 0.6 * long_short), (1 - 0.4 * long_short), (1 - 0.8 * long_short)), alpha=0.2) else: # 用不同深浅的红色填充空头区间, -1 < long_short < 0 if long_short 0, ref_rate, np.nan) sell_points = np.where(change


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有