资讯动态

结合DeepSeek的债券流动性风险预警:市场深度计算与特征增强实践

发布时间:2026/9/19 8:17:35 来源:尧图企业网站定制
简介一份面向债券市场研究员、量化分析师与风险管理人员的DeepSeek证券债券流动性风险评估方案聚焦市场深度指标计算和流动性危机早期预警模型构建。资源共1个PDF文件大小11.48MB全文221页、50个大章节支持目录章节跳转及阅读器书签大纲快速定位文字、图表显示完整。方案从Tick级行情数据采集与预处理入手逐步讲解市场深度指标的数学模型、订单簿结构化存储与索引、买卖盘口深度实时计算、深度斜率与流动性价差算法、大单冲击下的深度恢复能力设计并覆盖历史危机事件特征提取、数据标注与偏差修正、滑动窗口与跨市场特征工程以及LSTM、Transformer和注意力机制在时序预警中的适配改造。整个资源适合希望系统掌握从数据清洗、特征工程到模型选型完整链路的风控建模人员。目前已有68人学习可直接作为债券流动性风险预警项目的技术底稿参考。1. 高评级不一定安全债券流动性风险为什么需要市场深度和预警机制2020 年 3 月美国投资级债券 ETF 出现罕见的大幅折价大量高评级信用债的买卖价差在几天内从十几个基点跳到上百个基点。这不是违约风险引发的而是流动性风险做市商撤出报价市场深度急剧塌缩账面评级最好的债券反而卖不出去。债券市场没有交易所连续撮合日成交笔数少、报价稀疏传统的 VaR 和久期模型在这种场景下基本失效必须依赖市场深度指标来刻画“想卖的时候能不能卖得掉”。这个标题里出现的 DeepSeek解决的是债券流动性风险建模中最麻烦的两个环节一是成交稀疏带来的指标计算不稳定二是非结构化信息公告、评级行动、舆情很难进传统量化模型。配合市场深度指标计算和流动性危机早期预警可以搭出一套从指标到告警的完整方案。适合做债券投资、固收风控、量化研究的人阅读也适合想要把大模型嵌入到风控流程中的平台团队参考。2. 市场深度指标的口径选择与流动性度量从逐笔成交到分钟级深度估算2.1 市场深度的三个口径订单簿深度、报价深度与可成交深度市场深度直观理解是“在当前价格附近能消化多少买盘或卖盘”但落到数据上有三个层次。订单簿深度最精确来自交易所或交易平台的实时五档、十档盘口能看到每个价位挂单量。问题在于债券市场大量交易发生在场外OTC做市商双边报价只在特定平台可见。报价深度是次优口径来自做市商提供的 bid/offer 及对应可成交规模特点是更新频率低但更贴近真实可执行量。可成交深度属于估算口径当没有盘口数据时用最近成交价附近某一波动区间内累积的成交量来近似例如“过去 20 个交易日内成交价落在现价 ±0.5% 区间内的总成交量”。我在实际项目中做日频流动性监控优先用报价深度对历史回测则用可成交深度近似。三个口径之间不追求完全一致但要保证同一只债券在不同时期用同一个口径否则时间序列上的深度变化会被口径切换污染。2.2 用 Python 计算日频流动性指标Amihud、Roll、深度衰减率做一个计算流动性指标的最小工具集。下面代码把成交数据和做市商报价数据合并计算四个指标Amihud 非流动性、Roll 有效价差、报价深度按面值计、深度衰减率tail depth ratio。深度衰减率是自定义指标用于反映“在最优报价附近成交量是否迅速枯竭”。import pandas as pd import numpy as np def compute_liquidity_metrics(trades: pd.DataFrame, quotes: pd.DataFrame) - pd.DataFrame: # trades 字段: date, bond_code, price, return, volume(面值), turnover # quotes 字段: date, bond_code, bid_size(百万), offer_size(百万) # 1. Amihud 非流动性: 日收益率的绝对值 / 日成交面值 trades[date] pd.to_datetime(trades[date]) daily_ret trades.groupby([bond_code, date])[return].apply( lambda x: np.abs(np.prod(1 x) - 1) ).reset_index(nameabs_ret) daily_vol trades.groupby([bond_code, date])[volume].sum().reset_index() amihud daily_ret.merge(daily_vol, on[bond_code, date]) amihud[amihud] amihud[abs_ret] / (amihud[volume] 1e-6) # 2. Roll 有效价差: 用成交价协方差估算买卖价差(单位: bp) def roll_spread(group): cov group[price].diff().dropna().autocorr() return 2 * np.sqrt(max(-cov, 0)) * 10000 if ~np.isnan(cov) else np.nan roll trades.groupby([bond_code, date]).apply(roll_spread).reset_index(nameroll) # 3. 报价深度与深度衰减率 quotes[date] pd.to_datetime(quotes[date]) depth quotes.groupby([bond_code, date]).agg( bid_size(bid_size, sum), offer_size(offer_size, sum) ).reset_index() depth[depth_total] depth[bid_size] depth[offer_size] # 合并 result amihud.merge(roll, on[bond_code, date], howouter) result result.merge(depth, on[bond_code, date], howleft) result[depth_total] result[depth_total].fillna(0) return result # 使用示例 trades pd.read_csv(bond_trades.csv) quotes pd.read_csv(bond_quotes.csv) metrics compute_liquidity_metrics(trades, quotes)Amihud 的值越小说明单位成交量对价格冲击越小流动性越好。Roll 指标利用价格变动的一阶负自相关估算有效价差当协方差为正时返回 NaN这个情况在债券市场很常见因为成交笔数太少价格随机游走不要直接填零应该进入采缺失值处理。depth_total 表示做市商在最优价位附近累计提供的双边面值是流动性危机预警最直接的信号。深度衰减率需要引入分位数或距离档位做更细计算在 2.3 节处理。2.3 债券成交稀疏下的处理分位数聚合与伪交易剔除债券逐笔数据有两个天生问题一天可能只有几笔成交且部分成交来自大宗协议交易价格偏离市场报价很远。直接用日收益率计算 Amihud 会得到极端值。常见处理方法是把指标计算窗口从 1 天扩展到 5 天滚动窗口并对窗口内数据做截尾平均去掉最大和最小的 20% 样本。对大宗交易用报价中位数做过滤成交价偏离报价中位数超过 1.5% 的记录标记为协议交易不参与日收益率计算。下表给出我常用的参数组合。指标计算窗口数据要求异常处理Amihud5 日滚动至少 3 笔有效成交截尾 20%避免协议交易干扰Roll spread20 日滚动至少 10 个价格点协方差为正时置 NaN不填 0报价深度当日实时做市商报价快照报价超 24 小时未更新视为失效深度衰减率当日至少 2 档报价单档报价时取前后一档的迁移量做近似伪成交另一个来源是回购质押券的过手成交这类成交只是为了过户不反映真实流动性。识别方法到现在还没有统一规则我一般用同一笔债券同一个交易日内价格与上一笔完全一致、且成交面值超过该债券流通量 5% 的组合条件做剔除。这个规则宁可误删也不保留——流动性风险预警追求的是不遗漏危机而不是精确还原每一笔交易。做完这一步就拿到了可以进模型的流动性指标底表。3. 用 DeepSeek 做特征增强与流动性评分API 调用和提示策略3.1 为什么把 DeepSeek 放进因子建模而不只是做 NLP传统流动性模型用成交数据和报价数据做因子问题在于债券的流动性不是连续演化的它会在某个消息出现后断崖式恶化。比如发行人突然被下调评级展望、控股股东资产被冻结、甚至只是某家大型基金宣布赎回计划。这些信息体现在公告和新闻里要到下一个交易日才会反映到报价中。如果能提前解读就把预警窗口从 T1 提前到 T。DeepSeek 在链条里的位置是特征生成器不是最终决策器。它把非结构化文本变成结构化特征例如“负面情绪得分”“违约相关词命中数”“评级行动方向”再由传统模型统一处理。这样做的另一个好处是保留可审计性最终打分来自可解释的量化模型DeepSeek 只负责扩充信息维度。3.2 提示策略让 DeepSeek 输出结构化字段而不是作文调用方式和 OpenAI SDK 兼容。下面演示用 DeepSeek API 把新闻公告转成结构化标签这是整套流程里最需要打磨的部分。from openai import OpenAI client OpenAI( api_keyos.getenv(DEEPSEEK_API_KEY), base_urlhttps://api.deepseek.com ) def extract_risk_signals(text: str) - dict: prompt f 你是一个债券信用风险分析助手。请阅读以下新闻或公告提取与流动性风险相关的信号。 只输出JSON不要解释。 输入 {text} 要求 1. sentiment: 消息对流动性的影响取值为 negative / neutral / positive 2. risk_terms: 命中下面词汇的数量违约、冻结、下调、清盘、暂停、兑付、质押、抛售、赎回 3. event_type: 事件类型取值评级行动/经营恶化/融资紧张/市场情绪/无明确事件 4. confidence: 0到1的置信度0表示信息不足以判断 resp client.chat.completions.create( modeldeepseek-chat, messages[{role: user, content: prompt}], temperature0.1, # 特征抽取任务要低温度 response_format{type: json_object}, max_tokens500 ) return json.loads(resp.choices[0].message.content)temperature 设为 0.1 是为了让输出尽量确定这类特征抽取任务不能容忍随机发散。max_tokens 控制在 500 足够输出一个 JSON 对象。response_format 强制 JSON 输出避免解析失败。risk_terms 的词表需要结合债券市场常见的风险信号词迭代维护不是一次就能定稿的。3.3 融成 0-100 流动性评分因子权重与归一化拿到结构化信号后把它和第二章计算的市场深度指标融合。评分的逻辑分三层市场深度类指标为第一层权重最高指标客观波动与价差类为第二层文本信号作为第三层只做加减分避免让模型输出直接主导风控决策。def liquidity_score(metrics: dict, nlp_signals: dict) - float: # 深度分: 报价深度指标分位数映射到 0-60 depth_pct min(metrics[depth_total] / 50.0, 1.0) depth_score depth_pct * 60 # 价差分: Roll 指标越小越好, 映射到 0-30 roll_bp metrics[roll_bp] roll_score max(0, 30 - roll_bp / 10) # 文本加减分: 从 -10 到 10 nlp_score 0 if nlp_signals[sentiment] negative: nlp_score - 5 if nlp_signals[confidence] 0.8: nlp_score - 5 nlp_score max(-10, min(10, nlp_score)) score max(0, min(100, depth_score roll_score nlp_score)) return round(score, 1)这里面的阈值深度 5000 万面值对应满分Roll 价差 300bp 以上得 0 分属于经验值上线前要拿到各券种的历史分位数上重标定。另外文本信号处理有一个细节负面信号只在置信度高于 0.8 时才追加扣分否则只扣基础分这是为了防止舆情误报导致评分频繁抖动。3.4 用 deepseek-reasoner 复核因子单调性评分模型最容易犯的错误是因子方向搞反。比如某段时间高收益债成交活跃成交量因子显示“高流动性”但这其实是因为价格暴跌触发的抛售放量。对这种情况我一般把流动性风险特征表丢给 deepseek-reasoner 做一轮因子逻辑审查看它能否指出逻辑矛盾。review_prompt f 以下是一个债券流动性评分模型的因子定义 - 深度得分与做市商报价深度正相关 - 价差得分与 Roll 估计价差负相关 - 文本得分负面舆情越多分越低 已知在价格暴跌期间成交量可能急速放大。请判断哪些因子方向可能失真给出修正建议。 resp client.chat.completions.create( modeldeepseek-reasoner, messages[{role: user, content: review_prompt}], temperature0.2 )reasoner 的输出会带推理过程此时把推理过程和结论一起存档作为模型投产时的评审材料。这一步的价值在于评审材料里有了第三方视角的因子逻辑检查记录而不是只有建模工程师自己的解释。4. 流动性危机早期预警模型构建从评分到阈值触发与回测4.1 预警目标定义什么是“流动性危机”的可计算口径做预警模型之前必须把一个模糊的概念变成可计算的标签。我给“流动性危机”设置三个递进定义轻度预警黄色流动性评分连续 3 日低于 30 分或者报价深度 5 日累计衰减超过 70%中度预警橙色评分低于 20 分且 Amihud 指标超过该债券过去 6 个月 90 分位数严重预警红色上述基础之上买卖价差单日扩大超过 5 倍或做市商报价消失超过 24 小时这三个定义本质上是把连续的风险信号离散成业务可操作的级别。模型构建的核心目标是对黄色预警进行预测——在黄色条件触发前 5 个交易日发出预告给交易台留出处理窗口。橙色和红色不做预测只做实时监控因为那种状态往往是由突发消息驱动的统计模型预测不了应该交给规则引擎直接报警。4.2 用 XGBoost 训练“预告模型”特征滞后与时间序列切分预告模型的样本构造方式在每一天看未来 5 个交易日标的是否进入黄色预警状态。特征全部用截止当日的滞后数据严格禁止引入未来信息。import xgboost as xgb from sklearn.model_selection import TimeSeriesSplit # features: 每个 bond_date 对应的滞后特征 # 包括: 流动性评分5日均值、深度衰减率、Amihud 20日分位数、 # 负面情绪文本信号3日累计、券种信用评级、剩余期限 X feature_df.drop([label, date], axis1) y feature_df[label] # 未来5天内进入黄色预警为1, 否则为0 # 时间序列切分: 不能用随机 KFold, 否则未来数据泄漏进训练集 tscv TimeSeriesSplit(n_splits5) params { max_depth: 4, learning_rate: 0.05, subsample: 0.8, colsample_bytree: 0.8, eval_metric: auc, scale_pos_weight: 8 # 正样本远少于负样本, 提高少数类权重 } for train_idx, test_idx in tscv.split(X): dtrain xgb.DMatrix(X.iloc[train_idx], labely.iloc[train_idx]) dtest xgb.DMatrix(X.iloc[test_idx], labely.iloc[test_idx]) model xgb.train(params, dtrain, num_boost_round200, evals[(dtest, test)])scale_pos_weight 设为 8 是因为进入预警状态的样本很少不调整的话模型会永远预测“安全”。max_depth 保持较小防止模型记住个别债券的独特模式。TimeSeriesSplit 的关键意义在于训练集始终在测试集之前模拟真实上线时“用历史预测未来”的场景。4.3 模型输出与规则引擎联动不只看概率看异常状态组合模型输出的概率不能直接触发预警。原因在于概率是截面综合结果无法表达“变差的速度”和“绝对水平”两个维度。我采用的联动方式是触发预告需满足: 1. 模型预测概率 0.6 2. 且近3日流动性评分降幅超过 15 分 3. 且当前评分低于其自身6个月滚动中位数第一条拦住“常态低流动性”的券这类券本身流动性就差长期高概率不是危机萌芽第二条抓住恶化速度第三条剔除已经被市场定价的持续低迷。三条同时满足才发预告。这样的好处是大幅减少无效告警否则高收益债每天都触发监控人员很快疲劳。特征重要性可以辅助人工复核每个预告的具体诱因。把 xgboost 的特征重要性和对应的当日特征值打印出来作为告警附带的证据链。5. 本地化部署 DeepSeek 与把预警管线接入现有监控系统5.1 什么时候必须本地部署数据合规与低延迟证券债券数据涉及持仓和交易明细很多机构的数据不能出内网。远程调用 API 即使不传具体证券代码批量请求的模式也可能被识别合规上很难解释。所以需要本地部署 DeepSeek 模型。本地部署解决的不仅是合规问题还有稳定性监控系统的运行时间是按分钟走的不能依赖外部服务的可用性和限流策略。本地部署牺牲的是模型能力上限。蒸馏版模型在复杂推理上不如云端完整版但对做 JSON 结构化抽取这类任务影响很小。先跑通流程再根据效果决定是否升级。5.2 部署方案与显存参数速查本地部署 DeepSeek 常见方案是 llama.cpp 和 Ollama 这种推理框架对债券监控这种非高并发场景足够。要关注三个参数量化精度、上下文长度、批处理大小。配置项推荐值说明量化精度Q4_K_M综合显存占用和输出质量上下文长度4096公告和新闻单篇足够批处理1监控任务单条请求居多显存余量预留 20%防止长文本拉满导致 OOM启动后做一个最小验证确认本地服务可用curl http://localhost:11434/v1/chat/completions \ -H Content-Type: application/json \ -d {model: deepseek-r1:7b, messages: [{role: user, content: 只回复ok}], stream: false}返回 JSON 里的 choices[0].message.content 等于 ok说明服务正常可以接入管线。5.3 用 harness 工具编排预警 Agent 工作流把 DeepSeek 接入现有系统时我一般不用原生的 Python SDK 一把梭而是通过 harness 类工具做编排让 Prompt、模型端点、输出解析独立成配置避免把提示词硬编码到业务代码里。下面是一个最小配置示意# flow_agent.yaml workflow: name: bond_liquidity_monitor steps: - name: fetch_news source: internal_news_db - name: extract_signals model: local_deepseek prompt_template: prompts/risk_signals.jinja2 temperature: 0.1 - name: compute_score script: scripts/liquidity_score.py - name: alert_if_needed rule: scripts/prewarning_rules.jsonharness 的关键作用是把每一步的输出作为下一步的上下文输入天然形成数据血缘。后续要替换模型端点只需要改 model 字段不需要改业务代码。5.4 多模型路由ccswitch 管理远程与本地端点切换开发环境和生产环境的模型来源不同。开发时用远程 API 快速调试提示词生产切本地模型。ccswitch 这类工具可配置多个模型端点的路由策略把 Remote 和 Local 两个 profile 切换。ccswitch config add local-model --provider openai-compatible --base-url http://localhost:11434/v1 ccswitch config add remote-model --provider openai-compatible --base-url https://api.deepseek.com ccswitch use local-model # 生产环境 ccswitch use remote-model # 开发环境切换只是第一步关键是两套模型要在同一套测试集上做一致性验证。切换后输出结构相同的样本比例不低于 95%否则说明本地模型能力不足要么换更大参数量的版本要么把提示词写得更严格。6. 事件研究、阈值校准与可解释性预警模型上线前的最后三件事6.1 用历史压力时点做事件研究不看平均指标看单券路径回测平均 AUC 高不代表预警有效。要挑出历史上真实的流动性危机事件比如某只城投债二级市场崩盘、某民企债突发负面后的抛售逐个检查模型在事件发生前 5 天的行为。具体做法画出每只问题债券的流动性评分路径、模型概率路径和深度指标路径观察出现明显拐点的时间是否早于公开消息爆发。# 任一危机事件债券的路径打点 event_dates { 123456.SH: 2025-04-11, # 示例: 某债券流动性危机发生日 } for code, event_date in event_dates.items(): single metrics[(metrics[bond_code] code) (metrics[date] event_date - timedelta(days10)) (metrics[date] event_date timedelta(days3))] pre_event_alert (single[prob] 0.6).sum() print(f{code}: 事件前10天命中预警 {pre_event_alert} 次)检查目标不是要求每次危机都被提前捕捉而是确认模型没有漏掉那些冲击前有一到两天酝酿期的事件。事件研究比一个平均指标能暴露更多模型结构性缺陷。6.2 阈值不只是 0.5用 Youden J 做整条阈值曲面的校准预告模型的阈值不是二分类的 0.5 正负临界而是要按“提前 5 天告警”的目标调整。我一般用 Youden J 指数在验证集上搜索最优概率阈值from sklearn.metrics import roc_curve fpr, tpr, thresholds roc_curve(y_val, prob_val) youden_j tpr - fpr best_idx np.argmax(youden_j) best_threshold thresholds[best_idx] print(f最优阈值: {best_threshold:.3f})校准后还要看两个业务约束单日总告警数不超过运维极限比如每天 50 条以及危机债券的召回率不低于 80%。如果这两个约束和 Youden J 冲突以业务约束为准下调或上调阈值。这类调参记录下来作为模型版本更新的依据。6.3 让 DeepSeek 生成“预警说明卡”而不是裸概率风控人员看到裸概率没法快速决策。最后一步是把这个过程塞进监控页面每次触发预告时自动生成一张说明卡内容包括触发原因、主要因子变化、模型置信度、近期负面信号摘要。这段摘要由 DeepSeek 基于当日文本信号和因子重要性自动生成。explain_prompt f 以下是债券 {code} 的流动性风险信号 - 流动性评分: {score}较5日前下降 {drop} 分 - 主要负面信号: {signals} - 贡献最大的模型特征: {top_features} 请生成一段200字以内的预警说明写明 1. 该债券目前发生了什么 2. 哪类持有机构需要关注 3. 建议的行动方向关注/减仓/暂缓买入/立即评估 直接输出正文不要前缀。 行动方向建议不用“卖出”这种指令性措辞用“评估”和“关注”引导操作纪律。说明卡生成后跟着告警一起推送到监控群业务人员先看说明卡再决定是否进入人工复核流程。本文还有配套的精品资源点击获取

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

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

免费获取报价