4、时间序列对齐:不同数据源的时间戳对齐、非交易时间过滤、节假日处理
做债券量化,最头疼的事是什么?
我个人觉得,不是策略不赚钱,而是数据对不上。你想想看,交易所的行情是一个时间戳,外汇交易中心的报价是另一个时间戳,Wind 的数据又是一种格式。三个数据源摆在你面前,时间戳差个几毫秒,你的策略就可能发出错误信号。
这一节,我们就来啃这块硬骨头。
4.1 时间戳对齐:为什么这么难?
债券市场不像股票,没有统一的中央撮合引擎。银行间市场、交易所市场、场外市场,各有各的节奏。我刚开始做债券做市的时候,就踩过一个坑——把交易所的 tick 数据和银行间的报价直接拼在一起跑回测,结果收益率曲线画出来像锯齿。
后来才发现,两个市场的时间戳精度不一样。交易所精确到毫秒,银行间可能只精确到秒。对齐?硬对齐会出大问题。
核心原则:时间戳对齐不是简单地把时间格式统一,而是要保证「同一时刻」的数据在逻辑上可比。
具体怎么做?我一般分三步走:
- 统一时区——所有数据强制转成 UTC+8,别留什么本地时间。
- 统一精度——以最低精度为准。如果某个数据源只有秒级,那就全部降采样到秒级。
- 填充缺失——对齐后出现空值,用前向填充(ffill)或者插值处理。
这里给一段我常用的对齐代码,你们可以直接拿去用:
import pandas as pd
# 假设 df1 是交易所数据(毫秒级),df2 是银行间数据(秒级)
df1['timestamp'] = pd.to_datetime(df1['timestamp'], unit='ms')
df2['timestamp'] = pd.to_datetime(df2['timestamp'], unit='s')
# 统一降采样到秒级,取每秒最后一条记录
df1_second = df1.set_index('timestamp').resample('1S').last()
df2_second = df2.set_index('timestamp').resample('1S').last()
# 合并对齐
aligned = pd.merge(df1_second, df2_second, left_index=True, right_index=True, how='outer')
aligned = aligned.ffill().bfill() # 前后填充
小技巧:对齐之后,记得检查一下时间戳的连续性。我习惯画一个时间差分布图,如果出现超过 5 秒的间隔,那就要怀疑是不是数据丢了。
4.2 非交易时间过滤:别让垃圾数据污染模型
债券市场有固定的交易时段。银行间市场一般是 9:00-12:00 和 13:30-17:00。交易所债券市场也类似。但很多人做数据清洗时,直接把全天 24 小时的数据都扔进模型。
结果呢?模型学了一堆「非交易时间」的噪音。我见过一个团队,回测曲线漂亮得不行,实盘一跑就崩。后来查原因,发现他们把凌晨 3 点的垃圾行情当成了有效信号。
过滤非交易时间,我的做法很简单:
- 第一步:定义交易时段。用配置文件写死,别硬编码在代码里。
- 第二步:用
between_time()直接切分。 - 第三步:检查边界情况。比如 12:00:00 这条数据算上午还是下午?我一般把 12:00:00 归到上午,12:00:01 算下午。
# 定义交易时段
morning_start = '09:00:00'
morning_end = '12:00:00'
afternoon_start = '13:30:00'
afternoon_end = '17:00:00'
# 过滤
df_aligned = df_aligned.between_time(morning_start, morning_end) \
.append(df_aligned.between_time(afternoon_start, afternoon_end))
注意:有些债券品种有夜盘交易,比如国债期货。别一刀切把晚上 8 点的数据也过滤掉了。一定要先确认你做的品种有没有夜盘。
4.3 节假日处理:中国市场特有的坑
说到节假日,我就想起一件事。有一年国庆节前,我跑了一个策略,结果模型在 9 月 30 日下午突然发出大量交易信号。我一看,原来是模型把「节前效应」当成了常态。
中国的债券市场,节假日影响非常大。春节、国庆这种长假,前后几天的流动性、波动率都跟平时不一样。如果不做节假日处理,模型很容易过拟合。
我的处理方案是这样的:
| 场景 | 处理方法 | 备注 |
|---|---|---|
| 法定节假日 | 直接剔除 | 用中国外汇交易中心发布的节假日表 |
| 调休工作日 | 保留但标记 | 比如国庆调休的周六,流动性可能异常 |
| 节前/节后 1-2 天 | 添加特征标签 | 让模型自己学习节假日效应 |
代码实现也不复杂:
import pandas as pd
from pandas.tseries.holiday import AbstractHolidayCalendar, Holiday, nearest_workday
# 定义中国债券市场节假日(示例)
class ChinaBondHolidayCalendar(AbstractHolidayCalendar):
rules = [
Holiday('New Year', month=1, day=1, observance=nearest_workday),
Holiday('Spring Festival', month=1, day=1, offset=pd.DateOffset(days=7)), # 简化处理
Holiday('National Day', month=10, day=1, offset=pd.DateOffset(days=7)),
]
cal = ChinaBondHolidayCalendar()
holidays = cal.holidays(start='2024-01-01', end='2024-12-31')
# 过滤节假日
df_clean = df_aligned[~df_aligned.index.normalize().isin(holidays)]
避坑指南:我曾经直接用 Python 的 pandas.tseries.holiday 内置的美国节假日日历去处理中国数据,结果 Thanksgiving 那天模型跑出来一堆异常信号。嗯,从那以后我再也不敢偷懒了。一定要用中国市场的节假日表。
4.4 知识体系总览
说了这么多,我画了一张图,把时间序列对齐的核心逻辑串起来。你们可以对照着看:
这张图从左到右,就是整个对齐流程。数据源进来,先对齐时间戳,再过滤非交易时间,最后输出干净的数据。节假日处理其实贯穿在第二步和第三步之间,我习惯在过滤完非交易时间之后,再单独做一次节假日剔除。
好了,这一节的内容就到这里。时间序列对齐这件事,说白了就是「慢工出细活」。你前期花多少功夫在数据清洗上,后期模型就给你多少回报。别嫌麻烦,这是做量化交易的基本功。
公众号:蓝海资料掘金营,微信deep3321