4. 高频数据预处理:去重、去噪、时间对齐、缺失值处理、异常值检测

高频行情数据在采集、传输和存储过程中,不可避免地会引入各种质量问题。原始数据通常包含重复记录、噪声、时间戳偏差、缺失值和异常跳变。本章将系统讲解高频数据预处理的五大核心步骤,确保标签校准建立在干净、可靠的数据基础之上。

核心原则:高频数据预处理的目标是“保留真实微观结构,剔除系统错误与偶然异常”。过度清洗会丢失有效信息,清洗不足则导致标签偏差。每一步都需要结合市场微观结构知识进行判断。

4.1 数据去重

重复数据通常由交易所冗余推送、网关重传或存储系统写入异常导致。重复记录会扭曲统计指标(如成交量、笔数)并导致标签错误。

重复类型 特征 处理方法
完全重复 所有字段(时间戳、价格、成交量、买卖方向)完全相同 保留第一条,删除后续完全相同的记录
时间戳重复 时间戳相同,但价格或成交量不同 按业务规则合并或保留最后一条(需确认撮合顺序)
跨日重复 同一笔交易在收盘后再次推送 按交易日+唯一标识(如 TradeID)去重
# Python 示例:基于时间戳和价格去重
import pandas as pd

def deduplicate_tick_data(df):
    # 完全重复去除
    df = df.drop_duplicates(subset=['timestamp', 'price', 'volume', 'side'])
    # 按时间戳排序后,保留同一毫秒内的最后一条(假设为最终状态)
    df = df.sort_values('timestamp').drop_duplicates(subset='timestamp', keep='last')
    return df.reset_index(drop=True)

4.2 数据去噪

高频行情中的噪声主要来源于微观结构效应(买卖价差、离散价格档位)和传输抖动。去噪的目标是平滑随机波动,同时保留趋势和突变点。

  • 价格噪声:价格在买卖价差之间来回跳动,形成“锯齿”形态。使用中值滤波或移动平均可有效抑制。
  • 成交量噪声:单笔成交量异常小(如1股)或异常大(如错误合并),需结合阈值过滤。
  • 时间戳噪声:网络延迟导致时间戳出现毫秒级抖动,需进行时间对齐。
去噪策略对比:
  • 中值滤波:对价格序列取滑动窗口(如5笔)中位数,抗脉冲噪声能力强,适合去除报价跳动。
  • 卡尔曼滤波:基于状态空间模型,适合实时处理,但需设定过程噪声和观测噪声参数。
  • 小波去噪:将信号分解到不同频率尺度,适合分离高频噪声与低频趋势。
高频行情去噪流程对比 原始行情序列 价格跳动噪声 成交量毛刺噪声 时间戳抖动噪声 中值滤波 卡尔曼滤波 小波去噪 去噪后序列 保留趋势结构 抑制随机跳动 对齐时间戳 过滤毛刺

4.3 时间对齐

不同数据源(如逐笔成交、逐笔委托、Level-2快照)的时间戳精度和基准可能不同。时间对齐是将所有数据统一到同一时间坐标系下的关键步骤。

对齐方式 适用场景 实现方法
精确对齐 同一交易所、同一精度(如微秒级) 直接按时间戳匹配,取最近邻记录
插值对齐 不同精度(如毫秒 vs 微秒) 线性插值或前向填充(forward fill)
聚合对齐 需要降频(如从逐笔到1秒K线) 按固定时间窗口聚合(OHLCV)
# Python 示例:前向填充对齐
def align_timestamps(df, target_freq='1s'):
    # 设置时间戳为索引
    df = df.set_index('timestamp')
    # 重采样到目标频率,使用前向填充
    aligned = df.resample(target_freq).ffill()
    return aligned.reset_index()

4.4 缺失值处理

高频数据中的缺失值可能由网络丢包、交易所暂停交易或数据采集故障引起。处理方式取决于缺失模式和数据用途。

  • 连续缺失(如停牌期间):不填充,直接标记为“无交易区间”。
  • 零星缺失(单笔丢失):使用前向填充(last observation carried forward, LOCF)或线性插值。
  • 开盘/收盘缺失:参考集合竞价数据或相邻交易日同一时段数据。
注意:高频数据中,缺失值填充需谨慎。使用前向填充时,若缺失时间过长(如超过10秒),填充值可能严重偏离真实市场状态。建议设置最大填充间隔阈值。

4.5 异常值检测

异常值检测是预处理中最关键的环节,直接影响标签校准的准确性。高频行情中常见的异常包括价格跳变和成交量异常。

4.5.1 价格跳变检测

价格跳变指相邻两笔交易价格出现不符合市场微观结构的剧烈变动。检测方法包括固定阈值法和统计法。

检测方法 原理 阈值设定
固定阈值法 价格变动超过预设百分比(如0.5%) 根据品种波动率设定,如股指期货0.2%,个股0.5%
Z-score法 计算价格变动的Z分数,超过3视为异常 动态计算,需剔除极端值后重新计算
中位数绝对偏差法 使用MAD(Median Absolute Deviation)替代标准差,更稳健 MAD超过3倍中位数绝对偏差
市场微观结构法 结合买卖盘口深度判断价格跳变是否合理 跳变幅度超过当前买卖价差+深度加权
# Python 示例:基于MAD的价格跳变检测
import numpy as np

def detect_price_jumps(prices, threshold=3.0):
    # 计算价格变动率
    returns = np.diff(prices) / prices[:-1]
    # 计算中位数和MAD
    median = np.median(returns)
    mad = np.median(np.abs(returns - median))
    # 计算修正Z-score
    modified_z_scores = 0.6745 * (returns - median) / mad
    # 标记异常
    jump_indices = np.where(np.abs(modified_z_scores) > threshold)[0] + 1
    return jump_indices, modified_z_scores

4.5.2 成交量异常检测

成交量异常通常表现为单笔成交量远超正常水平(如错误合并)或远低于正常水平(如碎片化成交)。

  • 单笔成交量过大:超过该品种日均单笔成交量的10倍,或超过当日累计成交量的5%。
  • 单笔成交量过小:低于最小交易单位(如1手),或连续多笔成交量极低。
  • 成交量与价格不匹配:价格大幅变动但成交量极小,或价格不变但成交量激增。
异常值检测流程 原始行情数据 价格跳变检测 Z-score / MAD / 阈值 成交量异常检测 单笔量 / 累计量 / 匹配 异常标记与分类 保留 / 修正 / 剔除

4.6 综合预处理流程

在实际工程中,上述五个步骤需要按顺序执行,形成完整的预处理流水线。

  1. 步骤1:数据加载与格式统一 — 将不同数据源转换为统一格式(如DataFrame),检查时间戳精度和字段完整性。
  2. 步骤2:去重 — 删除完全重复和时间戳重复的记录,保留唯一标识。
  3. 步骤3:时间对齐 — 将所有数据对齐到统一时间基准,处理不同精度问题。
  4. 步骤4:去噪 — 应用中值滤波或卡尔曼滤波,平滑微观结构噪声。
  5. 步骤5:缺失值处理 — 根据缺失模式选择填充或标记。
  6. 步骤6:异常值检测与处理 — 检测价格跳变和成交量异常,标记或修正异常记录。
  7. 步骤7:质量验证 — 检查预处理后的数据统计特征(如最大回撤、成交量分布),确保未引入新偏差。
工程实践建议:
  • 预处理流水线应支持增量处理(incremental processing),避免每次全量重算。
  • 所有异常标记应保留原始字段和标记原因,便于后续审计和标签校准回溯。
  • 建议对每个品种单独设定预处理参数(如跳变阈值、滤波窗口),因为不同品种的微观结构差异显著。
# 完整预处理流水线示例
def preprocess_tick_pipeline(df, config):
    # 1. 去重
    df = deduplicate_tick_data(df)
    # 2. 时间对齐(重采样到1ms)
    df = align_timestamps(df, target_freq='1ms')
    # 3. 去噪(中值滤波,窗口=5)
    df['price_smooth'] = df['price'].rolling(window=5, center=True).median()
    # 4. 缺失值填充(前向填充,最大间隔3笔)
    df['price_smooth'] = df['price_smooth'].ffill(limit=3)
    # 5. 异常检测
    jump_idx, _ = detect_price_jumps(df['price_smooth'].values, threshold=config['jump_threshold'])
    df.loc[jump_idx, 'is_anomaly'] = True
    return df

经过上述预处理后,高频行情数据将具备一致性、连续性和可靠性,为后续的标签校准(如买卖方向识别、订单簿重建、微观结构特征提取)奠定坚实基础。