简介:这是一份面向计算机相关专业学生、教师及企业员工的外汇预测学习资源,基于LSTM网络构建汇率走势预测模型,适合作为深度学习与时间序列分析的入门实践,也可用于课程设计、毕业设计或作业参考。压缩包共31个文件,约13.41MB,包含Python脚本、Jupyter Notebook、CSV汇率数据、模型权重与配置文件等,覆盖数据准备、模型训练、测试与预测的完整流程,并附有README说明使用方法与依赖安装。资源中提供了USDJPY、GBPUSD等外汇对的历史数据与已训练模型,读者可借此理解LSTM门控机制、数据标准化与序列切分等关键环节,同时对照代码掌握TensorFlow/Keras的建模思路。目前已有48人学习下载,适合希望快速上手深度学习预测项目、积累实战经验的学习者参考,但需注意市场预测存在不确定性,模型结果仅供学习交流,不可用于商业用途。
1. 外汇预测模型用 LSTM 到底靠不靠谱:先看清它的能力边界
很多人第一次接触外汇预测,都是被“用 LSTM 预测汇率,准确率 90%”这类标题吸引进来的。我当初也一样,觉得只要把历史价格喂进 LSTM,它就能吐出明天的收盘价。实际跑过几轮之后才发现,LSTM 在外汇这种高噪声、低信噪比的时间序列上,既不是万能钥匙,也不是完全没用——它的价值在于捕捉序列里的非线性惯性,而不是预测每一次跳动。这个标题里的“Python 源码、数据、模型”三件套,本质上就是一条从原始汇率数据到可训练张量、再到 LSTM 网络、最后到预测输出的完整链路。适合谁?适合已经会一点 Python、想拿真实金融数据练手时间序列建模的工程师,也适合做量化策略原型、需要快速验证特征工程是否有效的人。如果你指望它直接拿去实盘赚钱,那大概率会失望;但如果你想搞懂 LSTM 在金融序列上的真实表现、知道它什么时候翻车、怎么调参能稳住,那这套东西值得动手复现一遍。
2. 数据从哪来、怎么洗:外汇日线数据的获取与预处理
2.1 外汇数据的常见来源与字段选择
做外汇预测,第一步不是写模型,而是搞到干净的数据。常见做法有三种:一是用免费金融数据接口拉取历史汇率,比如通过yfinance获取主要货币对(EURUSD、USDJPY、GBPUSD 等)的日线数据;二是从公开的经济数据库下载 CSV,比如部分央行或国际组织发布的汇率时间序列;三是用券商或数据商提供的 API,但这类通常有权限门槛。我一般会先用yfinance快速拉一份 EURUSD 的日线,字段至少保留Open、High、Low、Close、Volume,其中外汇现货没有真实成交量,Volume经常是 0 或缺失,所以实际建模时我通常只保留 OHLC 四个价格字段。
选日线而不是分钟线,是因为 LSTM 对序列长度敏感,分钟级数据噪声更大、样本量爆炸,新手很容易在数据量上先把自己拖垮。日线数据一天一条,十年也就 2500 条左右,配合滑动窗口构造样本,足够跑通一个最小可用的 LSTM 预测流程。
2.2 用 Pandas 做缺失值处理与收益率转换
原始价格序列不能直接喂给 LSTM,原因有两个:一是价格是非平稳的,均值方差随时间漂移;二是不同货币对的价格量级差异大,直接输入会让网络偏向大数值。我一般会把收盘价转成对数收益率,再做标准化。下面这段代码是从拉取数据到生成训练用数组的完整过程:
import yfinance as yf import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler # 拉取 EURUSD 日线数据,周期选 2015-01-01 到 2024-12-31 df = yf.download("EURUSD=X", start="2015-01-01", end="2024-12-31") df = df[["Open", "High", "Low", "Close"]].copy() # 前向填充缺失值,再删掉仍然为 NaN 的行 df.ffill(inplace=True) df.dropna(inplace=True) # 计算对数收益率,避免价格非平稳 df["log_return"] = np.log(df["Close"] / df["Close"].shift(1)) df.dropna(inplace=True) # 只用收益率作为特征,做 MinMax 标准化到 [0,1] scaler = MinMaxScaler(feature_range=(0, 1)) scaled = scaler.fit_transform(df[["log_return"]].values) # 构造滑动窗口:用过去 60 天预测下一天 def create_sequences(data, window=60): X, y = [], [] for i in range(window, len(data)): X.append(data[i-window:i, 0]) y.append(data[i, 0]) return np.array(X), np.array(y) X, y = create_sequences(scaled, window=60) X = X.reshape((X.shape[0], X.shape[1], 1)) # LSTM 需要 [样本, 时间步, 特征] print(X.shape, y.shape)这段代码里几个关键点:ffill是前向填充,外汇数据周末停盘,周一开盘前会有缺口,前向填充比插值更符合实际交易逻辑;log_return把价格转成收益率,这一步不做的话,后面 LSTM 很可能只是学会“预测昨天的价格”,看起来 loss 很低,实际没有预测能力;window=60是回看窗口,我一般会在 30 到 120 之间试,60 是日线数据里比较稳的起点;reshape成三维是 PyTorch 和 Keras 的 LSTM 层要求的输入格式,漏掉这一步会直接报维度错误。
注意:
yfinance的汇率数据偶尔会有异常跳点,拉下来之后最好画一下收盘价曲线,肉眼扫一遍有没有明显错误再往下走。
3. 搭一个能跑的 LSTM 模型:PyTorch 实现与训练参数
3.1 网络结构怎么定:层数、隐藏单元与 Dropout
LSTM 做时间序列预测,结构不需要太深。我试过 1 层、2 层、3 层,在外汇日线这种数据量下,2 层 LSTM 加一层全连接输出通常就够了,再深容易过拟合。隐藏单元数一般取 32、64、128,我习惯从 64 开始。Dropout 放在 LSTM 层之间,取 0.2 左右,能明显缓解训练集 loss 下降但验证集 loss 反弹的问题。
下面是一个可以直接跑的 PyTorch 模型定义和训练循环:
import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset # 划分训练集和测试集,前 80% 训练,后 20% 测试 split = int(len(X) * 0.8) X_train, X_test = X[:split], X[split:] y_train, y_test = y[:split], y[split:] train_ds = TensorDataset(torch.tensor(X_train, dtype=torch.float32), torch.tensor(y_train, dtype=torch.float32)) train_loader = DataLoader(train_ds, batch_size=32, shuffle=True) class LSTMModel(nn.Module): def __init__(self, input_size=1, hidden_size=64, num_layers=2, dropout=0.2): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, dropout=dropout) self.fc = nn.Linear(hidden_size, 1) def forward(self, x): out, _ = self.lstm(x) # out: [batch, seq, hidden] out = out[:, -1, :] # 取最后一个时间步 return self.fc(out) model = LSTMModel() criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) for epoch in range(50): model.train() total_loss = 0 for xb, yb in train_loader: pred = model(xb).squeeze() loss = criterion(pred, yb) optimizer.zero_grad() loss.backward() optimizer.step() total_loss += loss.item() if (epoch + 1) % 10 == 0: print(f"Epoch {epoch+1}, Loss: {total_loss/len(train_loader):.6f}")batch_first=True表示输入张量第一维是 batch,这是 PyTorch 里最容易搞混的参数之一,不设的话默认 batch 在第二维,后面取out[:, -1, :]就会取错。out[:, -1, :]是取序列最后一个时间步的隐藏状态,因为我们要预测的是下一天,用最后一步的输出最合理。损失函数用 MSE,优化器用 Adam,学习率 1e-3 是常规起点,如果 loss 震荡厉害就降到 1e-4。
3.2 训练轮数与早停:怎么判断模型有没有学废
50 个 epoch 是保守设置,实际跑的时候我会同时看训练 loss 和测试 loss。如果训练 loss 一直降、测试 loss 先降后升,说明过拟合,需要加 Dropout 或减层数;如果两个 loss 都不降,可能是学习率太大或者数据标准化有问题。早停策略我一般设 patience=10,连续 10 个 epoch 测试 loss 不改善就停。
还有一个容易被忽略的点:LSTM 的初始隐藏状态默认是全零,短序列没问题,但如果你把 window 拉到 200 以上,可以考虑用前一段序列的最终状态作为下一段的初始状态,不过在外汇日线上,这个技巧带来的提升很有限,新手可以先不碰。
4. 预测结果怎么评估:别只看 MSE,方向准确率才是关键
4.1 反标准化与预测值还原
模型输出的是标准化后的收益率,要还原成可解释的数值,得用 scaler 逆变换。下面这段代码把测试集预测结果还原,并计算两个指标:MSE 和方向准确率。
model.eval() with torch.no_grad(): pred_test = model(torch.tensor(X_test, dtype=torch.float32)).squeeze().numpy() # 逆变换回原始收益率尺度 pred_inv = scaler.inverse_transform(pred_test.reshape(-1, 1)).flatten() true_inv = scaler.inverse_transform(y_test.reshape(-1, 1)).flatten() mse = np.mean((pred_inv - true_inv) ** 2) # 方向准确率:预测涨跌方向是否和真实一致 direction_acc = np.mean((pred_inv > 0) == (true_inv > 0)) print(f"MSE: {mse:.8f}, Direction Accuracy: {direction_acc:.4f}")MSE 衡量的是数值误差,但外汇预测里更关心方向对不对。方向准确率能到 55% 以上,说明模型有一点预测能力;如果只有 50% 左右,基本等于随机猜。我跑过多次,EURUSD 日线方向准确率通常在 52% 到 56% 之间波动,超过 60% 的要么是数据泄漏,要么是过拟合。
4.2 可视化对比与误差分布检查
光看数字不够,我习惯把预测值和真实值画在同一张图上,再画一下误差的直方图。如果误差分布明显偏离零均值,说明模型有系统性偏差,可能需要加特征或者换损失函数。这一步用 Matplotlib 几行就能搞定,不展开代码,但强烈建议做。
提示:测试集千万别拿去调参,调参只看验证集。我见过太多人反复在测试集上试参数,最后报出来的指标好看,实际一上模拟盘就原形毕露。
5. 避坑与排查:外汇 LSTM 预测里最容易翻车的 4 个地方
5.1 现象:训练 loss 很低但预测是一条直线
原因:数据没有做平稳化处理,模型学会了直接输出均值。价格序列本身有趋势,LSTM 发现预测昨天收盘价就能让 MSE 很小,于是躺平。解决:一定要转成收益率或做差分,确保输入是平稳序列。
5.2 现象:验证集 loss 比训练集还低
原因:数据泄漏。常见于滑动窗口构造时,训练集和验证集之间有重叠,或者标准化时用了全量数据的均值和方差。解决:标准化参数只在训练集上 fit,再 transform 验证集和测试集;滑动窗口切分时确保训练集和验证集在时间上不重叠。
5.3 现象:方向准确率忽高忽低,换一段数据就崩
原因:外汇市场本身存在 regime shift,某段时间趋势明显,LSTM 表现好;震荡市里就失效。解决:不要指望一个模型通吃所有行情,可以加波动率特征或者用滚动窗口重新训练,但根本上是接受它的局限性。
5.4 现象:GPU 上跑得飞快但结果和 CPU 不一致
原因:PyTorch 在 GPU 上默认使用非确定性算法,LSTM 的 cuDNN 实现有随机性。解决:设置torch.manual_seed和torch.backends.cudnn.deterministic = True,但会牺牲一点速度。如果只是做原型验证,CPU 跑也够用。
6. 进阶技巧:用多特征输入和滚动预测提升实用性
单变量 LSTM 只用了历史收益率,信息量有限。我后来习惯加几个外生特征:一是过去 5 天的波动率(用收益率标准差算),二是星期几的 one-hot 编码(外汇有周一效应),三是简单移动平均线的偏离度。这些特征不需要额外数据源,从 OHLC 里就能算出来。多特征输入时,input_size改成特征数,X的最后一维对应增加。
另一个实用技巧是滚动预测:每次预测完一天,把预测值当作已知输入,继续预测下一天。但这样误差会累积,我一般只滚动 3 到 5 步,再远就没有参考价值了。下面是一个滚动预测的简化写法:
def rolling_predict(model, last_window, steps=5): model.eval() preds = [] window = last_window.copy() # shape: [1, seq_len, features] for _ in range(steps): with torch.no_grad(): p = model(torch.tensor(window, dtype=torch.float32)).item() preds.append(p) # 把预测值拼到窗口末尾,去掉最早一天 new_step = np.array([[[p]]]) # 单特征时这样拼,多特征要补齐其他维度 window = np.concatenate([window[:, 1:, :], new_step], axis=1) return preds这段代码里window[:, 1:, :]是去掉最早的时间步,new_step是预测出的新一步,拼成新的窗口。多特征时new_step的其他特征可以用最近值填充或者简单复制,但会引入额外误差,所以滚动步数不宜多。
最后说一个我自己的习惯:每次跑完模型,不管指标好坏,我都会把数据区间、窗口大小、网络结构、学习率、最终方向准确率记在一个表格里。跑过十几轮之后,你就能看出哪些参数组合在你用的数据上更稳,而不是每次凭感觉调。这个方案值不值得做?如果你是想学 LSTM 在真实序列上的完整流程,它值得;如果你想靠它直接做交易决策,那还需要加上风控、仓位管理和更严谨的回测框架。希望帮到你。
本文还有配套的精品资源,点击获取