3、日志数据清洗:缺失值处理、异常值检测与剔除、时间戳对齐与标准化
做量化交易的朋友都知道,日志数据这东西,看着规整,实际上脏得很。我入行头两年,就吃过不少亏。有一次策略回测跑出来收益曲线漂亮得不像话,结果一查,原来是某天的成交数据全丢了,系统自动补了个零。嗯,从那以后,我对数据清洗这件事就格外上心。
说白了,日志清洗就是给数据「洗澡」。把脏的、乱的、不对齐的,统统处理干净。今天咱们就聊聊这个环节里的三个核心动作:缺失值、异常值、时间戳。
3.1 缺失值处理:别让空值坑了你
日志里出现缺失值,太常见了。网络断了、行情源挂了、系统重启了,都可能导致某条记录没写进去。我个人习惯,拿到日志第一件事就是跑个 isnull() 看看。
核心原则:不要盲目填充,先搞清楚缺失的原因。
常见的处理方式有三种:
- 直接删除:如果缺失比例很小(比如低于5%),而且缺失是随机的,直接删掉最省事。
- 向前填充:用上一个有效值补上。适合价格、持仓这类变化缓慢的数据。
- 插值法:用前后值取平均,或者线性插值。适合成交量、波动率这类有趋势的数据。
我在项目中遇到过一种情况:某只股票的分钟线日志,连续10分钟都是空值。当时我用了向前填充,结果策略以为价格没变,直接下了单。后来我加了个阈值判断——连续缺失超过3笔,直接标记为「数据异常」,不参与计算。
# 我个人常用的缺失值处理逻辑
import pandas as pd
def clean_missing(df, max_consecutive=3):
# 先检查连续缺失
missing_mask = df['price'].isna()
consecutive = missing_mask.groupby((~missing_mask).cumsum()).transform('sum')
# 连续缺失超过阈值,标记为异常
df.loc[consecutive > max_consecutive, 'data_quality'] = 'bad'
# 普通缺失用向前填充
df['price'].fillna(method='ffill', inplace=True)
return df
小技巧:我习惯在清洗后保留一列「数据质量」标记。这样回测时能清楚知道哪些数据是原始值,哪些是补出来的。
3.2 异常值检测与剔除:别让极端值带偏你
异常值这东西,有时候是行情真的暴涨暴跌,有时候就是数据写错了。怎么区分?我总结了一套「三步走」的方法。
第一步:肉眼扫一遍
别笑,这步真有用。把价格、成交量画成折线图,一眼就能看出哪些点不对劲。比如某只股票平时成交量几百万,突然某天显示几十亿,那八成是数据录入时多打了个零。
第二步:用统计方法筛
我个人偏爱「3σ原则」和「IQR方法」。3σ适合正态分布的数据,IQR更稳健,不受极端值影响。
# IQR方法检测异常值
def detect_outliers_iqr(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
return outliers
第三步:结合业务逻辑判断
这一步最容易被忽略。你想想看,某只股票涨停了,价格突然跳升10%,这在统计上可能是异常值,但在业务上完全合理。我曾经犯过这个错——把某次「乌龙指」事件的数据当异常剔除了,结果回测少算了一笔大额亏损。
注意:异常值不一定要剔除。如果是真实的极端行情,保留它反而能让策略更鲁棒。我建议先标记,再决定是剔除还是保留。
3.3 时间戳对齐与标准化:统一时钟,统一节奏
做量化最头疼的事之一,就是不同数据源的时间戳对不上。交易所的时间、行情商的时间、自己服务器的时间,各有各的偏差。我记得有一次,策略在模拟盘跑得好好的,一上实盘就亏钱。查了两天才发现,是行情时间戳比实际慢了200毫秒。
时间戳对齐,核心就两件事:
- 统一时区:全部转成UTC,或者全部用北京时间。千万别混着用。
- 统一精度:有的数据精确到秒,有的精确到毫秒。对齐到同一个精度,我一般选毫秒。
# 时间戳标准化示例
def standardize_timestamp(df, time_col='timestamp'):
# 转成datetime格式
df[time_col] = pd.to_datetime(df[time_col])
# 统一时区到UTC
df[time_col] = df[time_col].dt.tz_localize('Asia/Shanghai', ambiguous='infer')
df[time_col] = df[time_col].dt.tz_convert('UTC')
# 对齐到整秒(或整毫秒)
df[time_col] = df[time_col].dt.floor('1s')
return df.sort_values(time_col)
还有一个坑:不同交易所的休市时间不一样。A股中午休市,美股没有。如果你把A股和美股的数据直接拼在一起,中间那段空白时间就会造成对齐错误。我的做法是,先按交易所分开清洗,最后再合并。
对齐后的数据,一定要做一次「时间连续性检查」。看看相邻两条记录的时间差是不是均匀的。如果突然出现一个大跳变,那说明中间有数据丢了。
3.4 本章知识体系总览
下面这张图,是我自己画的数据清洗流程。每次做新项目,我都会先按这个框架走一遍,基本不会漏掉什么。
这张图看着简单,但每一步都有讲究。缺失值处理不是无脑填充,异常值检测不是一刀切,时间戳对齐更不是转个格式就完事。我做了这么多年,每次清洗还是会遇到新问题。嗯,这就是量化交易的魅力——你永远不知道数据里藏着什么惊喜。
我的习惯:每次清洗完数据,我都会写一份「数据清洗报告」,记录发现了多少缺失值、多少异常值、做了哪些处理。这样以后复盘时,能清楚知道数据是怎么来的。
公众号:蓝海资料掘金营,微信deep3321