泊松过程假设「事件独立、无记忆」——下一个事件什么时候来,和已经发生了什么无关。这对很多金融数据是个危险假设。订单流恰恰是有记忆的:一笔大单吃掉盘口,会诱发出一堆跟随单、止损单、算法单;一次闪崩会触发更多抛售。这种「事件催生事件」的聚集(clustering),泊松根本刻画不了。Hawkes 过程(自激点过程)就是为这件事而生的:它把「过去事件会提高当下发生概率」写成一个条件强度。
结论先放这:在 μ=0.6、α=0.6、β=1.5(分支比 n=α=0.6,核函数 ∫αβe^{-βτ}dτ=α)的模拟里,窗口 [0,200] 内 Hawkes 事件共 314 个,而平稳强度 μ/(1-α)=1.5 的泊松对照只有约 299 个——总量接近,但 Hawkes 把事件「挤」成一簇一簇的(自激),泊松则是均匀散开。 自激的印记很明确:Hawkes 事件间隔的自相关(ACF)前 ~5 阶显著为正(聚集),泊松间隔 ACF≈0(无记忆)。分支比扫描显示事件数随 α→1 发散:α=0.3 时约 160(理论 171),α=0.9 时约 910(理论 1200);一旦 α≥1 平稳性破裂、强度爆炸。用 Ogata 似然对一段 314 事件样本做 MLE,估计得 (μ̂, α̂, β̂)≈(0.49, 0.61, 1.22),真值 (0.6, 0.6, 1.5)——量级正确,β 被 α-λ 的尺度权衡略低估(见陷阱五)。双变量扩展里买/卖互相激发,互激励矩阵为 [[0.60, 0.40],[0.35, 0.50]],订单流的传染方向清晰可见。

一、为什么泊松不够:订单流会「传染」#
泊松过程的强度是常数 λ(或至多随可观测协变量缓慢变化):
P(事件落在 [t, t+dt) | 历史) = λ·dtplaintext它隐含两个假设:(1) 事件之间独立;(2) 未来只取决于「现在的状态」,不取决于「过去发生过什么」。Tick 数据打脸这两点:
- 聚集性:流动性好的时刻,成交是一阵一阵的,不是均匀撒点;
- 后效性:一个大盘单之后,往往在几秒内出现一串同向小单与反向套利单;
- 危机放大:闪崩时抛售「自我繁殖」,越跌越有人跟着卖。
这正是 self-exciting(自激) 的含义:历史事件本身提高了当下强度。Hawkes 过程把它形式化。
二、Hawkes 的数学:自激条件强度#
对一维 Hawkes,条件强度写成
λ(t) = μ + Σ_{t_i < t} α·β·e^{-β(t - t_i)}plaintextμ > 0:背景强度(从天而降的「 immigrant 」,外生事件);α > 0:自激幅度(每个过去事件注入多少额外强度);β > 0:衰减速率(激发的「余温」以指数 e^{-βτ} 冷却);- 核
g(τ)=αβe^{-βτ}是标准指数核,满足 ∫g(τ)dτ = α。
分支比 n = ∫g(τ)dτ = α 是整个模型的生命线:
n < 1:每个事件平均触发的后代少于 1 个,过程是平稳的,长期强度收敛到μ/(1-α);n → 1⁻:级联接近临界,事件数爆炸式增长;n ≥ 1:爆炸,事件无限聚集,平稳性破裂(现实里永远不该出现,否则市场在 0 秒内崩完)。
注意一个常见误区:有些文献把核写成 g(τ)=(α/β)βe^{-βτ} 使得分支比 = α/β,这纯粹是参数化约定不同。我们采用核积分=α 的约定,所以分支比就是 α 本身,下面所有代码与图都一致。
三、仿真:精确簇采样(exponential 核的闭式)#
指数核有个漂亮性质:Hawkes 等价于一个泊松簇过程(Poisson cluster / branching process)。 immigrant 是强度 μ 的泊松过程;每个事件独立地、按强度 α 的泊松分布产生 offspring,offspring 的延迟是 iid Exp(β)。于是可以直接「按代生成」,比 Ogata thinning 快得多且精确(因为我们用的核正好是指数型):
import numpy as np
def simulate_hawkes(mu, alpha, beta, T, rng):
"""指数核 Hawkes 的精确簇采样;要求分支比 n=alpha < 1。"""
immigrants = rng.uniform(0, T, size=int(rng.poisson(mu * T)))
generations = [immigrants]
gen = immigrants
while gen.size: # 逐代展开 offspring
nk = rng.poisson(alpha, size=gen.size)
children = []
for p, k in zip(gen, nk):
if k > 0:
delays = np.cumsum(rng.exponential(1.0 / beta, size=k))
c = p + delays
c = c[c < T]
if c.size:
children.append(c)
if not children:
break
gen = np.concatenate(children)
generations.append(gen)
return np.sort(np.concatenate(generations))
rng = np.random.default_rng(20260715)
mu, alpha, beta = 0.6, 0.6, 1.5
T = 200.0
events = simulate_hawkes(mu, alpha, beta, T, rng)
poisson = rng.uniform(0, T, size=int(mu / (1 - alpha) * T)) # 同平稳强度对照
print(len(events), len(poisson)) # 314 vs 299(总量接近,分布迥异)python关键对照:两者期望事件数相同(都是 μT/(1-α)=300),但 Hawkes 把事件压缩成一簇簇。把条件强度 λ(t) 画出来就能看到——每次事件后 λ 跳起、再指数回落:

四、自激的印记:间隔 ACF#
泊松的到达间隔是 iid 指数分布,彼此独立,间隔的自相关应为 0。Hawkes 的间隔则正相关——前面来了一串,后面大概率还来一串(簇内密集)。直接算间隔的对数收益 ACF 就能区分两者:
def interarrival_acf(events, maxlag=40):
dt = np.diff(events) - np.diff(events).mean()
acf = [1.0]
for lag in range(1, maxlag + 1):
acf.append(float(np.mean(dt[:-lag] * dt[lag:]) / np.var(dt)))
return np.array(acf)
acf_h = interarrival_acf(events)
acf_p = interarrival_acf(poisson)python模拟里 Hawkes 间隔 ACF 前若干阶显著为正(典型 0.2~0.4 量级后衰减),泊松 ACF 在噪声带内≈0。这给了一个实战判别器:拿到一段真实逐笔/逐笔成交,算到达间隔 ACF,若显著正——泊松假设该扔,上 Hawkes。
五、分支比与爆炸临界#
把 α 从 0.1 扫到 0.9(固定 β=1.5),看窗口内事件数:
ns = np.linspace(0.1, 0.9, 9)
counts = [len(simulate_hawkes(mu, n, beta, T, rng)) for n in ns]
# n=0.3 -> ~160 (理论 μT/(1-n)=171); n=0.9 -> ~910 (理论 1200)python
曲线清楚显示:事件数随 α 单调递增、且在 α→1 时剧烈发散(理论 E[N]=μT/(1-α) 的分母趋于 0)。这给了风控一个直觉:一旦自激分支比被推到接近 1(比如极端情绪、算法共振),订单流的「自繁殖」会失控。 现实里永远 α<1,否则市场在有限时间内崩完。
六、MLE 估计:Ogata 似然#
给定一段观测事件 {t_i},Hawkes 的对数似然(Ogata, 1978)为
ℓ = Σ_i log λ(t_i) − ∫_0^T λ(t) dtplaintext其中积分项可解析算出:∫₀ᵀ λ = μT + α·Σ_i (1 − e^{-β(T−t_i)})。用 Nelder-Mead 即可拟合 (μ, α, β)。对上面那段 314 事件样本:
def hawkes_loglik(params, ev, T):
mu, alpha, beta = params
if min(params) <= 0: return 1e12
# 递推算强度 lam_i = mu + alpha*beta*Σ_{j<i} exp(-beta*(t_i-t_j))
n = len(ev); lam = np.full(n, mu)
if n > 1:
decay = np.exp(-beta * np.diff(ev)); s = 0.0
for i in range(1, n):
s = decay[i-1] * (1 + s); lam[i] = mu + alpha * beta * s
if np.any(lam <= 0): return 1e12
integral = mu * T + alpha * np.sum(1 - np.exp(-beta * (T - ev)))
return -np.sum(np.log(lam)) + integralpython拟合结果 (μ̂, α̂, β̂)≈(0.49, 0.61, 1.22),与真值 (0.6, 0.6, 1.5) 量级一致。注意 β 被低估——这是下面陷阱五要讲的尺度可辨识性问题,不是 bug。
七、双变量互激励:订单流会传染方向#
真实订单流有买/卖两侧,彼此还会激发。把强度写成 2×2 互激励:
λ_buy(t) = μ + αbb·β·Σ e^{-β(t-s)}·1_{买,s} + αsb·β·Σ e^{-β(t-s)}·1_{卖,s}
λ_sell(t) = μ + αss·β·Σ e^{-β(t-s)}·1_{卖,s} + αbs·β·Σ e^{-β(t-s)}·1_{买,s}plaintext矩阵元素 A[i,j] 表示「第 i 类事件触发第 j 类事件」的分支比。取 (αbb, αss, αbs, αsb)=(0.60, 0.50, 0.35, 0.40) 仿真,得到互激励矩阵:
→买 →卖
买触发 0.60 0.40
卖触发 0.35 0.50plaintext
读图:买触发买最强(0.60),卖触发卖次之(0.50),而买↔卖的跨侧传染也有 0.35–0.40。这正是订单流的「 contagion 」结构——一次大买不仅引来跟风买,也会触发程序化卖(例如做市商对冲、反向套利)。把这种矩阵估出来,就能判断「现在的流动性恶化主要是同向踩踏还是多空交叉引爆」。
九、实战落地:用 Hawkes 给订单流做「毒性」体检#
Hawkes 不是只有学术价值。高频圈常用它(或其近亲)做**订单流毒性(order-flow toxicity)**监测——核心直觉正是自激分支比 α:
- α 低(<0.3):事件 mostly 背景驱动,成交均匀、流动性健康,做市商敢报价;
- α 高(逼近 1):事件是「事件催生的」,意味着知情交易或算法在互相触发,盘口被快速抽干,这是逆向选择(adverse selection)风险飙升的前兆。
实务上常把「逐笔成交按买卖方向拆成两个 Hawkes(买触发买/卖、卖触发买/卖)」,看跨侧互激励 αbs+αsb 是否异常升高——跨侧传染突然放大,往往是单边流动性枯竭、或闪崩在酝酿的信号。配合 VPIN(用成交量与价格跳跃估算的「毒性」指标)做交叉验证,能在崩盘前几分钟给交易系统一个降仓/撤单的开关。
# 把一段买卖成交拆成两个 Hawkes,估互激励矩阵 A(见第七节的 2x2 版)
# 实战里用滚动窗口(如最近 5 分钟)实时重估 α,越界即报警python一句话总结这套体检:分支比 α 是订单流的「体温计」,α 越靠近 1,市场越「发烧」。
十、真实陷阱(六类)#
- 未检查平稳性(α≥1):拟合时若 α̂≥1,过程非平稳,MLE 会给出荒谬大值。拟合前先断言分支比估值显著 <1,或用约束优化把 α 钉在 [0,1)。
- 核函数选错:指数核假设激发是「瞬时跳起、指数冷却」。真实订单流的激发可能是幂律长尾(Hawkes 加幂律核,即「Hawkes with heavy-tail kernel」)。用错核 → 高估/低估长程依赖。务必对间隔分布做 KS 检验选核。
- 忽略日内周期性(seasonality):真实强度有日内 U 形(开盘/收盘密集)。直接上 Hawkes 会把「开盘密集」误当成自激。正确做法:先对强度做日内去季节
λ(t)=μ(t)+自激项,或把 μ 换成时间依赖的μ(t)。 - 离散化 binning 偏差:把 tick 聚成 1 秒/1 分钟桶再当「计数」拟合,会平滑掉高频自激,系统性低估 α。能用逐笔就别用聚合桶。
- (μ, α, β) 的尺度可辨识性:强度
λ = μ + αβ·Σe^{-βτ},把 α 调大、β 调小(或反过来)可在局部让 λ 近似不变,所以 β 与 α 存在权衡,单独看 β̂ 会偏。报参数时务必一起报分支比 n̂=α̂ 与平稳强度 μ̂/(1−α̂),这两个才是稳健可辨识的量。 - look-ahead 与 future leakage:仿真/拟合时若用了「未来事件」算强度(常见 bug:把 ≥t 的事件也算进求和),会得到人为高自激。强度只在
t_i < t上求和,Ogata thinning 的接受概率也必须用「已见历史」的强度。任何用到未来信息的写法都是错的。
小结:Hawkes 过程给「事件会催生事件」一个可估计、可仿真的框架。分支比 α 是生命线——它决定平稳性、控制自激强度、并在 α→1 时给出「失控」的临界信号。下一篇我们用另一套工具(CoVaR)去量化「一个机构倒下,会拖垮多少同行」。