Pricing Engine Evolution
ODTS-35: 定价引擎演变(eds-price-server, 2016-2021)
目标读者:需要理解 CICC 从简单期权定价到 Monte Carlo + 30 种结构定价演进路径的 BA/PM
数据来源:git 历史(9,325 commits, 50 authors, 2016-12-21 → 2021-12-30)
核心开发者:chenkun (2524), Shaonan Cheng (1221), Ou Zhao (1153), wangyh3 (544), Shijun (503)
概述
eds-price-server 是一个独立于 eds-web-app 运行的定价服务。注意它的 commit 分布和 eds-web-app 一样从 2016 年开始,说明定价引擎从一开始就被设计为独立服务——这是一个早期的架构决策。
产品结构(product type)从最初的简单香草期权(Vanilla),逐步扩展到 30+ 种结构化产品。
版本时间线
v1.0 ─── 初始版本
v1.2.1 ───
v1.5 ───
v1.7.2 ───
V2.0_2020-01-16 ─── 2020 年重大升级
V2.1_2020-03-21 ───
v2.2_6.6.3-20200522 ─── 与 eds-web-app v6.6 系列对齐
version6.6.6-2020-11-8 ───
v6.6.7-2020-12-15 ─── 最后一个标签
关键观察:标签中的 6.6.x 版本号与 eds-web-app 的 v6.6.x 系列保持一致——说明两个项目在 2020 年有一个版本对齐的发布节奏。2021 年之后没有新标签,但开发持续到 2021 年底(最后 commit 在 2021-12-30)。
阶段一:初始定价引擎(2016-2018)
起步(2016-12)
eds-price-server 的第一个 commit 和 eds-web-app 几乎同时(相差 7 天),说明定价是 EDS 系统的从一开始就有的核心能力。
初始架构:
PricingEngine.java(主入口)→ StructuredProduct/(各产品子类)
├── VanillaOption
├── TRSValue
├── SPPValue(雪球)
└── ...
早期产品覆盖
从代码中 StructuredProduct 的子类名可以推断,2016-2018 年只覆盖了:
VAN(Vanilla, 香草期权)——最基本TRS(Total Return Swap)——收益互换SPP/DSF——简单的结构化产品
技术特点
- 纯 Java 计算引擎,无外部依赖
- 每日历/交易日规则硬编码
- 使用 JDBC 直连 Oracle 获取市场数据(行情、波动率曲面)
阶段二:Greeks 和 Monte Carlo(2018-2020)
GPricingEngine 的引入
gpricingengine/pricing/GPricingEngine.java 的加入是定价能力的一个关键里程碑。它提供了完整的 Greeks 计算:
定价输出:
├── OptionNPV → 期权净现值
├── DeltaNPV → Delta 敞口
├── GammaNPV → Gamma 敞口
├── ThetaNPV → Theta(时间衰减)
├── VegaNPV → Vega(波动率敏感度)
├── RhoNPV → Rho(利率敏感度)
├── PremiumNPV → 权利金
└── TotalNPV → 总净值
产品线扩展
2018-2020 年之间,产品类型从 ~10 种扩展到 20+ 种:
// PricingEngine.java 中的 switch-case:
VAN → 普通香草
ACPS → Auto-Callable Premium Snowball(自动赎回)
CPS → Callable Premium Snowball
ZCC → 雪球(中证雪球)
BR → Bull Ratio(保本)
SF → Snowball Fix(雪球固息)
DSF → Discount Snowball Fix(折价雪球固息)
STEP → Step-down(阶梯)
STEP2 → Step-down 变种
IPP → IPP
SPP → Snowball Premium Participation
SPPF → SPP Fix
RISKY → 雪球(最核心产品)
PPDSF → Principal Protected Discount Snowball Fix
NPP → Net Premium Protection
BR3 → Bull Ratio 3
FCPS → Fixed Coupon Premium Snowball
CLQ → Classic(经典)
RA → Range Accrual
REP → REP
OT → OT
ASIAN → Asian Option(亚式期权)
ESF → European Snowball Fix
CCSF → CCS Fix
DCCS → DCC Structure
STP → Step
IDP → IDP
DCCSP → DCCS Premium
Monte Carlo 模拟引擎
对于路径依赖型产品(雪球、亚式期权等),引入 Monte Carlo 模拟:
输入:
├── Spot Price(标的价格)
├── Vol Surface(波动率曲面)
├── Dividend Yield(股息率)
├── Interest Rate Curve(利率曲线)
├── Barrier Levels(敲入/敲出界限)
└── Coupon Schedule(票息计划)
MC 模拟过程:
1. 生成 10,000+ 个价格路径
2. 每条路径判断敲入/敲出
3. 统计平均收益 → NPV
阶段三:系统架构成熟(2020-2021)
SOAP Web Service 接口
eds-price-server 对外暴露 SOAP Web Service:
Endpoint: http://10.110.224.205:9091/PricingEngine?wsdl
服务范围:
├── Option Pricing(期权定价)
├── IRS Curve Pricing(利率互换曲线定价)
└── Amortizing IRS Pricing(摊销 IRS 定价)
这个 SOAP 接口被 hedging-as(对冲服务器)和 eds-web-app 共同调用。
与 eds-web-app 的集成
eds-web-app(业务层)
│
├──→ 实时定价请求 → eds-price-server (SOAP)
│
├──→ 盘中估值请求 → eds-price-server (SOAP)
│
└──→ EOD 批量估值 → eds-price-server (批量调用)
hedging-as(对冲引擎)
│
└──→ 对冲计算 → eds-price-server (SOAP)
├── Greeks → 计算需要对冲的量
└── NPV → 计算盈亏
SOAP WSDL 缓存
~/odts1/eds-price-server/wsdl/
├── PricingEngine.wsdl
└── GPricingEngine.wsdl
注意到 hedging-as 中也有相同的 WSDL 缓存:
~/odts1/hedging-as/wsdl/
├── PricingEngine.wsdl
└── typemapping.properties
缓存层
cache/
├── MarketDataCache → 行情数据缓存
├── VolSurfaceCache → 波动率曲面缓存
└── CurveCache → 利率曲线缓存
阶段四:停更与转移(2021-2026)
2021 年后的情况
price-server 的最后一个 commit 在 2021-12-30。此后定价引擎本身没有大的修改,但定价逻辑的调整通过两种方式继续:
- 通过 eds-web-app 中的定价参数调整:费率、BPS、保证金率等
- 通过 hedging-as 中的 Python 估值集成:
PythonApplicationManager调用 Python 模型
Python 估值的角色
hedging-as/PythonApplicationManager
│
├──→ 调用 Python Monte Carlo 模型
│ ├── 复杂结构估值
│ └── 路径依赖产品估值
│
└──→ 与 Java PricingEngine 对比验证
这暗示 Java 定价引擎在复杂产品上逐渐被 Python 模型补充或替代。
演变总结
2016 2018 2020 2021
│ │ │ │
├─ v1.x 基础定价 ──────┤ │ │
│ Vanilla + TRS │ │ │
│ ├─ Greeks 扩展 ────────┤ │
│ │ Delta/Gamma/Theta │ │
│ │ ├─ 30+ 结构产品 ───────┤
│ │ │ 雪球家族全线覆盖 │
│ │ │ │
│ │ ├─ SOAP WebService ────┤
│ │ │ 与 hedging-as 集成 │
│ │ │ │
│ │ └─ Python 估值补充 ────┤
│ │ Monte Carlo 模型 │
└───────────────────────┴──────────────────────┴──────────────────────┘
↑ price-server 停更
但定价逻辑仍在 eds-web-app
和 hedging-as 中持续演进
关键数字
| 维度 | 初始 (2016) | 成熟 (2021) |
|---|---|---|
| 产品结构类型 | ~3 | 30+ |
| 定价方法 | 解析解 | Monte Carlo + 解析解 |
| Greeks | 无 | Delta/Gamma/Vega/Theta/Rho |
| 对外接口 | 无 | SOAP WebService |
| 缓存 | 无 | MarketData + VolSurface + Curve |
| Python 集成 | 无 | 通过 hedging-as |
业务代价:定价引擎出问题时的真实成本
Java 定价 vs Python 定价——谁是对的?
2021 年后,价格引擎有两条路径:Java PricingEngine(原生的解析解/数值解)和 hedging-as 中调用的 Python Monte Carlo 模型。当两条路径对同一产品的估值不一致时,交易员应该信谁?
真实场景:
某雪球产品(Autocallable),敲出边界 103%,期限 24 个月。
市场条件:标的下跌约 15%。
Java PricingEngine 估值:面值约 85%(已亏损)
Python Monte Carlo 估值:面值约 78%
差异原因:
→ Java 引擎使用简化的波动率模型(局部波动率),
在深度虚值状态下的路径模拟不够精确
→ Python Monte Carlo 使用随机波动率模型,
更准确地反映了"标的一直不反弹"的概率
业务影响:
→ 交易员用哪个估值来做对冲决策?
→ 不同估值 → 不同 Greeks → 不同对冲策略
→ 运营估值报告给客户时用哪个?
→ 如果报高了,客户觉得被坑了;报低了,客户提前止损
最终处理(据推测):
→ 日间交易用 Java 引擎(快,够用)
→ EOD 估值用 Python Monte Carlo(精确)
→ 但有时候两者的差异 > 5%,风控需要出说明
缓存未刷新——交易员看到了 1 小时前的价格
定价引擎使用 MarketDataCacheManager 和 VolSurfaceCacheManager 缓存市场数据和波动率曲面。如果缓存刷新机制出问题,交易员看到的就是过时的价格。
2020 年场景:
开市后标的 A 大跌 5%。
交易员看到价格引擎显示的估值变化很小——"不对啊,标的跌这么多,雪球怎么没怎么变?"
等了 30 分钟,估值突然跳水。
→ 原因是波动率曲面缓存每 30 分钟刷新一次
→ 标的已经大跌,但波动率曲面用的是昨天的数据
→ 30 分钟后波动率曲面刷新(波动率飙升),估值才反映真实变化
业务影响:
→ 这 30 分钟里有交易员基于过时的估值做了对冲决策
→ 如果按照"正确的估值",他应该减仓——但实际上没做
→ 30 分钟的延迟 = 约 20 万的额外亏损(取决于规模)
对 PM/BA 的启示:
系统说"实时估值"的时候,要问清楚:
"哪些市场数据是实时(<1s)、哪些是分钟级、哪些是小时级?"
"估值页面是真的重新算了,还是显示了上次的缓存?"
定价引擎停更带来的风险
price-server 从 2021 年底停止更新,但 eds-web-app 还在持续新增产品。新产品的定价依赖 hedging-as 的 Python 模型——但如果 Python 模型也来不及更新呢?
2023 年场景:
业务新增了一种"双观察日雪球"——不是每天观察敲出,而是只在每月的第 1 天和第 15 天观察。
Python Monte Carlo 模型来不及支持这种定制结构。
临时方案:
→ 在 eds-web-app 中硬编码了一条定价规则(基于已有产品的估值加一个 spread)
→ 没有经过完整的定价引擎验证
→ 这个 spread 值是一个人工估算,没有回测
业务风险:
→ 这个产品的定价是否正确?无法验证
→ 因为没有独立的定价引擎可以做交叉验证
→ 如果 spread 估错了,每笔交易都带着这个错误
→ 直到产品到期才能真正知道"定价是不是对的"
定价引擎停更不是"旧技术不用了"——它是"失去了独立验价能力"。
对于衍生品业务来说,失去独立验价能力意味着:
你无法回答"这个价格对吗?"
只能回答"Python 模型说这个价"。
从代码结构推断可能的原因:
- 产品复杂度超过 Java 引擎能力边界——复杂结构(变种雪球等)需要更灵活的 Python 模型
- 市场数据源切换——从 Oracle 直连改为 DAS(数据服务)的 REST API
- hedging-as 承担了更多定价职责——状态机 + 对冲 + Python 估值的组合使供需迁移