请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3351393 新帖:11

HMM择时进一步研究

外汇老法师发表于:5 月 9 日 18:52回复(1)


一、研究过程:方法参考陈大神的帖子

1、研究对象。考虑到之后以沪深300期货作为回测对象,因此以沪深300指数为研究对象,采用python中的hmmlearn.hmm模块中的GaussianHMM模型进行拟合,拟合样本范围为2005-2014,测试样本为2015-2017。

2、观测变量选取。选取日对数收益率、5日对数收益率、日极差对数收益率(最高减最低后取对数)作为观测变量。三个观测变量经偏度、峰度及正态性检验,符合高斯分布。

3、拟合。隐含状态数取13,进行GaussianHMM模型拟合.。

4、分析。画出沪深300指数2005-2014年每日股价所处的状态;画出每个状态在10年间的累计收益图。由图分析出状态4、9、10为上涨状态、状态0、2、8为下跌状态。

5、简单回测。在拟合样本及测试样本中,分别将每个上涨状态的下一天的累计收益率,减去下跌状态下一天的累计收益率,得到拟合范围及测试范围的隐马尔科夫择时模型的累计收益率图。

6、初步结论:2005-2014年,该策略多空累计收益约55倍,且少有回撤。2015-2017的测试样本,该策略累计收益约1.7倍,收益率曲线前半段上升较为平稳,16年7月之后收益有所回撤。

二、回测

1、回测思路:

(1)选取沪深300股指期货10年至17年作为回测对象,调仓周期为每天,时间为开盘,为尽可能模拟沪深300指数收益,设佣金为0,保证金比率为100%。每天开盘,将截止昨日收盘前90天的日对数收益率、5日对数收益率、日极差对数收益率输入第一步中拟合出的HMM模型对昨日进行隐含状态预测。昨日预测为上涨状态即做多、下跌状态即做空。其结果与原先大相近庭,收益为负数。

(2)不在每天进行模型预测,而将“一、研究过程”中产生的信号直接输入进行回测,15-17年收益为38.75%,最大回撤为12.87%,表现大幅度提高。

2、反思:经过反复试验,发现隐马尔科夫模型具有前视性,原先使用完整收益序列预测每一天的状态时会使用到未来数据,其预测结果为最大后验概率的状态。而真实回测时预测最后一天的状态,状态预测会根据未来数据变化而变化、不稳定。

三、稳定HMM信号模型

为提高HMM的稳定性,构造稳定HMM信号模型。利用观测变量正态分布的特征,计算前750天观测变量的均值与标准差,对当日及之后共5天的观测值进行模拟。将这未来5天的模拟数据与过去750天的观测变量连同一起产生1000个样本,输入HMM模型进行预测,产生1000个当天状态的预测值。若某状态预测频率高于80%,则认为其为稳定信号。根据稳定信号进行回测,11-14年收益率为32.49%,最大回撤16.32%;15-17年收益率为12.49%,最大回撤为16.13%。可见稳定HMM信号模型稍稍改善了模型预测能力,但收益和原HMM模型不可同日而语。

四、HMM状态动量反转策略
1、思路:改变一下使用HMM模型的思路。根据前面的研究,HMM模型虽然在预测上不稳定,但其对历史数据具有极强形态识别能力。考虑根据HMM识别出的牛熊特征,进行择时。

2、回测实现:仍以沪深300指数期货为标的,佣金为0、保证金比率为100%。每个交易日,利用HMM模型获取过去60天的牛熊状态,分类为牛、熊与震荡,状态值分别为1,-1和0。选取过去10天为短周期、60天为长周期,将短周期和长周期每日状态值进行累加,得到牛熊指标。当短期牛熊指标大于6(短期动量)或者长期牛熊指标小于-25且短期牛熊指标大于0(长期反转),则做多;做空策略相对称。该策略在10年6月至16年12月,累计收益率为81.77%,最大回撤为20.18%。

3、结论:该策略有一定的收益潜力,能够识别大趋势,在大涨大跌中获取趋势收益,但在震荡市中表现尚一般。另外,该模型收益受参数影响大,有过拟合的风险。

才疏学浅,对hmm研究不深,最后没研究出啥牛逼成果。在这里抛砖迎玉,欢迎大家批评指正,提供新思路哈。

pickle文件:https://pan.baidu.com/s/1dFIMjTZ

from hmmlearn.hmm import GMMHMM,GaussianHMMimport datetimeimport numpy as npimport pandas as pdimport seaborn as snsfrom matplotlib import cmfrom matplotlib import pyplot as pltimport scipy.stats as scs freq='1d'stock='000300.XSHG'startdate = '2005-04-08'enddate = '2014-12-31'df = get_price([stock], start_date=startdate, end_date=enddate, frequency=freq, fields=['close','volume','high','low'])close = df['close'][stock]high = df['high'][stock][5:]low = df['low'][stock][5:]volume = df['volume'][stock][5:]money = df['volume'][stock][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)def normality_test(arr):  print "Skew of dataset %14.3f" % scs.skew(arr)  print "Skew test p-value %14.3f" % scs.skewtest(arr)[1]  print "Kurt of dataset %14.3f" % scs.kurtosis(arr)  print "Kurt test p-value %14.3f" % scs.kurtosistest(arr)[1]  print "Norm test p-value %14.3f" % scs.normaltest(arr)[1] normality_test(logreturn)
Skew of dataset         -0.360
Skew test p-value          0.000
Kurt of dataset          2.995
Kurt test p-value          0.000
Norm test p-value          0.000
hmm = GaussianHMM(n_components = 13, covariance_type='diag',n_iter = 5000).fit(X)latent_states_sequence = hmm.predict(X)len(latent_states_sequence)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)
data = pd.DataFrame({'datelist':datelist,'logreturn':logreturn,'state':latent_states_sequence}).set_index('datelist')plt.figure(figsize=(20,15))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(loc='upper left')plt.grid(1)plt.annotate(i, xy=(2400, np.exp(data['state %d_return' %i].cumsum())[2300]), xytext=(2400, np.exp(data['state %d_return' %i].cumsum())[2300]))
buy = (latent_states_sequence == 9) + (latent_states_sequence == 4)+ (latent_states_sequence == 10)buy = np.append(0,buy[:-1])sell = (latent_states_sequence == 0) + (latent_states_sequence == 2)+ (latent_states_sequence == 8)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)
startdate2 = '2015-01-01'enddate2 = '2017-04-19'df2 = get_price([stock], start_date=startdate2, end_date=enddate2, frequency=freq, fields=['close','volume','high','low'])close2 = df2['close'][stock]high2 = df2['high'][stock][5:]low2 = df2['low'][stock][5:]volume2 = df2['volume'][stock][5:]money2 = df2['volume'][stock][5:]datelist2 = pd.to_datetime(close2.index[5:])logreturn2 = (np.log(np.array(close2[1:]))-np.log(np.array(close2[:-1])))[4:]logreturn52 = np.log(np.array(close2[5:]))-np.log(np.array(close2[:-5]))diffreturn2 = (np.log(np.array(high2))-np.log(np.array(low2)))closeidx2 = close2[5:]X2 = np.column_stack([logreturn2,diffreturn2,logreturn52])latent_states_sequence = hmm.predict(X2)data2 = pd.DataFrame({'datelist':datelist2,'logreturn':logreturn2,'state':latent_states_sequence}).set_index('datelist')buy = (latent_states_sequence == 9) + (latent_states_sequence == 4)+ (latent_states_sequence == 10)buy = np.append(0,buy[:-1])sell = (latent_states_sequence == 0) + (latent_states_sequence == 2)+ (latent_states_sequence == 8)sell = np.append(0,sell[:-1])data2['backtest_return'] = data2.logreturn.multiply(buy,axis = 0) - data2.logreturn.multiply(sell,axis = 0)plt.figure(figsize = (15,8))plt.plot_date(datelist2,np.exp(data2['backtest_return'].cumsum()),'-',label='backtest result')plt.legend()plt.grid(1)
/opt/conda/envs/python2/lib/python2.7/site-packages/sklearn/utils/deprecation.py:70: DeprecationWarning: Function log_multivariate_normal_density is deprecated; The function log_multivariate_normal_density is deprecated in 0.18 and will be removed in 0.20.
  warnings.warn(msg, category=DeprecationWarning)
import pickleoutput = open('hmm300day.pkl', 'wb')pickle.dump(hmm,output)output.close()

全部回复

0/140

量化课程

    移动端课程