4. 数据准确性校验:价格合理性检查、成交量校验、买卖盘平衡校验

行情数据里,我最怕什么?不是延迟,不是丢包,而是数据错了你还不知道。价格多一个小数点,成交量多一个零,买卖盘不平衡——这些错误一旦进入策略,轻则亏手续费,重则直接爆仓。

今天我们就来聊聊数据准确性校验。说白了,就是给行情数据做「体检」。我做了这么多年量化,总结下来,核心就三块:价格合理性、成交量校验、买卖盘平衡。

4.1 价格合理性检查

价格是交易的生命线。价格错了,后面全白搭。我个人习惯,先做三件事:

4.1.1 涨跌停检查

A股有±10%的涨跌停限制,科创板是±20%,期货每个品种还不一样。你想想看,如果某只股票突然报了个涨停价以上的买单,那肯定是脏数据。

核心逻辑: 当前价格必须在 [昨日收盘价 × (1 - 涨跌停比例), 昨日收盘价 × (1 + 涨跌停比例)] 区间内。
def check_price_limit(price, prev_close, limit_ratio=0.1):
    lower = prev_close * (1 - limit_ratio)
    upper = prev_close * (1 + limit_ratio)
    if price < lower or price > upper:
        return False  # 价格超出涨跌停范围
    return True

嗯,这里要注意:期货的涨跌停比例是动态的,有些品种在临近交割月会调整。我遇到过一回,某期货合约的涨跌停比例从5%突然调到7%,我的校验逻辑没跟上,结果把正常数据当异常给过滤了。那叫一个尴尬。

4.1.2 最小变动单位检查

每个品种都有最小变动单位。A股是0.01元,螺纹钢期货是1元,黄金期货是0.02元。如果价格不能被最小变动单位整除,那数据肯定有问题。

避坑指南: 我曾经在检查黄金期货时,发现价格是 398.55,但最小变动单位是 0.02。398.55 ÷ 0.02 = 19927.5,不是整数。后来发现是数据源把 398.54 写成了 398.55。这种错误,肉眼根本看不出来。
def check_tick_size(price, tick_size):
    # 注意浮点数精度问题
    remainder = round(price / tick_size, 10) % 1
    return abs(remainder) < 1e-9 or abs(remainder - 1) < 1e-9

4.2 成交量校验

成交量校验,说白了就是看数据合不合理。我一般从两个维度下手:

4.2.1 非负性检查

这个最简单,但也最容易忽略。成交量、成交金额、持仓量,都必须是 ≥ 0 的。出现负数?直接扔掉。

4.2.2 单调性检查

成交量是累计值,所以每一笔的成交量必须 ≥ 上一笔。如果出现成交量减少,那肯定是数据回退或者乱序了。

注意: 有些交易所会在盘后做数据修正,导致成交量「回退」。这种情况要区分对待。我的做法是:盘中做严格校验,盘后做宽松校验。
def check_volume_monotonic(current_vol, prev_vol):
    if current_vol < prev_vol:
        return False  # 成交量回退,异常
    return True

4.3 买卖盘平衡校验

买卖盘平衡,是很多人容易忽略的点。我刚开始做量化时也吃过亏——买卖盘数据不平衡,导致我的订单簿重建全乱了。

4.3.1 买卖盘档位一致性

买一到买五的价格必须递增,卖一到卖五的价格也必须递增。如果买一的价格比买二还高,那数据肯定有问题。

4.3.2 买卖盘价格交叉检查

买一的价格必须 ≤ 卖一的价格。如果买一 ≥ 卖一,那就意味着买卖盘交叉了——这在正常行情里是不允许的。

核心逻辑: 买一 ≤ 买二 ≤ ... ≤ 买五 ≤ 卖五 ≤ ... ≤ 卖一?不对,应该是:买一 ≤ 买二 ≤ ... ≤ 买五 < 卖五 ≤ ... ≤ 卖一。
def check_order_book(bids, asks):
    # bids: [(price, volume), ...] 按价格降序排列
    # asks: [(price, volume), ...] 按价格升序排列
    
    # 检查买盘是否递增
    for i in range(len(bids) - 1):
        if bids[i][0] >= bids[i+1][0]:
            return False
    
    # 检查卖盘是否递增
    for i in range(len(asks) - 1):
        if asks[i][0] >= asks[i+1][0]:
            return False
    
    # 检查买卖盘是否交叉
    if bids[-1][0] >= asks[0][0]:
        return False
    
    return True

4.3.3 买卖盘总量平衡

这个不是强制的,但我建议做。买盘总金额和卖盘总金额应该大致相当。如果买盘是卖盘的10倍,那要么是数据错了,要么是市场极端异常。

个人经验: 我一般设置一个阈值,比如买卖盘总量比超过 5:1 就报警。但要注意,在开盘和收盘时段,这个比例可能会暂时失衡,所以我会加一个时间窗口判断。

4.4 知识体系总览

下面这张图,是我自己总结的数据准确性校验框架。你可以把它当成一个检查清单,每次接入新数据源时,对着过一遍。

数据准确性校验框架 行情数据校验 价格合理性检查 涨跌停范围检查 最小变动单位检查 成交量校验 非负性检查 单调性检查 买卖盘平衡校验 档位一致性检查 价格交叉检查 总量平衡检查 校验通过 → 数据入库 校验失败 → 标记异常 / 丢弃 / 等待重传

4.5 实战建议

最后,说几点我的实战心得:

  • 校验要分层: 第一层做快速检查(涨跌停、非负性),第二层做深度检查(单调性、买卖盘平衡)。快速检查不过的直接丢弃,减少计算开销。
  • 异常要分级: 价格超出涨跌停 → 直接丢弃;成交量回退 → 标记等待下一笔确认;买卖盘不平衡 → 报警人工介入。
  • 别忘了日志: 每次校验失败,都要记录原始数据、校验规则、失败原因。我吃过一次亏,数据源出了问题,但没日志,排查了三天才找到根因。
最后提醒一句: 校验规则不是越严格越好。太严格会误杀正常数据,太宽松又漏不过异常。我的建议是:先宽松上线,再根据实际数据逐步收紧。别一上来就搞个「完美」的校验逻辑,你会被误报淹死的。

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