第4章 数据预处理与特征工程:Tick级数据清洗、特征构建与标准化
做高频策略,圈里有个共识:数据决定上限,模型只是逼近这个上限。我见过太多人把精力全花在调参上,结果数据本身就有问题——那真是白费功夫。今天咱们就聊聊Tick级数据怎么洗、特征怎么造、标准化怎么做。
4.1 Tick级数据清洗:别让脏数据毁了你的策略
先说说Tick数据长什么样。交易所每笔成交都会推一条记录,包含时间、价格、成交量、买卖方向。但实际拿到的数据,嗯,问题不少。
4.1.1 常见脏数据类型
| 问题类型 | 典型表现 | 处理方式 |
|---|---|---|
| 时间戳错乱 | 后一笔时间早于前一笔 | 按时间排序后剔除异常 |
| 价格异常 | 价格超出当日涨跌停板 | 用前一笔价格替换或剔除 |
| 成交量异常 | 单笔成交量超过持仓量 | 标记为可疑,人工复核 |
| 重复数据 | 完全相同的记录出现多次 | 去重保留第一条 |
我个人习惯,拿到原始数据后先做三件事:排序、去重、范围检查。排序用时间戳,去重按时间+价格+成交量联合判断,范围检查就是看价格是否在合理区间内。
核心原则:宁可少一条数据,也不要一条脏数据。高频策略对噪声极其敏感,一条错误的价格就能让信号完全跑偏。
4.1.2 实战清洗代码
下面是我常用的清洗流程,用Python实现,效率还不错:
import pandas as pd
import numpy as np
def clean_tick_data(df):
"""
Tick数据清洗函数
df: 包含['time', 'price', 'volume', 'direction']的DataFrame
"""
# 1. 按时间排序
df = df.sort_values('time').reset_index(drop=True)
# 2. 去重
df = df.drop_duplicates(subset=['time', 'price', 'volume'])
# 3. 价格范围检查(假设是螺纹钢,价格在3000-5000之间)
price_low, price_high = 3000, 5000
df = df[(df['price'] >= price_low) & (df['price'] <= price_high)]
# 4. 成交量检查(单笔不超过1000手)
df = df[df['volume'] <= 1000]
# 5. 时间连续性检查(相邻时间差不超过5秒)
df['time_diff'] = df['time'].diff().dt.total_seconds()
df = df[df['time_diff'] <= 5.0]
return df.drop('time_diff', axis=1)
避坑指南:我曾经在清洗股指期货数据时,没注意夜盘和日盘的时间连续性,结果把夜盘最后一笔和日盘第一笔之间的时间差当成了异常,直接删掉了开盘数据。后来才发现,夜盘收盘到日盘开盘本来就有间隔。所以,一定要区分交易时段。
4.2 特征构建:从Tick数据中挖出有用信息
数据洗干净了,接下来就是造特征。高频策略的特征,说白了就是从微观结构里找规律。我重点讲三个:价差、订单簿斜率、成交量分布。
4.2.1 价差特征
价差是最基础的特征。但别小看它,不同市场、不同品种的价差模式差异很大。比如股指期货的价差通常很窄,而某些商品期货的价差可能波动剧烈。
常用的价差特征包括:
- 买卖价差:ask_price - bid_price,反映流动性
- 相对价差:(ask - bid) / mid_price,标准化后的价差
- 价差变化率:当前价差 / 前N笔平均价差 - 1,捕捉价差突变
代码实现很简单:
def compute_spread_features(df):
"""
计算价差相关特征
df: 包含bid_price, ask_price的DataFrame
"""
df['spread'] = df['ask_price'] - df['bid_price']
df['mid_price'] = (df['ask_price'] + df['bid_price']) / 2
df['rel_spread'] = df['spread'] / df['mid_price']
# 价差变化率(相对前10笔的均值)
df['spread_ma10'] = df['spread'].rolling(10).mean()
df['spread_change'] = df['spread'] / df['spread_ma10'] - 1
return df
4.2.2 订单簿斜率
订单簿斜率,说白了就是看买卖双方的挂单力量对比。斜率越大,说明某一方的挂单越集中,价格越容易朝那个方向移动。
计算方式:
- 取订单簿前N档的挂单量
- 对价格和挂单量做线性回归
- 回归系数就是斜率
我一般取前5档,因为太深的档位流动性差,参考意义不大。
def compute_orderbook_slope(df, levels=5):
"""
计算订单簿斜率
df: 包含bid_price_1..5, bid_vol_1..5, ask_price_1..5, ask_vol_1..5
"""
slopes = []
for i in range(len(df)):
# 买方数据
bid_prices = [df.loc[i, f'bid_price_{j}'] for j in range(1, levels+1)]
bid_vols = [df.loc[i, f'bid_vol_{j}'] for j in range(1, levels+1)]
# 卖方数据
ask_prices = [df.loc[i, f'ask_price_{j}'] for j in range(1, levels+1)]
ask_vols = [df.loc[i, f'ask_vol_{j}'] for j in range(1, levels+1)]
# 合并所有价格和挂单量
all_prices = bid_prices + ask_prices
all_vols = bid_vols + ask_vols
# 线性回归求斜率
slope = np.polyfit(all_prices, all_vols, 1)[0]
slopes.append(slope)
df['orderbook_slope'] = slopes
return df
经验之谈:订单簿斜率在开盘和收盘时段特别有用。我记得有一次做螺纹钢策略,开盘时斜率突然从正变负,我立刻意识到有大单在压盘,果断调整了仓位方向。那笔交易帮我躲过了当天的暴跌。
4.2.3 成交量分布
成交量分布,就是看成交量在价格上的分布情况。如果成交量集中在某个价格区间,说明那里是重要的支撑或阻力位。
我常用的方法:
- 成交量加权平均价(VWAP):累计成交额 / 累计成交量
- 成交量分布标准差:衡量成交量在价格上的离散程度
- 成交量集中度:前N笔成交量占总成交量的比例
def compute_volume_distribution(df, window=100):
"""
计算成交量分布特征
df: 包含price, volume的DataFrame
"""
# VWAP
df['cum_amount'] = (df['price'] * df['volume']).cumsum()
df['cum_volume'] = df['volume'].cumsum()
df['vwap'] = df['cum_amount'] / df['cum_volume']
# 滚动窗口内的成交量集中度
df['vol_concentration'] = df['volume'].rolling(window).apply(
lambda x: x.max() / x.sum()
)
return df
4.3 特征标准化:让模型学得更快
特征造好了,但不同特征的量纲差异很大。比如价差可能是0.5,而成交量可能是5000。如果不做标准化,模型会天然偏向数值大的特征。
常用的标准化方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score标准化 | (x - mean) / std | 数据近似正态分布 |
| Min-Max标准化 | (x - min) / (max - min) | 数据有明确边界 |
| Robust标准化 | (x - median) / IQR | 数据有较多异常值 |
我个人偏好Robust标准化,因为Tick数据里异常值太多了。用中位数和四分位距,能有效抵抗异常值的干扰。
from sklearn.preprocessing import RobustScaler
def robust_scale_features(df, feature_cols):
"""
对指定特征列做Robust标准化
"""
scaler = RobustScaler()
df[feature_cols] = scaler.fit_transform(df[feature_cols])
return df, scaler
注意:标准化参数一定要用训练集计算,然后应用到测试集。千万别在整个数据集上做标准化,否则会造成未来信息泄露。我见过有人犯这个错,回测曲线漂亮得不行,实盘一跑就崩。
4.4 本章知识体系
下面这张图,把数据预处理和特征工程的整个流程串起来了:
整个流程走下来,数据从原始状态变成了干净、有信息量、量纲统一的特征矩阵。这时候再扔给模型,效果会好很多。
最后说一句:特征工程没有标准答案。不同品种、不同市场,有效的特征组合可能完全不同。我的建议是:先做一批基础特征,然后通过回测和实盘验证,逐步迭代优化。别指望一次就能找到完美特征集。
公众号:蓝海资料掘金营,微信deep3321