用于分析的绘图:带指标的价格图、资金曲线与回撤图、收益率分布以及导出图表
使用 matplotlib 的“图形与坐标轴”接口构建每份回测报告都需要的四种图表,正确解读它们,并以印刷质量导出。
你将学到什么
fig, ax = plt.subplots()模式,以及为什么对于任何需要复用的图表,它都优于快捷的series.plot()。- 如何堆叠共享同一时间轴的价格、指标和资金面板,以及何时把资金轴切换为对数刻度。
- 如何绘制回撤图和收益率直方图,以及在每种图中应该关注什么。
- 图形尺寸、DPI 和文件格式如何决定导出图像的效果。
讲解
研究中的图表不是装饰;它们是检查数字是否正确的最快方法。一条在一天内跳升 30% 的资金曲线,通常意味着数据错误,而不是一笔绝佳的交易。本课通过 matplotlib(pandas 在底层调用的绘图库)的面向对象接口来使用它。
图形、坐标轴与共享时间轴
图形(figure)是整块画布;坐标轴(axes)对象是其中的一个绘图区域。fig, axes = plt.subplots(3, 1, figsize=(10, 8), sharex=True, height_ratios=[3, 1, 1]) 会创建三个高度不同、上下堆叠并能同步缩放的面板。每个面板的套路都相同:调用 ax.plot(x, y, label=...),然后调用 ax.set_ylabel、ax.legend() 和 ax.grid(True, alpha=0.3)。为每个面板加一个简短的标题,并标注单位(€、%、点)。
带指标的价格图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6), sharex=True, height_ratios=[3, 1])ax1.plot(df.index, df.close, lw=1, label='Close')ax1.plot(df.index, df.sma50, lw=1, label='SMA 50')ax1.fill_between(df.index, df.bb_lo, df.bb_hi, alpha=0.15, label='Bollinger')ax1.legend(loc='upper left'); ax1.set_ylabel('Price (€)')ax2.plot(df.index, df.rsi, lw=1); ax2.axhline(70, ls='--'); ax2.axhline(30, ls='--')ax2.set_ylabel('RSI'); fig.autofmt_xdate(); fig.tight_layout()
fill_between为一个区间着色;axhline 绘制参考水平线;autofmt_xdate 旋转日期标签,使它们不会重叠。若要标出第 8 课交易清单中的交易,入场用 ax1.scatter(entries.index, entries.price, marker='^'),离场则用向下的三角形。
资金曲线与回撤
只要账户在整个期间内增长超过一倍,就请用 ax.set_yscale('log') 绘制资金曲线。在线性坐标轴上,从 €10,000 到 €11,000 的变动与后来从 €40,000 到 €44,000 的变动相比显得微不足道,尽管两者都是 +10%。回撤面板绘制 dd = equity / equity.cummax() - 1 并为其填色:ax.fill_between(dd.index, dd, 0, alpha=0.4)。解读时关注三点:最深处(最大回撤)、低于零的最长时段(水下时间,往往比深度更令人痛苦),以及回撤是否集中在某一种市场状态中。
收益率分布
ax.hist(returns, bins=50, density=True) 显示日收益率或每笔交易收益率的分布形状。叠加一条均值和标准差相同的正态曲线,就能看到肥尾:真实收益率中的极端日子比钟形曲线预测的更多。对于交易清单,请改为绘制 R 倍数:一个健康的趋势系统会显示出大量接近 −1R 的小额亏损和一条长长的右尾;一个做空波动率的系统则呈现镜像形态,这是一个警示信号。用 returns.skew() 和 returns.kurt() 报告偏度和峰度(pandas 返回的是超额峰度,因此 0 表示尾部接近正态)。
导出
fig.savefig('equity.png', dpi=150, bbox_inches='tight') 写出一张栅格图像;像素尺寸 = 英寸 × DPI,因此一张 10 × 6 英寸、150 DPI 的图形为 1,500 × 900 像素。若要在报告中保持清晰,请使用 .svg 或 .pdf 矢量格式输出,并使用 bbox_inches='tight' 裁掉白边。在循环中保存后务必调用 plt.close(fig),否则一个生成 40 张观察列表图表的脚本(第 12 课)会在内存中保留 40 个图形。用 plt.rcParams['figure.dpi'] = 110 一次性设定默认值,而不是在每次调用中设定。
实例演算
假设示例:一个回测资金序列(以欧元计,数字为编造)及其图表。资金数值中不计手续费和税费。
- 各年末资金:€10,000、€13,000、€11,700、€16,000、€24,000。回撤序列:峰值为 10,000;13,000;13,000;16,000;24,000 → 回撤为 0;0;11,700 / 13,000 − 1 = −10.0%;0;0。最大回撤 −10%,水下时间约一年。
- 在线性坐标轴上,最后一段(+€8,000)主导了整个画面;在对数坐标轴上,+30%、−10%、+36.8%、+50% 这几步以其真实的相对大小显示,−10% 的那一年也清晰可见,而不会被压平。
- 日收益率:均值 0.06%,标准差 1.1%,超额峰度 4.2。在具有该标准差的正态分布下,−3.3% 的一天(三个标准差)大约每 740 天才出现一次;而直方图显示在 1,000 天中出现了七次。这一差距就是肥尾,也是止损可能被跳空越过的原因(第 8 课)。
- 导出:
figsize=(8, 5)、dpi=200得到 1,600 × 1,000 像素,足以用于幻灯片;同一图形保存为 SVG 后与分辨率无关,文件通常小于 100 kB。
常见错误
练习活动
在纸上完成:(a) 一条资金曲线依次为 20,000 → 26,000 → 19,500 → 28,000(以欧元计,四个点)。计算每个点的回撤。(b) 你想要一张在 100 DPI 下恰好为 1,200 × 800 像素的图形;应传入什么 figsize?(c) 某策略 R 倍数直方图的最高柱位于 +0.2R 到 +0.4R 之间,而一条细长的左尾延伸到 −6R。用一句话说明这种形态暗示了该策略的什么特点,以及夏普比率无法显示的风险。
显示答案
(a) 峰值:20,000;26,000;26,000;28,000。回撤:0;0;19,500 / 26,000 − 1 = −25.0%;0。
(b) figsize = (1,200 / 100, 800 / 100) = (12, 8) 英寸。
(c) 大量小额盈利加上罕见的巨额亏损,是一个出售风险的策略(例如卖出期权或摊低成本加仓)的典型特征;夏普比率由均值和标准差构成,会被频繁的小额盈利拉高,从而低估一次 −6R 的日子抹去数月收益的可能性。
局限性:观测值少于大约 100 个的直方图噪声太大,无法判断尾部形状;此时请改为报告实际的最差数值。
要点回顾
- 为价格、指标、资金和回撤面板使用共享 x 轴的
plt.subplots;标注单位。 - 对数刻度的资金曲线能诚实地显示百分比变动;回撤图显示深度和水下时间。
- 收益率或 R 倍数的直方图能揭示汇总统计量所掩盖的肥尾和不对称性。
- 像素 = 英寸 × DPI;报告优先使用 SVG 或 PDF,并在循环中关闭图形。
下一步
第 11 课从单一品种扩展到多个品种:相关系数矩阵和协方差矩阵、由权重计算投资组合波动率,以及在 pandas 中进行再平衡模拟。
自测
回答问题后核对答案。你可以随意重试多次。
供您自行复习的自动自测。您的答案在浏览器中核对,我们不会查看或评估。这不是考试,也不是资格证书。
您的进度
“已完成”仅表示您将本课标记为已学完,并不是对您理解程度的测试。 学习进度和笔记仅保存在此设备的此浏览器中,而非账户中。