隐马尔可夫模型|股票预测
内容:
- 内容回顾
- HMM总结
- HMM股票预测
内容概要
HMM知识总结
更多回顾:
更多公式推导:
HMM股票预测模型
- 数据准备
下载地址:
2. 数据设定
- 目的:通过历史数据进行建模,通过模型进行对数据分析和预测
- HMM时间轴:以日为时间的离散状态系统,每一天是一个HMM的状态结点
- 可见层特征: 读取数据文件中的两个重要数据作为可见层特征:收盘涨跌值,交易量,由于这些属性是连续的,所以选用高斯模型。
- 隐藏层状态;定义为三种状态:对应涨,平,跌
- 预测方式:通过查看隐藏层转换矩阵的转换概率,根据最后一个结点的隐藏状态预测未来一天的涨跌可能
3. 数据读取
- 把CSV.csv历史文件下载以后上传到google colab的云盘中
- 打开google colab notebook,配置工作环境以及工作文件目录
from google.colab import drive
drive.mount('/content/drive')
import os
print(os.getcwd())
os.chdir("/content/drive/My Drive/Colab Notebooks")
# path = "/content/gdrive/My Drive/sample"
# os.chdir(path)
# 返回上一级目录
print(os.getcwd())
# os.chdir('../')
4. 程序包导入
- 安装hmmlearn依赖库
import datetime
import numpy as np
from matplotlib import cm, pyplot as plt
from matplotlib.dates import DayLocator
from hmmlearn.hmm import GaussianHMM # 选择的HMM模型
5. 从数据文件读入“CSV.csv数据” 抽取时间,以及对应的收盘价和交易量
def load_sample_stock():
import csv
ret = []
with open('CSV.csv', newline='') as csvfile:
reader = csv.DictReader(csvfile)
for idx, row in enumerate(reader):
ret.append((datetime.datetime.strptime(row["Date"], "%Y-%m-%d"),
float(row["Close"]), float(row["Volume"])))
return ret
quotes = load_sample_stock()
注意:只是读取到了一个list当中,3列
6. 逐行读取需要的特征到一个列表中,再转换为np.array数组
dates = np.array([q[0] for q in quotes], dtype=datetime.datetime)[1:]
print(dates)
close_v = np.array([q[1] for q in quotes])
print(close_v)
volume = np.array([q[2] for q in quotes])[1:]
print(volume)
7. 计算收盘涨跌值
因为第一天的数据会被用来计算读二天的收盘涨跌值特征样本,所以马尔可夫链是从第二天的数据样本开始训练的。计算涨跌值的特征:
# np.diff()是用来计算数组中前后两个值差额的函数
diff = np.diff(close_v)
print(diff)
print("原来收盘值的长度: {}, 现在的收盘涨跌值的长度: {}".format(len(close_v), len(diff)))
8. 将涨跌额和交易量合并为一个二维数组
X= np.column_stack([diff, volume])
print(X)
9. 初始化GaussianHMM模型,并使用X变量进行训练(可见层的数据特征样本)
- n_components=3:表示指定使用了三个隐藏层状态
- covariance_type='diag': 定义协方差矩阵为对角线矩阵,即每个特征的高斯分布有自己的方差参数,相互之间不相关
- n_iter=10000: 定义了Baum-Welch无监督学习的最大迭代次数
- fit(X): 函数完成训练后,model已经成为了拟合该股票涨跌情况的HMM模型
# 初始化一个GaussianHMM模型
model = GaussianHMM(n_components=3, covariance_type='diag', n_iter=10000)
# 用X_train变量进行模型训练
model.fit(X)
模型参数的结果分析
- 均值矩阵
- 由于使用的是Baum-Welch的无监督训练法,即在训练中没有给出隐含状态的任何信息
- 所以需要在训练后观察均值矩阵的具体数值才能知道每个隐含状态的含义
# 均值矩阵
# 由于使用的是Baum-Welch的无监督训练法,即在训练中没有给出隐含状态的任何信息
# 所以需要在训练后观察均值矩阵的具体数值才能知道每个隐含状态的含义
# 打印均值矩阵
print("Model means: ")
print(model.means_)
# 结论:
# 1: 矩阵共三行,每一行代表隐藏层的结点的一种状态(平,涨, 跌),每一行的两个元素分别带表:涨跌值的均值和交易量的均值
# 由于系统的目标是预测涨跌幅值:只看第一列的数据:状态0均值是-0.0075(接近于零)得知该状态为平;
# 状态1均值为0.15(涨1毛多)得知该状态为涨;
# 状态3均值为-0.049(跌4分多)得知该状态为跌;
2. 协方差矩阵
- 特征值在每个隐藏层状态下的协方差矩阵
- 这里我们只考虑涨跌幅特征的协方差(每个二维矩阵的左上角)
- 状态"平"的方差为0.111,可信度居中
- 状态“涨”的方差为1.317,是三个状态中的方差最大值,即该状态的变化范围较大,最不可信
- 状态“跌”的方差为0.074,是三个状态中的方差最小值,也就是说该状态的预测非常可信
- 有了u和sigma我们可以利用正态分表可以去估计涨跌幅的置信区间
# 协方差矩阵
print("Covariance means: ")
print(model.covars_)
# 三个协方差矩阵,分别对应隐藏层状态:'平','涨', '跌'.
# 每个协方差矩阵的非对角线元素都为0,这对应了对GaussianHMM初始化参数convariance_type='diag'的配置
3. 转换概率矩阵
# 转换概率矩阵:三个隐藏层的状态转移概率输出表
print("Transition matrix: ")
print(model.transmat_)
# 三行依旧代表三个隐含层的状态: 平,涨,跌
# 第一行最大数值:0.7415,因此"平"倾向于保持自己的状态,即第二天仍旧为平的概率大
# 第二行的最大数值:0.5756,得知"涨"后,第二天倾向与变为"涨",但同时也有不小的概率是会"平"(0.4234),而涨后跌的可能性比较小
# 第三行最大数值;0.7855,得知"跌"后,第二天还是"跌"的可能性比较大。
# 根据转换概率我们可以评价该支股票接下来适合短期抄底
4. 可视化短线预测
目的:编写程序预测最后一天的涨跌可能。
- 可视化预测
- 选取最近一段数据调用.predict()方法预测该段数据的隐藏状态
# 预测
X1 = X[-26:]
print(X1)
dates = dates[-26:]
print(dates)
close_v = close_v[-26:]
hidden_states = model.predict(X1)
# 保存每种隐藏层状态下的特征的均值和方差;
predict_means = []
predict_vars = []
for idx, hid in enumerate(range(model.n_components)):
comp = np.argmax(model.transmat_[idx])
predict_means.append(means_[comp])
predict_vars.append(vars_[comp])
# print(X)
print(hidden_states)
print(model.means_)
print(model.covars_)
print("done")
# 使用matplot 分别画出这段日期的状态图,对于不同预测状态使用不同的端点形状和颜色进行示意
fig, axs = plt.subplots(model.n_components+1, sharex = True, sharey = True)
for i, ax in enumerate(axs[:-1]):
ax.set_title("{0}the hidden state".format(i))
mask = hidden_states == i
yesterday_mask = np.concatenate(([False], mask[:-1]))
if len(dates[mask]) <=0:
continue
if predict_means[i] > 0.01:
# 上涨预测, 端点形状是上三角,用红色
ax.plot_date(dates[mask], close_v[mask], "^", c="#FF0000")
elif predict_means[i] < -0.01:
# 下跌预测, 端点形状是下三角,用绿色
ax.plot_date(dates[mask], close_v[mask], "v", c="#00FF00")
else:
# 平,端点形状是加号,用黑色
ax.plot_date(dates[mask], close_v[mask], "+", c="#000000")
# Format the tick
ax.xaxis.set_minor_locator(DayLocator())
ax.grid(True)
ax.legend(["Mean: %0.3f\nvar: %0.3f"%(predict_means[i], predict_vars[i])], loc='center left', bbox_to_anchor =[1, 0.5])
# 最后用一个子图打印出这段日期的真实走势用做对比
axs[-1].plot_date(dates, close_v, "-", c = "#000000")
axs[-1].grid(True)
# box = axs[-1].get_position()
# axs[-1].set_position([box.x0, box.y0, box.width*0.8, box.height])
ax.xaxis.set_minor_locator(DayLocator())
# 调整格式,并显示作图
fig.autofmt_xdate()
plt.subplots_adjust(left=None, bottom=None, right=0.75, top=None, wspace=None, hspace=0.43)
plt.show()
对于股票预测问题:涉及到很多专业领域的东西,特征样本的清洗等需要很多知识,这里只是找了两个特征,预测了一下,准确了,但是肯定不咋地。所以还是一句话:股市有风险,风险还很大。
代码下载地址: