股票量化交易实战:从数据清洗到回测防坑的深度学习全流程
简介:在深度学习驱动的量化交易中,模型架构并非决胜关键,真正决定策略鲁棒性与实盘收益的,是数据处理流程、特征构造逻辑与回测系统的严谨程度。以LSTM为代表的时序模型只是特征提取器,其输入张量承载的信息质量直接决定了预测上限。围绕股票预测这一典型场景,技术要点涵盖环境配置(CUDA/conda)、数据清洗(复权、去极值)、时间序列切分防止未来函数,以及特征工程中的横截面信息融合。回测环节更需警惕滑点、手续费与过拟合陷阱,通过事件驱动模拟和参数敏感性分析验证策略泛化能力。本文从通用概念与工程实践切入,系统梳理从原始行情到可交易信号的全链路方法,帮助读者避开数据泄漏与自欺欺人的回测曲线,构建真正可信的量化实验闭环。 先说明一个比较反直觉的事实:你下载到的“基于深度学习的股票量化交易.zip”这类压缩包,真正值钱的往往不是里面的模型代码,甚至不是训练好的权重文件。模型部分在网上开源的一大把,甚至GitHub上随便一搜就有几百个star过万的LSTM预测股价项目。真正决定这套东西能不能用、能不能赚到钱的,是那个zip包里最容易被你忽略的数据处理流程、特征构造逻辑和回测系统怎么写的。
我自己前后折腾了将近三年,从最开始的“把股票价格丢进LSTM就等着收钱”,到后来逐步理解这个领域到底难在哪。如果你手头正好有一个类似的zip包,或者正准备自己搭一套,这篇文章会按我理解的最优路径,把整个环节拆开讲清楚。包括环境怎么配、数据怎么处理、模型怎么选、回测怎么避开自欺欺人的大坑,以及那些工程细节里最容易被忽略的隐形地雷。
1. 解压之后先别急着跑——项目包的正确打开方式
拿到zip包,第一反应肯定是解压、装依赖、python train.py。但我建议你先做一件事:把整个项目的目录结构完整看一遍,搞清楚每个文件夹是干嘛的。一个结构合格的项目包,至少应该有五大部分,缺了哪块你都得警觉。
1.1 一个靠谱项目包的目录长什么样
我见过太多所谓“量化项目”压缩包,里面的代码乱七八糟,甚至有的连requirements.txt都没有,data目录下直接躺着几十个CSV。真正可复现的项目,目录结构应该是分层的:
data/:原始行情数据的存放目录。注意,原始数据应该和缓存数据、中间处理结果分开,避免每次跑要重新下载完整历史数据。features/或processor/:特征工程,原始数据到这个环节被转换成模型能吃的数值张量。models/:模型定义,不管是PyTorch还是TensorFlow,模型的网络结构、损失函数、训练逻辑都应该在这里。backtest/:回测引擎,这一块尤其重要。你后面会看到,回测代码写得好不好,直接决定你看到的“漂亮曲线”到底是真实能力还是海市蜃楼。configs/:配置文件,所有超参数、数据路径、训练轮数、交易手续费率都放在这里,而不是写死在代码里。
如果zip包里有类似结构,那恭喜你,这个项目至少是认真搭的。如果打开发现只有一个train.py和一个predict.py,所有参数都写死在代码里,那你得格外小心,后面我讲到的数据泄漏和过拟合问题大概率都存在。
1.2 按什么顺序读代码
我推荐是“倒着读”。先读backtest里的代码,再读features,最后才看模型定义。为什么?因为回测代码决定了你“怎么判断模型有没有用”,如果你连评价标准都没搞明白,那训练多少epoch都是白搭。
反过来讲,很多人一上来就盯着模型结构看,看到LSTM层、注意力机制就觉得高大上,结果整个项目里最水的往往就是模型部分——因为深度学习模型本身只是特征提取器,它处理的是你已经喂给它的张量。真正决定预测效果上限的,是张量里面承载的信息质量。
一句话总结:项目包里各个模块的价值排序是,数据处理和特征工程 > 回测引擎 > 模型结构 > 各种花哨的优化技巧。
2. 环境搭建——GPU驱动、CUDA与conda环境的组合拳
如果说数据处理是量化系统的灵魂,那环境配置就是肉身。很多新手卡在第一步就放弃了,因为深度学习环境确实麻烦。我先说说我踩过的坑,然后直接给你一条最稳的路线。
2.1 三种路线怎么选
根据自己的硬件条件,环境配置有三条路线。
路线A:本机有NVIDIA GPU(显存>=6G)
这是最舒服的路线,也是项目包里文档最常见的前提条件。完整的配置链条是:NVIDIA显卡驱动 -> CUDA Toolkit -> cuDNN -> Python环境 -> PyTorch。注意,显卡驱动是驱动,CUDA是CUDA,它们俩是分开装的。很多人以为装了驱动就等于有CUDA了,完全不是一回事。
驱动装完你用nvidia-smi命令能看到显卡信息,这个命令本身会显示一个CUDA Version,那是“驱动支持的CUDA最高版本”。比如说驱动版本是545.xx,显示CUDA Version: 12.3,意思是你可以装12.3及以下任何版本的CUDA Toolkit,但装13.x肯定不行。
路线B:没有NVIDIA GPU,用CPU硬扛
CPU跑深度学习不是不行,但小马拉大车的体验很差。如果只是做日线级别数据的股票预测,其实CPU跑LSTM完全够用——因为股票日线数据量并不大,沪深300全部成分股一天也就300行数据,哪怕攒10年也就75万行,这个规模CPU训练完全能扛得住。但如果你要做分钟级别的tick数据,那CPU就别想了。
路线C:租云服务器
现在很多云平台提供GPU租用服务,按小时计费那种。适合没有好显卡、但想正经跑深度学习实验的人。你在本地把数据处理和特征工程的代码写好,模型训练扔到云上。注意区分“CPU实例”和“GPU实例”,很多云平台默认给的是CPU实例,4块钱一小时那种,你跑个大模型会发现慢到怀疑人生。
2.2 用conda管理环境的理由
我强烈建议用conda而不是直接pip装。不是说要上某种“标准”,而是conda在解决依赖冲突方面确实省心。深度学习环境最烦的坑就是Python版本和CUDA版本不匹配,比如PyTorch 2.1要求Python版本在3.8-3.11之间,如果系统里面已经是Python 3.12,pip装就会报错或者编译失败。
conda可以创建独立环境,比如:
conda create -n quant python=3.10
conda activate quant
然后在环境里面装PyTorch。这样即使搞坏了也能无损删除,重新建一个。用pip硬装要是环境坏了,就是系统级的大清理,太伤。
2.3 PyTorch还是TensorFlow
现在的趋势很明显,PyTorch已经成了学术和量化圈的事实标准。一个直接原因:动态图机制对debug友好得多,print就能看到张量形状和数值;另一个原因是生态,QuantLib、FinRL这些量化库对PyTorch的支持都比TensorFlow好。如果是新项目,默认PyTorch不要犹豫。
安装命令建议去PyTorch官网用configure工具选好系统版本和CUDA版本,复制生成命令。不要图省事直接pip install torch,那个默认装的是CPU版本。
2.4 环境验证命令
装完之后别急着跑项目,先把环境验证通了再说:
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"
如果打印出True,说明GPU版本装好了。如果打印出False,先别怪命,检查两件事:第一,驱动是不是装好(nvidia-smi看得到显卡);第二,装的PyTorch是不是GPU版(pip list查torch的版本号后面有没有+cu字样)。
这一步很关键,因为很多项目包的第一个报错就是 Torch not compiled with CUDA enabled ,然后新手就卡在这里来回重装系统。
3. 数据准备——模型吃的是张量,不是K线图
环境配好之后,最难、最决定成败的一环来了:数据处理。我可以负责任地告诉你,80%的“机器学习策略回测很漂亮、实盘就完蛋”的情况,问题根源都在数据环节,而不是模型。
3.1 数据源选型
做A股日线级别策略,数据源常见的有这么几个选择:
- 免费数据源:akshare、tushare(部分接口免费),适合快速验证想法。
- 商业数据源:Wind、Choice、聚宽、米筐,数据质量好,但都收费。
- 第三方抓取:自己写爬虫从各大财经网站拉数据,不推荐,反爬机制会让你痛不欲生,而且数据质量没保证。
需要注意的一点是:不同的数据源对“复权价”的处理可能不同。前复权、后复权、不复权,这三种价格算出来的收益率序列在长周期上差异巨大。做策略时一般用后复权价,因为它能真实反映“如果你一直持有,你的资产变化情况”。前复权会改变历史价格,导致历史回测结果和当时实盘看到的价格不一致,容易产生幻觉。
3.2 数据清洗的关键步骤
拿到原始数据,别急着算特征。先做清洗,有四个关键步骤:
去重与去极值 :同一个交易日的K线可能出现多行(数据源拼接错误),按时间排序后去除重复行。对于涨跌幅超过阈值(比如A股无涨跌停限制前的老股票,或者科创板创业板20%涨跌幅)的记录,要做异常值处理。注意这里的阈值要按板块设置,主板10%,创业板和科创板20%。
缺失值填充 :停牌股票那天没有交易数据,怎么办?常见方案是:前向填充(用前一天的价格填上),或者直接剔除该股票当天的样本。我个人倾向于剔除——因为停牌期间的价格信息没有实际交易意义,填充只会制造“连续成交”的假象。
复权处理 :用后复权价格作为原始价格序列。这里有个细节:后复权价格数值很大(茅台可能显示几千上万),但这不影响后续收益率的计算,因为我们算收益率用的是diff/log return,是相对变化量。
对齐交易日期 :不同股票的交易日期集合不完全一致。把日期对齐到公共交易日历(比如中证指数公司的交易日历),确保矩阵里每一行对应的是同一个日期。
3.3 训练集、验证集、测试集切分——防止未来数据泄漏的生死线
这是整个量化项目里最重要、但最容易被忽视的一步。我见过很多失败的案例,训练集和测试集的切分是随机抽样——随机抽样意味着模型在训练时已经看过未来的数据,这样的回测曲线当然漂亮,因为那是“开卷考试”。
正确的做法是严格按照时间顺序切分:
# 假设你的整个数据是 dates_sorted 按日期升序排列
total_len = len(dates_sorted)
train_end = int(total_len * 0.7)
valid_end = int(total_len * 0.85)
# 前70%做训练,70%-85%做验证,最后15%做测试
train_data = df.iloc[:train_end]
valid_data = df.iloc[train_end:valid_end]
test_data = df.iloc[valid_end:]
这段代码看起来简单,但它的意义是:训练集是2020年以前的数据,模型只能用2020年前的数据学规律;验证集是2020-2021年的数据,用来调整超参数和早停;测试集是最后一段(比如2021年以后),只在最终评估时用一次,用完就再也不碰。
很多人容易犯的一个隐蔽错误是:在处理数据时,用了全样本的均值和标准差做标准化,然后才切分训练测试集。这会导致测试集的分布信息提前泄漏到训练过程中。正确的做法是先切分,再在训练集上计算均值和标准差,然后把同样的参数应用到验证集和测试集。
3.4 样本与特征的组织方式
对于深度学习时序模型,数据需要组织成形如 (样本数, 时间步长, 特征维度) 的三维张量。比如你用过去60个交易日的数据预测未来5天的走势,那每个样本就是 (60, 特征数) 的一个矩阵。滑动窗口切片的代码大概长这样:
def create_sequences(data, input_width=60, label_width=5):
X, y = [], []
for i in range(len(data) - input_width - label_width + 1):
X.append(data.iloc[i:i+input_width].values)
# 标签是未来5日的收益,这里用未来第5天价格与当前价格的对数差
future_price = data['close'].iloc[i + input_width + label_width - 1]
current_price = data['close'].iloc[i + input_width - 1]
y.append(np.log(future_price / current_price))
return np.array(X), np.array(y)
注意,这里的标签用的是“未来第5天相对当前的价格变化”,而不是未来5天每天收益的平均值。前者衡量的是“如果你在第0天买入、第5天卖出,你会赚多少”,更接近交易的真实语境。
4. 特征工程与标签构造——模型一半的好坏,在你喂进张量之前就决定了
在金融时序预测里,什么样的特征才有意义?这个问题比“用什么模型”更值得花时间。我把它拆成三个问题:标签怎么定、特征怎么算、以及为什么要避免“未来函数”。
4.1 标签设计——预测目标到底定成什么
常见的预测目标有几种:
- 回归目标 :预测未来N日的绝对收益率或对数收益率。这是最直接的,但训练的损失函数(MSE)会天然放大异常样本的影响,比如某只股票突然连续涨停,那一波的loss会主导整个训练过程,让模型变得只关注大涨大跌,忽略正常状态。
- 分类目标 :按未来N日收益率的某分位数,把样本分成上涨、下跌、横盘三类,或者分成五档(月度调仓的多因子模型常用五档)。分类的优点是鲁棒性强,不要求精确预测涨多少,只要求判断方向;缺点是丢失了幅度信息。
- 排序目标 :在横截面上排序,把“未来收益最高的一组”和“最低的一组”作为正负样本。这个思路和多因子选股里的IC值逻辑一致。排序目标更适合“选股”场景,即你并不是预测某只股票涨不涨,而是预测“池子里面哪几只相对更强”。
我给个实用建议:如果是初学者快速验证流程,先做三分类(涨/平/跌),阈值用比如未来5日收益超过2%算涨,低于-2%算跌,中间算平。如果已经有一定基础,想直接做选股系统,直接用排序目标或者回归加排序的混合思路。
4.2 特征怎么算——技术指标 vs 量价因子 vs 市场微观结构
输入特征可以从几个维度来组织:
技术指标类特征 :MA、MACD、RSI、布林带、成交量移动平均等。这些特征本身信息量不大,因为都是价格序列的“重排”,但作为非线性模型的输入,它们可以帮助模型识别“当前价格处于什么相对位置”。
量价关系类特征 :成交量/持仓量的变化率、量比(当日成交量/过去5日均量)、换手率等。所谓“量在价先”,量价关系特征在很多场景下比纯价格特征更有效。
横截面特征 :这是个很容易被忽略但效果显著的方向。比如把同一交易日全市场股票的“当日涨跌幅”、“换手率”、“市值排名”作为特征拼接进去——这等于让模型知道“今天这只股票在整个市场里处于什么分位水平”,类似量化里的行业相对强弱。模型如果只知道单只股票的绝对涨跌,很容易被大盘的系统性波动带偏;加入横截面信息可以隔离出个体独立的“alpha信号”。
时间特征 :月份、星期几、距上次财报公布的天数等。A股的“春季躁动”、“周四效应”这些周期现象,有一部分是真实统计规律(虽然很难说是否持续有效),把时间特征喂进去,模型至少能“自己学习”这些周期关系。
4.3 标准化——别让量纲问题害了训练
模型训练之前,特征必须标准化。我这里不是指“归一化到0-1”,而是计算z-score(减去均值除以标准差)。原因在于,神经网络对输入特征的尺度非常敏感。如果某个特征的数值范围是0到100,另一个是0到1,前者的梯度会主导更新方向,导致训练过程震荡甚至不收敛。
标准化的代码很简短:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 注意:只在训练集上fit,得到均值和标准差
scaler.fit(X_train_2d)
# 然后用同一套参数transform验证集和测试集
X_valid_2d = scaler.transform(X_valid_2d)
X_test_2d = scaler.transform(X_test_2d)
这里的X_train_2d是指把三维数据展平成二维,fit后再reshape回三维。这个操作很简单,但它的位置很关键——只允许在训练集上fit,再作用于其他数据集。如果你在全部数据上fit,虽然模型训练本身不会报错,但你已经在用未来信息优化输入分布了,测试集被“污染”了,回测结果会虚高。
4.4 避免未来函数
特征计算时必须小心“未来函数”。举例来说,如果某一天你要计算“过去5日的最高价”,那没问题,因为它只用了前一天及之前的5日数据。但如果你计算“过去5日最高价包括今天”,那在当日的特征里已经包含了今天的最高价信息,而真实交易中你今天还没收盘,你根本不知道今天的最高价会是多少。
这个看起来是小问题,但在分钟级或者高频策略里非常致命。回测时要严格按照“t时刻只能用t时刻及之前的数据”这个原则来检查特征。
5. 模型选型与训练过程——从基线模型到LSTM再到CNN的进阶路线
数据处理和特征工程做完,才轮到模型登场。
5.1 为什么不要一上来就上Transformer
现在大模型热,很多人一看到“深度学习”就直接想上Transformer或者各类先进的注意力架构。但我个人的经验是:在股票量化这个领域,先跑通简单的——MLP、LSTM、CNN——再做扩展。原因有三点:
第一,金融时序数据的信噪比极低,复杂模型很容易过拟合到噪声细节上。Transformer需要海量数据才不容易过拟合,而股票日线数据即使加了很多股票,有效独立样本量也没那么大。第二,复杂模型的训练成本高、调试成本更高。你损失函数不收敛,很难判断是模型结构问题还是数据问题。第三,在大多数场景下,LSTM和CNN的效果已经不差。
先把基线结果跑出来,形成一个“对照”,再去加复杂度。如果复杂模型的验证集效果比简单模型好不了多少,那它就是过拟合。
5.2 三个常用模型各自怎么用
MLP(多层感知机) :最简单,输入是展平后的特征窗口(比如60个交易日 x 20个特征 = 1200维向量),中间加两三个全连接层,输出softmax三分类或线性回归。它的局限在于:展平会丢失时间维度上的局部相关性。但它可作为一个性能下限参考。
LSTM(长短期记忆网络) :这是处理金融时序最经典的模型,因为它的门控机制能“记住”长期依赖关系——比如过去20天里有一根大阳线,这个信息对未来几天的走势可能仍然有影响。一个可参考的配置:
import torch.nn as nn
class StockLSTM(nn.Module):
def __init__(self, input_size, hidden_size=64, num_layers=2, num_classes=3, dropout=0.2):
super().__init__()
self.lstm = nn.LSTM(
input_size=input_size,
hidden_size=hidden_size,
num_layers=num_layers,
batch_first=True,
dropout=dropout
)
self.classifier = nn.Sequential(
nn.Linear(hidden_size, 32),
nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(32, num_classes)
)
def forward(self, x):
# x shape: (batch, seq_len, num_features)
out, (h_n, _) = self.lstm(x)
# 取最后一个时间步的隐状态做分类
return self.classifier(h_n[-1])
这里有个关键细节: batch_first=True 表示输入维度是 (batch, time_steps, features) , h_n[-1] 取的是最后一层LSTM的最终隐状态,而不是把所有时间步的输出都丢进分类器。如果取所有时间步再合并,分类器的输入维度会非常大,而且早期时间步的信息对预测未来走势的贡献通常不如近期信息,效果反而可能变差。
CNN(卷积神经网络) :直接用一维卷积在时间维度上滑动,提取局部时间模式。这种方法的直觉是:未来几天的走势往往由最近几天的量价形态决定,比如“放量突破”、“缩量回调”这些局部模式。一维CNN配几个卷积核,可以并行捕捉多种局部形态。还可以把多个资产的时间序列堆叠成多通道输入,类似图像处理里多通道的思路。
一个常用的简化方案是CNN + LSTM混合:先用CNN提取局部特征,降维后再送进LSTM捕捉长期依赖,这对分钟级数据尤其好用。日线级别的数据,单独用CNN或LSTM就够。
5.3 训练流程:早停、学习率与batch_size
训练参数直接影响结果,这里给一份“不太会出错”的默认配置:
- 损失函数 :分类用CrossEntropyLoss,回归用MSE或HuberLoss。HuberLoss对大异常值的惩罚比MSE轻,对金融数据更稳。
- 优化器 :Adam,初始学习率1e-3。如果loss在训练集上不下降,首先检查学习率,而不是换模型。
- batch_size :日线数据建议64到256之间。太小样本梯度噪声大,太大训练速度慢且容易收敛到尖锐局部极小值。
- 早停 :监控验证集loss,如果连续10个epoch没有下降,则停止训练并回滚到验证集loss最小的那个参数。这直接决定模型是否过拟合。
early_stopping_patience = 10
best_val_loss = float('inf')
wait = 0
for epoch in range(max_epochs):
train_loss = train_one_epoch(model, train_loader)
val_loss = evaluate(model, valid_loader)
if val_loss < best_val_loss:
best_val_loss = val_loss
wait = 0
torch.save(model.state_dict(), 'best_model.pt')
else:
wait += 1
if wait >= early_stopping_patience:
print(f'Early stop at epoch {epoch}')
break
注意一点,每个epoch结束做一次验证集评估并在验证集loss最小时保存模型,这个“保存最优模型”的动作很多人会漏,跑完直接用的最后一轮的权重。在过拟合模型上,最后一轮的权重经常已经远远偏离最佳状态,回测成绩会明显变差。
6. 回测系统——你离实盘还差一个滑点和手续费
模型训练完,你得到了一个预测器:对过去60天的数据输入,模型输出未来5天的涨跌概率分布。但到此为止,你只得到了一个“信号生成器”,它不等于交易策略,更不等于钱。要从信号变成策略,必须经过回测验证。这一环的坑,比模型训练里的还多。
6.1 向量化回测 vs 事件驱动回测
回测系统有两种构建方式。
向量化回测 :一次性对全部数据计算信号、持仓和收益,速度快,适合初步筛选策略。但它的缺点是难以处理复杂的资金管理和交易约束。
事件驱动回测 :用循环模拟每一个交易日的决策过程:今天收盘后生成信号,明天开盘执行交易,成交后更新持仓和现金,然后继续。它更贴近真实交易,适合精细验证。
我的建议是:先用向量化回测快速大概验证信号有没有方向性收益,如果收益为正,再实现事件驱动回测做更精细的验证。如果方向性收益本身就是负的,后面的精细化就没有意义。
6.2 手续费和滑点怎么建模
这是回测里最容易被“美化”的地方。很多教程里的回测根本不扣手续费,也不加滑点,出来的年化收益率吓人。但真实交易中,A股的手续费包括:
- 佣金:通常在万2.5到万3之间,最低5元。
- 印花税:卖出时收取,目前是成交金额的0.05%。
- 过户费:上海证券交易所收取,目前是0.001%。
滑点是指你看到的价格和实际成交价格之间的差异。对于流动性一般的股票,滑点可能不小,保守估计单边滑点按0.02%到0.05%建模比较合理。
一个简单的回测扣费逻辑如下:
# 假设 commission_rate 是佣金费率(比如 0.00025)
# 假设 stamp_duty_rate 是印花税率(比如 0.0005,卖出收取)
# 假设 slippage 是滑点比例(比如 0.0002)
def simulate_trade(price, side, amount):
if side == 'buy':
# 买入实际成本 = 价格 * (1 + 滑点) * 数量 * (1 + 佣金)
cost = price * (1 + slippage) * amount * (1 + commission_rate)
else: # sell
# 卖出实际收入 = 价格 * (1 - 滑点) * 数量 * (1 - 佣金 - 印花税)
revenue = price * (1 - slippage) * amount * (1 - commission_rate - stamp_duty_rate)
return cost, revenue
注意,单次买卖一次(一个来回),实际损耗至少是:佣金两边万5,印花税卖出万5,滑点两边约万4,加起来一个完整的“买-卖”周期成本大约在千分之一以上。如果你的策略平均持仓周期短、换手率高,累积交易成本会吞噬掉绝大部分超额收益。
6.3 过拟合怎么自查
当你看到回测曲线非常漂亮,年化百分之好几十,夏普比率超过3,先别高兴,做几个快速自查。
检查一:把训练集和测试集分开看收益 。如果测试集收益显著低于训练集,说明过拟合已经比较严重了。如果测试集收益甚至为负,那说明这个策略并没有真正学到可泛化的规律。
检查二:换一段历史时期再测 。比如你的训练数据截止到2021年,试试只看2022年的表现,或者只看2018年(熊市)的表现。一个稳健的策略应该在不同市场环境下都有一定生存能力,而不是只在牛市区间有效。
检查三:加上随机种子重训 。把模型训练时的随机种子改掉,比如seed从42换到10086,重新训练一遍,看回测结果波动大不大。如果换了随机种子结果剧烈变化,说明模型对初始条件非常敏感,稳定性堪忧。
检查四:做参数敏感性分析 。把关键超参数(比如预测窗口从5天改成3天或10天)微调一下,看看策略收益是否剧烈变化。如果稍微改动参数结果就变负,说明你的策略是在一个特定参数点上“雕刻”出来的,泛化能力很差。
7. 实战中绕不开的坑与解决办法
如果到这里你还愿意继续折腾,那说明你是真的想把这个事做成,而不是玩玩而已。最后分享一些我实操中切实踩过、后来才解决的坑,它们不在任何深度学习教程里。
7.1 数据泄漏的隐蔽形态
除了前面说的随机切分、全样本标准化,还有一种更隐蔽的数据泄漏:特征计算时用了未来信息。我遇到过最坑的一个是,在构建某技术指标时用了“包括当日”的未来5日均值,结果回测极其优秀,仿佛模型有预知能力。后来排查了整整一天才找到根源。从那以后,我的铁律是:所有特征计算函数,在生成特征时只允许访问 t 时刻及 t 时刻之前的数据,并且严格测试过边界情况。
7.2 GPU环境装好了但模型不跑
有些项目包里的模型代码比较老,兼容性有问题。比如某个老版本的PyTorch占位张量用 torch.autograd.Variable ,新版本已经移除了;又比如有的代码写 model.cuda() ,新版PyTorch更推荐 model.to(device) 。遇到这类问题,最快的解决路径是升级项目代码到当前版本的API,而不是降级PyTorch——因为老版本有其他安全隐患和依赖冲突,降级之后新的包可能又装不上了。
7.3 模型文件加载报错
load_state_dict 最常见的报错是 size mismatch ,一般是模型定义改过但权重文件没更新。解决方式是打印 torch.load 加载出来的键和形状,与当前模型的 state_dict 对比,人工核对哪一层不匹配。还有一个容易忽略的点:如果训练时用了 DataParallel (多卡训练),权重文件的键名会多一个 module. 前缀,加载时需要处理一下。项目包如果自带权重文件,先确认它是在什么版本的PyTorch下保存的,不同版本之间有时不兼容。
7.4 别把所有钱押在一个模型上
我在这个领域摸索出来的最大的一个心得是:不要指望单一深度学习模型能稳定赚钱。股票市场是一个极其复杂、非平稳的系统,任何模型都可能在未来某个时间失效。更稳妥的做法是:用多个不同类型的预测器做集成(比如LSTM、CNN、XGBoost各出一个信号),然后根据信号一致性决定仓位。信号一致性强时多配置一些仓位,分歧大时保持低仓位甚至空仓。这本质上是把“模型是否可靠”的不确定性也纳入了风控框架。
7.5 从回测到实盘还有一道鸿沟
回测通过只是最低门槛。实盘和回测的差别,还包括:行情推送的延迟、订单执行的不确定性、极端行情下的流动性枯竭、以及你自己的心态问题。建议第一次做实盘时,用最小仓位跑一段时间,比如资金量的1%到2%,先把流程调试通顺,验证实盘成交价格和回测预期有没有系统性的偏差,再考虑逐步加仓。没必要一上来就是满仓干,AI不会让你一夜暴富,但市场一定会给冒进的人上课。
更多推荐

所有评论(0)