마켓 레짐이란 무엇인가
금융 시장은 항상 같은 방식으로 움직이지 않습니다. 상승 추세(Bull), 하락 추세(Bear), 횡보(Sideways) 등 서로 다른 시장 국면(Market Regime)이 존재하며, 각 국면에서 최적의 전략은 완전히 다릅니다. 추세 추종 전략은 횡보장에서 손실을 내고, 평균 회귀 전략은 강한 추세장에서 큰 손실을 입습니다.
마켓 레짐 감지(Market Regime Detection)란 현재 시장이 어떤 국면에 있는지 통계적으로 판별하는 기법입니다. 이를 자동매매 시스템에 결합하면 국면별로 전략을 전환하거나 포지션 크기를 조절할 수 있습니다.
HMM(은닉 마르코프 모델) 기반 레짐 감지
Hidden Markov Model(HMM)은 마켓 레짐 감지에 가장 널리 쓰이는 통계 모델입니다. 시장의 관측 가능한 수익률 뒤에 ‘은닉된’ 상태(레짐)가 존재한다고 가정하고, 각 레짐의 수익률 분포와 전이 확률을 추정합니다.
HMM의 핵심 구성 요소
- 은닉 상태(Hidden States): 관측할 수 없는 시장 국면 (예: 저변동 상승, 고변동 하락)
- 관측값(Observations): 실제 수익률 데이터
- 전이 확률(Transition Probability): 한 레짐에서 다른 레짐으로 전환될 확률
- 방출 확률(Emission Probability): 각 레짐에서 특정 수익률이 나올 확률 분포
파이썬 HMM 레짐 감지 구현
import numpy as np
import pandas as pd
from hmmlearn.hmm import GaussianHMM
import yfinance as yf
# 데이터 수집
data = yf.download("SPY", start="2020-01-01", end="2025-12-31")
returns = data['Close'].pct_change().dropna()
# 특성 행렬 구성: 수익률 + 변동성
features = pd.DataFrame({
'returns': returns,
'volatility': returns.rolling(20).std()
}).dropna()
# 2-상태 가우시안 HMM 학습
model = GaussianHMM(
n_components=2,
covariance_type="full",
n_iter=1000,
random_state=42
)
model.fit(features.values)
# 레짐 예측
regimes = model.predict(features.values)
features['regime'] = regimes
# 각 레짐별 통계
for i in range(2):
mask = features['regime'] == i
mean_ret = features.loc[mask, 'returns'].mean() * 252
vol = features.loc[mask, 'returns'].std() * np.sqrt(252)
print(f"Regime {i}: 연간수익률={mean_ret:.2%}, 연간변동성={vol:.2%}")
레짐별 전략 전환 시스템
레짐을 감지한 후 각 국면에 맞는 전략을 자동으로 전환하는 것이 핵심입니다. 아래는 레짐별 전략 매핑 예시입니다.
| 레짐 | 특성 | 적합 전략 | 포지션 크기 |
|---|---|---|---|
| 저변동 상승 | 낮은 변동성, 양의 수익률 | 추세 추종, 모멘텀 | 100% |
| 고변동 하락 | 높은 변동성, 음의 수익률 | 숏 전략, 헤지, 현금 보유 | 30~50% |
| 횡보 | 중간 변동성, 평균 회귀 | 평균 회귀, 박스권 매매 | 50~70% |
레짐 전환 자동매매 코드
class RegimeSwitchingStrategy:
"""레짐 전환 기반 자동매매 전략"""
def __init__(self, hmm_model, lookback=60):
self.model = hmm_model
self.lookback = lookback
self.strategies = {
0: self.momentum_strategy, # 저변동 상승
1: self.defensive_strategy, # 고변동 하락
}
self.position_scales = {0: 1.0, 1: 0.3}
def detect_regime(self, recent_data):
"""최근 데이터로 현재 레짐 판별"""
features = self._compute_features(recent_data)
regime = self.model.predict(features)[-1]
proba = self.model.predict_proba(features)[-1]
return regime, proba
def momentum_strategy(self, data):
"""추세 추종: 20일 이평선 돌파"""
sma20 = data['close'].rolling(20).mean()
if data['close'].iloc[-1] > sma20.iloc[-1]:
return 'BUY'
return 'SELL'
def defensive_strategy(self, data):
"""방어적: RSI 과매도 반등만 진입"""
rsi = self._compute_rsi(data['close'], 14)
if rsi.iloc[-1] < 30:
return 'BUY'
elif rsi.iloc[-1] > 70:
return 'SELL'
return 'HOLD'
def generate_signal(self, data):
"""레짐 감지 → 전략 선택 → 신호 생성"""
regime, proba = self.detect_regime(data)
confidence = proba[regime]
# 레짐 확신도가 낮으면 포지션 축소
if confidence < 0.7:
scale = self.position_scales[regime] * 0.5
else:
scale = self.position_scales[regime]
strategy_fn = self.strategies[regime]
signal = strategy_fn(data)
return {
'signal': signal,
'regime': regime,
'confidence': confidence,
'position_scale': scale
}
def _compute_features(self, data):
returns = data['close'].pct_change().dropna()
vol = returns.rolling(20).std()
features = pd.DataFrame({
'returns': returns, 'volatility': vol
}).dropna()
return features.values
def _compute_rsi(self, prices, period=14):
delta = prices.diff()
gain = delta.where(delta > 0, 0).rolling(period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(period).mean()
rs = gain / loss
return 100 - (100 / (1 + rs))
레짐 감지 고도화 기법
기본 HMM 외에도 레짐 감지 정확도를 높이는 다양한 기법이 있습니다.
1. 다중 특성 활용
수익률과 변동성 외에 거래량, VIX, 금리 스프레드 등을 추가하면 레짐 분류 정확도가 향상됩니다.
# 다중 특성 HMM
features = pd.DataFrame({
'returns': returns,
'volatility': returns.rolling(20).std(),
'volume_change': data['Volume'].pct_change().rolling(10).mean(),
'high_low_range': ((data['High'] - data['Low']) / data['Close']).rolling(10).mean()
}).dropna()
model_multi = GaussianHMM(n_components=3, covariance_type="full", n_iter=1000)
model_multi.fit(features.values)
2. 온라인 학습과 적응
시장 구조는 시간이 지나면서 변합니다. 정기적으로 HMM을 재학습하여 최신 시장 패턴에 적응시켜야 합니다.
class AdaptiveRegimeDetector:
"""주기적 재학습으로 시장 변화에 적응"""
def __init__(self, n_regimes=2, retrain_days=60):
self.n_regimes = n_regimes
self.retrain_days = retrain_days
self.model = None
self.last_train_date = None
def fit_or_update(self, data, current_date):
needs_retrain = (
self.model is None or
(current_date - self.last_train_date).days >= self.retrain_days
)
if needs_retrain:
features = self._prepare_features(data)
self.model = GaussianHMM(
n_components=self.n_regimes,
covariance_type="full",
n_iter=500
)
self.model.fit(features)
self.last_train_date = current_date
return self.model
3. 레짐 전환 필터링
잦은 레짐 전환은 과도한 매매를 유발합니다. 최소 유지 기간이나 확률 임계값을 설정하여 변동성 기반 포트폴리오 관리와 결합하면 안정적인 시스템을 구축할 수 있습니다.
def filter_regime_changes(regimes, probas, min_duration=5, min_confidence=0.75):
"""레짐 전환 노이즈 필터링"""
filtered = regimes.copy()
current_regime = regimes[0]
days_in_regime = 0
for i in range(len(regimes)):
if regimes[i] != current_regime:
# 새 레짐 확률이 충분히 높고, 기존 레짐 유지 기간이 최소 이상일 때만 전환
if probas[i][regimes[i]] >= min_confidence and days_in_regime >= min_duration:
current_regime = regimes[i]
days_in_regime = 0
else:
filtered[i] = current_regime
days_in_regime += 1
return filtered
백테스트: 레짐 전환 vs 단일 전략
레짐 전환 전략의 효과를 검증하기 위해 단일 전략 대비 성과를 비교해보겠습니다.
def backtest_regime_switching(data, model, initial_capital=10000):
"""레짐 전환 전략 백테스트"""
features = compute_features(data)
regimes = model.predict(features)
probas = model.predict_proba(features)
# 레짐 필터링 적용
regimes = filter_regime_changes(regimes, probas)
capital = initial_capital
position = 0
results = []
for i in range(1, len(data)):
regime = regimes[i]
price = data['close'].iloc[i]
prev_price = data['close'].iloc[i-1]
ret = (price - prev_price) / prev_price
# 레짐별 포지션 비율
target_exposure = {0: 1.0, 1: -0.3, 2: 0.0}
exposure = target_exposure.get(regime, 0)
# 수익 계산
pnl = capital * exposure * ret
capital += pnl
results.append({
'date': data.index[i],
'capital': capital,
'regime': regime,
'exposure': exposure
})
results_df = pd.DataFrame(results)
total_return = (capital - initial_capital) / initial_capital
sharpe = (results_df['capital'].pct_change().mean() /
results_df['capital'].pct_change().std()) * np.sqrt(252)
print(f"총 수익률: {total_return:.2%}")
print(f"샤프 비율: {sharpe:.2f}")
print(f"최대 낙폭: {compute_max_drawdown(results_df['capital']):.2%}")
return results_df
def compute_max_drawdown(equity_curve):
peak = equity_curve.expanding().max()
drawdown = (equity_curve - peak) / peak
return drawdown.min()
실전 적용 시 주의사항
마켓 레짐 감지를 실전에 적용할 때 반드시 고려해야 할 사항들입니다.
- 과적합 위험: HMM의 상태 수를 너무 많이 설정하면 과적합됩니다. 보통 2~3개가 적절합니다.
- 레짐 전환 지연: HMM은 후행 지표입니다. 레짐 전환 신호가 실제 전환보다 늦게 나올 수 있으므로, 전환 초기 손실을 감수할 준비가 필요합니다.
- 거래 비용: 레짐 전환 시 전략 변경에 따른 포지션 재조정 비용을 반드시 포함하여 백테스트해야 합니다.
- 샘플 외 검증: 반드시 워크포워드 분석으로 샘플 외 성과를 검증하세요.
- 다중 자산 적용: 단일 자산보다 여러 자산의 레짐을 동시에 추적하면 더 강건한 시스템을 구축할 수 있습니다.
마무리
마켓 레짐 감지는 자동매매 시스템의 적응력을 크게 높여주는 핵심 기법입니다. HMM을 기반으로 시장 국면을 분류하고, 각 국면에 맞는 전략을 자동 전환함으로써 단일 전략의 한계를 극복할 수 있습니다. 핵심은 과적합을 피하면서도 충분한 적응력을 갖추는 균형을 찾는 것입니다. 레짐 전환 필터링과 주기적 재학습을 결합하면 실전에서도 안정적으로 작동하는 시스템을 구축할 수 있습니다.