SVAR实证复现:影子银行对中小企业融资挤出效应与脉冲响应
2026/9/18 5:10:23 网站建设 项目流程

简介:这是一份面向金融学研究者、高校师生及政策研究人员的实证论文,围绕影子银行与民营中小企业融资之间的关系展开,借助结构向量自回归(SVAR)模型检验二者的Granger因果关系。论文指出影子银行对中小企业从正规金融机构融资存在“挤出”效应,短期融资需求会刺激影子银行膨胀但长期不可持续,并进一步讨论其提高融资成本、风险与难度的问题,最后提出以市场为导向、坚持金融服务实体经济、发挥互联网金融积极作用的改革思路。资源内含1个PDF文件,压缩包约1.24MB,便于直接阅读与引用,知识点涵盖影子银行、信贷配给、Granger因果检验、脉冲响应分析及金融深化改革等核心概念。目前已有77人学习,适合需要理解中小企业融资困境与金融改革方向的读者参考。

1. 一篇 2015 年的 SVAR 实证论文,为什么值得当技术活拆一遍

2015 年《华东经济管理》第 4 期登过一篇论文,作者王蕾、张婧婕、陈霄,用 2002 年 2 月到 2009 年 11 月共 94 个月的月度数据,把影子银行规模(Sbk)和私营企业及个体贷款(Pel)塞进一个两变量 SVAR 模型,结论是两者互为 Granger 原因,影子银行对正规渠道融资存在挤出效应。

这份 PDF 值得当技术资源拆的地方,是它的建模链条没有跳步:变量口径定义、二次指数平滑填补缺失值、ADF 单位根检验、五准则选滞后阶数、AR 特征根稳定性、AB 型 SVAR 的短期约束、正交化脉冲响应,一步不落。换成「消费信贷余额对信用卡逾期率」这类双变量场景,整条流水线可以直接迁移。

反直觉的点在脉冲响应。中小企业融资对影子银行扰动的响应不是当期就转负,而是先波动下降,第 5 个月触底,之后缓慢回升,整条曲线始终压在零轴下方。挤出效应是滞后出现的,只看当期系数会得出相反判断。

2. 变量构造与缺失值回填:Sbk 加总口径和 Pel 的口径对齐

变量口径决定后面所有结论。同样的 SVAR 代码,把 Sbk 换成社会融资规模,脉冲响应形状可能完全反过来。这一层没定清楚,后面调参数都是在给错误输入做精修。

2.1 影子银行规模为什么用三项加总

论文梳理了国内几种测度思路,认为影子银行中约 82% 来自委托贷款、信托贷款和民间借贷,另外未贴现银行承兑汇票也贡献了一部分。最终用于建模的 Sbk,是委托贷款、信托贷款、未贴现银票三者的月度加总,民间借贷因为缺少可统计的总量数据被排除在外。

这个取舍有代价:口径漏掉民间借贷会低估真实规模,但换任何「可统计、可复现」的口径都得做类似妥协。做类似研究时,常见做法是在稳健性检验里换一版口径再跑一次,看脉冲响应方向是否翻转。Pel 选得更讲究,用的是其他商业银行对私营企业及个体发放的人民币短期贷款,而不是更宽泛的「中小企业贷款余额」——它按月公布,颗粒度够,且直接对应「从正规金融机构获得的融资」这个被解释对象。

变量含义构造口径频率观测数
Sbk影子银行规模委托贷款 + 信托贷款 + 未贴现银行承兑汇票,月度加总94
Pel私营企业及个体贷款其他商业银行对私营企业及个体工商户发放的人民币短期贷款94
样本区间2002-02 至 2009-112009 年后部分口径停止公布,故截断94

2.2 点状缺失用二次指数平滑回填

先把数据读进来并对齐到月度频率,索引错位是这类面板数据最常见的第一道坑。

import pandas as pd df = (pd.read_csv("sbk_pel_monthly.csv", parse_dates=["month"], index_col="month") .asfreq("MS")[["sbk", "pel"]]) # MS = 每月月初,避免月末/月初索引错位

asfreq("MS")把索引强制成每月初频率,缺失月份会以 NaN 出现而不是被静默跳过。接下来处理缺口,论文用的是二次指数平滑法。

from statsmodels.tsa.holtwinters import ExponentialSmoothing def fill_by_quadratic_smoothing(series: pd.Series) -> pd.Series: """Holt 线性趋势,即经典二次指数平滑,回填点状缺失。""" observed = series.dropna() fit = ExponentialSmoothing( observed, trend="add", # 水平 + 趋势,对应二次指数平滑 seasonal=None, # 月度数据不额外挂季节项 initialization_method="estimated", # 初始水平/趋势由似然估计给出 ).fit(optimized=True) # 对 alpha、beta 做数值优化 fitted = pd.Series(fit.fittedvalues, index=observed.index) return series.combine_first(fitted.reindex(series.index)) df["sbk"] = fill_by_quadratic_smoothing(df["sbk"]) df["pel"] = fill_by_quadratic_smoothing(df["pel"])

逻辑是先拿观测值拟合 Holt 模型,再让原始值优先、缺口位置用拟合值补。参数上,trend="add"决定模型有线性趋势项,这正是二次指数平滑与一次指数平滑的唯一区别;initialization_method="estimated"换成heuristic会让前几期拟合值偏差明显变大;optimized=True关掉的话,平滑系数取默认经验值,长缺口位置的回填会偏离原序列的波动幅度。

注意:平滑法只适合点状缺口。某个变量连续缺 6 个月以上,拟合值会沿着自己的趋势外推,等于人为造出一段过于平滑的曲线,ADF 检验和脉冲响应都会被这段人造数据带偏。补齐后把缺失位置单独标记,跑一遍含缺口和剔除该段的对照。

2.3 ADF 检验:确认 I(0) 再进 VAR

两列时间序列进 VAR 前必须确认平稳,否则统计量分布失真,容易出伪回归。论文用 EViews 6.0 做 ADF,结论是 Sbk 与 Pel 都通过检验,为零阶单整 I(0),可以直接建 VAR 而不必差分。

from statsmodels.tsa.stattools import adfuller def adf_row(s: pd.Series, name: str, maxlag: int = 12) -> dict: stat, pval, used_lag, nobs, crit, _ = adfuller( s.dropna(), maxlag=maxlag, regression="c", autolag="AIC" ) return {"变量": name, "ADF统计量": round(stat, 4), "p值": round(pval, 4), "使用滞后阶": used_lag, "样本量": nobs, "5%临界值": round(crit["5%"], 4)} print(pd.DataFrame([adf_row(df["sbk"], "Sbk"), adf_row(df["pel"], "Pel")]).to_string(index=False))

参数说明:maxlag=12让信息准则在最多 12 阶里挑滞后,月度数据给到一年的长度上限比较稳;regression="c"表示检验方程带常数项、不带趋势项,这两列都是有长期均值的水平变量,硬加趋势项会削弱检验力;autolag="AIC"自动控制滞后阶数,避免残差自相关把检验结果抬高。

判读方式:ADF 的原假设是「序列存在单位根」,p 值小于 0.05 才能拒绝。如果这里没过,别急着差分,先查是不是样本区间跨了口径调整或断点,那种情况差分也救不回来。

3. 从 VAR(6) 到 AB 型 SVAR:滞后阶数与识别约束怎么定

滞后阶数和识别约束是这套模型里最容易被拍脑袋决定的两步,也是复现结果对不上的主要来源。论文在这两处的处理路径比较清楚,值得逐条拆。

3.1 五个准则打架时听谁的

论文先算 Sbk 与 Pel 的动态相关系数,最大值出现在当期,说明企业对影子银行规模变化的反应很快;短期相关系数的最大值落在第六期。随后做 VAR 滞后阶数检验,在 5% 显著水平下,LR、FPE、AIC 三个准则都指向 6 阶,SC 与 HQ 在自己的最小值处偏向更短的阶数,最终取 6。

准则倾向阶数特点
LR6似然比检验,小样本下比 AIC 保守
FPE6最终预测误差,看样本外预测能力
AIC6惩罚轻,倾向偏大的阶数
SC偏短惩罚重,小样本容易压到 1 阶
HQ偏短惩罚介于 AIC 与 SC 之间
from statsmodels.tsa.api import VAR model = VAR(df[["pel", "sbk"]]) print(model.select_order(maxlags=8).summary()) res = model.fit(6)

select_order一次给出 aic、bic(对应 SC)、hqic、fpe 四个准则的选择结果。maxlags=8控制搜索上界,样本只有 94 个月时给到 8 比给 12 稳,每多一个阶数就多吃掉一个观测。为什么最后听 AIC 而不是 SC:两变量小样本 VAR 里 SC 容易把阶数压到 1,残差自相关没清干净,脉冲响应会出现假振荡。先由动态相关系数找到第六期的证据,再用 AIC 印证,两条线互相对上才定 6 阶,这个顺序比单纯看准则表更可靠。

3.2 VAR(6) 估计与 AR 特征根稳定性

论文给出的 VAR(6) 估计结果里,Pel 方程 R² 约 0.645、调整后约 0.589;Sbk 方程 R² 约 0.412、调整后约 0.318。Sbk 方程拟合度明显更低,说明影子银行规模受模型外因素影响更大——这是个信号,模型对 Pel 的解释力强于对 Sbk 的解释力,后续读脉冲响应时对 Sbk 那条曲线要更谨慎。

稳定性检查看 AR 特征根,全部模必须落在单位圆内。

import numpy as np roots = res.roots # 伴随矩阵特征根的倒数 modulus = np.abs(roots) print("特征根模:", np.round(modulus, 4), "是否全部小于1:", bool((modulus < 1).all()))

res.roots返回的是特征根的倒数,判据是模全部小于 1。任何一根的模大于等于 1,模型就不稳定,脉冲响应不收敛,长期读数发散,图也就没法解释。遇到不稳定,常见处理是延长滞后阶数,或者对变量做差分,但差分会让「水平变量之间存在长期关系」这个前提失效,得先想清楚建模目标是短期动态还是长期均衡。

3.3 AB 型 SVAR 的那个约束设在哪

VAR 只能描述变量之间的滞后关系,估不出当期关系。影子银行与中小企业融资之间反应迅速,当期影响不能丢,所以要升到 SVAR。两变量情况下,结构式需要施加 k(k-1)/2 = 1 个约束才能识别。

论文选的是短期约束:民营中小企业融资对当期的影子银行规模没有反应,用矩阵写出来就是 A 矩阵的第一行第二列取 0。经济含义是企业这个月从正规银行拿到多少贷款,不可能因为这个月才出现的影子银行规模变化而立刻改变,授信审批有周期;反过来,影子银行规模会在当期对中小企业融资的变化作出反应。

3.4 A、B 矩阵的极大似然估计

Python 生态里没有现成的 AB 型 SVAR,似然函数得自己写。

import numpy as np from scipy.optimize import minimize def neg_loglik(theta, S, T): """AB 型 SVAR 的负对数似然。theta = [a21, b11, b22]。""" a21, b11, b22 = theta A = np.array([[1.0, 0.0], [a21, 1.0]]) # a12 = 0,即施加的短期约束 B = np.diag([b11, b22]) # 对角阵,结构冲击之间不相关 Ainv = np.linalg.inv(A) Sigma = Ainv @ B @ B.T @ Ainv.T # 简约式残差协方差的结构形式 sign, logdet = np.linalg.slogdet(Sigma) if sign <= 0 or b11 <= 0 or b22 <= 0: return 1e10 # 惩罚非法参数区域 return 0.5 * T * (logdet + np.trace(np.linalg.inv(Sigma) @ S)) S = res.sigma_u.values T = res.nobs best = minimize(neg_loglik, x0=[-0.5, 60.0, 900.0], args=(S, T), method="Nelder-Mead", options={"maxiter": 5000}) a21, b11, b22 = best.x print(f"a21={a21:.4f} b11={b11:.2f} b22={b22:.2f}")

逻辑是A ε_t = B u_t这个标准形式下,简约式残差与其协方差满足 Σ = A⁻¹BB'A'⁻¹,把这个 Σ 代进多元正态的对数似然,待估参数只剩 a21、b11、b22 三个,恰好等于 2×2 对称协方差的自由元素个数,属于恰好识别。

参数说明:theta的顺序固定为 A 的非对角元素在前、B 的对角元素在后;S用 VAR(6) 估出来的残差协方差矩阵,T取有效样本量;b11 <= 0 or b22 <= 0的判断是为了挡掉优化跑到负标准差上;初值按量级给,第二个方差比第一个大一个数量级,是因为 Sbk 的月度波动远大于 Pel。

论文估出来的两个标准差分别在数十和数百的量级,方向与上面给的初值一致。估完必须做一次自洽检查:把 Σ 反代回去,看A @ Sigma @ A.TB @ B.T是否近似相等,不等就说明优化没收敛,换初值或者改用方差平方根参数化重跑。

4. Granger 因果检验与结构脉冲响应的读法

系数表只是中间产物,能对外表述的结论来自因果检验和脉冲响应。这两块最容易读错,一个是滞后阶数的敏感性,一个是正交化的口径。

4.1 Granger 因果检验对滞后阶数极其敏感

论文用 1 到 6 阶分别做了检验,结果分成两段。

检验用滞后阶数原假设:Sbk 不是 Pel 的 Granger 原因原假设:Pel 不是 Sbk 的 Granger 原因
1 阶接受(p 值约 0.16)拒绝
2~6 阶拒绝拒绝
from statsmodels.tsa.stattools import grangercausalitytests for lag in range(1, 7): o1 = grangercausalitytests(df[["pel", "sbk"]], maxlag=lag, verbose=False) o2 = grangercausalitytests(df[["sbk", "pel"]], maxlag=lag, verbose=False) print(f"lag={lag} Sbk→Pel p={o1[lag][0]['ssr_ftest'][1]:.3f} " f"Pel→Sbk p={o2[lag][0]['ssr_ftest'][1]:.3f}")

列顺序不能反:grangercausalitytests默认检验的是「第二列不是第一列的 Granger 原因」,想让 Pel 当被解释变量,就得把 pel 放第一列。ssr_ftest是 F 检验的 p 值,与论文表 5 的判读口径一致,用ssr_chi2test会在小样本下给出偏乐观的 p 值。1 阶的结论和 2 阶以后相反,不是数据有问题,而是自由度变了——滞后阶数直接决定结论方向,这也是论文把 1 到 6 阶全列出来的原因。

4.2 结构脉冲响应得自己算

res.irf()给的是 Cholesky 正交化,变量顺序决定分解结果,和论文的 AB 型识别不是一回事。要做 AB 型 SVAR 的脉冲响应,得手动把结构冲击还原出来。

Ainv = np.linalg.inv(np.array([[1.0, 0.0], [a21, 1.0]])) Bmat = np.diag([b11, b22]) Phi = res.ma_rep(maxn=24) # VAR 的 MA 系数,形状 (25, 2, 2) Theta = np.array([p @ Ainv @ Bmat for p in Phi]) # 简约式冲击 → 结构冲击 resp_pel_to_sbk = Theta[:, 0, 1] # Pel 对第 2 个结构冲击(Sbk)的响应 for h in range(0, 25, 5): print(f"h={h:02d} {resp_pel_to_sbk[h]:.2f}")

res.ma_rep(maxn=24)给出 VAR 的移动平均系数序列,Ainv @ Bmat把简约式冲击翻译成结构冲击,两者相乘才是结构脉冲响应。索引Theta[:, 0, 1]的含义是第 1 个变量对第 2 个结构冲击在第 h 期的响应,和论文图 2 对应;把变量索引对调就得到图 3。这一步最容易出错的地方是漏乘Ainv,那样得到的其实是简约式响应,形状相近但幅度和置信带都不对。

4.3 两条曲线上的关键读数

论文图 2 是中小企业融资对影子银行扰动的响应:整条曲线压在零轴下方,第 5 个月到达最低点,之后回升但未穿过零轴。这个形态对应「挤出效应」,影子银行规模扩张后,中小企业从中长期看从正规银行拿到的钱是减少的。

图 3 反过来:影子银行对中小企业融资扰动的响应,当期为正,第 5 期达到正向最大值,之后衰减并趋向零。意思是中小企业短期融资需求的扩张会推高影子银行规模,但这个推动不具持续性。

读这两条曲线有三件事不能省。置信带要一起看,脉冲响应的点估计单独没有意义,偏离带跨零就说明该期响应在统计上不显著。横轴单位是月、纵轴是亿元,别把「第 5 期」当成 5 个季度。转折点要单独核对,第 5 期触底这种读数,如果置信带在这一期跨零,就不能把最低点当成结论,只能说方向为负。

5. 复现偏差排查与可复用的验证链

跑完一遍代码,结果和论文对不上一半以上,通常不是模型选错了,而是几个固定环节存在细微差异。把这几处先排掉,再谈结论差异才有意义。

5.1 三处最常见的偏差来源

现象常见成因处理方式
滞后阶数与论文不一致样本区间截取点或缺失值回填方式不同固定 p=6 重跑,比较各准则曲线而非单点结果
A、B 估出负的方差优化初值离真值太远,落到非法区域换初值,或改用标准差的平方根做参数化
响应形状相似但幅度差一倍变量单位不统一(元与亿元混用)全部换算到亿元,或对序列分别取自然对数

第三条尤其隐蔽。Sbk 与 Pel 的量级差一到两个数量级,B 矩阵的两个对角元素也因此差一个数量级,这正是识别里把两个方差分开估的意义。单位不统一不会让模型报错,只会让脉冲响应的幅度解释失去意义。

5.2 一条可复用的断言链

把关键中间件落盘,再用断言把假设固定下来,比每次重跑后肉眼看图可靠得多。

import numpy as np def check_svar_fit(A, B, Sigma, tol=1e-4): """校验 A、B 与简约式残差协方差是否自洽。""" return np.allclose(A @ Sigma @ A.T, B @ B.T, atol=tol) A_hat = np.array([[1.0, 0.0], [a21, 1.0]]) B_hat = np.diag([b11, b22]) assert check_svar_fit(A_hat, B_hat, res.sigma_u.values), "A、B 与简约式协方差不自洽" assert (np.abs(res.roots) < 1).all(), "VAR 不稳定,脉冲响应不可用" assert res.nobs > 6 * 2 * 2, "有效样本量不足,滞后阶数应下调" np.save("irf_pel_to_sbk.npy", resp_pel_to_sbk) # 中间件落盘,便于跨口径对照

check_svar_fit用矩阵等式做自洽校验,atol=1e-4是按亿元量级设的容差;res.nobs那条断言对应经验规则——两变量 VAR(6) 的参数个数接近 26 个,样本量低于参数量的三倍时,AIC 选出的阶数会明显不稳。把脉冲响应数组落盘之后,换口径、换区间、换滞后阶数都只是改一行参数,重跑一次就能看到结论有没有翻转,也才能在报告里说清哪一版结果是稳健的。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询