3. 数据质量初探:缺失值检测、异常值检测、重复值检测、数据一致性检查

做市业务里,数据就是我们的命根子。但说实话,我见过太多团队一上来就急着建模,结果被脏数据坑得死去活来。今天咱们就聊聊数据清洗的第一步——质量初探。说白了,就是给你的数据做个全面体检。

3.1 缺失值检测:别让空值毁了你的策略

缺失值这东西,在量化数据里太常见了。交易所网络波动、数据源中断、撮合引擎延迟,随便哪个环节出点问题,你的数据表里就会多出几个NaN。

核心思路:先定位,再分析,最后决定怎么处理。别一上来就fillna(),那是在给自己埋雷。

我个人习惯用pandas的isnull()配合sum(),一眼就能看出每列的缺失情况。代码很简单,但效果很直观:

import pandas as pd
import numpy as np

# 模拟做市数据
df = pd.DataFrame({
    'timestamp': pd.date_range('2024-01-01', periods=100, freq='1s'),
    'bid_price': np.random.uniform(100, 101, 100),
    'ask_price': np.random.uniform(100.5, 101.5, 100),
    'bid_volume': np.random.randint(1, 100, 100),
    'ask_volume': np.random.randint(1, 100, 100),
    'spread': np.random.uniform(0.1, 0.5, 100)
})

# 人为制造缺失值
df.loc[10:15, 'bid_price'] = np.nan
df.loc[30:35, 'ask_volume'] = np.nan

# 缺失值检测
missing_count = df.isnull().sum()
missing_ratio = df.isnull().mean() * 100

print("缺失值统计:")
print(missing_count)
print("\n缺失比例(%):")
print(missing_ratio.round(2))

输出结果大概长这样:

列名 缺失数量 缺失比例(%)
timestamp 0 0.00
bid_price 6 6.00
ask_price 0 0.00
bid_volume 0 0.00
ask_volume 6 6.00
spread 0 0.00

我的经验:做市数据里,如果bid_price和ask_price同时缺失,那大概率是交易所断流了。如果只有一边缺失,可能是数据推送的时序问题。这两种情况处理方式完全不同,千万别一刀切。

3.2 异常值检测:揪出那些离谱的价格

异常值在量化做市里特别要命。一个错误的报价,可能让你的策略瞬间亏掉几天的利润。我遇到过最夸张的一次,某交易所的撮合引擎抽风,把一笔成交价写成了正常价格的100倍,还好我们检测得早。

常用的方法有三种,我按推荐程度排个序:

  1. Z-score方法——适合正态分布的数据,比如spread、收益率
  2. IQR方法——适合偏态分布,比如成交量、订单笔数
  3. 业务规则法——这个最靠谱,比如价格不能为负、spread不能大于某个阈值

来看看代码实现:

from scipy import stats

# Z-score异常检测
z_scores = np.abs(stats.zscore(df['spread'].dropna()))
outliers_z = df['spread'][z_scores > 3]

# IQR异常检测
Q1 = df['bid_volume'].quantile(0.25)
Q3 = df['bid_volume'].quantile(0.75)
IQR = Q3 - Q1
outliers_iqr = df[(df['bid_volume'] < Q1 - 1.5*IQR) | 
                  (df['bid_volume'] > Q3 + 1.5*IQR)]

# 业务规则检测
# 比如:spread不能超过0.5,否则就是异常
outliers_biz = df[df['spread'] > 0.5]

print(f"Z-score检测到异常值: {len(outliers_z)} 个")
print(f"IQR检测到异常值: {len(outliers_iqr)} 个")
print(f"业务规则检测到异常值: {len(outliers_biz)} 个")

注意:别迷信统计方法。我记得有一次,Z-score把正常的市场波动当成了异常,差点让我们错过了一个大行情。你想想看,如果市场突然出现巨量成交,那其实是机会,不是错误。

3.3 重复值检测:别让数据膨胀

重复数据在量化做市里是个隐形杀手。它不会让你的策略报错,但会悄悄扭曲你的统计结果。比如计算平均spread时,重复数据会让结果偏小,因为重复的往往是流动性好的时刻。

检测重复值,我一般看两个维度:

  • 完全重复——所有列都一样,这种直接删
  • 部分重复——比如timestamp相同但价格不同,这种要小心
# 完全重复检测
duplicates_full = df.duplicated()
print(f"完全重复行数: {duplicates_full.sum()}")

# 基于timestamp的重复检测
duplicates_ts = df.duplicated(subset=['timestamp'])
print(f"时间戳重复行数: {duplicates_ts.sum()}")

# 查看重复的具体数据
if duplicates_ts.any():
    print("\n时间戳重复的数据:")
    print(df[duplicates_ts].head())

避坑指南:我曾经在处理Level2数据时,发现同一个时间戳出现了两条不同的报价。后来查出来是数据源做了快照和增量两次推送。这种情况不能简单去重,得根据业务逻辑合并。

3.4 数据一致性检查:让数据自洽

一致性检查,说白了就是看数据内部有没有矛盾。做市数据里最常见的几个检查点:

检查项 规则 违反后果
买卖价关系 ask_price > bid_price 套利空间为负,数据错误
spread计算 spread = ask_price - bid_price 统计指标失真
时间顺序 timestamp严格递增 回测结果不可信
成交量非负 bid_volume >= 0, ask_volume >= 0 逻辑错误
# 一致性检查
inconsistencies = []

# 检查买卖价关系
bad_spread = df[df['ask_price'] <= df['bid_price']]
if len(bad_spread) > 0:
    inconsistencies.append(f"买卖价倒挂: {len(bad_spread)} 行")

# 检查spread计算是否正确
df['calc_spread'] = df['ask_price'] - df['bid_price']
spread_diff = df[abs(df['spread'] - df['calc_spread']) > 0.001]
if len(spread_diff) > 0:
    inconsistencies.append(f"Spread计算不一致: {len(spread_diff)} 行")

# 检查时间顺序
time_diff = df['timestamp'].diff()
bad_time = time_diff[time_diff < pd.Timedelta(0)]
if len(bad_time) > 0:
    inconsistencies.append(f"时间戳逆序: {len(bad_time)} 行")

# 检查成交量
bad_volume = df[(df['bid_volume'] < 0) | (df['ask_volume'] < 0)]
if len(bad_volume) > 0:
    inconsistencies.append(f"负成交量: {len(bad_volume)} 行")

if inconsistencies:
    print("发现数据不一致问题:")
    for issue in inconsistencies:
        print(f"  - {issue}")
else:
    print("数据一致性检查通过!")

核心原则:数据一致性检查不是一次性工作。我建议把它写成自动化脚本,每天跑一次。做市业务的数据量太大,人工检查根本不现实。

3.5 知识体系总览

说了这么多,咱们用一张图把整个数据质量初探的流程串起来。这样你脑子里就有个清晰的框架了。

数据质量初探:四维检测体系 原始数据 缺失值检测 异常值检测 重复值检测 一致性检查 isnull() / sum() 缺失比例分析 模式识别 Z-score / IQR 业务规则法 可视化检测 duplicated() 完全/部分重复 业务去重 买卖价关系 时间顺序 计算验证 清洗后的高质量数据 四个维度缺一不可,交叉验证才能保证数据质量

嗯,这张图把咱们今天聊的内容都串起来了。从原始数据出发,经过四个维度的检测,最终得到干净可用的数据。每一步都有对应的工具和方法,你照着这个框架走,基本不会漏掉什么。

最后说一句:数据质量初探不是一次性工作。做市业务的数据是流式的,每天都有新数据进来。我建议你把这套检测流程做成定时任务,每天跑一遍。发现问题及时处理,别等到回测时才发现数据有问题,那代价就大了。


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