根据回测结果,发现HMM用起来有问题,请高手们指点!
之前做过相关方面的研究,针对的是国外的期货市场。也有朋友建议说拿A股来试试看。所以有了下面的这篇东西。
Renaissance & Medallion(文艺复兴科技和大奖章)量化圈都非常熟悉了。Simons一群物理学家和数学家碰撞在一起,1989年到2008年的yearly return达到35.6%。文艺复兴大概一百多个员工,AUM:50亿美金,在全球金融危机的08年,大部分对冲基金都亏损,而大奖章的return高达80%。
神秘的文艺复兴科技和神秘的大奖章基金,到底一群数学家和物理学家聚在一起搞出了什么赚钱利器?外界猜测众说纷纭。而隐马尔科夫模型也由于一些原因被推举出来。
成立初期的创始人中,有一位科学家发明了广泛应用在语音识别等领域的鲍姆-威尔士算法,用来确定不可确知的变量可能出现的概率。今天要介绍的HMM模型,也是在语音识别中运用非常成功的模型,最早是由鲍姆等人提出的。
之前拜读人大的一位教授14年写的一本书,解密复兴科技:基于隐蔽马尔科夫模型的时序分析方法(亚马逊链接),书中介绍了为什么觉得HMM是Renaissance使用的模型,并且含有详细的公式推导和运算,以及附上了一些些实证结果(个人认为还有很多有待补充和粗糙的地方)。感兴趣推荐研读。
HMM模型,又叫隐马尔科夫模型。要正确的理解和搞懂模型,教材里有很多经典的例子。我从自己理解后的角度尽量浅显的给大家做一个解释,方便大家快速理清概念投入应用。
(摘自某次做汇报的PPT)
我们能观测到的序列$$ Y1,...,Yn $$ 称为可观测序列,如股价,成交量,资金净额等等。
而每一个可观测值的产生对应着市场状态序列$$Z1,...,Zn$$ ,每个状态通过不同的分布函数来产生观测值。
通过HMM模型,可以用简单的输入,来得出对目前市场状态的判断,从而帮助我们进行择时选择。因为市场状态不是显性可观测的,属于隐藏状态,我们通过对可观测变量的处理来进行推测。
这里对HMM模型进行了扩展得到HMS-GMD模型,因为收益率序列尖峰厚尾的特性导致的非正态分布,引入了混合高斯分布作为状态到观测值之间产生关系的分布函数。
将HMM模型看作一个黑箱子,这个黑箱子可以利用极其方便、简洁的数据,处理后得出:
而黑箱子需要事先给定两个参数:状态数目、混合高斯分布的成分数目。
当然,输入这里是拿单一的价格序列举例。输入也可以是并行的数据矩阵,比如从价格、成交量、资金净额等多个角度来看。
总结一下,使用模型需要在初期设定:
下面拿A股市场来做检验。
模型的设定如下:
HMM模型的算法使用hmmlearn模块。简单介绍一下函数的各个参数意思。
from hmmlearn.hmm import GMMHMM,GaussianHMM
import datetime
import numpy as np
import pandas as pd
import seaborn as sns
from matplotlib import cm
from matplotlib import pyplot
startdate = '2012-06-01'
enddate = '2016-04-07'
df = get_price(['000300.XSHG'], start_date=startdate, end_date=enddate, frequency='daily', fields=['close','volume','high','low'])
close = df['close']['000300.XSHG']
high = df['high']['000300.XSHG'][5:]
low = df['low']['000300.XSHG'][5:]
volume = df['volume']['000300.XSHG'][5:]
money = df['volume']['000300.XSHG'][5:]
datelist = pd.to_datetime(close.index[5:])
logreturn = (np.log(np.array(close[1:]))-np.log(np.array(close[:-1])))[4:]
logreturn5 = np.log(np.array(close[5:]))-np.log(np.array(close[:-5]))
diffreturn = (np.log(np.array(high))-np.log(np.array(low)))
closeidx = close[5:]
X = np.column_stack([logreturn,diffreturn,logreturn5])
len(X)
#【oneshotbin】讲数据分为样本内和样本外的
XInSample = X[:300]
XOutSample = X[300:]
#【oneshotbin】将数据内数据送入训练
hmm = GaussianHMM(n_components = 6, covariance_type='diag',n_iter = 5000).fit(XInSample) # X
# 【oneshotbin】将数据外数据逐根送入做预测,请注意比较相邻两次的预测输出结果会发现:前一个已经预测出的状态,在后面 i=5 时会发生改变
for i in range(10):
Xsplit=XOutSample[:i+1]
i = i+1
print('i= %d----------------------'%i)
retMatrix=hmm.predict(Xsplit)
print(retMatrix)
i= 1---------------------- [4] i= 2---------------------- [4 4] i= 3---------------------- [4 4 4] i= 4---------------------- [4 4 4 4] i= 5---------------------- [4 4 4 2 2] i= 6---------------------- [4 4 4 2 2 2] i= 7---------------------- [4 4 4 2 2 2 2] i= 8---------------------- [4 4 4 2 2 2 2 2] i= 9---------------------- [4 4 4 2 2 2 2 2 2] i= 10---------------------- [4 4 4 2 2 2 2 2 2 2]
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-1-f51739d7a748> in <module>() ----> 1 XInSample = X[:6] 2 print(XInSample) 3 XOutSample = X[6:] 4 print(XOutSample) 5 NameError: name 'X' is not defined
sns.set_style('white')
plt.figure(figsize = (15, 8))
for i in range(hmm.n_components):
state = (latent_states_sequence == i)
plt.plot(datelist[state],closeidx[state],'.',label = 'latent state %d'%i,lw = 1)
plt.legend()
plt.grid(1)
#[Oneshotbin]看起来真美,有没有未来?没有未来,错,有未来。
print(X)
for i in range(10):
Xsplit=X[:i+1]
i = i+1
print('i= %d'%i,Xsplit)
retMatrix=hmm.predict(X)
print(retMatrix)
以上。我们看到了六个状态的HMM模型输出的市场状态序列。需要注意的是:HMM模型只是能分离出不同的状态,具体对每个状态赋予现实的市场意义,是需要人为来辨别和观察的。
下面我们来用简单的timming策略来识别6种latent_state所带来的效果。
data = pd.DataFrame({'datelist':datelist,'logreturn':logreturn,'state':latent_states_sequence}).set_index('datelist')
plt.figure(figsize=(15,8))
for i in range(hmm.n_components):
state = (latent_states_sequence == i)
idx = np.append(0,state[:-1])
data['state %d_return'%i] = data.logreturn.multiply(idx,axis = 0)
plt.plot(np.exp(data['state %d_return' %i].cumsum()),label = 'latent_state %d'%i)
plt.legend()
plt.grid(1)
上图可以看出:
以上的意义归结是存在一定主观性的。因为HMM模型对输入的多维度观测变量进行处理后,只负责分出几个类别,而并不会定义出每种类别的实际含义。所以我们从图形中做出上述的判断。
我们根据模拟出来的隐藏状态,来进行择时。
策略是这样设计的:
我们来看一下收益效果:
buy = (latent_states_sequence == 1) + (latent_states_sequence == 2)
buy = np.append(0,buy[:-1])
sell = (latent_states_sequence == 0) + (latent_states_sequence == 3) \
+ (latent_states_sequence == 4) + (latent_states_sequence == 5)
sell = np.append(0,sell[:-1])
data['backtest_return'] = data.logreturn.multiply(buy,axis = 0) \
- data.logreturn.multiply(sell,axis = 0)
plt.figure(figsize = (15,8))
plt.plot_date(datelist,np.exp(data['backtest_return'].cumsum()),'-',label='backtest result')
plt.legend()
plt.grid(1)
鉴于卖空指数对散户来说没什么可操作性,我们单看能做多的A股市场。选择嘉实沪深300基金来复制沪深300指数。
策略是这样设计的:
鉴于研究模块自己搭伪回测算出来的收益曲线很难考虑到滑点、交易规则限制等各方面的要求,所以如下的策略回测只贴上最后的图。代码具体见策略。
我采取的方式是把研究模块得出的状态序列导出,导入到回测模块使用。
回测结果截图贴在这。回撤和收益都看起来很漂亮。虽然我也没太懂为什么跟可卖空的结果差不多= =,可能是单纯算收益率的伪回测不太准吧。第二是状态1(绿色)有涨有跌,卖空它也有亏钱的时候。
搞量化的小伙伴们每每总是以为发现了『天上掉钱』的秘籍法宝,但仔细想想每一个模型都不是那么简单的。而且相信一部分人已经反映过来了,这是个In-Sample的测试,模型是根据全部时间段的数据得出状态序列的。也就是,天然的,我们就提前知道了什么时候涨跌。简单说,有未来函数。
但是,这不能否认模型优秀的模式识别能力。接下来要做的,是真正的分开In-Sample和Out-Sample来做回测。用一段时间训练得到的模型参数来分析之后一段时间的市场状态。
本文意在抛砖,大神们一起来研究HMM吧。
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程