Expoentes de Hurst para Detectar Reversão à Média em Forex

Expoentes de Hurst para Detectar Reversão à Média em Forex

Usando o expoente de Hurst para identificar dinamicamente regimes de reversão à média vs. tendência em mercados forex. Teoria completa, implementação em Python e resultados de backtest para um sistema de trading adaptativo por regime.

hurstmean-reversionforexstatistics

O Problema do Regime

Todo trader sistemático eventualmente enfrenta a mesma pergunta: esse mercado está em tendência ou revertendo à média agora?

Aplique uma estratégia de trend-following em um mercado mean-reverting e você será picotado. Aplique uma estratégia de reversão à média em um mercado em tendência e vai explodir tentando pegar facas caindo. A estratégia não está errada — o regime está errado.

A maioria dos traders resolve isso por instinto. Nós queríamos um número.

Achado principal (Fev 2026): Após testes extensivos, descobrimos que o expoente de Hurst R/S com lookback de 100 barras em crypto horário dispara H > 0.6 em 100% das barras (média H = 0.758), com WR direcional de 48-50% — cara ou coroa. O filtro de regime funciona em forex diário mas está completamente quebrado em crypto horário. Veja o cemitério de estratégias para a autópsia completa. Publicamos este post como está porque a metodologia é sólida — o modo de falha é instrutivo.

Distribuição de Hurst em crypto horário — o filtro que nunca filtra Cada barra classificada como “tendência” (H > 0.6). O “filtro de regime” tem zero conteúdo informacional em crypto horário. WR direcional: 48-50% em todos os thresholds.

O Expoente de Hurst: Teoria

O expoente de Hurst H mede a memória de longo prazo de uma série temporal. Originalmente desenvolvido por Harold Edwin Hurst enquanto estudava padrões de enchente do rio Nilo nos anos 1950, ele quantifica a tendência de uma série a regredir à média ou persistir em uma direção:

  • H = 0.5 — Random walk. Sem memória. Movimentos de preço são independentes. É o que a hipótese dos mercados eficientes prevê.
  • H < 0.5 — Mean-reverting (anti-persistente). Movimentos grandes tendem a ser seguidos por reversões. Uma alta torna uma queda mais provável.
  • H > 0.5 — Trending (persistente). Movimentos grandes tendem a ser seguidos por continuação. Uma alta torna outra alta mais provável.

Quanto mais H desvia de 0.5, mais forte a tendência. Um H de 0.3 é fortemente mean-reverting. Um H de 0.7 é fortemente trending.

Calculando o Expoente de Hurst

Implementamos o método de Rescaled Range (R/S), que é robusto e bem estudado:

import numpy as np
import pandas as pd
from scipy import stats

def hurst_rs(series: np.ndarray, min_window: int = 10) -> float:
    """
    Estimate Hurst exponent using the Rescaled Range (R/S) method.

    The R/S statistic scales as n^H, where H is the Hurst exponent.
    We compute R/S for multiple window sizes and fit the log-log slope.
    """
    n = len(series)
    if n < min_window * 4:
        return np.nan

    # Window sizes: powers of 2 up to n/2
    max_power = int(np.log2(n // 2))
    min_power = int(np.log2(min_window))
    window_sizes = [2**i for i in range(min_power, max_power + 1)]

    rs_values = []

    for w in window_sizes:
        n_windows = n // w
        if n_windows < 2:
            continue

        rs_list = []
        for i in range(n_windows):
            segment = series[i * w:(i + 1) * w]
            mean = segment.mean()
            deviations = segment - mean
            cumulative = np.cumsum(deviations)

            R = cumulative.max() - cumulative.min()  # Range
            S = segment.std(ddof=1)                    # Standard deviation

            if S > 0:
                rs_list.append(R / S)

        if rs_list:
            rs_values.append((w, np.mean(rs_list)))

    if len(rs_values) < 3:
        return np.nan

    # Log-log regression: log(R/S) = H * log(n) + c
    log_n = np.log([v[0] for v in rs_values])
    log_rs = np.log([v[1] for v in rs_values])

    slope, _, r_value, _, _ = stats.linregress(log_n, log_rs)

    return slope


def rolling_hurst(prices: pd.Series, window: int = 200, step: int = 1) -> pd.Series:
    """Compute rolling Hurst exponent over a price series."""
    returns = prices.pct_change().dropna().values

    hurst_values = []
    indices = []

    for i in range(window, len(returns), step):
        h = hurst_rs(returns[i - window:i])
        hurst_values.append(h)
        indices.append(prices.index[i + 1])  # +1 for the pct_change offset

    return pd.Series(hurst_values, index=indices, name='hurst')

Também implementamos o método de Detrended Fluctuation Analysis (DFA) como validação cruzada. Quando ambos os métodos concordam sobre o regime, nossa confiança é maior:

def hurst_dfa(series: np.ndarray, min_window: int = 10) -> float:
    """
    Estimate Hurst exponent using Detrended Fluctuation Analysis.

    DFA is more robust to non-stationarity than R/S.
    """
    n = len(series)
    cumulative = np.cumsum(series - series.mean())

    max_power = int(np.log2(n // 4))
    min_power = int(np.log2(min_window))
    window_sizes = [2**i for i in range(min_power, max_power + 1)]

    fluctuations = []

    for w in window_sizes:
        n_windows = n // w
        if n_windows < 2:
            continue

        f_list = []
        for i in range(n_windows):
            segment = cumulative[i * w:(i + 1) * w]
            x = np.arange(w)
            # Linear detrend
            coeffs = np.polyfit(x, segment, 1)
            trend = np.polyval(coeffs, x)
            residuals = segment - trend
            f_list.append(np.sqrt(np.mean(residuals**2)))

        fluctuations.append((w, np.mean(f_list)))

    if len(fluctuations) < 3:
        return np.nan

    log_n = np.log([f[0] for f in fluctuations])
    log_f = np.log([f[1] for f in fluctuations])

    slope, _, _, _, _ = stats.linregress(log_n, log_f)
    return slope

A Estratégia Adaptativa por Regime

Com um expoente de Hurst rolante, podemos alternar dinamicamente entre famílias de estratégia:

def regime_adaptive_signals(
    df: pd.DataFrame,
    hurst_window: int = 200,
    mr_threshold: float = 0.43,
    trend_threshold: float = 0.57,
    rsi_period: int = 14,
    ma_fast: int = 20,
    ma_slow: int = 50,
) -> pd.DataFrame:
    """
    Switch between mean-reversion and trend-following based on Hurst exponent.

    H < mr_threshold  -> Mean reversion mode (RSI-based)
    H > trend_threshold -> Trend following mode (MA crossover)
    Otherwise           -> No trade (ambiguous regime)
    """
    df = df.copy()

    # Compute rolling Hurst
    df['hurst'] = rolling_hurst(df['close'], window=hurst_window)

    # Regime classification
    df['regime'] = 'neutral'
    df.loc[df['hurst'] < mr_threshold, 'regime'] = 'mean_reverting'
    df.loc[df['hurst'] > trend_threshold, 'regime'] = 'trending'

    # Mean reversion signals: RSI extremes
    delta = df['close'].diff()
    gain = delta.where(delta > 0, 0).rolling(rsi_period).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(rsi_period).mean()
    rs = gain / loss
    df['rsi'] = 100 - (100 / (1 + rs))

    df['mr_long'] = (df['regime'] == 'mean_reverting') & (df['rsi'] < 30)
    df['mr_short'] = (df['regime'] == 'mean_reverting') & (df['rsi'] > 70)

    # Trend following signals: MA crossover
    df['ma_fast'] = df['close'].rolling(ma_fast).mean()
    df['ma_slow'] = df['close'].rolling(ma_slow).mean()

    df['trend_long'] = (df['regime'] == 'trending') & (df['ma_fast'] > df['ma_slow'])
    df['trend_short'] = (df['regime'] == 'trending') & (df['ma_fast'] < df['ma_slow'])

    # Combined signal
    df['long'] = df['mr_long'] | df['trend_long']
    df['short'] = df['mr_short'] | df['trend_short']

    return df

A decisão de design chave: não operamos na zona neutra (H entre 0.43 e 0.57). Quando o expoente de Hurst está perto de 0.5, o mercado se comporta aleatoriamente, e nenhuma família de estratégia tem edge. Ficar de fora reduz a frequência de trades mas melhora dramaticamente a qualidade.

Resultados: Quatro Pares Principais de Forex, H1, 2021–2025

ParTradesWin RatePFSharpeMax DD% Tempo no Mercado
EURUSD28448.6%1.370.98-9.2%41.3%
GBPUSD31247.1%1.290.87-11.4%44.7%
USDJPY26751.3%1.421.08-8.1%38.9%
AUDUSD29846.8%1.240.79-12.8%43.2%

USDJPY performa melhor, provavelmente porque exibe a estrutura de regime mais clara — os períodos de intervenção do BOJ criam regimes de tendência fortes que o expoente de Hurst identifica com clareza.

Distribuição de Regimes

Nos quatro pares, o expoente de Hurst rolante passa aproximadamente:

  • 32% do tempo abaixo de 0.43 (mean-reverting)
  • 26% do tempo acima de 0.57 (trending)
  • 42% do tempo na zona neutra (0.43–0.57)

Isso confirma o que a maioria dos traders experientes intui: mercados estão em um regime claramente operável apenas cerca de 58% do tempo. Os outros 42% são ruído.

Validação: O Expoente de Hurst É Realmente Preditivo?

Um indicador rolante só é útil se prevê comportamento futuro, não apenas descreve o passado. Testamos isso medindo se o expoente de Hurst no tempo t prevê o retorno adequado à estratégia de t+1 a t+n:

def validate_hurst_predictiveness(df: pd.DataFrame, forward_bars: int = 50):
    """
    Test whether low Hurst predicts future mean reversion
    and high Hurst predicts future trend continuation.
    """
    df = df.copy()

    # Future return autocorrelation (proxy for trending vs mean-reverting)
    returns = df['close'].pct_change()
    df['fwd_autocorr'] = returns.rolling(forward_bars).apply(
        lambda x: x.autocorr(lag=1), raw=False
    ).shift(-forward_bars)

    # Split by Hurst regime
    mr_regime = df[df['hurst'] < 0.43]['fwd_autocorr'].dropna()
    trend_regime = df[df['hurst'] > 0.57]['fwd_autocorr'].dropna()
    neutral = df[df['hurst'].between(0.43, 0.57)]['fwd_autocorr'].dropna()

    return {
        'mean_reverting_autocorr': mr_regime.mean(),
        'trending_autocorr': trend_regime.mean(),
        'neutral_autocorr': neutral.mean(),
    }

Resultados em EURUSD: autocorrelação média foi -0.08 em regimes mean-reverting identificados pelo Hurst (confirmando anti-persistência) e +0.11 em regimes trending (confirmando persistência). O regime neutro mostrou autocorrelação próxima de zero (+0.01). O expoente de Hurst é genuinamente preditivo.

Considerações Práticas

O tamanho da janela importa. Usamos 200 barras (aproximadamente 8 dias de trading no H1). Janelas mais curtas (50-100 barras) são mais ruidosas mas mais responsivas. Janelas mais longas (300-500 barras) são mais suaves mas atrasam mudanças de regime. Encontramos 200 como o melhor compromisso.

Estimativas de Hurst têm incerteza. Com 200 pontos de dados, o erro padrão na estimativa de Hurst é aproximadamente ±0.05. Por isso usamos uma zona morta (0.43 a 0.57) ao invés de operar exatamente em 0.5.

Transições de regime são graduais. Mercados não flipam de mean-reverting para trending instantaneamente. O expoente de Hurst transiciona entre regimes ao longo de 20-50 barras. Não espere sinais nítidos.

O Que Aprendemos

O expoente de Hurst é uma das ferramentas mais subestimadas em trading quantitativo. Ele fornece uma resposta principiada e matematicamente fundamentada à pergunta que todo trader faz: “que tipo de mercado é esse?”

A estratégia adaptativa por regime não é nosso sistema de maior performance (esse é a estratégia de colapso de entropia com PF 1.44), mas é o mais intelectualmente satisfatório. Transforma um julgamento qualitativo (“isso parece picotado”) em um sinal quantitativo, e funciona.


Isso faz parte da nossa série de pesquisa quant aberta. Veja Colapso de Entropia — Timing de Volatilidade para nossa estratégia de melhor performance, ou 31 Estratégias Testadas, 4 Sobreviveram para o panorama completo do que testamos.