创业板是一种专门的证券市场,是对主板市场的重要补充. 它可以为创业型企业、高科技企业等企业提供融资,为没有资质在主板上市的企业提供及时的金 融服务. 2009 年 10 月 30 日,中国创业板正式上市. 2010 年 6 月 1 日,为了更全 面地反应创业板市场的整体表现,深圳证券交易所正式编制和发布创业板指数.

本案例使用的是通过 Python 第三方库 Tushare得到的创业板指数数据集, 时间跨度从2014年3月1日到2017年3月1日. 数据集包括8个特征,主要 包括日期时间、开盘、最高、收盘和成交量等特征,共 729 个样本。

在本案例中,我们首先对数据集进行预处理,对特征日期的格式进行转换。然后,使用隐马尔可夫模型分析影响股票市场的隐含变量,寻找最优的隐含状态序列。

数据预处理

首先,我们加载案例所需要的第三方库,并且屏蔽运行代码产生的warning信息。

In [1]:
import warnings
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.mlab import normpdf

warnings.filterwarnings("ignore")

使用Pandasread_csv函数读入数据,保存在变量stock_data中。

In [2]:
stock_data = pd.read_csv('./input/stock_index.csv')
stock_data.head()
Out[2]:
date open high close low volume price_change p_change
0 2017-03-01 1925.70 1938.68 1931.97 1923.75 8144560.5 4.81 0.25
1 2017-02-28 1926.14 1931.82 1927.16 1918.32 7828431.5 0.39 0.02
2 2017-02-27 1937.56 1943.98 1926.77 1923.53 9418535.0 -11.67 -0.60
3 2017-02-24 1925.10 1939.39 1938.44 1921.30 10466620.0 12.49 0.65
4 2017-02-23 1919.24 1925.95 1925.95 1906.81 9231558.0 5.98 0.31
In [19]:
print u'数据集stock_data的大小为: ', stock_data.shape
数据集stock_data的大小为:  (729, 8)

然后,我们查看stock_data各个特征的信息,包括特征的数据类型,以及是否存在缺失值等。

In [20]:
stock_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 729 entries, 0 to 728
Data columns (total 8 columns):
date            729 non-null object
open            729 non-null float64
high            729 non-null float64
close           729 non-null float64
low             729 non-null float64
volume          729 non-null float64
price_change    729 non-null float64
p_change        729 non-null float64
dtypes: float64(7), object(1)
memory usage: 45.6+ KB

info()返回的信息来看,数据集stock_data没有缺失值,特征时间的数据类型object,具体来说就是字符串类型。接下来,我们将特征时间的类型从字符型转换为datetime类型。

In [21]:
stock_data['date'] = pd.to_datetime(stock_data['date'])
stock_data.head()
Out[21]:
date open high close low volume price_change p_change
0 2017-03-01 1925.70 1938.68 1931.97 1923.75 8144560.5 4.81 0.25
1 2017-02-28 1926.14 1931.82 1927.16 1918.32 7828431.5 0.39 0.02
2 2017-02-27 1937.56 1943.98 1926.77 1923.53 9418535.0 -11.67 -0.60
3 2017-02-24 1925.10 1939.39 1938.44 1921.30 10466620.0 12.49 0.65
4 2017-02-23 1919.24 1925.95 1925.95 1906.81 9231558.0 5.98 0.31
In [22]:
stock_data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 729 entries, 0 to 728
Data columns (total 8 columns):
date            729 non-null datetime64[ns]
open            729 non-null float64
high            729 non-null float64
close           729 non-null float64
low             729 non-null float64
volume          729 non-null float64
price_change    729 non-null float64
p_change        729 non-null float64
dtypes: datetime64[ns](1), float64(7)
memory usage: 45.6 KB

我们把stock_data的行索引设置为stock['date'],这样我们可以根据不同的日期进行数据切片。

In [23]:
stock_data.index = stock_data['date']
In [24]:
stock_data.head()
Out[24]:
date open high close low volume price_change p_change
date
2017-03-01 2017-03-01 1925.70 1938.68 1931.97 1923.75 8144560.5 4.81 0.25
2017-02-28 2017-02-28 1926.14 1931.82 1927.16 1918.32 7828431.5 0.39 0.02
2017-02-27 2017-02-27 1937.56 1943.98 1926.77 1923.53 9418535.0 -11.67 -0.60
2017-02-24 2017-02-24 1925.10 1939.39 1938.44 1921.30 10466620.0 12.49 0.65
2017-02-23 2017-02-23 1919.24 1925.95 1925.95 1906.81 9231558.0 5.98 0.31

比如,我们查询2017年2月份之后的数据,这里需要用到Python的内置日期库datetime

In [26]:
import datetime as dt
stock_data[:dt.datetime(2017, 1, 31)]
Out[26]:
date open high close low volume price_change p_change
date
2017-03-01 2017-03-01 1925.70 1938.68 1931.97 1923.75 8144560.50 4.81 0.25
2017-02-28 2017-02-28 1926.14 1931.82 1927.16 1918.32 7828431.50 0.39 0.02
2017-02-27 2017-02-27 1937.56 1943.98 1926.77 1923.53 9418535.00 -11.67 -0.60
2017-02-24 2017-02-24 1925.10 1939.39 1938.44 1921.30 10466620.00 12.49 0.65
2017-02-23 2017-02-23 1919.24 1925.95 1925.95 1906.81 9231558.00 5.98 0.31
2017-02-22 2017-02-22 1921.55 1925.30 1919.97 1909.12 8582111.00 -1.11 -0.06
2017-02-21 2017-02-21 1896.86 1924.03 1921.08 1895.43 10336038.00 26.12 1.38
2017-02-20 2017-02-20 1884.99 1894.96 1894.96 1876.67 7227233.50 12.09 0.64
2017-02-17 2017-02-17 1896.70 1904.87 1882.87 1881.00 7762939.50 -14.76 -0.78
2017-02-16 2017-02-16 1891.87 1900.97 1897.63 1886.41 6811759.50 5.27 0.28
2017-02-15 2017-02-15 1911.04 1920.29 1892.36 1890.09 8416104.00 -17.04 -0.89
2017-02-14 2017-02-14 1914.14 1918.79 1909.40 1906.41 7436229.00 -4.28 -0.22
2017-02-13 2017-02-13 1902.46 1919.20 1913.68 1894.15 8643653.00 9.00 0.47
2017-02-10 2017-02-10 1913.23 1915.43 1904.68 1900.71 8299373.50 -9.40 -0.49
2017-02-09 2017-02-09 1903.67 1916.41 1914.08 1901.88 8668234.00 10.45 0.55
2017-02-08 2017-02-08 1887.34 1903.63 1903.63 1881.14 7381156.00 14.50 0.77
2017-02-07 2017-02-07 1897.87 1900.35 1889.13 1881.73 6329100.00 -11.32 -0.60
2017-02-06 2017-02-06 1877.14 1903.33 1900.45 1874.71 6581493.00 23.69 1.26
2017-02-03 2017-02-03 1886.97 1888.81 1876.76 1872.70 5384174.50 -9.47 -0.50
2017-02-02 2017-02-02 1887.87 1928.29 1928.05 1887.87 9518.39 41.82 2.22

数据可视化

我们尝试使用折线图,展示stock_data的开盘stock_data[open]和收盘stock_data[closes]

In [28]:
plt.figure() 
stock_data['open'].plot(color = 'r') 
stock_data['close'].plot(color = 'b')

plt.legend(['open price', 'close price'], loc='upper right')

plt.title('Stock Index Trend') 
plt.show()
然后,我们再尝试绘制涨跌幅度的分布图。
In [24]:
returns = stock_data['p_change']
[n, bins, patches] = plt.hist(returns, 100)

mu = np.mean(returns)
sigma = np.std(returns)

x = normpdf(bins, mu, sigma)

plt.plot(bins, x, color='blue', lw=2)
plt.show()

Gaussian HMM

隐马尔科夫模型是一种简单的贝叶斯网模型,属于有向图模型的一种。

3

下面,我们考察指数收盘时的变动情况,也就是说相邻两天stock_data['close']的差值。为了匹配起见,特征datevolume的取值也需要向后推一天,即$len(close) = stock\_data['close'].shape[0] - 1$

In [35]:
diff = np.diff(stock_data['close'])

dates = stock_data['date'][1:]
close = stock_data['close'][1:]
volume = stock_data['volume'][1:]

为了方便可视化,我们在这里选取指数的涨跌情况和成交量两个指标进行考察,即输入HMM模型的数据集为X,大小为(728, 2)

In [39]:
X = np.column_stack([diff, volume])
X.shape
Out[39]:
(728, 2)
In [43]:
from hmmlearn.hmm import GaussianHMM

# 创建Gaussian MMM模型
model = GaussianHMM(n_components=4, covariance_type="diag", n_iter=1000)

## 使用数据集X训练模型
model.fit(X)

# 预测最优的隐含状态序列
hidden_states = model.predict(X)

模型训练完成后,我们查看响应的参数,比如说转移矩阵,每个隐含状态的分布参数。

In [49]:
print u"转移矩阵为: "
print model.transmat_
print ''

print u"每个隐含状态分布的均值和方差: "

for number in range(model.n_components):
    print u"{0}th 隐含状态".format(number)
    print u"均值: ", model.means_[number]
    print u"方差: ", np.diag(model.covars_[number])
    print ''

plt.figure(figsize=(50,10), dpi = 80)
fig, axs = plt.subplots(model.n_components, sharex=True, sharey=True)
colours = cm.rainbow(np.linspace(0, 1, model.n_components))
for i, (ax, colour) in enumerate(zip(axs, colours)):
    # Use fancy indexing to plot data in each state.
    
    mask = hidden_states == i
    ax.plot_date(dates[mask], close[mask], ".-", c=colour)
    ax.set_title(u"{0}th hidden state".format(i))

    # Format the ticks.
    #ax.xaxis.set_major_locator(YearLocator())
    #ax.xaxis.set_minor_locator(MonthLocator())
    fig.tight_layout() 
    ax.grid(True)

plt.show()
转移矩阵为: 
[[  9.04310589e-01   5.20731232e-02   1.83724618e-08   4.36162699e-02]
 [  5.14284091e-02   8.93985353e-01   5.45862377e-02   6.31992689e-32]
 [  3.62623299e-37   5.66925015e-02   9.43307499e-01   2.10849011e-81]
 [  4.11187791e-02   2.42664112e-53   2.90143941e-92   9.58881221e-01]]

每个隐含状态分布的均值和方差: 
0th 隐含状态
均值:  [ -2.22808486e+00   8.85590688e+06]
方差:  [  5.65856533e+02   2.47669737e+12]

1th 隐含状态
均值:  [ -4.34220091e+00   1.40581494e+07]
方差:  [  2.88704019e+03   4.48134656e+12]

2th 隐含状态
均值:  [  4.31283459e+00   1.85360488e+07]
方差:  [  9.17194074e+03   1.88504898e+13]

3th 隐含状态
均值:  [ -4.36531149e-01   5.25855526e+06]
方差:  [  3.91260920e+02   2.54849089e+12]

<matplotlib.figure.Figure at 0x116db0c90>