halo 的技术博客

返回

问题:你那个漂亮的夏普率,是真的吗?#

夏普率(Sharpe Ratio, SR)是量化里最常被引用的一个数:单位风险赚多少超额收益。回测跑出年化 1.5,几乎所有人都会点头——「不错,可以上」。

但这里藏着一个几乎所有人都会栽的坑:夏普率是一个估计量,它有抽样误差,而传统夏普率完全不告诉你这个误差有多大。

三个盲点,个个致命:

  1. 不看样本长度。跑 60 天算出 SR=1.5,和跑 5 年算出 SR=1.5,是完全不同的两回事。前者极可能是运气,后者才有说服力。但夏普率公式里根本没有 N,它对样本长度一无所知。

  2. 不看高阶矩。夏普率的标准误依赖收益分布的形状。如果收益负偏(暴跌集中)、肥尾(极端频繁),那么夏普率估计的标准误会显著变大——同样一个点估计,可信度大打折扣。但传统夏普率假装收益是正态的,对偏度峰度视而不见。

  3. 不给显著性。它永远只吐一个点估计。1.5 就是 1.5,没有置信区间,没有”这个策略真的有效吗”的概率答案。你没法回答老板那句「你有多确定这不是过拟合?」

概率夏普率(Probabilistic Sharpe Ratio, PSR) 由 López de Prado 和 Bailey 提出,一次性补齐这三样。它不再问「夏普率是多少」,而是问一个更本质的问题:

「真实夏普率大于某个基准 SR 的概率是多少?」*

答案是一个 0 到 1 之间的概率。PSR = 0.95 意味着「有 95% 的把握,真实夏普率超过基准」。这才是能拿去做决策的东西。

PSR 公式:把三个盲点一次补上#

PSR 的构造思路是标准的统计推断:估计量 + 标准误 → z 统计量 → 概率。

夏普率估计 SR̂ 的渐近标准误(经偏度峰度校正)是:

σ(SR̂) = sqrt[ (1 − γ₃·SR̂ + (γ₄−3)/4·SR̂²) / (N−1) ]
plaintext

其中 γ₃ 是偏度,γ₄ 是峰度(γ₄−3 即超额峰度),N 是样本长度。然后 PSR 就是:

PSR(SR*) = Φ[ (SR̂ − SR*)·sqrt(N−1) / sqrt(1 − γ₃·SR̂ + (γ₄−3)/4·SR̂²) ]
plaintext

Φ 是标准正态 CDF。拆开看这个公式怎么补三个盲点:

  • sqrt(N−1):样本越长,分子越大,PSR 越接近 1。样本长度直接进公式——这补上盲点 1。
  • −γ₃·SR̂ 项:偏度 γ₃<0(负偏)时,分母变大,PSR 变小。负偏惩罚可信度——这补上盲点 2 的一半。
  • +(γ₄−3)/4·SR̂² 项:超额峰度>0(肥尾)时,分母变大,PSR 变小。肥尾惩罚可信度——补上盲点 2 的另一半。
  • Φ[…]:输出是概率不是点估计。给出显著性——补上盲点 3。

代码就是照抄公式:

注意频率一致:如果用日频收益,SR̂ 和 SR* 都要是日频的(年化 SR=1.0 对应日频 SR≈1.0/√252≈0.063)。混用频率是最常见的错误。

样本长度:同一个夏普率,长短天差地别#

先看 PSR 最直接的教训——样本长度的威力。

同一个年化夏普率,样本越短 PSR 越低。SR=1.0 要跑到约 500 天才勉强过 95% 门槛

三条曲线是三个不同的年化夏普率(0.5 / 1.0 / 1.5),横轴是样本长度,纵轴是 PSR(真实 SR>0 的概率),红线是 95% 置信门槛:

  • 年化 SR=1.5(绿):约 200 天就能过 95% 门槛。强信号,样本需求低。
  • 年化 SR=1.0(蓝):要跑到约 500 天(两年)才勉强达标。注意 252 天(满一年)时 PSR 只有 0.84——连 95% 都没到。
  • 年化 SR=0.5(橙):1000 天都远远够不着 95%。弱信号需要海量样本才能证明它不是零。

这张图应该钉在每个量化研究员的墙上。它说的是:“高夏普”和”可信”是两码事。 一个跑了两个月的 SR=2.0,PSR 可能还不如一个跑了五年的 SR=0.8。样本长度不是细节,它是决定一个数字有没有意义的前提。

偏度峰度:肥尾如何悄悄侵蚀你的置信度#

样本长度是显性的,高阶矩的侵蚀是隐性的——也更阴险,因为你的夏普率点估计根本不变,可信度却在暗中流失。

负偏度和厚峰度都会压低 PSR:同样的夏普率和样本长度,收益越肥尾越难达到显著

固定年化 SR=1.2、N=500,扫偏度峰度:

  • 左图(扫偏度):偏度从 +1 降到 −1.5,PSR 一路下滑。负偏度直接拉低置信度——这符合金融直觉,负偏意味着”平时小赚、偶尔巨亏”,这种收益模式的夏普率天然更不可信,因为它的风险藏在稀有的左尾里,样本没见够那些暴跌前,你根本不知道真实风险有多大。
  • 右图(扫峰度):超额峰度从 0 升到 12,PSR 同样下滑。肥尾抬高标准误、压低 PSR——极端事件越频繁,夏普率估计的抖动越大。

这就是 PSR 相比传统夏普率最深刻的地方:两个策略可以有完全相同的夏普率点估计,但如果一个正态、一个负偏肥尾,它们的可信度天差地别。 传统夏普率把它们判为平手,PSR 会告诉你负偏肥尾那个其实证据薄弱得多。

实务含义很直接:那些”稳定小赚”的策略(卖期权、套息、做市)天生负偏肥尾,它们的夏普率需要更长的样本、更严的 PSR 门槛才能确认。 因为它们的真实风险恰恰藏在还没发生的那次崩盘里。

MinTRL:开发前就知道样本够不够#

PSR 是”事后”判断——策略跑完了算它可不可信。但更实用的是”事前”问:要证明这个夏普率显著,我至少需要多长的回测?

这就是 最小回测长度(Minimum Track Record Length, MinTRL)——把 PSR 公式反解出 N:

MinTRL = 1 + (1 − γ₃·SR̂ + (γ₄−3)/4·SR̂²)·(Z_conf / (SR̂ − SR*))²
plaintext

Z_conf 是目标置信度对应的正态分位数(95% 对应 1.645)。

def min_track_record_length(sr, skew, exkurt, sr_star=0.0, conf=0.95):
    """最小回测长度:要以 conf 置信度证明 SR>sr_star,至少需要多少个观测。
    sr / sr_star 同频。返回观测数(如日频则为交易日数)。"""
    z_conf = stats.norm.ppf(conf)
    if sr <= sr_star:
        return np.inf
    var_term = 1 - skew*sr + exkurt/4.0 * sr**2
    return 1 + var_term * (z_conf / (sr - sr_star))**2

# 年化SR=1.0, 转日频
sr_d = 1.0 / np.sqrt(252)
n_normal = min_track_record_length(sr_d, 0.0, 0.0)       # 正态
n_fat = min_track_record_length(sr_d, -1.0, 8.0)         # 强负偏肥尾
print(f"正态: {n_normal/252:.2f} 年")     # ~2.7 年
print(f"肥尾: {n_fat/252:.2f} 年")        # ~2.9 年
python

要证明 SR>0(95%置信)至少需要多长回测:夏普率越低、收益越肥尾,样本需求越陡峭

图里三条曲线是不同分布形状下的 MinTRL:

  • 年化 SR=1.0 时,正态需要约 2.7 年,强负偏肥尾需要约 2.9 年
  • 年化 SR=0.5 时,需求飙到 10 年以上——弱信号要用极长样本才能证明。
  • 年化 SR=2.0 以上时,需求降到一年以内——强信号很快就能确认。

曲线是陡峭的双曲线形状:夏普率每降一点,样本需求非线性暴涨。 这解释了为什么低夏普策略(哪怕真实有效)在实务中极难被”证明”——你可能等不到统计显著就先怀疑它失效了。

MinTRL 的最大价值是改变研发流程:在你还没开始跑长回测之前,先估个大概夏普率,用 MinTRL 反推需要多少数据。如果你手头只有 1 年数据、目标夏普 0.8,MinTRL 告诉你这根本证不出显著——那就别浪费时间做过度精细的调参,先想办法拿更多数据或找更强的信号。

蒙特卡洛验证:PSR 判据靠谱吗#

公式再漂亮也得验。用蒙特卡洛检验 PSR 判据的经验行为:

5000 条模拟:真实年化SR=0.8、只跑一年时,PSR>95% 的通过率仅约 20%——绝大多数样本无法达到显著

设定真实年化 SR=0.8、N=252(一年),生成 5000 条独立收益序列,每条算它的样本夏普率和 PSR。散点图横轴是样本估计的年化夏普率、纵轴是该样本的 PSR:

  • 样本夏普率横向散布很宽——从负值一直到 2.0 以上。同一个真实 SR=0.8,一年样本能”抽”出千奇百怪的估计值。 这本身就是”短样本不可信”的铁证。
  • 只有约 20% 的样本 PSR 超过 95% 门槛。也就是说,即便真实策略确实有 SR=0.8,用一年数据去检验,八成的时候你都无法在 95% 置信度下确认它有效。
  • PSR 随样本夏普率单调上升——估出来越高,越可能过门槛,符合预期。

这个 20% 通过率说明了 PSR 判据的保守性是合理的:它宁可放过真策略(假阴性),也不轻易把运气认成 alpha(假阳性)。在量化里,把过拟合当成 alpha 上实盘的代价,远大于错过一个真策略——PSR 的门槛设计正是这个取舍。

A. 实现细节#

  • 频率一致:SR̂ 和基准 SR* 必须同频。本文日频演示,年化 SR 除以 √252 转日频。混频是最常见错误,会让 PSR 完全失真。
  • 偏度峰度:用 scipy stats.skewstats.kurtosis(后者默认返回超额峰度,公式里的 (γ₄−3) 直接用它)。
  • 基准 SR*:默认设 0(问”真实 SR 是否 > 0”)。也可设为竞品策略或无风险基准的夏普率,做相对比较。
  • MinTRL 单位:返回观测数,日频则为交易日数,除以 252 转年。
  • 蒙卡设定:单位波动率下 μ=SR,故直接用 normal(sr_daily, 1.0, N) 生成,样本夏普率 = 均值/标准差。

B. 已知偏差#

  • 渐近近似:PSR 的标准误公式是大样本渐近结果,在极短样本(N<100)下本身就不准——讽刺的是,恰恰是短样本最需要 PSR,而它此时也最不可靠。
  • 高阶矩估计误差:PSR 依赖偏度峰度,而这两个矩在短样本下噪声极大(峰度尤甚)。估歪的高阶矩会让 PSR 跟着歪,这是”用有误差的量去校正另一个量”的固有代价。
  • 独立同分布假设:公式假设收益 i.i.d.。真实收益有自相关(尤其是低频再平衡、有杠杆或路径依赖的策略),会让有效样本量小于名义 N,PSR 因此偏乐观。
  • 只管一个夏普率:PSR 检验单个策略的显著性,但如果你试了 100 个策略挑出最好的那个,PSR 不足以防过拟合——那需要 Deflated Sharpe Ratio(DSR)把多重检验的选择偏差也扣掉。

C. 结果解读#

  1. “高夏普”必须配”够长样本”才有意义:这是 PSR 最硬核的教训。年化 SR=1.0 跑满一年的 PSR 只有 0.84——连显著都算不上。任何只报夏普率不报样本长度和 PSR 的回测,都应该被打个问号。

  2. 负偏肥尾策略要过更高的门槛:卖期权、套息、做市这类”稳定小赚”策略天然负偏肥尾,PSR 会系统性压低它们的可信度——这不是 bug 是 feature。它们的真实风险藏在还没发生的崩盘里,本就该用更长样本、更严标准来确认。

  3. MinTRL 应该前置到研发流程:与其跑完长回测再发现样本不够,不如开发前用 MinTRL 估算数据需求。低夏普目标(<0.5)的 MinTRL 动辄十年以上——这直接告诉你该去找更强的信号,而不是在弱信号上过度调参。

  4. PSR 是防自欺的第一道闸,但不是最后一道:它解决”单个策略是否显著”,不解决”从一堆策略里挑最好那个”的选择偏差。真要防过拟合,PSR 之后还得上 Deflated Sharpe Ratio 和组合式净化交叉验证(CPCV)。把 PSR 当成必要非充分条件——过不了 PSR 的策略直接毙掉,过了的还要继续拷问。

一句话总结:概率夏普率把”夏普率是多少”换成”夏普率真的大于零的概率是多少”,一次补齐样本长度、高阶矩、显著性三个盲点——而它配套的 MinTRL 让你在开发前就知道,手里的数据到底够不够证明你的策略不是运气。

概率夏普率 PSR:给你的夏普率算出一个『真的大于零』的置信度
https://blog.halo26812.eu.org/blog/probabilistic-sharpe-ratio
Author halo
Published at 2026年7月27日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨