Protocol Registry
ODTS-49: tradedesign — Protobuf 协议中心(约 6.5 年,15,014 commits 的消息总线枢纽)
目标读者:需要理解 CICC OTC 衍生品系统 37 个以上子系统如何通过 Protobuf 协议通信的 BA/PM
数据来源:git 历史(15,014 commits, 230 authors, 1,709 proto 文件, 2015-09-17 → 2022-04-06 停滞)
核心开发者(按提交数):chenkun (682), Yuping Ai (632), Shaonan Cheng (462), Ou Zhao (348), wangyh3 (249)
业务问题
35+ 个子系统如何知道彼此的存在?当一个系统要查找一笔交易的信息时,它怎么知道该调用什么接口、传入什么参数、得到什么格式的响应?
答案是一个集中式的协议注册中心(Protocol Registry)。这不是一个运行时服务(没有进程、没有部署),而是一个被所有项目引用的 JAR 包,包含了整个 CICC OTC 衍生品生态系统中每个服务之间通信所使用的全部 Protobuf 消息定义。
这就是 tradedesign 项目。它的 15,014 次提交和 1,709 个 .proto 文件定义了 37 个子系统之间的全部通信契约。
Tradedesign 概览
tradedesign/
│
├── PbMessageEnum.proto ← 全局消息类型枚举(381 行)
├── PbMessageHead.* ← 37 个子系统的消息头文件
│
├── bookingAs/ ← 簿记系统协议(50+ 子域)
├── hedgingAs/ ← 对冲/定价系统协议
├── trade/ ← 交易系统协议
├── gwmsAdm/ ← GWMS 管理协议
├── gwmsIc/ ← GWMS IC 协议
├── gwmsWm/ ← GWMS 财富管理协议
├── gwmsUser/ ← GWMS 用户协议
├── interBank/ ← 银行间协议
├── mm/ ← 做市商(Market Maker)协议
├── sacAS/ ← SAC 监管报送协议
├── ta/ ← 交易助理(Trade Assistant)协议
├── web/ ← Web 前端协议
├── pricingAS/ ← 定价服务协议
├── gateway/ ← 网关协议
├── reck/ ← 结算核对(Reckoning)协议
├── ac/ ← 账户协议
├── seCustom/ ← 策略引擎自定义协议
├── util/ ← 工具层协议
├── dataCenter/ ← 数据中心协议
└── resources/ ← 资源文件
两种视角看 tradedesign
视角一:编译期依赖。所有 Java 项目(eds-web-app、hedging-as、sac-report、eds-price-server 等)在 pom.xml 或 build.gradle 中依赖 tradedesign 的 JAR 包。Proto 文件被编译为 Java 类,所有服务的 request/response 对象都来自同一个来源。
视角二:运行时不存在。Tradedesign 不是一个服务。没有 tradedesign 进程在运行。它只是一个定义。实际的通信走 Netty TCP、HTTP REST 或文件,但消息的**形状(shape)**由 tradedesign 定义。
沟通模型:Message 信封
整个体系的通信统一使用一个 Message Proto 信封:
message Message {
required MSG msgType = 1; // 消息类型(是查询还是响应?)
required int32 msgMode = 2; // 消息模式(0=查询, 1=响应)
required string sequence = 3; // 消息序列号(请求-响应配对)
required string sessionId = 4; // 会话 ID
optional Notification notification = 7;
optional string gatewayId = 8;
optional string userId = 9;
// ...
}
每个子系统有自己的 PbMessageHead.* 文件,通过 import 机制引入各自需要的 Proto 文件,然后在 Message 信封的 notification 字段中承载具体的业务消息。
MSG 枚举分配了每个子系统的消息号空间:
| 子系统 | Msg 起始 | 说明 |
|---|---|---|
| Notification | 1000 | 通用通知 |
| Util | 10000 | 工具类消息 |
| Trade | 20000 | 交易系统 |
| MarketMaker | 21000 | 做市商 |
| Gateway | 30000 | 网关 |
| Reck | 40000 | 结算核对 |
| Web | 50000 | Web 前端 |
| GwmsAdm | 60000 | GWMS 管理 |
| GwmsIc | 60001 | GWMS IC |
| GwmsWm | 60002 | GWMS 财富管理 |
| GwmsUser | 60003 | GWMS 用户 |
| Ta | 60005 | 交易助理 |
每个子系统在自己的消息号空间内定义具体的消息类型(如 Trade_Msg_OpenPosition、Gateway_Msg_Login),避免了跨系统的消息 ID 冲突。
37 个子系统协议
每个 PbMessageHead.* 文件对应一个子系统,定义了该子系统使用的全部消息类型:
| Head 文件 | 子系统 | 协议内容 |
|---|---|---|
1webApp | 新一代 Web 应用 | new-edsweb 使用的 API |
acApp | 账户应用 | 账户管理的 App 端 |
acWeb | 账户 Web | 账户管理 Web |
acct | 会计 | 会计分录、科目 |
algo | 算法交易 | 算法交易指令 |
bookingAs | 簿记系统 | 核心 OTC 簿记(详见下节) |
ciccQuantAS | 量化平台 | CICC 量化分析服务 |
comm-agw | 商品网关 | 大宗商品系统桥接 |
core | 核心 | 跨系统共用功能 |
edsApp | EDS 应用 | 主后端 eds-web-app |
gui | 桌面 GUI | 交易员桌面客户端 |
gui.odts | OTC GUI | OTC 专用桌面客户端 |
gw | 通用网关 | 通用网关协议 |
gwms | 财富管理 | GWMS 主协议 |
gwmsAdm | GWMS 管理 | 产品、费用、审核 |
gwmsIc | GWMS IC | 内部管控 |
gwmsUser | GWMS 用户 | 用户管理 |
gwmsWm | GWMS 财富 | 财富管理业务 |
hedgingAS | 对冲系统 | 定价、对冲、风险(hedging-as) |
hk | 香港 | 香港业务专属 |
interBank | 银行间 | 银行间市场 |
interbankGW | 银行间网关 | 银行间桥接 |
login | 登录 | 统一认证 |
marketMaker | 做市商 | 做市报价、成交 |
mmgui | 做市 GUI | 做市桌面客户端 |
priceServer | 定价服务 | 独立定价服务(eds-price-server) |
pricingAS | 定价服务(新) | 新一代定价应用 |
reck | Reckoning | 结算核对(EOD批量中的对账) |
riskEngine | 风险引擎 | 风险计算 |
sacAsWeb | SAC 报送 | 监管报送 Web |
se | 策略引擎 | 策略执行引擎 |
strategyEngine | 策略引擎 | 策略定义和执行 |
ta | 交易助理 | 交易辅助功能 |
trade | 交易 | 核心交易协议 |
util | 工具 | 通用基础协议 |
web | Web 前端 | 通用 Web 协议 |
BookingAs:OTC 核心域模型(50+ 子域)
bookingAs/ 是 tradedesign 中最大的协议集合,定义了 OTC 衍生品簿记系统的全部业务对象。它与 hedging-as 系统的 com.cicc.hedging.contract.* Java 类一一对应。
bookingAs/
├── accountsubject/ ← 会计科目
├── ams/ ← 资产管理
├── bank/ ← 银行信息
├── cashmovement/ ← 资金变动
├── clientVersion/ ← 客户端版本
├── confirmation/ ← 交易确认书
├── contract/ ← 合约(核心!36 个 proto 文件)
├── contractMargin/ ← 合约保证金
├── contractSettle/ ← 合约结算
├── contractevent/ ← 合约事件(分红、行权等)
├── corpration/ ← 公司信息
├── counterPartyId/ ← 对手方 ID 管理
├── counterparty/ ← 对手方
├── currency/ ← 币种
├── deskEntity/ ← 交易台实体
├── dividendRate/ ← 股息率
├── dynamic/ ← 动态属性
├── element/ ← EAV 元素定义
├── employee/ ← 员工
├── eod/ ← EOD 数据
├── event/ ← 权益事件
├── futureInfo/ ← 期货信息
├── global/ ← 全局配置
├── historyTrade/ ← 历史交易
├── instrument/ ← 标的物
├── interestRate/ ← 利率
├── model/ ← 定价模型
├── netAsset/ ← 净资产
├── netPosition/ ← 净持仓
├── netValue/ ← 净值
├── nqaIndexContract/ ← NQA 指数合约
├── paramShift/ ← 参数偏移
├── parameter/ ← 参数
├── pricingModel/ ← 定价模型参数
├── product/ ← 产品
├── productLine/ ← 产品线
├── reckoning/ ← 对账
├── refresh/ ← 刷新
├── report/ ← 报表
├── risk/ ← 风险
├── status/ ← 状态
├── tagMgt/ ← 标签管理
├── template/ ← 模板
├── todayTrade/ ← 当日交易
├── tradeaccount/ ← 交易账户
├── underlying/ ← 标的物管理
├── underlyingApply/ ← 标的物申请
├── underlyingControl/ ← 标的物控制
├── user/ ← 用户
├── volatility/ ← 波动率
这 50+ 个子域映射了 CICC OTC 系统的完整领域模型。每个子域包含若干 Bean*.proto(数据对象)和 Msg*.proto(请求/响应消息)。例如 contract/ 下有 BeanContract.proto(合约数据)、MsgContract.proto(合约请求/响应)、MsgContractAudit.proto(合约审核)等。
按模块分布的 Proto 文件量
trade/order/ 136 ← 交易指令(最大模块)
hedgingAs/contract/ 77 ← 对冲合约
trade/query/ 73 ← 交易查询
gateway/msg/ 57 ← 网关消息
util/bean/ 44 ← 工具数据对象
gwmsAdm/ofs/ 44 ← GWMS 开放式基金
util/msg/ 42 ← 工具消息
web/product/ 40 ← Web 产品
web/risk/ 37 ← Web 风控
gwmsWm/msg/ 37 ← GWMS 财富消息
hedgingAs/quotation/ 36 ← 对冲报价
bookingAs/contract/ 36 ← 簿记合约
hedgingAs/position/ 28 ← 对冲持仓
bookingAs/volatility/ 27 ← 簿记波动率
web/nav/ 26 ← Web 净值
interBank/query/ 26 ← 银行间查询
bookingAs/risk/ 26 ← 簿记风控
Total: 1,709 个 Proto 文件。
跨系统消息流示例:一笔交易的生命周期
以一笔雪球期权交易为例,展示 tradedesign 如何协调 7 个子系统的通信:
交易员敲入交易
│
▼
[1] web/product ← 交易员在前端选择产品类型
│ (MsgGetProductAssesment, MsgGetBusinessFee)
▼
[2] bookingAs/contract ← 簿记系统创建合约
│ (MsgContract → BeanContract)
▼
[3] hedgingAs/contract ← 对冲系统接收合约,计算 Greeks
│ (BeanContract → MsgCalHedgeTrade)
▼
[4] hedgingAs/quotation ← 生成报价
│ (MsgQuotationNew, BeanQuotation)
▼
[5] trade/order ← 确认成交
│ (MsgOpenPosition)
▼
[6] bookingAs/confirmation ← 生成确认书
│ (MsgConfirmation)
▼
[7] gateway/msg ← 通过网关发送给 IMS
(Gateway_Msg_Contract → IMS)
每一步的消息结构都由 tradedesign 中的 Proto 文件定义。这意味着没有”接口文档过期”的问题——消息定义本身就是文档,代码自动从 Proto 编译生成,永远不会与运行时不一致。
Gateway 层:跨系统的消息路由器
gateway/ 目录定义了 57 个网关协议,是整个系统的”神经系统”:
gateway/msg/ ← 网关消息(57 个 proto 文件)
├── MsgLogin.proto ← 登录请求
├── MsgHeartbeat.proto ← 心跳
├── MsgSubscribe.proto ← 订阅行情
├── MsgUnsubscribe.proto ← 取消订阅
├── Gateway_Msg_Contract.proto ← 合约转发(→ IMS)
├── Gateway_Msg_Trade.proto ← 交易指令转发
└── ...
Gateway 的作用:
- 协议转换:将 Protobuf 消息转换为 IMS(Investment Management System)能理解的格式
- 路由分发:根据消息类型,将请求转发到正确的后端系统
- 会话管理:维护客户端到后端的长连接
- 流控:控制消息发送速率(做市商场景下尤其重要)
Gateway 消息类型(从 PbMessageEnum 推断):
Gateway_Msg_Login— 认证登录Gateway_Msg_Subscribe— 订阅行情Gateway_Msg_Contract— 合约信息同步Gateway_Msg_Trade— 交易指令Gateway_Msg_HeartBeat— 心跳维持Gateway_Msg_Knock— 成交回报Gateway_Msg_Quotation— 行情推送
为什么有 15,014 次提交?
从 git 历史看,tradedesign 与实现系统几乎是同期启动的:它的第一个提交在 2015-09-17,比主后端 eds-web-app(2016-12-28)早约 15 个月,比定价引擎 eds-price-server(2016-12-21)早约 1 年。提交峰值在 2017-2019 年(每年超过 1,500 次),然后逐步下降。它并不是”比实现早 6 年”的超前规划,而是在系统从 Excel/原型走向多子系统架构的同一时期,同步生长出来的协议中枢。
原因一:协议定义先行。tradedesign 是整个系统的”先有接口,后有实现”策略的产物。 每次新功能开发的第一步,就是在这里定义 Proto 消息。然后各自实现方(前端、后端、网关)根据同样的 Proto 定义开发。这保证了各方对接口的理解一致。tradedesign 在 2015 年起步,eds-web-app / eds-price-server 在 2016 年底跟进——协议定义比首批实现早约一年,为后续 37 个子系统的接入铺好了地基。
原因二:跨团队协作。 每个团队修改自己负责子系统的 Proto 文件时都会产生一次提交。37 个子系统、70+ 位开发者的修改汇聚于此。当 chenkun(682 次提交,最多产的贡献者)修改一个合约字段时,可能触发了 bookingAs、hedgingAs、gwms 三个团队的上下游联动,产生 5-10 次往返提交。
原因三:生成的 Java 类也在这个 repo 里。 Proto 文件编译后的 Java 类(com/framework/protobuf/)直接提交到了版本控制中。这意味着每次修改 proto → 重新编译 → 提交生成代码,双倍提交。
为什么它停更了?
Tradedesign 在 2022 年初之后不再有新提交。原因是:
直接原因:Protobuf 不再主导新开发。 2022 年之后的新系统(odyssey report-processing-service、new-edsweb)不再使用 tradedesign 的 Protobuf 协议。它们使用 REST API(JSON)或 Kafka 消息队列,消息格式在各自项目中定义,不再需要集中的协议注册中心。
业务原因:开发范式转变。 2015-2022 年的系统(hedging-as、eds-web-app)依赖 Netty TCP + Protobuf 进行跨进程通信,因此需要集中的协议定义。2022 年之后,HTTP REST + JSON 成为新服务的标准通信方式,协议定义从”集中的 Proto 文件”变成了 “各自 Swagger/OpenAPI 文档”。
遗留效果: tradedesign 作为历史存档仍然宝贵——它记录了 2015-2022 年间 CICC OTC 系统的全部接口契约。如果未来需要理解某个遗留消息的格式,tradedesign 是唯一的权威来源。
业务启示
Tradedesign 的故事告诉读者三件事:
第一,集中式协议注册中心是大型金融系统的常见模式。 当系统数量超过 5 个时,分散的接口文档会让集成变成噩梦。CICC 选择了 Protobuf + 集中式协议仓库作为解决方案,这与 Google 内部的做法一致(Google 的所有 RPC 接口定义都在一个中央仓库中)。
第二,协议定义先行(contract-first)的开发模式在 2015 年是领先的。 今天这种模式叫 “API-first” 或 “Schema-first”(OpenAPI/AsyncAPI),但在 2015 年前后使用 Protobuf 集中定义所有接口,仍然是一个早于行业普遍采用(大多在 2018 年之后)的选择。
第三,架构迁移的代价在协议层最明显。 2022 年转向 REST + JSON 意味着 tradedesign 被废弃——但这不是一个简单的决定。因为现有的 Protobuf 消息格式已经嵌入了约 7 年的业务逻辑。迁移到新协议意味着重写所有消息格式,而不仅仅是换一个序列化框架。
Tradedesign 是一个”看不见”的系统——没有进程、没有部署、没有告警——但它在约 6.5 年间(2015-2022)定义了 37 个子系统之间的全部通信契约。它是 CICC OTC 生态系统的”宪法”,规定了每个系统如何与邻居对话。15,014 次提交的积累最终在 2022 年被 REST API 取代,但它留下的 1,709 个 Proto 文件仍然是理解这个系统架构的最好入口。