시장 레짐 감지 퀀트 전략

시장 레짐이란?

시장 레짐(Market Regime)이란 시장이 특정 기간 동안 보이는 구조적 상태를 의미합니다. 크게 추세장(Trending), 횡보장(Mean-Reverting), 고변동성 위기(Crisis) 세 가지로 분류됩니다. 같은 전략이라도 레짐에 따라 성과가 극적으로 달라지기 때문에, 현재 시장이 어떤 레짐인지 파악하는 것이 퀀트 투자의 핵심입니다.

추세추종 전략은 추세장에서 빛나지만 횡보장에서 손실이 쌓이고, 평균회귀 전략은 정반대입니다. 레짐 감지(Regime Detection)를 통해 전략을 동적으로 전환하면 전체 수익률을 크게 개선할 수 있습니다.

레짐 감지 핵심 방법론

퀀트 트레이딩에서 사용되는 주요 레짐 감지 기법을 정리했습니다.

방법 원리 장단점
히든 마코프 모델 (HMM) 관측 불가능한 상태를 확률적으로 추정 이론적으로 강력, 파라미터 민감
변동성 클러스터링 변동성 수준으로 레짐 분류 직관적, 구현 간단
이동평균 기울기 MA 방향과 강도로 추세 판단 실시간 적용 용이, 후행성
ADX 지표 추세 강도 수치화 (0~100) 추세/횡보 구분 명확, 방향 미표시

히든 마코프 모델(HMM) 구현

HMM은 시장 레짐 감지의 학술적 표준입니다. 수익률 데이터에서 관측할 수 없는 숨겨진 상태(레짐)를 확률적으로 추정합니다. 파이썬의 hmmlearn 라이브러리로 구현할 수 있습니다.

import numpy as np
import pandas as pd
from hmmlearn.hmm import GaussianHMM

def detect_regimes_hmm(prices, n_regimes=3):
    """HMM 기반 시장 레짐 감지

    Args:
        prices: 종가 시계열 (pd.Series)
        n_regimes: 레짐 수 (기본 3: 강세/약세/횡보)

    Returns:
        레짐 라벨이 포함된 DataFrame
    """
    # 수익률 + 변동성 피처 생성
    returns = np.log(prices / prices.shift(1)).dropna()
    volatility = returns.rolling(20).std().dropna()

    # 피처 정렬
    df = pd.DataFrame({
        'returns': returns,
        'volatility': volatility
    }).dropna()

    features = df[['returns', 'volatility']].values

    # HMM 학습
    model = GaussianHMM(
        n_components=n_regimes,
        covariance_type='full',
        n_iter=200,
        random_state=42
    )
    model.fit(features)

    # 레짐 예측
    regimes = model.predict(features)
    df['regime'] = regimes

    # 레짐별 특성 출력
    for i in range(n_regimes):
        mask = df['regime'] == i
        avg_ret = df.loc[mask, 'returns'].mean() * 252
        avg_vol = df.loc[mask, 'volatility'].mean() * np.sqrt(252)
        print(f"레짐 {i}: 연간수익률 {avg_ret:.1%}, 연간변동성 {avg_vol:.1%}")

    return df

변동성 기반 간편 레짐 분류

HMM이 부담스럽다면 변동성 수준만으로 레짐을 분류하는 간편한 방법도 효과적입니다. 실시간 적용이 쉽고 직관적이라 실전에서 많이 사용됩니다.

def volatility_regime(prices, lookback=20):
    """변동성 기반 레짐 분류

    Returns:
        'low_vol': 저변동 횡보장
        'normal': 보통 시장
        'high_vol': 고변동 위기/추세장
    """
    returns = np.log(prices / prices.shift(1)).dropna()
    current_vol = returns.iloc[-lookback:].std()
    historical_vol = returns.std()

    vol_ratio = current_vol / historical_vol

    if vol_ratio < 0.7:
        return 'low_vol', vol_ratio
    elif vol_ratio > 1.5:
        return 'high_vol', vol_ratio
    else:
        return 'normal', vol_ratio

def adx_regime(high, low, close, period=14):
    """ADX 기반 추세/횡보 판단"""
    # True Range
    tr = pd.DataFrame({
        'hl': high - low,
        'hc': abs(high - close.shift(1)),
        'lc': abs(low - close.shift(1))
    }).max(axis=1)

    # +DM, -DM
    plus_dm = np.where(
        (high - high.shift(1)) > (low.shift(1) - low),
        np.maximum(high - high.shift(1), 0), 0
    )
    minus_dm = np.where(
        (low.shift(1) - low) > (high - high.shift(1)),
        np.maximum(low.shift(1) - low, 0), 0
    )

    # 평활화
    atr = pd.Series(tr).rolling(period).mean()
    plus_di = 100 * pd.Series(plus_dm).rolling(period).mean() / atr
    minus_di = 100 * pd.Series(minus_dm).rolling(period).mean() / atr

    dx = 100 * abs(plus_di - minus_di) / (plus_di + minus_di)
    adx = dx.rolling(period).mean()
    current_adx = adx.iloc[-1]

    if current_adx > 25:
        return 'trending', current_adx
    else:
        return 'ranging', current_adx

레짐 기반 전략 스위칭

레짐 감지의 진짜 가치는 전략 스위칭에 있습니다. 레짐에 따라 최적의 전략을 자동으로 전환하면 단일 전략 대비 월등한 성과를 기대할 수 있습니다.

class RegimeSwitch:
    """레짐 기반 전략 스위칭 시스템"""

    def __init__(self):
        self.strategies = {
            'trending': self.trend_following,
            'ranging': self.mean_reversion,
            'high_vol': self.risk_off,
        }

    def trend_following(self, prices):
        """추세장: 이동평균 크로스오버"""
        ma_short = prices.rolling(10).mean().iloc[-1]
        ma_long = prices.rolling(30).mean().iloc[-1]
        if ma_short > ma_long:
            return 'BUY', 1.0
        elif ma_short < ma_long:
            return 'SELL', 1.0
        return 'HOLD', 0

    def mean_reversion(self, prices):
        """횡보장: 볼린저 밴드 평균회귀"""
        ma = prices.rolling(20).mean().iloc[-1]
        std = prices.rolling(20).std().iloc[-1]
        current = prices.iloc[-1]
        z_score = (current - ma) / std

        if z_score < -2:
            return 'BUY', abs(z_score) / 3
        elif z_score > 2:
            return 'SELL', abs(z_score) / 3
        return 'HOLD', 0

    def risk_off(self, prices):
        """고변동: 포지션 축소 또는 현금 보유"""
        return 'REDUCE', 0.3

    def execute(self, prices, high, low, close):
        """레짐 감지 후 전략 실행"""
        # 1단계: 레짐 감지
        vol_regime, vol_ratio = volatility_regime(prices)
        adx_label, adx_val = adx_regime(high, low, close)

        # 2단계: 레짐 결정
        if vol_regime == 'high_vol':
            regime = 'high_vol'
        elif adx_label == 'trending':
            regime = 'trending'
        else:
            regime = 'ranging'

        # 3단계: 전략 실행
        strategy_fn = self.strategies[regime]
        action, confidence = strategy_fn(prices)
        print(f"레짐: {regime} | 전략: {strategy_fn.__name__} | "
              f"신호: {action} ({confidence:.1%})")
        return regime, action, confidence

레짐 전환 감지와 리스크 관리

레짐이 전환되는 순간이 가장 위험합니다. 추세장에서 횡보장으로 바뀌는데 추세추종 전략을 유지하면 연속 손절이 발생합니다. 레짐 전환을 빠르게 감지하는 것이 핵심입니다.

  • 전환 버퍼: 레짐 변경 후 2~3일 확인 기간을 두고 전략 전환 (위플래시 방지)
  • 포지션 축소: 레짐 불확실 시 포지션을 50%로 줄여 리스크 관리
  • 복합 확인: ADX + 변동성 + HMM 중 2개 이상 동의할 때만 레짐 확정
  • 손절 조정: 고변동 레짐에서는 손절폭을 1.5~2배로 확대

변동성 돌파 자동매매 전략은 추세장 레짐에서 특히 효과적이며, 레짐 감지와 결합하면 진입 타이밍을 크게 개선할 수 있습니다.

백테스트: 레짐 스위칭 효과

레짐 스위칭이 실제로 효과가 있는지 확인하려면 엄밀한 백테스트가 필수입니다. 핵심은 미래 데이터 유출(look-ahead bias)을 철저히 방지하는 것입니다.

def backtest_regime_switch(prices, high, low, close):
    """레짐 스위칭 백테스트 (단순화)"""
    switcher = RegimeSwitch()
    results = []
    position = 0  # -1, 0, 1

    for i in range(60, len(prices)):
        window = prices.iloc[:i]
        h = high.iloc[:i]
        l = low.iloc[:i]
        c = close.iloc[:i]

        regime, action, conf = switcher.execute(window, h, l, c)

        # 포지션 업데이트
        if action == 'BUY' and position <= 0:
            position = 1
        elif action == 'SELL' and position >= 0:
            position = -1
        elif action == 'REDUCE':
            position = 0

        # 일간 수익률
        daily_return = (prices.iloc[i] / prices.iloc[i-1] - 1) * position
        results.append({
            'date': prices.index[i],
            'regime': regime,
            'position': position,
            'return': daily_return
        })

    df = pd.DataFrame(results).set_index('date')
    total_return = (1 + df['return']).cumprod().iloc[-1] - 1
    sharpe = df['return'].mean() / df['return'].std() * np.sqrt(252)
    print(f"총 수익률: {total_return:.1%} | 샤프비율: {sharpe:.2f}")
    return df

백테스트 과최적화 방지법을 참고하여 레짐 감지 파라미터의 과적합을 반드시 점검하세요.

정리

시장 레짐 감지는 퀀트 투자의 메타 전략입니다. 핵심 포인트를 정리하면:

  • 시장은 추세장·횡보장·고변동 위기 등 구조적 상태를 반복
  • HMM, 변동성 클러스터링, ADX로 현재 레짐 감지 가능
  • 레짐에 따라 추세추종/평균회귀/리스크오프 전략 자동 전환
  • 레짐 전환 시점이 가장 위험 — 버퍼 기간과 포지션 축소 필수
  • 복수 감지 방법의 합의(consensus)로 정확도 향상

단일 전략의 한계를 넘어, 시장 상태에 맞는 전략을 동적으로 운영하는 것이 진정한 퀀트 투자의 핵심입니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux