
🔁 AI + 주식 자동매매 프로젝트 공유
3편. 백테스트 시스템 만들기 — 실전급 백테스트 엔진 설계와 구현
백테스트는 전략의 ‘이론적 성능’을 보여주는 시험대가 아니라, 실전으로 옮길 수 있는지 검증하는 엔진입니다.
좋은 백테스터는 다음을 허용해야 합니다: 현실적 체결 모델, 비용/슬리피지 적용, 포지션·마진·리밸런스 검증, 그리고 재현 가능한 실험 관리.
이번 글은 제가 실무에서 만든 백테스트 엔진의 설계·구현·운영 경험을 기반으로, 핵심 설계 패턴과 코드, 그리고 현장에서 부딪힌 문제와 해결책을 정리합니다.
1. 요구사항(실무 관점)
- 재현성: 동일한 데이터·파라미터로 같은 결과가 나와야 함. (버전 관리)
- 현실성: 슬리피지·수수료·최소수량·체결확률을 반영.
- 확장성: 개별 종목 → 멀티종목·포트폴리오로 확장 가능.
- 속도: 대량 백테스트(수백 종목, 수년치 분봉) 처리 가능.
- 검증성: 거래 로그(trade blotter), P&L 타임라인, 회귀테스트 지원.
- 실험관리: 하이퍼파라미터 스위핑 및 결과 비교(메타데이터 저장).
2. 전체 아키텍처 (권장)
[데이터 레이크]
├─ Raw (일/분/틱) Parquet
└─ Adjusted / Corporate Actions (배당/액면/분할)
[Backtest Engine]
├─ Loader (데이터 로딩 + 시간 동기화)
├─ Execution Simulator (체결 모델)
├─ Portfolio Engine (포지션/레버리지/마진)
└─ Risk & Metrics (MDD, Sharpe, Turnover)
[Orchestration]
├─ Job Queue (Celery / Prefect)
└─ UI/Reporting (Jupyter + Streamlit / Grafana)
[Storage]
├─ 결과: PostgreSQL / ClickHouse (trade logs)
└─ 아티팩트: S3 (파라미터, 리포트)
핵심은 데이터 정합성(특히 시계열 정렬, 시차 보정)과 체결 모델의 현실성입니다.
3. 백테스트 방식 비교 (실무에서 선택 기준)
- 벡터라이즈드(패싯) 백테스트
- 장점: 빠름, 대량 실험에 적합
- 단점: 복잡한 체결 모델 표현 어려움(슬리피지/부분체결 등)
- 이벤트 드리븐(시뮬레이션) 백테스트
- 장점: 현실적인 체결/주문 로직 적용 가능
- 단점: 느림, 구현이 복잡
- 하이브리드(핵심 연산은 벡터, 주문은 이벤트)
- 실무에서 가장 현실적이고 효율적
실무 추천: 하이브리드 아키텍처 — 일괄 벡터 연산으로 시그널 만들고, 이벤트 시뮬레이터로 주문/체결 처리.
4. 핵심 컴포넌트 & 코드 (간단한 이벤트 드리븐 샘플)
아래 코드는 교육 목적의 간단한 이벤트-드리븐 백테스터 골격입니다. 실제 운영 시 로그/예외·CSV→Parquet 로더·병렬 처리가 필요합니다.
# backtester.py (단순화된 예시)
import pandas as pd
from collections import deque
class Order:
def __init__(self, symbol, size, price=None, type='market'):
self.symbol = symbol
self.size = size
self.price = price
self.type = type
class Trade:
def __init__(self, symbol, size, price, timestamp, fee):
self.symbol = symbol
self.size = size
self.price = price
self.timestamp = timestamp
self.fee = fee
class ExecutionSimulator:
def __init__(self, slippage_bp=1.0, commission_per_share=0.01):
self.slippage_bp = slippage_bp
self.commission_per_share = commission_per_share
def execute(self, order, market_price, volume_available):
# 단순 모델: market order 전량 체결, 슬리피지는 BP(1bp = 0.01%)
slippage = market_price * (self.slippage_bp / 10000.0)
exec_price = market_price + slippage if order.size > 0 else market_price - slippage
traded_size = min(abs(order.size), volume_available)
fee = traded_size * self.commission_per_share
return Trade(order.symbol, traded_size * (1 if order.size>0 else -1), exec_price, pd.Timestamp.now(), fee)
class Portfolio:
def __init__(self, cash):
self.cash = cash
self.positions = {}
self.trades = []
def apply_trade(self, trade):
cost = trade.size * trade.price + trade.fee
self.cash -= cost
self.positions.setdefault(trade.symbol, 0)
self.positions[trade.symbol] += trade.size
self.trades.append(trade)
def run_backtest(price_df, signal_df, init_cash=1000000):
sim = ExecutionSimulator(slippage_bp=2.0, commission_per_share=0.005)
port = Portfolio(init_cash)
for timestamp, row in price_df.iterrows():
# 시그널이 있으면 주문 생성 (간단 예: signal_df contains target position)
if timestamp in signal_df.index:
target = signal_df.loc[timestamp] # dict symbol->target_shares
for sym, target_shares in target.items():
current = port.positions.get(sym, 0)
size = target_shares - current
if size == 0:
continue
order = Order(sym, size)
market_price = row[sym] # price_df columns: symbols
volume_available = 1e6 # 간단화: 충분한 유동성 가정
trade = sim.execute(order, market_price, volume_available)
port.apply_trade(trade)
return port
이 예시는 교육용입니다. 실제로는 체결 확률, 부분 체결, 호가 기반 시뮬레이션(Limit/IOC), 슬리피지 분포 모델 등 정교한 로직이 필요합니다.
5. 현실적 체결 모델 (중요)
- 고정 BP 슬리피지: 가격 × (bp/10000) — 간단하지만 상황별 차이를 반영하지 못함.
- 비율 기반 슬리피지: 주문 크기 / 평균 일 거래량(ADV)에 비례 — 대형주/소형주 차별화 가능.
- 호가 레벨 시뮬레이션: 실거래 호가(Depth)를 사용해 실제 체결 가격 산출 — 가장 현실적이나 데이터·연산비가 큼.
- 체결 확률 모델: 제한가·시장가의 체결확률을 통계적으로 모델링.
실무에서는 ADV 대비 주문비율 → 슬리피지와 체결확률을 산출하는 방식이 가장 현실성/성능 균형이 좋았습니다.
6. 비용(수수료)·세금·슬리피지 반영
- 수수료: per-share 또는 브로커 % 수수료. (한국은 거래세 고려)
- 세금: 장기/단기 차익 구분이나 거래세를 간단히 반영.
- 환율비용: 해외 주식은 환전 비용·스프레드 포함.
- 마켓 임팩트 비용: 주문 규모가 커질수록 비선형 증가 — 실전에서는 α * (size/ADV)^β 형태를 사용.
7. 실무에서 발생한 주요 문제와 해결책
- 데이터 문제 — 스플릿/배당 미조정
- 문제: 과거 가격이 부정확하면 신호·성과 왜곡
- 해결: corporate action(배당·분할·합병) 적용된 adjusted price 사용. Raw 가격은 반드시 보관.
- 생존자 편향(Survivorship Bias)
- 문제: 과거에 상장폐지된 종목들을 빼면 성능이 과대평가됨.
- 해결: 당시 상장 상태를 반영한 유효 종목 리스트를 사용.
- 룩어헤드(lookahead) 바이어스
- 문제: 당일 장중 공개되는 정보나 결산 공시를 백테스트 시점에 미리 사용하면 안 됨.
- 해결: 타임스탬프 엄격 관리 — 모든 외부 정보는 해당 시간 이전에만 접근.
- 슬리피지 과소평가
- 문제: 단순 고정 슬리피지로는 실제 슬리피지 재현 불가.
- 해결: 과거 주문실행 로그로 슬리피지 분포를 추정해 시뮬레이션에 반영.
- 리밸런스 타이밍 민감도
- 문제: 같은 전략이라도 리밸런스 시각(장초·장중·장마감)에 따라 결과가 크게 달라짐.
- 해결: 여러 시나리오(장초/정오/마감)로 민감도 분석 수행.
8. 성능·검증 지표 및 리포트
필수 메트릭:
- 누적수익률, 연환산수익률(CAGR)
- 샤프, Sortino
- MDD(Max Drawdown), Time under water
- 거래 빈도, 평균 보유기간, turnover
- 비용 포함/미포함 성과 비교
- 베타·알파(벤치마크 대비)
로그:
- Trade blotter(시간, 종목, 사이즈, 가격, fee, 슬리피지)
- Position timeline
- Cash timeline
보고서 자동화: 결과(파라미터 포함) → PDF/HTML 리포트 → S3 저장 → DB에 메타데이터 등록
9. 병렬화·스케일링(실무 팁)
- 종목 단위 분산: 각 종목 백테스트를 병렬로 돌리고, 결과를 합산해 포트폴리오 성과 재구성.
- 시간 분할: 기간을 분할해 여러 워커에서 병렬 실행 후 합치기.
- 데이터 캐시: 파케이(parquet)를 사용해 부분 로딩, 메모리 낭비 절감.
- ClickHouse / ClickStream: 트레이드 로그 집계·쿼리에 용이.
10. CI / 재현성
- 데이터 버전 tagging (예: data_v20251201)
- 코드 버전: Git 태그(백테스트 실행 시 Git SHA 기록)
- 환경: Docker 이미지와 requirements.txt 고정
- 테스트: 작은 시나리오(샌드박스) 자동 회귀테스트 설정
11. 실전 팁 3가지
✔ TIP 1 — 항상 비용 포함성 비교를 기본으로 하라
수수료·슬리피지·환전비용을 포함하지 않은 결과는 무의미합니다.
운영 전 반드시 “비용 포함” 결과로 전략 허용 기준을 세우세요.
✔ TIP 2 — 데이터 검증 스크립트를 자동화하라
결함 데이터(결측·중복·비정상 급등) 탐지 파이프라인을 만들어서, 백테스트 시작 전에 자동으로 차단하거나 경고를 띄우게 하세요.
작은 데이터 문제 하나가 전체 결과를 망칠 수 있습니다.
✔ TIP 3 — 민감도(Stress) 테스트를 규범화하라
리밸런스 시점, 슬리피지 상한, 체결확률 하한 등 여러 시나리오에서 성능이 유지되는지 테스트해야 실제 운영에서 생존 확률이 높아집니다.
12. 적용 사례(내 경험 요약)
- 퀀트 전략 A: 벡터화된 모멘텀 + 이벤트 드리븐 체결. 벡터만으로는 연 수익 20% → 이벤트 시뮬레이션 적용 후 운영 가능 레벨로 MDD 감소하지만 수익률 소폭 하락.
- 시장충격 시나리오: 2022년 대형 급락을 모사한 스트레스 테스트에서 포지션 크기 제한을 두지 않으면 파산 가능성 확인 → ADV 기반 주문제한 도입 후 실거래와 유사한 리스크 프로파일 재현.
13. 다음 편 예고
4편. ML + LLM 기반 시그널 생성 실험기 — 모델 학습·검증·운영 파이프라인
- 특징 엔지니어링(시계열·텍스트 융합)
- 샘플링·라벨링 실무 이슈 (label leakage 주의)
- 온라인 학습·모델 서빙·A/B 테스트 적용 방법
📌 추천 태그
#백테스트 #자동매매 #퀀트 #데이터엔지니어링 #포트폴리오
#슬리피지 #체결모델 #FinTech #Python #모델검증
'AI 기반 자동매매 & 금융데이터 분석' 카테고리의 다른 글
| 5편. 실시간 자동매매 시스템 구축 — FastAPI · vLLM · 전략 Executor 설계 (0) | 2025.12.05 |
|---|---|
| 4편. ML + LLM 기반 시그널 생성 실험기 — 모델 학습·검증·운영 파이프라인 (0) | 2025.12.05 |
| 2편. 감성 분석 기반 시그널 실험 — 뉴스·트위터·리포트로 투자심리 점수 만들기 (0) | 2025.12.05 |
| 1편. 국내/미국 주식 데이터 수집 자동화 실전 가이드 (0) | 2025.12.05 |
| TimeParents v1.2.0 — Windows Game Time Management Tool for Kids (0) | 2025.11.30 |