python 中的 Hurst 指数

Hurst Exponent in python

from datetime import datetime
from pandas.io.data import DataReader
from numpy import cumsum, log, polyfit, sqrt, std, subtract
from numpy.random import randn

def hurst(ts):

    """Returns the Hurst Exponent of the time series vector ts"""
    # Create the range of lag values
    lags = range(2, 100)

    # Calculate the array of the variances of the lagged differences
    # Here it calculates the variances, but why it uses 
    # standard deviation and then make a root of it?
    tau = [sqrt(std(subtract(ts[lag:], ts[:-lag]))) for lag in lags]

    # Use a linear fit to estimate the Hurst Exponent
    poly = polyfit(log(lags), log(tau), 1)

    # Return the Hurst exponent from the polyfit output
    return poly[0]*2.0


# Download the stock prices series from Yahoo
aapl = DataReader("AAPL", "yahoo", datetime(2012,1,1), datetime(2015,9,18))

# Call the function
hurst(aapl['Adj Close'])

从这段估计Hurst Exponent的代码来看,当我们要计算滞后差的方差时,为什么还要用标准差和平方根呢?困惑了很久,不知道为什么别人没有同样的困惑。我误解了它背后的数学原理吗?谢谢!

我也很困惑。我也不明白 std 的 sqrt 来自哪里,并且花了 3 天时间试图弄清楚。最后我注意到 QuantStart 归功于 Tom Starke 博士,他使用的代码略有不同。 Tom Starke 博士感谢 Ernie Chan 博士,并前往 his blog。我能够找到足够的信息来根据他的原则编写我自己的代码。这不使用 sqrt,使用方差而不是标准差,并在末尾使用 2.0 除数而不是 2.0 乘数。最后,它似乎给出了与您 post 的 quantstart 代码相同的结果,但我能够从第一原理理解它,我认为这很重要。我整理了一个 Jupyter Notebook 使它更清晰,但我不确定我是否可以 post 在这里,所以我会尽力在这里解释。先贴代码,再贴解释。

lags = range(2,100)
def hurst_ernie_chan(p):

    variancetau = []; tau = []

    for lag in lags: 

        #  Write the different lags into a vector to compute a set of tau or lags
        tau.append(lag)

        # Compute the log returns on all days, then compute the variance on the difference in log returns
        # call this pp or the price difference
        pp = subtract(p[lag:], p[:-lag])
        variancetau.append(var(pp))

    # we now have a set of tau or lags and a corresponding set of variances.
    #print tau
    #print variancetau

    # plot the log of those variance against the log of tau and get the slope
    m = polyfit(log10(tau),log10(variancetau),1)

    hurst = m[0] / 2

    return hurst

Chan 博士没有在此页面上提供任何代码(我相信他在 MATLAB 中工作,而不是 Python)。因此,我需要根据他在博客中给出的注释和他对博客上提出的问题的回答,将我自己的代码放在一起。

  1. 陈博士指出,如果 z 是对数价格,则以 τ 为间隔采样的波动率是波动率 (τ)=√(Var(z(t)-z(t-τ )))。对我来说,另一种描述波动率的方法是标准差,所以 std(τ)=√(Var(z(t)-z(t-τ)))

  2. std 只是方差的根,所以 var(τ)=(Var(z(t)-z(t-τ)))

  3. 陈博士接着说:一般来说,我们可以写成 Var(τ) ∝ τ^(2H) 其中 H 是 Hurst 指数

  4. 因此 (Var(z(t)-z(t-τ))) ∝ τ^(2H)

  5. 取每一边的对数,我们得到 log (Var(z(t)-z(t-τ))) ∝ 2H log τ

  6. [ log (Var(z(t)-z(t-τ))) / log τ ] / 2 ∝ H(给出 Hurst 指数),其中我们知道方括号中的项最左边是 tau 的对数对数图的斜率和相应的一组方差。

如果你运行那个函数并比较 Quantstart 函数的答案,它们应该是相同的。不确定是否有帮助。

这里发生的一切都是数学符号的变体

我来定义

d = subtract(ts[lag:], ts[:-lag])

那么很明显

np.log(np.std(d)**2) == np.log(np.var(d))
np.log(np.std(d)) == .5*np.log(np.var(d))

那么你就等价了

2*np.log(np.sqrt(np.std(d))) == .5*np.log(np.sqrt(np.var(d)))

polyfit 的函数输出与其输入成比例

OP 发布的代码是正确的。

混淆的原因是它首先求平方根,然后通过将斜率(return由 polyfit 计算)乘以 2 来反算。

更详细的解释,继续阅读。

tau 是用 "extra" 平方根计算的。然后,计算其对数。 log(sqrt(x)) = log(x^0.5) = 0.5*log(x) (这是关键)。 polyfit 现在用 y 乘以 "extra 0.5" 进行拟合。所以,得到的结果也乘以了将近0.5。返回其中的两倍 (return poly[0]*2.0) 会抵消初始的(看似)额外的 0.5。

希望这样更清楚。

根据 Ernest Chan 的“算法交易”(第 44 页)中的直观定义:

Intuitively speaking, a “stationary” price series means that the prices diffuse from its initial value more slowly than a geometric random walk would.

有人会想要检查时间序列的 方差 且滞后增加 与滞后 。这是因为对于正态分布——对数价格被认为是正态的(在一定程度上)——正态分布总和的方差是成分方差的总和。

根据 Ernest Chan 的引文,对于均值回归过程,实现的方差将小于理论预测。

将其放入代码中:

def hurst(p, l):
    """
    Arguments:
        p: ndarray -- the price series to be tested
        l: list of integers or an integer -- lag(s) to test for mean reversion
    Returns:
        Hurst exponent
    """
    if isinstance(l, int):
        lags = [1, l]
    else:
        lags = l
    assert lags[-1] >=2, "Lag in prices must be greater or equal 2"
    print(f"Price lags of {lags[1:]} are included")
    lp = np.log(p)
    var = [np.var(lp[l:] - lp[:-l]) for l in lags]
    hr = linregress(np.log(lags), np.log(var))[0] / 2
    return hr