Learning
VOL. VII · NO. 54 · OTC Derivatives · 19 JUL 2026

Trade Lifecycle

OTC 衍生品 · 19 JUL 2026 · 16 min read · 3,586 words
· · ·

ODTS 05 — 交易生命周期:从询价到结算

为什么需要这篇文档

业务型实习生加入交易台后,知道 Java、Spring Boot、Vue,读过 01–04 了解了系统演进,但还是答不出最重要的问题:

客户想交易一个 XXX。发生了什么?哪段代码跑了?什么顺序?

这篇文档追踪一笔交易在 ODTS 中从端到端的路径。每一步说清楚:业务事件是什么、对应什么纸质单据 (paper)、哪个软件组件处理、关键类在哪里。


第一阶段:交易前(定价与报价)

1.1 客户询价

业务: 客户(对冲基金或私人银行)电话/邮件/IM 联系销售:“一个 6 个月中证 500 雪球,80/100 障碍,报价多少?”

销售 在前台定价工具里配参数——标的、期限、障碍、名义本金、币种、杠杆。这是试算 (what-if),还没有交易存在。

1.2 定价引擎计算报价

定价速度就是业务速度: 从销售收到询价到报价发回客户,时间窗口通常是 15-30 分钟。超过 30 分钟,客户可能已经去别家询价了。系统报价模块的响应时间决定了销售一天能做多少个询价——不是理论上的”能不能算出来”,是”能不能在客户挂电话之前算出来”。

系统: hedging-as(定价应用服务)

hedging-as/src/com/cicc/action/quotation/  — 报价入口
hedging-as/src/com/cicc/pricing/          — 定价引擎核心
hedging-as/src/com/cicc/pricing/model/    — 定价领域对象
hedging-as/src/com/cicc/pricing/calculation/ — 分析方法

关键类:

  • QuotationAction — 接收前端送来的参数,传给引擎
  • PricingService — 编排计算:加载市场数据 → 构建收益函数 → 运行模拟
  • PricingEngine — 分析引擎(蒙特卡洛 / PDE / 解析解)
  • edslib/payoff/ — 雪球、TRS、NDF 等产品的收益函数定义
  • edslib/valuation/ — 估值方法(贴现、Greeks)

内部流程:

  1. QuotationAction 反序列化前端请求为 QuotationRequest,包含:产品类型、标的、名义本金、障碍(雪球)、利差(TRS)、定盘日历、币种、起始日、到期日
  2. 请求路由到 PricingService.getPrice()
    • dataCenter/ 加载即期价、股息率、利率、波动率曲面
    • 调用 edslib.payoff.PayoffFactory.create(productType, params) 构建收益函数
    • 调用 edslib.valuation.MonteCarloValuation.evaluate()ClosedFormValuation.evaluate()(取决于产品类型)
  3. 结果——中间价、买卖价差、Greeks(delta/gamma/vega/theta)——返回为 QuotationResult

修定价 bug 的入口: hedging-as/src/com/cicc/pricing/service/PricingService.javagetPrice()。收益函数在 edslib/payoff/,你的产品可能是 SnowballPayoff.javaTrsPayoff.java

1.3 销售谈判与确认

业务: 销售加上交易台的利润(例如 +50bp),发给客户。客户接受。

系统: 报价保存为 QuotationRecord(status=PENDING)。还没有具有法律约束力的合约。


第二阶段:交易簿记(交易录入)

2.1 交易员簿记交易

业务: 交易员将商定的条款录入系统。这叫簿记 (booking)——交易成为有约束力的合约的时刻。

系统: eds-web-app(核心 EDS 后端)通过 new-edsweb(Vue 2 SPA)

new-edsweb/src/views/option-contract/       — 合约簿记页面
eds-web-app/src/.../ContractController.java
hedging-as/src/.../contract/Contract.java   — 交易领域模型
hedging-as/src/.../contract/ContractSaveService.java — 持久化

UI 展示的内容:

  • 合约类型选择器: TRS / NDF / Snowball / Vanilla Option / Barrier Option / Swap
  • 合约条款表单: 根据类型不同最多 80 个字段(标的、名义本金、行权价、障碍、敲入/敲出、融资利率、利差、定盘日、付款日、结算币种等)
  • Leg 编辑器: 大多数 OTC 交易有 2 个以上的 legs。TRS 有融资 leg + 价格回报 leg。Swap 有两个固定/浮动 leg。每个 leg 单独定义。
  • 对手方选择器: 从 CRM 拉取 (customerInfo)

后端做的事 (ContractController / ContractAction):

簿记错误的业务代价: 80 个字段的表格,交易员在几分钟内填完。2019-2021 年间,平均每月有 2-3 笔交易因人工录入错误需要修正——比如名义本金多打一个零(从 1000 万变成 1 亿)、对手方选错、日期不一致。每次修正需要:交易员提交修改申请 → 中台审核 → 合规确认。平均每笔修正耗时 2-4 个工作日。更严重的情况:如果错误被确认书环节发现(交易已经发生但法律文件错了),修改需要对手方重新签署确认书——多 1-2 周。

  1. 校验: ContractSaveService.validateTerms() 检查产品类型与 leg 结构兼容、标的在参考数据中存在、对手方有 ISDA 资质且不在限制名单、日期一致、名义本金在授信额度内
  2. ID 生成: 系统生成 contractId(如 TRS20250716001),前缀编码产品类型,中间是交易日,后缀是序号
  3. 持久化: ContractHelper.save() 写入 CtrContract 表及关联的 leg 表
  4. 状态: PENDING_APPROVAL——交易尚未合法生效

找合约的 DB 记录: 主表是 CtrContract。唯一标识符(如 TRS20250716001)是所有子表的主键。Legs 在 CtrContractLeg,现金流在 CtrContractLegCashFlow

2.2 簿记数据结构

核心领域模型在 hedging-as 里(因为定价是生命周期中逻辑最重的部分):

hedging-as/src/com/cicc/service/contract/model/
├── ContractLegStruct.java          — leg 结构(收/付、币种、时间表)
├── ContractInstrumentRelate.java   — 工具到合约的映射
├── ContractUnderlyingRelate.java   — 标的到合约的映射
├── ContractNotional.java           — 名义本金(金额 + 币种 + 时间表)
├── ContractWithLegs.java           — 聚合对象:合约 + 所有 legs
├── ContractMonitor.java            — 监控状态
└── TodayContractRaInfo.java        — 风险分析快照

CtrContract 的一行:

contractId    VARCHAR(32) PK     — 如 TRS20250716001
productType   VARCHAR(16)        — TRS, NDOLLAR, SNOWBALL, SWAP, OPTION
tradeDate     DATE               — 簿记日
startDate     DATE               — 生效日
maturityDate  DATE               — 到期日
ccy           VARCHAR(3)         — 结算币种
counterpartyId VARCHAR(32)       — 外键→CounterParty
status        VARCHAR(16)        — PENDING_APPROVAL, ACTIVE, TERMINATED, EXPIRED
notional      DECIMAL(20,4)
version       INT                — 修改时递增

第三阶段:交易后处理

3.1 信用与合规检查

业务: 交易生效前,中台 (middle office) 检查:客户在授信额度内吗?交易符合 ISDA 要求吗?对手方在制裁名单上吗?

系统: eds-web-appcustomerInfo 模块 + blackOrWhiteList 模块

  1. BlackOrWhiteListController 检查对手方是否在监管黑名单上(OFAC、欧盟制裁等)
  2. CustomerMarginController 检查授信使用率:所有活跃交易的名义本金之和 vs 对手方的授信额度
  3. 检查通过后,合约状态从 PENDING_APPROVALACTIVE

业务规则: 信用检查是软性的——失败了进人工审核。黑名单检查是硬性的——永久阻止交易。

3.2 确认书生成

业务: 交易需要一份法律确认书 (confirmation document)——用 ISDA 标准法律语言写的合约条款。双方都需要签署。

系统: eds-web-app — 确认书引擎

eds-web-app/src/com/cicc/action/contractFile/  — 确认书文件生成
eds-web-app/src/.../controller/ContractController.java
  ├── startPdfConvert()    — 批量 Word→PDF
  └── generateFile()       — 生成确认书文档
  1. startPdfConvert() 触发 ContractFileBatchOperateAction:读取交易台所有活跃合约,逐条渲染到 Word 模板,转成 PDF
  2. 模板引擎将合约数据合并到 ISDA 主确认书模板
  3. 生成的 PDF 上传到 S3,在 ContractFile 表中建立关联
  4. 确认书通过邮件或确认书门户发送给对手方

确认书争议的现实: 并非每笔交易都能顺利签署。2020-2022 年间大约 2-3% 的确认书遇到争议——通常是对手方不同意某一项条款措辞。最常见的争议原因不是交易要素错误,而是法律措辞(比如终止事件的定义范围)。争议确认书的平均解决时间是 2-4 周,期间合约状态卡在 PENDING,双方的法定权益处于不确定状态。2021 年有一笔大额 TRS(名义本金 5 亿)因为对手方法务部门对 ISDA 定义有不同解读,确认书争议了 6 周才签署。

模板目录: eds-web-app/template/ 包含 Word 模板文件。每个产品类型有自己的模板。

3.3 对手方签署

业务: 对手方审阅并签署确认书。这可能需要几天或几周(有些对手方很慢)。

系统: 确认书状态在 CtrContract.confirmationStatus 追踪:PENDING(待签)、SIGNED(已签回)、DISPUTED(争议)


第四阶段:生命周期管理(日常工作)

交易生效后,每天都有工作:定盘重置 (fixing reset)、保证金追缴 (margin call)、票息支付、公司行为处理。

4.1 日固定盘与利率重置

业务: 交易的浮动 leg 定期重置。TRS 的融资 leg 每天/每月重置为 SHIBOR/TONA。雪球的票息观察日决定是否付票息。

系统: hedging-as — EOD 批处理

EOD 流程:

  1. FixingScheduler 在日终触发(可配置,通常是北京时间 17:00)
  2. 对每个有浮动利率敞口的活跃合约:
    • eds-price-server 读取定盘利率(聚合自路透/彭博)
    • 计算利息/票息金额:名义本金 × 利率 × 计息因子
    • CtrContractLegCashFlow 中生成 CashFlowRecord
  3. 对雪球合约:检查是否发生了敲出或敲入事件(标的价相对障碍位)
  4. 对 NDF 合约:计算定盘价 vs 合约价的结算金额

4.2 按市值计价 (Mark-to-Market)

业务: 每笔交易有一个当前市场价值 (market value)。交易台需要知道:“如果我们今天平掉这笔交易,要收多少钱或付多少钱?”

系统: hedging-as — 定价引擎(和报价时同一个引擎,但每晚批量跑)

每晚:ValuationAction.startValuation() 触发所有活跃合约的估值 → PricingService.revalue() 加载当前市场数据并重新运行收益函数 → 结果存入 ValuationResult / TodayContractRaInfoCtrContractEod 更新合约的日 P&L、delta、gamma、vega、theta

4.3 保证金与担保品管理

业务: OTC 交易由担保品 (collateral) 保障。如果交易对你有利 (in the money/ITM),对手方可能需交担保品。如果对你不利,你交担保品。

系统: hedging-as — margin 模块

  1. MarginCalculator 按合约计算初始保证金 (IM) 和变动保证金 (VM)
  2. IM 基于 VaR 模型:“这笔交易如果清算需要 5 天,会亏多少?”
  3. VM 就是按市值计价:如果交易对客户价值 +1000 万,客户必须交 1000 万
  4. 结果发布到 MarginCall 表推到前端给担保品团队

4.4 公司行为

业务: 标的股票分红、拆股、合并。交易的条款可能需要调整。

当 TRS 的标的股票分红时:

  1. eds-price-server 从市场数据检测到分红事件
  2. ContractDividendHelper 识别所有受影响的合约
  3. 对 TRS:分红记入/扣减给相关方(取决于总收益互换的性质——收益接收方获得分红)
  4. ContractDividendHelper.adjustForDividend() 调整合约的现金流计划

4.5 定盘/结算事件

业务: 在定盘日,系统读取定盘利率,计算结算金额,生成支付义务。在付款日,系统检查敲出/敲入状态,如果障碍未被触发,票息到期,生成资金变动。


第五阶段:对冲管理

业务: 交易台卖给客户雪球时,不会保留风险。他们会对冲——通常买入/卖出标的股票或指数期货。

系统: 对冲通过独立的对冲交易系统或 OMS 管理。

  1. 定价引擎计算每笔交易的 delta、gamma、vega
  2. 对冲交易台汇总每个标的的净 delta
  3. 净 delta 通过买入/卖出期货或再平衡期权来对冲
  4. 对冲效果通过对冲 P&L 与客户交易 P&L 的对比来监控

关键监控工具: odts-option-web 的实时对冲仪表盘、单合约 delta/gamma 视图、hedging-as 的 EOD 对冲头寸计算


第六阶段:监管与会计

6.1 SAC 报告

业务: 每笔 OTC 交易必须在监管时限内报告给中国证券业协会 (SAC)。

系统: SacReportController 生成每日 SAC 报告。格式:SAC 标准 XML,包含交易明细、对手方信息、名义本金、估值。报告通过 API 提交到 SAC 的报告系统。

6.2 会计凭证

业务: 每笔资金变动产生会计凭证。财务团队需要看 P&L、应计利息、保证金划拨。

6.3 客户估值报告

业务: 客户每月收到估值报告,显示投资组合的当前价值。


第七阶段:交易生命周期结束

7.1 到期

业务: 到期日发生最终结算:

  • 融资 leg 的最后一笔利息计算
  • 价格回报 leg 的最终表现计算
  • 产生净支付

系统: ContractSettleHelper.finalSettle() 计算最终支付 → 结算指令生成并发送到支付系统 → 合约状态 ACTIVEEXPIRED

7.2 提前终止

业务: 客户或交易台可能提前终止。需要计算终止支付额(交易的市场价值),双方同意终止金额,交易关闭。

7.3 更替 (Novation)

业务: 交易转移到另一个对手方。在重组中罕见但会发生。在系统中实现为修改 counterpartyId 字段,新旧对手方都需确认。


每日时间表

时间系统活动如果出问题
08:00eds-price-server市场数据导入8:30 仍未完成 → 交易员用昨天数据开盘
09:30new-edsweb交易台开始簿记交易前端加载慢 → 客户在线上等报价
15:00eds-web-app同日簿记截止交易员电话催”等我 5 分钟”——每天都发生
16:00hedging-as定盘处理卡住 → 17:00 估值启动推迟
17:00hedging-asEOD 估值(按市值计价)估值太慢 → 保证金计算跟不上
18:00hedging-as保证金计算算错 → 发错催缴 → 对方投诉
18:30ofareg监管报告 (SAC)报错 → 运营加班修数据重跑
19:00eds-web-app确认书生成模板不匹配 → 运营手工改
19:30eds-web-app客户估值报告延迟 → 客户第二天邮件追问

代码来找

场景目录
修定价公式hedging-as/src/.../pricing/
加新产品类型hedging-as/src/.../edslib/payoff/
修交易簿记 bugeds-web-app/src/.../ContractController.java
改确认书模板eds-web-app/template/
调 EOD 定价hedging-as/src/.../action/valuation/
加对冲监控页odts-option-web/src/modules/hedge-monitor/
修保证金计算hedging-as/src/.../action/margin/
更新 SAC 报告eds-web-app/src/.../action/sacReport/

内行才知道的:簿记状态机里藏着”幽灵状态”

成熟的 PM/BA 总有一天会被问到:“这笔交易现在到底在什么状态?” 系统里有一个中心化的状态机,但它定义的状态比真正能流转到的状态多得多——有些状态是 2015 年设计时的”理想流程图”,从来没有被触发过。

状态机在哪

hedging-as/src/com/cicc/service/contract/statemachine/
├── ContractStateMachine.java   — 状态枚举 + 转移规则
└── StateTransition.java        — 每个转移的前置条件

ContractStateMachine 里枚举了一长串状态:BOOKEDCONFIRMEDEXECUTEDSETTLEDEXPIREDKNOCKED_OUTTERMINATEDDISPUTEDAMENDED…… 但实际上,绝大多数交易只经过 BOOKED → CONFIRMED → (EXECUTED/EXPIRED) → SETTLED 这条主干。像 DISPUTEDAMENDED 这类”看起来很合理”的状态,在生产里几乎不被写入——它们停留在代码里,是因为早期设计者认为”肯定会有争议和修订流程”。

业务陷阱: 如果你在做报表或风控看板,按 ContractStateMachine 的全量状态维度来分组统计,会得到一堆永远为 0 的桶。这不是数据缺失,是状态机过度设计。看真实分布要去 CtrContract 表的 status 字段实际值,而不是读枚举。

一个真实踩坑:敲出之后状态没及时翻

雪球敲出后,StateTransition 理论上应在 EOD 的 BarrierCheck 步骤把状态翻成 KNOCKED_OUT。但 2021 年出过一次 bug:敲出事件发生在北京时间 14:50(早盘收盘前),而 BarrierCheck 用的是 T 日收盘定盘价 (fixing price) 来判断,定盘价要到 15:00 才生成。结果这笔雪球在 14:50 已经实质敲出、客户已被告知,但系统状态直到当晚 EOD 跑完才翻——中间那 4 个小时里,它在”已敲出但状态仍是活跃”的灰色地带,风控日报把它的名义本金照常计入了未敲出敞口。

业务影响: 当天的风险敞口日报高估了约 ¥2 亿名义本金的敲入风险。后来加了一条盘中 (intraday) 敲出快检:在 MarketDataRefresh 之后、Valuation 之前插一步 IntradayBarrierCheck,用实时价而非定盘价做一次轻量敲出预判,只用于风控预警,不改变正式状态——正式状态仍以收盘定盘价为准。这条”双轨”逻辑现在还活着,是新人在 eod/ 包里最容易看懵的一段。

下一篇:06-Code-Map —— 每个项目的详细职责指南