켈리 기준이란? 수학적으로 최적인 베팅 크기
켈리 기준(Kelly Criterion)은 1956년 벨 연구소의 존 켈리(John L. Kelly Jr.)가 발표한 공식으로, 장기 자산 성장률을 수학적으로 극대화하는 최적의 베팅 비율을 계산합니다. 워런 버핏, 에드워드 소프, 짐 시몬스 등 전설적인 투자자들이 활용한 것으로 알려진 이 공식은, 퀀트 자동매매에서 “얼마나 투자할 것인가”라는 핵심 질문에 수학적 답을 제시합니다.
많은 트레이더가 진입·청산 전략에만 집중하지만, 실제로 자금 관리(Position Sizing)가 장기 수익에 미치는 영향이 더 큽니다. 승률 60%인 전략도 과도한 베팅으로 파산할 수 있고, 승률 45%인 전략도 적절한 자금 관리로 수익을 낼 수 있습니다. 켈리 기준은 이 균형점을 정확히 찾아줍니다.
켈리 공식의 수학적 원리
기본 켈리 공식은 다음과 같습니다.
f* = (bp - q) / b
여기서:
f* = 최적 베팅 비율 (전체 자본 대비)
b = 순 배당률 (이길 때 받는 금액 / 잃을 때 잃는 금액)
p = 승률
q = 패률 (1 - p)
예를 들어 승률 55%, 손익비 1.5:1인 전략이라면:
- f* = (1.5 × 0.55 − 0.45) / 1.5 = 0.25 (25%)
- 매 거래마다 전체 자본의 25%를 투자하는 것이 수학적 최적
그러나 실전에서는 풀 켈리(Full Kelly)가 변동성이 극도로 크기 때문에, 대부분의 전문 트레이더는 하프 켈리(Half Kelly) 또는 쿼터 켈리(Quarter Kelly)를 사용합니다.
연속 수익률 분포를 위한 켈리 공식
주식·암호화폐처럼 수익률이 연속적인 자산에는 이진(win/lose) 켈리가 아닌 연속 켈리 공식을 사용해야 합니다.
f* = μ / σ²
여기서:
f* = 최적 투자 비율
μ = 기대 수익률 (평균 일별/거래별 수익률)
σ² = 수익률의 분산
이 공식은 기대 수익률이 높을수록 많이 투자하고, 변동성이 클수록 적게 투자하라는 직관적인 결론을 수학적으로 도출합니다.
파이썬 환경 설정과 데이터 준비
pip install yfinance pandas numpy matplotlib scipy
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize_scalar
# 데이터 수집
ticker = "SPY"
df = yf.download(ticker, start="2015-01-01", end="2025-12-31")
df['return'] = df['Close'].pct_change().dropna()
df = df.dropna()
print(f"평균 일별 수익률: {df['return'].mean()*100:.4f}%")
print(f"일별 표준편차: {df['return'].std()*100:.2f}%")
print(f"연율화 수익률: {df['return'].mean()*252*100:.1f}%")
print(f"연율화 변동성: {df['return'].std()*np.sqrt(252)*100:.1f}%")
이진 켈리 계산기 구현
매매 전략의 승률과 손익비로부터 최적 베팅 비율을 계산하는 함수입니다.
def binary_kelly(win_rate, win_loss_ratio):
"""
이진(승/패) 켈리 기준 계산
- win_rate: 승률 (0~1)
- win_loss_ratio: 평균 이익 / 평균 손실
"""
p = win_rate
q = 1 - p
b = win_loss_ratio
kelly_fraction = (b * p - q) / b
return {
'full_kelly': max(kelly_fraction, 0),
'half_kelly': max(kelly_fraction / 2, 0),
'quarter_kelly': max(kelly_fraction / 4, 0),
'edge': b * p - q, # 기대값 (양수여야 유효)
}
# 다양한 전략 시나리오 분석
scenarios = [
{'name': '보수적 추세추종', 'win_rate': 0.45, 'ratio': 2.0},
{'name': '스캘핑', 'win_rate': 0.65, 'ratio': 0.8},
{'name': '스윙 트레이딩', 'win_rate': 0.55, 'ratio': 1.5},
{'name': '변동성 돌파', 'win_rate': 0.50, 'ratio': 1.8},
{'name': '평균 회귀', 'win_rate': 0.60, 'ratio': 1.2},
]
print(f"{'전략':<16} {'승률':>6} {'손익비':>6} {'Full':>8} {'Half':>8} {'Edge':>8}")
print("-" * 60)
for s in scenarios:
k = binary_kelly(s['win_rate'], s['ratio'])
print(f"{s['name']:<16} {s['win_rate']:>5.0%} {s['ratio']:>6.1f} "
f"{k['full_kelly']:>7.1%} {k['half_kelly']:>7.1%} {k['edge']:>7.3f}")
연속 켈리와 포트폴리오 적용
실제 거래 수익률 데이터로부터 최적 투자 비율을 계산합니다.
def continuous_kelly(returns):
"""
연속 수익률 분포 기반 켈리 계산
f* = μ / σ²
"""
mu = returns.mean()
sigma_sq = returns.var()
if sigma_sq == 0:
return 0
kelly = mu / sigma_sq
return {
'full_kelly': kelly,
'half_kelly': kelly / 2,
'quarter_kelly': kelly / 4,
'mean_return': mu,
'variance': sigma_sq,
'sharpe_daily': mu / returns.std()
}
# SPY 데이터 기반 켈리 계산
ck = continuous_kelly(df['return'])
print(f"연속 켈리 비율: {ck['full_kelly']:.2f}x (레버리지)")
print(f"하프 켈리: {ck['half_kelly']:.2f}x")
print(f"일별 샤프: {ck['sharpe_daily']:.4f}")
켈리 기준 백테스팅: 고정 비율 vs 켈리 비율
켈리 기준으로 포지션 크기를 조절한 전략과 고정 비율 전략의 성과를 비교합니다.
def backtest_kelly(returns, fractions, initial_capital=10000):
"""
다양한 투자 비율에 따른 자본 성장 시뮬레이션
"""
results = {}
for name, f in fractions.items():
capital = [initial_capital]
for r in returns:
new_capital = capital[-1] * (1 + f * r)
# 파산 방지: 자본이 0 이하가 되지 않도록
new_capital = max(new_capital, 0.01)
capital.append(new_capital)
capital = np.array(capital)
total_return = (capital[-1] / initial_capital - 1) * 100
# MDD 계산
peak = np.maximum.accumulate(capital)
drawdown = (capital - peak) / peak
mdd = drawdown.min() * 100
# 연율화 수익률
years = len(returns) / 252
annual_return = ((capital[-1] / initial_capital) ** (1/years) - 1) * 100
results[name] = {
'capital': capital,
'total_return': total_return,
'annual_return': annual_return,
'mdd': mdd,
'final_capital': capital[-1]
}
return results
# 투자 비율 시나리오
ck = continuous_kelly(df['return'])
fractions = {
'25% 고정': 0.25,
'50% 고정': 0.50,
'100% 고정': 1.00,
'Quarter Kelly': ck['quarter_kelly'],
'Half Kelly': ck['half_kelly'],
'Full Kelly': ck['full_kelly'],
'2x Kelly (과잉)': ck['full_kelly'] * 2,
}
results = backtest_kelly(df['return'].values, fractions)
# 결과 출력
print(f"{'전략':<18} {'최종자본':>12} {'연수익률':>10} {'MDD':>10}")
print("-" * 55)
for name, r in results.items():
print(f"{name:<18} ${r['final_capital']:>10,.0f} "
f"{r['annual_return']:>9.1f}% {r['mdd']:>9.1f}%")
롤링 켈리: 시장 변화에 적응하는 동적 포지션 사이징
과거 전체 데이터의 켈리 비율은 미래에 유효하지 않을 수 있습니다. 롤링 윈도우로 최근 N일 데이터를 기반으로 켈리 비율을 동적으로 조정합니다.
def rolling_kelly_backtest(returns, lookback=60, max_fraction=2.0,
kelly_mult=0.5, initial_capital=10000):
"""
롤링 켈리 백테스팅
- lookback: 켈리 계산에 사용할 과거 일수
- max_fraction: 최대 투자 비율 (레버리지 상한)
- kelly_mult: 켈리 배수 (0.5 = Half Kelly)
"""
capital = [initial_capital]
kelly_history = []
for i in range(lookback, len(returns)):
window = returns[i-lookback:i]
mu = window.mean()
var = window.var()
if var > 0 and mu > 0:
f = (mu / var) * kelly_mult
f = min(f, max_fraction) # 레버리지 상한
f = max(f, 0) # 공매도 방지
else:
f = 0 # 기대 수익률이 음수면 투자 안함
kelly_history.append(f)
new_capital = capital[-1] * (1 + f * returns[i])
capital.append(max(new_capital, 0.01))
return np.array(capital), np.array(kelly_history)
# 롤링 Half Kelly 실행
capital_rolling, kelly_hist = rolling_kelly_backtest(
df['return'].values, lookback=60, kelly_mult=0.5
)
# 고정 투자와 비교
capital_fixed = [10000]
for r in df['return'].values[60:]:
capital_fixed.append(capital_fixed[-1] * (1 + 1.0 * r))
capital_fixed = np.array(capital_fixed)
print(f"롤링 Half Kelly 최종: ${capital_rolling[-1]:,.0f}")
print(f"100% 고정 투자 최종: ${capital_fixed[-1]:,.0f}")
print(f"평균 켈리 비율: {kelly_hist.mean():.2f}x")
print(f"켈리 비율 범위: {kelly_hist.min():.2f} ~ {kelly_hist.max():.2f}")
다중 자산 켈리: 포트폴리오 최적 배분
여러 자산에 동시에 투자할 때, 각 자산에 얼마를 배분할지 결정하는 다중 자산 켈리 공식입니다.
def multi_asset_kelly(returns_df, kelly_mult=0.5):
"""
다중 자산 켈리 최적 배분
f* = Σ^(-1) × μ (공분산 역행렬 × 기대수익률 벡터)
"""
mu = returns_df.mean().values
cov = returns_df.cov().values
try:
cov_inv = np.linalg.inv(cov)
full_kelly = cov_inv @ mu
adjusted = full_kelly * kelly_mult
# 음수(공매도) 제거 옵션
adjusted_long = np.maximum(adjusted, 0)
return pd.DataFrame({
'asset': returns_df.columns,
'full_kelly': full_kelly,
'adjusted': adjusted,
'long_only': adjusted_long,
'weight': adjusted_long / adjusted_long.sum()
if adjusted_long.sum() > 0 else adjusted_long
})
except np.linalg.LinAlgError:
return None
# 다중 자산 데이터 수집
tickers = ['SPY', 'QQQ', 'TLT', 'GLD', 'BTC-USD']
data = yf.download(tickers, start="2020-01-01", end="2025-12-31")['Close']
returns_df = data.pct_change().dropna()
# 최적 배분 계산
allocation = multi_asset_kelly(returns_df, kelly_mult=0.25)
print(allocation.to_string(index=False))
켈리 기준의 한계와 실전 보완 전략
켈리 기준은 강력하지만 몇 가지 중요한 한계가 있습니다.
- 파라미터 추정 오류: 승률과 손익비가 정확해야 하지만, 과거 데이터의 통계치가 미래를 보장하지 않습니다. 이를 보완하기 위해 보수적인 추정치(Half Kelly)를 사용합니다.
- 극단적 변동성: 풀 켈리는 이론적으로 최적이지만, 실전에서는 50% 이상의 MDD를 경험할 수 있습니다. 대부분의 전문 트레이더가 1/4~1/2 켈리를 권장하는 이유입니다.
- 정규분포 가정: 연속 켈리 공식은 수익률이 정규분포를 따른다고 가정하지만, 실제 시장은 팻테일(fat tail)과 비대칭성을 보입니다.
- 상관관계 변화: 다중 자산 켈리에서 공분산 행렬은 시간에 따라 변합니다. 위기 시 상관관계가 급등하면 분산 효과가 사라집니다.
- 거래 비용 무시: 기본 공식은 수수료와 슬리피지를 고려하지 않습니다. 실전에서는 거래 비용을 차감한 순수익률로 계산해야 합니다.
실전 적용: 매매 전략에 켈리 기준 통합하기
class KellyPositionSizer:
"""매매 전략에 통합하는 켈리 포지션 사이저"""
def __init__(self, lookback=100, kelly_mult=0.25, max_position=0.3):
"""
- lookback: 통계 계산 기간
- kelly_mult: 켈리 배수 (0.25 = Quarter Kelly 권장)
- max_position: 최대 포지션 비율
"""
self.lookback = lookback
self.kelly_mult = kelly_mult
self.max_position = max_position
self.trade_history = []
def record_trade(self, pnl_pct):
"""거래 결과 기록"""
self.trade_history.append(pnl_pct)
def get_position_size(self, capital):
"""현재 자본 기준 최적 포지션 크기 반환"""
if len(self.trade_history) < 20:
# 데이터 부족 시 최소 비율
return capital * 0.02
recent = np.array(self.trade_history[-self.lookback:])
# 이진 켈리
wins = recent[recent > 0]
losses = recent[recent < 0]
if len(losses) == 0 or len(wins) == 0:
return capital * 0.02
win_rate = len(wins) / len(recent)
avg_win = wins.mean()
avg_loss = abs(losses.mean())
win_loss_ratio = avg_win / avg_loss
kelly = (win_loss_ratio * win_rate - (1 - win_rate)) / win_loss_ratio
kelly = max(kelly, 0) * self.kelly_mult
# 최대 비율 제한
position_pct = min(kelly, self.max_position)
return capital * position_pct
def stats(self):
"""현재 통계"""
if len(self.trade_history) < 2:
return "데이터 부족"
recent = np.array(self.trade_history[-self.lookback:])
wins = recent[recent > 0]
losses = recent[recent < 0]
return {
'trades': len(recent),
'win_rate': f"{len(wins)/len(recent)*100:.1f}%",
'avg_win': f"{wins.mean()*100:.2f}%",
'avg_loss': f"{abs(losses.mean())*100:.2f}%",
'kelly_fraction': self.get_position_size(10000) / 10000
}
# 사용 예시
sizer = KellyPositionSizer(lookback=50, kelly_mult=0.25, max_position=0.20)
# 과거 거래 기록 시뮬레이션
np.random.seed(42)
fake_trades = np.random.normal(0.003, 0.02, 100) # 평균 +0.3%, 표준편차 2%
for t in fake_trades:
sizer.record_trade(t)
capital = 100000
position = sizer.get_position_size(capital)
print(f"추천 포지션 크기: ${position:,.0f} ({position/capital*100:.1f}%)")
print(sizer.stats())
시각화: 켈리 비율에 따른 성장 곡선
# 다양한 켈리 배수에 따른 자본 성장 비교
multipliers = [0.25, 0.5, 0.75, 1.0, 1.5, 2.0]
plt.figure(figsize=(14, 7))
for mult in multipliers:
cap, _ = rolling_kelly_backtest(
df['return'].values, lookback=60,
kelly_mult=mult, max_fraction=3.0
)
dates = df.index[60:]
label = f"{mult}x Kelly"
plt.plot(dates, cap[1:], label=label, linewidth=1.5)
plt.yscale('log')
plt.title('켈리 배수별 자본 성장 곡선 (로그 스케일)', fontsize=14)
plt.xlabel('날짜')
plt.ylabel('자본 (로그)')
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('kelly_growth_curves.png', dpi=150)
plt.show()
자주 묻는 질문 (FAQ)
켈리 기준을 쓰면 절대 파산하지 않나요?
이론적으로 켈리 기준은 파산 확률이 0입니다. 항상 자본의 일정 비율만 투자하므로 한 번에 전액을 잃지 않기 때문입니다. 그러나 실전에서는 갭 하락, 유동성 부족 등으로 예상보다 큰 손실이 발생할 수 있어 하프 켈리 이하를 권장합니다.
승률이 낮아도 켈리 기준이 유효한가요?
네, 손익비(Payoff Ratio)가 충분히 크면 승률이 40%대라도 양의 켈리 비율이 나옵니다. 핵심은 승률이 아니라 기대값(Edge)입니다. 기대값 = (승률 × 평균이익) − (패률 × 평균손실)이 양수이면 켈리가 유효합니다.
암호화폐 자동매매에도 적용 가능한가요?
가능합니다. 다만 암호화폐는 변동성이 극도로 높아 풀 켈리 적용 시 극단적인 드로다운을 경험합니다. 암호화폐에는 쿼터 켈리(0.25x)를 권장하며, 롤링 윈도우를 짧게(30~60일) 설정하여 변동성 변화에 빠르게 적응하도록 하세요.
결론: 켈리 기준 실전 적용 로드맵
켈리 기준은 "얼마나 투자할 것인가"에 대한 수학적으로 최적인 답을 제공합니다. 진입·청산 전략만큼이나 중요한 자금 관리를 체계화하여 장기 수익을 극대화하세요.
- 기본 이해 → 이진 켈리 공식으로 자신의 전략 최적 비율 계산
- 보수적 적용 → 반드시 Half Kelly 이하로 시작 (Quarter Kelly 권장)
- 연속 켈리 → 실제 수익률 분포 기반으로 정밀 계산
- 롤링 적용 → 시장 변화에 적응하는 동적 포지션 사이징
- 전략 통합 → 기존 매매 전략에 KellyPositionSizer 클래스 연결
켈리 기준을 자금 관리 프레임워크로 확립했다면, 변동성 돌파 전략이나 그리드 트레이딩에 켈리 포지션 사이징을 적용하여 전략 성과와 리스크 관리를 동시에 최적화해 보세요.