一条国债收益率曲线,上有 3 个月、下有 30 年,中间十几个关键期限,每个期限一个即期利率。如果我们想「理解」这条曲线,难道要给 11 个期限各建一个模型?不需要。经验告诉我们,整条曲线 90% 以上的变动,只由三个动作驱动:整体上下(水平)、长短端利差变化(斜率)、中段相对两端鼓起或塌陷(曲率)。 Nelson-Siegel(1987)用一个极简的函数,把这三个动作压缩成三个因子。
结论先放这里:NS 模型用 4 个参数(3 个因子 + 1 个衰减参数 λ)就能高保真拟合整条收益率曲线,且三个因子有明确的经济含义——β0 是长端水平、β1 是短长端利差、β2 是中段曲率。它们是利率建模、曲线估值、风险分解的「最小可行因子」。

一、为什么需要「节俭」的曲线模型#
收益率曲线上的点高度共线:1 年和 2 年的利率几乎永远同涨同跌;10 年和 15 年也高度同步。如果你对每个期限独立建模,会得到 11 个高度相关的序列,参数爆炸、过拟合、还无法解释。
更好的思路是因子降维:把曲线看成「少数几个形状(shape)的线性组合」。NS 提出的三形状是:
- 水平(level):整条曲线一起平移——所有期限同涨同跌;
- 斜率(slope):短端和长端反向——曲线变陡或变平;
- 曲率(curvature):中段相对两端凸起或塌陷——典型的「驼背」形态。
这三个形状几乎能解释收益率曲线协方差矩阵的绝大部分。
二、Nelson-Siegel 的函数形式#
NS 把期限 τ 处的即期利率写成:
三个载荷(loading)函数分别是:
- 水平载荷:常数 1(对所有期限一样);
- 斜率载荷:,在短端为 1、随期限单调衰减到 0;
- 曲率载荷:,在短端为 0、中段达到峰值、长端回到 0——这就是「蝴蝶型」。
λ 是唯一的「形状」参数,它决定斜率和曲率在哪个期限区间作用最明显。λ 越小,曲率峰越往短端挤;λ 越大,越往长端延。
三、Python:用 scipy 拟合一条曲线#
我们用 scipy.optimize.least_squares 对一组(含噪声的)观测点做非线性最小二乘拟合:
import numpy as np
from scipy.optimize import least_squares
def ns_loadings(tau, lam=0.5):
x = tau / lam
c1 = (1 - np.exp(-x)) / x
c2 = c1 - np.exp(-x)
return np.ones_like(tau), c1, c2
def nelson_siegel(tau, b0, b1, b2, lam=0.5):
L, S, C = ns_loadings(tau, lam)
return b0 + b1 * S + b2 * C
def residuals(params, tau, y_obs):
b0, b1, b2, lam = params
return nelson_siegel(tau, b0, b1, b2, lam) - y_obs
# 观测点(含噪声)
tau = np.array([0.25, 0.5, 1, 2, 3, 5, 7, 10, 15, 20, 30], dtype=float)
y_obs = np.array([2.10, 2.25, 2.55, 2.95, 3.20, 3.55, 3.75, 3.95,
4.10, 4.18, 4.25]) + np.random.default_rng(7).normal(0, 0.04, 11)
sol = least_squares(residuals, x0=[3.5, -1.0, 1.0, 0.5], args=(tau, y_obs),
bounds=([0, -5, -5, 0.1], [15, 5, 5, 5]))
b0, b1, b2, lam = sol.x
print(f"拟合因子: β0={b0:.3f} β1={b1:.3f} β2={b2:.3f} λ={lam:.3f}")python跑出来大致是 β0≈4.0(长端水平约 4%)、β1≈-1.5(斜率负、曲线向上倾斜)、β2≈1.0(中段略鼓)。四个参数,就把 11 个点的曲线拟合到误差 < 5bps。图 1 同时画出了观测点、拟合曲线和三大成分分解——你能直观看到「水平」是那条水平线、「斜率」是短高长低的倾斜、「曲率」是 mid 段的鼓包。
四、三因子的时序演化#
把 NS 模型对每一天的曲线都拟合一遍,把每天的 (β0, β1, β2) 连起来,就得到三条因子时序(图 2):

它们的经济含义非常干净:
- β0(水平):跟随长期经济增长与通胀预期,最平滑、最持久;
- β1(斜率):跟随货币政策周期——加息周期斜率压平甚至倒挂,宽松周期陡峭化;
- β2(曲率):最「跳」,反映市场对中段(比如 2–5 年)的供需错配或预期突变。
五、载荷曲线:λ 决定形状的作用区间#
图 3 展示了不同 λ 下斜率载荷和曲率载荷的形状:

- λ 小(如 0.3):曲率峰值挤在 1 年以内,模型主要刻画短端弯曲;
- λ 大(如 1.0):曲率峰延伸到 3–5 年,模型更关注中段。
实务中 λ 常固定为 0.5 或 1.0(对应半衰期约 0.5 / 1 年),也可以把它作为第 4 个待估参数让数据自己选形状——代价是优化非凸、可能对初值敏感,需要多组初值网格搜索。
六、NS 与主成分(PCA):为什么选 NS 而不是 PCA#
有人会问:既然要降维,直接用 PCA 对收益率曲线做主成分分解不行吗?行,而且 PCA 的「前三大主成分」在形状上几乎就是水平、斜率、曲率——和 NS 三因子高度对应。但 NS 有两个 PCA 给不了的优势:
- 可解释性:NS 的 β 有直接的利率含义,能和宏观经济叙事对接;PCA 的主成分是「统计构造」,解释起来要绕一圈。
- 外推与插值:NS 是闭式函数,给任意一个 τ 都能算利率;PCA 只在观测期限上有载荷,中间期限要靠插值。
代价是 NS 拟合是非线性的(含 λ),PCA 是线性的、有解析解。实务里常见做法是:用 PCA 定 λ(让 NS 曲率峰对准 PCA 能量最大的期限),再用 NS 做可解释建模。
from sklearn.decomposition import PCA
pca = PCA(n_components=3)
scores = pca.fit_transform(y_curve_panel) # 每行一条曲线
print("前3主成分解释度:", pca.explained_variance_ratio_.cumsum())
# 通常 cumsum 第3项 > 0.98,说明三因子足够刻画曲线python七、NS 模型的四个局限#
NS 很美,但别神化它:
- 无套利(no-arbitrage)缺失:NS 直接拟合即期利率,不保证由它推导的远期利率曲线无套利。想做定价,得上其扩展 Nelson-Siegel-Svensson(加第 4 个曲率因子) 或 动态无套利模型(如 Hull-White / affine term structure)。
- λ 固定的代价:固定 λ 等于先验「曲率只在中段」;若某段曲线在短端鼓包(如 2023 年美债短端倒挂加剧),固定 λ 的 NS 拟合会系统性偏差。
- 外推危险:NS 在观测期限范围内拟合好,但拿去外推 50 年、100 年,c1、c2 都趋近于 0,曲线会平铺在 β0——这对养老金长负债贴现是隐患。
- 因子正交性假象:β0/β1/β2 在载荷层面「形状不同」,但估计时仍可能高度相关(尤其 β1 与 β2),做风险分解时要小心共线。
结论#
Nelson-Siegel 是利率曲线建模的「最小可行起点」:4 个参数、3 个可解释因子、闭式载荷、拟合快、含义清。它是曲线估值、债券相对价值(见上一篇信用利差因子)、利率风险分解的通用语言。等你需要的不再是「描述曲线」而是「给衍生品无套利定价」时,再从 NS 升级到 Svensson 或无套利动态模型——但无论如何,NS 都是你理解期限结构的那把第一把尺子。
如果你要把它接上真实数据,下一步是用 中国国债收益率曲线(中债登每日发布的关键期限点)循环拟合、把每日 (β0, β1, β2) 存成因子面板,再对因子自身做均值回复或预测——那时你得到的,是一个可交易、可风控的利率曲线因子库。
所有图表均由文中 Python 代码真实计算生成(模拟数据),仅用于方法演示,不构成投资建议。