halo 的技术博客

返回

先问一个看似简单、其实很多人答错的问题:你砸出一笔买单,把价格推高了 5bp,一秒钟之后,这 5bp 还在吗?

有三种流行的答案,对应三种世界观:

  • 永久派:还在。你的成交泄露了信息,市场永久性地重新定价了,5bp 不会退回去。
  • 瞬时派:没了。你吃穿了几档订单簿,但挂单立刻补回来,价格弹回原点,冲击是”一锤子”的,成交完就结束。
  • 传播子派:还剩一部分,而且在慢慢消退。冲击既不是永久的也不是瞬时的,它有余韵——推高之后按某个衰减节奏一点点退去,可能几秒、几分钟、甚至更久才完全消化。

结论先放这:真实市场是第三种。 Jean-Philippe Bouchaud 和合作者在 2004 年前后提出的传播子模型(Propagator Model)把这个”余韵”写成一个衰减核 G(t),整条价格轨迹就是所有历史成交冲击按 G(t) 衰减后的叠加。这个模型不只是拟合得更好——它背后有一个深刻到近乎必然的逻辑:如果冲击不是这样衰减的,市场就会遍地是无风险套利。 本文从零把这套逻辑和代码走一遍。

冲击核 G(t):一笔成交的余韵长什么样#

传播子模型的核心对象是冲击核(Impact Kernel) G(t):一笔成交发生后,经过 t 个事件(或 t 单位时间),它对当前价格还残留多少冲击。

三种世界观对应三种 G(t) 的形状:

  • 永久冲击:G(t) = 常数。冲击永不消退。
  • 瞬时冲击:G(t) 在 t>0 时立刻归零。
  • 传播子:G(t) 缓慢衰减。经验研究发现它接近一个幂律 G(t) ~ t^(-β),β 通常在 0.5 附近——衰减得非常慢,有长长的尾巴。
import numpy as np

lag = np.arange(1, 200)
G_power = 1.0 / (lag ** 0.5)         # 幂律衰减(传播子)β=0.5
G_exp   = np.exp(-lag / 15.0)        # 指数衰减(对照)
G_perm  = np.ones_like(lag) * 0.35   # 永久冲击(不衰减)
python

冲击核 G(t):幂律衰减 vs 指数衰减 vs 永久冲击

左图是三种核在普通坐标下的样子:永久冲击是一条水平线;指数衰减在 t≈75 就基本归零;而幂律衰减(红线)虽然一开始下降快,但拖着一条长尾——到 t=200 还残留 0.07 的冲击没消完。右图用 log-log 坐标揭示本质区别:幂律在 log-log 上是一条直线(这是幂律的指纹),而指数衰减在 log-log 上迅速向下坠落。

这个”长尾”是全文的关键。幂律核意味着:一笔很久以前的大单,它的冲击到现在还没完全消化。市场的价格,是被一长串历史成交的余韵共同撑着的。

价格是历史冲击的叠加#

有了核 G(t),传播子模型的价格公式就写出来了。t 时刻的价格 = 基准价 + 过去每一笔成交的冲击按各自的衰减程度加权求和:

p(t)=p0+s<tG(ts)f(vs)εsp(t) = p_0 + \sum_{s<t} G(t-s)\, f(v_s)\, \varepsilon_s

其中 ε_s 是第 s 笔成交的方向(买 +1 / 卖 −1),v_s 是成交量,f(v) 是冲击强度函数(经验上是的,常取 f(v)~√v,即成交量翻倍冲击并不翻倍)。

N = 300
signs = rng.choice([-1, 1], size=N)         # 每笔成交方向
vols  = rng.gamma(2.0, 1.0, size=N)
f_v   = np.sqrt(vols)                        # 冲击强度 ~ √成交量(凹)

price = np.zeros(N)
for t in range(N):
    contrib = 0.0
    for s in range(t):                       # 累加所有历史成交的余韵
        contrib += (1.0/np.sqrt(t-s)) * f_v[s] * signs[s]
    price[t] = 100.0 + 1e-4 * 100 * contrib
python

传播子模型:价格是历史成交冲击的叠加

蓝线是完整的传播子价格——它看起来就像一条正常的随机波动价格序列,但它的每一个点都是过去所有成交冲击叠加的结果。橙线单独拎出了某一笔大单(红色竖线标注)的冲击轨迹:它在成交那一刻把价格顶上去,然后按幂律核缓慢衰减,几十笔成交之后还没完全退回去。

这就是传播子模型的世界观:市场价格不是一堆独立跳变的拼接,而是无数笔成交冲击的余韵在时间轴上层层叠加、互相干涉的结果。 你现在看到的价格,一半是新成交推的,一半是老成交还没消化完的余温。

为什么核必须衰减:订单流的长记忆#

到这里你可能会问:凭什么说冲击一定是”缓慢衰减”这种形状?为什么不能是永久的,或瞬时的?

答案藏在一个惊人的经验事实里:订单流方向有极强的长记忆。

如果你去统计真实市场里连续成交的方向(买还是卖),你会发现它们远不是独立的——一笔买单后面更可能跟着买单,一笔卖单后面更可能跟着卖单。而且这种正相关衰减得极慢,能一直拖到上百笔成交之后仍然显著(同样是幂律衰减)。原因很实际:大机构的单子拆成几百上千笔小单在几小时内慢慢执行(正是上一篇 TWAP/VWAP 讲的事),所以订单流里充满了同方向的连续片段。

def long_memory_signs(n, p_cont=0.62, seed=1):
    r = np.random.default_rng(seed)
    s = np.zeros(n); s[0] = r.choice([-1, 1])
    for i in range(1, n):
        s[i] = s[i-1] if r.random() < p_cont else -s[i-1]  # 高概率延续上一方向
    return s
python

订单流方向的长记忆与冲击为何必须衰减

左图是成交方向的自相关:红线(真实订单流)从 0.25 起步,缓慢衰减、长时间保持正值——这就是长记忆。灰线(独立订单流)则贴着零线抖动,毫无记忆。

现在关键的矛盾来了,看右图。假设冲击是永久的,那么价格 = 所有历史成交方向的累加。订单流有长记忆(买后面跟买),累加起来就会形成强趋势(橙线一路向上)。而强趋势意味着价格可预测——你看到过去几笔都是买、就知道后面大概率还是买、价格还会涨,于是你抢先买入,无风险套利。这在有效市场里是不可能长期存在的。

传播子的衰减核,正是市场自我修复这个矛盾的机制。 当冲击按幂律衰减时(蓝线),老的买入冲击在慢慢退去,恰好抵消掉订单流长记忆带来的持续推力,最终让价格回到接近(无法预测下一步涨跌)的无套利状态。Bouchaud 团队证明了一个精妙的定量关系:冲击核的衰减速度 β 和订单流自相关的衰减速度 γ 必须满足一个特定的匹配条件(大致是 β ≈ (1−γ)/2),价格才能恰好保持无套利。冲击衰减得太快,价格会均值回复(也可套利);衰减太慢,价格会趋势(也可套利)。只有幂律核以恰到好处的速度衰减,市场才能既允许订单流长记忆存在、又不泄露可套利的价格趋势。

这是传播子模型最深刻的地方:冲击核不是随便拟合出来的经验曲线,它的形状被”市场必须近似无套利”这个约束几乎唯一地钉死了。

从数据反推冲击核:响应函数#

理论说核是幂律衰减的,那怎么从真实成交数据里把它测出来?工具是响应函数(Response Function) R(l):在一笔方向为 ε 的成交之后,隔 l 个事件,价格平均移动了多少(乘上方向对齐):

R(l)=E[(pt+lpt)εt]R(l) = \mathbb{E}\big[(p_{t+l}-p_t)\cdot \varepsilon_t\big]

直觉上,R(l) 度量的是”一笔成交对未来 l 步价格的平均净影响”。我用一段两万笔的模拟传播子数据(含长记忆订单流 + 幂律核 + 独立微观结构噪声)把它反推出来:

def response_function(price, signs, maxl):
    R = []
    for l in range(1, maxl+1):
        dp = price[l:] - price[:-l]
        R.append(np.mean(dp * signs[:-l]))
    return np.array(R)
python

从成交数据反推冲击响应函数 R(l)

蓝点是标定出的响应函数:R(l) 从接近零起步(R(1)≈0.019),随着滞后 l 单调上升,然后逐渐趋平饱和(渐近到约 0.54)。红色虚线是一个饱和曲线拟合作为参照。

这条”先升后趋平”的形状本身就是传播子模型的强证据。它同时否定了另外两种世界观:

  • 如果冲击是瞬时的,R(l) 应该在 l=1 就跳到最大值然后立刻掉回去——不会有这条缓慢爬升的曲线。
  • 如果冲击是永久的,R(l) 会随 l 无限线性增长(趋势)——不会饱和趋平。

先升后平恰恰是传播子的签名:单笔成交的直接冲击(核 G)在衰减,但因为订单流长记忆,后续同方向成交不断跟进补充,两股力量此消彼长,净响应先累积上升、再在衰减和补充达到平衡时趋于饱和。诚实说一句:真实的 R(l) 常带一点 S 形(前段加速),单指数饱和曲线只能近似捕捉大势,精确刻画需要把订单流自相关也建模进去——但”从近零上升到饱和”这个定性结论是稳健的,它就是传播子存在的直接指纹。

已知偏差(一定要说清楚)#

  1. 模型是单一资产、事件时间的简化版。真实的传播子研究要区分事件时间 vs 物理时间、区分主动成交与被动成交、处理买卖价差的离散跳变(bid-ask bounce)。本文用等权事件时间是为了让核的作用干净可见。
  2. 冲击强度函数 f(v)~√v 是示意的。经验上冲击对成交量确实是凹的(著名的”平方根律”也出现在这里),但精确形状因市场、因标的而异,需要用自家成交数据标定。本文的 √v 只是一个合理的凹函数占位。
  3. 无套利匹配条件是渐近结论。β 与订单流自相关指数 γ 的匹配关系(β≈(1−γ)/2)是在幂律、长时间尺度的渐近意义下成立的。有限样本、短尺度、非平稳市场都会让它偏离,本文的模拟是理想化的验证而非严格证明。
  4. 响应函数标定对噪声敏感。R(l) 是两个随机量的乘积期望,独立微观结构噪声、有限样本会让估计在长滞后处方差很大(图中尾部的抖动就是),实盘标定需要大样本 + 稳健估计。

结果解读#

市场冲击不是一锤子买卖,它有记忆。 传播子模型最颠覆直觉的一点是:你今天砸出的一笔单,它对价格的影响不会立刻消失,而会以幂律的节奏拖很久。这对执行有直接含义——如果你在短时间内密集下同方向的单,你不只是在承受每一笔的即时冲击,还在给自己”叠加”前面几笔尚未消退的余韵,让后续成交越来越贵。这正是上一篇里”拆单要摊开、参与率要压平”的微观结构根源。

核的衰减速度是被无套利钉死的,不是自由参数。 这是传播子模型和普通经验拟合模型的本质区别。它告诉你:市场冲击的形状不是偶然,而是”允许大单长记忆存在”和”不泄露可套利趋势”这两个约束博弈出的唯一解。理解这一点,你就能预判:一个订单流长记忆更强的市场(比如机构占比高、拆单更普遍),它的冲击核会衰减得相应更慢。

响应函数是连接理论和数据的桥。 R(l) 的”先升后平”形状是可以在真实数据上测出来的,它同时证伪了瞬时冲击和永久冲击两种简化观点。任何认真做执行成本建模的团队,第一步都是把自家成交的 R(l) 标定出来,用它反推冲击核,再用这个核去优化拆单节奏、预估大单的全周期冲击成本。

传播子模型是市场微观结构里少有的”美学与实用兼得”的理论:它用一个衰减核统一了三种割裂的冲击观,用无套利条件把这个核的形状几乎唯一地推导出来,又能落到可标定、可优化的实操层面。它告诉你一件很反直觉但很重要的事——市场是有记忆的,而这份记忆的衰减节奏,恰恰是它保持效率的方式。

传播子模型价格冲击:把每笔成交的冲击写成核函数衰减
https://blog.halo26812.eu.org/blog/propagator-model-price-impact
Author halo
Published at 2026年7月25日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨