数据采集与清洗:获取Level-2行情数据、处理缺失值与异常值、时间对齐与重采样、数据标准化

做流动性预判模型,数据是地基。地基不稳,楼盖得再漂亮也得塌。

我个人习惯把数据采集与清洗看作整个流程里最枯燥、但也最关键的环节。你想想看,模型再牛,喂进去的是垃圾,出来的只能是垃圾。今天我们就来聊聊,怎么把Level-2行情数据从原始状态,变成模型能吃的干净数据。

获取Level-2行情数据

Level-2数据,说白了就是比普通行情更细的切片。普通行情3秒一张快照,Level-2能做到3-5笔/秒。我刚开始做的时候,觉得这玩意儿不就是数据多点嘛,后来才发现,细节里藏着魔鬼。

获取方式主要有三种:

  • 交易所直连:延迟最低,但门槛高,适合机构
  • 数据服务商API:比如万得、聚宽、米筐,适合个人和中小团队
  • 第三方数据仓库:已经清洗好的数据,直接买,省事但贵

我个人建议,初期先用服务商API。为什么呢?因为省时间。你不需要自己处理底层协议,也不用担心断线重连。我在项目中遇到过,自己写直连程序,结果交易所协议升级,折腾了一周才搞定。嗯,那周我基本没睡好。

核心字段:获取Level-2数据时,重点关注以下字段——

  • 时间戳(精确到毫秒)
  • 最新成交价
  • 买卖十档挂单(价格+数量)
  • 逐笔成交明细
  • 大单统计(单笔>=100手)
# 伪代码示例:从聚宽获取Level-2数据
import jqdatasdk as jq

jq.auth('username', 'password')
df = jq.get_price(
    security='000001.XSHE',
    start_date='2024-01-01',
    end_date='2024-01-31',
    frequency='minute',
    fields=['open', 'close', 'high', 'low', 'volume', 'money']
)
print(df.head())

处理缺失值与异常值

数据拿到手,第一件事不是建模,而是检查数据质量。我见过太多人,数据都没看两眼,直接跑模型,结果跑出来一堆鬼画符。

缺失值处理

Level-2数据缺失,常见原因有:

  • 股票停牌(全天无数据)
  • 盘中临时停牌(某段时间无数据)
  • 网络传输丢包(个别时间点缺失)

处理方式,我一般分情况讨论:

缺失类型 处理方法 适用场景
连续缺失(如停牌) 直接删除该时间段 停牌期间无交易,保留无意义
零星缺失(1-2个时间点) 前向填充(ffill) 流动性指标变化缓慢
随机缺失 插值法(线性/样条) 价格类数据,趋势明显

避坑指南:我曾经用均值填充处理过缺失的买卖价差数据,结果模型预测的流动性偏差了30%。后来才发现,均值填充会抹平极端值,而流动性预判恰恰需要捕捉极端情况。从那以后,我改用前向填充+标记缺失时间点。

异常值处理

异常值,说白了就是那些明显不合理的数据。比如:

  • 成交价为负数(数据错误)
  • 单笔成交量超过总股本(数据错误)
  • 买卖价差为0(可能是撮合错误)
  • 价格瞬间跳变超过10%(可能是数据异常)

我常用的方法是3σ原则和IQR(四分位距)法。但注意,金融数据往往有厚尾特征,直接用3σ可能会误删有效数据。我个人习惯先用IQR法粗筛,再结合业务逻辑人工判断。

# 异常值检测:IQR法
import numpy as np

def detect_outliers_iqr(data, column):
    Q1 = data[column].quantile(0.25)
    Q3 = data[column].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
    return outliers

时间对齐与重采样

不同数据源的时间戳,精度可能不一样。有的精确到秒,有的精确到毫秒。你想想看,如果不对齐,模型会以为两个不同时间点的数据是同时发生的,那预测结果能准吗?

时间对齐,我一般做两步:

  1. 统一时间基准:把所有时间戳转为UTC+8,精确到毫秒
  2. 对齐到标准时间轴:比如以1分钟为间隔,把数据对齐到整分钟

重采样,说白了就是把高频数据降频。比如把逐笔成交数据,重采样成1分钟K线。为什么要做?因为流动性预判模型通常不需要那么高的频率,1分钟或5分钟就够了。频率太高,噪声大,模型容易过拟合。

重采样规则

  • 价格类:取最后一条(收盘价)
  • 成交量类:求和
  • 买卖价差:取平均值
  • 挂单深度:取最后一条
# 重采样示例:1分钟K线
import pandas as pd

df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)

resampled = df.resample('1T').agg({
    'price': 'last',
    'volume': 'sum',
    'bid_ask_spread': 'mean',
    'depth': 'last'
}).dropna()

数据标准化

数据标准化,就是把不同量纲的数据,映射到同一个尺度。比如成交量和价格,一个几百万,一个几十块,直接放一起建模,模型会认为成交量更重要——其实不一定。

常用的标准化方法:

方法 公式 适用场景
Z-score标准化 (x - μ) / σ 数据近似正态分布
Min-Max标准化 (x - min) / (max - min) 数据有明确边界
Robust标准化 (x - median) / IQR 数据有异常值

我个人习惯用Robust标准化。为什么呢?因为金融数据里异常值太多了,Z-score会被异常值拉偏,Min-Max会被极端值压缩。Robust用中位数和IQR,抗干扰能力强。我在项目中对比过,用Robust标准化的模型,预测稳定性比Z-score高了大概15%。

注意:标准化时,一定要用训练集的参数去转换测试集。不要用测试集的均值和标准差,否则会造成数据泄露,模型评估结果会虚高。我曾经犯过这个错,回测结果漂亮得不行,实盘一跑就崩。嗯,那教训够我记一辈子。

# Robust标准化示例
from sklearn.preprocessing import RobustScaler

scaler = RobustScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)  # 注意:用训练集的参数

本章知识体系

下面这张图,概括了数据采集与清洗的完整流程。你可以把它当作操作手册,做项目时对照着来。

数据采集与清洗流程 数据获取 Level-2行情数据 缺失值处理 删除/填充/插值 异常值处理 3σ/IQR/业务规则 时间对齐 统一时间基准 重采样 降频到1min/5min 数据标准化 Z-score/Min-Max/Robust 输出:干净、对齐、标准化的流动性数据集 可用于流动性预判模型训练 每个步骤都包含数据质量检查,确保输出可靠 质量检查不通过则返回

总结一下:数据采集与清洗,说白了就是四个字——去伪存真。把错误的数据去掉,把缺失的数据补上,把不同频率的数据对齐,把不同量纲的数据标准化。每一步都做扎实了,模型才能跑得稳。我个人经验,数据清洗花的时间,至少占整个项目的一半。别嫌麻烦,这钱花得值。

蓝海数据掘金营,专注资料整理