一、研究過程:方法參考陳大神的帖子
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()