第17章 机器学习在统计套利中的应用:聚类寻找配对、LSTM预测价差

说实话,传统统计套利走到今天,很多老方法已经不够用了。你想想看,几千只股票里手工找配对,那得看到猴年马月?而且价差预测用简单的均值回归模型,遇到市场结构变化就失灵。所以这一章,我打算聊聊怎么用机器学习来升级我们的基差交易工具箱。

我个人习惯把机器学习在统计套利里的应用分成两块:配对发现价差预测。前者解决「跟谁做交易」的问题,后者解决「什么时候进场出场」的问题。咱们一个一个来。

17.1 聚类算法:自动寻找交易配对

传统方法找配对,要么算相关系数,要么算协整检验。但这两招都有个毛病——你得先有个候选池。比如你盯着银行板块,那就只能在银行股里找。万一跨板块的配对更赚钱呢?

聚类算法可以打破这个限制。它不看行业标签,只看价格行为特征。说白了,就是把走势相似的股票自动归到一堆,然后我们在同一堆里找配对。

17.1.1 特征工程:怎么描述一只股票的价格行为?

聚类之前,得先把每只股票变成一组数字特征。我一般用这几类:

  • 收益率统计特征:过去60天的均值、标准差、偏度、峰度
  • 波动率特征:已实现波动率、波动率聚类程度(用GARCH残差)
  • 相关性特征:与大盘的相关性、与板块指数的相关性
  • 动量特征:过去5天、20天、60天的累计收益率

嗯,这里要注意:特征不是越多越好。我在项目中遇到过,加了太多噪音特征,聚类结果反而变差了。建议先用PCA降个维,保留解释方差95%以上的主成分。

我的经验: 特征标准化非常重要。不同量纲的特征直接丢进KMeans,结果会被波动率这种大数值特征主导。我习惯用StandardScaler先处理一遍。

17.1.2 聚类实战:KMeans + 层次聚类

我个人偏好先用KMeans做粗聚类,再用层次聚类做细分类。为什么?KMeans快,但需要指定K值;层次聚类慢,但不需要预设类别数。两者结合,效果不错。

from sklearn.cluster import KMeans
from scipy.cluster.hierarchy import linkage, fcluster
import numpy as np

# 假设 features 是 (n_stocks, n_features) 的矩阵
# 第一步:KMeans粗聚类
kmeans = KMeans(n_clusters=10, random_state=42)
coarse_labels = kmeans.fit_predict(features)

# 第二步:对每个粗聚类内部做层次聚类
final_labels = np.zeros_like(coarse_labels)
for cluster_id in range(10):
    mask = coarse_labels == cluster_id
    if np.sum(mask) < 5:  # 少于5只股票就不细分了
        final_labels[mask] = cluster_id * 100
        continue
    
    sub_features = features[mask]
    Z = linkage(sub_features, method='ward')
    sub_labels = fcluster(Z, t=3, criterion='maxclust')
    final_labels[mask] = cluster_id * 100 + sub_labels

# 最终,同一 final_labels 的股票就是潜在配对池

这段代码跑完之后,你会得到一堆标签。比如标签 203 表示第2个粗聚类下的第3个子类。同一标签下的股票,走势高度相似,可以进一步做协整检验来确认配对。

避坑指南: 我曾经直接用日收益率做聚类,结果发现很多股票因为某一天同时暴涨暴跌被聚到一起,但平时走势根本不相关。后来我改用「滚动窗口相关性矩阵」作为距离度量,效果好了很多。

17.2 LSTM预测价差:从回归到序列建模

找到配对之后,传统做法是假设价差均值回归,用布林带或Z-score触发交易。但市场不是永远均值回归的——趋势行情里,价差可能长期偏离。这时候就需要一个能捕捉时序依赖的模型。

LSTM(长短期记忆网络)天然适合干这个。它能记住过去一段时间的价差模式,预测未来几步的走势。说白了,就是让模型学会「价差现在这样走,接下来大概率会怎么走」。

17.2.1 数据准备:构造监督学习样本

LSTM需要把时间序列转成「特征-标签」对。假设我们预测未来5分钟的价差:

import numpy as np

def create_sequences(spread, lookback=60, horizon=5):
    X, y = [], []
    for i in range(lookback, len(spread) - horizon):
        X.append(spread[i-lookback:i])
        y.append(spread[i+horizon])
    return np.array(X), np.array(y)

# spread 是价差序列,lookback=60表示用过去60个时间步
# horizon=5表示预测未来第5步的价差
X, y = create_sequences(spread, lookback=60, horizon=5)

这里有个细节:我习惯把价差做差分后再输入LSTM。为什么?因为原始价差可能非平稳,差分后模型更容易学到变化模式。预测完再反差分回去就行。

17.2.2 模型搭建:一个轻量级LSTM

别一上来就搭几十层,过拟合会让你哭的。我一般用2-3层LSTM,每层32-64个单元,加个Dropout防过拟合。

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout

model = Sequential([
    LSTM(64, return_sequences=True, input_shape=(60, 1)),
    Dropout(0.2),
    LSTM(32, return_sequences=False),
    Dropout(0.2),
    Dense(16, activation='relu'),
    Dense(1)
])

model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=50, batch_size=32, validation_split=0.2)

训练完之后,模型会输出一个预测值。你可以把这个预测值跟当前价差比较:如果预测价差远高于当前价差,说明价差可能要涨,那就做多价差(买一腿卖另一腿)。反之亦然。

核心逻辑: LSTM预测的不是「涨还是跌」,而是「未来价差的具体数值」。这个数值跟当前价差的差值,就是你的预期收益。差值越大,信号越强。

17.2.3 实战中的坑与对策

我在实盘里用过LSTM预测价差,踩过几个坑,分享给你:

  • 过拟合严重:LSTM在训练集上表现完美,一到测试集就崩。对策是增加Dropout、减少层数、用早停法。
  • 预测滞后:LSTM预测的价差往往比实际慢半拍。我试过用双向LSTM(BiLSTM),滞后问题有所缓解。
  • 市场结构变化:模型在牛熊转换时完全失效。我的做法是定期重训练,比如每周用最新数据微调一次。
一个小技巧: 不要把LSTM的预测直接当交易信号。我习惯把预测值跟传统Z-score结合——当两者方向一致时再进场,胜率能提高不少。

17.3 知识体系总览

下面这张图是我自己画的,把本章的核心逻辑串起来了。你看一眼就能明白整个流程:

机器学习在统计套利中的应用流程 原始价格数据 特征提取与标准化 KMeans+层次聚类 协整检验确认配对 LSTM预测价差 聚类找到潜在配对池 协整检验确认统计关系 LSTM预测价差方向与幅度 结合传统信号生成交易指令

你看,整个流程其实就五步:数据→特征→聚类→检验→预测。每一步都有坑,但每一步也都有对应的解法。我个人觉得,机器学习在统计套利里最大的价值不是替代传统方法,而是辅助决策——帮你更快找到配对,更准预测价差。

最后说一句:别指望模型100%准确。我见过太多人把LSTM当印钞机,结果亏得底朝天。记住,任何模型都是工具,风控才是命根子。

公众号:蓝海资料掘金营,微信deep3321