3、时间序列数据处理:时间索引、重采样、滑动窗口、缺失值处理、金融数据可视化基础
做金融回测,说白了就是在跟时间打交道。
我刚开始接触这块时,以为把K线数据塞进去就能跑策略了。结果呢?数据对不上、时间戳乱跳、缺失值搞得回测结果一塌糊涂。嗯,今天咱们就把这些坑一个个填上。
3.1 时间索引:数据的“身份证”
金融数据里,时间就是索引。没有时间索引的数据,就像没有标签的零件——你根本不知道它该装在哪。
我个人习惯用 pandas 的 DatetimeIndex。它比普通索引快得多,而且支持各种时间切片操作。
import pandas as pd
# 假设你有一份CSV,里面是股票日线数据
df = pd.read_csv('stock_data.csv')
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
# 现在你可以这样切数据
df_2023 = df['2023'] # 整年数据
df_jan = df['2023-01'] # 1月数据
df_range = df['2023-01-01':'2023-01-31'] # 指定区间
我的小技巧: 数据量大的时候,用
pd.to_datetime() 时加上 format 参数,能提速3-5倍。比如 format='%Y%m%d'。
3.2 重采样:把数据“揉”成你想要的粒度
你拿到的可能是分钟级数据,但策略只需要日线。怎么办?重采样。
我在项目中遇到过这样的情况:高频交易数据动辄几百万行,直接跑策略内存直接爆掉。重采样后,数据量降到几千行,速度飞起。
# 分钟数据 -> 日线数据
df_daily = df_minute.resample('D').agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum'
})
# 常用的重采样规则
# 'D' - 日, 'W' - 周, 'M' - 月, 'Q' - 季度, 'Y' - 年
# '5min' - 5分钟, 'H' - 小时
注意: 重采样时,
open 取第一个值,close 取最后一个值。别搞反了,否则回测结果会完全失真。
3.3 滑动窗口:让策略“活”起来
回测的核心是什么?是模拟真实交易。真实交易中,你只能看到过去的数据,不能看到未来。滑动窗口就是干这个的。
我曾经犯过一个低级错误:用全量数据计算均线,然后回测。结果策略表现好得离谱——因为它“偷看”了未来的数据。嗯,这就是传说中的未来函数。
# 计算20日均线
df['ma20'] = df['close'].rolling(window=20).mean()
# 计算布林带
df['std20'] = df['close'].rolling(window=20).std()
df['upper'] = df['ma20'] + 2 * df['std20']
df['lower'] = df['ma20'] - 2 * df['std20']
# 滚动窗口的其他用法
df['max_high'] = df['high'].rolling(10).max() # 10日最高价
df['min_low'] = df['low'].rolling(10).min() # 10日最低价
核心原则: 滑动窗口的
window 参数决定了你看多远的“历史”。窗口越大,信号越平滑,但反应越迟钝。
3.4 缺失值处理:别让“坑”毁了你的策略
金融数据里,缺失值太常见了。停牌、节假日、数据源问题……你想想看,如果不去处理,回测时一个 NaN 就能让整个策略崩掉。
我建议的处理顺序是这样的:
- 先检查:
df.isnull().sum()看看哪些列有缺失 - 再判断:缺失比例高不高?高的话考虑换数据源
- 最后处理:根据场景选方法
# 常见处理方法
# 1. 向前填充(推荐用于金融数据)
df.fillna(method='ffill', inplace=True)
# 2. 向后填充
df.fillna(method='bfill', inplace=True)
# 3. 插值法
df.interpolate(method='linear', inplace=True)
# 4. 直接删除(慎用)
df.dropna(inplace=True)
避坑指南: 我曾经用
dropna() 删除了所有缺失行,结果回测时发现数据量少了30%。后来一查,原来是节假日停牌导致的。从那以后,我改用 ffill 填充,数据完整多了。
3.5 金融数据可视化基础:一眼看出问题
数据好不好,看一眼图就知道了。我习惯在回测前先画图,看看数据有没有异常跳变、缺失区间、或者明显的错误。
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 画K线图(简化版)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
# 价格走势
ax1.plot(df.index, df['close'], label='收盘价', color='blue')
ax1.set_ylabel('价格')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 成交量
ax2.bar(df.index, df['volume'], color='gray', alpha=0.5)
ax2.set_ylabel('成交量')
ax2.legend()
ax2.grid(True, alpha=0.3)
# 格式化x轴
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.tight_layout()
plt.show()
我的习惯: 画图时加上
alpha 参数(透明度),数据重叠时能看清细节。另外,成交量用柱状图,价格用折线图,这是金融圈的“潜规则”。
3.6 本章知识体系
下面这张图,是我自己总结的时间序列处理流程。你照着这个顺序走,基本不会出大问题。
这张图里,我特意把“滑动窗口”和“可视化”放在了重采样和缺失值处理之后。为什么?因为数据必须先“干净”,才能做计算和展示。顺序搞反了,后面全是白费功夫。
一句话总结: 时间索引是骨架,重采样是整形,滑动窗口是计算,缺失值处理是清创,可视化是体检。五步走完,你的数据就能上“回测手术台”了。
公众号:蓝海数据掘金营,微信deep3321