몬테카를로 시뮬레이션 투자 활용

몬테카를로 시뮬레이션이란

몬테카를로 시뮬레이션(Monte Carlo Simulation)은 난수를 이용해 수천~수만 번의 시나리오를 생성하고, 그 결과의 확률 분포를 분석하는 통계적 기법입니다. 이름은 도박의 도시 몬테카를로에서 유래했으며, 맨해튼 프로젝트에서 핵반응 시뮬레이션에 처음 사용되었습니다.

퀀트 투자에서 몬테카를로 시뮬레이션은 미래 수익률의 불확실성을 정량화하는 핵심 도구입니다. 단일 시나리오가 아닌 수천 가지 가능한 경로를 생성해 포트폴리오의 리스크를 측정하고, 전략의 견고성을 검증합니다.

투자에서 몬테카를로가 필요한 이유

전통적 백테스트는 과거 하나의 경로만 검증합니다. 하지만 과거가 반복된다는 보장은 없습니다. 몬테카를로 시뮬레이션은 이 한계를 극복합니다:

  • 확률적 리스크 측정: “최악의 경우 얼마나 잃을 수 있는가”를 확률로 답할 수 있습니다
  • 전략 견고성 검증: 백테스트 과적합을 방지하고 다양한 시장 환경에서의 성과를 예측합니다
  • 포지션 사이징: 파산 확률을 기반으로 최적 투자 비중을 결정합니다
  • VaR 계산: Value at Risk를 정밀하게 추정합니다

기하 브라운 운동(GBM) 기반 가격 시뮬레이션

주가 시뮬레이션의 가장 기본적인 모델은 기하 브라운 운동(Geometric Brownian Motion)입니다. 주가가 로그정규분포를 따른다고 가정하고, 드리프트(평균 수익률)와 변동성으로 미래 경로를 생성합니다.

import numpy as np
import matplotlib.pyplot as plt

def monte_carlo_gbm(S0, mu, sigma, T, dt, n_simulations):
    """
    기하 브라운 운동 기반 몬테카를로 시뮬레이션
    
    S0: 초기 가격
    mu: 연간 기대 수익률
    sigma: 연간 변동성
    T: 시뮬레이션 기간 (년)
    dt: 시간 간격 (1/252 = 일봉)
    n_simulations: 시뮬레이션 횟수
    """
    n_steps = int(T / dt)
    paths = np.zeros((n_steps + 1, n_simulations))
    paths[0] = S0
    
    for t in range(1, n_steps + 1):
        z = np.random.standard_normal(n_simulations)
        paths[t] = paths[t-1] * np.exp(
            (mu - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * z
        )
    
    return paths

# 예시: 삼성전자 시뮬레이션
paths = monte_carlo_gbm(
    S0=70000,          # 현재가 7만원
    mu=0.08,           # 연 8% 기대 수익률
    sigma=0.25,        # 연 25% 변동성
    T=1,               # 1년
    dt=1/252,          # 일봉
    n_simulations=10000 # 1만 번 시뮬레이션
)

위 코드는 1만 개의 가능한 가격 경로를 생성합니다. 이 경로들의 분포를 분석하면 1년 후 가격이 어떤 범위에 있을 확률이 높은지 파악할 수 있습니다.

VaR(Value at Risk) 계산

몬테카를로 시뮬레이션의 가장 대표적인 투자 활용은 VaR 계산입니다. VaR는 “특정 신뢰 수준에서 일정 기간 내 최대 예상 손실”을 의미합니다.

def calculate_var(paths, confidence=0.95):
    """몬테카를로 VaR 계산"""
    final_prices = paths[-1]
    initial_price = paths[0, 0]
    
    # 수익률 분포
    returns = (final_prices - initial_price) / initial_price
    
    # VaR: 하위 (1-confidence) 백분위수
    var = np.percentile(returns, (1 - confidence) * 100)
    
    # CVaR (Expected Shortfall): VaR 이하 평균 손실
    cvar = returns[returns <= var].mean()
    
    return {
        'VaR_95': f'{var:.2%}',
        'CVaR_95': f'{cvar:.2%}',
        'median_return': f'{np.median(returns):.2%}',
        'prob_loss': f'{(returns < 0).mean():.1%}',
        'worst_case': f'{returns.min():.2%}',
        'best_case': f'{returns.max():.2%}'
    }

result = calculate_var(paths)
# 예: {'VaR_95': '-30.12%', 'CVaR_95': '-38.45%', ...}

95% VaR가 -30%라면, "95% 확률로 1년 내 손실이 30%를 넘지 않는다"는 의미입니다. CVaR(조건부 VaR)는 VaR를 초과하는 극단 손실의 평균으로, 꼬리 리스크를 더 정확히 측정합니다.

자동매매 전략 검증에 활용

몬테카를로 시뮬레이션은 자동매매 전략의 견고성 테스트에도 강력합니다. 핵심 방법은 두 가지입니다:

1. 수익률 셔플링(Bootstrap)

실제 매매 기록의 수익률을 무작위로 재배열해 수천 개의 가상 성과를 생성합니다. 이를 통해 전략의 샤프 비율이 우연이 아닌지 확인할 수 있습니다.

def bootstrap_strategy(trade_returns, n_simulations=5000):
    """매매 수익률 부트스트랩으로 전략 견고성 검증"""
    n_trades = len(trade_returns)
    max_drawdowns = []
    sharpe_ratios = []
    
    for _ in range(n_simulations):
        # 수익률 무작위 재배열
        sampled = np.random.choice(trade_returns, size=n_trades, replace=True)
        equity = np.cumprod(1 + sampled)
        
        # 최대 낙폭
        peak = np.maximum.accumulate(equity)
        dd = (equity - peak) / peak
        max_drawdowns.append(dd.min())
        
        # 샤프 비율
        sr = sampled.mean() / sampled.std() * np.sqrt(252)
        sharpe_ratios.append(sr)
    
    return {
        'median_mdd': f'{np.median(max_drawdowns):.2%}',
        'worst_mdd_95': f'{np.percentile(max_drawdowns, 5):.2%}',
        'median_sharpe': f'{np.median(sharpe_ratios):.2f}',
        'prob_positive_sharpe': f'{(np.array(sharpe_ratios) > 0).mean():.1%}'
    }

2. 파라미터 민감도 분석

전략 파라미터에 무작위 노이즈를 추가해 성과가 얼마나 달라지는지 측정합니다. 파라미터를 조금만 바꿔도 성과가 급변한다면 과적합 신호입니다.

포트폴리오 최적화 시뮬레이션

여러 자산에 투자할 때 몬테카를로를 활용한 효율적 프론티어(Efficient Frontier) 탐색도 가능합니다.

def efficient_frontier_mc(returns_df, n_portfolios=10000, risk_free=0.03):
    """몬테카를로로 효율적 프론티어 탐색"""
    n_assets = returns_df.shape[1]
    results = []
    
    for _ in range(n_portfolios):
        # 랜덤 비중 생성
        weights = np.random.random(n_assets)
        weights /= weights.sum()
        
        # 포트폴리오 수익률·변동성
        port_return = np.dot(weights, returns_df.mean()) * 252
        port_vol = np.sqrt(
            np.dot(weights.T, np.dot(returns_df.cov() * 252, weights))
        )
        sharpe = (port_return - risk_free) / port_vol
        
        results.append({
            'return': port_return,
            'volatility': port_vol,
            'sharpe': sharpe,
            'weights': weights
        })
    
    return results

1만 개의 랜덤 포트폴리오 중 최대 샤프 비율 포트폴리오최소 변동성 포트폴리오를 찾아 최적 자산 배분을 결정합니다. 리밸런싱 전략과 결합하면 더욱 효과적입니다.

시뮬레이션 횟수와 정확도

시뮬레이션 횟수 정확도 소요 시간 (참고) 적합한 용도
1,000회 대략적 추정 < 1초 빠른 프로토타이핑
10,000회 실용적 수준 수 초 일반 분석·VaR
100,000회 높은 정밀도 수십 초 꼬리 리스크·CVaR
1,000,000회 매우 정밀 수 분 정밀 옵션 가격·극단 시나리오

시뮬레이션 횟수를 N배 늘리면 오차는 √N배 줄어듭니다. 실무에서는 1만~10만 회가 비용 대비 효과가 가장 좋습니다.

실전 적용 시 주의사항

  • 가정의 한계: GBM은 수익률이 정규분포를 따른다고 가정하지만, 실제 시장은 팻 테일(fat tail)이 존재합니다. Student-t 분포나 점프 확산 모델을 고려하세요.
  • 입력 데이터 품질: 평균 수익률과 변동성 추정이 부정확하면 시뮬레이션 전체가 무의미합니다. 충분한 기간의 데이터를 사용하세요.
  • 상관관계 변동: 위기 상황에서는 자산 간 상관관계가 급등합니다. 고정 상관행렬 대신 스트레스 시나리오를 별도로 테스트하세요.
  • 리스크 관리와 병행: 몬테카를로 결과는 의사결정 보조 도구입니다. 단독으로 매매 판단에 사용하지 마세요.

마무리

몬테카를로 시뮬레이션은 불확실성을 숫자로 바꾸는 가장 실용적인 도구입니다. VaR 계산, 전략 견고성 검증, 포트폴리오 최적화까지 퀀트 투자의 거의 모든 영역에서 활용됩니다. NumPy만으로 충분히 구현 가능하니, 여러분의 자동매매 시스템에 몬테카를로 시뮬레이션을 추가해 리스크를 정량적으로 관리해 보세요.

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