AlgoLab Blog · 퀀트·백테스트 실무 · 2026

백테스트 수수료·세금 반영 — 국내주식 거래비용 0.2%

퀀트 · 백테스트 2026-08-10 · 약 9분 읽기 · 알고랩 AlgoLab
한 줄 요약 국내주식 백테스트에 넣어야 할 비용은 딱 세 가지입니다 — ① 위탁수수료(매수·매도 양쪽) ② 증권거래세(매도에만) ③ 슬리피지. 2026년부터 증권거래세는 코스피·코스닥 모두 매도금액의 0.20% 수준이 됐고(코스피는 거래세 0.05% + 농어촌특별세 0.15%), 여기에 위탁수수료와 슬리피지가 더해집니다. 코드로는 backtradercerebro.broker.setcommission()set_slippage_perc(), vectorbtPortfolio.from_signals(fees=, slippage=) 두 줄이면 들어갑니다. 다만 두 라이브러리 모두 "매도에만 붙는 세금"을 기본 기능으로 표현하지 못하므로, 회전율이 높은 전략이라면 커미션 클래스를 직접 상속해야 정확해집니다.

백테스팅 가이드대로 코드를 돌려 깔끔하게 우상향하는 곡선을 처음 봤을 때의 기분은 다들 비슷합니다. 그리고 그 곡선의 절반 이상이 비용 한 줄에서 나온 착시인 경우도 꽤 흔합니다.

비용을 빼먹은 백테스트가 위험한 이유는 결과가 "조금 낙관적"이 되는 게 아니라 전략의 순위 자체가 뒤바뀌기 때문입니다. 비용 0에서 1등이던 고회전 전략이 비용을 넣으면 꼴찌가 되는 일이 그래서 생깁니다. 이 글은 국내주식 기준으로 무엇을 얼마나 넣어야 하고, 그것을 코드 어디에 적는지만 다룹니다.

이 글의 순서

  1. 왜 비용이 순위를 바꾸는가 — 회전율의 산수
  2. 국내주식 비용 3종 정확히
  3. backtrader에 넣기
  4. vectorbt에 넣기
  5. 매도에만 붙는 세금 — 커스텀 커미션
  6. 슬리피지를 찍지 말고 재는 법
  7. 비용 반영 후 확인할 지표

1. 왜 비용이 순위를 바꾸는가

산수 하나면 충분합니다. 왕복 한 번(매수 → 매도)에 붙는 비용을 c, 연간 왕복 횟수를 n이라 하면 연간 비용 부담 ≈ c × n입니다. 여기서 c는 전략과 무관하게 거의 고정이고, n만 전략마다 다릅니다.

전략 성격연간 왕복 n왕복비용 c = 0.25% 가정
장기 리밸런싱 (분기 1회)4회연 1.0%
스윙 (2주 보유)약 25회연 6.3%
단기 (2~3일 보유)약 100회연 25%
데이트레이딩 (하루 1회전)약 250회연 62%

표의 오른쪽 열은 수익률 전망이 아니라 비용만 따로 떼어 곱한 산술값입니다. 그래도 무엇이 문제인지는 분명합니다. 하루 한 번 회전하는 전략은 연간 60% 수준의 비용 허들을 먼저 넘고 나서야 본전입니다. 비용을 0으로 두고 돌린 백테스트는 이 허들을 통째로 지운 것이라 애초에 다른 게임을 시뮬레이션한 셈입니다.

시간대(스캘핑·스윙·포지션) 선택이 단순한 취향 문제가 아닌 이유가 여기 있습니다. 회전율은 곧 비용 구조입니다.

2. 국내주식 비용 3종 정확히

① 위탁수수료 — 매수·매도 양쪽

증권사에 내는 매매 수수료입니다. 증권사·계좌 개설 경로·이벤트에 따라 편차가 큽니다. 비대면 계좌에 수수료 우대가 붙는 경우가 많아 같은 증권사라도 계좌마다 다릅니다. 그래서 백테스트에 넣을 값은 남의 블로그가 아니라 본인 계좌의 고지에서 가져와야 합니다. API 이용료와 시세 데이터 이용료가 따로 나가는지는 증권사 API 수수료 정리에서 다뤘습니다.

② 증권거래세 — 매도에만

이익이 났든 손실이 났든 팔기만 하면 나가는 세금입니다. 보도에 따르면 2026년부터 코스피·코스닥·K-OTC의 증권거래세율이 각각 0.05%포인트 인상됐습니다.

시장증권거래세농어촌특별세매도 시 합계
코스피(유가증권)0.05%0.15%0.20%
코스닥0.20%없음0.20%

백테스트 관점에서 반가운 점이 하나 있습니다. 두 시장의 합계 부담이 같아졌으므로 코스피·코스닥 혼합 유니버스를 돌릴 때 시장별로 세율을 분기할 필요가 없습니다. pykrx로 종목 리스트를 가져와 시장 구분 컬럼으로 세율을 나눠 붙이던 코드는 단순해집니다.

확인하고 쓰십시오. 세율과 적용 시점은 정책에 따라 바뀝니다. 위 수치는 2026년 8월 10일 기준 공개된 보도와 안내를 근거로 정리한 것이며, 백테스트 코드에 상수로 박기 전에 국세청·한국거래소·거래 증권사의 공식 고지로 반드시 대조하십시오. 본 글은 특정 종목이나 수익률에 대한 예측·권유가 아닙니다.

③ 슬리피지 — 신호가와 체결가의 차이

가장 자주 누락되고, 가장 크게 물리는 항목입니다. 백테스트는 보통 종가나 시가에 체결됐다고 가정하지만 실제 봇은 호가창에서 체결됩니다. 시장가로 던지면 반대편 호가부터 잡아먹으며 체결되고, 지정가로 걸면 아예 체결이 안 되는 경우가 생깁니다. 주문 유형별 차이는 주문 종류 정리를 참고하십시오.

비용 0 수수료+세금 +슬리피지 같은 전략·같은 데이터. 달라진 것은 비용 설정 두 줄뿐이다.
비용 항목을 하나씩 켤 때 곡선이 어떻게 내려앉는지 — 개념 도식(실제 수치 아님)

3. backtrader에 넣기

backtrader브로커 객체에 설정하는 방식입니다. cerebro.broker에 수수료와 슬리피지를 각각 걸어 줍니다.

import backtrader as bt

cerebro = bt.Cerebro()
cerebro.broker.setcash(10_000_000)

# ① 수수료 — 비율(perc) 방식. 0.00015 = 0.015%
cerebro.broker.setcommission(
    commission=0.00015,      # 위탁수수료 (본인 계좌 고지값으로 교체)
    commtype=bt.CommInfoBase.COMM_PERC,
    percabs=True,            # True면 0.00015를 '절대비율'로 해석
    stocklike=True,          # 주식형: 증거금·선물 로직 배제
)

# ② 슬리피지 — 체결가를 불리한 쪽으로 이동
cerebro.broker.set_slippage_perc(
    perc=0.001,              # 0.1%
    slip_open=True,          # 시가 주문에도 적용
    slip_match=True,         # 슬리피지 적용 후에도 체결 인정
)

cerebro.adddata(data)
cerebro.addstrategy(MyStrategy)
cerebro.run()

percabs를 반드시 확인하십시오. 이 값이 Falsecommission=0.000150.00015%로 해석돼 비용이 1만분의 1로 줄어듭니다. 비용을 넣었는데 곡선이 그대로라면 십중팔구 여기입니다.

4. vectorbt에 넣기

vectorbt포트폴리오를 만들 때 인자로 넘깁니다. pandas 시리즈로 만든 진입·청산 신호를 그대로 넣는 구조라 훨씬 짧습니다.

import vectorbt as vbt
import pandas as pd

# close: pandas Series (예: pykrx·FinanceDataReader로 받은 종가)
entries = fast_ma > slow_ma
exits   = fast_ma < slow_ma

pf = vbt.Portfolio.from_signals(
    close,
    entries,
    exits,
    init_cash=10_000_000,
    fees=0.00015 + 0.0020/2,   # 수수료 + 세금(왕복 절반씩 근사)
    slippage=0.001,            # 0.1%
    freq='1D',
)

print(pf.stats())

vectorbtfeesslippage매수·매도 양쪽에 동일하게 적용됩니다. 그래서 매도에만 붙는 0.20% 세금을 절반(0.10%)씩 양쪽에 나눠 얹는 근사를 흔히 씁니다. 왕복 총액은 같아지므로 전략 간 비교 목적으로는 충분히 쓸 만합니다. 다만 중간에 청산되지 않고 끝난 포지션이 많은 백테스트에서는 오차가 남습니다. 라이브러리 선택 기준은 파이썬 백테스팅 라이브러리 비교에 정리해 두었습니다.

5. 매도에만 붙는 세금 — 커스텀 커미션

정확도가 필요하면 backtraderCommInfoBase를 상속해 주문 크기의 부호로 매수·매도를 갈라 세금을 붙입니다. size가 양수면 매수, 음수면 매도입니다.

class KrStockCommission(bt.CommInfoBase):
    """국내주식: 수수료는 양방향, 증권거래세는 매도에만"""
    params = (
        ('commission', 0.00015),   # 위탁수수료
        ('tax',        0.0020),    # 매도분 세금 (2026 기준·확인 후 사용)
        ('stocklike',  True),
        ('commtype',   bt.CommInfoBase.COMM_PERC),
        ('percabs',    True),
    )

    def _getcommission(self, size, price, pseudoexec):
        value = abs(size) * price
        fee = value * self.p.commission
        if size < 0:                       # 매도일 때만
            fee += value * self.p.tax
        return fee

cerebro.broker.addcommissioninfo(KrStockCommission())

검증 방법. 이 클래스가 제대로 먹었는지 확인하려면 1주 매수 → 1주 매도만 하는 최소 전략을 돌려 cerebro.broker.getvalue()의 감소분을 손으로 계산한 값과 대조하십시오. 한 줄 차이로 세금이 양방향에 붙어 버리는 실수를 여기서 잡습니다.

6. 슬리피지를 찍지 말고 재는 법

0.1%는 관행적으로 많이 쓰이는 값이지만 근거 있는 숫자는 아닙니다. 더 나은 방법이 둘 있습니다.

방법 A — 호가 단위로 하한선을 잡는다

시장가 주문이 반대편 호가 한 틱에서 체결된다고 보면, 슬리피지 하한 ≈ 호가 단위 ÷ 주가입니다. 호가 단위는 가격대별로 다르고 개편 이력도 있으므로 한국거래소 공식 안내로 현재 값을 확인한 뒤 종목의 가격대에 맞춰 계산하십시오. 같은 1틱이라도 저가주일수록 비율이 커집니다 — 이것이 저가주 전략이 백테스트보다 실전에서 유독 나쁜 이유 중 하나입니다.

방법 B — 실제로 측정한다

모의투자나 소액 실계좌로 직접 재는 것이 가장 정확합니다. 신호가 뜬 시점의 가격과 체결 조회로 받은 실제 체결가를 함께 기록해 두고 차이의 분포를 봅니다. 체결 로그를 남기는 구조가 이미 있다면 추가 작업 없이 뽑을 수 있습니다.

한국투자증권 KIS REST API라면 주문 응답의 주문번호로 체결 내역을 되조회해 주문 단가와 실제 체결 단가를 한 행에 나란히 적재하면 되고, 키움 REST API도 구조는 같습니다. 모의투자로 먼저 재려면 모의투자 전환 시 tr_id 차이를 확인하십시오 — 같은 기능이라도 실전과 모의의 tr_id가 다릅니다. 이렇게 모은 실측 분포의 중앙값이 아니라 상위 구간을 백테스트에 넣는 것이 안전합니다. 슬리피지는 평소에 작고 하필 중요한 순간에 커지는 성질이 있기 때문입니다.

📉
백테스트와 실계좌가 다르게 나온다면

비용 설정·체결 가정·데이터 정합성 중 어디서 벌어졌는지 가르는 작업이 먼저입니다. 알고랩은 검증 구조를 포함해 자동매매 프로그램을 맞춤 제작합니다.

상담해 보기 →

7. 비용 반영 후 확인할 지표

비용을 넣고 나면 수익 곡선만 보지 말고 아래 셋을 반영 전과 나란히 놓고 비교하십시오.

지표보는 이유
MDD (최대낙폭)비용은 손실 구간에서 더 아프게 작동한다. 낙폭이 얼마나 깊어졌는가
샤프지수 (Sharpe)변동성 대비 성과. 고회전 전략은 여기서 가장 크게 무너진다
CAGR과 매매 횟수둘을 함께 봐야 "횟수를 줄이면 나아지는가"가 보인다

그리고 비용을 두 배로 올려 한 번 더 돌려 보십시오. 비용 가정을 조금 바꿨을 뿐인데 결론이 뒤집히는 전략은 애초에 비용 추정 오차 안에서 노는 전략이라는 뜻입니다. 이런 민감도 점검은 데이터 스누핑 함정을 피하는 절차와 함께 묶어 두는 편이 좋습니다.

워크포워드로 구간을 나눠 검증할 때도 비용은 구간마다 동일하게 걸어야 합니다. 학습 구간에서만 비용을 빼고 검증 구간에만 넣으면 최적화가 비용을 모르는 상태에서 파라미터를 고르게 되어 회전율이 과도하게 높은 조합이 선택됩니다. backtradervectorbt비용 설정은 파라미터 탐색 루프 바깥에 한 번만 두는 구조가 맞습니다.

확인 캐치. 이 글의 backtrader·vectorbt 인자명과 동작은 2026년 8월 10일 기준 각 라이브러리 공식 문서를 근거로 정리했으며 버전에 따라 달라질 수 있습니다. 세율·수수료는 정책과 증권사 사정으로 변경되므로 공식 고지로 확인하십시오. 백테스트 결과는 과거 데이터에 대한 계산일 뿐 미래 성과를 보장하지 않습니다. 본 글은 학술·기술적 방법론 설명이며 특정 종목·수익률에 대한 예측이나 투자 권유가 아닙니다.

마무리

비용을 넣는 데 걸리는 시간은 코드 두 줄, 5분입니다. 그 5분을 아끼면 몇 달치 개발과 실계좌 손실로 대가를 치릅니다. 새 전략을 돌릴 때 가장 먼저 세팅하는 것이 비용이어야 하는 이유입니다.

비용을 정직하게 넣고도 살아남는 전략을 찾았다면, 그다음은 실전 투입 전 3단계 검증입니다.

검증까지 포함해 맡기고 싶다면

비용 반영·체결 가정·실계좌 대조까지 설계에 넣습니다. 24시간 빠른 답변 가능합니다.

무료 상담 시작하기