2008 年雷曼倒下时,真正可怕的不是一个投行破产,而是它把整张金融网络拖进连锁违约。系统性风险研究的核心问题从来不是”哪家会倒”,而是”谁倒了会连累最多人”。这一篇我们用图论把这个问题变成可计算的工程问题。
结论先放这:把机构的收益相关性矩阵翻译成一张关联图,节点的『中心度』直接对应它的系统重要性——中心度最高的少数机构就是网络枢纽,冲击从它们注入会沿关联边快速扩散到整张网。用 k-NN 图 + 特征向量中心度,能在 40 家机构的模拟网络里清晰揪出 Top5 枢纽;用 BFS 传染模拟,能算出冲击从枢纽注入后的感染深度与广度。 附完整 Python 与六类真实陷阱(高阶)。

一、为什么相关性网络比”相关性矩阵”更锋利#
研究系统性风险,最自然的起点是相关系数矩阵。但一张 40×40 的相关矩阵有 780 个数,肉眼看不出结构:谁是枢纽?谁和谁绑得最紧?冲击从哪条路传?
图的威力在于强制做稀疏化 + 可视化结构。我们把相关性 C_ij 当成”机构 i 和 j 之间的边权”,然后用一个稀疏化规则(比如每个节点只连它相关性最高的 k 个邻居)把稠密矩阵变成一张可读的网。一旦变成图,“系统重要性”就不再是抽象概念,而是一组图论指标:
- 节点度(degree):连了几条边,度越高越”忙”。
- 中心度(centrality):更精细——它不仅看连多少,还看连的是”谁”。连在一堆叶子上的节点,不如连在另一个枢纽上的节点重要。
- 最短路径:冲击从 A 到 B 最少经过几跳,跳数越少传染越快。
import numpy as np
def knn_graph(C, k=3):
"""每个节点连它相关性最高的 k 个邻居(无向、对称化)。"""
N = C.shape[0]
A = np.zeros((N, N))
for i in range(N):
order = np.argsort(-C[i])
for j in order[1:k + 1]: # 跳过自己
A[i, j] = 1
A = np.maximum(A, A.T) # 对称化:i 连 j 则 j 也连 i
return A
def degree(A):
return A.sum(1)
def eigenvector_centrality(A, iters=100):
"""用幂迭代算邻接矩阵主特征向量 = 特征向量中心度。"""
N = A.shape[0]
v = np.ones(N) / np.sqrt(N)
for _ in range(iters):
v = A @ v
n = np.linalg.norm(v)
if n < 1e-12:
break
v = v / n
return np.abs(v) / v.sum()pythonknn_graph 这一步是整套方法的灵魂:它把”弱相关噪声”全部剪掉,只保留每个机构最在意的几个关联对象。剪完之后,网络结构立刻浮现——类内(同类型机构)稠密、类间(跨类型)只有少数桥接边。
二、谁是系统重要性机构?中心度给你答案#
中心度用邻接矩阵的主特征向量度量:一个节点重要,当且仅当它连着重要的节点。这比”数边数”更聪明——枢纽的邻居还是枢纽,叶子连的只是一堆叶子。

图上中心度分布极度不均匀:少数机构(图中 Top5,N 开头的几个)中心度明显高于长尾,它们就是网络的系统重要性枢纽。在模拟设定里,这几位恰好集中在银行类——符合直觉:银行是跨市场流动性中介,一边连券商、一边连保险信托,天然处在网络中心。
注意一个工程上的诚实点:k-NN 的 k 决定了图的稀疏度,也决定了中心度的形状。k 太小,图太碎,中心度区分度低;k 太大,弱相关噪声混进来,中心度被稀释。k=3 是个常用的折中,但必须做敏感性检查(换 k=2/4/5 看 Top 枢纽稳不稳)。
三、冲击传染:从枢纽注入会发生什么#
光有静态中心度还不够——系统性风险的本质是动态传染。我们做一个最简单的传染模拟:从某个节点注入单位 shock,沿关联边按衰减系数 β 传播(BFS),看整张网被感染多少、感染负载怎么分布。
def contagion(A, source, beta=0.6, max_depth=6):
"""从 source 注入单位 shock,沿关联边按 beta 衰减传播(BFS)。"""
infected = {source: 1.0}
frontier = {source}
depth = 0
while frontier and depth < max_depth:
nxt = set()
for u in frontier:
for v in range(A.shape[0]):
if A[u, v] > 0 and v not in infected:
infected[v] = infected[u] * beta
nxt.add(v)
frontier = nxt
depth += 1
load = np.zeros(A.shape[0])
for k, val in infected.items():
load[k] = val
return loadpython
结果一目了然:从中心度最高的枢纽(红色星标)注入 shock,感染负载沿边快速铺满整张网;红色越深表示感染越重。对比实验(代码里换 source 为叶子节点)会显示:从叶子注入,shock 多半烂在局部,传不出三跳就衰减没了。枢纽和叶子的”系统重要性”差了不是一个量级——这正是 CoVaR、SRISK 这类监管指标想量化的东西,而图论给了我们一个更直观的几何视角。
四、节点度分布:少数枢纽、多数叶子#
最后看整个网络的拓扑形状——节点度分布。它告诉我们这张网是”均匀网状”还是”星型辐射”。

分布呈明显的右偏:绝大多数机构度=1(只连一个邻居的叶子),少数机构度很高(枢纽)。这是典型的无标度网络(scale-free)特征——真实金融网络正是这样:大量边缘机构 + 少数核心中介。这个结构有深刻含义:无标度网络对随机故障极鲁棒、对定向攻击极脆弱。随机死几个叶子无所谓,但只要打掉中心度 Top3 的枢纽,整张网的连通性会瞬间崩塌。这解释了为什么监管要把”大而不能倒”当成核心命题。
五、六类真实陷阱(这才是重点)#
-
相关性是双向因果混淆:我们用收益相关做边,但相关性 ≠ 关联结构。两只基金同买一个 ETF,相关性高是因为共同暴露而非直接关联——图会把”假的亲密”当成”真的传染路径”。必须区分”直接敞口关联”和”共同因子暴露”。
-
相关性的时变与危机突变:模拟用了静态相关矩阵。真实市场里相关性在平静期低、危机期飙升(所有资产一起跌)。你用危机前的相关建图,危机来时整张网的结构已经彻底改道——hub 可能换人,桥接边可能全断。图必须滚动重估,且危机期的图才有真正的系统性含义。
-
k-NN 的 k 是主观刀:k 选几完全是你定的。k=1 得到一堆互不连通的碎片(度全=1),k=N−1 退化回稠密全连接(度全=N−1),两种极端都无意义。k 的选取必须有经济学依据(或做 k 敏感性扫描),不能为了”图好看”调 k。
-
中心度只在”正确稀疏化”下有意义:如果用全连接图(所有相关性都当边),每个节点度都≈N,中心度塌成均匀,区分度归零。图论指标的威力完全建立在稀疏化剪枝之上——剪错了,指标就是垃圾。
-
传染模拟的 β 是拍脑袋:第三节的
beta=0.6是假设,真实冲击传导取决于机构间的实际敞口网络(谁持有谁的债务),而非收益相关。用相关网络近似敞口网络,β 的物理含义是模糊的。真要做实盘级传染分析,得用真实的银行间敞口矩阵( disclosured 数据),不是收益相关。 -
幸存者与范围偏差:模拟里 40 家机构全程在线。真实分析若只覆盖”上市金融机构”,会漏掉影子银行、对冲基金这些真正在危机里先爆的节点。图的边界决定了你能看到的传染路径——边界之外的冲击,图上一片空白却真实存在。
六、总结#
把系统性风险从”矩阵”翻成”图”,价值在于三件事:第一,结构可视化——谁连谁、谁是枢纽一眼可见;第二,指标可计算——中心度、度、最短路径把”系统重要性”变成数字;第三,传染可模拟——BFS 让我们能问”如果这家倒了,会波及多少家”。
但务必记住:图是相关性网络,不是敞口网络。它擅长揭示”谁和谁绑在一起、谁在中心”,不擅长回答”真实的钱从谁流向谁”。前者是系统性风险的地图,后者才是起火点。监管真正要防的,是地图中心那几个枢纽节点——它们的中心度,就是”大而不能倒”的量化注脚。
完整代码(k-NN 建图、中心度幂迭代、BFS 传染、度分布)已随图给出,全部基于自洽合成的机构关联网络,逻辑可直接替换成真实收益/敞口数据落地。