켈리 기준 자금 관리법

켈리 기준이란?

켈리 기준(Kelly Criterion)은 기대값이 양수인 베팅에서 자본 성장률을 극대화하는 최적 비율을 계산하는 공식입니다. 1956년 존 켈리(John Kelly)가 정보 이론에서 유도한 이 공식은 오늘날 퀀트 트레이딩, 포트폴리오 관리, 자동매매 포지션 사이징에 널리 활용됩니다.

핵심 아이디어는 단순합니다. 너무 적게 베팅하면 자본 성장이 느리고, 너무 많이 베팅하면 파산 위험이 커집니다. 켈리 기준은 이 두 극단 사이의 수학적 최적점을 찾아줍니다.

켈리 공식 유도

이진 결과(승/패)를 가진 가장 기본적인 켈리 공식은 다음과 같습니다:

변수 의미
f* 최적 베팅 비율 (자본 대비)
p 승률 (이길 확률)
q 패률 (1 – p)
b 손익비 (평균 이익 / 평균 손실)
f* = (b × p - q) / b = p - q / b

예를 들어 승률 55%, 손익비 1.5인 전략이라면:

f* = (1.5 × 0.55 - 0.45) / 1.5
f* = (0.825 - 0.45) / 1.5
f* = 0.25  → 자본의 25%

파이썬으로 켈리 기준 구현

실전 트레이딩에서는 과거 매매 기록을 바탕으로 켈리 비율을 계산합니다.

import numpy as np
from dataclasses import dataclass

@dataclass
class KellyResult:
    full_kelly: float
    half_kelly: float
    quarter_kelly: float
    win_rate: float
    payoff_ratio: float
    edge: float

def calculate_kelly(trades: list[float]) -> KellyResult:
    """과거 매매 수익률 리스트로 켈리 비율 계산
    
    Args:
        trades: 개별 매매 수익률 리스트 (예: [0.02, -0.01, 0.03, ...])
    
    Returns:
        KellyResult: 풀/하프/쿼터 켈리 및 통계
    """
    trades = np.array(trades)
    
    wins = trades[trades > 0]
    losses = trades[trades < 0]
    
    if len(losses) == 0 or len(wins) == 0:
        return KellyResult(0, 0, 0, 0, 0, 0)
    
    win_rate = len(wins) / len(trades)
    avg_win = np.mean(wins)
    avg_loss = abs(np.mean(losses))
    payoff_ratio = avg_win / avg_loss
    
    # 켈리 공식
    kelly = (payoff_ratio * win_rate - (1 - win_rate)) / payoff_ratio
    kelly = max(kelly, 0)  # 음수면 베팅하지 않음
    
    edge = win_rate * avg_win - (1 - win_rate) * avg_loss
    
    return KellyResult(
        full_kelly=round(kelly, 4),
        half_kelly=round(kelly / 2, 4),
        quarter_kelly=round(kelly / 4, 4),
        win_rate=round(win_rate, 4),
        payoff_ratio=round(payoff_ratio, 4),
        edge=round(edge, 6),
    )

# 사용 예시
sample_trades = [0.02, -0.01, 0.03, -0.015, 0.025, -0.008,
                 0.04, -0.02, 0.015, -0.012, 0.035, -0.01]

result = calculate_kelly(sample_trades)
print(f"승률: {result.win_rate:.1%}")
print(f"손익비: {result.payoff_ratio:.2f}")
print(f"풀 켈리: {result.full_kelly:.1%}")
print(f"하프 켈리: {result.half_kelly:.1%}")
print(f"엣지(기대값): {result.edge:.4%}")

풀 켈리 vs 하프 켈리

이론적 최적인 풀 켈리(Full Kelly)는 실전에서 매우 공격적입니다. 파라미터 추정 오류, 시장 변화, 테일 리스크를 고려하면 하프 켈리 또는 쿼터 켈리가 더 현실적입니다.

방식 비율 특징 적합한 상황
풀 켈리 f* 최대 성장률, 높은 변동성 파라미터 확신 높을 때
하프 켈리 f*/2 성장률 75%, 변동성 50% 실전 권장 기본값
쿼터 켈리 f*/4 보수적, 낮은 드로다운 불확실한 시장 환경

에드워드 소프(Edward Thorp)는 실전에서 항상 하프 켈리 이하를 사용했으며, 워런 버핏의 집중 투자 방식도 켈리 기준의 변형으로 해석됩니다.

연속 수익률 분포에서의 켈리

실제 트레이딩 수익률은 이진 결과가 아닌 연속 분포를 따릅니다. 로그 정규 분포를 가정한 연속 켈리 공식은 다음과 같습니다:

def continuous_kelly(returns: np.ndarray) -> float:
    """연속 분포 기반 켈리 비율 계산
    
    f* = μ / σ² (초과수익률 / 분산)
    """
    mu = np.mean(returns)
    sigma_sq = np.var(returns, ddof=1)
    
    if sigma_sq == 0:
        return 0
    
    kelly = mu / sigma_sq
    return max(kelly, 0)

def kelly_with_risk_free(returns: np.ndarray, rf_rate: float = 0.0) -> float:
    """무위험 수익률을 고려한 켈리 비율
    
    f* = (μ - rf) / σ²
    """
    excess = np.mean(returns) - rf_rate
    sigma_sq = np.var(returns, ddof=1)
    
    if sigma_sq == 0:
        return 0
    
    return max(excess / sigma_sq, 0)

# 일별 수익률 데이터로 계산
daily_returns = np.array([0.005, -0.003, 0.008, -0.002, 0.004,
                          -0.006, 0.007, 0.001, -0.004, 0.006])

full_k = continuous_kelly(daily_returns)
print(f"연속 켈리 비율: {full_k:.2f}")
print(f"하프 켈리: {full_k/2:.2f}")
print(f"레버리지 해석: {full_k:.1f}x")

멀티 에셋 켈리 기준

여러 자산에 동시에 투자할 때는 공분산을 고려한 멀티 에셋 켈리를 사용합니다. 이는 마코위츠 평균-분산 최적화와 수학적으로 동일한 구조입니다.

def multi_asset_kelly(returns_matrix: np.ndarray) -> np.ndarray:
    """멀티 에셋 켈리 최적 비중 계산
    
    f* = Σ⁻¹ × μ  (공분산 역행렬 × 기대수익률 벡터)
    
    Args:
        returns_matrix: (N일 × M자산) 수익률 행렬
    
    Returns:
        최적 비중 벡터
    """
    mu = np.mean(returns_matrix, axis=0)
    cov = np.cov(returns_matrix, rowvar=False)
    
    try:
        cov_inv = np.linalg.inv(cov)
    except np.linalg.LinAlgError:
        cov_inv = np.linalg.pinv(cov)
    
    kelly_weights = cov_inv @ mu
    
    # 하프 켈리 적용
    half_kelly = kelly_weights / 2
    
    return half_kelly

# 3자산 포트폴리오 예시
np.random.seed(42)
btc_returns = np.random.normal(0.001, 0.03, 100)
eth_returns = np.random.normal(0.0015, 0.04, 100)
sol_returns = np.random.normal(0.002, 0.05, 100)

matrix = np.column_stack([btc_returns, eth_returns, sol_returns])
weights = multi_asset_kelly(matrix)

assets = ['BTC', 'ETH', 'SOL']
for asset, w in zip(assets, weights):
    print(f"{asset}: {w:.1%}")

켈리 기준의 한계와 주의사항

켈리 기준을 실전에 적용할 때 반드시 알아야 할 한계점이 있습니다:

  • 파라미터 추정 오류: 승률과 손익비는 과거 데이터에서 추정합니다. 미래가 과거와 다르면 켈리 비율도 틀립니다. 충분한 표본(최소 100회 이상)이 필요합니다.
  • 팻 테일 리스크: 금융 시장 수익률은 정규 분포보다 꼬리가 두꺼운 분포를 따릅니다. 극단적 손실이 발생하면 켈리 비율이 과도할 수 있습니다.
  • 연속 베팅 가정: 켈리 기준은 무한히 많은 독립 베팅을 가정합니다. 실전에서는 매매 횟수가 제한적이고 연속 손실(drawdown)이 발생합니다.
  • 심리적 부담: 풀 켈리는 최대 낙폭(MDD)이 매우 클 수 있습니다. 대부분의 트레이더는 20~30% 이상의 드로다운을 심리적으로 견디기 어렵습니다.

실전 적용: 롤링 켈리 시스템

시장 환경 변화에 적응하기 위해 롤링 윈도우로 켈리 비율을 동적으로 업데이트하는 방법입니다.

class RollingKelly:
    """롤링 윈도우 기반 동적 켈리 포지션 사이징"""
    
    def __init__(self, window=50, kelly_fraction=0.5, max_position=0.3):
        self.window = window
        self.kelly_fraction = kelly_fraction  # 하프 켈리
        self.max_position = max_position      # 최대 포지션 한도
        self.trade_history = []
    
    def add_trade(self, pnl_pct: float):
        """매매 결과 기록"""
        self.trade_history.append(pnl_pct)
    
    def get_position_size(self, capital: float) -> float:
        """현재 최적 포지션 크기 반환"""
        if len(self.trade_history) < 20:
            # 최소 20회 매매 전에는 보수적으로
            return capital * 0.02
        
        recent = self.trade_history[-self.window:]
        result = calculate_kelly(recent)
        
        # 프랙션 켈리 적용
        adjusted = result.full_kelly * self.kelly_fraction
        
        # 상한 제한
        capped = min(adjusted, self.max_position)
        
        return capital * max(capped, 0)
    
    def report(self):
        if len(self.trade_history) < 20:
            print("데이터 부족 (최소 20회 필요)")
            return
        
        recent = self.trade_history[-self.window:]
        result = calculate_kelly(recent)
        adjusted = result.full_kelly * self.kelly_fraction
        
        print(f"최근 {len(recent)}회 매매 기준:")
        print(f"  승률: {result.win_rate:.1%}")
        print(f"  손익비: {result.payoff_ratio:.2f}")
        print(f"  풀 켈리: {result.full_kelly:.1%}")
        print(f"  적용 비율: {adjusted:.1%} ({self.kelly_fraction:.0%} 켈리)")

# 사용 예시
kelly_system = RollingKelly(window=50, kelly_fraction=0.5)

# 과거 매매 기록 입력
for trade in sample_trades * 5:
    kelly_system.add_trade(trade)

kelly_system.report()
position = kelly_system.get_position_size(capital=10000)
print(f"$10,000 자본 기준 포지션: ${position:,.0f}")

마무리

켈리 기준은 자동매매 포지션 사이징의 이론적 최적해를 제시하는 강력한 도구입니다. 핵심은 풀 켈리를 그대로 쓰지 않고, 하프 켈리 이하로 보수적으로 적용하는 것입니다. 충분한 매매 기록, 롤링 윈도우 업데이트, 최대 포지션 제한을 함께 사용하면 실전에서도 안정적으로 활용할 수 있습니다.

켈리 기준과 함께 슬리피지 최소화 전략을 적용하면 이론과 실전의 괴리를 줄일 수 있습니다. 또한 몬테카를로 VaR 리스크 측정으로 켈리 비율의 안전 마진을 검증하는 것도 추천합니다.

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