使用 LSTM 对股票涨跌额进行预测(仅适合作业示例)


前言

在机器学习和深度学习中,时间序列预测是一项常见且具有挑战性的任务。LSTM(Long Short-Term Memory)是一种常用的循环神经网络(RNN)结构,善于处理序列数据与长期依赖问题。因此,许多教程会以 LSTM 为例对股票价格或涨跌进行预测。

然而,需要明确的一点是:本篇教程中的模型仅为示例用途,不具备实际预测价值。实际股市预测需要考虑极其复杂的因素,包括宏观经济、市场情绪、政策风险、突发事件等。本教程简化了许多环节,结果仅能作为参考代码与学习对象,并不建议将其用于真实的投资决策。

教程目标

使用 Pandas 读取本地股票历史数据(CSV 格式)。
使用 MinMaxScaler 对数据进行归一化。
构建简单的 LSTM 模型对股票的“收盘价”进行预测。
训练模型并可视化预测结果。
强调此模型对实际预测的局限性与滞后性,仅作为课程作业或研究演示。

数据准备

在本教程中,我们假设你已经有一份本地 CSV 文件,其中包含股票的历史交易数据。该文件应至少包含如下列:

股票代码|交易日期|开盘价|最高价|最低价|收盘价|昨收价|涨跌额|成交额(千元)
例如:000001.SZ.csv

股票代码,交易日期,开盘价,最高价,最低价,收盘价,昨收价,涨跌额,成交额(千元)
000001.SZ,2019-01-02,8.53,8.60,8.46,8.50,8.53,-0.03,1234567

请根据自己的数据进行相应调整。

代码示例

下面的代码展示了如何使用 LSTM 对股票涨跌额进行预测的基础流程。其中包含数据读取、数据预处理、模型定义与训练、以及预测结果的可视化与误差评估。

import numpy as np
import pandas as pd
import torch
from sklearn.metrics import mean_squared_error, mean_absolute_error
from sklearn.preprocessing import MinMaxScaler
from torch import nn
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt

# 读取本地 CSV 文件
pandas_df = pd.read_csv("000001.SZ.csv", header=0)

# 转换 '交易日期' 为 datetime 类型并按日期排序
pandas_df['交易日期'] = pd.to_datetime(pandas_df['交易日期'], errors='coerce')
pandas_df.sort_values('交易日期', inplace=True)

# 去除空值行
pandas_df = pandas_df.dropna()

# 选择用于训练的特征列
features = ['开盘价', '最高价', '最低价', '收盘价', '昨收价', '涨跌额', '成交额(千元)']
data = pandas_df[features]

# 使用 MinMaxScaler 对特征进行归一化
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(data)
scaled_df = pd.DataFrame(scaled_data, columns=features, index=pandas_df['交易日期'])

# 将数据按时间分割
train_data = scaled_df[scaled_df.index < '2020-01-01']
test_data = scaled_df[scaled_df.index >= '2020-01-01']

# 创建时间序列数据集的函数,使用过去 N 天的数据来预测未来的收盘价
def create_dataset_for_profit(df, _time_step=60):
    X, y = [], []
    for i in range(len(df) - _time_step - 1):
        X.append(df.iloc[i:(i + _time_step)].values)  # 特征
        y.append(df.iloc[i + _time_step, 3])  # 预测目标,选择收盘价("收盘价"的索引是 3)
    return np.array(X), np.array(y)

# 创建时间序列数据集
time_step = 20  # 使用过去 60 天的数据来预测未来一天的收盘价

# 创建训练集和测试集的数据集
X_train, y_train = create_dataset_for_profit(train_data, time_step)
X_test, y_test = create_dataset_for_profit(test_data, time_step)

# 检查训练集和测试集的形状
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)


# 将数据转换为 PyTorch 张量
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)
X_test_tensor = torch.tensor(X_test, dtype=torch.float32)
y_test_tensor = torch.tensor(y_test, dtype=torch.float32).view(-1, 1)

# 创建训练集和测试集的 DataLoader
train_data = TensorDataset(X_train_tensor, y_train_tensor)
test_data = TensorDataset(X_test_tensor, y_test_tensor)

train_loader = DataLoader(train_data, batch_size=32, shuffle=False)
test_loader = DataLoader(test_data, batch_size=32, shuffle=False)


class LSTMModel(nn.Module):
    def __init__(self, input_size, hidden_layer_size, output_size):
        super(LSTMModel, self).__init__()

        # 第一层 LSTM
        self.lstm1 = nn.LSTM(input_size, hidden_layer_size, batch_first=True)

        # 第二层 LSTM
        self.lstm2 = nn.LSTM(hidden_layer_size, hidden_layer_size, batch_first=True)

        # 全连接层
        self.fc = nn.Linear(hidden_layer_size, output_size)

    def forward(self, x):
        # 第一层 LSTM
        out, (hn, cn) = self.lstm1(x)

        # 第二层 LSTM
        out, (hn, cn) = self.lstm2(out)

        # 只取最后一时刻的输出
        out = self.fc(out[:, -1, :])
        return out

# 模型超参数
input_size = len(features)  # 特征数量
hidden_layer_size = 32  # 隐藏层神经元个数
output_size = 1  # 预测收盘价

# 初始化模型
model = LSTMModel(input_size=input_size, hidden_layer_size=hidden_layer_size, output_size=output_size)

# 打印模型结构
print(model)

# 定义损失函数和优化器
criterion = nn.MSELoss()  # 由于目标是预测连续数值(收盘价),我们使用 MSELoss
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# 训练模型
epochs = 100
for epoch in range(epochs):
    model.train()
    running_loss = 0.0
    for inputs, labels in train_loader:
        # 清零梯度
        optimizer.zero_grad()

        # 前向传播
        outputs = model(inputs)

        # 计算损失
        loss = criterion(outputs, labels)

        # 反向传播
        loss.backward()

        # 更新参数
        optimizer.step()

        # 累积损失
        running_loss += loss.item()

    # 每 5 个 epoch 输出一次损失
    if (epoch + 1) % 5 == 0:
        print(f'Epoch [{epoch + 1}/{epochs}], Loss: {running_loss / len(train_loader)}')


# 测试模型
model.eval()
predictions = []
real_values = []

with torch.no_grad():
    for inputs, labels in test_loader:
        outputs = model(inputs)
        predictions.append(outputs.numpy())
        real_values.append(labels.numpy())

# 将预测结果转换为 numpy 数组
predictions = np.concatenate(predictions)
real_values = np.concatenate(real_values)

# 可视化预测结果


# 创建图形,设置图形大小
plt.figure(figsize=(12, 6))

# 绘制真实值和预测值
plt.plot(real_values, color='blue', label='真正的收盘价', linewidth=2, linestyle='-', marker='o', markersize=6, alpha=0.8)
plt.plot(predictions, color='red', label='预测的收盘价', linewidth=2, linestyle='--', marker='x', markersize=6, alpha=0.8)

# 添加标题
plt.title('股票收盘价预测 (LSTM)', fontsize=16, fontweight='bold')

# 设置 x 和 y 轴的标签
plt.xlabel('时间', fontsize=14)
plt.ylabel('收盘价 ', fontsize=14)

# 添加网格,设置透明度
plt.grid(True, which='both', linestyle='--', linewidth=0.5, alpha=0.6)

# 设置 x 轴为时间格式
plt.xticks(rotation=45, fontsize=12)
plt.yticks(fontsize=12)

# 添加图例
plt.legend(fontsize=12)

# 显示图表
plt.tight_layout()  # 自动调整布局
plt.show()

# 计算均方误差(MSE)
mse = mean_squared_error(real_values, predictions)
print(f'Mean Squared Error (MSE): {mse}')

# 计算均方根误差(RMSE)
rmse = np.sqrt(mse)
print(f'Root Mean Squared Error (RMSE): {rmse}')

# 计算平均绝对误差(MAE)
from sklearn.metrics import mean_absolute_error
mae = mean_absolute_error(real_values, predictions)
print(f'Mean Absolute Error (MAE): {mae}')


结果展示

在这里插入图片描述

Mean Squared Error (MSE): 6.731566827511415e-05
Root Mean Squared Error (RMSE): 0.008204612880945206
Mean Absolute Error (MAE): 0.006247698795050383

从图像可以看出:
股票价格变化复杂且多变,仅使用过去的价格数据难以捕捉未来的行情变动。模型在实际使用中通常会表现出滞后性与不稳定性,预测结果往往与现实走势有较大偏差。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值