본문 바로가기
편한경제

파이썬 ETF 자동 리밸런싱 — 10년 백테스트로 본 진짜 효과와 코드 공개

by 주식하는 개발자 퍼플 2026. 6. 22.
반응형

파이썬으로 ETF 포트폴리오를 자동 리밸런싱하면 수익이 늘까요? 결론부터 말하면, 10년치 실제 데이터로 직접 백테스트해 보니 리밸런싱과 그냥 묻어두기(매수후보유)의 수익은 사실상 같았습니다(10년에 +1.1%포인트). 리밸런싱의 진짜 효과는 수익이 아니라 "내가 정한 위험 비중을 지키는 것"이었고, 그 귀찮고 직관에 반하는 규율을 사람 대신 지켜주는 게 파이썬 자동화입니다. 백테스트 숫자와 그대로 돌아가는 리밸런싱 코드를 전부 공개합니다.

안녕하세요, 주식하는 개발자 퍼플입니다. "리밸런싱하면 수익률이 올라간다"는 말을 흔히 듣지만, 정작 10년을 직접 돌려본 글은 드물더군요. 그래서 yfinance로 받은 실제 총수익 데이터로 '리밸런싱 vs 안 함'을 백테스트하고, 결과를 바탕으로 바로 실행되는 리밸런싱 계산기까지 파이썬으로 만들어 봤습니다. 아래 숫자는 전부 그 계산 결과입니다.

결론 먼저: 리밸런싱은 '수익 부스터'가 아니다

성장 ETF(VOO)와 인컴 ETF(SCHD)를 50:50으로 담아 10년을 굴렸습니다. 결과는 셋으로 요약됩니다.

  • 수익은 무승부. 매년 리밸런싱(275.37%)이나 한 번 사서 묻어두기(274.26%)나 10년 누적 차이는 1.1%포인트, 연복리로는 14.15% vs 14.12%. 사실상 같습니다.
  • 위험(낙폭)도 거의 같다. 최대낙폭 -33.50% vs -33.51%, 변동성 16.80% vs 16.84%. 둘 다 주식형이라 리밸런싱의 위험 저감 효과는 미미했습니다.
  • 진짜 차이는 '비중 쏠림'. 묻어두기는 50:50이 어느새 56.7:43.3으로 성장주 쪽에 쏠렸습니다. 내가 정하지 않은 위험을 떠안게 된 거죠. 리밸런싱은 이걸 막아 비중을 그대로 지켰습니다.

즉 리밸런싱은 돈을 더 벌어주는 기술이 아니라, 내가 정한 위험 수준을 흐트러지지 않게 지키는 규율입니다. 그리고 이 규율은 사람이 손으로 지키기 가장 어려운 종류죠. 그래서 코드가 필요합니다.

리밸런싱이 뭐고, 왜 하나

리밸런싱은 시간이 지나며 틀어진 자산 비중을 원래 목표대로 되돌리는 것입니다. 성장주(③ 국내상장 미국S&P500 ETF 같은)와 인컴 자산(② 커버드콜 월배당 ETF)을 반반 담았다고 해봅시다. 성장주가 더 오르면, 가만히 둬도 비중은 저절로 성장주 쪽으로 커집니다.

위 차트 아래쪽이 그 '쏠림'입니다. 50%로 시작한 VOO 비중이 가만히 두면 한때 59%까지 올라갔다가 56.7%로 끝납니다. 문제는 이게 내가 결정한 게 아니라는 점입니다. "나는 안정적인 반반 포트폴리오를 원해"라고 시작했는데, 어느새 성장주에 더 크게 베팅한 상태가 됩니다. 시장이 꺾이면 더 크게 다치죠. 리밸런싱은 오른 자산을 조금 팔고 덜 오른 자산을 사서, 비중을 다시 50:50으로 맞추는 행위입니다.

10년 백테스트: 직접 돌려봤다

yfinance로 VOO·SCHD의 10년치 총수익(배당 재투자) 데이터를 받아, 매년 첫 거래일에 50:50으로 되돌리는 전략한 번 사고 그대로 두는 전략을 비교했습니다.

50:50 (VOO+SCHD), 10년 매년 리밸런싱 매수후보유(안 함)
누적 총수익 275.37% 274.26%
CAGR(연복리) 14.15% 14.12%
최대낙폭(MDD) -33.50% -33.51%
연변동성 16.80% 16.84%
최종 비중(VOO/SCHD) 48.7 / 51.3 56.7 / 43.3

표가 말하는 건 분명합니다. 수익도, 낙폭도, 변동성도 거의 차이가 없습니다. "리밸런싱 보너스로 수익이 뛴다"는 흔한 주장은 이 구간·이 조합에선 성립하지 않았습니다(오른 걸 파는 손실과 싸게 사 모으는 이득이 서로 상쇄됐죠). 유일하게 또렷한 차이는 맨 아랫줄, 최종 비중입니다. 리밸런싱은 의도한 50:50 근처를 지켰고, 안 하면 56.7:43.3으로 쏠렸습니다.

참고로 한 자산만 100% 담았다면 VOO는 324.44%(연 15.55%), SCHD는 224.07%(연 12.48%)였습니다. 이 둘은 지난 SCHD vs VOO 백테스트 글의 숫자와 정확히 일치합니다. 50:50 블렌드는 그 중간(275%)에 자리하면서, 한쪽에 몰빵했을 때의 변동성(VOO 18.05%)보다 낮은 16.8%를 기록했습니다. 블렌드의 목적이 바로 이것이고, 리밸런싱은 그 블렌드가 시간이 지나도 블렌드로 남게 해줍니다.

그래서, 코드 — 리밸런싱 계산기

여기가 개발자의 영역입니다. 리밸런싱의 핵심은 단순한 산수예요. 현재 비중과 목표 비중의 차이를 주문 수량으로 바꾸는 것. 증권사도, API 키도 필요 없는 순수 파이썬 함수입니다.

def rebalance_orders(holdings, prices, target_weights, band=0.0):
    """현재 보유분을 목표 비중으로 되돌리는 정수 주문(주) 계산.
    holdings        {종목: 보유수량}
    prices          {종목: 현재가}
    target_weights  {종목: 목표비중}   # 합 = 1.0
    band            리밸런싱 밴드. |현재비중-목표비중|이 band 미만이면 매매 안 함.
    반환            {종목: 주문수량}    # +면 매수, -면 매도
    """
    total = sum(holdings.get(t, 0) * prices[t] for t in target_weights)
    orders = {}
    for t, target_w in target_weights.items():
        current_value = holdings.get(t, 0) * prices[t]
        current_w = current_value / total if total else 0.0
        if abs(current_w - target_w) < band:        # 밴드 안이면 건너뜀
            orders[t] = 0
            continue
        target_value = total * target_w
        orders[t] = int((target_value - current_value) / prices[t])  # 정수 주
    return orders

백테스트와 똑같이 50:50이 56.6:43.4로 틀어진 상황을 넣어 봤습니다(국내상장 ETF 기준, 가격은 예시값):

holdings = {"379800.KS": 298, "441640.KS": 394}        # KODEX 미국S&P500 / 커버드콜
prices   = {"379800.KS": 19_000, "441640.KS": 11_000}
target   = {"379800.KS": 0.5, "441640.KS": 0.5}

print(rebalance_orders(holdings, prices, target))
# 평가금액 합계 9,996,000원 (현재 56.6% / 43.4%)
# → {'379800.KS': -34, '441640.KS': 60}
#   즉 KODEX 미국S&P500 34주 매도, 커버드콜 60주 매수

이 주문대로 체결하면 비중은 다시 50.1 : 49.9로 돌아옵니다. 한 가지 정직하게 짚으면, 정수 단위로 사고팔다 보니 매도액(64.6만)과 매수액(66.0만)이 딱 안 맞아 약 1.4만원의 현금 정산이 생깁니다. 실전에선 이 자투리를 현금으로 채우거나 다음 적립금으로 메우면 됩니다.

band(리밸런싱 밴드)는 실전의 핵심 옵션입니다. band=0.05로 두면 비중이 목표에서 5%포인트 이상 벌어졌을 때만 매매합니다. 매번 칼같이 맞추면 거래비용·세금만 늘기 때문에, "어느 정도 틀어지면 그때 손본다"는 게 현실적이죠. 위 예시는 6.6%포인트 벌어져 밴드를 넘었으니 실행됩니다.

자동화의 마지막 1마일 — 증권사 API

계산기까지는 브로커도 키도 필요 없었습니다. 진짜 자동화, 즉 계산한 주문을 실제로 내는 단계에서만 증권사 API가 등장합니다. 이 블로그가 구글에서 꾸준히 검색되는 그 영역이죠. 다행히 지금은 주요 증권사 모두 OS를 안 가리는 REST API를 제공해서, 예전보다 훨씬 붙이기 쉬워졌습니다.

  • 키움증권 — 전통의 OpenAPI+Windows 전용 32비트 OCX(ActiveX/COM) 방식이라 영웅문을 깔고 32비트 파이썬으로 붙여야 해서 서버 자동화가 까다로웠습니다. 하지만 지금은 별도로 키움 REST API(openapi.kiwoom.com, 앱키·앱시크릿·접근토큰)가 나와 있어, OS 제약 없이 requests로 깔끔하게 붙습니다. 서버에 올려 돌릴 거라면 이 REST 쪽이 훨씬 편합니다.
  • 한국투자증권 KIS Developers — 마찬가지로 REST API + 앱키·앱시크릿(apiportal.koreainvestment.com) 방식입니다.

둘 다 인증 모델이 같습니다 — 앱키·앱시크릿으로 접근토큰(access token)을 발급받고, 그 토큰으로 잔고 조회·주문 API를 호출합니다. 구조는 어느 쪽이든 동일합니다 — ① 잔고 조회로 현재 보유수량·가격을 받아오고 → ② 위 rebalance_orders로 주문 수량을 계산하고 → ③ 그 수량만큼 주문 API를 호출한다. 개념만 보이면 이렇습니다.

# 의사코드 — 실제 엔드포인트·인증은 각 증권사 공식 문서를 따르세요.
holdings, prices = broker.get_balance()              # ① 잔고 조회
orders = rebalance_orders(holdings, prices, target, band=0.05)  # ② 계산
for ticker, qty in orders.items():                    # ③ 주문
    if qty > 0:   broker.buy(ticker, qty)
    elif qty < 0: broker.sell(ticker, -qty)

여기에 운영체제 스케줄러(윈도우 작업 스케줄러, 리눅스 cron)로 "분기 첫 영업일 장 마감 후 실행"을 걸면, 사람이 손대지 않는 자동 리밸런싱이 완성됩니다. 실제 주문 함수(broker.buy 등)의 정확한 호출법은 증권사마다 달라 키움 REST API·한국투자증권 KIS Developers 공식 문서를 보고 채워 넣으면 됩니다. 검증 안 된 API 호출을 흉내 내기보다, 키 없이도 100% 돌아가는 계산기 부분을 확실히 잡는 게 먼저입니다.

함정 — 자동화 전에 알아야 할 것

  • 거래비용·세금이 수익을 깎는다. 너무 자주 맞추면 매매수수료와 (일반계좌의) 매도 시 세금이 쌓입니다. 위 백테스트는 비용 0 가정이라 차이가 1%포인트였지, 비용을 넣으면 잦은 리밸런싱이 오히려 손해일 수 있습니다. → band로 매매 빈도를 줄이세요.
  • 국내상장 ETF는 매매차익에 배당소득세 15.4%. 리밸런싱하며 오른 ETF를 팔면 차익에 세금이 붙습니다. 그래서 리밸런싱은 절세계좌 안에서 할수록 유리합니다.
  • 그 절세 그릇이 바로 연금저축·IRP입니다. 연금저축 세액공제 계좌 안에서는 매매차익·분배금 과세가 인출 시점까지 미뤄져(과세이연), 계좌 안에서 리밸런싱을 아무리 해도 그때그때 세금이 안 나갑니다. 자동 리밸런싱의 최적지는 연금계좌라는 뜻이죠. 단 연금계좌는 국내상장 ETF만 담기니, 위 예시처럼 ②③ 같은 국내상장 상품으로 짜야 합니다.
  • 백테스트는 과거다. 지난 10년이 무승부였다고 미래가 그렇다는 보장은 없습니다. 코드는 도구일 뿐, 어떤 자산을 어떤 비중으로 담을지는 본인의 판단입니다.

누구에게 맞나

  • 이미 증권사 API를 다뤄본 개발자 → 1순위. 계산기는 위 코드 그대로, 마지막 주문 호출만 본인 증권사 문서로 채우면 끝입니다.
  • 비중이 자꾸 틀어지는 게 신경 쓰이는 투자자 → 코드를 안 짜더라도 "리밸런싱은 수익이 아니라 위험 관리"라는 결론만 가져가도 충분합니다. 증권사 앱에서 분기에 한 번 수동으로 맞춰도 효과는 같습니다.
  • 반대로, 단일 ETF에 적립식으로만 묻는 사람 → 리밸런싱할 비중 자체가 없으니 해당 없음. 굳이 만들 필요 없습니다.

요약

성장+인컴 50:50 포트폴리오를 10년 백테스트한 결과, 자동 리밸런싱의 수익(275.37%)은 그냥 묻어두기(274.26%)와 사실상 같았고 낙폭·변동성도 거의 동일했습니다. 유일한 실질 차이는 비중으로, 리밸런싱을 안 하면 50:50이 56.7:43.3으로 성장주에 쏠려 의도하지 않은 위험을 떠안습니다. 즉 리밸런싱은 수익을 키우는 마법이 아니라 정한 위험 수준을 지키는 규율이고, 그 직관에 반하는 규율을 사람 대신 지켜주는 게 파이썬 자동화입니다. 핵심 계산기는 키 없이 돌아가는 순수 파이썬 몇 줄이며, 실제 주문만 증권사 REST API(키움·한국투자증권 모두 앱키·앱시크릿 방식)로 연결하면 됩니다. 그리고 그 리밸런싱은 연금저축 같은 절세계좌 안에서 할 때 세금까지 아낄 수 있습니다.


데이터 기준: 누적수익·CAGR·MDD·변동성은 yfinance의 VOO·SCHD 10년치 총수익(배당 재투자) 데이터를 직접 계산한 값입니다(공통 거래일 2016-06-20~2026-06-18, 2,514거래일, 2026년 6월 기준). 리밸런싱은 매년 첫 거래일 50:50 복원, 거래비용·세금은 0 가정입니다. 단일 ETF 수치(VOO 324.44%·SCHD 224.07%)는 SCHD vs VOO 글과 동일 데이터로 교차검증했습니다. 코드 예시의 종목가격(19,000·11,000원)은 계산 시연용 예시값으로 실제 시세가 아니며, 증권사 API의 실제 호출법은 각 사 공식 문서를 따르세요.

과거 성과가 미래 수익을 보장하지 않으며, 투자 판단과 책임은 본인에게 있습니다.

📮 새 글, 놓치지 마세요

ETF·연금·투자전략을 데이터로 파헤치는 글을 꾸준히 올립니다. 이 블로그의 '구독하기' 버튼(글 아래 또는 우측 상단)을 누르면 새 글을 티스토리 홈·앱에서 바로 받아볼 수 있어요.

반응형
  • 페이스북 공유하기
  • 트위터 공유하기
  • 카카오툭 공유하기
  • 네이버 블로그 공유하기
  • 네이버 밴드에 공유하기

댓글