资讯动态

TENET-CODE实战:R语言尾部风险网络建模与系统性风险识别

发布时间:2026/10/22 3:19:33 来源:尧图企业网站定制
简介TENET-CODE是一份围绕尾部事件驱动神经网络TENET的经典实现代码面向金融风险管理、量化投资与机器学习方向的研究者和实践者帮助其理解极端风险事件的建模思路与落地方式。资源包共53个文件约2.37MB以R脚本、CSV数据、TXT说明、Markdown文档和PNG图表为主另含少量Excel表格与R数据文件覆盖数据预处理、模型训练、参数调优与结果可视化等环节。代码涉及尾部事件识别、神经网络结构、损失函数与优化器选择、金融时间序列特征工程、训练验证集划分及AUC-ROC等评估指标并包含VaR、CoVaR、连通性网络与系统性重要金融机构等实战模块。已有1250人学习下载适合希望深入掌握TENET模型实现细节、提升金融市场极端风险预测能力的读者参考。1. 拆开 TENET-CODE一份把尾部风险做成网络的 R 代码包金融风控里有个老问题正常波动谁都能建模真正要命的是尾部。2008 年那种级别的回撤、单日暴跌、机构间风险同时爆表传统线性模型基本抓不住。TENET 这套东西的思路是把尾部事件当成一等公民用网络结构去刻画机构之间的极端风险传导。我拿到 TENET-CODE.zip 的第一反应是这不是一份跑个 demo 就完事的玩具而是一套完整的 R 工程从数据清洗、SIM 单指数模型、线性与非线性网络、VaR/CoVaR 一路铺到总联动指标和分组网络。它适合谁做金融风险量化、想复现系统性风险网络、或者手头有收益率面板数据想找一套能改能跑的框架的人。不适合只想调个包看结论的人因为这份代码里大量逻辑需要你自己接数据、调参数、看中间输出。2. TENET 的建模逻辑为什么用网络而不是回归2.1 从单指数模型到总联动TENET 到底在算什么先把概念理清楚不然后面看代码会迷。TENET 全称 Tail Event Driven Neural Network尾部事件驱动神经网络。它的核心不是预测某只股票明天涨跌而是回答一个更结构化的问题当某家机构或某个板块进入尾部状态时风险通过什么路径、以多大强度传到别人身上。传统做法是跑分位数回归算 CoVaR一家一家算算完拼成矩阵。问题是金融机构之间的联动往往是非线性的市场恐慌时相关性会突然跳升线性回归在这种时刻会系统性低估风险。TENET 用神经网络去拟合这种非线性映射再把拟合出来的连接强度整理成网络。代码包里几个文件名其实已经把模块划分说清楚了文件/目录作用TENET_SIM单指数模型把个股收益分解到市场因子TENET_Linear线性版本的网络估计TENET_VaR_CoVaR计算 VaR 与条件 VaRTENET_total_connectedness总联动指数TENET_total_in_out_individual单机构的入/出连接度TENET_total_in_out_groups分组层面的入/出连接度TENET_group_network分组网络构建TENET_SIFIs系统重要性金融机构识别这套结构是层层递进的先 SIM 降维再估网络再算联动最后落到 SIFIs 识别。你如果只想要一个总联动数字跑到 TENET_total_connectedness 就够了要做监管视角的机构排序就得把 in/out individual 和 SIFIs 都跑通。2.2 环境与数据准备R 版本、依赖和输入格式这份代码是 R 写的历史文件 .Rapp.history、.Rhistory 说明作者是在 RStudio 里长期迭代的。我一般会先确认三件事R 版本、依赖包、数据格式。R 版本建议 4.x太老的 3.x 在神经网络相关包上容易出兼容问题。依赖方面代码里涉及神经网络训练、分位数计算、矩阵运算常见需要的是 quantreg、nnet 或 neuralnet、以及基础的 zoo/xts 处理时间序列。具体包名以你解压后脚本里的 library() 调用为准不要凭记忆装。数据输入看文件名就知道大概BS_CFin_20170202_Weekly_Fina.xls 是资产负债表类的财务数据GICS_Fin_Maco_weekly_Return2008_2016.csv 是 GICS 金融板块加宏观的周度收益率时间跨度 2008 到 2016。也就是说这套代码默认吃的是周频面板数据行是时间列是机构或因子。# 读取周度收益率面板第一列通常是日期 ret - read.csv(GICS_Fin_Maco_weekly_Return2008_2016.csv, header TRUE, stringsAsFactors FALSE) # 日期列转成 Date方便后续按时间切片 ret$Date - as.Date(ret$Date) # 检查缺失金融面板里停牌、退市会留 NA colSums(is.na(ret)) # 收益率列去掉日期列转成矩阵喂给后续模型 ret_mat - as.matrix(ret[, -1])这段逻辑很直白读进来、转日期、查缺失、转矩阵。参数上唯一要留意的是日期格式如果你的日期是 2016/1/3 这种as.Date 要补 format 参数否则会整列变 NA后面全崩。缺失值不要无脑填 0金融收益率填 0 等于人为制造一个“无波动”的假观测常见做法是前向填充或直接剔除该机构。提示先拿一小段数据跑通全流程再上全样本。这套代码模块多全样本第一次跑很容易在某个中间步骤卡住定位成本高。3. 跑通核心链路从 SIM 到 VaR/CoVaR 的操作步骤3.1 单指数模型 SIM降维与因子提取SIM 这一步的作用是把每家机构的收益率拆成市场因子加个体残差。为什么要拆因为直接拿原始收益率估网络市场共同波动会淹没机构间的真实传导你会得到一个人人相连的稠密网络没有信息量。# 对每家机构跑单指数模型r_i alpha beta * r_market e_i sim_fit - function(stock_ret, market_ret) { fit - lm(stock_ret ~ market_ret) # 普通最小二乘 list(alpha coef(fit)[1], beta coef(fit)[2], resid residuals(fit)) # 残差留作网络输入 } # 对面板每一列循环收集残差矩阵 resid_mat - sapply(seq_len(ncol(ret_mat) - 1), function(j) { sim_fit(ret_mat[, j], ret_mat[, Market])$resid })逻辑说明lm 拟合市场因子beta 是系统性暴露resid 是个体特质部分。参数上市场因子那一列必须明确指定代码里假设它叫 Market你换数据要改列名。残差矩阵才是后面网络估计的真正输入。这一步的坑在于 beta 的稳定性如果某机构 beta 在样本期内剧烈变化单一 beta 会失真常见做法是滚动窗口估 beta但这份代码默认是全样本你要自己判断是否够用。3.2 VaR 与 CoVaR尾部阈值的设定VaR 是单机构在给定置信水平下的最大损失CoVaR 是“当某机构处于危机状态时另一机构的 VaR”。TENET 的尾部驱动就体现在这里它不建模整个分布只关注超过阈值的部分。# 计算单机构 VaR置信水平 0.95即 5% 分位 library(quantreg) var_fun - function(x, tau 0.05) { quantile(x, probs tau, na.rm TRUE) # 经验分位数 } # 对残差矩阵每列算 VaR var_vec - apply(resid_mat, 2, var_fun, tau 0.05) # 标记尾部状态低于 VaR 的观测记为 1 tail_flag - sweep(resid_mat, 2, var_vec, FUN )参数说明tau 是尾部概率0.05 对应 95% 置信。这个值直接决定你抓多少极端样本设太小如 0.01样本不够神经网络训不动设太大如 0.10尾部就不“尾”了。我一般先用 0.05 跑看尾部样本量再调。tail_flag 是逻辑矩阵后续网络估计会用它做条件。注意 quantile 默认算法在样本少时和不同插值方式有差异跨机构比较时保持同一算法。3.3 网络估计与总联动把连接强度拼成矩阵到这一步才是 TENET 的主体。用神经网络拟合“一家机构尾部状态”对“另一家机构收益”的影响把拟合出的连接强度整理成邻接矩阵再算总联动。library(nnet) # 以机构 j 的尾部状态为输入预测机构 i 的收益 tenet_link - function(target_ret, tail_input) { dat - data.frame(y target_ret, x as.numeric(tail_input)) fit - nnet(y ~ x, data dat, size 3, # 隐藏层 3 个节点 linout TRUE, decay 0.01, # 回归输出 权重衰减 maxit 500, trace FALSE) predict(fit, newdata dat) # 返回拟合值 } # 双重循环构建连接矩阵机构数多时建议并行 n - ncol(resid_mat) adj - matrix(0, n, n) for (i in 1:n) { for (j in 1:n) { if (i ! j) { pred - tenet_link(resid_mat[, i], tail_flag[, j]) adj[i, j] - cor(resid_mat[, i], pred, use complete.obs) } } }逻辑说明size 是隐藏层节点数3 是个保守起点节点越多拟合能力越强但越容易过拟合decay 是权重衰减控制复杂度linoutTRUE 表示输出连续值而不是分类。adj[i,j] 表示 j 的尾部状态对 i 的解释强度用相关系数衡量。这个双重循环是性能瓶颈机构上百时纯循环会很慢常见做法是并行化或先做变量筛选。总联动就是把 adj 矩阵按行按列加总代码包里 TENET_total_connectedness 干的就是这件事。注意神经网络有随机初始化同一份数据两次跑结果不会完全一样。要可复现训练前 set.seed 固定种子并在论文或报告里写明种子。4. 避坑与排查这份代码最容易翻车的五个地方4.1 现象跑完 adj 矩阵全是接近 1 的高相关原因没有做 SIM 降维直接拿原始收益率估网络市场共同因子让所有机构看起来高度联动。解决确认你喂给网络的是 SIM 残差而不是原始收益检查 resid_mat 是否真的去掉了市场因子。4.2 现象nnet 报错或输出全是常数原因输入没有标准化或者尾部标记全 0/全 1 导致输入无变异。解决对残差做 scale 标准化检查 tail_flag 每列的均值接近 0 或 1 的列直接剔除说明该机构样本期内没有尾部事件。4.3 现象VaR 算出来是正数尾部标记反了原因收益率量纲或符号约定不一致有的数据用百分比有的用小数。解决统一量纲确认 VaR 是负的分位数tail_flag 用“小于 VaR”而不是“大于”。这个坑很隐蔽标记反了后面全反。4.4 现象总联动指数逐年上升看起来“风险越来越大”原因样本期覆盖 2008 到 2016前期危机期联动本来就高后期平稳期低如果窗口没对齐会误读趋势。解决明确你的滚动窗口长度对比不同窗口下的联动曲线不要拿单条曲线下结论。4.5 现象SIFIs 排序每次跑都不一样原因神经网络随机性加样本敏感性排名靠后的机构本来就不稳定。解决多次训练取平均连接矩阵或者只关注排名前列且跨次稳定的机构别把第 15 名和第 16 名的差异当信号。5. 进阶用法把 TENET 输出接到你自己的风控流程里跑通全链路只是起点真正有价值的是把输出用起来。我一般会做三件事。第一把总联动指数做成时间序列和已知的风险事件对齐看它是不是在事件前就有抬升。这一步不需要改代码把 TENET_total_connectedness 的输出按时间存下来画图即可。如果联动指数在几次市场剧烈波动前都提前抬头这套东西对你的场景就有预警价值。第二用 in/out 连接度做机构分层。TENET_total_in_out_individual 给的是每家机构的“发出风险”和“接收风险”强度。发出高、接收低的机构是风险源头接收高、发出低的是风险承接方。监管视角关注前者交易对手管理关注后者。你可以按这两个维度画散点把机构分到四个象限比单一排名信息量大得多。第三分组网络做板块级传导。TENET_group_network 和 TENET_total_in_out_groups 把机构聚合到组适合看银行、保险、券商之间的风险流向。聚合规则要你自己定常见是按 GICS 子行业或按资产规模分组。分组后节点少网络图可读性高适合放进汇报材料。进阶方向用到的模块关键参数联动预警total_connectedness滚动窗口长度机构分层total_in_out_individual入/出连接度阈值板块传导group_network分组规则验证方法上我习惯做两件事一是把 TENET 的联动结果和简单相关系数矩阵对比如果两者完全一致说明你的非线性部分没起作用参数可能没调对二是留一段样本外数据看尾部时期的联动估计是否比线性方法更贴近实际。这两步能帮你判断这套代码在你的数据上到底有没有增量。从那以后我每次拿到这类网络型风险代码都强制先跑一遍线性版本做基准再上非线性不然根本说不清提升来自模型还是来自数据。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑