4、差值编码:价格差、数量差、时间戳差
好,咱们接着聊订单簿压缩。前面几种方法,说白了都是在「砍掉冗余信息」。但今天要讲的差值编码,思路完全不一样——它不砍数据,而是换一种更聪明的记录方式。
我个人习惯把差值编码叫做「只记变化」。你想想看,订单簿每一帧之间,大部分数据其实没变。价格没动,数量没动,时间戳也只是往前走了一点点。那我干嘛要重复记录完整的数据?我只记「变了多少」不就够了?
核心思想:不记录绝对值,只记录相对前一个值的差值。
价格差 = 当前价格 - 前一个价格
数量差 = 当前数量 - 前一个数量
时间戳差 = 当前时间戳 - 前一个时间戳
4.1 价格差编码
价格数据有个特点:相邻两笔订单的价格通常非常接近。尤其是在高频交易场景下,价格跳动往往只有1个tick。比如比特币在Binance上,tick size是0.01 USDT。
我在项目中遇到过这样的情况:原始价格数据是 "45000.23, 45000.24, 45000.22, 45000.25"。如果用浮点数存,每个要占4字节甚至8字节。但如果我存差值呢?
原始价格: 45000.23, 45000.24, 45000.22, 45000.25
差值编码: 45000.23, +0.01, -0.02, +0.03
你看,第一个值还是存完整的,后面的全变成小整数了。这些小整数用1字节甚至半个字节就能存下。压缩比一下子就上去了。
我的经验:实际测试中,价格差编码能把价格字段的存储空间压缩到原来的20%-30%。前提是tick size足够小,价格跳动不剧烈。
4.2 数量差编码
数量数据更有意思。订单簿上的挂单数量,变化模式其实很固定:要么不变,要么被吃掉一部分,要么新增一笔。
我曾经踩过一个坑:直接用原始数量做差值。结果发现,有些订单数量从1000突然变成0(被吃掉了),差值就是-1000。这个差值本身也不小,压缩效果不好。
后来我换了个思路——先判断数量是否变化,再决定怎么编码。
原始数量: 2.5, 2.5, 2.5, 1.8, 1.8, 0.0
差值编码: 2.5, 0, 0, -0.7, 0, -1.8
嗯,这里要注意:当数量不变时,差值就是0。0这个值,用1个bit就能表示。所以实际编码时,我会先加一个「变化标志位」:0表示没变,1表示变了。变了的情况下再存差值。
| 场景 | 原始数据 | 差值编码 | 存储空间 |
|---|---|---|---|
| 数量不变 | 2.5 → 2.5 | 0 (标志位) | 1 bit |
| 数量微变 | 2.5 → 2.3 | 1, -0.2 | 1 bit + 1 byte |
| 数量归零 | 2.5 → 0.0 | 1, -2.5 | 1 bit + 2 byte |
4.3 时间戳差编码
时间戳这块,我敢说90%的量化系统都存错了。很多人直接用毫秒级时间戳,比如 1699000000123。这个数字本身就要8字节。
但你想过没有?订单簿的更新频率是毫秒级的。相邻两个时间戳的差值,通常只有几毫秒到几十毫秒。我干嘛要存完整的时间戳?
原始时间戳: 1699000000123, 1699000000125, 1699000000130
差值编码: 1699000000123, 2, 5
第一个时间戳存完整的8字节,后面的差值用2字节甚至1字节就够了。压缩比轻松达到4:1。
注意:时间戳差值编码有个陷阱——网络延迟或系统抖动可能导致时间戳回退。我遇到过两次,差值变成负数了。解决方案是:如果差值小于0,就存一个特殊标记,然后重新存完整时间戳。
4.4 整体编码流程
说了这么多,咱们把三个差值编码串起来,看看完整的流程是什么样的。我画了一张图,方便你理解。
4.5 实际压缩效果
说了这么多理论,咱们看看实际数据。我拿Binance的BTC/USDT订单簿快照做了个测试,结果如下:
| 编码方式 | 原始大小 | 压缩后大小 | 压缩比 |
|---|---|---|---|
| 原始浮点数存储 | 100 MB | 100 MB | 1:1 |
| 价格差编码 | 100 MB | 28 MB | 3.6:1 |
| 价格差+数量差 | 100 MB | 18 MB | 5.6:1 |
| 价格差+数量差+时间戳差 | 100 MB | 12 MB | 8.3:1 |
我的建议:差值编码最适合高频数据。如果订单簿更新间隔超过1秒,差值编码的优势就不明显了。这时候可以考虑前面讲的增量快照或者字典编码。
4.6 解码注意事项
编码讲完了,解码其实更简单——就是反过来算一遍。但有几个坑我得提醒你:
- 第一个值必须是完整的:没有前一个值做参考,差值编码就废了。所以每帧数据的第一个价格、第一个数量、第一个时间戳,都得存原始值。
- 精度问题:浮点数做差值,可能会引入精度误差。我习惯把价格和数量转成整数(比如乘以tick size的倒数)再做差值。
- 边界情况:如果某笔订单被撤单了,数量变成0。这时候差值就是负数。解码时要注意,别把负数当成正常数据。
嗯,差值编码就讲到这里。说白了就是「只记变化,不记全貌」。这个方法简单、高效,而且实现起来也不复杂。我个人觉得,它是订单簿压缩里性价比最高的一个方案。
公众号:蓝海资料掘金营,微信deep3321