3. 数据采集与接入:数据源类型与实时流式接入

数据采集这块,说白了就是给异常检测系统「喂饭」。饭的质量好不好,直接决定了系统能不能干活。我见过太多项目,算法模型写得天花乱坠,结果数据源没搞好,上线第一天就崩了。

今天咱们重点聊三个核心数据源:订单簿、成交记录、账户信息。以及怎么把它们实时接入系统。

3.1 数据源类型:三大核心数据

异常交易检测,离不开这三类数据。我习惯把它们比作「交易的三要素」:谁在交易、交易了什么、交易结果如何。

3.1.1 订单簿(Order Book)

订单簿就是市场的「实时供需快照」。它记录了当前所有未成交的买单和卖单。

核心字段:

字段名 说明 示例值
symbol 交易对 BTC/USDT
bids 买单列表(价格, 数量) [[50000, 1.5], [49990, 2.0]]
asks 卖单列表(价格, 数量) [[50010, 0.8], [50020, 1.2]]
timestamp 快照时间戳 1710000000000

我在项目中遇到过一个问题:订单簿数据量极大,每秒可能更新上千次。如果全量推送,带宽和存储都扛不住。所以实际中常用「增量更新」——只推送变化的部分。

避坑指南: 我曾经因为没处理好订单簿的「深度快照」和「增量流」的同步,导致系统里买卖单价格错位,检测模型直接误报了一堆虚假异常。后来加了序列号校验才解决。

3.1.2 成交记录(Trade / Fill)

成交记录是「已经发生」的交易事实。它比订单簿更可靠,因为订单簿可能被撤单操纵,但成交记录是板上钉钉的。

核心字段:

字段名 说明 示例值
trade_id 成交唯一ID 123456789
price 成交价格 50005.5
quantity 成交数量 0.5
side 主动成交方向 buy / sell
timestamp 成交时间 1710000001000

你想想看,如果一个人频繁用「对倒」手法制造虚假成交量,成交记录里就会留下明显痕迹——同一价格、同一数量、买卖交替出现。这就是异常检测的典型特征。

3.1.3 账户信息(Account Info)

账户信息是「谁在交易」的画像数据。包括账户余额、持仓、历史交易行为等。

核心字段:

字段名 说明 示例值
account_id 账户ID acc_001
balance 账户余额 100000.00
positions 持仓列表 [{"symbol":"BTC","qty":2.5}]
risk_level 风控等级 high

我个人习惯把账户信息作为「辅助特征」来用。比如,一个账户突然从低风险变成高风险,同时订单簿上出现大量异常挂单——这基本就是预警信号了。

3.2 实时流式接入:Kafka 实战

实时性要求高的场景,比如秒级检测,必须用流式接入。Kafka 是目前业界最主流的选择。

为什么选 Kafka?说白了,它有三个核心优势:

  • 高吞吐: 单机每秒能处理几十万条消息
  • 持久化: 消息落盘,挂了也能恢复
  • 解耦: 数据生产者和消费者互不依赖
我的经验: 刚开始用 Kafka 时,我总把分区数设得特别大,觉得越多越快。结果发现分区太多反而导致 rebalance 频繁,消费者组经常「震荡」。后来我按数据源类型分主题,每个主题 3-6 个分区,效果最好。

典型接入架构:

订单簿 成交记录 账户信息 Kafka 集群 Topic: orderbook Topic: trades Topic: accounts 分区: 3 | 副本: 2 异常检测引擎 消费者组: anomaly-detector

Kafka 生产者代码示例(Python):

from kafka import KafkaProducer
import json

producer = KafkaProducer(
    bootstrap_servers=['localhost:9092'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

# 发送订单簿增量数据
orderbook_update = {
    "symbol": "BTC/USDT",
    "bids": [[50000, 1.5]],  # 新增买单
    "asks": [],               # 无变化
    "timestamp": 1710000000000
}

producer.send('orderbook', value=orderbook_update)
producer.flush()
注意: 生产环境中一定要设置 acks=all 和 retries 参数。我曾经因为没配重试,网络抖动时丢了 3 秒的订单簿数据,导致检测模型在那段时间完全「失明」。

3.3 批量数据导入:历史数据回测

实时流式接入搞定了,但别忘了历史数据。做模型训练和回测时,我们需要批量导入过去几周甚至几个月的交易数据。

我常用的批量导入方式:

  • CSV/Parquet 文件: 适合离线分析,用 Pandas 直接读取
  • 数据库导出: 从 MySQL/PostgreSQL 导出到分析集群
  • 对象存储: 从 S3/MinIO 拉取历史快照

批量导入示例(Python):

import pandas as pd
from kafka import KafkaProducer

# 读取历史成交记录
df = pd.read_parquet('historical_trades.parquet')

producer = KafkaProducer(
    bootstrap_servers=['localhost:9092'],
    value_serializer=lambda v: v.encode('utf-8')
)

# 逐行发送到Kafka(模拟实时流)
for _, row in df.iterrows():
    message = row.to_json()
    producer.send('trades_historical', value=message)
    
producer.flush()
print(f"已导入 {len(df)} 条历史成交记录")
小技巧: 批量导入时,我习惯加一个「时间戳偏移」字段,把历史数据的时间戳统一偏移到现在,这样回测时检测引擎不用改任何代码。

3.4 数据质量检查:别让脏数据毁了模型

数据接进来了,但质量怎么样?我见过最离谱的情况:某交易所的订单簿里,买单价格比卖单还高,明显是数据错乱。如果直接喂给模型,检测结果全是错的。

我常用的数据质量检查清单:

检查项 检查方法 处理方式
时间戳单调递增 比较前后两条记录的时间戳 丢弃乱序数据或重新排序
价格非负且合理 检查价格是否在 [0, 1e6] 范围内 标记异常或丢弃
买卖单不交叉 检查 bids 最高价 < asks 最低价 丢弃该快照并告警
数据完整性 检查必填字段是否缺失 补默认值或丢弃

嗯,这里要注意:数据质量检查最好放在 Kafka 消费者端做,而不是生产者端。因为生产者可能来自不同交易所,格式不统一,统一在消费者端清洗更可控。

好了,数据采集和接入这块就聊到这儿。记住一句话:数据质量决定了检测系统的天花板。把数据源管好了,后面的模型和策略才能发挥价值。


专注资料整理