回撤恢复期:亏完之后,你还要在水下待多久
有个问题,你可以现在就问自己一遍:如果你的账户从最高点掉下去 30%,你能忍多久?
大多数人回答这个问题的时候,脑子里想的是「30% 我扛得住」。这句话回答的其实是另一个问题——坑有多深。真正会把人熬走的不是深度,是你要在这个坑里待多长时间。掉下去可能只用三个月,爬回原来那个高点,可能要三年。这三年里,你每次打开账户,看到的都是一个比你最好那天低的数字。
坑深是一次性的疼,水下时间是每天都在的疼。前者你看报告的时候会记住,后者你要真的活过一遍才知道。
先说人话:这件事是三段,不是两段
想象你从游泳池边跳下去。整个过程有三个时刻:你离开水面的那一刻、你沉到最深处的那一刻、你重新浮出水面的那一刻。
多数人讲「回撤」——就是账户从历史最高点往下掉的那个幅度——只讲了前两个时刻之间的事:我下去了多深。但你憋不憋得住气,取决于从离开水面到重新浮出来,一共多久。而这段时间里,触底只是中点,不是终点。
放到账户上,同样三个时刻:
- 峰值日:你的账户创下历史新高的那一天。这是水面。
- 谷底日:你的账户跌到最低的那一天。这是最深处。
- 回本日:你的账户重新超过当初那个峰值的那一天。这是你重新露头。
峰值日到谷底日,是下跌期;谷底日到回本日,是恢复期;从峰值日到回本日的整段,业内叫水下时间(underwater duration,字面意思就是「在水面以下待的时长」)。
关键在这儿:这三段的长短完全不成比例。跌下去往往很快——市场恐慌的时候,几周就能把你一年的成果抹掉。爬回来往往很慢,因为爬回来靠的不是恐慌,是一天一天的正常赚钱速度。下跌是事件驱动的,恢复是速度驱动的。 这两者压根不是一个量级的时间尺度。
投资层:为什么这个数才是你真正扛不住的那个
一个纯算术的例子(假设值,仅为演示)
坑深 30%,意味着你的钱只剩 0.7。要回到 1,需要涨 1÷0.7 − 1 ≈ 42.9%。这个「亏得越深、回本要求越离谱」的算术,亏损的不对称数学 那篇已经讲透了,这里不重复。
我们把它换成时间来看。假设某套方法长期的复利速度是每年 10%(这只是一个用来做算术的假设数字,不是任何预期收益),那么补上 42.9% 这个缺口需要多久?
log(1÷0.7) ÷ log(1.1) ≈ 3.74
接近四年。也就是说,一个「只有 30%」的回撤,在一套年化 10% 的方法上,理论最快也要将近四年才能回到原点——而且这还是假设你从谷底开始就一路顺风、中间不再有任何波折的理想情况。现实里中间还会再有小坑,实际时间只会更长。
这个换算值得你自己拿计算器再按一遍,因为它会改变你对回撤的感受方式。30% 听起来是个可以忍的数字,「四年不创新高」听起来就完全是另一回事了。懒得手按的话,回撤恢复期测算器填两个数就出年数,把回撤幅度往上挪几档,你会看到那条曲线抬头有多陡。
它伤的是你的耐心,不是你的本金
本金那部分是可逆的——只要方法还有效,钱总会回来。真正不可逆的是三样东西:
时间。 那四年的机会成本没有人赔给你。你的钱被卡在一个坑里,什么也没做成。
信心。 人对「一直不好转」的耐受度远低于对「亏一大笔」的耐受度。亏 30% 那天你会难受一周,然后咬牙坚持。可当第 18 个月还没回本、账户曲线躺平在半山腰不动的时候,「这套方法是不是已经失效了」这个念头会一天比一天大声。绝大多数人放弃一套方法,不是在最痛的那一天,而是在不痛不痒拖了很久的某个平常日子。
行为。 水下待得越久,你越容易做那件最糟糕的事——在接近谷底的位置改规则、加杠杆想快点回本、或者干脆换一套「看起来更好」的方法。而换方法这个动作本身,等于把你已经忍受完的那段痛苦一笔勾销,重新从水面下开始计时。
所以,如果只允许你看一个数字来判断「我这个人能不能配这套方法」,看恢复期比看最大回撤有用得多。深度考验的是你的账户,长度考验的是你这个人。
最深的坑,往往不是最久的坑
这是很多人没意识到的一点:最大回撤和最长水下时间常常不是同一段。
一次剧烈的暴跌,可能跌 35%,但因为跌得急、市场反弹也急,八个月就回本了。而另一段温吞的阴跌,只跌了 18%,却是三年半的慢慢磨——磨到你都忘了自己还在水下。
如果你的报告只写了「最大回撤 35%」,那次让你真正熬不住的 18% 慢性回撤,在报告里根本不会露面。这就是下面要说的系统层问题的根源。
系统层:在真实系统里它长什么样(以及它其实没被算出来)
先把回撤序列这件事讲清楚。几乎所有系统的算法都是同一个骨架——当前净值 ÷ 历史最高净值 − 1(「净值」就是你这笔钱现在值多少,通常从 1 起算)。历史最高净值就是那个「只涨不跌的水位线」,专业点叫累计最大值(cummax)。
Vibe-Trading 的风险 X 光模块里,这三行就是全部:
equity = (1.0 + port).cumprod()
peak = equity.cummax()
dd = equity / peak - 1.0
第一行把每天的收益率滚成净值曲线,第二行记住「到今天为止的最高水位」,第三行相减——得到的 dd 就是一条永远小于等于 0 的曲线:等于 0 的日子表示你在水面上(正在创新高),小于 0 的日子表示你在水下,数值就是当天的水深。
把这条曲线画出来,就是「水下图」(underwater plot)。freqtrade 的绘图模块专门给它留了一整行子图,画的正是这条一直吊在零线以下的曲线。这张图为什么值得看?因为它把「深度」和「长度」同时摆在你眼前:坑的深浅看纵轴,坑的宽窄看横轴。而你只看一个「最大回撤 35%」的数字时,横轴那一维被彻底压扁了。
关键发现:三个系统都只算到谷底为止
这才是本篇最想让你知道的事。
freqtrade 的回测报告里有一项叫「Drawdown duration」(回撤持续时间),听名字像是「你在水下待了多久」。但它的计算就一行:
drawdown_duration = drawdown.low_date - drawdown.high_date
high_date 是峰值日,low_date 是谷底日。这个减法算出来的是峰值到谷底——只有下跌那半程。恢复期那半程,压根不在这个数里。
Vibe-Trading 的风险 X 光同样只吐三个字段:最大回撤值、回撤开始日、谷底日。没有回本日。
qlib 更简洁,它的风险分析函数只返回一个标量:
max_drawdown = (cumulative_curve / cumulative_curve.cummax() - 1).min()
一个 .min(),取整条曲线上最深的那一点。连日期都没有,更不用说时长。
结论很清楚:你在主流回测报告上看到的所有回撤数字,默认都不包含「爬回来要多久」这件事。 不是这些工具做得不好,而是「回本日」在工程上是个麻烦的东西——它可能根本不存在(回测结束时你还在水下),也可能一段回撤里套着好几个小反弹,「算不算回本」需要定义。工具选择了回避,把这个判断留给了看图的人。
代价是:如果你只读数字不读图,你会系统性地低估这套方法的痛苦程度。想系统地学怎么读这条曲线,可以看 怎么看一条收益曲线;想知道最大回撤这个数本身的口径问题,看 最大回撤:从数字到体感。
同一条曲线,两种口径两个数
qlib 的风险分析函数里还有个细节值得一提:它支持两种累计方式。「加总」模式下,回撤算的是收益率累加值减去累加值的历史最高;「连乘」模式下,才是上面那个净值除以峰值。
# 加总模式
max_drawdown = (r.cumsum() - r.cumsum().cummax()).min()
# 连乘模式
max_drawdown = (cumulative_curve / cumulative_curve.cummax() - 1).min()
同一条收益序列,这两个式子给出的数不一样,量纲也不一样——前者是收益率的累加差值,后者是百分比。拿两份用不同口径出的报告直接比大小,比的是口径不是策略。这是回测偏差总览里那类「数字对但意义错」的典型。
系统会不会替你「熬不住」
有意思的是,回撤在系统里除了被统计,还会被当成刹车。freqtrade 有个保护插件:在一个回看窗口内,如果回撤超过设定的阈值,就全局停止开新仓,锁定到一个计算出来的时间点为止。
这是机器版的「先歇一歇」。但它有个结构性的盲区值得你留意:判断只在那个回看窗口内做。一段比窗口更长的慢性阴跌,每一个窗口切片看起来都很温和,于是它一次也不会触发。 而前面说过,恰恰是这种慢性回撤最能把人熬走。窗口长度、阈值这些都是可配置项,具体默认值以你手上那个版本的文档为准——但机制上的这个盲区,改参数是改不掉的。
系统还有一件事做得比人好:它同时记录「历史最大回撤」和「当前回撤」。freqtrade 的回撤结果对象里专门有一组 current_ 开头的字段,回答的是「此时此刻我在不在水下、水深多少」。对实盘来说这才是最要紧的那个数——历史最大回撤是一个已经翻篇的故事,当前水深是你现在正站的地方。
失效边界与常见误用
样本量是 1。 一段十年的历史里,「最长恢复期」这个数只有一个观测值。它不是一个统计意义上稳定的估计,而是一次具体经历。换一段历史,这个数可能翻倍。用一个样本量为 1 的数字去规划自己未来的忍耐力,是很危险的。 想看它的分布长什么样,得靠 蒙特卡洛检验 这类办法把它模拟成一个范围,而不是记住一个点。
截尾问题。 如果回测结束的那天你还在水下,这段回撤的恢复期是「未完成」的——真实值只会比报告里的更长,不会更短。很多报告会安静地把这段当成已经结束来算,或者干脆不算。看到回测末尾那条曲线正吊在半空的时候,要额外警惕。
时间轴不一定是日历。 freqtrade 的回撤序列是建在每一笔交易的平仓时刻上的,不是每一个自然日。如果某段时间一笔交易都没成交,这段序列上就没有点。序列上的「一步」和现实里的「一天」不是一回事——而你熬的是自然日,不是交易笔数。
把恢复期当承诺。 「历史上最长熬了三年就回本了」这句话,隐含了一个不成立的前提:坑总会被爬出来。历史数据里的每一次回撤都回本了,只是因为没回本的那些方法已经不在你的样本里了——这是幸存者偏差在时间维度上的版本。一套正在失效的方法,它的水下时间可以是无穷。
只看最大值,忽略占比。 除了「最长那一次」,还有个更能说明常态的数:整段历史里你有多大比例的日子是在水面以下的。有些方法最长恢复期不吓人,但常年七八成时间在水下——曲线一直在半山腰蹭,只是每次都不深。这种方法的持有体验,比一次深坑但大部分时间在创新高的方法糟糕得多。
用它反推该不该割。 恢复期是一个描述性的统计量,它告诉你历史上发生过什么,不告诉你现在这一次会不会一样。拿它当止损或加仓的依据,是把统计描述当成了预测。
「最大回撤 30%」和「三年不创新高」是同一件事的两种说法,但对你的心理是两种完全不同的东西。 报告只会告诉你前一种。评估一套方法之前,把它的水下图调出来看一眼横轴——最长的那一段有多宽。如果那个宽度你想想都觉得难熬,那这套方法就不适合你,跟它的年化数字多好看没有关系。
小结
- 一次回撤是三个时刻两段路:峰值日 → 谷底日(下跌期)、谷底日 → 回本日(恢复期);两段加起来才是水下时间。
- 下跌是事件驱动的,往往很快;恢复是速度驱动的,往往很慢。所以恢复期通常远长于下跌期。
- 主流系统默认不算恢复期:freqtrade 报告里的「回撤持续时间」是峰值到谷底、Vibe-Trading 只给到谷底日、qlib 只给一个标量最深值。回本日要你自己从水下图上读。
- 最深的坑不一定是最久的坑。让人放弃的经常是那段「只跌了一点点却拖了很久」的慢性回撤,而它在「最大回撤」这个数里根本不出现。
- 这个数的样本量通常只有 1,还可能被回测末尾截断;把它当经历参考可以,当规划依据不行。
一句话记住这篇:看一套方法,别只问它最惨的时候亏了多少,要问它最惨的时候,你要难受多久。
本文所引源码均来自上述快照版本,是阅读代码得出的机制说明,不是运行结果;具体行为请以你手上那个版本为准。文中所有百分比与年数均为演示算术用的假设值,不代表任何预期收益。回 量化与 AI 投研概念库 看其它概念各自在说什么;本站内容为投资教育,不构成任何投资建议,边界见 免责声明。
本文为投资教育整理,关键数据与结论请结合下列权威来源验证。
- freqtrade 源码快照 b3404c9(2026-08-18):freqtrade/data/metrics.py、freqtrade/optimize/optimize_reports/optimize_reports.py、freqtrade/plot/plotting.py、freqtrade/plugins/protections/max_drawdown_protection.py ↗
- qlib 源码快照 79633dd(2026-07-23):qlib/contrib/evaluate.py ↗
- Vibe-Trading 源码快照 3a752d5(2026-08-04):agent/backtest/risk_xray.py、agent/backtest/engines/base.py ↗