最直接计算累计收益率的方法是用 np.cumprod(1 + returns) - 1,而非 pct_change().cumsum();最大回撤需基于滚动最高值计算,即 (price - rolling_max) / rolling_max 的最小值;使用 empyrical 库时须传入净值序列而非收益率。

用 numpy 和 pandas 计算累计收益率最直接
累计收益率本质是价格序列的相对变化累积,不是简单求和。直接对日收益率用 np.cumprod(1 + returns) - 1 是最稳妥的做法,避免复利计算误差。
常见错误是误用 df['price'].pct_change().cumsum() —— 这算的是“算术累计”,忽略复利效应,涨跌不对称时偏差明显(比如先跌20%再涨20%,实际没回本,但算术累加会显示0)。
-
returns = df['price'].pct_change().dropna()得到日收益率 -
cum_ret = (1 + returns).cumprod() - 1才是正确累计值 - 若数据含分红或拆股,必须用复权价,否则
pct_change()结果失真
最大回撤必须基于净值曲线,不能只看价格高点低点
最大回撤(Max Drawdown)定义为:从任意历史高点到后续最低点的跌幅最大值。它依赖路径,不是静态极值差。直接用 df['price'].max() - df['price'].min() 完全错误。
标准做法是维护一个滚动最高值,逐点计算当前回撤:
rolling_max = df['price'].expanding().max() drawdown = (df['price'] - rolling_max) / rolling_max max_dd = drawdown.min()
注意三点:
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 必须用
expanding().max(),不是rolling(window=...).max() - 如果输入是收益率序列而非价格,得先转成净值曲线:
equity_curve = (1 + returns).cumprod() - 某些库(如
empyrical)封装了max_drawdown(),但内部仍是上述逻辑,传入净值序列而非收益率
处理空值、非交易日和频率不一致的数据
金融数据常有缺失或非连续日期(如节假日),直接计算会导致 NaN 污染或回撤低估。
-
pct_change()对首行返回NaN,务必用.dropna()清理,否则cumprod()会全为NaN - 日频数据混入周末,需先用
df = df.asfreq('D').ffill()或按实际交易日重采样 - 不同资产频率不同(如期货日线 vs 债券周度),统一前先确认是否需插值——多数回撤分析要求原始频率,插值会平滑真实波动
用 empyrical 库省事但要注意输入类型
第三方库能快速验证,但接口容易用错。比如 empyrical.max_drawdown() 接收的是净值序列(从1开始的累计曲线),不是收益率序列。
典型错误写法:empyrical.max_drawdown(returns) → 返回 0.0 或负值,因为函数把收益率当净值用了。
- 正确用法:
equity = (1 + returns).cumprod(); empyrical.max_drawdown(equity) -
empyrical.cumulative_returns(returns)才是直接算累计收益率,等价于手动(1+returns).cumprod()-1 - 该库不处理空值,调用前仍需
returns = returns.dropna()
最大回撤对局部高点极其敏感,哪怕一个异常峰值(如财报暴雷前的假突破)都会拉高分母,压低回撤值——检查 rolling_max 曲线是否合理,比依赖单一数字更重要。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










