证券衍生品风控:VaR可复现计算与DeepSeek场景自动生成

发布时间:2026/9/17 20:06:39
证券衍生品风控:VaR可复现计算与DeepSeek场景自动生成 简介《DeepSeek证券衍生品风险管理方案》是一份面向量化风控从业者、衍生品交易与金融工程学习者的232页技术文档聚焦风险价值VaR计算、压力测试场景自动生成与Greeks风险指标管理等核心议题帮助读者理解大模型如何嵌入传统风控流程。全文共55个大章节从历史模拟法、蒙特卡洛模拟到方差-协方差法的VaR优化实现再到隐含波动率曲面建模、极端市场事件语义理解、多因子场景关联规则挖掘、Delta对冲实时建议与Gamma动态预警等均有展开。包内仅1个PDF文件体积约11.25MB支持目录章节跳转、阅读器左侧书签大纲显示与章节快速定位正文、图表与目录均显示完整。目前已有120人学习关注适合希望系统梳理大模型在证券衍生品风控落地路径的读者按章节查阅与对照实践。1. 证券衍生品风控最尴尬的场面VaR 算得出来场景却写不出来证券衍生品风控最尴尬的场面不是算不出模型而是算完没人敢用一个含场外期权、收益互换和雪球结构的组合日终只报一个 VaR 数字交易台追问「标的跳空 8% 会怎样」「波动率曲面整体上移 20 个点会怎样」风控只能现场手拼场景。DeepSeek 在这类方案里的位置不是定价也不是替人拍板而是把风险价值计算的口径固化成可复现的代码路径把压力测试场景自动生成成结构化、可审计、能直接喂给定价引擎的因子冲击清单。它解决的是覆盖率与一致性问题几千个头寸、几十个风险因子靠人写场景必然漏。适合已有 Python 或 Java 定价库、但场景库靠个人经验维护的券商自营、资管与期货风控团队。232 页量级的完整方案里真正卡住落地的通常是 VaR 口径对齐和场景自动生成这两段。2. 风险价值计算的可复现实现历史模拟法与参数法的口径对齐同一个组合在两个系统里算出两个 VaR差异几乎从不来自公式而是来自口径。置信水平取 99% 还是 97.5%、持有期是 1 日还是 10 日、样本窗口是 250 日还是 500 日、期权用一阶近似还是全价重估这四项任意一项不同结果就能差出 30%。这一章先把参数钉死再给出可以直接跑的历史模拟法实现最后讨论什么情况下必须放弃解析法。2.1 置信水平、持有期与样本窗口三个必须钉死的参数置信水平决定尾部估计的噪声水平。取 99% 意味着 500 个样本里只有 5 个点落在阈值之外用 5 个点估计分位数方差极大。常见折中是日常限额用 95%资本计量用 99%同时把期望损失一并报出减少「VaR 相同但尾部厚度完全不同」的争论。持有期的选择要跟着平仓周期走而不是跟着报表周期走。流动性好的股指期货用 1 日场外雪球、亚式期权这类难平仓结构用 10 日甚至更长。用平方根法则把 1 日 VaR 放大到 10 日前提是收益独立同分布且头寸线性含 gamma 的组合套用会系统性低估。样本窗口要和因子结构匹配。250 日覆盖不了一次完整的波动率抬升周期750 日又会把不同市场状态混在一起。我一般取 500 个交易日或者改用 EWMA 加权日频衰减因子 λ 取 0.94让近端波动主导。参数常见取值取值偏大的后果取值偏小的后果置信水平95% / 97.5% / 99%尾部估计噪声大、资本占用高突破频繁、限额失去约束力持有期1 日 / 10 日平方根缩放误差被放大覆盖不了实际平仓周期样本窗口250 / 500 / 750 日混入过期市场状态覆盖不到极端行情衰减因子 λ0.94日频/ 0.97月频对近期波动不敏感指标日间跳动剧烈还有两个口径要在文档里写死绝对 VaR相对初始市值还是相对 VaR相对期望损益以及全价法还是损益法。口径没写死模型验证阶段一定会吵。2.2 用 Python 跑通组合级历史模拟 VaR 的最小可复现路径历史模拟法的核心是把历史上的因子收益直接重放到当前头寸上。下面这段代码假设你已经有风险因子时间序列和一套因子敞口敞口可以来自 delta 汇总也可以从历史损益回归反推。import numpy as np import pandas as pd def historical_var(risk_factors: pd.DataFrame, # 列因子行交易日 exposures: np.ndarray, # 组合在各因子上的敞口 confidence: float 0.99, holding_days: int 1, window: int 500) - dict: # 只保留最近 window 个交易日避免混入不同市场状态 rf risk_factors.iloc[-window - 1:] # 价格类因子用对数收益利率类因子需单独用差分后再合并 rets np.log(rf / rf.shift(1)).dropna() # 一阶近似组合模拟损益 敞口向量 点乘 因子收益 pnl rets.values exposures # 持有期缩放仅在收益独立同分布且头寸线性时成立 pnl pnl * np.sqrt(holding_days) # 左尾分位数取负得到正数形式的 VaR var -np.percentile(pnl, (1 - confidence) * 100) tail pnl[pnl -var] return { var: float(var), es: float(-tail.mean()), # 期望损失对尾形更敏感 obs: int(len(pnl)), worst: float(-pnl.min()), # 历史最差一次重放 }逻辑说明先按窗口截断样本再用对数收益构造因子变动矩阵然后用一次矩阵乘法把因子历史重放成组合损益序列最后取左尾分位数。整个过程没有分布假设这是历史模拟法最大的优点也是它最大的缺点——它只能看到历史上发生过的冲击。参数说明window取 500 是经验折中低于 250 时尾部分位数基本不可信holding_days的平方根缩放对含 gamma 的期权组合会低估正确做法是用重叠窗口直接抽 10 日收益代价是有效样本数掉到十分之一confidence与限额体系绑定同一张报表里不要混用两个置信水平。注意利率类因子收益率曲线节点不能直接做对数差分负利率情形下会直接报错。常见做法是对绝对值做差分把单位统一成基点再和价格类因子拼成同一个矩阵但要记得两者量纲不同分位数合并前先做标准化。2.3 参数法、Cornish-Fisher 修正与全价重估的取舍参数法方差-协方差算得快VaR z * sqrt(wΣw)适合几千个线性头寸的实时监控但完全不认识期权的非线性。Cornish-Fisher 修正在正态分位数上加偏度和峰度项能部分修正厚尾代价是当偏度绝对值超过 1 时结果可能失去单调性。import numpy as np from scipy.stats import norm def cornish_fisher_quantile(p: float, skew: float, excess_kurt: float) - float: 在正态分位数上做偏度/峰度修正p 取 0.01 表示 99% 置信左尾。 z norm.ppf(p) return (z (z ** 2 - 1) * skew / 6 (z ** 3 - 3 * z) * excess_kurt / 24 - (2 * z ** 3 - 5 * z) * skew ** 2 / 36)逻辑说明把标准正态分位数按三阶、四阶矩展开修正得到更能反映厚尾的分位数。参数说明p是左尾概率与置信水平互补skew是组合损益偏度excess_kurt是超额峰度正态为 0。这两个矩必须来自组合整体损益而不是单个因子否则修正没有意义。含深度虚值期权、敲入敲出结构的组合唯一可信的做法是全价重估把因子冲击写回定价参数重新跑一遍定价引擎。这套引擎同时也是压力测试的执行器两处共用一套定价代码能省掉大量口径对账工作。方法数据依赖非线性处理单次耗时量级适用场景方差-协方差协方差矩阵不支持毫秒线性头寸实时监控历史模拟500 日以上因子历史一阶近似秒组合级日终 VaRCornish-Fisher组合偏度峰度部分修正毫秒厚尾线性组合全价重估完整定价引擎精确分钟到小时期权组合、压力测试3. 压力测试场景自动生成用 DeepSeek 把因子冲击变成可执行清单人工写场景的问题是覆盖率和一致性都靠人。同一个人今天写的「波动率跳升」用 20 个点明天用 15 个点跨月就不可比。DeepSeek 在这条链路上的角色是场景生成器给它风险因子清单、当前市场状态和历史极值锚点让它输出一批结构化冲击向量再由规则层做硬校验。生成和校验必须分开模型负责多样性代码负责正确性。3.1 场景模板从风险因子冲击到损益映射场景本身是三层结构因子冲击层、映射层、损益层。因子冲击层描述标的价、隐含波动率、偏度、相关系数、无风险曲线、汇率、基差这些量怎么变映射层把因子变化翻译成定价引擎的入参比如波动率曲面要按 ATM、skew、tenor 三个维度分别冲击损益层做全价重估。用一个统一 JSON schema 描述好处是场景可以入库、可以版本对比、可以直接被回测脚本消费。{ scenario_id: HYP-VOL-SHIFT-01, type: hypothetical, horizon_days: 1, revaluation: full, factors: { equity_price: {shock_type: relative, value: -0.08}, implied_vol_atm: {shock_type: absolute, value: 0.20, unit: vol_point}, implied_vol_skew: {shock_type: relative, value: 0.15}, ir_curve: {shock_type: parallel, value: 0.0050}, correlation: {shock_type: absolute, value: 0.10} }, anchors: [hist_max_1d_down, vol_spike_95pct] }shock_type区分相对冲击和绝对冲击这一步不能省波动率的 20 个点差异用相对值还是绝对值结果能差一个数量级。anchors字段记录这个场景参考了哪个历史极值或统计分位事后审计时能说清生成依据。horizon_days决定是瞬时冲击还是路径冲击雪球类结构必须用路径。3.2 调用 DeepSeek 生成场景请求体、提示词与结构化输出DeepSeek 开放平台提供 OpenAI 兼容接口直接换base_url和 key 就能复用现有 SDK。场景生成要求稳定输出温度调低并且强制 JSON 输出避免模型把 JSON 包在解释文字里。import json import os from openai import OpenAI client OpenAI( api_keyos.environ[DEEPSEEK_API_KEY], base_urlhttps://api.deepseek.com/v1, ) PROMPT 你是证券衍生品风控场景设计助手。 当前组合风险因子{factor_list} 当前市场状态{market_state} 历史极值锚点{anchors} 请生成 {n} 个压力测试场景每个场景给出因子冲击向量。 要求 1. 冲击幅度不得小于历史锚点不得超出 {limit} 给出的边界 2. 价格下跌类场景必须同时考虑波动率上升方向不能矛盾 3. 只输出 JSON结构为 {{scenarios: [...]}}不要输出任何解释。 resp client.chat.completions.create( modeldeepseek-chat, temperature0.2, # 场景生成要稳不要发散 response_format{type: json_object}, messages[ {role: system, content: 只输出合法 JSON不要输出 markdown 代码块。}, {role: user, content: PROMPT.format( factor_listfactors, market_statestate, anchorsanchors, n40, limitlimits)}, ], ) payload json.loads(resp.choices[0].message.content)逻辑说明system prompt 负责格式约束user prompt 负责业务约束两者分开写改业务规则时不用动格式部分。temperature设 0.2 是为了让同一份输入生成的场景大体稳定同时保留一点多样性如果设 0每天生成的场景会高度重复覆盖不了新风险。参数说明response_format用json_object能显著降低解析失败率但仍要在外层套 try/except 并对失败结果重试n按因子维度控制因子多的时候一次 40 个场景分两三批生成再合并单次请求不要塞太多。如果持仓数据不能出内网可以本地化部署一套权重用兼容 OpenAI 协议的推理服务起内网端点把base_url换成内网地址。代价是场景语言理解和多样性弱一些需要把边界约束写得更死。按 token 计费的商用接口在日终批量生成场景这个用量下成本可控具体单价以开放平台当期价目为准不要写死在配置文件里。3.3 生成结果的硬校验边界、符号与历史锚点模型输出必须过三道校验才能进场景库。第一道是边界裁剪每个因子的冲击幅度都要落在风控委员会确认的区间内。第二道是符号一致性价格暴跌场景里波动率不能下降相关系数不能反向。第三道是历史锚点对比生成的最坏场景不能比历史极值还温和否则场景库形同虚设。场景族生成方式典型因子冲击主要用途历史情景从历史窗口重放因子收益复用真实路径VaR 回溯验证假设情景DeepSeek 生成 规则约束多因子联合冲击日常压力测试反向压力测试反解使损失触及限额的冲击求解因子向量限额校准单因子敏感性规则枚举阶梯值单因子小幅扰动损益归因与解释LIMITS { equity_price: (-0.30, 0.30), implied_vol_atm: (-0.30, 0.50), ir_curve: (-0.02, 0.02), } def validate_scenario(sc: dict) - list[str]: errs [] factors sc.get(factors, {}) for name, item in factors.items(): lo, hi LIMITS.get(name, (-1e9, 1e9)) v item.get(value, 0.0) if not (lo v hi): errs.append(f{name} 冲击 {v} 超出边界 [{lo}, {hi}]) # 符号一致性价格下跌时波动率必须上升或持平 if name equity_price and v -0.03: vol factors.get(implied_vol_atm, {}).get(value, 0.0) if vol 0: errs.append(f{sc[scenario_id]} 价格下跌但波动率下降方向矛盾) return errs逻辑说明校验函数只做确定性判断不依赖模型。参数说明LIMITS的边界由风控委员会定通常参考历史极值的 1.5 到 3 倍改动要走审批留痕符号规则可以按因子对扩展比如汇率与利率的联动方向。除这三道之外还要做去重把因子向量归一化后算欧氏距离距离小于阈值 0.02 的场景只保留冲击更极端的那个否则 40 个场景里可能有 15 个实质相同。提示把每次调用的模型名、温度、提示词哈希、生成时间一起写入场景表的审计字段。半年后回看某个场景能查到它是哪次生成、过了哪些校验这比场景本身更重要。4. 全面风控覆盖的日终编排VaR 与压力测试接进流水线算得准不等于天天能出数。日终风控的真正难点在编排行情什么时候到、曲线什么时候建好、持仓快照取哪一版、VaR 和场景重估谁先谁后、失败了谁来补。这一章给出一条可直接照搬的流水线以及限额阈值怎么设、出问题先看哪里。4.1 数据链路与任务依赖从行情落地到指标入库一条典型的日终链路是行情与曲线落地 → 持仓与敞口快照 → 因子映射与质量检查 → VaR 计算 → 场景生成与全价重估 → 指标入库与报表发布。关键在于依赖顺序不能乱场景重估必须等曲线和持仓都稳定之后跑否则半天算力全废。#!/usr/bin/env bash set -euo pipefail RISK_DATE${1:-$(date %F)} export RISK_DATE python -m risk.ingest.market --date $RISK_DATE --source mdw --retry 3 python -m risk.ingest.position --date $RISK_DATE --book ALL --snapshot eod python -m risk.factor.quality --date $RISK_DATE --report /data/qa/factor.json python -m risk.var.historical --date $RISK_DATE --window 500 --confidence 0.99 python -m risk.scenario.generate --date $RISK_DATE \ --top-k 40 --validate --out /data/scenarios python -m risk.scenario.revalue --date $RISK_DATE \ --engine pricing --incremental --workers 16 python -m risk.report.publish --date $RISK_DATE --target riskdb逻辑说明set -euo pipefail保证任何一步失败立即中断避免半成品数据入库。因子质量检查单独成步是为了在 VaR 之前拦住脏数据——宁可不出版本也不要出一个错的 VaR。参数说明--retry 3针对行情源抖动--snapshot eod明确取日终快照而不是盘中版本--incremental让重估只跑新增或变动场景日终大组合能省掉一半以上算力--workers 16按重估引擎的 CPU 核数调定价引擎通常是 CPU 密集。任务调度用 Airflow 或类似工具时把每个python -m映射成一个 taskposition_snapshot和market_data_landing并行scenario_revalue依赖两者。失败重跑要能按 task 粒度重来而不是整条链重跑。4.2 限额阈值与告警参数VaR、ES 与场景损失怎么配比限额体系不能只挂一个 VaR。合理的做法是三层VaR 管日常波动ES 管尾部厚度场景损失管极端情形。三层阈值互相独立设置任意一层突破都要触发动作这样可以避免「VaR 没超但极端场景已经爆掉」的盲区。指标计算口径告警阈值示意触发动作1 日 VaR99% 置信500 日窗口限额的 85%邮件通知交易台1 日 VaR同上限额的 100%冻结新增头寸风控复核期望损失 ES99% 左尾均值限额的 90%提高监控频率至日内最差场景损失场景库全量重估限额的 120%启动对冲方案评估突破次数250 日滚动连续 2 日突破强制模型复核阈值不是拍脑袋定的一般用回溯结果校准拿过去 3 年的历史数据跑一遍看 99% VaR 的突破次数是否接近理论值一年约 2.5 次。突破过多说明模型低估突破过少说明资本浪费。4.3 常见失败模式排查先看哪张表、先跑哪条命令日终不出数时排查顺序比排查技巧更重要。我一般按这个顺序走先看因子质量报告再看持仓对账最后看引擎日志。因子缺失或停牌是最常见的源头。检查因子质量报告里stale标记的比例超过 5% 就要停下来处理。常见做法是用高度相关的代理因子插补并在指标上打标而不是直接跳过——跳过会让组合敞口凭空少一块。持仓与敞口不同步表现为名义本金对不上。用行数和名义本金两个维度交叉对账只看行数会漏掉数量改错但行数没变的情况。VaR 单日跳变多数是滚动窗口里掉出了一个极端日。把当日窗口内最差 5 个损益点打出来就能判断是市场真变了还是数据接入变了。场景不可执行通常是参数超出了波动率曲面的插值边界。校验层要提前拦截并做边界裁剪同时记录被裁剪的场景避免每天生成一批实际跑不了的场景。接口调用超时或解析失败必须配置降级路径重试两次仍失败就落回本地规则场景库保证日终有数可出同时在报表上标注本次场景来源后续补跑。5. 模型验证与生成场景的可信度Kupiec 检验与幂等性回归VaR 上线后要做回溯检验最通用的工具是 Kupiec 的 PO F 检验统计实际突破次数和理论突破次数是否一致。import numpy as np from scipy.stats import chi2 def kupiec_pof(exceptions: int, observations: int, confidence: float 0.99) - dict: p 1.0 - confidence if exceptions 0: lr -2.0 * observations * np.log(1.0 - p) else: p_hat exceptions / observations lr -2.0 * ( np.log((1 - p) ** (observations - exceptions) * p ** exceptions) - np.log((1 - p_hat) ** (observations - exceptions) * p_hat ** exceptions) ) return { lr: float(lr), p_value: float(1.0 - chi2.cdf(lr, df1)), exceptions: exceptions, observations: observations, }逻辑说明LR 统计量服从自由度 1 的卡方分布p 值小于 0.05 说明实际突破次数与设定置信水平显著不符模型需要重估。参数说明observations一般取 250 或 500 个交易日样本太短检验没有功效突破的判定要用当日实际损益和上一日算出的 VaR 比较不能拿当日的 VaR 比当日的损益那是前视偏差。对 DeepSeek 生成的场景验证方式不同于数值模型。我做两类回归一是幂等性回归同一份输入、同一组参数跑两次把场景向量四舍五入到固定小数位后做哈希哈希应一致不一致说明温度或提示词有随机源没固定二是覆盖度回归把当日生成的场景库与前一日的做并集比对看新增场景是否集中在某几个因子长期只覆盖价格因子、不覆盖偏度和基差说明提示词里的因子清单需要重新组织。一个能省大量算力的技巧是增量重估日终生成的场景里通常只有 20% 到 30% 是新增或因子冲击有变动的其余与昨日一致。把场景向量规范化后做指纹只对指纹变化的场景调定价引擎其余直接复用昨日结果并更新日期戳。一个含几千个场外期权头寸的组合全量重估要跑两三个小时增量重估能压到二十分钟以内日终窗口才真正排得下。指纹要包含因子冲击、定价参数映射版本和引擎版本三项任意一项变了都必须重算否则复用出来的数字不可比。本文还有配套的精品资源点击获取