2. 金融时间序列基础
在进入 Pandas 的清洗与对齐实战之前,我们必须先建立对金融时间序列的准确认知。很多数据问题并非出在代码能力上,而是出在对“时间”这一维度本身的理解偏差:把交易日当成自然日、把不同频率的数据直接相减、把时间戳的时区忽略掉。本章将围绕时间序列的定义、金融数据的常见类型、时间戳、时间间隔、频率与周期六个核心概念展开,为后续的索引构建、重采样与对齐打下基础。
一、什么是金融时间序列
时间序列是按时间顺序排列的观测值集合。当观测对象是金融资产或金融指标时,就构成了金融时间序列。它的本质是“一个时间索引 + 一组数值列”的二维结构,其中时间索引具有唯一性、有序性和可比较性。与普通表格数据最大的区别在于:时间序列的相邻观测之间存在顺序依赖,缺失的时间点往往携带信息,而重复的时间点通常意味着数据错误。
在 Pandas 中,这一结构通常表现为 DatetimeIndex 作为行索引,配合价格、成交量等数值列。理解这一点,是后续所有清洗操作的前提。
二、金融数据的常见类型
金融数据种类繁多,但从时间序列处理的角度,可以按“时间粒度”和“更新规律”分为几大类。下表梳理了最常见的三类数据及其特征:
| 数据类型 | 典型字段 | 时间粒度 | 更新规律 | 常见问题 |
|---|---|---|---|---|
| 价格数据 | 开盘、最高、最低、收盘、复权价 | 分钟、日、周 | 交易日更新 | 停牌缺失、除权跳空 |
| 成交量数据 | 成交量、成交额、换手率 | 分钟、日 | 与价格同步 | 单位不统一、零值 |
| 财务指标 | 营收、净利润、ROE、EPS | 季度、年度 | 报告期披露 | 披露日与报告期错位 |
价格与成交量属于高频交易数据,通常每个交易日都有记录;财务指标属于低频基本面数据,其“时间”往往有两个含义:报告期(如 2023Q1)和披露日(如 2023-04-28)。在后续对齐时,若混用这两个时间,会导致严重的前视偏差。
三、时间戳、时间间隔与频率
时间戳(Timestamp)是时间轴上的一个具体点,例如 2024-03-15 09:30:00。在 Pandas 中对应 pd.Timestamp,它是不可变且可比较的。时间间隔(Timedelta)是两个时间戳之间的差值,例如 1 days 00:00:00,对应 pd.Timedelta。而频率(Frequency)描述的是时间序列的规则性,例如日频 D、交易日频 B、月频 M、季度频 Q。
这里有一个关键区分:时间间隔是“实际差值”,频率是“预期规则”。两个相邻交易日之间的实际间隔可能是 1 天(周二到周三),也可能是 3 天(周五到周一)。因此,金融日频数据不能简单用 D 表示,而应使用 B(工作日)或自定义的交易日历。
核心原理:频率决定了重采样和对齐的“目标网格”。如果频率设置错误,后续的 resample、asfreq、reindex 都会产生错误的时间点,进而污染整个分析结果。
四、周期与季节性:被忽视的维度
周期(Period)与频率不同,它表示一段时间区间而非一个时间点,例如“2024年3月”是一个月度周期。在 Pandas 中对应 Period 和 PeriodIndex。金融数据中,周期概念常用于财报分析:2023Q1 的营收属于一个季度周期,而不是某一天。
季节性则是周期在时间上的重复模式,例如“周一效应”“月末效应”“财报季效应”。识别季节性有助于判断数据中的异常值究竟是真实信号还是周期性波动。在清洗阶段,若发现某指标每月末固定出现尖峰,应先确认是否为季节性,而非直接当作异常值剔除。
五、案例场景:从原始数据到规范时间索引
假设我们拿到一份某股票的日线数据,字段包含日期、收盘价、成交量。原始日期是字符串格式,且包含周末和节假日。我们的目标是将它转换为以交易日为频率的规范时间序列。
import pandas as pd
raw = pd.DataFrame({
"date": ["2024-03-15", "2024-03-18", "2024-03-19", "2024-03-20"],
"close": [10.25, 10.40, 10.35, 10.55],
"volume": [1200000, 980000, 1500000, 1100000]
})
raw["date"] = pd.to_datetime(raw["date"])
ts = raw.set_index("date").sort_index()
print(ts.index)
# DatetimeIndex(['2024-03-15', '2024-03-18', '2024-03-19', '2024-03-20'],
# dtype='datetime64[ns]', name='date', freq=None)
print(ts.index.freq)
# None
可以看到,虽然数据是按交易日排列的,但 Pandas 无法自动推断出频率,因为 3 月 15 日到 3 月 18 日之间跨过了周末,实际间隔是 3 天。此时若强行设置 freq="D",会引入周末的空值;若设置 freq="B",则能正确表达“工作日”这一规则。更严谨的做法是使用交易日历进行 reindex,确保时间网格与真实市场一致。
实用技巧:对于 A 股数据,可使用 pd.date_range(start, end, freq="B") 生成工作日序列,再与数据对齐;若需精确到交易日,应引入交易所日历(如 exchange_calendars 库)生成真实交易日索引。
六、常见误区与复盘
- 误区一:把自然日频率当作交易日频率。用
D重采样日线数据,会在周末和节假日产生大量空值,后续填充会引入虚假信息。 - 误区二:忽略时间戳的时区。跨市场数据(如与 A 股)若未统一时区,对齐时会出现数小时的偏移,导致“未来数据”混入。
- 误区三:混淆报告期与披露日。财务指标若按报告期对齐,会提前使用尚未公布的数据,造成前视偏差。
- 误区四:认为时间间隔等于频率。周五到下周一间隔 3 天,但频率仍是“每个交易日一次”,二者不能混为一谈。
复盘要点:在清洗任何金融时间序列之前,先回答三个问题——时间索引是什么类型?频率是自然频率还是交易频率?时间戳是否带时区?这三个问题的答案,决定了后续所有对齐与重采样操作的正确性。
掌握以上基础概念后,我们才能在 Pandas 中正确地构建时间索引、识别频率、处理缺失与重复,并为后续的多源数据对齐做好准备。时间序列的清洗,本质上是对“时间”这一维度的尊重与校准。