4、数据校验基础:非空校验、类型校验、范围校验、逻辑一致性校验
订单簿数据,说白了就是交易所的「心跳」。数据一旦出错,你的策略可能瞬间从盈利变成爆仓。我见过太多人把精力全花在策略优化上,结果被一个空字段坑得血本无归。
今天咱们就聊聊数据校验的四个基本功。嗯,这四招练好了,至少能挡住 90% 的脏数据。
4.1 非空校验:别让空值毁了你的策略
非空校验是最基础的一关。但你别小看它,我遇到过好几次行情剧烈波动时,交易所的某个字段突然返回 null,结果我的策略直接崩了。
哪些字段必须非空?
- 价格:没有价格,你拿什么成交?
- 数量:空数量意味着无效订单
- 时间戳:没有时间,数据就失去了时序意义
- 订单方向:买还是卖,必须明确
核心原则:对于订单簿的每一档(level),价格和数量必须同时存在,或者同时为空。不能出现「有价格没数量」或「有数量没价格」的情况。
// 伪代码示例:非空校验
function validateNotNull(level) {
if (level.price === null || level.price === undefined) {
return false; // 价格为空,直接拒绝
}
if (level.quantity === null || level.quantity === undefined) {
return false; // 数量为空,直接拒绝
}
return true;
}
我的习惯:我会在数据入口处就做非空校验,而不是等到策略层再处理。早发现,早止损。
4.2 类型校验:别把字符串当数字用
类型校验看起来简单,但坑特别多。你想想看,交易所返回的数据,有时候价格是字符串 "123.45",有时候是浮点数 123.45。你的代码能兼容吗?
常见类型问题:
- 价格字段是字符串,但你当数字去加减乘除
- 数量字段是整数,但你当浮点数处理,精度丢失
- 时间戳是字符串格式,但你直接当时间戳用
我曾经踩过的坑:有一次交易所把价格字段从数字改成了字符串,我的策略没做类型校验,结果所有价格比较都变成了字符串比较,比如 "9.99" > "100.00" 居然返回 true。那笔交易亏了不小。
// 类型校验示例
function validateType(level) {
// 价格必须是数字
if (typeof level.price !== 'number') {
level.price = parseFloat(level.price);
if (isNaN(level.price)) {
return false; // 转换失败,拒绝
}
}
// 数量必须是数字
if (typeof level.quantity !== 'number') {
level.quantity = parseFloat(level.quantity);
if (isNaN(level.quantity)) {
return false;
}
}
return true;
}
建议:在数据接入层统一做类型转换,后续所有逻辑都基于统一类型处理。别在策略层到处写 parseFloat,那样容易出 bug。
4.3 范围校验:数据不能离谱
范围校验,说白了就是判断数据是否在合理区间内。比如比特币价格突然变成 0.01 美元,或者数量变成负数,这明显是异常数据。
常见的范围校验规则:
| 字段 | 合理范围 | 说明 |
|---|---|---|
| 价格 | > 0 | 价格不能为负或零 |
| 数量 | > 0 | 数量不能为负或零 |
| 时间戳 | 当前时间 ± 5分钟 | 防止历史数据或未来数据混入 |
| 订单簿深度 | 1 ~ 1000 档 | 太浅或太深都可能异常 |
注意:范围校验的阈值要根据具体交易品种动态调整。比如比特币价格范围是 0 ~ 100 万,但某个山寨币可能只有 0.001 ~ 10 美元。写死阈值会出问题。
// 范围校验示例
function validateRange(level, symbol) {
const config = getSymbolConfig(symbol); // 获取该品种的配置
// 价格范围校验
if (level.price <= 0 || level.price > config.maxPrice) {
return false;
}
// 数量范围校验
if (level.quantity <= 0 || level.quantity > config.maxQuantity) {
return false;
}
// 时间戳范围校验(允许前后5分钟偏差)
const now = Date.now();
if (Math.abs(level.timestamp - now) > 5 * 60 * 1000) {
return false;
}
return true;
}
我的做法:我会在配置中心维护每个交易品种的阈值,而不是硬编码在代码里。这样调整起来方便,不用重新部署。
4.4 逻辑一致性校验:数据要「讲得通」
逻辑一致性校验,是四个校验中最难但最重要的。它检查的是数据内部是否自洽。比如:
- 买卖方向不能矛盾:同一个价格,不能同时出现在买单和卖单里(除非是撮合瞬间)
- 价格排序必须正确:买单从高到低排列,卖单从低到高排列
- 数量不能超过总量:单档数量不能超过该品种的总流通量
- 时间戳必须递增:后一条数据的时间戳不能早于前一条
我曾经遇到的情况:有一次交易所的订单簿数据,买单的最高价居然比卖单的最低价还高。这明显是逻辑错误,如果我不做校验,策略会以为有套利机会,直接进场被割。
// 逻辑一致性校验示例
function validateConsistency(orderbook) {
const bids = orderbook.bids; // 买单
const asks = orderbook.asks; // 卖单
// 1. 买单价格必须从高到低
for (let i = 1; i < bids.length; i++) {
if (bids[i].price >= bids[i-1].price) {
return false; // 排序异常
}
}
// 2. 卖单价格必须从低到高
for (let i = 1; i < asks.length; i++) {
if (asks[i].price <= asks[i-1].price) {
return false; // 排序异常
}
}
// 3. 买卖价格不能交叉
if (bids.length > 0 && asks.length > 0) {
if (bids[0].price >= asks[0].price) {
return false; // 买卖价格交叉,异常
}
}
return true;
}
避坑指南:逻辑一致性校验要放在最后一步做。因为前面的非空、类型、范围校验已经过滤掉了大部分脏数据,逻辑校验的压力会小很多。
4.5 校验流程总览
这四个校验,我建议按顺序执行:
- 非空校验:先确保数据存在
- 类型校验:再确保数据格式正确
- 范围校验:然后确保数据在合理区间
- 逻辑一致性校验:最后确保数据自洽
为什么是这个顺序?你想想看,如果一个字段是空的,你去做类型校验和范围校验,肯定会报错。先过滤掉空值,后面的校验效率更高。
嗯,这套流程我用了好几年,基本没出过大的数据问题。你刚开始可能觉得麻烦,但养成习惯后,你会发现它帮你省了无数排查 bug 的时间。
最后说一句:数据校验不是一次性的工作。随着市场变化,校验规则也要持续迭代。比如某个品种突然暴涨,你的价格范围阈值就要跟着调整。保持警惕,别让数据成为你策略的短板。
公众号:蓝海资料掘金营,微信deep3321