隐马尔科夫链模型对于沪深300指数建模的进一步研究

  • 1 研究概述

    在Force Avatar”HMM在股票上的简单应用”一文中,利用隐马尔科夫链模型(Hidden Markov Model, HMM)对沪深300指数预测建模,取得了较为理想的效果。本文基于该文章中的源代码,对模型所使用的可观测序列的正态性进行分析和修正,并对隐藏状态数目的选择作了简单的探讨。分析结果表明,对于可观测序列正态性的修正,及合理的隐藏状态数目选择,能够显著提升模型的预测能力。

    2 问题分析

    2.1 可观测序列的正态性分析和处理

    在Force Avatar的文章中,选取了三个可观测序列(每日最高最低价格的对数差值, 每5日的指数对数收益差和每5日的指数成交量的对数差)进行可观测序列进行建模,并假设这三个指标服从正态分布。

    在基于正态分布可观测序列的HMM模型中,对于参数的估计(概率转移矩阵,概率发射矩阵)和隐藏状态的标注是通过极大似然估计实现的,其最大似然函数表达式中包含了多元正态分布的概率密度函数。如果所选择的可观测序列偏离正态分布,将导致参数估计有偏,和对于隐藏状态的标注出现较高的错误率。

    通过直方图观察所选的三个可观测序列的分布,发现第一个可观测序列(每日最高最低价格的对数差值)较为显着地偏离正态分布。因此对其进行Box-Cox Transformation数据转换的方法,使其更接近正态分布。

    此外,在经验协方差矩阵计算过程中,具有较大方差的可观测序列将具有较大的权重。如果可观测序列的单位不同,其方差的差距可能达到数个数量级。这里为了保证所选的可观测序列在建模中具有接近的权重,对其分布进行标准化(均值调整为0,标准差调整为1个单位)。

    2.2 隐藏状态数量的选择

    在广发证券的一篇HMM模型研究报告中,隐藏状态的数目设定为3个。
    探寻西蒙斯投资之道:基于 HMM 模型的周择时策略研究

    文中并未提及选择三个隐藏状态的原因。在Force Avatar所提到的HMM模型所关心的三个问题中,隐藏状态均假设为已知。由于目前尚未查到关于隐藏状态数目选择的相关研究文献,目前对于隐藏状态对于建模的影响为个人推测:

    以Force Avatar一文中用掷骰子作为例子解释HMM模型,其中骰子的数目为隐藏状态数目,抛掷结果为可观测序列。如果我们用3颗不同的骰子抛掷获得一个观测序列,但在建模中我们假设隐藏状态数目为5个,因为HMM无法识别隐藏状态的合理数目,因此对于任意给定的隐藏状态数目和初始分布,模型总能通过反复迭代对参数进行估计,和对隐藏状态进行标注。因此我们最终得到的5个隐藏状态中,有3颗将会对应真骰子,而另外2颗则是这3颗真骰子线性组合得到的假骰子。

    由于这2颗假骰子是真骰子的线性组合,因此它们可能和真骰子以大致相等的概率导致同一观测序列,从而导致隐藏状态的标准错误,和概率转移矩阵和概率发射矩阵参数估计的不稳定。

    对于证券市场来说,隐藏状态有无穷多个。在建模时,我们希望每个隐藏状态有相互独立的特征,保证我们能对市场状态有清楚的判断,作出投资决策。合理隐藏状态的选取是一个较为复杂的问题,HMM模型对于隐藏状态标注的准确度取决于可观测序列的数目,数据的数量和质量,迭代次数,数据分布的稳定性等多种因素。

    大体上,我们可以认为市场有三种显著不同的特征:快速上升,快速下跌和震荡。直觉上,震荡这种状态最为复杂,可能有不同的亚隐藏状态,例如震荡上升,震荡下跌等等复杂的的市场状态。而在Force Avatar得到的结果中,震荡行情中出现多种状态的标注,导致我们无法准确判断隐藏状态对应的含义。因此,在目前的建模中,基于以下理由把隐藏状态设定为3个:

    1. 希望模型能够正确标注快速上升,快速下跌和震荡三种市场状态,并不期望目前的简单模型能够对震荡行情中的复杂状态准确标注。

    2. 震荡行情所包含的隐藏状态的标注较为复杂,标注的准确率较低,考虑到交易中实际产生的交易费用,我们希望模型标准的隐藏状态的含义是清晰和准确的,而不希望基于不清晰的隐藏状态含义进行交易。

    3 结果讨论

    基于以上讨论,在以下的源代码中,我们分别对未修正的可观测序列和修正后的可观测序列进行建模。我们先对3隐藏状态HMM模型和6隐藏状态HMM模型进行对比。根据3个隐藏状态得到的模型进行交易,在2013年初左右收益率即达到100%,2014年初达到150%,根据6个隐藏状态得到的模型进行交易,在2015年初左右收益率达到100%,2015年中达到150%;此外,在2015年下半年的股灾中,根据3隐藏状态模型进行交易,收益率仍然维持在200%左右,而根据3隐藏状态模型进行交易,收益率仍然跌至170%左右

    《隐马尔科夫链模型对于沪深300指数建模的进一步研究》

    《隐马尔科夫链模型对于沪深300指数建模的进一步研究》

    然后,我们对基于未修正可观测序列的3隐藏状态HMM模型和基于修正可观测序列的3隐藏状态HMM模型进行对比。这两个HMM模型中均能对快速上升,快速下跌和震荡行情三种市场状态进行较为准确的标准。基于修正可观测序列的HMM模型在2015年上半年的牛市中收益率达到300%,远高于基于未修正可观测序列的HMM模型的收益率;且在2015年下半年的股灾中,基于修正可观测序列的模型表现出色,在稍微回调以后重新上升,在2015年年底和其峯值大致持平。

    《隐马尔科夫链模型对于沪深300指数建模的进一步研究》

    《隐马尔科夫链模型对于沪深300指数建模的进一步研究》

    3 研究展望

    在本文中,我们对于HMM模型的隐藏状态数目选择进行了讨论,并对所选的可观测序列正态性进行了分析和处理,调整以后的HMM对于沪深300的测试结果显著改善。

    在下一步的研究中,可以对模型的以下方面作进一步改善:

    1. 对建模时选择的2000步迭代合理性进行检验。理想的迭代数目应该使参数估计和隐藏状态标注的准确性收敛。

    2. 选择更多的尽可能正交的,符合正态分布可观测序列序列向量作为指标进行建模。理论上,相互正交的可观测序列向量比存在线性相关的可观测序列向量包含更多的市场信息,且和HMM模型中的观测独立性假设一致,同时能够降低经验协方差矩阵非对角元素估计的误差。

    3. 对符合正态分布的可观测序列进行方差分析,判断其可合理使用的时间范围,并通过检验结果进一步了解市场结构的变化。

    4. 通过交叉验证(Cross-Validation)进一步确定合理的隐藏状态数目。

    最后,在此对于Force Avatar 分享自己的研究策略提出致谢。

  • HMM_Model_Modified_3_Hidden_States.ipynb

from hmmlearn.hmm import GaussianHMM

import numpy as np

from matplotlib import cm, pyplot as plt

import matplotlib.dates as dates

import pandas as pd

import datetime

from scipy import stats # To perfrom box-cox transformation

from sklearn import preprocessing # To center and standardize the data.

# Source Code from previous HMM modeling

# Note that numbers of hidden states are modified to be 3, instead of 6.

beginDate = ‘2005-01-01’

endDate = ‘2015-12-31’

n = 3 # Hidden states are set to be 3 instead of 6

data = get_price(‘CSI300.INDX’,start_date=beginDate, end_date=endDate,frequency=’1d’)

data[0:9]

volume = data[‘TotalVolumeTraded’]

close = data[‘ClosingPx’]

logDel = np.log(np.array(data[‘HighPx’])) – np.log(np.array(data[‘LowPx’]))

logDel

logRet_1 = np.array(np.diff(np.log(close)))#这个作为后面计算收益使用

logRet_5 = np.log(np.array(close[5:])) – np.log(np.array(close[:-5]))

logRet_5

logVol_5 = np.log(np.array(volume[5:])) – np.log(np.array(volume[:-5]))

logVol_5

logDel = logDel[5:]

logRet_1 = logRet_1[4:]

close = close[5:]

Date = pd.to_datetime(data.index[5:])

  • 通过直方图来观察所选指标(可观测序列)分布的正态性。其中第一个指标(每日最高最低价格的对数差值)明显偏离正态分布

# the histogram of the raw observation sequences

n, bins, patches = plt.hist(logDel, 50, normed=1, facecolor=’green’, alpha=0.75)

plt.show()

n, bins, patches = plt.hist(logRet_5, 50, normed=1, facecolor=’green’, alpha=0.75)

plt.show()

n, bins, patches = plt.hist(logVol_5, 50, normed=1, facecolor=’green’, alpha=0.75)

plt.show()

  • 《隐马尔科夫链模型对于沪深300指数建模的进一步研究》
  • 《隐马尔科夫链模型对于沪深300指数建模的进一步研究》
  • 《隐马尔科夫链模型对于沪深300指数建模的进一步研究》

通过Box-Cox Transformation来对第一个指标进行调整,使其更接近正态分布。

同时对三个指标的分布进行标准化(调整其均值为0,且标准差调整为1),保证指标在参数估计中具有大致相等的权重

# Box-Cox Transformation of the observation sequences

boxcox_logDel, _ = stats.boxcox(logDel)

# Standardize the observation sequence distribution

rescaled_boxcox_logDel = preprocessing.scale(boxcox_logDel, axis=0, with_mean=True, with_std=True, copy=False)

rescaled_logRet_5 = preprocessing.scale(logRet_5, axis=0, with_mean=True, with_std=True, copy=False)

rescaled_logVol_5 = preprocessing.scale(logVol_5, axis=0, with_mean=True, with_std=True, copy=False)

# the histogram of the rescaled observation sequences

n, bins, patches = plt.hist(rescaled_boxcox_logDel, 50, normed=1, facecolor=’green’, alpha=0.75)

plt.show()

n, bins, patches = plt.hist(rescaled_logRet_5, 50, normed=1, facecolor=’green’, alpha=0.75)

plt.show()

n, bins, patches = plt.hist(rescaled_logVol_5, 50, normed=1, facecolor=’green’, alpha=0.75)

plt.show()

  • 《隐马尔科夫链模型对于沪深300指数建模的进一步研究》 《隐马尔科夫链模型对于沪深300指数建模的进一步研究》 《隐马尔科夫链模型对于沪深300指数建模的进一步研究》 . . . In [55]: # Observation sequnces matrix 
    # Rescaled observation sequences matrix 
    rescaled_A =np.column_stack([rescaled_boxcox_logDel, rescaled_logRet_5, rescaled_logVol_5]) 

    . . .

    对于未修正的指标进行隐马尔科夫链建模。

    对于未修正的指标进行隐马尔科夫链建模。

    In [64]:

    x

     

    # HMM modeling based on raw observation sequences
    model = GaussianHMM(n_components= 3, covariance_type="full", n_iter=2000).fit([A])
    hidden_states = model.predict(A)
    hidden_states
    plt.figure(figsize=(25, 18)) 
    for i in range(model.n_components):
        pos = (hidden_states==i)
        plt.plot_date(Date[pos],close[pos],'o',label='hidden state %d'%i,lw=2)
        plt.legend(loc="left")

    《隐马尔科夫链模型对于沪深300指数建模的进一步研究》 . . . In [67]:  

    for i in range(3):
        pos = (hidden_states==i)
        pos = np.append(0,pos[:-1])#第二天进行买入操作
        df = res.logRet_1
        res['state_ret%s'%i] = df.multiply(pos)
        plt.plot_date(Date,np.exp(res['state_ret%s'%i].cumsum()),'-',label='hidden state %d'%i)
        plt.legend(loc="left")

    . . . In [83]:  

    long = (hidden_states==0)  #做多
    short = (hidden_states == 1)  #做空
    long = np.append(0,long[:-1]) #第二天才能操作
    short = np.append(0,short[:-1]) #第二天才能操作
    res['ret'] =  df.multiply(long) - df.multiply(short)  
    plt.plot_date(Date,np.exp(res['ret'].cumsum()),'r-')

    Out[83]:

    [<matplotlib.lines.Line2D at 0x7fc541b9d1d0>]

    《隐马尔科夫链模型对于沪深300指数建模的进一步研究》 . . . In [ ]: 对修正后的指标进行隐马尔科夫链建模。 . . . In [71]:  

    # HMM modeling based on processed observation sequences
    rescaled_model = GaussianHMM(n_components= 3, covariance_type="full", n_iter=2000).fit([rescaled_A])
    rescaled_hidden_states = rescaled_model.predict(rescaled_A)
    rescaled_hidden_states
    plt.figure(figsize=(25, 18)) 
    for i in range(model.n_components):
        pos = (rescaled_hidden_states==i)
        plt.plot_date(Date[pos],close[pos],'o',label='hidden state %d'%i,lw=2)
        plt.legend(loc="left")

    《隐马尔科夫链模型对于沪深300指数建模的进一步研究》 . . . In [73]:  

    for i in range(3):
        pos = (rescaled_hidden_states==i)
        pos = np.append(0,pos[:-1])#第二天进行买入操作
        df = res.logRet_1
        res['state_ret%s'%i] = df.multiply(pos)
        plt.plot_date(Date,np.exp(res['state_ret%s'%i].cumsum()),'-',label='hidden state %d'%i)
        plt.legend(loc="left")

    . . . In [74]:  

    long = (rescaled_hidden_states==0)  #做多
    short = (rescaled_hidden_states==1) + (rescaled_hidden_states == 2)  #做空
    long = np.append(0,long[:-1]) #第二天才能操作
    short = np.append(0,short[:-1]) #第二天才能操作
    res['ret'] =  df.multiply(long) - df.multiply(short)  
    plt.plot_date(Date,np.exp(res['ret'].cumsum()),'r-')

    Out[74]:

    [<matplotlib.lines.Line2D at 0x7fc541b4fe48>]

    《隐马尔科夫链模型对于沪深300指数建模的进一步研究》

  • A = np.column_stack([logDel,logRet_5,logVol_5]) 
点赞