news 2026/10/8 15:31:04

神经网络预测股指全流程:从数据预处理到选股落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
神经网络预测股指全流程:从数据预处理到选股落地

简介:《基于神经网络的股指预测与选股研究》是一份面向金融数据分析、深度学习及量化投资初学者的学术论文PDF,内容聚焦投资组合理论、RBP神经网络补全缺损数据、NARX神经网络构建多步预测模型,以及Markowitz均值-方差模型在选股中的应用,适合需要系统掌握神经网络建模与金融场景结合的读者。压缩包内共1个PDF文件,大小1.83MB,结构紧凑,便于离线阅读。已有119人学习该资源,资料核心价值在于完整呈现从异常值检测(描述性统计、箱型图、Z-score、DBSCAN、孤立森林)到缺失值处理,再到NARX网络三层结构、参数调整与误差修正的推导过程,并给出股指预测均误差在0.092以内的实证结果,可帮助读者理解如何用Python与MATLAB工具箱复现时间序列预测流程,并迁移至自身量化研究或课程设计中。

1. 神经网络预测股指:跑通全流程比选对网络更重要

神经网络做股指预测,网上资料一大半只讲单点原理,要么停在反向传播公式推导,要么贴一个跑不动的demo;真正能把数据、模型、回测串成完整链路的很少。这份《基于神经网络的股指预测与选股研究》PDF恰好是后者,从行情数据预处理讲起,一路走到网络选型、训练评估和选股落地,相当于一份可复现的数据建模指导。适合正在写量化课题、需要照着一套流程复现的研究生,也适合已经写过几个模型但预测结果一到实盘就翻车的从业者。下面按我的拆读路线,把特征构造、网络结构、参数和坑位逐段过一遍。

2. 数据与特征工程:建模前先把时间序列的边界想清楚

金融时序数据有两点和图像、文本截然不同:一是样本之间强顺序相关,打乱即坏;二是原始价格里藏着除权除息产生的人为跳空。这里,数据准备阶段要处理三件事:复权、特征、切分。

2.1 数据形态与复权:为什么很多人第一步就错

很多人在拿到行情接口后直接dropna开训,常见结果是训练集表现不错,换到新数据立刻失灵。第一道坎是数据频率。日线是研究的常规起点;分钟线虽然样本量更大,但噪声更强、交易成本也更难仿真,刚开始就用分钟数据,很容易把回测做成“看起来很赚、实盘无法复现”。先跑通日线,再考虑降频到分钟级。

第二道坎是复权。除权除息日,股票价格会被人为调整,不处理的话,模型会把分红送股当成一次真实大跌或大涨。我习惯统一用后复权价格,让收益率序列连续可算。取数时把复权因子一起留档,后面做组合市值折算时还要倒推回来。

提示:不同行情接口的复权参数差异较大,取数后先抽查几个除权日的价格,确认没有残留跳空,再进入下一步。

2.2 特征构造:滑动窗口、技术指标与标签定义

特征分两层:底层是原始行情,上层是衍生技术指标。技术指标不必堆太多,RSI、MACD、布林带位置、量能均线这几个够起步;特征过多,模型学到的大概率是噪声,训练速度也明显变慢。

import pandas as pd import numpy as np # df 已按日期升序排列,且使用后复权价格 df["ret_1d"] = df["close"].pct_change() df["vol_ma20"] = df["volume"].rolling(20).mean() df["close_ma20"] = df["close"].rolling(20).mean() df["boll_pos"] = (df["close"] - df["close_ma20"]) / df["close"].rolling(20).std() # 金融日线场景里,头部无效区间直接删除比前向填充更稳妥 df = df.ffill().dropna()

pct_change()计算单日收益率,rolling(20)是滑动窗口聚合;boll_pos表示收盘价在布林带内的相对位置,比直接给上下轨数值更便于模型比较。ffill()只是兜底,如果中间有大段停牌导致的缺失,建议按停牌规则单独处理,不要盲目填充。

接下来把连续行情切成监督学习的样本:

feature_cols = ["ret_1d", "vol_ma20", "close_ma20", "boll_pos"] window = 60 # 每个样本包含过去 60 个交易日 horizon = 5 # 预测未来 5 个交易日的走向 samples, labels, timestamps = [], [], [] for i in range(window, len(df) - horizon): samples.append(df[feature_cols].iloc[i-window:i].values) future_ret = df["close"].iloc[i + horizon] / df["close"].iloc[i] - 1 labels.append(1 if future_ret > 0 else 0) # 二元分类标签 timestamps.append(df.index[i]) X = np.array(samples) # (样本数, 60, 特征数) y = np.array(labels) # (样本数,)

这段代码的输出是三维数组X:第一维是样本数,第二维是时间窗口长度,第三维是特征数。horizon=5意味着标签是“未来5日后是否上涨”;步长越短标签噪声越大、但交易成本覆盖更困难,步长越长趋势性更强、样本量却更少。实践里我常用5到10作为初始步长,先跑基线,再对比不同步长对结果的影响。

如果要做回归而不是分类,把labels.append(1 if future_ret > 0 else 0)换成labels.append(future_ret)即可,损失函数同步换成 MSE。

2.3 数据集切分:时间序列不能随机打乱

新手最容易犯的错误是照搬图像分类的习惯,直接调用train_test_split(random_state=42)。金融时序一旦随机切分,训练阶段就可能见到未来区间的样本,测试指标虚高,实盘立刻现原形。

正确做法是纯按时间顺序切分:

train_end = int(len(X) * 0.7) val_end = int(len(X) * 0.85) X_train, y_train = X[:train_end], y[:train_end] X_val, y_val = X[train_end:val_end], y[train_end:val_end] X_test, y_test = X[val_end:], y[val_end:] from sklearn.preprocessing import StandardScaler # 标准化只用训练集拟合,验证集和测试集沿用同一组统计量 scaler = StandardScaler() X_train = scaler.fit_transform(X_train.reshape(-1, X_train.shape[-1])).reshape(X_train.shape) X_val = scaler.transform(X_val.reshape(-1, X_val.shape[-1])).reshape(X_val.shape) X_test = scaler.transform(X_test.reshape(-1, X_test.shape[-1])).reshape(X_test.shape)

fit_transform只出现在训练集里,这就是“归一化不穿越”的关键;验证集和测试集只允许transform,不允许重新计算均值和方差。切分比例不必死守 7:2:1,行情数据时间跨度越长,测试集占比可以适当提高,我一般留最近20%以上做最终验证。更严格的验证方式是滚动回测,做法放在第4章与训练评估一起讲,因为两者是同一套流程。

3. 三种网络结构怎么选:BP、LSTM、一维CNN的定位与实现

同样的数据,不同结构的神经网络学到的模式不同。这里按复杂度逐步拆三种常见结构,并给出可以直接落地的模型定义。

3.1 BP前馈网络:先搭一个能跑通的基线

BP(反向传播)前馈网络,也就是全连接网络,是所有后续模型的起点。它不假设任何时间结构,只把过去60个交易日的特征展平成一个大向量,再通过若干隐藏层拟合标签。正因为“笨”,它最适合当基线——如果MLP都跑不出正收益,问题多半在数据和标签,不在模型结构。

import torch import torch.nn as nn class MLP(nn.Module): def __init__(self, input_dim, hidden_dim=128): super().__init__() self.net = nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Dropout(0.3), nn.Linear(hidden_dim, 64), nn.ReLU(), nn.Linear(64, 1) # 输出未来收益率或类别分值 ) def forward(self, x): # x 形状: (batch, window, feature_dim),展平后进全连接 return self.net(x.view(x.size(0), -1))

使用时input_dim = window * feature_dim,也就是把每个样本的时间维和特征维全部展开。隐藏层宽度和层数不一定要大,金融数据样本量通常有限,128维起步即可。Dropout(0.3)是为了抑制过拟合,如果验证集损失下不来,先别动它,说明瓶颈可能不在过拟合。

3.2 LSTM:把顺序信息装进结构里

LSTM 在时序建模里如此常用,靠的是门控机制:既能跨长距离传递信息,又缓解了反向传播过程中的梯度消失。对 60 个交易日的窗口来说,两到三层 LSTM 足够;层数再加深,金融数据那点信号撑不住这么多参数。

class LSTMPredictor(nn.Module): def __init__(self, feature_dim, hidden_dim=64, num_layers=2): super().__init__() self.lstm = nn.LSTM( input_size=feature_dim, hidden_size=hidden_dim, num_layers=num_layers, batch_first=True, dropout=0.2 ) self.fc = nn.Linear(hidden_dim, 1) def forward(self, x): out, _ = self.lstm(x) # out: (batch, seq_len, hidden_dim) last = out[:, -1, :] # 取最后一个时间步的隐状态作为整段序列摘要 return self.fc(last)

batch_first=True表示输入形状是(batch, seq_len, feature_dim),与第2章构造的 X 正好对齐。取最后一个时间步的隐状态,是把整段序列的信息压缩成一个向量再输出;如果想把所有时间步都用起来,可以改成对所有隐状态做平均池化,但效果差异通常不大,代码复杂度却明显上升。

3.3 一维CNN与混合结构:局部特征的另一条路

一维卷积可以理解为在时间维度上做局部滑动特征提取,参数比全连接少,训练又比 LSTM 快。一个常见做法是先让卷积抽短期形态,再交给 LSTM 聚合长程依赖,形成卷积-LSTM 混合结构:

class ConvLSTM(nn.Module): def __init__(self, feature_dim, hidden_dim=64): super().__init__() self.conv = nn.Sequential( nn.Conv1d(feature_dim, 32, kernel_size=5, padding=2), nn.ReLU(), nn.MaxPool1d(2) ) self.lstm = nn.LSTM(input_size=32, hidden_size=hidden_dim, batch_first=True) self.fc = nn.Linear(hidden_dim, 1) def forward(self, x): # Conv1d 期望 (batch, channels, length),需要先转置 x = x.transpose(1, 2) # (batch, feature_dim, seq_len) x = self.conv(x) # (batch, 32, seq_len // 2) x = x.transpose(1, 2) # (batch, seq_len // 2, 32) out, _ = self.lstm(x) return self.fc(out[:, -1, :])

kernel_size=5意味着每次看 5 个交易日内的局部形态,padding=2让卷积输出长度不变,MaxPool1d(2)把序列长度减半,降低后续 LSTM 的计算量。这个结构的边界要清楚:短周期特征越强,卷积的优势越明显;如果特征本身没有局部相关性,Conv1d 和 MLP 的结果不会有本质区别。

三种结构在金融场景里没有绝对优劣。实践中的定位是:MLP 负责给出基线,LSTM 处理时间依赖,混合结构在数据量更大时能多榨出一点信号。不要指望换网络解决数据质量差的问题,数据建模的前八成工作都在第2章。

4. 训练与评估:损失函数、滚动回测与结果判读

训练参数的设定直接决定第一版模型的天花板。下面先说损失函数和优化器怎么选,再聊评估指标,最后给出滚动回测的流程。

4.1 损失函数与训练参数:第一版方案怎么定

损失函数取决于标签类型。下表是常规对应关系:

标签类型损失函数输出层
未来收益率(回归)MSELoss1个神经元,无激活
涨跌二分类BCEWithLogitsLoss1个神经元,内部加sigmoid
涨/平/跌三分类CrossEntropyLoss3个神经元,输出分布

优化器从 Adam 起步,学习率1e-3是安全起点;如果验证损失震荡明显,降到3e-4。批大小取 64 到 128,取决于显存和样本量。最小训练循环如下:

optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-5) criterion = nn.MSELoss() # 回归任务;分类任务换 nn.BCEWithLogitsLoss() for epoch in range(50): model.train() for X_batch, y_batch in train_loader: optimizer.zero_grad() pred = model(X_batch).squeeze() loss = criterion(pred, y_batch) loss.backward() optimizer.step() model.eval() with torch.no_grad(): val_loss = criterion(model(X_val).squeeze(), y_val) # 这里按验证集损失做早停,保存最优权重

weight_decay=1e-5是 L2 正则,防止参数过大;model.eval()配合no_grad()会关掉 Dropout 和梯度计算,保证验证集指标稳定。早停的常见参数是 patience=10,即连续 10 轮验证损失不下降就停止,保留验证损失最低的那份权重。

4.2 评估指标:不要只盯着损失

MSE 只反映数值拟合程度,对交易而言更重要的是方向有没有押对。我一般在测试集上同时看三样:RMSE、方向准确率和 IC 值。IC 是预测值与真实值之间的秩相关,用 Spearman 系数计算:

from scipy.stats import spearmanr model.eval() with torch.no_grad(): pred = model(X_test).squeeze().numpy() ic, _ = spearmanr(pred, y_test) acc = ((pred > 0) == (y_test > 0)).mean() print(f"IC = {ic:.4f}, 方向准确率 = {acc:.2%}")

IC 的绝对值在长期样本上通常只有 0.02 到 0.05,能稳定超过 0.05 已经算有边际预测力。判断模型有没有用,先跑一个空模型:直接用“未来收益率等于最近 N 日均值”作为预测,看它能不能跑赢这个简单规则。如果空模型胜出,神经网络参数再精调也没意义。

4.3 滚动回测:验证泛化能力的基本功

固定一次训练加一次测试,只能证明模型在某段时间有效,而市场结构会变。更可信的验证方式是滚动回测:每隔一定交易日重训一次,用截至当前的样本预测未来,再把所有预测拼接成连续结果。

step = 5 # 每隔 5 个交易日重训一次 horizon = 5 # 每次预测未来 5 个交易日 for t in range(train_end, len(X) - horizon, step): X_tr, y_tr = build_samples_up_to(t) # 只用 t 时刻之前的数据 model = LSTMPredictor(feature_dim=X.shape[-1]) train_one(model, X_tr, y_tr) pred_t = model.predict_next(horizon) # 保存 pred_t 用于拼接收益曲线

代码里的train_one和build_samples_up_to是按项目封装的简化示意,核心是每次重训都在 t 时刻截断,绝不使用 t 之后的数据。step控制重训频率,step=5相当于每周重训,成本高但信号更新快;样本量紧张时可以把 step 拉到 10 或 20。滚动回测结果如果和固定切分差距不大,模型比较稳定;若差距悬殊,大概率存在泄漏或过拟合。

5. 避坑与排查:五个把模型毁在回测之外的常见问题

最贵的错误往往不在模型本身,而在数据、标签和回测假设里。下面五条踩坑记录来自我拆读这份资料时的复核,也来自自己跑实验时交过的学费,按现象、原因、解决三段式写。

5.1 标签泄漏:回测漂亮,实盘失灵

现象:测试集准确率明显偏高,回测曲线近乎完美,但在模拟盘或新时间段上,表现远差于回测。

原因:特征或标签里混入了未来数据。常见来源是 rolling 指标默认中心对齐,第 t 个样本意外包含了后面交易日的统计量;另一个是构造“未来5日收益”标签时循环边界算错,把未来行也塞进了特征区间。

解决:写完代码先查循环边界,再用一个极简检查——把标签随机打乱后重训模型,如果准确率依然显著高于随机水平,几乎可以断定存在泄漏。技术指标计算时显式控制窗口偏移方向,不要依赖 pandas 的默认对齐。

5.2 归一化穿越:用了全样本统计量当标准

现象:验证集和测试集指标比训练集还好,模型在后期行情上表现“未卜先知”。

原因:对特征做StandardScaler()时,顺手对整个 X 调用了fit_transform,全样本的均值和方差被带入训练阶段。静态数据这样做影响不大,时间序列则完全不同,未来信息已经参与计算。

解决:严格记住顺序——先切分,再标准化。只在训练集上fit,验证集和测试集只transform。我习惯把 scaler 单独保存成文件,滚动回测每一步都用同一组统计量,避免重复拟合。

5.3 除权缺口:模型把分红当成暴跌

现象:某只股票的回测出现大量极端负收益样本,模型预测持续看空,但查看原始K线发现那些时点并没有真实大跌。

原因:数据没做复权。除权日价格被强制调整,未复权序列上出现人为跳空,神经网络把它当成真实的大幅波动来学习。

解决:统一使用后复权价格建模。同时注意,后复权价格和真实市价之间存在复权因子差异,建模用后复权序列,交易撮合要用原始价格,两个版本都要留档。

5.4 涨跌停与停牌:回测能买到的,实盘不行

现象:回测信号次日命中率不错,但实盘下单时股票涨停封板或停牌,根本成交不了,持仓和回测对不上。

原因:候选股池没有过滤极端状态,回测撮合默认按收盘价成交,而真实市场在涨停一字板和停牌时没有流动性。

解决:在候选股池里剔除当日涨停、跌停、停牌状态的个股;回测撮合加滑点,单边至少 0.1% 到 0.2%,同时保留“次日开盘价成交”这类更保守的假设。宁可回测收益低一点,也不要让假成交骗了自己。

5.5 反复调参:测试集被用成了训练集

现象:模型在测试集上被调得越来越好,但换一段没看过的行情,效果立刻回到随机水平。

原因:每次调参都看测试集指标,人的判断已经潜移默化地把测试集信息写进参数选择,测试集不再独立。

解决:把数据分成训练、调试、最终验证三份。平时只在调试集上调参,所有参数定稿后,最多在最终验证集上跑一次记录结果,跑完不再回头调整。数据量允许的话,再保留一段最新行情做“出师检验”。

6. 从预测到选股:组合落地时的两个关键动作

模型输出只是一个分数,要变成可执行的组合,还有两个环节容易被忽略。

6.1 用预测值排序代替绝对值阈值

无论网络输出的是未来收益率还是上涨概率,都不建议按固定阈值筛选。市场环境变化时,预测分布整体会漂移。我一般把所有候选股票的预测值做横截面排名,取前20到30只等权持仓,持仓周期5到10个交易日,到期重平衡。如果输出是分类概率,同样按概率排序;预测绝对值只作参考,不作阈值。

6.2 回测假设要显式配置

组合回测必须带上双边手续费、滑点以及成交限制。基准选沪深300或中证500,看累计收益曲线的同时,一定要看最大回撤和夏普。如果扣除成本后的策略收益跑不赢基准,不要急着换网络结构,先回第2章检查信号质量——大多数问题的根源不在模型。

从那以后,我每拿到一个新模型,都强制走一遍“时间顺序切分→只跑一次最终测试集→随机打乱标签检查泄漏→加滑点回测”这套固定流程,一步也不省。模型收益高不高是结果,流程可不可复现才是底线。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 15:31:03

物联网高职生必看:数据分析从加分项变分水岭

物联网专业的同学,尤其是高职三年制这一批,到了2026年毕业时,面临的就业环境会和前几年有很大不同。单纯会配网关、会烧录单片机、会接传感器,已经越来越不够用了。我最近看了大量物联网方向的热搜词和招聘要求,发现一…

作者头像 李华
网站建设 2026/10/8 15:31:02

S/4HANA客户主数据迁移实战:用cl_md_bp_maintain替代BAPI

今年年初我接了一个活,要把几百家客户主数据从老系统搬到 S/4HANA。最开始我想得很简单,老项目的 ABAP 代码还在,BAPI_CUSTOMER_CREATEFROMDATA1这些 BAPI 直接搬过来不就行了?结果第一轮测试就被打脸:一批客户导入到一…

作者头像 李华
网站建设 2026/10/8 15:30:08

信创环境文件夹上传:webkitdirectory与相对路径还原实战指南

信创环境下做前端,最头疼的不是业务逻辑,而是浏览器适配。尤其是文件上传这块,需求方一句“要保留文件夹路径”,就能让一个原本半小时搞定的功能,变成全员一起排查的攻坚战。最近我们在国产化终端上就接了这么个活儿&a…

作者头像 李华
网站建设 2026/10/8 15:30:05

法律人DeepSeek使用指南:提示词、API与RAG工作流

简介:DeepSeek法律人使用指南.pdf 是一份面向法律从业人员、法学研究者及关注法律智能化工具读者的实操型资料,聚焦DeepSeek推理模型在法学领域的落地方法。内容从使用前景写到核心与进阶技巧,涵盖法律信息检索、立法条文修订对比、案例比对分…

作者头像 李华
网站建设 2026/10/8 15:27:38

截断观测下非线性温度估计:EKF与KF对比及条件矩修正

1. 从一次散热片测温翻车说起:截断观测为什么能把滤波器带偏 做温度估计实验时,我遇到过一件很典型的事:热电偶贴在散热片表面,数据采集卡量程设置成 0~100℃。加热棒功率给大了,散热片温度冲到接近110℃&a…

作者头像 李华
网站建设 2026/10/8 15:27:36

Agent-Reach 实战:用 Python CLI 构建可扩展 AI Agent 的完整指南

1. 从标题拆解 Agent-Reach 的真实定位 1.1 这个标题背后藏着什么 第一次看到 "Agent-Reach" 这个名字,我的直觉是:这是一个把 AI Agent 能力"伸出去"的工具。Reach 这个词在工程语境里通常意味着触达、连接、扩展边界。结合热搜词…

作者头像 李华