2. 核心数据模型设计:订单、成交、行情、账户、风控事件的数据结构设计
做交易系统这么多年,我最大的体会就是:数据模型是系统的骨架。骨架歪了,后面再怎么填肉都别扭。今天咱们就来聊聊这五个核心模型——订单、成交、行情、账户、风控事件。每个模型我都踩过坑,有些坑现在想起来还肉疼。
2.1 订单模型:最基础也最容易翻车
订单模型是所有交易系统的起点。你想想看,一笔订单从创建到最终完成,要经过多少状态转换?我见过不少新手直接用一个字段存状态,结果后面查历史记录时一脸懵。
我个人习惯把订单模型拆成两部分:订单头和订单明细。订单头存公共信息,明细存每个标的的具体数据。这样设计,一个订单可以同时买多只股票,也能方便地做撤单、改单操作。
// 订单头结构(简化版)
{
"order_id": "202503210001", // 订单编号,全局唯一
"account_id": "ACC001", // 账户编号
"client_id": "CLT001", // 客户编号
"order_type": "LIMIT", // 订单类型:LIMIT/MARKET/STOP
"direction": "BUY", // 买卖方向
"status": "NEW", // 当前状态
"create_time": 1742524800000, // 创建时间戳
"update_time": 1742524805000, // 最后更新时间
"total_quantity": 10000, // 总数量
"total_amount": 500000.00, // 总金额
"status_history": [ // 状态变更历史
{"status": "NEW", "time": 1742524800000},
{"status": "PARTIAL_FILLED", "time": 1742524803000}
]
}
// 订单明细结构
{
"order_id": "202503210001",
"symbol": "600519.SH", // 股票代码
"price": 50.00, // 委托价格
"quantity": 10000, // 委托数量
"filled_quantity": 5000, // 已成交数量
"filled_amount": 250000.00, // 已成交金额
"commission": 250.00, // 手续费
"status": "PARTIAL_FILLED"
}
2.2 成交模型:别小看这个"小兄弟"
成交模型看着简单,其实门道不少。一笔订单可能拆成多笔成交,每笔成交又可能来自不同的交易所。我见过最离谱的情况是:成交记录和订单记录对不上账,最后查了三天才发现是时间戳精度不一致导致的。
// 成交记录结构
{
"trade_id": "TRADE202503210001", // 成交编号
"order_id": "202503210001", // 关联订单编号
"symbol": "600519.SH",
"direction": "BUY",
"price": 50.00, // 成交价格
"quantity": 5000, // 成交数量
"amount": 250000.00, // 成交金额
"trade_time": 1742524803000, // 成交时间(交易所时间)
"system_time": 1742524803005, // 系统接收时间
"exchange_id": "SSE", // 交易所代码
"commission": 250.00, // 手续费
"tax": 0.00, // 印花税
"settlement_date": "20250322" // 交收日期
}
这里有个关键点:trade_time 和 system_time 必须分开存。为什么?因为交易所返回的时间和系统收到的时间可能有毫秒级差异。做回测时,这个差异会直接影响策略评估的准确性。我记得有一次做高频策略回测,就因为没区分这两个时间,回测结果和实盘差了 30%。
2.3 行情模型:数据量大到让你怀疑人生
行情数据是交易系统里数据量最大的部分。以 A 股为例,一天 4 小时交易时间,每只股票每秒至少推送 2-3 次快照。全市场 5000 多只股票,一天下来就是几亿条数据。如果不设计好模型,存储和查询都会爆炸。
// 行情快照结构(Level-1)
{
"symbol": "600519.SH",
"timestamp": 1742524800000, // 行情时间
"open": 49.50, // 开盘价
"high": 51.00, // 最高价
"low": 49.00, // 最低价
"close": 50.50, // 收盘价(当前最新价)
"volume": 5000000, // 成交量
"amount": 250000000.00, // 成交额
"bid_price": [50.00, 49.99, ...], // 买一到买五价格
"bid_volume": [1000, 2000, ...], // 买一到买五数量
"ask_price": [50.01, 50.02, ...], // 卖一到卖五价格
"ask_volume": [1500, 1800, ...] // 卖一到卖五数量
}
// 逐笔成交结构(Level-2)
{
"symbol": "600519.SH",
"trade_id": "T2025032100001",
"timestamp": 1742524800000,
"price": 50.00,
"volume": 1000,
"amount": 50000.00,
"trade_type": "BUYER_INITIATED", // 买方主动/卖方主动
"buy_order_id": "B202503210001",
"sell_order_id": "S202503210001"
}
2.4 账户模型:钱的事不能马虎
账户模型直接关系到资金安全,设计时必须严谨。我见过最坑的设计是把账户余额和持仓放在同一张表里,结果并发更新时经常出现数据不一致。
// 账户资金结构
{
"account_id": "ACC001",
"client_id": "CLT001",
"currency": "CNY",
"total_assets": 1000000.00, // 总资产
"available_balance": 500000.00, // 可用余额
"frozen_balance": 200000.00, // 冻结余额
"position_value": 300000.00, // 持仓市值
"margin_used": 0.00, // 已用保证金
"profit_loss": 5000.00, // 浮动盈亏
"update_time": 1742524800000
}
// 持仓结构
{
"account_id": "ACC001",
"symbol": "600519.SH",
"direction": "LONG", // 多/空
"quantity": 10000, // 持仓数量
"available_quantity": 8000, // 可用数量
"frozen_quantity": 2000, // 冻结数量
"cost_price": 48.50, // 成本价
"current_price": 50.00, // 当前价
"market_value": 500000.00, // 市值
"profit_loss": 15000.00, // 浮动盈亏
"profit_loss_ratio": 0.0309 // 盈亏比例
}
账户模型设计时,有几点要特别注意:
- 资金和持仓必须分开表,避免并发更新时的锁竞争
- 每次资金变动都要记录流水,方便对账和审计
- 冻结余额和冻结数量要单独字段,不能靠计算得出
2.5 风控事件模型:救命用的数据结构
风控事件模型可能是最容易被忽视的。很多人觉得风控就是几个阈值判断,随便存一下就行。但真正出事的时候,你会发现风控数据是唯一能帮你还原现场的证据。
// 风控事件结构
{
"event_id": "RISK202503210001",
"event_type": "POSITION_LIMIT", // 事件类型
"severity": "WARNING", // 严重级别:INFO/WARNING/CRITICAL
"account_id": "ACC001",
"symbol": "600519.SH",
"trigger_time": 1742524800000, // 触发时间
"trigger_value": 1000000.00, // 触发时的值
"threshold_value": 800000.00, // 阈值
"rule_id": "RULE001", // 触发的规则编号
"rule_name": "单品种持仓上限", // 规则名称
"action_taken": "BLOCK_ORDER", // 采取的动作
"status": "PROCESSED", // 处理状态
"handler": "SYSTEM", // 处理人/系统
"handle_time": 1742524801000, // 处理时间
"details": { // 扩展信息
"current_position": 2000000,
"max_position": 800000,
"exceed_amount": 200000
}
}
风控事件模型设计时,我建议重点关注以下几点:
- 事件类型要可扩展,用枚举或字典表,不要硬编码
- 严重级别要明确,INFO 只是记录,WARNING 需要关注,CRITICAL 必须立即处理
- 触发值和阈值都要存,方便事后分析为什么触发了风控
- 采取的动作要记录,比如是阻止订单、强制平仓还是只告警
2.6 数据模型之间的关系
这五个模型不是孤立的,它们之间有紧密的关联。我画了一张图,帮你理清它们之间的关系:
从这张图可以看出:
- 订单和成交是一对多关系,一笔订单可能产生多笔成交
- 成交参考行情数据,成交价格必须在行情范围内
- 订单和成交都归属于账户,账户模型是资金维度的核心
- 风控事件监控账户和订单,一旦触发就阻断后续操作
- 行情数据是风控的输入,比如价格异常波动会触发风控
好了,核心数据模型就聊到这儿。记住一句话:好的数据模型能让你少写一半的 bug。下一章咱们聊聊数据模型在数据库中的落地实现,包括表结构设计、索引优化和分库分表策略。
公众号:蓝海资料掘金营,微信deep3321