halo 的技术博客

返回

你写过均值回复策略吗?很可能写过——布林带、价格偏离均线超过 N 个标准差就反向、z-score 回零就平仓。这类策略有个没人说出口的隐含假设:价格是随机游走,偏离用普通收益率(一阶差分)白化就够了。

这个假设在大部分教科书里是真的,在真实市场里却经常不成立。真实价格有「记忆」:有时黏在均值附近反复拉扯(适合均值回复),有时顺着趋势一路走(硬做均值回复就是送钱)。决定这两种性格的,是一个叫 Hurst 指数 H 的量。本文用分数布朗运动(fBm) 把这套关系从公式到数字完整跑通,并给出「先估 H、再按 D=H+0.5 做分数差分」的自适应均值回复写法。

一、问题:均值回复的隐藏假设是「H=0.5」#

经典均值回复对价格 PtP_t 做的第一件事,是算偏离:

zt=PtMA(P)σ(PMA(P))z_t = \frac{P_t - \text{MA}(P)}{\sigma(P - \text{MA}(P))}

这里 σ\sigma 用的是「价格减均线的残差」的标准差,而残差本质是价格的增量(近似收益率)。策略能成立,要求这个增量是白噪声——也就是「今天偏离多少」和「昨天偏离多少」无关

但增量白噪声,等价于价格 PtP_tI(1) 随机游走,等价于 Hurst 指数 H=0.5H=0.5。现实是:

  • H<0.5H < 0.5:增量反持久,涨了更可能回落,价格比随机游走更「黏」在均值——均值回复 regime;
  • H>0.5H > 0.5:增量持久,涨了更可能接着涨,价格比随机游走更「顺」——趋势 regime;
  • H=0.5H = 0.5:标准随机游走。

所以,用「普通收益率白化」去做均值回复,等于假定 H=0.5H=0.5——在 H0.5H \ne 0.5 时,你要么把真均值回复的偏离给过度差分打碎了(留不住记忆),要么把趋势的顺向记忆当成「均值回复的偏离」去反向挨打。

二、分数布朗运动:用 H 直接造出三种记忆#

分数布朗运动 BtHB_t^H 是一个由 H(0,1)H \in (0,1) 控制的连续过程,它的**增量(差分)**有明确的记忆结构。我们用 Davies-Harte 谱方法生成 fGn(分数高斯噪声,即 fBm 的增量),再累加得到 fBm 价格路径:

跑出来的三条路径肉眼就能区分:

路径增量估计 H(R/S)增量 lag-1 自相关
H=0.30(均值回复)0.454−0.223
H=0.50(随机)0.565−0.013
H=0.70(趋势)0.724+0.280

H=0.30H=0.30 的增量 lag-1 自相关是负的(反持久,涨了易回落);H=0.70H=0.70 的是正的(持久,涨了易续涨)。把 HH 从 0.3 拉到 0.7,增量自相关从 −0.22 平滑翻到 +0.28——这正是「均值回复 ↔ 趋势」的数学开关。

分数布朗运动三态:H<0.5 黏均值,H>0.5 顺趋势

三、H 怎么估?——坑:必须在「增量」上估,不是价格#

估 Hurst 最经典的是 R/S 重标极差:在多个窗口 ww 上算 (R/S)(R/S),对 logwlog(R/S)\log w \sim \log(R/S) 做 OLS,斜率就是 HH。但有个几乎人人会踩的坑:

R/S 估的是「增量的记忆」。你必须在 PtP_t 的增量(收益)上估 HH,而不是在价格 PtP_t 水平上估。

如果你直接把 fBm 价格水平丢进 R/S,斜率会恒等于约 1.03——因为价格水平的积分阶数是 D=H+0.51D=H+0.5 \approx 1,R/S 把 DD 当成了 HH,三类 regime 全糊成一坨。只有对增量估,三类才干净分离:

H(真实)价格水平 R/S(错误)增量 R/S(正确)
0.301.032(恒≈1)0.443
0.501.041(恒≈1)0.590
0.701.034(恒≈1)0.730

R/S 重标极差:对「增量(收益)」估计才得真 Hurst

上图三条增量序列的 log-log 拟合斜率分别是 0.44 / 0.59 / 0.73,和真实 HH 干净对应。记住:H 刻画的是增量的记忆,不是价格的记忆——这是全文第一个也是最容易致命的陷阱。

四、重写均值回复:分数差分 D = H + 0.5#

既然价格 Pt=BtHP_t = B_t^HI(D) 过程,积分阶数 D=H+0.5D = H + 0.5,那么:

  • H=0.5D=1.0H=0.5 \to D=1.0:普通一阶差分恰好白化,常规收益率就够;
  • H<0.5D<1.0H<0.5 \to D<1.0:只需分数阶差分(留一部分记忆)即可平稳——过度用一阶差分反而把有用的均值回复记忆打碎;
  • H>0.5D>1.0H>0.5 \to D>1.0:需要超过一阶差分才能去掉趋势——否则趋势的顺向记忆残留,被误判成「均值回复偏离」。

于是「重写均值回复」的核心动作是:(1L)D(1-L)^D 分数差分把价格变成平稳残差,残差的 DD 由数据里估出的 HH 决定。López de Prado 的二项式权重写法:

def frac_diff_weights(D, K=120):
    """(1-L)^D 的二项式展开权重, 截断到 K 项"""
    w = np.zeros(K + 1); w[0] = 1.0
    for k in range(1, K + 1):
        w[k] = -w[k - 1] * (D - k + 1) / k
    return w

def apply_frac_diff(x, D, K=120):
    w = frac_diff_weights(D, K)
    xp = np.concatenate([np.full(K, x[0]), x])   # 前视填充, 减小边界偏差
    return np.convolve(xp, w, mode="full")[K:K + len(x)]
python

H=0.30H=0.30 的价格,用 D=0.30+0.5=0.80D = 0.30 + 0.5 = 0.80 做分数差分,和普通一阶差分(D=1D=1)对比:

残差类型lag-1 自相关解读
普通差分 D=1D=1−0.245残留反持久记忆(被过度差分)
分数差分 D=0.8D=0.8−0.088更接近白化,记忆被合理保留利用

普通一阶差分把 H<0.5H<0.5 的反持久记忆过度打掉了(自相关 −0.245),而分数差分 D=0.8D=0.8 把残留记忆压到 −0.088,既平稳又没浪费那点均值回复信息。

H=0.30 价格:D=0.8 分数差分比普通差分 D=1 更干净

五、蒙特卡洛:分数差分是「正确白化」,不是「稳赚」#

我必须诚实:不存在一个「分数差分均值回复稳赢朴素」的 P&L 幻象。均值回复在均值回复 regime 本来就该赚,在趋势 regime 本来就该亏,换不换差分都改不了这个事实。分数差分的真实价值是正确白化——把残差里不该有的记忆去掉,让 z-score 的信号含义是干净的。

蒙特卡洛 400 条路径(每个 regime),比较「朴素:对价格偏离做 z-score」vs「分数:对 D=H+0.5D=H+0.5 残差做 z-score」:

regime平均估出 D朴素残差 lag-1 自相关分数残差 lag-1 自相关朴素信号数/路径分数信号数/路径
H=0.30(均值回复)0.930.696−0.20115.325.0
H=0.70(趋势)1.230.9630.0357.024.7

两个关键结论:

  1. 趋势 regime 里,朴素方法的残差 lag-1 自相关高达 0.96——也就是说,你对趋势价格做的「均值回复偏离」,根本不是白噪声,而是趋势的顺向残余。你以为在抓偏离,其实在逆着趋势加仓。分数差分把这项压到 0.04,趋势记忆被移除,残差才是真正可用来判「是否过度偏离」的序列。
  2. 分数差分让 z-score 触发更频繁(信号更多),但每一次触发的含义是干净的:它不再把趋势的「还没走完」误判成「已经偏离过头」。

再扫一遍 DD 的取值会更直观——残差 lag-1 自相关随 DD 穿过 0 的拐点,正好落在 DH+0.5D \approx H+0.5:

分数差分扫描:D=H+0.5 把残差记忆压到 0 (正确白化)

  • H=0.30H=0.30:残差自相关在 D0.7D\approx0.7 穿过 0;
  • H=0.70H=0.70:残差自相关在 D1.3D\approx1.3 穿过 0。

朴素差分固定 D=1D=1 落在两者中间:对均值回复 regime 过度差分,对趋势 regime 差分不足——两头不讨好

六、把框架用起来:Hurst-自适应均值回复信号#

把上面三块拼起来,就是一套 regime 自适应的均值回复写法:

实务上,这个框架给你两件事:

  1. 差分阶数 D 自适应:趋势市自动用 D>1D>1(多差一点去趋势),均值回复市用 D<1D<1(少差一点留记忆);
  2. 白化后的信号更可信:你做均值回复时,真正在赚「偏离回归」的钱,而不是在偷偷赌趋势反转。

但请把它当成特征工程/白化工具,而不是「自动选 regime + 自动印钱」的黑箱——下一节六类陷阱会告诉你为什么。

七、六类真实陷阱(实战必看)#

  1. H 必须在增量上估:直接对价格水平做 R/S,斜率恒≈1,三类 regime 完全分不开,后面全错。这是本文第一个、也是最致命的坑。
  2. D=H+0.5 是 fBm 的理论关系,真实资产未必精确:股票日收益常 H0.5H\approx0.5 附近,但高频/商品可能 HH 随周期漂移。不要硬信一个固定的 HH,用滚动窗口估。
  3. 分数差分的边界瞬态:(1L)D(1-L)^D 在序列开头 K 个点是 NaN 或不稳,必须丢弃前 K 个(本文丢 150 点)再算 z-score,否则开头一堆假信号。
  4. K 截断 vs 记忆长度:权重截断 K 太小会漏掉长记忆(尾巴权重没算完),太大边界瞬态更长。本文 K=120 在 T=600~700 上平衡尚可,更长序列要调大。
  5. 白化 ≠ 赚钱:分数差分只保证残差「记忆被移除、可解释为偏离」,不保证偏离一定会回归。趋势 regime 里即使白化干净,均值回复照样亏——它帮你看清,不帮你翻盘。
  6. R/S 有限样本偏差:小样本下随机游走常被估成略偏低(<0.5<0.5),趋势/回复的边界会模糊。本文用 600 点、400 路径平均来压制,实盘单标的单窗口估计误差很大,务必配置信区间。

八、小结#

经典均值回复「用普通收益率白化偏离」,隐式假设了 Hurst H=0.5H=0.5。真实价格有记忆: H<0.5H<0.5 黏均值、H>0.5H>0.5 顺趋势。用分数布朗运动复现这套关系后,正确做法是先估 H、再按 D=H+0.5D=H+0.5 做分数差分——本文实证里,R/S 在增量上把三类 regime 干净分离(0.44/0.59/0.73), D=0.8D=0.8 分数差分把残留记忆从 −0.245 压到 −0.088,蒙特卡洛证明它把趋势 regime 的残差 lag-1 自相关从 0.96 降到 0.04(正确白化),从而不再把趋势误当均值回复。下一篇我们换个角度:均值回复赚的是「价格回归」,但价格回归往往伴随动能衰竭——用 RSI 背离,你能更早抓住趋势的尽头。


代码与图表均由 Python(numpy + matplotlib)真实计算,数据为带结构的可复现合成 fBm 面板(种子 20260718),非占位符。H 严格在增量上用 R/S 估计,D=H+0.5 分数差分白化,策略对比仅展示白化质量(残差自相关与信号数),不夸大 P&L。

分数布朗运动交易:用 Hurst 依赖重写均值回复策略
https://blog.halo26812.eu.org/blog/fractional-brownian-trading
Author halo
Published at 2026年7月18日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨