当前位置:   article > 正文

基于情感分析的LSTM预测股票走势_情感分析股票价格预测

情感分析股票价格预测

目录

一.LSTM

二.股票数据

 三.文本数据

 四.文本数据情感分析

五.数据合并,归一化分析

 六.对变量进行相关性分析

 七.基于lstm进行股票价格预测

一.LSTM

        在金融时间序列分析中,长短期记忆网络(LSTM)因其能够捕捉数据中的长期依赖关系而被广泛采用。本文就不在此进行多说,相关文章可以借鉴<如何从RNN起步,一步一步通俗理解LSTM>,LSTM的主要优势在于它的门控机制,包括遗忘门、输入门和输出门,如图2所示

二.股票数据

        研究采集2000年至2024年的浦发银行股票交易数据,涵盖开盘价、收盘价、最高价、最低价、成交量及成交金额等。爬取东方财经网的浦发银行评论共3479条,从国家统计局与Choice金融终端共收集到2000-2024年的股票价格数据共3393条数据,如图下所示 :

open

close

high

low

volume

sum

2010-02-03

19.31

19.89

19.93

19.01

76493075

1.5E+09

2010-02-04

19.61

19.66

19.89

19.6

46162099

9.09E+08

2010-02-05

19.2

19.42

19.68

19.16

43895954

8.5E+08

2010-02-08

19.45

19.22

19.5

19.08

37230982

7.17E+08

2010-02-09

19.2

19.42

19.49

19.17

26496190

5.14E+08

…………………………………………………………………………………………………………………………………………

2024-01-26

6.78

6.87

6.91

6.75

70997933

4.85E+08

2024-01-29

6.86

6.9

6.97

6.85

55269921

3.82E+08

2024-01-30

6.9

6.83

6.95

6.82

40737046

2.8E+08

2024-01-31

6.81

6.83

6.88

6.74

45661999

3.11E+08

2024-02-01

6.83

6.8

6.88

6.78

44252421

3.02E+08

2024-02-02

6.82

6.84

6.92

6.72

59482913

4.07E+08

 三.文本数据

        文本数据则包括新闻报道、金融报告以及社交媒体上的投资讨论,这些数据主要来自于东方财富网股吧,数据源丰富了本研究的情感分析和文本挖掘部分。为了确保数据的有效性和可靠性,所有文本数据均从公开可信的渠道收集,并通过程序自动化工具每日更新和存储数据。部分数据如下:

 四.文本数据情感分析

        案例采用基于机器学习的情感分析方法,主要利用了针对中文文本处理的工具SnowNLP以评估金融文本的情绪倾向。SnowNLP的核心功能是情感分析,该功能基于朴素贝叶斯分类器实现。

        在本案例中,代码通过调用SnowNLP(x).sentiments对评论标题进行情感得分计算,鉴于每天的评论数量众多,本研究对这些得分进行平均处理,以获得每日的情绪综合评分。,此方法返回一个介于 0(完全消极)到 1(完全积极)之间的分数。得分反映了文本内容的情绪倾向,通过设置阈值(0.5),将情感得分转化为具体的情绪类别(正面或负面),进而进行进一步的分析和统计。这种方法对于分析时间序列数据中的情绪变化尤其有用,还加深了我们对市场情绪变化的理解,使得预测更具前瞻性和适应性。代码如下所示:

  1. import pandas as pd
  2. from snownlp import SnowNLP
  3. # 加载数据
  4. data = pd.read_excel('浦发银行.xlsx')
  5. # 确保时间列存在且格式正确。由于时间列包括具体时间,因此需要匹配包括时间的格式
  6. data['update_time'] = pd.to_datetime(data['update_time'], format='%Y/%m/%d %H:%M', errors='coerce')
  7. # 确保时间和标题都不是空值
  8. data = data.dropna(subset=['update_time', 'title'])
  9. # 情感分析,计算情感得分
  10. data['sentiment_score'] = data['title'].apply(lambda x: SnowNLP(x).sentiments)
  11. # 根据情感得分定义情感类型
  12. threshold = 0.5
  13. data['sentiment_type'] = data['sentiment_score'].apply(lambda x: 'positive' if x >= threshold else 'negative')
  14. # 对每天的数据分组并计算positive和negative的平均值
  15. result = data.groupby(data['update_time'].dt.date).agg({
  16. 'title': 'count', # 计算每天的标题数量
  17. 'sentiment_score': ['mean', 'count'], # 计算每天的平均情感得分及其数量
  18. 'sentiment_type': lambda x: (x == 'positive').mean() # 计算正面情感的比例
  19. }).reset_index()
  20. # 重命名列便于理解
  21. result.columns = ['Date', 'Title Count', 'Average Sentiment Score', 'Sentiment Count', 'Positive Sentiment Ratio']
  22. # 创建新的列pos和neg
  23. result['Positive'] = result.apply(lambda x: x['Average Sentiment Score'] if x['Positive Sentiment Ratio'] >= 0.5 else None, axis=1)
  24. result['Negative'] = result.apply(lambda x: x['Average Sentiment Score'] if x['Positive Sentiment Ratio'] < 0.5 else None, axis=1)
  25. # 使用0.5填充空缺值(根据需要可以调整这个值)
  26. result['Positive'].fillna(0.5, inplace=True)
  27. result['Negative'].fillna(0.5, inplace=True)
  28. # 保存到Excel文件
  29. result.to_excel('分析结果.xlsx', index=False)

日期

积极情绪指数

日期

积极情绪指数

2018-12-15

0.8553

2018-11-30

0.5903

2018-12-16

0.9605

2018-12-01

0.8363

2018-12-19

0.6188

2018-12-02

0.9439

2018-12-21

0.5056

2018-12-04

0.7746

2018-12-27

0.9627

2018-12-06

0.7256

2018-12-29

0.7180

2018-12-08

0.7935

2018-12-30

0.6807

2018-12-10

0.8210

五.数据合并,归一化分析

  1. import pandas as pd
  2. # Load both Excel files
  3. result_path = '/mnt/data/分析结果.xlsx'
  4. data_result = pd.read_excel(result_path)
  5. bank_data_path = '/mnt/data/浦发银行.xlsx'
  6. data_bank = pd.read_excel(bank_data_path)
  7. # Ensure both date columns are in the same datetime format
  8. data_bank['日期'] = pd.to_datetime(data_bank['日期'], errors='coerce')
  9. data_result['Date'] = pd.to_datetime(data_result['Date'], errors='coerce')
  10. # Attempt to merge again
  11. merged_data_final = pd.merge(data_result, data_bank, left_on='Date', right_on='日期', how='inner')
  12. # Save the correctly merged data to a new Excel file
  13. final_merged_file_path = '/mnt/data/final_merged_data.xlsx'
  14. merged_data_final.to_excel(final_merged_file_path, index=False)

 六.对变量进行相关性分析

        分析结果如下,由此可见,pos.neg与股票技术的相关性很强

 七.基于lstm进行股票价格预测

  1. #!/usr/bin/python3
  2. # -*- encoding: utf-8 -*-
  3. from this import s
  4. import matplotlib.pyplot as plt
  5. import numpy as np
  6. import tushare as ts
  7. import pandas as pd
  8. import torch
  9. from torch import nn
  10. import datetime
  11. import time
  12. from sklearn.metrics import r2_score # Import r2_score from sklearn
  13. DAYS_FOR_TRAIN = 10
  14. class LSTM_Regression(nn.Module):
  15. """
  16. 使用LSTM进行回归
  17. 参数:
  18. - input_size: feature size
  19. - hidden_size: number of hidden units
  20. - output_size: number of output
  21. - num_layers: layers of LSTM to stack
  22. """
  23. def __init__(self, input_size, hidden_size, output_size=1, num_layers=2):
  24. super().__init__()
  25. self.lstm = nn.LSTM(input_size, hidden_size, num_layers)
  26. self.fc = nn.Linear(hidden_size, output_size)
  27. def forward(self, _x):
  28. x, _ = self.lstm(_x) # _x is input, size (seq_len, batch, input_size)
  29. s, b, h = x.shape # x is output, size (seq_len, batch, hidden_size)
  30. x = x.view(s * b, h)
  31. x = self.fc(x)
  32. x = x.view(s, b, -1) # 把形状改回来
  33. return x
  34. def create_dataset(data, days_for_train=5) -> (np.array, np.array):
  35. """
  36. 根据给定的序列data,生成数据集
  37. 数据集分为输入和输出,每一个输入的长度为days_for_train,每一个输出的长度为1。
  38. 也就是说用days_for_train天的数据,对应下一天的数据。
  39. 若给定序列的长度为d,将输出长度为(d-days_for_train+1)个输入/输出对
  40. """
  41. dataset_x, dataset_y = [], []
  42. for i in range(len(data) - days_for_train):
  43. _x = data[i:(i + days_for_train)]
  44. dataset_x.append(_x)
  45. dataset_y.append(data[:, 2][i + days_for_train])
  46. return (np.array(dataset_x), np.array(dataset_y))
  47. # 定义一个函数来对DataFrame的指定列进行归一化
  48. def normalize_columns(df, columns_to_normalize):
  49. for column in columns_to_normalize:
  50. df[column] = (df[column] - df[column].min()) / (df[column].max() - df[column].min())
  51. return df
  52. if __name__ == '__main__':
  53. t0 = time.time()
  54. data = pd.read_csv('浦发银行单一.csv', index_col='Date') # 读取文件
  55. print(data.shape)
  56. data = data.astype('float32').values # 转换数据类型
  57. data = data
  58. print(data.shape)
  59. print(data)
  60. for i in range(data.shape[1]):
  61. plt.plot(data[:, i])
  62. plt.savefig('data.png', format='png', dpi=200)
  63. plt.close()
  64. # 将价格标准化到0~1
  65. max_value = np.max(data[:, i])
  66. min_value = np.min(data[:, i])
  67. data[:, i] = (data[:, i] - min_value) / (max_value - min_value)
  68. print(data[:, i])
  69. dataset_x, dataset_y = create_dataset(data, DAYS_FOR_TRAIN)
  70. train_size = int(len(dataset_x) * 0.70)
  71. train_x = dataset_x[:train_size]
  72. train_y = dataset_y[:train_size]
  73. train_x = train_x.reshape(-1, 1, DAYS_FOR_TRAIN * 6)
  74. train_y = train_y.reshape(-1, 1, 1)
  75. train_x = torch.from_numpy(train_x)
  76. train_y = torch.from_numpy(train_y)
  77. #
  78. model = LSTM_Regression(6 * DAYS_FOR_TRAIN, 10, output_size=1, num_layers=4)
  79. model_total = sum([param.nelement() for param in model.parameters()]) # 计算模型参数
  80. print("Number of model_total parameter: %.8fM" % (model_total / 1e6))
  81. train_loss = []
  82. loss_function = nn.MSELoss()
  83. optimizer = torch.optim.Adam(model.parameters(), lr=1e-2, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
  84. for i in range(350):
  85. out = model(train_x)
  86. loss = loss_function(out, train_y)
  87. loss.backward()
  88. optimizer.step()
  89. optimizer.zero_grad()
  90. train_loss.append(loss.item())
  91. # 将训练过程的损失值写入文档保存,并在终端打印出来
  92. with open('log.txt', 'a+') as f:
  93. f.write('{} - {}\n'.format(i + 1, loss.item()))
  94. if (i + 1) % 1 == 0:
  95. print('Epoch: {}, Loss:{:.5f}'.format(i + 1, loss.item()))
  96. # 画loss曲线
  97. plt.figure()
  98. plt.plot(train_loss, 'b', label='loss')
  99. plt.title("Train_Loss_Curve")
  100. plt.ylabel('train_loss')
  101. plt.xlabel('epoch_num')
  102. plt.savefig('loss.png', format='png', dpi=200)
  103. plt.close()
  104. # torch.save(model.state_dict(), 'model_params.pkl') # 可以保存模型的参数供未来使用
  105. t1 = time.time()
  106. T = t1 - t0
  107. print('The training time took %.2f' % (T / 60) + ' mins.')
  108. tt0 = time.asctime(time.localtime(t0))
  109. tt1 = time.asctime(time.localtime(t1))
  110. print('The starting time was ', tt0)
  111. print('The finishing time was ', tt1)
  112. # for test
  113. model = model.eval() # 转换成测试模式
  114. # model.load_state_dict(torch.load('model_params.pkl')) # 读取参数
  115. # 注意这里用的是全集 模型的输出长度会比原数据少DAYS_FOR_TRAIN 填充使长度相等再作图
  116. dataset_x = dataset_x.reshape(-1, 1, DAYS_FOR_TRAIN * 6) # (seq_size, batch_size, feature_size)
  117. dataset_x = torch.from_numpy(dataset_x)
  118. pred_test = model(dataset_x) # 全量训练集
  119. # 的模型输出 (seq_size, batch_size, output_size)
  120. pred_test = pred_test.view(-1).data.numpy()
  121. pred_test = np.concatenate((np.zeros(DAYS_FOR_TRAIN), pred_test)) # 填充0 使长度相同
  122. assert len(pred_test) == len(data)
  123. # Calculate R^2 score
  124. r2 = r2_score(data[:, 3], pred_test)
  125. print(f'R^2 score: {r2:.4f}')
  126. plt.plot(pred_test, 'r', label='prediction')
  127. plt.plot(data[:, 3], 'b', label='real')
  128. plt.plot((train_size, train_size), (0, 1), 'g--') # 分割线 左边是训练数据 右边是测试数据的输出
  129. plt.legend(loc='best')
  130. plt.title(f'Prediction vs Real (R^2 score: {r2:.4f})')
  131. plt.savefig('result.png', format='png', dpi=200)
  132. plt.show()
  133. plt.close()

        为了维护稳定,对每列进行单独归一化处理,防止量纲问题,将每列的值转换到0-1的范围。鉴于LSTM需要序列数据作为输入,使用滑动窗口的方法从时间序列中提取特征,在代码中,days_for_train是窗口代码,即使用days_for_train 天的数据来预测下一天的数据。这里的_x是包含连续几天的数据特征。

        数据整形(reshape):输入的数据应该是(序列长度seq_len,批大小batch_size,特征数量input_size),这一特点是为了符合pytorch的要求,每个训练样本都被处理为单独的一个小批量,而每个批量都包含了序列的全部时间与全部特征,使得LSTM有效的学习数据序列的时间依赖性。

        批处理与模型输入:将数据转化为pytorch的tensor输入模型,对参数进行调整,最大化捕捉特征关系,提高拟合度并且避免过度拟合。随后做对比分析,并用r-square来做拟合程度评估 

 

         在原有的技术指标之上加入了pos与neg的变量,R^2 表示有预测值与真实值的拟合程度,越接近1表示拟合度越高,再加入pos与neg变量之后,拟合度由0.8186提高到了0.8824,可知pos与neg对预测结果的影响很大。本文LSTM代码借鉴LSTM-代码讲解(股票预测)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/819354
推荐阅读
相关标签
  

闽ICP备14008679号