ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于相关性分析的CNN-Attention-LSTM期货价格预测实战

基于相关性分析的CNN-Attention-LSTM期货价格预测实战 简介这是一套基于相关性分析的CNN-Attention-LSTM期货价格预测毕业设计项目面向深度学习、时间序列预测方向的在校学生、毕业设计者及算法工程师可解决期货数据清洗、相关性筛选、时间步构建与价格预测建模等全流程问题。压缩包共29个文件约30.28MB主要包含8个Python源码、6个npy数据集、3个xlsx表格、SQL数据库、模型checkpoint及两份PDF教程。py脚本覆盖相关性分析、时间步处理、模型定义、训练与预测等模块npy为模型训练与预测用输入输出数组xlsx/sql为原始周报及处理后的表格数据checkpoint为已训练模型权重PDF则附有配置教程与独立算法使用说明。代码注释超详细并附有预测接口脚本直接运行即可复现结果也便于按需修改后快速搭建自己的价格预测实验。目前已有677人学习下载适合用于课程设计、毕业设计或项目初期演示也方便替换数据、调整网络结构后做进一步研究。1. 期货价格预测为什么这么难CNN-Attention-LSTM要解决的核心矛盾用LSTM单独预测螺纹钢次日的收盘价训练集R²能到0.97验证集一测就掉到0.2——这是我在不少期货价格预测毕设里看到的第一堵墙。纯时序模型擅长记住历史却很难判断哪些K线才是决定下一步走势的关键。基于相关性分析的CNN-Attention-LSTM模型正是针对这个矛盾设计的组合方案先用相关性分析筛掉冗余特征再让CNN提取局部K线形态用Attention给关键时间步分配权重最后用LSTM消化长周期依赖。这篇笔记按数据、建模、训练、排错的顺序把整条链路拆成可复现的步骤适合正在做毕设、或者想入量化但缺一个可靠起点的读者。2. 模型原理拆解CNN、Attention、LSTM分别盯住行情里的哪些信息2.1 CNN抓局部一维卷积如何从K线序列里提取形态期货行情里真正有预测价值的信息往往不是某一天的涨跌而是连续几天的形态组合比如放量突破、三连阳缩量整理、长上影线试探压力位。这类“局部K线形态”正是CNN的舒适区。把每个交易日的特征当成一条通道用一维卷积核在交易日时间轴上滑动相当于让模型每看三天、五天就判断一次“这段像不像某个形态”。和全连接层相比Conv1d有两个实打实的优势。第一是局部感受野kernel_size3的卷积核每次只看3个交易日参数少不容易被远距离无效信息干扰第二是参数共享同一个卷积核扫过所有时间位置模型学到的是“某种模式长什么样”而不是“某个位置有什么特征”。这里要提一个常见的理解偏差CNN在这套模型里卷积方向是沿着时间轴走的in_channels不是1而是特征数。比如你有12个特征卷积核的通道数就是12每次覆盖“连续的3天×全部12个特征”这个小方块。out_channels64的意思是同时用64个不同的卷积核每个核负责提取一种形态模式。kernel_size3代表一次看3天kernel_size5就代表看一周的交易日具体设多少取决于你的交易周期判断。2.2 Attention做加权让模型知道哪几天最关键如果你看过transformer模型详解会知道Attention机制的核心思想就是“加权求和”。放到期货场景里这句话可以被翻译得更直白不是所有交易日对下一次预测的贡献都一样。央行公布政策、库存数据超预期、某天成交量突然放大到平时的三倍——这些关键日子的信息量比随后几天的缩量震荡重要得多。Attention就是让模型自己学会给这些日子分配更高的权重。常见的做法是加性注意力也就是先对LSTM每个时间步的输出做一次线性变换过tanh激活再压缩成一个标量分数最后softmax归一化成权重。训练完成后这些权重可以直接画出来看模型是不是把注意力集中在放量突破的那天是不是忽略了长期横盘的那段。这一步对毕设答辩特别有用它让模型从“黑匣子”变成了“能解释的黑匣子”老师问起来也有说头。需要厘清的是CNN解决的是“局部形态怎么提取”Attention解决的是“哪些时间步更值得看”而标题里的相关性分析解决的是“哪些特征值得喂进模型”。三者的作用范围不重叠这也是为什么这条技术路线在时序预测领域被反复验证因为它每个组件都在处理一个独立的痛点。2.3 LSTM管记忆长周期依赖为什么还是离不开它CNN和Attention都是“无状态”的结构——它们看一段窗口输出一个加权结果但本身不保留跨窗口的记忆。期货价格预测恰恰需要这种时序记忆前期的趋势斜率、支撑位附近的历史换手、震荡区间的持续时间这些信息跨越几十个交易日很难靠局部卷积捕捉。LSTM用三个门结构遗忘门、输入门、输出门控制信息的写入、保留和丢弃在长序列建模上的稳定性远好于普通RNN。在这套模型里LSTM的输入是CNN提取后的特征序列还是按时间顺序排列的输出是每个时间步的隐状态Attention作用在这些隐状态上而不是直接作用在原始特征上。这样设计的好处是Attention拿到的是“已经被CNN加工过、又被LSTM按时间顺序消化过”的高层表示加权汇总出来的上下文向量信息密度更高。层数方面我一般建议毕设直接从单层LSTM开始。期货日线数据量不大一个品种十年也就2000多个交易日两层甚至三层的LSTM只会让参数量暴增、训练变慢最后验证集损失照样回升。另外不要用双向LSTM——它虽然能在很多序列任务里刷高指标但预测场景下双向等于让模型看了未来数据属于典型的信息泄漏答辩时被追问一次就很难收场。2.4 为什么是“相关性分析”在前特征不是越多越好很多初学者拿到行情数据第一反应是能塞多少特征就塞多少MA5、MA10、MA20、MACD、KDJ、布林带、成交量、持仓量……一口气构造20多个维度喂给模型。结果训练慢、过拟合重测试集表现反而不如只用六个基础特征。原因在于这些技术指标高度相关。MA5、MA10、MA20本质上都是收盘价在不同窗口下的平均彼此相关系数随便一算就是0.9以上。这些冗余特征至少带来三个副作用一是放大噪声多个高度相关的特征会把同一个错误信号重复强化二是拖慢训练维度越高收敛越慢三是让优化器在梯度更新时无所适从相当于同一件事被重复说三遍模型不知道该信哪个版本。相关性分析在这个标题里的角色就是在数据进入模型之前先用统计方法把这些“一眼假”的特征删掉。它不属于模型组件而是数据流水线的前置步骤用最简单的皮尔逊相关系数就能完成第一轮筛选。这一点在毕设论文里值得单独写一小节模型再复杂也救不了脏乱差的输入。3. 相关性分析先行从期货数据到模型输入特征筛选这么做3.1 用Python准备数据获取行情并构造候选特征建立预测模型的第一步是把行情数据拿到手里并整理成干净的表结构。这里以螺纹钢主力连续合约为例数据接口用akshare的期货历史行情拿到之后先统一列名、排序、去空值再开始构造候选特征。import akshare as ak import pandas as pd import numpy as np # 获取螺纹钢主力连续合约日线数据 df ak.futures_main_sina(symbolRB0, start_date20200101, end_date20241231) df.columns [date, open, high, low, close, volume, hold, settle] df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) def rsi(close: pd.Series, period: int 14) - pd.Series: # 用收盘价序列计算相对强弱指标公式按标准定义实现 delta close.diff() gain delta.clip(lower0).rolling(period).mean() loss (-delta.clip(upper0)).rolling(period).mean() rs gain / loss return 100 - 100 / (1 rs) # 构造候选特征均线、RSI、日收益率 df[ma5] df[close].rolling(5).mean() df[ma10] df[close].rolling(10).mean() df[ma20] df[close].rolling(20).mean() df[rsi14] rsi(df[close], 14) df[ret_1] df[close].pct_change() df[vol_ratio] df[volume] / df[volume].rolling(10).mean() df.dropna(inplaceTrue)这段代码里有几个细节值得注意。rolling(5).mean()是窗口为5的移动平均窗口跨度对应5个交易日pct_change()计算的是当天相对前一天的收益率把非平稳的价格序列转换成了相对平稳的收益序列vol_ratio用当天成交量除以10日均量衡量“今天的量能异动程度”这是一个比原始成交量更稳定的特征。参数说明RSI的period14是行业默认值均线窗口取了5/10/20三档覆盖短、中、长三种节奏。如果你预测的是日内高频数据这些窗口都要大幅缩小如果是周线级别窗口则要放大到4/12/24。3.2 相关性矩阵与滞后相关性剔除冗余特征、找先行指标拿到候选特征之后先别急着建模做两件事第一用皮尔逊相关系数矩阵找出冗余特征对第二做滞后相关性分析看看哪些指标对未来的收益率有预测力。from sklearn.preprocessing import StandardScaler # 候选特征列表 feature_cols [open, high, low, close, volume, hold, ma5, ma10, ma20, rsi14, ret_1, vol_ratio] df_features df[feature_cols].copy() # 1) 皮尔逊相关系数矩阵找出冗余特征对 corr df_features.corr(methodpearson) high_corr_pairs [ (a, b) for a in corr.columns for b in corr.columns if a b and abs(corr.loc[a, b]) 0.90 ] print(高相关特征对:, high_corr_pairs) # 2) 滞后相关性把vol_ratio分别滞后1/3/5天看它们与未来5日收益率的关系 future_ret_5 df[close].shift(-5) / df[close] - 1 for lag in [1, 3, 5]: tmp pd.concat([df[vol_ratio].shift(lag), future_ret_5], axis1) tmp.columns [vol_lag, future_ret] c tmp.corr(methodspearman).iloc[0, 1] print(fvol_ratio lag {lag} vs future_ret_5: {c:.4f})第一步的corr()会输出一个12×12的矩阵high_corr_pairs这个列表会把所有两两相关系数绝对值大于0.90的特征对挖出来。这里的阈值0.90是经验值如果你发现跑出来还是有一大堆冗余可以收紧到0.95想要更保守放到0.85也行核心目的是删除明显重复的信息而不是追求极致的特征数量。滞后相关性这一步值得多说几句。它做的事情是把某个指标往回平移lag天再和未来5日的收益率做相关分析。如果vol_ratio滞后1天和未来5日收益率的Spearman相关系数明显高于滞后5天的说明“昨天的量能异动”比“5天前的量能异动”更有预测价值。这里用methodspearman而不是pearson因为量能对收益的影响往往不是线性的秩相关对异常值更稳健。需要特别提示这段滞后相关性分析只能使用训练集范围内的数据不要在全部数据上做完分析再回头划分数据集否则特征选择这一步也引入了未来信息和后面要讲的标准化泄漏属于同类问题。3.3 时序滑窗切分样本怎么组织才不会信息串味相关性分析把特征选定了接下来要做的是把纵向的行情数据切成横向的样本。时序预测不能用随机划分否则训练集和测试集的数据在时间上互相穿插模型看到的“未来”实际上已经出现在训练阶段里了。正确的做法是按时间顺序切分然后用滑窗生成一个个样本。def make_samples(feature_df: pd.DataFrame, window: int 30, horizon: int 5, close_idx: int 3) - tuple: # 滑动窗口样本生成特征窗口是[i-window, i)标签是i日后horizon个交易日的累计收益方向 data feature_df.values X, y [], [] for i in range(window, len(data) - horizon): X.append(data[i - window:i]) future_ret data[i horizon, close_idx] / data[i, close_idx] - 1 if future_ret 0.005: y.append(1) # 上涨 elif future_ret -0.005: y.append(-1) # 下跌 else: y.append(0) # 横盘 return np.array(X), np.array(y) # 按时间顺序切分前70%训练中间15%验证最后15%测试 train_end int(len(df) * 0.7) val_end int(len(df) * 0.85) window 30 horizon 5 X_train, y_train make_samples(df.iloc[:train_end], window, horizon) X_val, y_val make_samples(df.iloc[train_end - window:val_end], window, horizon) X_test, y_test make_samples(df.iloc[val_end - window:], window, horizon)这个函数是整条数据链路的枢纽重点看循环边界for i in range(window, len(data) - horizon)保证了滑窗的最后一天至少距离数据末尾有horizon天不然标签算不出来。特征窗口data[i-window:i]取到的是第i-1天及之前的数据标签用的是data[i horizon]和data[i]的比值特征和标签之间没有任何一天是重叠的这是硬性要求。参数上window30代表用最近30个交易日的数据做预测大约一个半月horizon5表示预测未来5个交易日的累计涨跌并将结果粗略分为上涨、下跌、横盘三个类别。0.005也就是0.5%的阈值是分类边界调小会让横盘类样本变少调大则会让模型倾向于把所有样本都归为横盘。如果你做的是回归任务而不是三分类直接让y等于future_ret数值即可。验证集和测试集的起点都往前多取了window天这是为了让滑窗在切分边界处也能取满30天的特征窗口不会因为前面不够长而丢掉样本。如果你直接从train_end开始切验证集那验证集的前30天样本就会因为缺窗口被浪费掉白白损失一批可用的验证样本。4. 用PyTorch实现CNN-Attention-LSTM模型代码、训练循环与超参基准4.1 模型定义CNN提取形态LSTM编码顺序Attention加权汇总模型结构按“CNN → LSTM → Attention → 全连接输出”的顺序搭建。输入是一个三维张量(batch, window, features)大约相当于“一批、30天、每天12个特征”的交易日历。下面给出一份可以直接跑通的最小可复现实现。import torch import torch.nn as nn class CnnAttentionLstm(nn.Module): def __init__(self, n_features: int, window_size: int, cnn_out: int 64, lstm_hidden: int 128, num_layers: int 1, num_classes: int 3, dropout: float 0.3): super().__init__() # CNN分支Conv1d在交易日时间轴上滑动每个特征作为独立通道 self.cnn nn.Sequential( nn.Conv1d(in_channelsn_features, out_channelscnn_out, kernel_size3, padding1), nn.BatchNorm1d(cnn_out), nn.ReLU(), nn.Dropout(dropout), ) # LSTM编码时间顺序输出每个时间步的隐状态 self.lstm nn.LSTM(input_sizecnn_out, hidden_sizelstm_hidden, num_layersnum_layers, batch_firstTrue, dropoutdropout if num_layers 1 else 0) # 加性注意力打分器把每个时间步的隐状态映射为一个权重分数 self.attn nn.Sequential( nn.Linear(lstm_hidden, 64), nn.Tanh(), nn.Linear(64, 1), ) self.fc nn.Linear(lstm_hidden, num_classes) def forward(self, x): # 输入x: (batch, window, features) x x.permute(0, 2, 1) # 转为(batch, features, window) c self.cnn(x) # (batch, cnn_out, window) c c.permute(0, 2, 1) # 转回(batch, window, cnn_out) h, _ self.lstm(c) # (batch, window, lstm_hidden) # 对每个时间步打分softmax归一化成权重 w torch.softmax(self.attn(h), dim1) # (batch, window, 1) context torch.sum(w * h, dim1) # (batch, lstm_hidden) return self.fc(context), w这里最容易写错的是维度方向。PyTorch的Conv1d要求输入是(batch, channels, length)与TensorFlow/Keras的(batch, length, features)相反所以forward第一行必须做一次permute。LSTM层的input_sizecnn_out等于64意思是每个时间步进来的是一个64维的CNN特征向量。Attention层作用在LSTM输出的每个时间步上输出w的形状是(batch, window, 1)含义是“这30天里每一天对最终预测的重要程度”。模型返回两个东西预测logits和注意力权重w。logits送进交叉熵损失函数w留作可视化和解释。调试的时候建议把w打印出来看两眼如果softmax之后的权重几乎均匀分布说明Attention没学到东西问题多半出在前面CNN或LSTM的表达能力不够而不是Attention层本身写错了。4.2 训练循环早停、梯度裁剪与学习率衰减缺一不可模型定义好之后训练循环的质量直接决定毕设能不能在答辩前拿出一个稳定结果。时序任务里我不建议开着学习率跑到底也不建议不做早停硬train到100个epoch然后取最后一个不稳定的checkpoint。import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset # 将NumPy数组转为PyTorch的Dataset train_ds TensorDataset(torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.long)) train_loader DataLoader(train_ds, batch_size64, shuffleFalse) model CnnAttentionLstm( n_featuresX_train.shape[2], window_sizeX_train.shape[1], num_classes3, ) loss_fn nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-3, weight_decay1e-5) scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, factor0.5, patience5) best_val_loss, patience, best_state float(inf), 0, None for epoch in range(100): model.train() for xb, yb in train_loader: optimizer.zero_grad() logits, _ model(xb) loss loss_fn(logits, yb) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() model.eval() with torch.no_grad(): val_logits, _ model(torch.tensor(X_val, dtypetorch.float32)) val_loss loss_fn(val_logits, torch.tensor(y_val, dtypetorch.long)) scheduler.step(val_loss) if val_loss best_val_loss: best_val_loss val_loss patience 0 best_state {k: v.clone() for k, v in model.state_dict().items()} else: patience 1 if patience 10: break model.load_state_dict(best_state)三个细节值得特别说明。第一DataLoader的shuffleFalse是铁律时序样本一旦打乱验证集里就会出现“未来样本预测过去”的荒谬组合整个训练结果失去意义。第二clip_grad_norm_(max_norm1.0)做梯度裁剪LSTM在训练后期经常出现梯度爆炸设置一个1.0的梯度上限能让损失曲线平稳很多。第三weight_decay1e-5是L2正则化参数越大惩罚越强防止模型过度相信某些特征的数值大小。早停的逻辑是连续10个epoch验证损失不创新低就停止训练然后回滚到best_state对应的权重。很多同学容易漏掉最后一步load_state_dict结果最终保存的是最后一次epoch的权重而不是表现最好的状态。这个bug非常隐蔽因为训练过程看起来完全正常只是预测结果莫名其妙地差了一截。顺带提醒验证集只用于早停和调参不要拿它反复试来试去试得多了验证集也就被“污染”了最终以测试集的结果为准。4.3 超参基准表与调参顺序超参数设多少是个经验问题但有一条主线期货日线数据的样本量普遍偏小模型容量必须克制。下面这组参数是我在类似任务里跑过多次的默认值。超参数推荐值调整逻辑window_size30短了噪声大长了引入无关行情30个交易日约一个半月cnn_out64卷积核数量64起步加到128在日线上收益不明显但训练时间翻倍lstm_hidden128隐层维度256以上在小样本上极易过拟合num_layers1先跑单层验证损失不降再考虑加一层dropout0.3CNN后和LSTM后各加一层下降到0.2以下过拟合风险升高batch_size64日线样本总量小64足够不需要用大batch加速lr1e-3Adam默认学习率配ReduceLROnPlateau衰减patience10早停窗口数据量小时可放宽到15调参顺序建议固定优先级先固定window和特征集把学习率调稳再动lstm_hidden最后才动dropout。不要一上来就网格搜索全部参数样本量摆在那里网格搜索的结果很容易被噪声主导。每次只改一个变量记录验证集指标的变化答辩时这个调试过程本身就是很好的素材。5. 避坑清单数据泄漏、标签错位、过拟合、非平稳与假相关5.1 数据泄漏标准化顺序搞反测试集结果全是假的现象训练前手滑对全量数据做了StandardScaler的fit_transform训练集指标正常验证集和测试集指标好到离谱换个时间段的数据又完全失效。原因scaler在fit时已经“见过”验证集和测试集的均值和方差相当于把测试集的分布信息送进了训练过程。测试不再是未知数据结果当然好看但这种好看经不起推敲。解决先按时间切分数据再在训练集上fit验证集和测试集只transform绝不重新fit。from sklearn.preprocessing import StandardScaler scaler StandardScaler() # 训练集fit_transform验证/测试集只有transform n_train X_train.shape[0] flat_train X_train.reshape(n_train, -1) X_train_scaled scaler.fit_transform(flat_train).reshape(X_train.shape) n_val X_val.shape[0] X_val_scaled scaler.transform( X_val.reshape(n_val, -1)).reshape(X_val.shape) n_test X_test.shape[0] X_test_scaled scaler.transform( X_test.reshape(n_test, -1)).reshape(X_test.shape)5.2 标签错位特征和标签的时间戳重叠等于让模型抄答案现象训练损失掉到0.01以下训练准确率冲到95%以上验证集准确率却不到50%跟抛硬币差不多。原因滑窗样本的特征窗口和标签区间发生了重叠。最常见的是把“当天”的数据也塞进了特征窗口比如特征用了data[i-window:i1]多取了第i天标签却是从第i天开始算的未来收益。还有一种更隐蔽的错位rolling类的技术指标计算时没有加shift(1)MA和RSI的当天值包含了当天收盘价而你预测的也是当天收盘价启动的行情二者天然相关。解决构造样本时特征窗口必须是data[i-window:i]标签从data[i]开始算所有技术指标在构造完成后统一做一次shift(1)。拿不准的时候写一个断言检查特征矩阵最后一行的日期是否早于标签对应的日期这个方法简单且有效。5.3 过拟合训练集指标华丽验证集一塌糊涂现象训练损失稳步下降到0.1验证损失从第15个epoch开始掉头向上后面越拉越大——典型的训练曲线和验证曲线开口。原因模型容量大、参数多而期货日线数据量实在太小。一个品种按每年240个交易日算十年也才2400个样本扣掉滑窗和horizon有效样本可能不到2000条。拿一个几十万参数的模型去拟合2000条数据过拟合几乎是必然的。解决最直接的办法是限制模型容量lstm_hidden降到64CNN输出降到32dropout提到0.4。其次是用早停锁住最佳验证状态。如果你想换种思路可以考虑把多个品种的数据拼在一起训练比如螺纹钢、热卷、铁矿石三个品种的日线联合建模样本量直接翻三倍预测时再按品种各自做标准化。这个做法在公开论文里很常见答辩讲出来也是有分量的设计。5.4 非平稳性直接预测价格 vs 预测收益率现象模型在训练段里预测价格的曲线几乎贴着真实价格走看起来效果惊人把测试段一拉出来预测曲线滞后真实走势好几天典型的“昨天的价格预测今天的价格”。原因价格序列本身是非平稳的包含趋势和漂移成分。模型学的不是规律而是“惯性”——昨天涨了今天大概率还涨、昨天的价格就是今天最合理的估计。这种预测对趋势段有效一旦行情反转模型必然翻车。解决把预测目标从“收盘价”换成“收益率”或“涨跌方向”。收益率序列是近似平稳的统计性质稳定得多模型学到的才是真正意义上的“涨跌规律”而不是“价格惯性”。这个问题在毕设开题时就该定下来预测绝对价格除了指标好看没有任何实际意义预测收益率或方向才是实盘能用、答辩能讲清楚的任务定义。5.5 皮尔逊相关系数的边界线性相关代表不了一切现象相关性热力图上vol_ratio和future_ret的皮尔逊相关系数有0.3放进模型后指标反而变差另外某个特征相关系数只有0.05单独测试时却能提升验证集准确率一个百分点。原因皮尔逊相关系数只能捕捉线性关系对异常值极其敏感期货里的涨跌停、成交量骤变会在一个点上把相关性拉偏。而特征和标签之间的关系往往是非线性的量能突然放大两倍可能是反转信号放大十倍可能还是反转信号放大二十倍可能就变成极端行情了这类关系用线性相关系数根本衡量不了。解决筛选阶段用Spearman秩相关做交叉验证它对异常值和非线性关系更稳健模型训练之前对极端样本做截尾处理比如收益率绝对值超过8%的样本直接剔除或用分位数压缩。记住一个原则相关性分析擅长做减法——告诉你哪些特征一定冗余不擅长做加法——不能因为相关性低就断定特征没用。判断一个特征该不该留最终还得靠验证集实测。6. 让毕设结论站得住评估指标、回测与稳定性验证模型训练完不要只看训练集损失就收工。三分类任务先看整体准确率再看混淆矩阵里每一类的召回率——期货数据里“横盘”样本往往占了大头哪怕模型把所有样本都预测成横盘准确率也能有40%左右这个数字很容易骗人。回归任务不要只报R²加上RMSE和MAE一起看而且要在收益率序列上算而不是在绝对价格上算。回测验证这一步很多毕设都没有认真做。最简单的方式是测试集上模型预测上涨就做多预测下跌就做空预测横盘就空仓把所有交易信号的收益累加起来扣除手续费和滑点。期货双边手续费加滑点按每笔万分之一到万分之二点五估算扣完之后收益曲线能稳定向上跑指标才有说服力。如果扣掉成本后曲线立刻变成亏损那说明模型的有效信号连交易成本都覆盖不了这本身就是一个值得写进论文的结论。稳定性验证建议再花半小时做一次把训练集和测试集的分界点往前挪一年重新训练看指标波动大不大或者换一个相关性高的品种验证同一条pipeline能不能复现。另外把Attention权重画出来检查模型是不是真的把注意力集中在放量跳空那几天——这个可视化既能辅助判断模型有没有学到有效模式论文里放一张图份量也完全不一样。我自己每次接这类任务都会先跑一个单LSTM的基线模型确认数据链路、标签定义和评价脚本全部跑通再上完整版CNN-Attention-LSTM。这一步看似多花了半天时间实际上把后面debug的时间省回来了大半。做毕设最怕的不是模型效果差而是模型效果好了却不知道为什么好换了一个参数或者换了一段行情就彻底崩掉到时候再回头查数据代价远大于一开始就做好基线验证。希望帮到你。本文还有配套的精品资源点击获取
返回列表