4. Tick数据时间特征:时间戳解析、交易时段划分与隔夜日内特征
Tick数据的时间维度是特征工程中最基础、也最容易被忽视的环节。原始Tick流通常只携带交易所时间戳(Exchange Timestamp)或接收时间戳(Receive Timestamp),而量化策略真正需要的是结构化时间特征:交易时段归属、隔夜跳空、日内周期性模式等。本章将系统讲解如何从原始时间戳中提取高价值特征。
4.1 时间戳解析与标准化
不同交易所、不同数据供应商的时间戳格式差异巨大。下表列出常见格式及解析策略:
| 数据源 | 原始格式示例 | 解析方式 | 注意事项 |
|---|---|---|---|
| NASDAQ ITCH | 2024-03-15 09:30:00.123456 | datetime + 微秒 | 使用UTC,需转本地交易所时区 |
| CME MDP 3.0 | 1710484200123456 (微秒时间戳) | 整数微秒 → datetime | 纪元起点为1970-01-01 |
| Binance API | 1710484200123 (毫秒时间戳) | 整数毫秒 → datetime | UTC+0,需转目标时区 |
| CSV原始Tick | 2024-03-15 09:30:00.123 | pandas.to_datetime | 注意毫秒/微秒精度丢失 |
关键原则: 始终保留原始时间戳的完整精度(微秒级),并在特征工程中统一使用UTC+0内部存储,仅在展示或按交易时段分组时转换为交易所本地时间。
以下代码展示标准解析流程:
import pandas as pd
import numpy as np
# 示例:解析CME微秒时间戳
def parse_cme_timestamp(ts_micro: int) -> pd.Timestamp:
"""将CME微秒时间戳转换为UTC Timestamp"""
return pd.Timestamp(ts_micro, unit='us', tz='UTC')
# 示例:解析Binance毫秒时间戳
def parse_binance_timestamp(ts_ms: int) -> pd.Timestamp:
return pd.Timestamp(ts_ms, unit='ms', tz='UTC')
# 批量解析并添加本地时间列
df['trade_time_utc'] = df['timestamp_micro'].apply(parse_cme_timestamp)
df['trade_time_et'] = df['trade_time_utc'].dt.tz_convert('America/New_York')
4.2 交易时段划分
不同交易时段的市场微观结构存在显著差异。以美股为例,标准时段划分如下:
| 时段名称 | 时间范围(美东时间) | 市场特征 | 特征工程要点 |
|---|---|---|---|
| 盘前(Pre-market) | 04:00 – 09:30 | 低流动性、高波动、机构主导 | 标记为独立时段,不参与日内模式统计 |
| 开盘(Open) | 09:30 – 10:00 | 高成交量、价格发现、订单不平衡 | 单独计算开盘波动率、成交量斜率 |
| 盘中(Regular) | 10:00 – 15:30 | 流动性充足、价差稳定 | 作为基准时段,计算相对特征 |
| 收盘(Close) | 15:30 – 16:00 | 成交量放大、尾盘效应、机构调仓 | 计算收盘动量、成交量加速比 |
| 盘后(After-market) | 16:00 – 20:00 | 低流动性、事件驱动 | 与盘前合并处理或单独标记 |
实践建议: 对于期货市场(如CME),注意区分日盘(Globex白天)和夜盘(Globex夜间)时段。例如,原油期货的电子盘几乎24小时交易,但流动性高峰集中在欧美重叠时段。
时段划分的代码实现:
def assign_trading_session(timestamp_et: pd.Timestamp) -> str:
"""根据美东时间分配交易时段"""
time_only = timestamp_et.time()
if time_only < pd.Timestamp('04:00').time():
return 'closed'
elif time_only < pd.Timestamp('09:30').time():
return 'pre_market'
elif time_only < pd.Timestamp('10:00').time():
return 'open'
elif time_only < pd.Timestamp('15:30').time():
return 'regular'
elif time_only < pd.Timestamp('16:00').time():
return 'close'
elif time_only < pd.Timestamp('20:00').time():
return 'after_market'
else:
return 'closed'
df['session'] = df['trade_time_et'].apply(assign_trading_session)
4.3 隔夜与日内时间特征
隔夜特征和日内周期性特征是Tick数据时间特征工程的两大支柱。下面通过SVG图展示完整的特征体系:
4.4 隔夜特征工程详解
隔夜特征的核心在于捕捉非连续交易时段之间的信息断层。以下是三个核心隔夜特征的计算方法:
def compute_overnight_features(df_daily: pd.DataFrame) -> pd.DataFrame:
"""
输入:按天聚合的日频DataFrame,包含开盘价、收盘价、成交量
输出:隔夜特征DataFrame
"""
df = df_daily.copy()
# 1. 隔夜收益率(对数收益率)
df['overnight_return'] = np.log(df['open'] / df['close'].shift(1))
# 2. 隔夜波动率(开盘前15分钟最高最低价差 / 前日收盘价)
df['overnight_volatility'] = (df['pre_high'] - df['pre_low']) / df['close'].shift(1)
# 3. 隔夜成交量占比
df['overnight_volume_ratio'] = df['pre_volume'] / df['total_volume']
return df
避坑提示: 隔夜收益率计算时,务必使用前一日收盘价与当日开盘价,而非当日收盘价。部分数据源可能将盘前交易合并到前一日,导致隔夜特征失真。建议单独提取盘前数据段进行验证。
4.5 日内时间特征工程
日内特征的核心是将时间戳映射到交易时段内的相对位置,并捕捉周期性模式。常用方法包括:
- 时段哑变量(Session Dummies): 将每个Tick标记为开盘、盘中、收盘等时段,用于后续分组统计。
- 归一化时间位置(Normalized Time): 计算当前时间距离开盘的秒数,除以当日总交易秒数,得到[0,1]区间的位置特征。
- 正弦/余弦编码(Sin/Cos Encoding): 将时间映射到单位圆上,避免午夜/午夜的边界跳跃问题。
以下代码展示完整的日内特征生成:
def generate_intraday_features(df: pd.DataFrame, session_start: str = '09:30',
session_end: str = '16:00') -> pd.DataFrame:
"""
生成日内时间特征
"""
df = df.copy()
start_sec = pd.Timestamp(session_start).time().hour * 3600 + \
pd.Timestamp(session_start).time().minute * 60
end_sec = pd.Timestamp(session_end).time().hour * 3600 + \
pd.Timestamp(session_end).time().minute * 60
total_sec = end_sec - start_sec
# 当前时间距午夜秒数
df['seconds_from_midnight'] = df['trade_time_et'].dt.hour * 3600 + \
df['trade_time_et'].dt.minute * 60 + \
df['trade_time_et'].dt.second
# 归一化时间位置 [0, 1]
df['time_position'] = (df['seconds_from_midnight'] - start_sec) / total_sec
df.loc[df['time_position'] < 0, 'time_position'] = 0.0
df.loc[df['time_position'] > 1, 'time_position'] = 1.0
# 正弦/余弦编码
df['time_sin'] = np.sin(2 * np.pi * df['time_position'])
df['time_cos'] = np.cos(2 * np.pi * df['time_position'])
# 距开盘/收盘的秒数
df['secs_since_open'] = df['seconds_from_midnight'] - start_sec
df['secs_to_close'] = end_sec - df['seconds_from_midnight']
return df
4.6 时间特征与标签校准的联动
时间特征不仅用于模型输入,还直接影响标签校准的质量。例如:
- 隔夜跳空标签校准: 如果标签基于未来N个Tick的收益率,隔夜跳空会导致标签分布出现极端值。建议在隔夜跳空超过3倍标准差时,对该Tick的标签进行缩尾处理或单独标记。
- 时段自适应标签: 不同时段的波动率差异巨大。同一收益率绝对值在开盘时段可能是正常波动,在盘中时段则可能是异常信号。建议按时段分别计算标签的Z-score。
- 时间衰减权重: 在回测或训练时,根据Tick距离当前时间的远近赋予不同权重,避免过时数据主导模型。
最佳实践: 在构建Tick级特征集时,始终将时间特征作为第一组特征加入模型。许多量化团队发现,仅使用时间特征+价格归一化特征,就能在简单策略上获得显著提升。时间特征是其他所有特征的“锚点”。
4.7 本章小结
| 特征类别 | 核心特征 | 典型应用 |
|---|---|---|
| 时间戳解析 | UTC时间、本地时间、微秒精度 | 数据对齐、跨交易所合并 |
| 交易时段 | 盘前/开盘/盘中/收盘/盘后 | 时段自适应模型、流动性预测 |
| 隔夜特征 | 隔夜收益率、隔夜波动率、隔夜成交量比 | 跳空策略、开盘动量 |
| 日内特征 | 归一化时间、sin/cos编码、距开盘秒数 | 均值回复、日内模式识别 |
| 标签联动 | 时段Z-score、隔夜跳空缩尾、时间衰减权重 | 标签校准、样本加权 |