Mandelbrot Ch. 3: The Hurst Exponent & Long Memory

阅读中文版

The rescaled range (R/S) method for measuring long memory, mean-reversion vs. trend persistence, and computing the Hurst exponent in Python.

🔊 Listen to Article (Chinese Audio)

Mandelbrot Fractal Ch. 3: The Hurst Exponent & Long Memory

"The flood records of the Nile told me a secret about financial markets: natural and market systems both have memory — past events continue to influence the future, rather than being instantly forgotten as random walk theory assumes." — Benoit Mandelbrot

An Accidental Discovery in Nile Hydrology

Harold Edwin Hurst, studying millennia of Nile flood records in the 1950s, found that high-flood years clustered with more high-flood years, and low years with more low years — evidence of long memory, not independent randomness.

Mandelbrot recognized this hydrological tool as the perfect test of the Efficient Market Hypothesis's core claim that price changes are independent and unpredictable.

Rescaled Range (R/S) Analysis

$$Y_k = \sum_{t=1}^{k}(X_t - \bar{X}), \quad R(n) = \max_k Y_k - \min_k Y_k$$

$$\frac{R(n)}{S(n)} \sim c \cdot n^H \implies \log\left(\frac{R(n)}{S(n)}\right) = \log(c) + H\log(n)$$

Interpreting the Hurst Exponent

Range Behavior Trading Implication
$H = 0.5$ Pure random walk No memory; technical analysis has no edge
$0.5 < H < 1.0$ Persistent trend Trend-following has mathematical grounding
$0 < H < 0.5$ Anti-persistent / mean-reverting Mean-reversion has mathematical grounding

Mandelbrot's empirical work on cotton, gold, and equity indices found most series cluster between $H = 0.5$ and $0.7$ — directly falsifying the pure random walk premise of EMH.

import numpy as np

def hurst_exponent(price_series, min_window=10, max_window=None):
    n = len(price_series)
    max_window = max_window or n // 2
    window_sizes = np.unique(np.logspace(np.log10(min_window), np.log10(max_window), 20).astype(int))
    rs_values = []
    for window in window_sizes:
        n_chunks = n // window
        if n_chunks < 1:
            continue
        rs_chunk = []
        for i in range(n_chunks):
            chunk = price_series[i*window:(i+1)*window]
            cumulative = np.cumsum(chunk - np.mean(chunk))
            s = np.std(chunk)
            if s > 0:
                rs_chunk.append((np.max(cumulative) - np.min(cumulative)) / s)
        if rs_chunk:
            rs_values.append(np.mean(rs_chunk))
    hurst, _ = np.polyfit(np.log(window_sizes[:len(rs_values)]), np.log(rs_values), 1)
    return hurst

Practical Execution Rules

  1. Measure the Hurst exponent before deploying trend or mean-reversion strategies — favor trend-following above $H = 0.55$, mean-reversion below $H = 0.45$.
  2. Re-estimate on a rolling window — $H$ drifts across regimes; do not rely on a single historical estimate.
  3. Treat $H$ as a framework filter, not a timing signal — combine with independent momentum or band signals for entries/exits.