튜토리얼약 8분 읽기

자동매매 리스크 관리 규칙 설계 — 손절·포지션 한도·서킷브레이커 개념

안전장치와 리스크 관리 규칙은 다른 층위다

앞선 글에서 예산 한도, 비가역 결정 차단, 사람의 최종 승인 같은 "시스템이 스스로 사고를 칠 수 없게 만드는 장치"를 다뤘다. 이번 글은 그 아래 층위, 즉 전략이 정상적으로 돌아가는 와중에도 손실을 특정 범위 안으로 묶어두는 규칙을 다룬다.

비유하자면 앞의 안전장치는 "차 자체에 브레이크와 에어백이 있는가"이고, 이번에 다룰 리스크 관리 규칙은 "운전 중에 얼마나 빨리 달릴지, 언제 감속할지를 정하는 규칙"에 가깝다. 둘 다 없으면 위험하지만, 막는 실패의 종류가 다르다.

이 글에서 다루는 세 가지 장치 — 손절, 포지션 한도, 서킷브레이커 — 는 각각 다른 방향에서 오는 손실을 막는다. 구체적인 임계값이나 실제 운영 중인 수치는 밝히지 않는다. 어떤 장치가 왜 필요한지, 어떻게 코드 구조로 표현하는지가 이 글의 초점이다.

서킷브레이커 시스템 전체를 멈추는 마지막 방어선 포지션 한도 자금이 소수 종목에 쏠리는 것을 제한 손절 개별 포지션의 손실을 제한
세 장치는 각각 개별 포지션·종목 집중도·시스템 전체라는 서로 다른 단위의 손실을 막는다 — 하나로 대체할 수 없다

1. 손절(Stop-loss) — 개별 포지션의 손실을 제한한다

손절은 가장 기본적인 장치다. 하나의 포지션(보유 종목)이 특정 손실 폭을 넘으면 더 버티지 않고 강제로 정리하는 규칙이다.

손절이 막는 실패는 명확하다. "이번엔 곧 반등할 거야"라는 근거 없는 기대로 손실을 계속 방치하는 것이다. 사람이 직접 매매할 때도 가장 흔한 실수 중 하나가 손절을 미루는 것인데, 자동매매 시스템이라고 이 유혹에서 자유롭지 않다. 전략 로직에 버그가 있어서 매도 신호가 정상적으로 나오지 않는 상황에서도, 손절은 독립적으로 작동해 손실을 막아줘야 한다.

python 코드 보기
def check_stop_loss(entry_price: float, current_price: float, stop_loss_pct: float) -> bool:
    """진입가 대비 손실률이 한도를 넘으면 True (매도 필요)"""
    loss_pct = (entry_price - current_price) / entry_price
    return loss_pct >= stop_loss_pct

여기서 중요한 설계 포인트는 손절 판단 로직을 전략 신호 생성 로직과 분리하는 것이다. strategy_fn이 "SELL"을 내지 않아도, 손절 체크는 매 틱마다 독립적으로 돌아야 한다. 한 함수 안에 다 몰아넣으면 전략 버그가 곧 손절 무력화로 이어질 수 있다.

2. 포지션 한도(Position Limit) — 집중도를 제한한다

손절이 "하나의 포지션이 얼마나 깊이 물릴 수 있는가"를 막는다면, 포지션 한도는 "얼마나 많은 자금이 한 곳에 몰릴 수 있는가"를 막는다.

전략이 특정 종목에 대해 계속 강한 매수 신호를 낸다고 해서, 전체 자금이 그 종목 하나에 쏠리게 두면 안 된다. 그 종목에 예상 못한 악재가 터지면 손절 라인이 있어도 손실 규모 자체가 커진다. 분산이 안 되면 손절은 "얼마나 깊이"만 막을 뿐 "얼마나 많이"는 막지 못한다.

python 코드 보기
def can_open_position(current_positions: dict, symbol: str, order_value: float,
                       total_equity: float, max_position_pct: float, max_symbol_count: int) -> bool:
    """새 포지션을 열어도 되는지 판단 (자금 집중도 + 종목 수 제한)"""
    existing_value = current_positions.get(symbol, 0)
    projected_pct = (existing_value + order_value) / total_equity

    if projected_pct > max_position_pct:
        return False
    if symbol not in current_positions and len(current_positions) >= max_symbol_count:
        return False
    return True

이 함수는 주문을 "생성"하는 게 아니라 "생성해도 되는지" 판단만 한다는 점이 핵심이다. 주문 실행 경로 앞단에 이 체크를 반드시 거치게 만들어서, 전략이 아무리 확신에 찬 신호를 내도 집중도 한도를 우회할 수 없게 해야 한다.

3. 서킷브레이커(Circuit Breaker) — 시스템 전체를 멈춘다

앞의 두 장치가 개별 포지션·개별 종목 단위였다면, 서킷브레이커는 시스템 전체를 일시 정지시키는 마지막 방어선이다. 증권거래소의 서킷브레이커(시장 전체 급락 시 거래를 일시 중단하는 제도)에서 이름을 따왔다.

서킷브레이커가 막는 실패는 "정상적인 개별 규칙들이 있는데도 전체 계좌가 짧은 시간에 예상 밖으로 빠르게 손실을 내는 상황"이다. 이런 상황은 개별 손절·포지션 한도가 정상 작동하고 있어도 발생할 수 있다. 예를 들어 여러 포지션이 동시에 손절 라인에 걸리는 시장 전체 충격이 오면, 개별 장치는 각자 제 역할을 하고 있어도 전체 손실 속도는 감당 못 할 수준일 수 있다.

python 코드 보기
def check_circuit_breaker(equity_history: list[float], lookback_minutes: int,
                           max_drawdown_pct: float) -> bool:
    """최근 구간 동안 전체 자산이 한도 이상 빠졌으면 True (시스템 정지 필요)"""
    if len(equity_history) < lookback_minutes:
        return False

    recent = equity_history[-lookback_minutes:]
    peak = max(recent)
    current = recent[-1]
    drawdown_pct = (peak - current) / peak
    return drawdown_pct >= max_drawdown_pct

서킷브레이커가 발동하면 신규 주문 생성 자체를 막고, 기존 포지션 처리는 사람의 최종 승인을 거치도록 설계하는 게 안전하다. 자동으로 전량 청산까지 시키는 건 또 다른 종류의 비가역 결정이라, 그 판단만큼은 사람에게 넘겨야 한다는 게 앞선 안전장치 글에서 다룬 원칙과 이어진다.

세 장치를 함께 두는 이유

세 장치를 표로 정리하면 이렇다.

| 장치 | 막는 실패 | 작동 단위 | |---|---|---| | 손절 | 하나의 포지션이 너무 깊이 물리는 것 | 개별 포지션 | | 포지션 한도 | 자금이 소수 종목에 과도하게 쏠리는 것 | 종목/포트폴리오 | | 서킷브레이커 | 전체 계좌가 짧은 시간에 급격히 무너지는 것 | 시스템 전체 |

하나만 있으면 나머지 두 실패 유형에 그대로 노출된다. 손절만 있으면 분산이 안 돼도 못 막고, 포지션 한도만 있으면 개별 종목이 깊이 물리는 걸 못 막는다. 세 개를 계층으로 쌓아야 서로 다른 방향에서 오는 손실을 각각 막을 수 있다.

오늘의 정리

  1. 리스크 관리 규칙은 앞선 안전장치(예산 한도·비가역 결정 차단·사람 승인)와 다른 층위로, 전략이 정상 작동하는 와중에도 손실 범위를 묶어두는 역할을 한다.
  2. 손절·포지션 한도·서킷브레이커는 각각 개별 포지션, 종목 집중도, 시스템 전체라는 서로 다른 단위에서 오는 손실을 막기 때문에 하나로 대체할 수 없다.
  3. 판단 로직(체크 함수)은 전략 신호 생성 로직과 분리해야, 전략에 버그가 있어도 리스크 관리 장치가 독립적으로 작동할 수 있다.

다음 글에서는 이런 규칙들이 실제로 붙는 대상, 즉 가장 단순한 형태의 매매 전략인 이동평균 교차 전략을 파이썬으로 구현하는 과정을 다뤄본다.


이 글의 코드는 개념 설명을 위한 의사코드이며 특정 실거래 시스템의 구현이 아닙니다. 이 글은 투자 조언이 아니며 특정 종목이나 매매기법을 추천하지 않습니다. 리스크 관리 규칙을 둔다고 해서 손실이 발생하지 않는다는 것을 보장하지 않으며, 투자 손실에 대한 책임은 투자자 본인에게 있습니다.

← 전체 글 목록