튜토리얼약 6분 읽기

포트폴리오 리밸런싱 로직 설계 — 목표비중과 허용오차로 매매를 결정하는 법

비중이 흐트러지는 걸 그냥 두면 안 되는 이유

여러 종목을 정해진 비중으로 들고 가려고 해도, 시간이 지나면 각 종목의 가격이 다르게 움직이면서 애초에 정한 비중에서 벗어난다. 이동평균 교차 전략처럼 매수·매도 신호를 만드는 로직과 별개로, "지금 비중이 얼마나 벌어졌고 얼마나 사고팔아야 원래 비중으로 돌아가는가"를 계산하는 로직이 따로 필요하다. 이게 리밸런싱이다.

이 글은 특정 종목이나 자산배분 비율을 추천하지 않는다. 목표비중이 이미 정해져 있다는 전제 하에, 그 비중을 유지하는 로직만 다룬다.

목표비중과 현재비중의 차이를 계산한다

각 종목에는 목표비중(target weight)이 있고, 시간이 지나면 실제 보유 비중(current weight)이 그 값에서 벌어진다. 이 차이를 드리프트(drift)라고 부르면, drift = current_weight - target_weight로 표현할 수 있다. 리밸런싱은 결국 이 drift를 다시 0에 가깝게 만드는 매매를 계산하는 작업이다.

허용오차(threshold) 없이 매번 리밸런싱하면 생기는 문제

매 순간 목표비중과 조금이라도 다르면 즉시 매매하는 방식은 수수료만 쌓이게 만든다. 대신 일정 허용오차(예: 목표비중 대비 일정 폭)를 넘어설 때만 리밸런싱을 실행하는 방식이 일반적이다.

리밸런싱 로직 — 의사코드

python 코드 보기
CASH_SYMBOL = "CASH"

def compute_rebalance_orders(holdings: dict, target_weights: dict, total_value: float, threshold: float):
    orders = []

    for symbol, target_weight in target_weights.items():
        if symbol == CASH_SYMBOL:
            continue  # 현금은 비중 계산(분모)에는 포함하되, 현금 자체에 매매 주문을 내지는 않는다

        current_value = holdings.get(symbol, 0)
        current_weight = current_value / total_value

        drift = current_weight - target_weight

        if abs(drift) < threshold:
            continue

        target_value = total_value * target_weight
        diff_value = target_value - current_value

        orders.append({
            "symbol": symbol,
            "action": "BUY" if diff_value > 0 else "SELL",
            "amount": abs(diff_value),
        })

    return orders

여기서 중요한 건 threshold를 두어 작은 차이는 무시하고, 실제로 유의미하게 벌어졌을 때만 주문을 만든다는 점이다. threshold를 너무 작게 잡으면 잦은 매매로 수수료가 쌓이고, 너무 크게 잡으면 비중 관리가 느슨해진다.

현금을 target_weights에 포함시키는 건 전체 비중 합을 맞추기 위해서지, 현금을 다른 종목처럼 사고팔 수 있어서가 아니다. 현금 비중이 목표보다 낮다고 "현금 매수 주문"을 내는 건 의미가 없다 — 현금은 다른 종목을 팔면 자연히 늘고 사면 자연히 준다. 그래서 위 코드는 현금 항목을 순회 대상에서는 제외하고, 다른 종목의 목표비중·현재비중 계산에 쓰이는 분모(total_value)에만 반영되게 한다.

리밸런싱 주기 정하기 — 시간 기준 vs 오차 기준

두 가지 접근이 있다. 정해진 주기(예: 매월 1회)마다 점검하는 방식과, drift가 threshold를 넘는 즉시 점검하는 방식이다. 실무에서는 두 방식을 같이 쓰는 경우가 많다 — 정기 점검을 기본으로 하되, drift가 threshold를 크게 넘으면 주기와 무관하게 즉시 리밸런싱한다.

자주 하는 실수

  1. 허용오차 없이 미세한 차이에도 매번 매매한다. 수수료가 누적되어 성과를 갉아먹는다.
  2. 리밸런싱 로직에 손절·리스크 관리 규칙을 섞어버린다. 리스크 관리 규칙은 별도 계층에서 처리해야 로직이 꼬이지 않는다.
  3. 현금 비중을 계산에서 빼먹는다. 현금도 target_weights에 포함해야 전체 비중 합이 맞아떨어진다. 다만 현금은 비중 계산에만 넣을 뿐, 현금 자체에 대한 매매 주문을 만들지는 않는다 — 다른 종목 주문의 결과로 자연히 채워진다.

오늘의 정리

  1. 여러 종목을 목표비중으로 유지하려면, 시간이 지나며 벌어지는 비중 차이를 계산하고 되돌리는 리밸런싱 로직이 별도로 필요하다.
  2. 허용오차 없이 매번 리밸런싱하면 수수료만 쌓이므로, 일정 오차를 넘을 때만 매매하는 threshold 설계가 필요하다.
  3. 리밸런싱 주기는 정기 점검과 오차 기준 점검을 함께 쓰는 방식이 실무적으로 흔하다.
  4. 현금도 비중 계산에는 포함해야 전체 비중이 맞아떨어지지만, 현금 자체를 매매 대상으로 다루지는 않는다 — 다른 종목 주문의 결과로 자연히 맞춰진다.

이 글의 코드는 리밸런싱 로직 구조를 설명하기 위한 의사코드이며 특정 종목이나 자산배분 비율을 추천하지 않습니다. 이 글은 투자 조언이 아니며, 수익을 보장하지 않습니다. 투자 손실에 대한 책임은 투자자 본인에게 있습니다.

← 전체 글 목록