자동매매 포지션 사이징 전략

자동매매에서 포지션 사이징이 중요한 이유

많은 트레이더가 “어디서 사고팔 것인가”에 집중하지만, 실제로 장기 수익을 결정하는 것은 “얼마나 살 것인가”입니다. 이것이 바로 포지션 사이징(Position Sizing)입니다. 동일한 진입 전략이라도 포지션 크기에 따라 수익과 파산 확률이 극적으로 달라집니다.

자동매매 봇을 운영한다면 포지션 사이징은 코드로 정확히 구현해야 하는 핵심 모듈입니다. 이 글에서는 실전에서 검증된 4가지 포지션 사이징 전략을 파이썬 코드와 함께 정리합니다.

1. 고정 금액 방식 (Fixed Dollar)

가장 단순한 방법으로, 매 거래마다 동일한 금액을 투입합니다.

# 매 거래당 100만 원 투입
fixed_amount = 1_000_000
position_size = fixed_amount / entry_price

장점: 구현이 쉽고 예측 가능합니다.
단점: 계좌가 커져도 같은 금액을 투자하므로 복리 효과가 없습니다. 반대로 계좌가 줄어도 같은 금액을 넣어 파산 위험이 높아집니다.

적합한 상황: 전략 테스트 초기 단계, 리스크를 최소화하고 싶을 때

2. 고정 비율 방식 (Fixed Fractional)

계좌 잔고의 일정 비율을 매 거래에 투입합니다. 계좌가 커지면 투자금도 커지고, 줄면 투자금도 줄어 자연스러운 리스크 조절이 됩니다.

def fixed_fractional(account_balance, risk_pct=0.02):
    """계좌의 2%를 리스크로 설정"""
    risk_amount = account_balance * risk_pct
    return risk_amount

# 계좌 1000만 원, 리스크 2% → 1회 최대 손실 20만 원
risk = fixed_fractional(10_000_000, 0.02)  # 200,000원
# 손절폭이 5%라면 → 포지션 크기 = 200,000 / 0.05 = 400만 원
position_value = risk / stop_loss_pct

여기서 핵심은 “리스크 금액 ÷ 손절폭 = 포지션 크기”라는 공식입니다. 손절 전략과 직접 연동되므로, 손절폭이 넓으면 포지션이 작아지고, 좁으면 커집니다.

장점: 복리 효과 + 자동 리스크 조절. 대부분의 전문 트레이더가 사용하는 기본 방식입니다.
단점: 연속 손실 시 포지션이 줄어 회복이 느려집니다.

3. 켈리 기준 (Kelly Criterion)

켈리 기준은 수학적으로 장기 자본 성장률을 최대화하는 최적 배팅 비율을 계산합니다.

def kelly_fraction(win_rate, avg_win, avg_loss):
    """
    win_rate: 승률 (0~1)
    avg_win: 평균 수익 (양수)
    avg_loss: 평균 손실 (양수)
    """
    W = win_rate
    R = avg_win / avg_loss  # 손익비
    kelly = W - (1 - W) / R
    return max(kelly, 0)

# 승률 55%, 평균수익 3%, 평균손실 2%
k = kelly_fraction(0.55, 0.03, 0.02)
# k ≈ 0.55 - 0.45/1.5 ≈ 0.25 → 계좌의 25%

풀 켈리(Full Kelly)는 이론상 최적이지만, 실전에서는 변동성이 극심합니다. 따라서 대부분의 퀀트 트레이더는 하프 켈리(Half Kelly) 또는 쿼터 켈리(Quarter Kelly)를 사용합니다.

배팅 비율 기대 성장률 최대 낙폭(MDD) 실전 적합성
풀 켈리 (100%) 최대 매우 큼 (50%+) 비현실적
하프 켈리 (50%) 최대의 75% 상당히 줄어듦 공격적 트레이더
쿼터 켈리 (25%) 최대의 44% 크게 줄어듦 보수적/권장

주의: 켈리 기준은 승률과 손익비가 정확해야 유효합니다. 백테스트에서 과최적화된 승률로 켈리를 계산하면 실전에서 과도한 포지션을 잡게 되어 치명적입니다.

4. 변동성 패리티 (Volatility Parity)

변동성 패리티는 각 자산의 변동성에 반비례하여 포지션 크기를 조절합니다. 변동성이 높은 자산은 적게, 낮은 자산은 많이 배분하여 각 포지션의 리스크 기여도를 균등하게 만듭니다.

import numpy as np

def volatility_parity_weights(returns_df, lookback=60):
    """각 자산의 변동성 역수로 가중치 계산"""
    vols = returns_df.tail(lookback).std() * np.sqrt(365)  # 연환산
    inv_vols = 1 / vols
    weights = inv_vols / inv_vols.sum()
    return weights

# BTC 연변동성 80%, ETH 연변동성 100%
# → BTC 가중치: (1/80) / (1/80 + 1/100) ≈ 0.556
# → ETH 가중치: (1/100) / (1/80 + 1/100) ≈ 0.444

장점: 다중 자산 포트폴리오에서 특정 고변동성 자산이 전체 리스크를 지배하는 것을 방지합니다. 페어 트레이딩에서 롱/숏 양쪽 포지션의 리스크를 균형 맞출 때도 유용합니다.

단점: 변동성 자체가 급변할 수 있으며, 저변동성 자산에 과도하게 집중될 위험이 있습니다.

자동매매 봇 구현: 통합 사이징 모듈

실전 자동매매 봇에서는 여러 방식을 조합하되, 최종 상한선을 반드시 설정해야 합니다:

class PositionSizer:
    def __init__(self, account_balance, max_position_pct=0.25):
        self.balance = account_balance
        self.max_position = account_balance * max_position_pct
    
    def calculate(self, risk_pct, stop_loss_pct, volatility=None):
        # 고정 비율 기반 계산
        risk_amount = self.balance * risk_pct
        position = risk_amount / stop_loss_pct
        
        # 변동성 보정 (선택)
        if volatility and volatility > 0.05:
            vol_scalar = 0.03 / volatility  # 목표 변동성 3%
            position *= min(vol_scalar, 1.5)
        
        # 절대 상한 적용
        return min(position, self.max_position)

핵심 원칙은 세 가지입니다:

  1. 1회 최대 손실은 계좌의 1~2% — 20연패해도 계좌의 60~80%가 남아야 합니다
  2. 단일 포지션은 계좌의 25% 이하 — 집중 투자는 자동매매에서 금물입니다
  3. 전체 노출(Total Exposure)은 계좌의 100% 이하 — 레버리지 남용 방지

포지션 사이징 실전 체크리스트

  1. 손절폭을 먼저 정한 후 포지션 크기를 역산하고 있는가?
  2. 연속 손실 10회에도 계좌가 생존 가능한 사이즈인가?
  3. 승률·손익비 추정치가 현실적인가? (백테스트 과최적화 경계)
  4. 다중 포지션 운용 시 전체 노출도를 관리하고 있는가?
  5. 변동성 급변 시 자동으로 사이즈가 축소되는 로직이 있는가?

마무리

포지션 사이징은 자동매매의 “보이지 않는 엔진”입니다. 화려한 진입 신호보다 지루한 사이징 로직이 장기 수익을 결정합니다. 고정 비율 방식으로 시작하여 점진적으로 변동성 보정과 켈리 기준을 적용해 보세요. 작게 시작하고, 검증된 만큼만 키우는 것이 자동매매 생존의 핵심입니다.

7) 포지션 사이징 전략 파이썬 구현

from abc import ABC, abstractmethod

class PositionSizer(ABC):
    @abstractmethod
    def calculate(self, balance: float, entry: float, stop_loss: float) -> float:
        """거래 수량 반환"""
        pass

class FixedFractional(PositionSizer):
    """고정 비율법: 매 거래마다 계좌의 N% 리스크"""
    def __init__(self, risk_pct: float = 2.0):
        self.risk_pct = risk_pct / 100

    def calculate(self, balance, entry, stop_loss):
        risk_amount = balance * self.risk_pct
        risk_per_unit = abs(entry - stop_loss)
        if risk_per_unit == 0:
            return 0
        return risk_amount / risk_per_unit

class KellyCriterion(PositionSizer):
    """켈리 기준: 승률과 손익비로 최적 비율 계산"""
    def __init__(self, win_rate: float, avg_win: float, avg_loss: float, 
                 fraction: float = 0.5):  # Half Kelly (보수적)
        self.kelly_pct = self._calc_kelly(win_rate, avg_win, avg_loss) * fraction

    def _calc_kelly(self, wr, aw, al):
        if al == 0: return 0
        b = aw / al  # 손익비
        return max(0, (b * wr - (1 - wr)) / b)

    def calculate(self, balance, entry, stop_loss):
        risk_amount = balance * self.kelly_pct
        risk_per_unit = abs(entry - stop_loss)
        if risk_per_unit == 0:
            return 0
        return risk_amount / risk_per_unit

class VolatilityBased(PositionSizer):
    """변동성 기반: ATR로 포지션 크기 조절"""
    def __init__(self, risk_pct: float = 2.0, atr_multiplier: float = 2.0):
        self.risk_pct = risk_pct / 100
        self.atr_mult = atr_multiplier

    def calculate_with_atr(self, balance: float, entry: float, atr: float) -> float:
        risk_amount = balance * self.risk_pct
        risk_per_unit = atr * self.atr_mult
        if risk_per_unit == 0:
            return 0
        return risk_amount / risk_per_unit

    def calculate(self, balance, entry, stop_loss):
        return FixedFractional(self.risk_pct * 100).calculate(balance, entry, stop_loss)

# 사용 예시
balance = 10_000_000  # 1000만원
entry = 65_000        # BTC 65,000 USDT
stop = 63_000         # 손절가

ff = FixedFractional(risk_pct=2.0)
print(f"고정 비율: {ff.calculate(balance, entry, stop):.4f} BTC")

kc = KellyCriterion(win_rate=0.45, avg_win=3.0, avg_loss=1.0, fraction=0.5)
print(f"Half Kelly: {kc.calculate(balance, entry, stop):.4f} BTC")

8) 관련 글

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