4、时间序列对齐:不同数据源的时间戳对齐、非交易时间过滤、节假日处理

做债券量化,最头疼的事是什么?

我个人觉得,不是策略不赚钱,而是数据对不上。你想想看,交易所的行情是一个时间戳,外汇交易中心的报价是另一个时间戳,Wind 的数据又是一种格式。三个数据源摆在你面前,时间戳差个几毫秒,你的策略就可能发出错误信号。

这一节,我们就来啃这块硬骨头。

4.1 时间戳对齐:为什么这么难?

债券市场不像股票,没有统一的中央撮合引擎。银行间市场、交易所市场、场外市场,各有各的节奏。我刚开始做债券做市的时候,就踩过一个坑——把交易所的 tick 数据和银行间的报价直接拼在一起跑回测,结果收益率曲线画出来像锯齿。

后来才发现,两个市场的时间戳精度不一样。交易所精确到毫秒,银行间可能只精确到秒。对齐?硬对齐会出大问题。

核心原则:时间戳对齐不是简单地把时间格式统一,而是要保证「同一时刻」的数据在逻辑上可比。

具体怎么做?我一般分三步走:

  1. 统一时区——所有数据强制转成 UTC+8,别留什么本地时间。
  2. 统一精度——以最低精度为准。如果某个数据源只有秒级,那就全部降采样到秒级。
  3. 填充缺失——对齐后出现空值,用前向填充(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 知识体系总览

说了这么多,我画了一张图,把时间序列对齐的核心逻辑串起来。你们可以对照着看:

时间序列对齐核心流程 数据源 A(交易所) 数据源 B(银行间) 数据源 C(Wind) 步骤一:时间戳对齐 统一时区 → 统一精度 → 填充缺失 步骤二:非交易时间过滤 定义交易时段 → between_time 切分 → 检查边界 ✅ 干净、对齐的时间序列

这张图从左到右,就是整个对齐流程。数据源进来,先对齐时间戳,再过滤非交易时间,最后输出干净的数据。节假日处理其实贯穿在第二步和第三步之间,我习惯在过滤完非交易时间之后,再单独做一次节假日剔除。

好了,这一节的内容就到这里。时间序列对齐这件事,说白了就是「慢工出细活」。你前期花多少功夫在数据清洗上,后期模型就给你多少回报。别嫌麻烦,这是做量化交易的基本功。


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