第15章:系统架构设计:数据存储与回放机制
做波动率曲面异常检测,最怕什么?
怕数据丢了,怕回测结果复现不了,怕生产环境出了bug却找不到根因。
我早年吃过这个亏。有一次策略在实盘跑得好好的,突然某天下午开始疯狂报错。我查了半天日志,发现是某个期权链的波动率曲面出现了「尖刺」——但等我回头想复盘时,发现原始tick数据已经被覆盖了。嗯,从那以后,我再也不敢轻视数据存储与回放机制的设计。
15.1 数据存储的核心诉求
波动率曲面数据,说白了就是不同行权价、不同到期日的隐含波动率构成的二维矩阵。但实际落地时,它比想象中复杂得多。
我个人习惯把数据存储的需求拆成三层:
- 原始层:交易所来的tick级期权行情,包括买卖价、成交量、未平仓量
- 计算层:经过插值、平滑后的波动率曲面快照
- 异常层:检测出的异常点、异常区间、以及对应的风险标签
你想想看,如果这三层数据混在一起存,查询效率会非常低。而且一旦需要回放某个历史时刻的曲面状态,你得能精确还原当时的计算上下文。
15.2 存储方案选型
我在项目中遇到过几种方案,这里直接给结论:
| 存储类型 | 适用场景 | 我的建议 |
|---|---|---|
| 关系型数据库(PostgreSQL) | 元数据、配置、异常标签 | 必选,用于管理资产信息和检测规则 |
| 时序数据库(InfluxDB / ClickHouse) | 高频曲面快照、tick数据 | 强烈推荐,写入和查询性能远超传统方案 |
| 列式存储(Parquet + S3) | 历史回放、批量分析 | 适合做离线回测和模型训练 |
| 内存数据库(Redis) | 实时检测、热数据缓存 | 用于生产环境的低延迟异常检测 |
为什么我推荐ClickHouse?因为它对时间序列数据的压缩比极高,而且支持SQL语法。我曾经用PostgreSQL存一天的期权tick数据,磁盘占用2.3GB;换成ClickHouse后,同样的数据只用了280MB。你想想看,这差距有多大。
15.3 数据回放机制设计
回放机制是整个系统的「时光机」。没有它,你根本没法验证异常检测算法在历史数据上的表现。
我设计的回放机制包含三个核心组件:
- 快照管理器:每隔固定时间(比如1分钟)保存一次完整的曲面状态
- 增量日志:记录两次快照之间的所有变化事件
- 回放引擎:根据时间戳,从最近的快照开始,逐条回放增量事件
回放引擎的核心逻辑其实不复杂,但有个坑要注意——事件顺序。同一个时间戳内,可能有多个事件同时发生(比如多个期权合约同时更新)。这时候必须按合约ID排序,否则回放出来的曲面会乱掉。
class ReplayEngine:
def __init__(self, snapshot_store, event_store):
self.snapshot_store = snapshot_store
self.event_store = event_store
def replay(self, target_time):
# 找到最近的快照
snapshot = self.snapshot_store.get_nearest(target_time)
current_state = snapshot.data
# 获取快照之后的所有事件
events = self.event_store.get_events(
start=snapshot.timestamp,
end=target_time
)
# 按时间戳和合约ID排序
events.sort(key=lambda e: (e.timestamp, e.contract_id))
# 逐条回放
for event in events:
current_state.apply(event)
return current_state
15.4 异常数据的特殊处理
检测出来的异常点,不能简单丢掉。我见过太多团队把异常数据直接删掉,结果回测时发现策略表现「异常好」——因为所有坏样本都被清除了。
正确的做法是:
- 异常数据单独存储,打上标签(比如「数据错误」「市场冲击」「流动性不足」)
- 回放时提供「净化模式」和「原始模式」两种选项
- 异常标签本身也要作为特征,输入到后续的风险模型中
15.5 系统架构总览
说了这么多,咱们用一张图把整个架构串起来。
15.6 生产环境的一些细节
最后分享几个我在生产环境中踩过的坑:
- 写入吞吐量:期权tick数据量很大,尤其是美股市场。我建议用批量写入,每100ms攒一批再写,别一条一条insert
- 数据压缩:波动率曲面数据有很强的时空相关性,用delta-of-delta压缩算法效果很好,压缩比能达到20:1
- 冷热分离:最近7天的数据放SSD,7天以上的放HDD或对象存储。查询时透明路由,用户无感知
- 校验和机制:每个数据块写入时计算CRC32,回放时校验。我遇到过磁盘静默错误导致曲面数据损坏的情况,有了校验和就能及时发现
好了,关于数据存储与回放机制,核心要点就这些。记住一句话:存储设计决定了你能回溯多远的历史,回放机制决定了你能多精确地还原历史。这两点做好了,异常检测和风险规避才有坚实的基础。