时间特征构建:让时序数据开口说话
做量化这些年,我越来越觉得时间特征就像数据的「指纹」。每个时间点都带着独特的印记——是工作日还是周末?是财报季还是平淡期?这些信息往往比价格本身更有预测力。
今天我们就来聊聊怎么从时间维度上「榨取」特征。我个人习惯把时间特征分成三类:基础日期特征、日历效应、滚动窗口统计量。咱们一个一个说。
一、基础日期特征:年、月、日、周、小时
说白了,就是把时间戳拆解成人类能理解的维度。你想想看,股票在周一和周五的表现能一样吗?期货夜盘和白天的波动率能一样吗?
我常用的特征包括:
- 年份:捕捉长期趋势,比如牛熊转换
- 月份:季节性效应,比如「五穷六绝七翻身」
- 日:月初月末效应,发工资日、结算日
- 星期几:周内效应,周一往往波动大
- 小时:日内效应,开盘收盘时段最活跃
实战经验:我在做A股日内策略时发现,上午9:30-10:00和下午14:30-15:00这两个时段,波动率是其他时段的2-3倍。如果不把小时特征加进去,模型根本抓不住这个规律。
代码实现其实很简单,用pandas几行就搞定:
import pandas as pd
# 假设df已经包含datetime列
df['year'] = df['datetime'].dt.year
df['month'] = df['datetime'].dt.month
df['day'] = df['datetime'].dt.day
df['weekday'] = df['datetime'].dt.weekday # 0=周一
df['hour'] = df['datetime'].dt.hour
# 还可以做one-hot编码
weekday_dummies = pd.get_dummies(df['weekday'], prefix='weekday')
df = pd.concat([df, weekday_dummies], axis=1)
小技巧:对于星期特征,我建议用sin/cos编码而不是直接one-hot。比如周一和周日其实很接近(都是周末附近),但one-hot编码会认为它们完全不相关。sin/cos能保留这种「循环距离」信息。
二、日历效应:节假日与财报季
嗯,这里要注意。基础日期特征只能捕捉「周期性」,但日历效应捕捉的是「事件性」。比如春节前一周和春节后一周,市场表现天差地别。
我一般会构建以下几类特征:
- 节假日标记:是否在节假日前/后N天
- 财报季标记:是否在财报披露密集期
- 特殊日期:美联储议息日、非农数据发布日
举个例子,我曾经做过一个回测:
| 特征 | 节假日前后3天 | 普通交易日 |
|---|---|---|
| 平均收益率 | +0.32% | +0.08% |
| 波动率 | 1.8% | 1.2% |
| 胜率 | 58% | 52% |
你看,节假日效应确实存在。但要注意,不同市场的节假日影响不同。A股受春节影响大,美股受感恩节影响大。
避坑指南:我曾经犯过一个错误——直接把所有节假日同等对待。后来发现,春节和国庆节虽然都是长假,但市场情绪完全不同。春节前资金偏紧,国庆前资金偏松。所以一定要区分节假日类型,不能一刀切。
代码实现上,我习惯维护一个节假日字典:
# 自定义节假日列表
holidays = {
'春节': ['2024-02-10', '2024-02-11', '2024-02-12'],
'国庆': ['2024-10-01', '2024-10-02', '2024-10-03'],
# ...
}
# 构建节假日前后N天特征
def holiday_effect(df, holiday_dates, window=3):
df['is_holiday'] = df['date'].isin(holiday_dates)
df['days_to_holiday'] = (df['date'] - holiday_dates).abs().min(axis=1)
df['pre_holiday'] = df['days_to_holiday'].between(1, window)
df['post_holiday'] = df['days_to_holiday'].between(-window, -1)
return df
三、滚动窗口统计量:均值、标准差、极值
这部分是时间序列特征工程的核心。说白了,就是用过去一段时间的统计量来预测未来。我个人最常用的窗口是5天、20天、60天,分别对应周、月、季度。
常用的统计量包括:
- 滚动均值:趋势方向,比如价格在均线上方还是下方
- 滚动标准差:波动率,衡量风险
- 滚动最大值/最小值:支撑位和阻力位
- 滚动偏度/峰度:分布形态,捕捉极端行情
我的经验:滚动窗口的大小选择很关键。窗口太小容易过拟合,窗口太大又反应迟钝。我一般会同时计算多个窗口,然后用特征选择算法筛选。比如用L1正则化,自动保留最有用的窗口。
代码实现:
# 滚动窗口统计量
windows = [5, 20, 60]
for w in windows:
df[f'ma_{w}'] = df['close'].rolling(w).mean()
df[f'std_{w}'] = df['close'].rolling(w).std()
df[f'max_{w}'] = df['close'].rolling(w).max()
df[f'min_{w}'] = df['close'].rolling(w).min()
# 归一化处理:当前值相对于滚动均值的偏离程度
df[f'zscore_{w}'] = (df['close'] - df[f'ma_{w}']) / df[f'std_{w}']
进阶技巧:除了简单滚动,还可以试试指数加权移动平均(EWMA)。它对近期数据赋予更高权重,反应更快。我在做高频策略时特别喜欢用EWMA,因为它能更快捕捉到趋势变化。
知识体系总览
下面这张图是我自己梳理的时间特征构建框架,你可以参考一下:
最后说一句,时间特征构建没有标准答案。不同市场、不同频率、不同策略,需要的特征都不一样。我的建议是:先构建一个大的特征池,然后用模型去筛选。别怕特征多,就怕特征不够。
好了,这一章就到这里。记住,时间特征是你和市场的「共同语言」——你给模型喂什么样的时间特征,它就还你什么样的预测能力。