4、重采样与对齐:Tick数据的时间魔法
做量化交易的朋友都知道,Tick数据是最原始的市场脉搏。但说实话,直接用Tick数据做策略,就像用显微镜看地图——细节太多反而看不清全局。今天我们就聊聊怎么把Tick数据重采样成1分钟、5分钟、日线,以及处理那些让人头疼的时间对齐问题。
4.1 为什么需要重采样?
我刚开始做高频策略时,总觉得数据越细越好。结果呢?回测跑得慢不说,还经常被微观噪声干扰。后来我明白了——不同策略需要不同粒度的数据。
- 高频策略:可能需要Tick级或1分钟数据
- 日内策略:5分钟、15分钟比较合适
- 中长线策略:日线甚至周线就够了
说白了,重采样就是给数据做「降噪」处理。把那些无意义的跳动过滤掉,保留真正的趋势信息。
4.2 Tick数据重采样为1分钟K线
先看一个最基础的操作。假设我们有一堆Tick数据,每条记录包含时间戳、价格、成交量。要合成1分钟K线,需要做这几件事:
import pandas as pd
import numpy as np
# 假设df是Tick数据,包含['timestamp', 'price', 'volume']
df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)
# 重采样为1分钟
minute_data = df.resample('1T').agg({
'price': 'ohlc', # Open, High, Low, Close
'volume': 'sum'
})
# 处理一下结构
minute_data.columns = ['open', 'high', 'low', 'close', 'volume']
minute_data.dropna(inplace=True)
嗯,这里有个坑要注意。我遇到过几次,Tick数据的时间戳可能不是严格对齐整分钟的。比如14:30:01.500这种。resample默认会按左闭右开的方式分组,14:30:01.500会被归到14:30这一组。但有些交易所的规则不一样,你得先确认清楚。
label='right' 才解决。
4.3 5分钟和日线重采样
5分钟其实就是在1分钟的基础上改个参数:
# 5分钟重采样
five_min_data = df.resample('5T').agg({
'price': 'ohlc',
'volume': 'sum'
})
日线稍微复杂一点。因为期货、股票的交易时间不一样,而且有夜盘。我一般这样处理:
# 日线重采样(注意:只取交易时段)
daily_data = df.between_time('09:30', '15:00').resample('1D').agg({
'price': 'ohlc',
'volume': 'sum'
})
# 如果有夜盘,需要单独处理
night_data = df.between_time('21:00', '23:00').resample('1D').agg({
'price': 'ohlc',
'volume': 'sum'
})
你想想看,如果不加between_time过滤,非交易时段的数据混进来,日线的开盘价和收盘价就全乱了。我见过有人直接把全天24小时的数据resample成日线,结果开盘价取到了凌晨3点的数据——那能对吗?
4.4 不同频率数据的时间对齐
这是实战中最头疼的问题。比如你要把1分钟数据和5分钟数据对齐,或者把股票数据和期货数据对齐。它们的时间戳可能不完全匹配。
我的做法是:
- 统一基准时间:选一个最细的粒度作为基准,比如1分钟
- 前向填充:用最近的有效值填充缺失的时间点
- 后向填充:某些场景下需要用未来的数据填充(注意未来信息泄露)
# 对齐1分钟和5分钟数据
minute_data = minute_data.resample('1T').ffill() # 前向填充
five_min_aligned = five_min_data.resample('1T').ffill()
# 合并
aligned_data = pd.concat([minute_data, five_min_aligned], axis=1)
aligned_data.dropna(inplace=True)
4.5 处理非交易时间段
非交易时间段包括:
- 午休时间:比如11:30-13:00
- 夜盘间隔:比如23:00-次日9:00
- 节假日:春节、国庆等
- 临时停牌:个股突发停牌
我的处理策略很简单:
# 定义交易时段
trading_sessions = [
('09:30', '11:30'),
('13:00', '15:00')
]
# 过滤非交易时段
def filter_trading_hours(df):
mask = pd.Series(False, index=df.index)
for start, end in trading_sessions:
mask |= df.index.indexer_between_time(start, end)
return df[mask]
# 节假日处理(需要自定义日历)
import pandas_market_calendars as mcal
cal = mcal.get_calendar('XSHG') # 上交所
schedule = cal.schedule(start_date='2024-01-01', end_date='2024-12-31')
df = df[df.index.normalize().isin(schedule.index)]
这里我特别想强调一点:不要简单地把非交易时段的数据删掉就完事了。有些策略需要知道隔夜跳空、开盘缺口这些信息。我一般会保留一个标记列,标明当前数据是来自哪个交易时段。
4.6 知识体系总览
下面这张图概括了本章的核心逻辑:
4.7 实战中的避坑指南
最后分享几个我实战中总结的经验:
1. 时间戳精度问题
不同交易所的时间戳精度不一样。上交所是毫秒级,深交所是微秒级,期货交易所可能是纳秒级。重采样前一定要统一精度,否则会出现「看起来对齐了,实际差了几毫秒」的情况。
2. 成交量复权
重采样时成交量直接求和没问题,但要注意除权除息日。我遇到过某股票10送10后,成交量突然暴增,其实是送股导致的,不是真实交易活跃。
3. 夜盘归属问题
夜盘的数据到底算当天的还是第二天的?不同品种规则不同。我一般把夜盘数据单独标记,策略里再决定怎么处理。
好了,重采样和对齐这块就聊这么多。记住一个原则:数据预处理做得越细致,策略回测就越可信。别嫌麻烦,这一步省下的时间,后面会加倍还给你。
公众号:蓝海资料掘金营,微信deep3321