时间特征构建:让时序数据开口说话

做量化这些年,我越来越觉得时间特征就像数据的「指纹」。每个时间点都带着独特的印记——是工作日还是周末?是财报季还是平淡期?这些信息往往比价格本身更有预测力。

今天我们就来聊聊怎么从时间维度上「榨取」特征。我个人习惯把时间特征分成三类:基础日期特征、日历效应、滚动窗口统计量。咱们一个一个说。

一、基础日期特征:年、月、日、周、小时

说白了,就是把时间戳拆解成人类能理解的维度。你想想看,股票在周一和周五的表现能一样吗?期货夜盘和白天的波动率能一样吗?

我常用的特征包括:

  • 年份:捕捉长期趋势,比如牛熊转换
  • 月份:季节性效应,比如「五穷六绝七翻身」
  • :月初月末效应,发工资日、结算日
  • 星期几:周内效应,周一往往波动大
  • 小时:日内效应,开盘收盘时段最活跃

实战经验:我在做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,因为它能更快捕捉到趋势变化。

知识体系总览

下面这张图是我自己梳理的时间特征构建框架,你可以参考一下:

时间特征构建知识体系 基础日期特征 年/月/日 星期/小时 日历效应 节假日 财报季 滚动窗口统计量 均值/标准差 最大/最小值 特征工程应用 模型输入特征矩阵 三个模块可以独立使用,也可以组合使用 建议先做基础日期特征,再叠加日历效应,最后加入滚动统计量

最后说一句,时间特征构建没有标准答案。不同市场、不同频率、不同策略,需要的特征都不一样。我的建议是:先构建一个大的特征池,然后用模型去筛选。别怕特征多,就怕特征不够。

好了,这一章就到这里。记住,时间特征是你和市场的「共同语言」——你给模型喂什么样的时间特征,它就还你什么样的预测能力。


专注资料整理