一条收益率曲线有几十个期限点(3 月、1 年、5 年、10 年、30 年……),看起来是高维对象。但做过利率的人都知道:整条曲线的变动,几乎只用三个数字就能解释。这就是「宏观因子定价」在固定收益里的经典形态——把收益率曲线投影到少数几个共同因子上,再对这些因子定价、对冲、预测。
最干净的工具是 Nelson-Siegel(1987)模型。它把期限 上的收益率写成三个宏观因子的线性组合:
- = 水平(level):长端水平,所有期限同载,代表「整体利率水位」;
- = 斜率(slope):短端高、长端趋于 0,代表「陡峭/倒挂」;
- = 曲率(curvature):中端凸、两端负,代表「 belly 相对两端」的扭曲;
- = 衰减参数,控制曲率峰值出现的位置(本文取 )。
本文用一段 10 个期限、约 12 年的日度数据,亲手把曲线拆开,并证明:收益率曲线的变动是三维的——前 3 个主成分恰好对应水平/斜率/曲率,累计解释 99.6% 的跨期限波动。
一、为什么要把曲线拆成因子#
如果你要管理一个债券组合,面对 30 个期限点,逐个对冲是不可能的。但三个因子就够了:
- 水平因子动,意味着「整体加息/降息」,所有久期一起动;
- 斜率因子动,意味着「短端相对长端」变化,曲线陡峭化或平坦化;
- 曲率因子动,意味着「中段相对两端」变化,蝴蝶(butterfly)交易。
把曲线投影到这三个因子上,组合的风险就从高维降成三维,定价、对冲、预测都变得可操作。这正是宏观因子定价的精髓:用少数共同因子逼近高维对象的变动。
二、三个因子的期限载荷#
Nelson-Siegel 的优雅之处在于,三个因子对各个期限的「影响力(载荷)」是固定的函数形状:
水平载荷恒为 1(所有期限平等);斜率载荷从 1 单调降到 0(短端敏感、长端不敏感);曲率载荷在中期达到峰值、两端为负(中段最敏感)。下面的载荷曲线一眼就能看出三个因子的「作用范围」。

三、Python:合成曲线 + 最小二乘反拟合#
下面这段代码生成合成收益率曲线,并用最小二乘把每天的曲线「反拟合」回三因子。真实落地时,这三因子就是你对任意一天曲线做的回归系数。
import numpy as np
taus = np.array([0.25, 0.5, 1, 2, 3, 5, 7, 10, 20, 30]) # 期限(年)
lam = 2.5
def ns_loadings(tau, lam=lam):
x = tau / lam
L0 = np.ones_like(tau) # level
L1 = (1 - np.exp(-x)) / x # slope
L2 = L1 - np.exp(-x) # curvature
return np.vstack([L0, L1, L2]).T # T × 3
# 三因子:慢变过程(水平带趋势、斜率/曲率均值回复)
T = 252 * 12
rng = np.random.default_rng(7)
b0 = np.zeros(T); b1 = np.zeros(T); b2 = np.zeros(T)
b0[0], b1[0], b2[0] = 0.035, 0.012, 0.0
for t in range(1, T):
b0[t] = 0.992*b0[t-1] + 0.008*0.035 + rng.normal(0, 0.0006)
b1[t] = 0.95 *b1[t-1] + 0.05 *0.012 + rng.normal(0, 0.0009)
b2[t] = 0.90 *b2[t-1] + rng.normal(0, 0.0011)
L = ns_loadings(taus)
Y = L @ np.vstack([b0, b1, b2]) + rng.normal(0, 0.0004, size=(len(taus), T)) # 加观测噪声
Y = Y.T # T × N
# 用最小二乘把每天的曲线反拟合成三因子
bhat = np.zeros((T, 3))
for t in range(T):
bhat[t] = np.linalg.lstsq(L, Y[t], rcond=None)[0]
Yfit = bhat @ L.T
r2 = 1 - ((Y - Yfit)**2).sum(1) / ((Y - Y.mean(1, keepdims=True))**2).sum(1)python四、拟合优度:R² 中位数 0.99#
把反拟合出的曲线叠回原始观测点,拟合非常好。本文合成数据下,NS 拟合的中位数 R² = 0.9921(最差一天也有 0.8481)——也就是说,三个因子就几乎完整还原了整条曲线的形状。下面是某天真实观测(散点)与 NS 拟合(实线)的对比。

这里有个诚实提醒:合成数据的观测噪声很小(0.04bp 量级),所以 R² 接近 1 是「设计如此」。实盘里,短端受流动性、repo、票息效应干扰,拟合 R² 会低一些,但三个因子解释 95%+ 的曲线变动仍是业界共识。
五、三个因子随时间怎么走#
把反拟合出的 画成时间序列,就是三条「宏观状态」曲线:水平随加息/降息周期缓慢移动,斜率在陡峭与倒挂间往复,曲率小幅波动。它们就是曲线变动的「底层驱动」。

六、PCA 验证:曲线是三维的#
最硬的证据来自主成分分析。把全样本收益率矩阵(T×N)做协方差分解,看前几个主成分解释多少跨期限波动:
Yc = Y - Y.mean(axis=0)
cov = np.cov(Yc, rowvar=False)
eigval, eigvec = np.linalg.eigh(cov)
order = np.argsort(eigval)[::-1]
var_exp = eigval[order] / eigval[order].sum()
cum_var = np.cumsum(var_exp)python结果是:PC1 解释 96.83%、PC2 解释 2.53%、PC3 解释 0.23%,前 3 主成分累计 99.60%。 换句话说,整条曲线 99.6% 的变动落在三个维度里。而且这三个主成分的形状,恰好对应 NS 的水平、斜率、曲率载荷——PCA 从数据里「无监督」地重建出了 Nelson-Siegel 从经济学里「有监督」设定的三因子。这就是宏观因子定价的根基:收益率曲线的低秩结构,是数据本身告诉你的,不是强加的。

七、真实陷阱(最容易翻车的地方)#
- 不是免费的。 NS 的载荷形状依赖 。固定 是惯例,但不同市场、不同段(短端 vs 长端)最优 不同;更稳健的做法是把 也作为拟合参数(Svensson 扩展加了第 4 个因子和第 2 个衰减参数)。
- 长端样本稀疏、短端噪声大。 30 年、20 年期限流动性差,观测点本身带买卖价差噪声,拟合会被长端 outlier 拉偏。实务要对不同期限点加权(流动性权重)。
- 因子是「统计因子」不是「定价因子」。 NS/PCA 给出的是描述性因子(曲线怎么动),不等于风险溢价因子(曲线为什么动、该赚什么钱)。要把它们变成定价因子,还需接上期限溢价、预期假说等经济约束(如 affine term structure model)。
- 水平因子与通胀/政策强相关,但滞后。 的缓慢移动反映货币政策周期,用它预测短端利率要小心时变——加息周期里水平因子的「趋势」会突然被政策转向打破(regime shift)。
- 截面 vs 时序混淆。 本文的 R² 是横截面拟合(同一天不同期限),证明「曲线形状能用 3 因子描述」;但它不证明「因子能预测未来收益率」。预测需要单独的时序检验,两者不能混为一谈。
八、小结#
宏观因子定价在利率市场的标准答案,就是用 Nelson-Siegel 把收益率曲线拆成水平、斜率、曲率三个因子:水平是整体水位,斜率是陡峭/倒挂,曲率是 belly 扭曲。本文用合成数据演示,三因子 NS 拟合 R² 中位数达 0.99,而 PCA 从数据里无监督地重建出同样的三维结构(前 3 主成分解释 99.6% 波动)。把高维曲线压成三维因子,债券组合的风险管理、对冲与预测才真正可操作——这就是「用少数共同因子逼近高维对象」的宏观因子定价思想。
本文曲线由 Nelson-Siegel 三因子 + 观测噪声合成,量级仅用于演示方法。实盘落地请用真实国债收益率(如 FRED 的 DGS 序列、或中国国债收益率曲线),把 作为拟合参数或改用 Svensson 四因子,并对长端流动性差的点降权。本文仅作研究方法示例,不构成任何投资建议。