4、特征工程入门:基础统计特征与滚动窗口计算
说实话,很多做量化的人一上来就搞深度学习、LSTM,结果数据都没洗干净,特征也没做扎实。我个人的经验是——基础统计特征才是高频策略的基石。你想想看,一个简单的均值突破策略,如果连均值的计算方式都没选对,那后面再复杂的模型也是白搭。
这一章,我们就来聊聊那些最基础、但最常用的统计特征。我会结合自己在实盘中的踩坑经历,帮你把这些概念真正落地。
4.1 基础统计特征:均值、方差、偏度、峰度
先问个问题:为什么高频数据需要这些统计量?
因为价格序列不是白噪声。它有趋势、有波动、有尖峰厚尾。这些统计量就是用来描述这些特性的。
4.1.1 均值(Mean)
均值是最简单的特征,但也是最容易被误用的。我在做股指期货高频策略时,曾经直接用简单移动平均(SMA)作为信号,结果回测漂亮,实盘一跑就崩。为什么?因为高频数据里经常有跳空和异常值,一个极端价格就能把均值拉偏。
所以,我建议在高频场景下,优先考虑截尾均值或中位数。比如去掉最高最低5%的数据再算平均,这样更稳健。
import numpy as np
import pandas as pd
# 生成模拟高频数据
np.random.seed(42)
price = pd.Series(np.random.randn(1000) * 0.5 + 100)
# 普通均值
mean_std = price.mean()
# 截尾均值(去掉两端5%)
from scipy import stats
mean_trimmed = stats.trim_mean(price, proportiontocut=0.05)
print(f"普通均值: {mean_std:.4f}")
print(f"截尾均值: {mean_trimmed:.4f}")
4.1.2 方差与标准差(Variance & Std)
方差衡量的是价格波动的剧烈程度。在高频交易里,方差就是风险,也是机会。
我记得有一次做螺纹钢的夜盘策略,发现白天和晚上的方差差异巨大。白天波动小,晚上波动大。如果用一个固定的阈值去判断突破,白天容易频繁假突破,晚上又容易错过真行情。后来我改用滚动方差来动态调整阈值,效果好了不少。
# 滚动标准差计算
window = 20
rolling_std = price.rolling(window=window).std()
# 动态阈值:均值 ± 2倍滚动标准差
upper = price.rolling(window=window).mean() + 2 * rolling_std
lower = price.rolling(window=window).mean() - 2 * rolling_std
4.1.3 偏度(Skewness)
偏度描述的是数据分布的不对称性。正偏度意味着右尾更长,负偏度意味着左尾更长。
在期货市场里,偏度能告诉你什么呢?举个例子,如果某品种的收益率序列呈现明显的负偏度,说明它更容易出现大跌(左尾风险)。这时候你再做多,就得小心了。
我曾经在橡胶期货上吃过这个亏。当时只看均值和方差,觉得价格合理,结果一个负偏度事件直接把我止损打穿。后来我养成了一个习惯:每次建仓前,先看一眼过去100笔tick数据的偏度。
from scipy.stats import skew
# 计算偏度
skewness = skew(price)
print(f"偏度: {skewness:.4f}")
# 如果偏度绝对值大于0.5,说明分布明显不对称
if abs(skewness) > 0.5:
print("注意:分布存在明显偏斜!")
4.1.4 峰度(Kurtosis)
峰度衡量的是数据分布的尖峭程度。高峰度意味着数据有更多的极端值(厚尾),低峰度意味着分布更平坦。
金融数据几乎都是高峰度的。这就是所谓的「尖峰厚尾」特征。我见过很多新手用正态分布去建模,结果回测时觉得参数很漂亮,实盘一遇到极端行情就爆仓。说白了,就是没考虑峰度的影响。
from scipy.stats import kurtosis
# 计算峰度(默认是超额峰度,即正态分布为0)
kurt = kurtosis(price)
print(f"超额峰度: {kurt:.4f}")
# 如果峰度大于3,说明尾部风险较大
if kurt > 3:
print("警告:尾部风险较高,建议使用稳健统计方法!")
4.2 滚动窗口计算
刚才我们一直在说「滚动窗口」,这其实是高频特征工程里最核心的操作之一。为什么?因为市场是动态的,昨天的数据对今天的意义不大,我们需要一个滑动的、局部的视角。
4.2.1 滚动窗口的基本用法
Pandas的rolling()函数是主力工具。但要注意,窗口大小的选择非常关键。窗口太小,噪声大;窗口太大,反应迟钝。
我个人习惯的做法是:先根据品种的微观结构来定。比如股指期货的tick数据,我一般用20笔(约1秒)作为最小窗口;如果是1分钟线,窗口就设在5-20之间。
# 滚动窗口计算多个统计量
window = 20
features = pd.DataFrame({
'price': price,
'rolling_mean': price.rolling(window).mean(),
'rolling_std': price.rolling(window).std(),
'rolling_skew': price.rolling(window).skew(),
'rolling_kurt': price.rolling(window).kurt()
})
# 前window-1个值为NaN,需要处理
features = features.dropna()
min_periods参数来设置最小有效数据量。
4.2.2 滚动窗口的实战技巧
这里分享一个我踩过的坑。有一次我在做铁矿石的跨期套利,用了滚动窗口计算价差的均值和标准差。结果发现,每次换月的时候,价差会出现巨大跳变,导致滚动窗口里的统计量全部失效。
后来我加了一个换月检测逻辑:在换月前后,重置滚动窗口,或者使用更长的窗口来平滑过渡。嗯,这个细节虽然小,但直接影响策略的稳定性。
# 处理换月时的滚动窗口重置
def rolling_with_reset(series, window, reset_indices):
"""
reset_indices: 需要重置窗口的位置列表(如换月时间点)
"""
result = series.rolling(window).mean()
for idx in reset_indices:
# 在重置点之后,重新开始计算
result.iloc[idx:idx+window] = np.nan
return result
4.3 时间衰减权重
滚动窗口有个问题:窗口内的所有数据权重相同。但现实中,越近的数据越重要。比如5秒前的价格比5分钟前的价格更有参考价值。
这就是时间衰减权重的意义。
4.3.1 指数加权移动平均(EWMA)
EWMA是最常用的时间衰减方法。它给近期的数据更高的权重,远期的数据权重指数级下降。
我记得在2018年做原油期货时,用SMA做趋势跟踪总是滞后,换成EWMA后信号提前了约3-5个tick。别小看这几个tick,在高频交易里,这就是盈亏的分水岭。
# 指数加权移动平均
span = 20 # 相当于窗口大小
ewma = price.ewm(span=span, adjust=False).mean()
# 指数加权标准差
ewm_std = price.ewm(span=span, adjust=False).std()
# 对比SMA和EWMA
sma = price.rolling(window=span).mean()
print(f"SMA最后值: {sma.iloc[-1]:.4f}")
print(f"EWMA最后值: {ewma.iloc[-1]:.4f}")
span参数相当于窗口大小,alpha=2/(span+1)是衰减因子。我一般用span=20或span=50,具体取决于品种的微观结构。如果你想要更快的衰减,可以减小span。
4.3.2 自定义时间衰减函数
有时候EWMA的指数衰减不一定适合所有场景。比如在某些高频策略里,你可能希望前10笔数据权重快速下降,之后保持一个平稳的衰减速度。这时候就需要自定义权重函数。
# 自定义线性衰减权重
def linear_decay_weights(window):
"""线性衰减:越近权重越大"""
weights = np.arange(1, window+1, dtype=float)
return weights / weights.sum()
# 自定义指数衰减权重
def exp_decay_weights(window, alpha=0.1):
"""指数衰减"""
weights = np.exp(-alpha * np.arange(window))
return weights / weights.sum()
# 应用自定义权重
window = 20
weights = linear_decay_weights(window)
weighted_mean = price.rolling(window).apply(
lambda x: np.dot(x, weights), raw=True
)
4.4 本章知识体系
说了这么多,我们来梳理一下。下面这张图展示了本章的核心逻辑:
4.5 实战建议与避坑指南
最后,我总结几条实战中的经验,希望能帮你少走弯路:
- 不要迷信单一统计量。均值、方差、偏度、峰度要结合起来看。比如均值上升但方差也在扩大,那这个上升可能不可靠。
- 滚动窗口的大小要动态调整。市场波动大的时候,窗口可以小一点;波动小的时候,窗口可以大一点。我一般用自适应窗口,根据ATR(平均真实波幅)来调整。
- 时间衰减权重不是万能的。EWMA对突变反应快,但也容易受噪声影响。如果你发现信号太频繁,可以试试双指数平滑(Holt-Winters)。
- 回测和实盘要统一。我曾经在回测里用了未来数据(不小心用了全量数据的均值),结果实盘亏得很惨。记住:滚动窗口计算时,只能用历史数据。
一句话总结: 基础统计特征就像盖房子的地基。地基不牢,再漂亮的策略也是空中楼阁。把均值、方差、偏度、峰度吃透,把滚动窗口和时间衰减用好,你的高频特征工程就成功了一半。
公众号:蓝海资料掘金营,微信deep3321