허스트 지수 추세 판별법

허스트 지수(Hurst Exponent)란?

허스트 지수는 영국의 수문학자 해럴드 허스트(Harold Edwin Hurst)가 나일강 수위 예측을 위해 개발한 통계 지표입니다. 금융 시장에 적용하면 시계열이 추세를 따르는지, 평균으로 회귀하는지, 랜덤워크인지를 객관적으로 판별할 수 있습니다.

  • H > 0.5: 지속성(Persistence) — 상승이 상승을, 하락이 하락을 부름. 추세추종 전략에 유리.
  • H = 0.5: 랜덤워크 — 과거가 미래를 예측하지 못함. 매매 자제.
  • H < 0.5: 반지속성(Anti-persistence) — 상승 후 하락, 하락 후 상승. 평균회귀 전략에 유리.

대부분의 기술적 지표가 “지금 추세인가?”를 사후적으로 판단하는 반면, 허스트 지수는 “이 시장이 추세를 만들 성질인가?”를 사전에 평가한다는 점에서 차별화됩니다.

R/S 분석법으로 허스트 지수 계산

가장 고전적인 계산법인 Rescaled Range(R/S) 분석입니다. 시계열을 다양한 길이의 구간으로 나누어 각 구간의 범위(R)를 표준편차(S)로 정규화한 뒤, 로그-로그 회귀로 허스트 지수를 추정합니다.

import numpy as np
import pandas as pd
import ccxt

# 바이낸스 일봉 데이터 수집
exchange = ccxt.binance()
ohlcv = exchange.fetch_ohlcv('BTC/USDT', '1d', limit=500)
df = pd.DataFrame(ohlcv, columns=['ts','open','high','low','close','volume'])
df['returns'] = df['close'].pct_change().dropna()
returns = df['returns'].dropna().values

def hurst_rs(series):
    """R/S 분석법으로 허스트 지수 계산"""
    n = len(series)
    if n < 20:
        return 0.5

    # 다양한 구간 크기
    sizes = []
    rs_values = []

    for size in range(10, n // 2):
        n_chunks = n // size
        rs_chunk = []

        for i in range(n_chunks):
            chunk = series[i * size:(i + 1) * size]
            mean_chunk = chunk.mean()
            deviate = np.cumsum(chunk - mean_chunk)
            R = deviate.max() - deviate.min()
            S = chunk.std(ddof=1)
            if S > 0:
                rs_chunk.append(R / S)

        if rs_chunk:
            sizes.append(size)
            rs_values.append(np.mean(rs_chunk))

    # 로그-로그 회귀
    log_sizes = np.log(sizes)
    log_rs = np.log(rs_values)
    hurst, _ = np.polyfit(log_sizes, log_rs, 1)

    return hurst

H = hurst_rs(returns)
print(f"허스트 지수 (R/S): {H:.4f}")
if H > 0.55:
    print("→ 지속성: 추세추종 전략 유리")
elif H < 0.45:
    print("→ 반지속성: 평균회귀 전략 유리")
else:
    print("→ 랜덤워크 근접: 매매 신중")

DFA 방법: 더 정확한 허스트 추정

Detrended Fluctuation Analysis(DFA)는 R/S 분석의 한계를 보완한 방법으로, 비정상(non-stationary) 시계열에서도 정확한 허스트 지수를 추정할 수 있습니다.

def hurst_dfa(series):
    """DFA(Detrended Fluctuation Analysis)로 허스트 지수 계산"""
    n = len(series)
    cumsum = np.cumsum(series - series.mean())

    scales = np.unique(np.logspace(
        np.log10(10), np.log10(n // 4), num=20
    ).astype(int))

    fluctuations = []
    for scale in scales:
        n_segments = n // scale
        if n_segments < 1:
            continue

        fluct = []
        for i in range(n_segments):
            segment = cumsum[i * scale:(i + 1) * scale]
            x = np.arange(scale)
            # 선형 추세 제거
            coeffs = np.polyfit(x, segment, 1)
            trend = np.polyval(coeffs, x)
            detrended = segment - trend
            fluct.append(np.sqrt(np.mean(detrended ** 2)))

        fluctuations.append(np.mean(fluct))

    # 로그-로그 회귀
    log_scales = np.log(scales[:len(fluctuations)])
    log_fluct = np.log(fluctuations)
    hurst, _ = np.polyfit(log_scales, log_fluct, 1)

    return hurst

H_dfa = hurst_dfa(returns)
print(f"허스트 지수 (DFA): {H_dfa:.4f}")

롤링 허스트로 실시간 레짐 감지

허스트 지수를 롤링 윈도우로 계산하면, 시장이 추세 국면에서 레인지 국면으로 전환되는 시점을 실시간으로 포착할 수 있습니다.

def rolling_hurst(returns_series, window=100, step=5):
    """롤링 허스트 지수 계산"""
    indices = []
    hurst_values = []

    for i in range(window, len(returns_series), step):
        chunk = returns_series[i - window:i]
        h = hurst_dfa(chunk)
        indices.append(i)
        hurst_values.append(h)

    return pd.Series(hurst_values, index=indices)

hurst_series = rolling_hurst(returns, window=100, step=5)

# 레짐 전환점 감지
def detect_regime_shift(hurst_series, threshold=0.5):
    """허스트 지수 기반 레짐 전환 감지"""
    regimes = []
    for h in hurst_series:
        if h > 0.6:
            regimes.append('STRONG_TREND')
        elif h > 0.5:
            regimes.append('MILD_TREND')
        elif h > 0.4:
            regimes.append('MILD_REVERT')
        else:
            regimes.append('STRONG_REVERT')
    return regimes

regimes = detect_regime_shift(hurst_series)
print(f"현재 레짐: {regimes[-1]}")
print(f"현재 허스트: {hurst_series.iloc[-1]:.4f}")

허스트 기반 전략 전환 시스템

핵심 아이디어는 간단합니다: 허스트가 높으면 추세추종, 낮으면 평균회귀. 이 원칙을 자동매매 시스템에 통합합니다.

class HurstAdaptiveStrategy:
    def __init__(self, trend_threshold=0.55, revert_threshold=0.45):
        self.trend_th = trend_threshold
        self.revert_th = revert_threshold

    def trend_following(self, df, period=20):
        """추세추종: 이동평균 교차"""
        sma_fast = df['close'].rolling(period).mean().iloc[-1]
        sma_slow = df['close'].rolling(period * 3).mean().iloc[-1]
        if sma_fast > sma_slow:
            return 'BUY'
        elif sma_fast < sma_slow:
            return 'SELL'
        return 'HOLD'

    def mean_reversion(self, df, period=20, z_entry=1.5):
        """평균회귀: 볼린저 밴드 역추세"""
        sma = df['close'].rolling(period).mean().iloc[-1]
        std = df['close'].rolling(period).std().iloc[-1]
        price = df['close'].iloc[-1]
        z = (price - sma) / std if std > 0 else 0

        if z < -z_entry:
            return 'BUY'   # 과매도 → 반등 기대
        elif z > z_entry:
            return 'SELL'  # 과매수 → 하락 기대
        return 'HOLD'

    def execute(self, df, hurst_value):
        """허스트 값에 따라 전략 자동 전환"""
        if hurst_value > self.trend_th:
            strategy = 'TREND_FOLLOW'
            signal = self.trend_following(df)
            confidence = min((hurst_value - 0.5) * 4, 1.0)
        elif hurst_value < self.revert_th:
            strategy = 'MEAN_REVERT'
            signal = self.mean_reversion(df)
            confidence = min((0.5 - hurst_value) * 4, 1.0)
        else:
            strategy = 'NEUTRAL'
            signal = 'HOLD'
            confidence = 0.0

        return {
            'hurst': hurst_value,
            'strategy': strategy,
            'signal': signal,
            'confidence': confidence
        }

# 사용 예시
adapter = HurstAdaptiveStrategy()
# result = adapter.execute(df, hurst_series.iloc[-1])

멀티 타임프레임 허스트 분석

단일 타임프레임의 허스트만으로는 노이즈에 취약합니다. 여러 타임프레임의 허스트를 종합하면 더 견고한 판단이 가능합니다.

def multi_timeframe_hurst(exchange, symbol='BTC/USDT'):
    """다중 타임프레임 허스트 분석"""
    timeframes = {'1h': 168, '4h': 180, '1d': 200}
    results = {}

    for tf, limit in timeframes.items():
        ohlcv = exchange.fetch_ohlcv(symbol, tf, limit=limit)
        closes = pd.DataFrame(ohlcv, columns=['ts','o','h','l','c','v'])
        rets = closes['c'].pct_change().dropna().values
        h = hurst_dfa(rets)
        results[tf] = h

    # 가중 평균 (긴 타임프레임에 더 높은 가중치)
    weights = {'1h': 0.2, '4h': 0.3, '1d': 0.5}
    weighted_h = sum(results[tf] * weights[tf] for tf in timeframes)

    return results, weighted_h

# results, avg_hurst = multi_timeframe_hurst(exchange)
# print(f"가중 허스트: {avg_hurst:.4f}")

실전 적용 시 주의사항

  • 충분한 데이터: 허스트 지수는 최소 100개 이상의 데이터 포인트가 필요합니다. 짧은 윈도우에서는 추정 오차가 커집니다.
  • 계산 방법 비교: R/S와 DFA 모두 계산하여 두 값이 일치하는지 교차 검증하세요. 큰 차이가 나면 데이터 특성을 재확인해야 합니다.
  • 0.5 근처 주의: H가 0.48~0.52 범위면 랜덤워크와 구별이 어렵습니다. 이 구간에서는 엔트로피 분석을 병행하여 보완하세요.
  • 구조적 변화: 허스트 지수 자체가 급변하면 시장 구조가 바뀌고 있다는 신호입니다. 꼬리 위험 헤지를 활성화하는 트리거로 활용할 수 있습니다.

마무리: 추세인가 레인지인가, 허스트가 답한다

많은 트레이더가 겪는 핵심 문제는 "지금 추세추종을 해야 하는가, 평균회귀를 해야 하는가"입니다. 추세장에서 역추세 매매를 하면 손절이 반복되고, 레인지에서 추세추종을 하면 휩소에 당합니다. 허스트 지수는 이 근본적 질문에 수학적 답을 제공합니다.

자동매매 시스템에 허스트 기반 전략 전환을 내장하면, 시장 환경이 바뀔 때마다 수동으로 전략을 교체할 필요 없이 시스템이 스스로 적응합니다. 이것이 진정한 적응형 퀀트 시스템의 출발점입니다.

허스트 범위 시장 성질 최적 전략 대표 지표
0.7 이상 강한 추세 모멘텀, 돌파 이동평균 교차
0.5~0.7 약한 추세 추세추종 (소량) ADX, MACD
0.3~0.5 약한 회귀 평균회귀 (소량) 볼린저 밴드
0.3 미만 강한 회귀 RSI 역추세 RSI, 스토캐스틱
위로 스크롤
WordPress Appliance - Powered by TurnKey Linux