1976 年 Fischer Black 观察到一件怪事:当股票下跌时,它的波动率往往比同等幅度上涨时跳得更凶。这违背了「波动只取决于幅度、不取决于方向」的直觉,也直接打脸了当时所有对称的波动率模型。这就是杠杆效应(leverage effect)——名字来自「股价跌→公司权益贬值→杠杆率上升→风险更大」的机制(当然现实里还有波动率反馈等别的解释,但现象是铁打的)。
标准 GARCH(1,1) 的方程是对称的:
注意这里只有 ——平方抹掉了符号,所以「跌 3%」和「涨 3%」对明天方差的贡献一模一样。本文用 GJR / TGARCH(Glosten-Jagannathan-Runkle, 1993) 的阈值项把方向加回来,从零模拟带杠杆效应的数据,再用 MLE 把它精确还原出来。
一、阈值项:给「坏消息」加一记重拳#
GJR-GARCH 在 项后加一个只对负冲击生效的阈值项:
其中 是指示函数:只有上一期收益为负时才取 1。于是:
- 利好():;
- 利空():。
只要 ,同等幅度的坏消息就会推高更多方差。 就是杠杆效应的强度刻度。
二、News Impact Curve:非对称的 V 形#
把上式画成「过去冲击 → 下一期方差 」的曲线,对称 GARCH 是对称的 U 形,GJR 则是左半边更陡的 V 形——负半轴被 抬高了:

这条曲线是判断「你的数据有没有杠杆效应」最直观的体检表。如果实测的 V 形左右不对称,那就别用对称 GARCH。
三、Python:模拟 + 高斯 MLE 拟合#
下面模拟一个真值 的 GJR 过程,再用 scipy.optimize 做极大似然估计,分别拟合「非对称 GJR」和「强制 γ=0 的对称 GARCH」:
import numpy as np
from scipy.optimize import minimize
def simulate_gjr(T=4000, omega=1e-6, alpha=0.06, gamma=0.09, beta=0.88, seed=407):
rng = np.random.default_rng(seed)
eps = np.zeros(T); sig = np.zeros(T)
sig[0] = np.sqrt(omega / (1 - alpha - 0.5 * gamma - beta))
for t in range(1, T):
z = rng.normal()
eps[t] = sig[t - 1] * z
ind = 1.0 if eps[t - 1] < 0 else 0.0
sig[t] = np.sqrt(omega + alpha * eps[t - 1]**2
+ gamma * eps[t - 1]**2 * ind + beta * sig[t - 1]**2)
return eps, sig
def garch_filter(eps, omega, alpha, gamma, beta):
s2 = np.empty(len(eps)); s2[0] = np.mean(eps**2)
for t in range(1, len(eps)):
ind = 1.0 if eps[t - 1] < 0 else 0.0
s2[t] = omega + alpha * eps[t - 1]**2 + gamma * eps[t - 1]**2 * ind + beta * s2[t - 1]
return np.maximum(s2, 1e-12)
def neg_ll(p, eps, fit_gamma=True):
omega, alpha, gamma, beta = p
if omega <= 0 or alpha < 0 or beta < 0 or (fit_gamma and gamma < 0):
return 1e10
s2 = garch_filter(eps, omega, alpha, gamma if fit_gamma else 0.0, beta)
ll = -0.5 * (np.log(2*np.pi) + np.log(s2) + eps**2 / s2)
return -ll.sum()
eps, sig = simulate_gjr()
# 跑拟合:非对称 GJR 与强制 γ=0 的对称 GARCH
from scipy.optimize import minimize
gjr = minimize(neg_ll, [1e-6, .06, .09, .88], args=(eps, True),
bounds=[(1e-9, None), (0, .5), (0, .5), (0, .99)], method="L-BFGS-B").x
sym = minimize(neg_ll, [1e-6, .06, 0., .88], args=(eps, False),
bounds=[(1e-9, None), (0, .5), (0, 0), (0, .99)], method="L-BFGS-B").x
ll_g, ll_s = -neg_ll(gjr, eps, True), -neg_ll(sym, eps, False)
def model_asym(p):
s2 = garch_filter(eps, *p)
return s2[1:][eps[:-1] < 0].mean() / s2[1:][eps[:-1] >= 0].mean()
print("GJR ", gjr, "LL", ll_g)
print("sym ", sym, "LL", ll_s, "gain", ll_g - ll_s)
print("asym ratio GJR=%.3f sym=%.3f" % (model_asym(gjr), model_asym(sym)))python四、结果:模型精确还原真值,且显著优于对称#
拟合输出(对数似然值为全样本累计):
- GJR 拟合:,LL = 14142.0;
- 对称 GARCH 拟合:强制 ,LL = 14089.2;
- 对数似然增益 = +52.8——在 4000 个观测下,这是压倒性的证据:对称模型被显著拒绝;
- 用各自模型过滤出的条件方差按「过去收益符号」分组,利空后的均值方差 / 利好后的均值方差:GJR ≈ 1.06,对称 ≈ 0.98——非对称模型确实学到了「坏消息更狠」,对称模型则左右无差。

再看原始数据的散点:横轴是昨天的收益,纵轴是今天的平方收益,按正负着色——左半边(坏消息)的点明显整体偏高,bin 均值线在负区翘起:

五、实盘三类真实陷阱#
陷阱一:波动率反馈 vs 杠杆效应,别混为一谈。 上面模拟用的是「收益冲击 → 波动」的单向 GJR 结构,但真实市场里存在波动率反馈:波动上升本身会压低价格,制造负收益,于是「跌」和「波动升」互为因果。纯 GJR 把它当成单向,会高估杠杆效应、低估反馈。严谨做法是用隔夜收益(剔除交易时段波动反馈)单独估计杠杆项,或与 Heston 类联立模型对照。
陷阱二:残差不是高斯的,用 t 分布或偏 t。 我们用高斯 MLE 很方便,但金融收益有厚尾 + 左偏——用正态似然会系统性低估极端事件的方差,并把 估得偏高(用条件方差去「硬扛」本该由厚尾解释的部分)。真实落地务必用 Student-t 或偏 t 分布的似然,并把自由度 一起估出来;否则你的 VaR/CVaR 会失真。
陷阱三:初值与局部最优。 GARCH 类的似然面在 接近 1 时极平坦,朴素初值常卡在边界(如 )。上面用了「无条件是方差」做 初值、并把参数框在合理界内;实战还要多组初值跑几遍取最优,否则你以为估到「真值」,其实只是局部极值。
六、落地路径与诚实结论#
真实复现时:
- 数据:日收益即可(GJR 不需要高频),但样本要跨越多空 regime,至少 2000 个交易日;
- 分布:默认用 Student-t 似然,自由度一起估;若做期权,考虑对上行/下行分别建模;
- 用途: 直接告诉你「你的标的对坏消息有多敏感」—— 大的资产(如小盘、加密)该给更宽的止损和更高的危机保证金;它也是波动率风险溢价和偏度风险溢价的建模基石;
- 组合进预测:把 GJR 条件方差接进上一篇文章的 HAR-RV,可同时建模「非对称 + 长记忆」,是实盘波动率预测的标配。
结论:杠杆效应是真实存在的,标准对称 GARCH 看不见它。GJR/TGARCH 用一行阈值项 就把方向信息请了回来——我们的 MLE 把真值 原样还原,对数似然比对称模型高出 52.8,条件方差在利空后确实更高。但记住:它假设「冲击→波动」是单向的、残差是高斯的、似然面是单峰的——这三条里任何一条在实盘被打破,你估出的 都可能是幻觉。先换 t 分布、再核查波动率反馈、多组初值收尾,杠杆效应才会从论文里的 0.09 变成你风控里真能用的刻度。