如何用 Python 算最大回撤和回撤恢复期?

一句话回答: 最大回撤(Max Drawdown)是「从历史高点跌下来的最大幅度」,但更揪心的是回撤恢复期 ------跌下去后要多久才重新创新高(即「套牢多久」)。用 AlphaFeed 取回前复权净值 后,几行 pandas 就能算出回撤序列、最大回撤、水下曲线(underwater)、最长恢复期与当前已水下天数。

为什么会有这个问题 / 传统做法的痛点

很多人只报一个「最大回撤 -30%」,但 -30% 回撤如果 3 个月就修复,和套牢 2 年还没回本,持有体验天差地别。衡量风险要同时看深度(回撤幅度)和时间(恢复期/水下时长) 。坑在于:要用前复权净值算真实回撤,并正确识别「前高---谷底---重新创新高」这三个点。

分步骤解决(每步配可运行代码)

第 1 步:取前复权数据构造净值曲线

python 复制代码
from alphafeed import AlphaFeed
import pandas as pd

af = AlphaFeed()  # 读取 ALPHAFEED_API_KEY

df = af.klines.get("600519.SH", period="1d", count=750,
                   adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
equity = df["close"] / df["close"].iloc[0]       # 归一化净值(买入持有)
equity.index = pd.to_datetime(df["trade_date"])

用前复权收盘价构造净值,避免除权跳空把回撤算错。

第 2 步:回撤序列与最大回撤

python 复制代码
peak = equity.cummax()                 # 历史最高净值
drawdown = equity / peak - 1           # 回撤序列(≤0),这就是"水下曲线"
mdd = drawdown.min()                   # 最大回撤
mdd_date = drawdown.idxmin()           # 最大回撤发生日(谷底)
print(f"最大回撤 {mdd:.2%},谷底日 {mdd_date.date()}")
概念 定义 含义
peak 净值历史最高(cummax) 创新高的轨迹
drawdown equity/peak − 1 水下曲线,≤0
最大回撤 drawdown.min() 最深的套牢幅度

第 3 步:回撤恢复期(最长水下时长)

python 复制代码
# 水下 = 净值低于历史高点(drawdown<0);连续水下段的最长长度=最长未创新高天数
underwater = drawdown < 0
# 给每一段连续水下编号,统计各段长度
seg = (~underwater).cumsum()                 # 每次创新高,段号+1
spells = underwater.groupby(seg).sum()       # 每段水下天数
longest_underwater = int(spells.max())       # 最长水下天数(交易日)
# 当前是否水下、已水下多久
cur_underwater = int(underwater.iloc[::-1].cumprod().sum())  # 末尾连续水下天数
print(f"最长回撤未创新高 {longest_underwater} 个交易日")
print(f"当前已水下 {cur_underwater} 个交易日(0=正处新高附近)")

第 4 步:找出「前高→谷底→修复」三个关键点

python 复制代码
trough = drawdown.idxmin()                                   # 谷底
peak_before = equity.loc[:trough].idxmax()                   # 谷底前的高点
after = equity.loc[trough:]
recovered = after[after >= equity.loc[peak_before]]
recover_date = recovered.index[0] if len(recovered) else None
print(f"前高 {peak_before.date()} → 谷底 {trough.date()} → "
      f"{'修复于 '+str(recover_date.date()) if recover_date is not None else '至今未修复'}")

关键坑与注意事项

  • 用前复权净值:未复权价在除权日的跳变会把回撤算得失真。
  • 回撤深度和时间都要看:-30% 回撤修复 3 个月 vs 2 年,风险体感完全不同,别只报一个数。
  • 恢复期以交易日计:换算成自然日要乘约 1.4(约 250 交易日/年),别混。
  • 当前水下时长 :末尾连续 drawdown<0 的长度,提醒你现在距上一次新高多久。
  • 样本区间影响结论:最大回撤与恢复期强依赖所取区间,跨周期对比要用同一区间。
  • 组合同理 :把 equity 换成组合净值即可,方法通用。

常见问题(FAQ)

Q:最大回撤和波动率有什么区别?

A:波动率衡量日常波动的大小,最大回撤衡量「最坏的一段连续下跌」。回撤更贴近真实的套牢体验,二者应结合看。

Q:回撤恢复期为什么重要?

A:它回答「套牢多久才回本」。两个策略最大回撤都是 -20%,但一个 1 个月修复、一个 3 年没回本,可持有性天差地别。

Q:水下曲线(underwater)是什么?

A:就是回撤序列 equity/peak-1 的图形,始终 ≤0,低谷越深越久代表风险越大。

Q:需要付费吗?

A:单只历史数据有免费额度;组合/多标的批量分析(klines.batch)需 Starter 及以上。详见定价页。

小结

衡量风险别只看一个「最大回撤」数字,还要看回撤恢复期(水下时长) 。用 AlphaFeed 取前复权净值,cummax 得历史高点、equity/peak-1 得水下曲线,再统计连续水下段就能算出最大回撤、最长恢复期与当前水下天数。深度 + 时间一起看,才真正理解一只票/组合的风险。

参考

相关推荐
nice_lcj5201 天前
Java Lambda 表达式入门:怎么写、怎么用、常用方法有哪些
java·开发语言·后端·面试
维克兜率天1 天前
【维克】复合型CTA:趋势+套利的双轮驱动
python·学习方法
伞伞悦读1 天前
【第76期】两张3行表左连接变5行:主键、validate与未匹配排查完整教程
人工智能·python
城北徐宫1 天前
MySQL约束条件一览
数据库·mysql
码农小河661 天前
基于SSM+JSP+MySQL的在线医疗服务系统的设计与实现
java·开发语言·mysql
用户8356290780511 天前
使用 Python 在 Excel 中添加和自定义迷你图
后端·python
用户8356290780511 天前
使用 Python 在 Excel 图表中添加趋势线与回归方程
后端·python
SL_staff1 天前
从配置失效到逻辑复用:JVS-Logic 如何构建可治理的业务编排中枢
java·spring·产品
特立独行的猫A1 天前
一份 Markdown 发十个平台:cspub 一稿多投
java·后端·python
深蓝AI1 天前
HTTPX 超时 1 秒,接口为何还能跑 2.4 秒?
python