분할매도 백테스트 — 승률은 오르고 기대값은 1/9
같은 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 중앙값은 가장 얕았습니다. 수정주가·배당 미포함·생존 종목만이라는 한계가 있고, 과거 결과는 미래를 보장하지 않습니다.
목차
- 실험 설정 — 바꾼 것은 청산 하나
- 결과 — 승률과 기대값이 반대로 움직인다
- 왜 그런가 — 거래를 '얼마나 올랐나'로 쪼개 보기
- 박스장에서는 분할이 이길까 — 연도별
- 비용 — 수수료는 그대로, 주문 수만 3배
- 봇에서 구현할 때 — 수량·호가단위·주문 API
- 재현 코드와 한계
- 자주 묻는 질문
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
읽는 법은 세 줄입니다.
- 승률은 C가 1등, 기대값은 꼴찌권. 1/3을 +5%에서 먼저 팔기 때문에 "조금이라도 벌고 나온 거래"가 늘어 승률이 11%p 올랐습니다. 대신 평균 이익이 18.39% → 7.30%로 쪼그라들었고, 평균 손실(−5.78%)은 거의 그대로라 거래당 기대값이 2.91% → 0.33%로 떨어졌습니다.
- B·C는 거래 수가 늘었다(933 → 1,294·1,180). 일찍 팔고 나와 다시 돌파 신호를 받기 때문입니다. 거래가 많아도 한 건당 기대값이 작으면 누적수익 중앙값은 7.2%·4.3%에 그쳤습니다.
- D는 타협안. 기대값 1.48%로 A의 절반 수준이지만, 종목별 MDD 중앙값은 −39.4%로 네 방식 중 가장 얕았고 22종목 중 5종목에서 누적수익 1위였습니다.
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%
- +5% 근처까지만 갔다 꺾인 거래(186건)에서 분할 C가 유일하게 손실을 −3.10% → −0.59%로 줄였습니다. "올랐다가 다 토해 낸 거래"를 막는 것, 이게 분할매도의 실제 효과입니다.
- +10~30% 구간(244건)에서도 B·C·D가 A보다 낫습니다. A는 10일 저가 이탈까지 기다리다 수익을 상당 부분 돌려줍니다.
- 그런데 +30% 넘게 간 101건에서 A는 평균 47.89%를 가져갔고 C는 9.91%에 멈췄습니다. 101건 × 47.89% ≈ 4,837%p로, A 전체 합(933건 × 2.91% ≈ 2,715%p)보다 큽니다. 이 10.8%를 빼면 A의 나머지 832건 기대값은 약 −2.55%입니다.
즉 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. 봇에서는 이것이 비용보다 큰 문제가 됩니다.
- KIS Open API는 계정 단위 초당 호출 제한이 있어 종목 수 × 3건의 지정가를 한꺼번에 걸면
EGW00201(초당 거래건수 초과)을 만나기 쉽습니다 → EGW00201 해결 - 키움 REST도 같은 이유로
429를 받습니다 → 키움 429 호출 제한 - 주문당 최소 수수료가 있는 계좌·채널이라면 소액 분할은 실제로 비용이 늘어납니다. 본인 계좌의 수수료 체계는 증권사 고지로 확인하십시오.
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),
}
한계는 접지 않고 적습니다.
- 생존 편향 — 지금 살아 있는 22종목만 썼습니다. 상장폐지 종목이 빠지면 추세 전략이 실제보다 좋게 나옵니다(생존 편향 실측).
- 배당 미포함·수정주가 — 오래 들고 가는 A가 배당을 더 받으므로 실제 격차는 조금 더 벌어질 수 있습니다.
- 같은 날 순서 가정 — 손절 먼저. 분봉으로 보면 일부 익절이 실제로는 먼저 체결됐을 수 있습니다.
- 익절 지점은 하나만 시험 — +5/+10/+15%. 목표를 ATR 배수로 두거나 분할 비율을 바꾸면 결과가 달라집니다. 전략마다 다시 돌려야 합니다.
- MDD는 거래를 순서대로 복리 연결한 값이라 실제 계좌의 동시 보유·자금 배분은 반영하지 않았습니다.
정리하면, 분할매도는 수익을 키우는 도구가 아니라 "올랐다가 다 토해 내는 거래"를 줄이는 심리·운영 도구에 가깝습니다. 추세형 전략이라면 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시간 빠른 답변 가능합니다.
무료 상담 시작하기