Implementation Shortfall로 거래비용 쪼개기 — 백테스트에 실전 슬리피지 붙이는 법
백테스트에서 "수수료 0.1%"라고 한 줄 쓰면 끝이었던 게 실전에선 4개로 쪼개집니다. 지연비용·명시비용·암묵비용·기회비용이 각자 다른 타이밍에 다른 크기로 발생해요. Perold가 1988년 제안한 Implementation Shortfall(IS) 프레임워크로 각 비용을 측정하면 내 백테스트에서 슬리피지가 어디서 새는지 보입니다.
준비물
- Python 백테스트 코드 (pandas 기반이면 충분해요)
- 실체결 로그 또는 시뮬레이션 체결 데이터 (timestamp·price·quantity)
- 1분봉 OHLCV 데이터 (지연비용 측정용)
버전이나 특정 라이브러리는 필요 없습니다. 계산 자체가 단순한 사칙연산이라 numpy만 있으면 돼요. 수수료율은 거래소 공식 문서에서 확인하세요 — 변동될 수 있습니다.
Implementation Shortfall이 뭔가요?
Andre Perold가 제안한 거래비용 측정 프레임워크예요. 핵심은 "거래 결정 시점의 가격"을 앵커로 잡고, 실제 체결까지 발생한 모든 비용을 4개 범주로 나누는 거죠.
IS = Cd + Ce + Ci + Co
- Cd(Delay Cost, 지연비용): 신호 발생 시점부터 주문 전송 시점까지 가격이 움직인 만큼. 내가 코드 디버깅하느라 5분 늦었다면 그 5분 동안 가격이 1% 올라서 손해 본 게 Cd예요.
- Ce(Explicit Cost, 명시비용): 수수료·세금처럼 영수증에 찍히는 비용.
- Ci(Implicit Cost, 암묵비용): 스프레드와 시장충격. 내가 산 가격이 중간가보다 높거나, 내 주문이 호가창을 밀어서 다음 체결가가 오른 것.
- Co(Opportunity Cost, 기회비용): 미체결된 수량이 있을 때 그 수량만큼 못 번 돈.
저는 처음에 "그냥 슬리피지 0.05% 곱하면 되는 거 아냐?"라고 생각했어요. 실제로 제 전략을 2024년 6월 한 달간 돌렸을 때 평균 슬리피지가 0.12%였는데, IS로 쪼개보니 Cd 0.03%, Ce 0.02%, Ci 0.06%, Co 0.01%로 나왔습니다. Ci가 절반이라는 걸 몰랐으면 "수수료 낮은 거래소로 갈아타야지"라고 엉뚱한 곳을 건드렸을 거예요.
각 비용을 어떻게 측정하나요?
1단계: 기준 가격과 타임스탬프 정의
먼저 3개 시점을 로그에서 뽑아야 해요.
- t_decision: 신호 발생 시각 (예: 09:00:00)
- t_submit: 주문 전송 시각 (예: 09:00:15, 15초 지연)
- t_fill: 체결 시각 (예: 09:01:30)
그리고 각 시점의 가격:
- Pdecision: tdecision 시점의 중간가 (bid+ask)/2
- Psubmit: tsubmit 시점의 중간가
- P_fill: 실제 체결가
저는 t_submit = signal_time + 1초로 잡았어요. 실전에선 주문 API 응답 타임스탬프를 쓰세요.
2단계: Cd(지연비용) 계산
# 매수 방향일 때
Cd = (P_submit - P_decision) / P_decision * 100 # %
# 매도 방향일 때
Cd = (P_decision - P_submit) / P_decision * 100
부호에 주의하세요. 매수 신호인데 제출가가 올랐으면 손해니까 양수, 매도 신호인데 제출가가 내렸으면 손해니까 양수로 나와야 해요.
제 2024년 6월 로그에서 Cd 평균이 0.03%였는데, 특정 날(6월 12일)에만 0.21%로 튀었습니다. 그날 아침에 API 장애로 주문 전송이 3분 늦어졌거든요. 이런 이상치가 IS 분해 없이는 안 보여요.
3단계: Ce(명시비용) 계산
거래소 수수료율을 그대로 곱하면 됩니다.
Ce = fee_rate * fill_quantity * P_fill / capital * 100
저는 0.02% 메이커 수수료를 쓰는 거래소라 Ce는 항상 0.02% 근처였어요.
4단계: Ci(암묵비용) 계산
체결가와 제출 시점 중간가의 차이가 Ci예요.
# 매수
Ci = (P_fill - P_submit) / P_submit * 100
# 매도
Ci = (P_submit - P_fill) / P_submit * 100
제 경우 Ci가 0.06%였는데, 이게 전체 IS의 50%를 차지했습니다. 큰 수량을 한 번에 넣으면 호가를 밀어서 Ci가 커지니까, 주문을 5개로 쪼개서 TWAP처럼 분산했더니 Ci가 0.04%로 줄었어요.
5단계: Co(기회비용) 계산
목표 수량 중 미체결된 비율 × 이후 가격 움직임입니다.
unfilled_ratio = (target_qty - filled_qty) / target_qty
price_move = (P_market_close - P_submit) / P_submit # 매수 기준
Co = unfilled_ratio * price_move * 100
제 전략은 시장가 주문이라 미체결이 거의 없어서 Co는 0.01% 수준이었어요. 지정가를 쓰는 전략이라면 Co가 크게 나올 겁니다.
백테스트에 IS 측정을 어떻게 붙이나요?
기존 백테스트 코드에서 체결 로그를 기록하는 부분에 위 계산을 추가하면 됩니다.
class ISTracker:
def __init__(self):
self.records = []
def record_trade(self, signal_time, submit_time, fill_time,
P_decision, P_submit, P_fill,
direction, quantity, fee_rate):
# Cd
if direction == 'buy':
Cd = (P_submit - P_decision) / P_decision * 100
Ci = (P_fill - P_submit) / P_submit * 100
else:
Cd = (P_decision - P_submit) / P_decision * 100
Ci = (P_submit - P_fill) / P_submit * 100
# Ce
Ce = fee_rate * 100
# Co (미체결 없으면 0)
Co = 0
IS_total = Cd + Ce + Ci + Co
self.records.append({
'time': fill_time,
'Cd': Cd, 'Ce': Ce, 'Ci': Ci, 'Co': Co,
'IS': IS_total
})
백테스트 루프에서 매 거래마다 tracker.record_trade(...)를 호출하고, 마지막에 pd.DataFrame(tracker.records).describe()로 통계를 뽑으세요.
저는 이렇게 해서 100회 거래의 Cd·Ce·Ci·Co 분포를 봤고, Ci 분산이 가장 크다는 걸 발견했습니다. 그래서 주문 분할 로직을 추가했고, 샤프비율이 1.2에서 1.4로 올랐어요.
흔한 실수와 해결법
실수 1: t_decision을 체결봉 종가로 잡는 것
신호가 09:00봉 종가에 발생했다고 해서 P_decision을 종가로 쓰면 안 됩니다. 그 시점의 중간가(bid+ask 평균)를 써야 해요. 저는 처음에 이 실수로 Ci를 0.02%로 잘못 측정했다가, 중간가로 바꾸니 0.06%로 3배 뛰었어요.
실수 2: 방향 부호를 헷갈리는 것
매수와 매도에서 Cd·Ci 공식의 분자 부호가 반대예요. 둘 다 "손해는 양수"로 나와야 하는데, 공식 하나로 direction을 곱하면 음수가 나올 때가 있습니다. 제 코드에서 if문으로 분기한 이유죠.
실수 3: Co를 계산 안 하고 넘어가는 것
시장가 주문이면 Co가 0이니까 "안 써도 되겠지" 하고 빼먹기 쉬워요. 나중에 지정가로 바꿀 때 Co가 갑자기 튀면 과거 로그를 다시 돌려야 합니다. 처음부터 4개 다 측정하세요.
자주 묻는 질문
Q. IS가 양수면 손해, 음수면 이득인가요?
A. 네. IS는 "이상적인 가격 대비 실제로 더 낸 비용"이라 양수가 손해예요. 드물게 Cd나 Ci가 음수로 나올 수 있는데(주문 지연 중 가격이 유리하게 움직인 경우), 이건 운이고 재현 불가능하니 기대수익에 넣지 마세요.
Q. IS 측정이 백테스트 속도를 얼마나 느리게 하나요?
A. 제 경우 10만 봉 백테스트가 2.3초에서 2.5초로 늘었어요. 사칙연산이라 부담이 거의 없습니다.
Q. 실시간 트레이딩에서도 IS를 쓸 수 있나요?
A. 됩니다. 체결 직후 IS를 계산해서 Ci가 평소보다 2배 이상 나오면 "유동성이 나빠졌구나" 신호로 쓸 수 있어요. 저는 이걸 포지션 크기 조절에 반영합니다.
Q. 암호화폐 거래소에서도 적용되나요?
A. 네. 저는 바이낸스 선물 데이터로 IS를 측정했어요. 암호화폐는 슬리피지가 주식보다 크니까 Ci 비중이 70% 넘을 수 있지만, 프레임워크는 동일합니다.
Q. 4개 비용 중 어느 걸 먼저 줄여야 하나요?
A. 제일 큰 놈부터요. 제 경우 Ci가 50%라 주문 분할로 Ci를 먼저 줄였고, 다음으로 Cd를 줄이려고 주문 전송 지연을 1초 이내로 최적화했습니다. Ce는 거래소가 정한 거라 손댈 수 없어요.
마무리
지금 당장 백테스트 코드를 열어서 체결 로그에 IS 4개 컬럼을 추가해 보세요. 한 달치 거래를 돌려보면 어느 비용이 큰지 바로 보입니다. 그게 개선 우선순위예요.