4. 时间序列基础:时间索引设置、重采样、滚动窗口、时间滞后操作

做汇率预测,说白了就是跟时间打交道。我刚开始接触这个领域时,以为拿到历史数据就能直接建模,结果跑出来的模型一塌糊涂。后来才明白——时间序列数据如果不先处理好索引、对齐好时间窗口,再牛的算法也白搭。

今天咱们就聊聊时间序列的四个基本功:时间索引设置、重采样、滚动窗口、时间滞后。这些操作在汇率预测里几乎天天用,你想想看,没有它们,你怎么把日频数据转成周频?怎么计算过去5天的移动平均?怎么构造滞后特征?

核心观点:时间序列特征工程的第一步,就是把时间这个维度变成可计算、可对齐、可滑动的结构。这一步做不好,后面全是坑。

4.1 时间索引设置:让数据认识时间

我见过太多人拿到CSV直接开干,结果pandas把日期列当成普通字符串。你算个rolling mean试试?报错。为什么?因为pandas不知道哪列是时间。

正确的做法是:把时间列设为索引。我个人习惯用 pd.to_datetime() 先转换格式,再用 set_index() 搞定。

import pandas as pd

# 假设你有一份欧元/美元日频数据
df = pd.read_csv('eur_usd_daily.csv')
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)

# 看一眼索引类型
print(df.index)
# 输出: DatetimeIndex(['2024-01-01', '2024-01-02', ...], dtype='datetime64[ns]', freq=None)

嗯,这里要注意:freq=None 表示pandas还没识别出数据频率。你最好手动指定一下,比如日频数据就设 df.index.freq = 'D'。我在项目中遇到过,如果不设freq,后面做shift操作时容易出警告,虽然不影响结果,但看着烦。

小技巧:如果你的数据是交易日历(非自然日),别设freq='D',因为周末没数据。建议用 pd.offsets.CustomBusinessDay() 自定义工作日历。

4.2 重采样:改变数据的时间粒度

汇率数据通常是日频,但有时候你需要周频、月频甚至小时频。重采样就是干这个的。

举个例子,你想把日频收盘价转成周频,取每周五的收盘价:

# 周频重采样,取每周最后一个交易日
weekly_close = df['close'].resample('W-FRI').last()

# 或者你想看每周平均汇率
weekly_mean = df['close'].resample('W').mean()

重采样的核心参数就两个:频率字符串聚合函数。频率字符串的规则我列个表,你收藏一下:

频率代码 含义 适用场景
D 自然日 连续数据
B 工作日 交易日数据
W 周(默认周日结束) 周频分析
W-FRI 周(周五结束) 外汇周频
M 月末 月频报告
Q 季末 季度分析

我曾经犯过一个错:用 resample('W') 处理外汇数据,结果发现每周的结束日是周日,而外汇市场周日不开盘。最后取到的数据全是NaN。后来改成 W-FRI 才解决问题。避坑指南:一定要确认你的市场日历

4.3 滚动窗口:滑动计算,捕捉局部趋势

滚动窗口是时间序列特征工程里最常用的操作之一。说白了,就是在一个固定大小的窗口里算统计量,然后窗口往前滑。

汇率预测里,滚动均值、滚动标准差、滚动最大值这些特征,能帮你捕捉短期趋势和波动率变化。

# 计算过去5天的移动平均线
df['ma_5'] = df['close'].rolling(window=5).mean()

# 计算过去20天的滚动标准差(波动率)
df['volatility_20'] = df['close'].rolling(window=20).std()

# 计算过去10天的滚动最大值(阻力位参考)
df['high_10'] = df['high'].rolling(window=10).max()

你想想看,为什么用滚动窗口?因为汇率市场有记忆效应——今天的价格受过去几天的影响。滚动窗口就是把这个「记忆」量化出来。

注意:滚动窗口会产生NaN值。比如window=5,前4行都是NaN。我个人习惯用 .dropna() 清理掉,或者用 min_periods 参数控制最少需要多少非空值。

还有一个进阶用法:滚动相关系数。比如你想看欧元和英镑的滚动相关性变化:

# 假设df包含eur_close和gbp_close两列
df['rolling_corr'] = df['eur_close'].rolling(60).corr(df['gbp_close'])

这个特征在汇率预测里很有用,因为货币对之间的相关性会随时间变化。我记得有一次做模型,加入滚动相关系数后,预测准确率提升了3%。

4.4 时间滞后:把过去的信息带到今天

滞后操作,就是让过去的数据「穿越」到现在。比如用昨天的收盘价预测今天的收盘价,这就是一个lag=1的特征。

在pandas里用 shift() 实现:

# 滞后1天
df['close_lag1'] = df['close'].shift(1)

# 滞后5天
df['close_lag5'] = df['close'].shift(5)

# 超前1天(未来数据,做标签用)
df['close_lead1'] = df['close'].shift(-1)

这里有个关键点:滞后特征不能泄露未来信息。你想想看,如果用当天的数据预测当天的涨跌,那叫作弊。所以做滞后时,一定要确保shift(1)之后,第t行的数据是第t-1天的。

实战建议:在汇率预测中,我通常构造3组滞后特征:短期(lag1-5)、中期(lag10-20)、长期(lag30-60)。然后通过特征选择算法筛选出最重要的几个。

我曾经踩过一个坑:做滞后特征时忘了排序。如果数据不是按时间升序排列,shift出来的值就全乱了。所以每次做shift之前,我都会先执行 df.sort_index(inplace=True)。嗯,这个习惯救过我很多次。

4.5 知识体系总览

下面这张图总结了时间序列基础操作的逻辑关系,你可以把它当作本章的思维导图:

时间序列基础操作知识体系 时间序列预处理 时间索引设置 重采样 resample 滚动窗口 rolling 时间滞后 shift pd.to_datetime() set_index() 指定freq频率 频率转换(D/W/M) 聚合函数(mean/sum) 日历对齐(W-FRI) 滚动均值/标准差 滚动最大值/最小值 滚动相关系数 shift(1) 滞后1天 shift(-1) 超前标签 多期滞后组合 核心目标:将时间维度转化为结构化特征

这四个操作不是孤立的。实际项目中,你经常需要组合使用。比如先重采样到周频,再计算滚动窗口,然后做滞后特征。我一般会写一个pipeline,把这几步串起来,避免手动操作出错。

个人经验:刚开始做特征工程时,别贪多。先把时间索引设对,然后加3-5个滚动窗口特征和3-5个滞后特征,跑个基线模型看看效果。等模型稳定了,再逐步增加复杂度。

好了,时间序列的基础操作就聊到这儿。这些工具虽然简单,但用好了能解决80%的时间特征问题。下一章咱们会把这些操作组合起来,构造真正能提升模型效果的汇率预测特征。

专注资料整理