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 校验流程总览

这四个校验,我建议按顺序执行:

  1. 非空校验:先确保数据存在
  2. 类型校验:再确保数据格式正确
  3. 范围校验:然后确保数据在合理区间
  4. 逻辑一致性校验:最后确保数据自洽

为什么是这个顺序?你想想看,如果一个字段是空的,你去做类型校验和范围校验,肯定会报错。先过滤掉空值,后面的校验效率更高。

数据校验流程 原始数据 ① 非空校验 ② 类型校验 ③ 范围校验 ④ 逻辑一致性校验 ✅ 合格数据 → 策略使用 过滤空值 统一数据类型 检查合理区间 检查数据自洽性

嗯,这套流程我用了好几年,基本没出过大的数据问题。你刚开始可能觉得麻烦,但养成习惯后,你会发现它帮你省了无数排查 bug 的时间。

最后说一句:数据校验不是一次性的工作。随着市场变化,校验规则也要持续迭代。比如某个品种突然暴涨,你的价格范围阈值就要跟着调整。保持警惕,别让数据成为你策略的短板。


公众号:蓝海资料掘金营,微信deep3321