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

분할매도 백테스트 — 승률은 오르고 기대값은 1/9

퀀트 · 청산 실측 2026-10-20 · 약 8분 읽기 · 알고랩 AlgoLab
한 줄 요약

같은 20일 돌파 진입에 청산만 바꿔 국내 22종목 10년(2016-01 ~ 2026-10-16)을 돌린 결과, +5%·+10%·+15%에서 1/3씩 나눠 파는 분할매도는 승률이 35.7% → 46.7%로 가장 높았지만 거래당 기대값은 0.33%로, 익절 없이 추세 이탈까지 들고 간 청산(2.91%)의 약 1/9이었습니다. 수수료·세금은 금액 비례라 나눠 팔아도 거래당 비용은 0.332%로 같았고, 늘어난 것은 주문 건수(1 → 3건)뿐입니다. 손해의 원인은 비용이 아니라 최고 +30% 넘게 오른 거래 101건(10.8%)의 상단을 잘라낸 것입니다. 절반만 +10%에 팔고 나머지를 추적한 방식(D)은 1.48%로 중간이었고 종목별 MDD 중앙값은 가장 얕았습니다. 수정주가·배당 미포함·생존 종목만이라는 한계가 있고, 과거 결과는 미래를 보장하지 않습니다.

목차

  1. 실험 설정 — 바꾼 것은 청산 하나
  2. 결과 — 승률과 기대값이 반대로 움직인다
  3. 왜 그런가 — 거래를 '얼마나 올랐나'로 쪼개 보기
  4. 박스장에서는 분할이 이길까 — 연도별
  5. 비용 — 수수료는 그대로, 주문 수만 3배
  6. 봇에서 구현할 때 — 수량·호가단위·주문 API
  7. 재현 코드와 한계
  8. 자주 묻는 질문

1. 실험 설정 — 바꾼 것은 청산 하나

"나눠 팔면 덜 후회한다"는 말은 많은데, 같은 진입에서 청산만 바꿔 비교한 숫자는 찾기 어렵습니다. 그래서 손절선 −5% vs ATR 2배 실측과 같은 틀(종목 22개·20일 돌파·FinanceDataReader 수정주가)을 그대로 쓰고 익절 방식만 네 가지로 바꿨습니다.

항목설정
진입종가가 직전 20거래일 고가 돌파 → 다음 날 시가 매수
공통 청산① 손절 −8%(장중 저가 터치 시 손절가 체결, 시가부터 아래면 시가) ② 추세 이탈 — 종가가 직전 10거래일 저가를 깨면 다음 날 시가
A 추세만익절 없음. ①②로만 나간다 (기준선)
B 전량 +10%+10% 지정가에 전량 매도
C 1/3 분할+5% / +10% / +15%에 1/3씩. 남은 몫은 ①②
D 1/2 + 추적+10%에 절반 매도 → 남은 절반의 손절가를 본전으로 올리고 ②로
비용수수료 매수·매도 각 0.015%(가정) + 증권거래세 0.20%(2026년 코스피·코스닥) + 체결마다 슬리피지 0.05% → 왕복 0.33%
종목·기간2016년 이전 상장 22종목(삼성전자·SK하이닉스·NAVER·카카오·에코프로·알테오젠 등), 2016-01-01 ~ ASOF = "2026-10-16"

일봉으로는 장중 순서를 모르므로, 같은 날 손절가와 익절가가 둘 다 닿으면 손절이 먼저라고 봤습니다(분할에 불리한 쪽이 아니라 모든 방식에 같은 보수적 가정). 이 가정이 결과를 얼마나 흔드는지는 손절·익절 같은 날 실측에 따로 있습니다. 비용 숫자의 근거는 백테스트 거래비용 글을 따랐습니다.

2. 결과 — 승률과 기대값이 반대로 움직인다

스크립트 출력 원문입니다(22종목 거래 합산).

ASOF 2026-10-16  tickers 22  trades total 4,354  round-trip cost 0.33%

[1] 청산별 요약 (22종목 거래 합산)
exit           trades   win%  expectancy  avg_win  avg_loss  hold_days  sell_orders  cost_per_trade
A 추세만            933  35.7%      2.91%   18.39%   -5.69%      22.5        1.00        0.341%
B 전량 +10%       1294  42.7%      0.30%    9.06%   -6.22%      12.0        1.00        0.332%
C 1/3 분할        1180  46.7%      0.33%    7.30%   -5.78%      14.6        3.00        0.332%
D 1/2 + 추적       947  42.2%      1.48%   11.52%   -5.87%      21.5        2.00        0.336%

[2] 종목별 누적수익 중앙값 / MDD 중앙값 / 1위 횟수
A 추세만        total_med    26.1%  mdd_med  -42.0%  best_in 12
B 전량 +10%    total_med     7.2%  mdd_med  -49.7%  best_in  2
C 1/3 분할     total_med     4.3%  mdd_med  -40.6%  best_in  3
D 1/2 + 추적   total_med     9.0%  mdd_med  -39.4%  best_in  5

읽는 법은 세 줄입니다.

승률 (막대 길이 = %) 거래당 기대값 (막대 길이 = %) A 추세만B 전량+10%C 1/3 분할D 1/2+추적 35.7 42.7 46.7 42.2 2.91 0.30 0.33 1.48 국내 22종목 · 2016-01 ~ 2026-10-16 · 20일 돌파 진입 동일 · 왕복 비용 0.33% 반영
나눠 팔수록 이기는 거래는 늘지만, 한 건이 벌어 오는 돈은 줄어든다

3. 왜 그런가 — 거래를 '얼마나 올랐나'로 쪼개 보기

A 방식의 거래 933건을 보유 중 최고 얼마까지 올랐는지(MFE, 최대 유리 변동)로 나누고, 같은 날 진입한 B·C·D 거래의 기대값을 나란히 놓았습니다.

[3] 거래를 '최대 얼마까지 올랐나(MFE)'로 나눠 본 거래당 기대값
bucket     n           A 추세만     B 전량 +10%      C 1/3 분할    D 1/2 + 추적
<+5%      402        -6.15%        -6.15%        -6.15%        -6.15%
+5~10%    186        -3.10%        -3.10%        -0.59%        -3.10%
+10~15%   101        -0.06%         9.31%         4.65%         5.12%
+15~30%   143         6.52%         9.78%         9.80%         8.36%
+30%+     101        47.89%         9.89%         9.91%        24.48%

즉 20일 돌파 같은 추세 추종 전략은 대부분 조금씩 잃고, 드문 대박 몇 건으로 버는 구조입니다. 분할매도는 그 대박의 상단을 +15%에서 잘라 버리므로, 자주 이기는 대신 전략의 엔진을 끕니다. D가 중간인 이유도 같습니다 — 절반은 +10%에서 잘리지만 나머지 절반이 추세를 끝까지 탔습니다(+30% 구간 24.48%).

4. 박스장에서는 분할이 이길까 — 연도별

흔한 반론은 "추세장에선 들고 가는 게 낫고, 박스장에선 나눠 파는 게 낫다"입니다. 진입 연도별로 갈라 코스피(KS11) 연간 등락과 나란히 놓았습니다.

[4] 연도별 거래당 기대값 + 코스피 연간 등락
year  kospi           A 추세만     B 전량 +10%      C 1/3 분할    D 1/2 + 추적
2016    3.3%        -0.28%        -0.36%        -0.63%        -0.25%
2017   21.8%         4.62%         1.98%         2.37%         3.70%
2018  -17.3%        -0.28%        -1.41%        -0.98%        -0.93%
2019    7.7%         1.71%        -0.35%        -0.27%        -0.09%
2020   30.8%         9.10%         1.32%         1.33%         5.18%
2021    3.6%         0.81%        -0.58%        -0.45%        -0.14%
2022  -24.9%        -2.09%        -1.29%        -1.30%        -1.55%
2023   18.7%         3.97%         0.64%         0.78%         2.16%
2024   -9.6%        -0.45%        -0.91%        -0.52%        -0.13%
2025   75.6%        12.14%         1.84%         1.65%         4.13%
2026   34.5%         0.35%         1.10%         0.56%         4.16%

이 데이터로는 "박스장엔 분할"이 확인되지 않았습니다. 코스피 등락이 한 자릿수였던 2016·2019·2021년에도 A가 C보다 높았습니다. C가 A를 앞선 해는 코스피가 24.9% 빠진 2022년과, 진행 중이라 미청산 포지션이 마지막 종가로 평가된 2026년뿐입니다. 2020년(+30.8%)·2025년(+75.6%) 같은 강한 상승장에서는 격차가 7~10%p로 벌어졌습니다. 다만 이건 돌파형 진입의 결과입니다. 눌림목·역추세 진입이라면 큰 추세를 기대하지 않으므로 결론이 바뀔 수 있습니다.

5. 비용 — 수수료는 그대로, 주문 수만 3배

"세 번 팔면 수수료가 세 배"는 오해입니다. 국내주식의 위탁수수료와 증권거래세 0.20%는 체결 금액에 붙으므로, 100주를 한 번에 팔든 33·34·33주로 나눠 팔든 총액이 같습니다. 출력의 cost_per_trade가 B 0.332% = C 0.332%로 같은 이유입니다(A가 0.341%로 약간 높은 건 오래 들고 가 매도 금액이 커져서입니다).

바뀌는 것은 주문 건수입니다 — sell_orders 1 → 3. 봇에서는 이것이 비용보다 큰 문제가 됩니다.

6. 봇에서 구현할 때 — 수량·호가단위·주문 API

① 수량을 나누고, 목표가를 호가단위로 올린다

1/3이 정수로 안 떨어지고, +5% 가격이 호가단위에 안 맞습니다. 누적 반올림으로 합계를 보존하고, 매도 지정가는 호가단위로 올림해 목표가보다 싸게 팔지 않게 합니다. 호가단위는 2023년 1월 개편 뒤 코스피·코스닥 공통 기준입니다(현재 값은 한국거래소 공지로 확인).

import math

def tick(p):                       # KRX 호가가격단위(2023-01 개편, 코스피·코스닥 공통)
    for lim, t in ((2000, 1), (5000, 5), (20000, 10), (50000, 50), (200000, 100), (500000, 500)):
        if p < lim:
            return t
    return 1000

def up_to_tick(p):                 # 매도 지정가는 호가단위로 '올림' — 목표가보다 싸게 팔지 않게
    t = tick(p)
    return int(math.ceil(p / t) * t)

def split_qty(q, n=3):             # 누적 반올림 — 합계가 항상 q
    cum = [round(q * k / n) for k in range(1, n + 1)]
    return [b - a for a, b in zip([0] + cum[:-1], cum)]

def plan(entry, q, tps=(0.05, 0.10, 0.15)):
    return [(qty, up_to_tick(entry * (1 + tp))) for qty, tp in zip(split_qty(q, len(tps)), tps) if qty > 0]

실행 결과(Python 3.10)입니다. 두 번째 줄이 함정입니다 — 2주는 세 번에 못 나눠서 +10% 주문이 사라집니다. 네 번째 줄은 목표가가 20,000원을 넘으며 호가단위가 10원 → 50원으로 바뀐 경우입니다.

71300 10 -> [(3, 74900), (4, 78500), (3, 82000)]
71300 2 -> [(1, 74900), (1, 82000)]
1985 100 -> [(33, 2085), (34, 2185), (33, 2285)]
18950 7 -> [(2, 19900), (3, 20850), (2, 21800)]

수량이 애초에 얼마나 나오는지는 포지션 사이징 글에서 정합니다. 분할을 쓸 거라면 사이징 단계에서 "분할 횟수 이상"을 최소 수량으로 두는 편이 깔끔합니다.

② 주문 — KIS TTTC0011U / 키움 kt10001

계획 한 줄이 지정가 매도 1건입니다. KIS는 현행 매도 tr_id TTTC0011U(모의 VTTC0011U), 키움 REST는 kt10001(POST /api/dostk/ordr)입니다. 구 tr_id(TTTC0801U)와의 차이는 KIS 주문 tr_id 신·구 글에 있습니다.

# KIS — TTTC0011U 지정가 매도 (공식 order_cash.py 파라미터 기준)
params = {
    "CANO": acct8, "ACNT_PRDT_CD": "01",
    "PDNO": "005930", "ORD_DVSN": "00",       # 00 지정가
    "ORD_QTY": "3", "ORD_UNPR": "74900",
    "EXCG_ID_DVSN_CD": "KRX",
    "SLL_TYPE": "01",                         # 01 일반매도
    "CNDT_PRIC": "",
}

# 키움 REST — kt10001 지정가 매도
body = {"dmst_stex_tp": "KRX", "stk_cd": "005930",
        "ord_qty": "3", "ord_uv": "74900", "trde_tp": "0"}   # 0 보통(지정가)

손절이 거부되는 함정 — 익절 지정가 3건을 미리 걸어 두면 그 수량은 매도 주문에 묶여 매도가능수량에서 빠집니다. 가격이 반대로 가서 −8% 손절이나 추세 이탈 매도를 내면 "주문가능수량 부족"으로 거부될 수 있습니다. 손절 전에 남은 익절 주문을 먼저 취소(키움 kt10003, KIS TTTC0013U)하고, 취소 확인 뒤 잔량을 매도하는 순서를 코드에 넣으십시오. 취소·정정의 주문번호 처리는 키움 정정·취소 글에 정리했습니다.

7. 재현 코드와 한계

전체 스크립트: /downloads/split_exit_backtest.py. 빈 폴더에서 처음부터 실행해 위 출력이 그대로 나오는 것을 확인했습니다(FinanceDataReader 0.9.202 · pandas 2.2.3). 기준일을 고정한 핵심 부분입니다.

ASOF = "2026-10-16"     # 기준일 고정 — None 이면 실행 시점까지 (숫자 달라짐)

def load(code):
    df = fdr.DataReader(code, "2015-06-01", ASOF)[["Open", "High", "Low", "Close"]].astype(float)
    if ASOF:
        df = df[df.index <= ASOF]          # 캐시·신규 다운로드 모두 같은 자르기
    return df[(df["Open"] > 0) & (df["Low"] > 0)]

EXITS = {
    "A 추세만":     dict(legs=[(1.0, None)], be=False),
    "B 전량 +10%":  dict(legs=[(1.0, 0.10)], be=False),
    "C 1/3 분할":   dict(legs=[(1/3, 0.05), (1/3, 0.10), (1/3, 0.15)], be=False),
    "D 1/2 + 추적": dict(legs=[(0.5, 0.10), (0.5, None)], be=True),
}

한계는 접지 않고 적습니다.

정리하면, 분할매도는 수익을 키우는 도구가 아니라 "올랐다가 다 토해 내는 거래"를 줄이는 심리·운영 도구에 가깝습니다. 추세형 전략이라면 D처럼 일부만 익절하고 나머지는 추세를 태우는 쪽이 이 데이터에서는 균형이 좋았습니다. 청산 설계 전반은 자동매매 청산 설계 글, 백테스트 전체 흐름은 자동매매 백테스트 가이드를 참고하십시오. 이 글은 특정 종목이나 전략을 권하지 않으며, 수치는 과거 데이터에 대한 학술적 비교입니다.

8. 자주 묻는 질문

분할매도를 하면 수수료가 세 배로 늘어나나요?

국내주식 위탁수수료와 증권거래세는 체결 금액에 비례하므로 같은 수량을 세 번에 나눠 팔아도 총비용은 거의 같습니다. 이 실측에서 거래당 비용은 전량 매도 0.332%, 1/3 분할 0.332%였습니다. 늘어나는 것은 주문 건수(1건에서 3건)이며, 주문당 최소 수수료가 있는 계좌라면 따로 계산해야 합니다.

분할매도가 기대값을 낮춘 이유는 무엇인가요?

큰 추세에서 나오는 수익을 미리 잘라내기 때문입니다. 추세 청산(A)의 거래 933건 중 최고 +30% 이상 오른 101건의 평균 수익이 47.89%였는데, 1/3 분할(C)은 같은 거래에서 9.91%만 가져갔습니다. 이 101건을 빼면 A의 기대값은 마이너스라서, 20일 돌파 같은 추세 전략에서 상단을 자르는 청산은 손해가 큽니다.

박스장에서는 분할매도가 유리하지 않나요?

이 실험에서는 확인되지 않았습니다. 코스피 연간 등락이 한 자릿수였던 2016·2019·2021년에도 추세 청산의 거래당 기대값이 1/3 분할보다 높았고, 분할이 앞선 해는 코스피가 24.9% 떨어진 2022년과 진행 중인 2026년뿐이었습니다. 진입 규칙이 돌파형이 아니라 역추세형이면 결과가 다를 수 있으니 자기 전략으로 다시 돌려 보십시오.

보유 수량이 2주면 1/3씩 나눠 팔 수 있나요?

세 번에는 나눌 수 없습니다. 누적 반올림으로 나누면 2주는 1주·0주·1주가 되어 +10% 구간 주문이 사라집니다. 수량이 분할 횟수보다 적으면 분할 횟수를 줄이거나, 봇이 주문 계획을 만들 때 0주 구간을 건너뛰도록 처리해야 합니다.

내 전략의 청산 규칙, 같은 방식으로 돌려 드립니다

진입은 그대로 두고 익절·손절·분할 방식을 바꿔 비교한 뒤, 고른 규칙을 KIS·키움 주문 코드까지 연결해 드립니다. 24시간 빠른 답변 가능합니다.

무료 상담 시작하기