用 Python 計算 Hurst 指數(shù)并預(yù)測市場趨勢

建造水庫是一個高風(fēng)險的問題。如果計算錯誤,成千上萬的人可能會失去他們的家園。在20世紀(jì)中期,英國水文學(xué)家埃德溫-哈羅德-赫斯特在解決這類問題方面取得了重大突破。他開發(fā)了后來被稱為Hurst指數(shù)的東西,這是一個衡量時間序列中自動相關(guān)的指標(biāo)。雖然它是為了估計河流中的水量而開發(fā),但正如我們所期望的那樣,對于涉及時間序列的數(shù)學(xué)計算,Hurst指數(shù)在金融市場研究中得到了有趣的應(yīng)用。
下面將展示如何計算Hurst指數(shù),并使用它來評估標(biāo)準(zhǔn)普爾500指數(shù)、比特幣和美元/瑞士法郎交易對的特性,看看我們是否能理解這些金融序列的交易趨勢。
什么是Hurst指數(shù)?
Hurst指數(shù)可以用來確定一個時間序列是否傾向于向單一方向移動(H>0.5),振蕩(H<0.5),或隨機(jī)(H=0.5)。
雖然赫斯特發(fā)現(xiàn)這種關(guān)系在各種自然現(xiàn)象中很有用,如洪水、河流排放和樹木年輪,但我們也可以用它來將一個市場歸類為趨勢性或均值回歸性。如果我們知道一個市場傾向于以某種方式行事,我們可以嘗試用適當(dāng)?shù)木祷貧w或趨勢跟蹤策略來捕捉這一點,或者在你的算法交易系統(tǒng)中使用它作為一個過濾器。
如何計算Hurst指數(shù)?計算Hurst指數(shù)需要估計多個不同時間段的R/S統(tǒng)計量,然后將其與各時間段繪制成對數(shù)圖,并找出斜率。這條線的斜率就是Hurst指數(shù) —— H。
有多種方法來估計Hurst指數(shù)。就我所知,重新標(biāo)定的范圍分析方法是最古老的,但使用去趨勢波動分析(DFA)是最常見的。我們將使用后者,因為盡管它聽起來很復(fù)雜,但它只需要3-4行代碼就能運(yùn)行,而且比我的重標(biāo)范圍分析實現(xiàn)快一個數(shù)量級。這兩種方法都是可行的,但它們略有不同。
假設(shè)我們有一個價格序列,我們稱之為x。然后我們可以看一下x在不同時間t的差異,并取其差值。這個時間差將被稱為lag(滯后)。我們想得到這些滯后差值的標(biāo)準(zhǔn)差的關(guān)系。
我們可以按以下步驟寫出我們的計算結(jié)果。
1、選擇一個滯后期的范圍(如2到100)。
2、計算x中所有點的滯后差值。
3、計算每個lag的標(biāo)準(zhǔn)差。
4、將標(biāo)準(zhǔn)差的對數(shù)與lag的對數(shù)作圖,以估計H。
數(shù)學(xué)上的公式為:

將所有的數(shù)據(jù)插入最后一個方程,我們可以找到最佳擬合線,我們就得到了H的值。讓我們來看看如何用Python來做這件事!
用Python中計算Hurst指數(shù)
首先導(dǎo)入幾個包:?
import numpy as np
import matplotib.pyplot as plt
然后定義我們的 hurst()函數(shù)。
def hurst(price, min_lag=2, max_lag=100):
lags = np.arange(min_lag, max_lag + 1)
tau = [np.std(np.subtract(price[lag:], price[:-lag]))
for lag in lags]
m = np.polyfit(np.log10(lags), np.log10(tau), 1)
return m, lags, tau
我們可以通過使用一個計算所有滯后差值的標(biāo)準(zhǔn)差的列表來理解,將所有的東西整合在幾行緊湊的線條中。
接下來,讓我們在一些我們知道答案的序列上測試這個函數(shù)。這將有助于確保我們的計算是正確的。
N = 10000
rand = np.cumsum(np.random.randn(N) + 0.01)
mr = np.cumsum(np.sin(np.linspace(0, N/3*np.pi, N))/2 + 1)
tr = np.cumsum(np.arange(N)/N)
m_rand, lag_rand, rs_rand = hurst(rand)
m_mr, lag_mr, rs_mr = hurst(mr)
m_tr, lag_tr, rs_tr = hurst(tr)
print(f"Hurst(Random):\t{m_rand[0]:.3f}")
print(f"Hurst(MR):\t{m_mr[0]:.3f}")
print(f"Hurst(TR):\t{m_tr[0]:.3f}")
Hurst(Random): 0.499
Hurst(MR): 0.075
Hurst(TR): 0.997
此外,我們可以將這些系列與我們的lag值和統(tǒng)計數(shù)據(jù)的對數(shù)圖一起繪制。

左欄中的圖是對數(shù)圖。你可以看到我們試圖在Y軸上擬合標(biāo)準(zhǔn)差統(tǒng)計數(shù)字。對于隨機(jī)和趨勢的時間序列,它們匹配得很好。均值回歸的是一個快速振蕩的正弦波——完全不一致,但斜率仍然是正的,并且小于0.5。在你的生活中,你永遠(yuǎn)不會看到這樣的金融時間序列,但它應(yīng)該給你信心,我們的函數(shù)是有效的!
金融時間序列的Hurst指數(shù)
將Hurst應(yīng)用于金融數(shù)據(jù)也同樣簡單明了。我們可以把它應(yīng)用于一些常見的時間序列,看看我們得到什么。
我們將使用美元/瑞士法郎交易對、BTC和SPY來感受一下這些數(shù)據(jù)。
用yfinance軟件包抓取數(shù)據(jù)。
import pandas as pd
import yfinance as yf
tickers = ['CHF=X', 'BTC-USD', 'SPY', 'GLD', 'USO']
start = '2010-01-01'
end = '2021-12-31'
yfObj = yf.Tickers(tickers)
df = yfObj.history(start=start, end=end)
df.drop(['Stock Splits', 'Dividends', 'Volume',
'Open', 'High', 'Low'], axis=1, inplace=True)
df.columns = df.columns.swaplevel()
我們在這里只對收盤價感興趣,所以我們可以放棄所有其他的列,并在這些列上運(yùn)行swaplevel(),這樣我們就可以按股票代碼進(jìn)行索引。
接下來,我們需要通過Hurst()函數(shù)運(yùn)行每個時間序列,以獲得指數(shù)和相關(guān)值。
vals = {c[0]: hurst(df[c].dropna().values) for c in df.columns}
一旦完成,我有一個輔助函數(shù)來繪制結(jié)果。
def plotHurst(m, x, y, series, name):
fig, ax = plt.subplots(1, 2, figsize=(15, 6))
ax[0].plot(np.log10(x), m[0] * np.log10(x) + m[1])
ax[0].scatter(np.log10(x), np.log10(y), c=colors[1])
ax[0].set_title(f"{name} (H = {m[0]:.3f})")
ax[0].set_xlabel(r"log($\tau$)")
ax[0].set_ylabel(r"log($\sigma_\tau$)")
ax[1].plot(series)
ax[1].set_title(f"{name}")
ax[1].set_ylabel("Price ($)")
ax[1].set_xlabel("Date")
return fig, ax
這使得繪制我們的數(shù)值變得更加容易。如果我們想繪制所有的數(shù)值,我們可以循環(huán)瀏覽我們的vals字典,并調(diào)用plt.show(),如下面的代碼所示。
for k, v in vals.items():
fig, ax = plotHurst(*v, df[k], k)
plt.show()
讓我們依次看一下結(jié)果,看看我們是否能從Hurst指數(shù)告訴我們的東西中得到一些啟示。
美元/瑞士法郎交易對均值回歸
我們將從Hurst指標(biāo)中看到的最強(qiáng)烈的信號開始,即美元/瑞士法郎交叉。

大多數(shù)交易員都有一種直覺,即貨幣對總體上是均值回歸的,而對于強(qiáng)勢貨幣之間的貨幣對,如美元和瑞士法郎,它尤其明顯。
這正是我們在這里看到的情況。
不過,值得注意的是,較短的時間框架與我們在這里看到的較長的時間框架有一定的偏差。在最短的時間范圍內(nèi),已實現(xiàn)波動率的期限結(jié)構(gòu)(即藍(lán)點所在的位置)低于紅線。
出于好奇,我繪制了各種最大lag值的Hurst指數(shù),看看這個系列是否曾經(jīng)成為一個趨勢系列。

并非如此。
它確實越長越平均,表明美元和瑞士法郎之間有一個相當(dāng)穩(wěn)定的長期關(guān)系。這是每日數(shù)據(jù),所以它可以提供一些指導(dǎo),作為你應(yīng)該交易的時間框架。考慮到它在60天以上會變得更加均值化,也許在這個時間范圍內(nèi)(或更長)運(yùn)行的均值化策略將是最有效的。
比特幣是一種動量游戲
比特幣暴漲暴跌的勢頭恰恰相反。

Hurst指數(shù)給出了一個相當(dāng)明確的信號:這是一個動量交易,而已實現(xiàn)的波動率期限結(jié)構(gòu)顯示出令人驚訝的少數(shù)偏差。
但有趣的是,2021年的情況。

在比特幣2020年的巨大牛市之后,它在2021年度過了一個隨機(jī)/平均回歸階段。請注意,這不是那么多的數(shù)據(jù),但有趣的是,不知道比特幣是否已經(jīng)進(jìn)入了一個趨勢變化。在2017年的大牛市之后,它在2018-2019年進(jìn)入了一個類似的趨勢,它有兩年時間在進(jìn)行均值回歸,直到2020年起飛。

到目前為止,2022年已經(jīng)顯示出類似的模式。它能在今年剩下的時間里持續(xù)下去嗎?
標(biāo)準(zhǔn)普爾500指數(shù)的均值回歸
這個對我來說是最大的驚喜。Hurst指數(shù)顯示,標(biāo)普500指數(shù)是一個平均回歸的時間序列。
看看它右邊的價格圖,它看起來有一個強(qiáng)大的上升趨勢。
我們的算法揭示了我們眼睛看不到的東西。

我們可以對標(biāo)準(zhǔn)普爾500指數(shù)進(jìn)行與美元/瑞士法郎匯率相同的分析,看看在不同的滯后期是否有任何拐點或赫斯特指數(shù)的變化。
當(dāng)我們這樣做時,我們會得到下面的圖表。

在這幅圖中,H值隨著滯后期的變化而明顯變化。在中短期內(nèi),該系列是平均回歸的,但在較長的時間范圍內(nèi),它變得有強(qiáng)烈的趨勢性。在非常長的時間內(nèi)(超過400天),它又回到了強(qiáng)烈的均值回歸。
讓我們看看這在不同的時間范圍內(nèi)是否成立。
我們將回到過去,從1993年開始到2021年,獲取SPY的數(shù)據(jù)。這將產(chǎn)生7,284個交易日。此外,我們將把它分為四個時間段,并在這四個時間段中的每一個時間段運(yùn)行相同的分析,以查看SPY的行為是否在較小的間隔內(nèi)發(fā)生變化。
這就產(chǎn)生了下面的圖表。

這表明,標(biāo)準(zhǔn)普爾500指數(shù)在不同的lag(滯后)期會經(jīng)歷更多的趨勢和更多的均值回歸時期。例如,比較2000-2007年期間。在這里,我們從互聯(lián)網(wǎng)的繁榮和蕭條到全球金融危機(jī)的高峰,幾乎所有的滯后期都是趨勢的。從那時起,在200-300區(qū)間內(nèi),趨勢的持續(xù)程度較低,但其他地方的均值回歸更為普遍。
值得注意的是,在我們看的每一個時間框架內(nèi),該系列似乎都在200-300天范圍內(nèi)有趨勢。這似乎表明,標(biāo)普500指數(shù)在這些時間范圍內(nèi)表現(xiàn)出強(qiáng)烈的趨勢行為,可以通過趨勢跟蹤模型加以利用,而均值回歸模型在較短的時間范圍內(nèi)會更有價值。
這種對200-300天范圍內(nèi)趨勢的觀察似乎與著名的海龜交易員杰里-帕克的軼事觀察相吻合,即現(xiàn)代趨勢跟蹤在這些200天以上的時間范圍內(nèi)效果最好。
總結(jié)
一個市場是傾向于趨勢、均值回歸,還是隨機(jī)的,這對交易者來說是有價值的信息。雖然赫斯特指數(shù)本身并不是一個入場信號,但它可以作為一個系統(tǒng)的過濾器。考慮到市場制度可能會隨著時間的推移而發(fā)生變化,有利于一種或另一種方法,用Hurst過濾器覆蓋你的模型,可以幫助防止你的算法在均值回歸的市場中買入突破,或在市場走向新高時在回撤之前做空。
從本質(zhì)上講,它可以幫助你的算法與它所交易的市場相匹配。當(dāng)然,這需要大量的回測。
