数据清洗与对齐:处理不同市场交易时间差异、缺失值处理、重采样与频率对齐
做跨市场分析,最头疼的是什么?
不是策略逻辑,不是模型选型。是数据本身。
我刚开始做跨市场套利的时候,天真地以为把A股和港股数据拉下来就能直接跑。结果一跑,全是NaN。为什么?A股中午休盘,港股不休息。美股还有盘前盘后。三个市场的时间轴根本对不上。
这一章,我们就来解决这个核心问题。说白了,就是让不同市场的时钟「同步」起来。
1. 不同市场的「时间鸿沟」
先看看几个典型市场的交易时间差异:
| 市场 | 交易时间(北京时间) | 特殊说明 |
|---|---|---|
| A股 | 09:30-11:30, 13:00-15:00 | 中午休市1.5小时 |
| 港股 | 09:30-12:00, 13:00-16:00 | 中午休市1小时 |
| 美股 | 21:30-04:00(夏令时) | 无午休,有盘前盘后 |
| 期货(夜盘) | 21:00-23:00, 09:00-15:00 | 分日盘和夜盘 |
你看,光是交易时段就五花八门。更别说节假日了。A股春节休市一周,美股可不过春节。这时候你拿两个市场的数据直接合并,结果就是一堆空洞。
核心原则:跨市场分析必须基于「对齐后的时间戳」,而不是原始交易时间。
2. 缺失值处理:别让NaN毁了你的模型
数据对齐后,缺失值就冒出来了。我见过有人直接dropna(),把一大半数据扔了。这太粗暴了。
我个人习惯分三步走:
- 先判断缺失原因——是休市导致的?还是数据源本身丢了?
- 再选择填充策略——前向填充、插值、还是用其他市场数据辅助填充?
- 最后做标记——填充过的数据最好加个flag,方便后续回测时剔除。
举个例子,A股中午休市产生的缺失,用前向填充(ffill)最合理。因为价格在休市期间没变。
import pandas as pd
# 假设df是A股分钟数据,中午11:30-13:00缺失
df['close'].fillna(method='ffill', inplace=True)
# 这样就把中午的价格延续到下午开盘前
但如果是美股和A股之间的跨市场缺失呢?比如A股收盘后,美股还在交易。这时候用ffill就不对了。我建议用「最近可用数据」对齐。
小技巧:对于高频策略,缺失值超过连续3个时间点,我建议直接标记为「无效区间」,而不是强行填充。强行填充会引入未来信息,回测结果会虚高。
3. 重采样:把不同频率的数据「拧」到一起
不同市场的数据频率往往不一样。A股可能是1分钟线,美股可能是5分钟线,期货可能是Tick级数据。怎么对齐?
重采样就是干这个的。
我常用的重采样规则:
- 低频转高频:用前向填充或插值。比如日线转小时线,把当日数据复制到每个小时。
- 高频转低频:用聚合函数。比如分钟线转小时线,取收盘价、最高价、最低价、成交量总和。
- 跨市场对齐:统一到一个「基准时间轴」。我一般用UTC时间,避免时区转换的麻烦。
# 将A股分钟数据重采样为5分钟线
df_5min = df.resample('5T').agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'
})
# 注意:resample会包含非交易时段,需要过滤掉
df_5min = df_5min.between_time('09:30', '15:00')
我曾经踩过的坑:重采样时没过滤非交易时段,结果把收盘后的空数据也聚合进去了,导致凌晨出现「假价格」。后来我加了一步:先过滤交易时段,再重采样。顺序很重要。
4. 频率对齐实战:多市场时间轴统一
好了,我们来看一个完整的例子。假设我们要分析A股和港股的联动性,数据都是1分钟线,但交易时间不同。
我的做法是:
- 把两个市场的数据都转成UTC时间戳
- 创建一个「联合时间轴」,包含两个市场所有交易时段的并集
- 对每个市场的数据,按联合时间轴重采样
- 缺失值用前向填充(因为非交易时段价格不变)
import pandas as pd
# 假设a_df和hk_df已经加载,时间列名为'timestamp'
# 步骤1:创建联合时间轴
all_times = pd.concat([
a_df['timestamp'],
hk_df['timestamp']
]).drop_duplicates().sort_values()
# 步骤2:重采样到联合时间轴
a_aligned = a_df.set_index('timestamp').reindex(all_times).fillna(method='ffill')
hk_aligned = hk_df.set_index('timestamp').reindex(all_times).fillna(method='ffill')
# 步骤3:合并
merged = pd.concat({
'A': a_aligned['close'],
'HK': hk_aligned['close']
}, axis=1)
# 步骤4:去掉两个市场都休市的时间段
merged.dropna(how='all', inplace=True)
嗯,这里要注意:reindex 加上 ffill 的组合,是我最常用的对齐方式。它不会引入未来数据,逻辑上也说得通——非交易时段的价格就是上一笔成交价。
5. 知识体系总览
下面这张图,是我自己整理的数据清洗与对齐流程。每次做新项目,我都会先过一遍这个流程,避免遗漏。
6. 避坑指南:我踩过的那些坑
做数据清洗这么多年,有些坑真的是刻骨铭心。
- 坑一:时区没统一。 有一次我把A股和美股数据直接合并,结果发现相关性算出来是负的。查了半天,原来是A股用的北京时间,美股用的美东时间,差了12个小时。从那以后,我所有数据一律转UTC。
- 坑二:节假日没处理。 春节、感恩节、圣诞节...每个市场都有自己的假期。我写过一个函数,专门拉取各市场的交易日历,然后做差集。这样就不会把非共同交易日的数据强行对齐了。
- 坑三:重采样时用了错误的聚合函数。 比如成交量用mean(),这不对。成交量应该用sum()。价格用last(),而不是mean()。这些细节决定了数据的准确性。
我的经验法则:数据清洗花的时间,至少占整个项目的60%。别嫌麻烦。数据干净了,后面的分析就是水到渠成的事。
好了,这一章的内容就到这里。数据清洗与对齐,说白了就是让不同市场的「时钟」同步。方法不复杂,但细节决定成败。你想想看,如果数据本身都是错的,再牛的策略也是白搭。