2. 高频行情数据基础:Tick级数据、Level2行情、快照与逐笔成交的区别
高频行情是坏账率哨兵系统的“燃料”。如果对行情数据的颗粒度、生成机制和更新频率理解有偏差,后续的坏账计算、延迟归因和风控阈值设定都会出现系统性错误。本章从数据架构视角,厘清Tick级数据、Level2行情、快照与逐笔成交四个核心概念,并给出它们在哨兵系统中的典型用途。
2.1 核心概念定义与对比
| 概念 | 数据粒度 | 更新方式 | 典型字段 | 哨兵系统用途 |
|---|---|---|---|---|
| Tick级数据 | 逐笔成交 | 事件驱动,每笔成交即推送 | 成交价、成交量、成交编号、买卖方向 | 坏账事件精确时间戳、最小粒度回测 |
| Level2行情 | 逐笔委托 + 十档盘口 | 委托变动即推送(通常3~100ms) | 十档买卖价量、逐笔委托明细、大单标识 | 盘口深度分析、流动性枯竭预警 |
| 快照 | 某一时刻的全市场状态 | 定时(通常1s或3s)或事件触发 | 最新价、涨跌幅、成交量、持仓量 | 状态基准、坏账率分母计算 |
| 逐笔成交 | 单笔撮合结果 | 撮合完成即推送 | 成交时间、价格、数量、买方/卖方订单号 | 坏账事件精确归因、滑点计算 |
关键认知: Tick级数据与逐笔成交在多数交易所语境下等价,但部分市场将Tick定义为“最小价格变动单位”,而非“单笔成交”。本课程中Tick级数据特指逐笔成交数据流。
2.2 数据流架构与延迟分层
哨兵系统需要同时接入上述四种数据,并在内存中维护一个“实时状态树”。下图展示了高频行情数据从交易所到哨兵引擎的典型分层架构:
架构建议: 在哨兵系统中,Tick和逐笔成交通常共享同一数据管道,但消费逻辑不同。Tick消费者侧重“成交事件计数”,逐笔成交消费者侧重“订单级归因”。建议在数据总线层使用两个独立的Topic/Partition,避免相互阻塞。
2.3 快照 vs 逐笔成交:坏账计算的关键差异
坏账率哨兵的核心计算逻辑依赖“成交价格偏离理论价格的程度”。快照和逐笔成交在此场景下的差异直接影响计算精度:
- 快照方式: 每1秒或3秒取一次最新价。坏账判定基于“快照价格 vs 基准价格”。
缺点: 快照间隔内可能发生多笔极端成交,但被平均化或丢失,导致坏账漏报。 - 逐笔成交方式: 每一笔成交都触发价格检查。坏账判定基于“单笔成交价 vs 实时盘口中价”。
优点: 无遗漏,可精确到微秒级归因;代价: 数据量是快照的100~1000倍,对计算引擎压力大。
避坑: 不要将快照数据用于逐笔坏账回测。快照的“最新价”可能滞后于实际成交,导致坏账事件的时间戳偏差超过秒级,在高速交易场景下完全不可用。必须使用逐笔成交数据做坏账事件的时间基准。
2.4 Level2行情在哨兵中的特殊价值
Level2行情不仅提供十档盘口,还包含逐笔委托明细。在坏账率哨兵中,Level2数据用于以下三个核心场景:
- 流动性枯竭预警: 当买一至买五的总深度低于阈值时,即使成交价未明显偏离,也可能因流动性不足导致后续坏账。
- 大单拆分识别: 通过逐笔委托的订单编号关联,识别是否为大单拆分后的“钓鱼单”,辅助区分恶意坏账与市场波动。
- 盘口滑点估算: 在坏账事件发生时,用Level2盘口数据估算“如果立即反向平仓,实际滑点成本”,作为坏账严重程度的辅助指标。
2.5 数据接入代码示例:Tick与快照的并行消费
以下Python代码展示如何使用异步方式同时消费Tick流和快照流,并在内存中维护一个“最新快照+最近100笔Tick”的窗口,供坏账计算使用:
import asyncio
from collections import deque
class HighFreqDataConsumer:
def __init__(self, tick_topic: str, snapshot_topic: str):
self.tick_queue = deque(maxlen=100) # 保留最近100笔Tick
self.latest_snapshot = {}
self.tick_topic = tick_topic
self.snapshot_topic = snapshot_topic
async def consume_tick(self, tick_data: dict):
"""处理逐笔成交数据"""
tick = {
'price': tick_data['price'],
'volume': tick_data['volume'],
'timestamp': tick_data['timestamp'],
'side': tick_data['side']
}
self.tick_queue.append(tick)
# 触发坏账检查
await self.check_bad_trade(tick)
async def consume_snapshot(self, snapshot: dict):
"""处理快照数据,更新基准状态"""
self.latest_snapshot = {
'last_price': snapshot['last_price'],
'bid1': snapshot['bid1'],
'ask1': snapshot['ask1'],
'timestamp': snapshot['timestamp']
}
async def check_bad_trade(self, tick: dict):
"""基于最新快照和Tick判断是否坏账"""
if not self.latest_snapshot:
return
mid_price = (self.latest_snapshot['bid1'] + self.latest_snapshot['ask1']) / 2
deviation = abs(tick['price'] - mid_price) / mid_price
if deviation > 0.01: # 偏离超过1%视为坏账
print(f"⚠ 坏账事件: 价格={tick['price']}, 中价={mid_price:.2f}, 偏离={deviation:.4f}")
# 使用示例(伪代码)
# consumer = HighFreqDataConsumer('tick_topic', 'snapshot_topic')
# asyncio.run(consumer.start())
工程要点: 上述代码中
deque(maxlen=100)用于限制内存占用。在生产环境中,Tick队列长度应根据哨兵系统的延迟容忍度动态调整。如果坏账检查需要回溯更多历史Tick,建议使用环形缓冲区(Ring Buffer)替代deque,以获得更稳定的内存分配性能。
2.6 四种数据在哨兵系统中的协同关系
下图展示四种数据在坏账率哨兵引擎中的协同工作流:
2.7 本章小结
- Tick级数据 = 逐笔成交,是坏账事件的最小粒度证据。
- Level2行情 提供盘口深度和委托明细,用于流动性分析和滑点估算。
- 快照 提供周期性状态基准,适合做坏账率分母计算,但不适合事件归因。
- 逐笔成交 是坏账哨兵的核心输入,必须保证其时间戳精度和顺序完整性。
- 四种数据在哨兵引擎中协同工作,任何一路的延迟或丢失都需触发降级策略。