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.
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.
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
| Par | Trades | Win Rate | PF | Sharpe | Max DD | % Tempo no Mercado |
|---|---|---|---|---|---|---|
| EURUSD | 284 | 48.6% | 1.37 | 0.98 | -9.2% | 41.3% |
| GBPUSD | 312 | 47.1% | 1.29 | 0.87 | -11.4% | 44.7% |
| USDJPY | 267 | 51.3% | 1.42 | 1.08 | -8.1% | 38.9% |
| AUDUSD | 298 | 46.8% | 1.24 | 0.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.