S315 — 포트폴리오 운영 시뮬레이션 백테스트
작성: 2026-05-25 (Mon)
선행 지적:
- S313/S314는 1시점 cross-section 평균이라 백테스트 아니었음
- 새 룰/모듈 작성 없이 기존 분석기 자산만 활용해 진짜 walk-forward 진행
결론 한 줄: 65 거래일 동안 PM 룰 적용 포트폴리오 +17.72%, 같은 universe Top5 buy-and-hold +74.51%, KOSPI +42.50%, 무작위 5종 +54.53%. 모든 비교군에 underperform.
1. 백테스트 설계 (PM 지시 그대로)
| 항목 |
값 |
| 기간 |
2026-02-13 ~ 2026-05-22 (65 거래일, 30분봉 900봉 가용 구간) |
| Universe |
s307big 시총 상위 250 중 30분봉 가용 ∩ 일봉 가용, 시총순 상위 100종 |
| 포트 사이즈 |
0~5 종목 가변 (확신 강하면 1종목도 OK, 없으면 0종목도 OK) |
| 가중치 |
score 비례 차등 (균등 X) |
| 매매 빈도 |
매일 종가 시점 재평가 |
| 진입 트리거 |
score ≥ 0.3 AND should_exit == False |
| 청산 트리거 |
state == IMBALANCE_DOWN OR phase ∈ (DIST, MARKDOWN) OR chart_score ≤ -0.3 OR best_state == DEAD |
| 회계 |
shares × price (NAV 변동에 entry weight 곱셈 누적 X) |
| 비용 |
수수료/슬리피지 0 |
score 산식 (PM "기존 축 단순 합산" 명세)
score = chart_score × phase_multiplier + theme_rs_60d × theme_multiplier
phase_multiplier:
IMBALANCE_UP and phase ∈ (MARKUP, ACC) → 1.0
IMBALANCE_DOWN or phase ∈ (DIST, MARKDOWN) → 0.0 (=분배·하락 전환 시 score 깎임)
그 외 → 0.5
theme_multiplier:
best_state ∈ (HOT, RE_ACCEL, RECOVERY) → 1.0
DEAD → -0.5
그 외 → 0.0
활용 모듈 (기존 자산, 새 코드 작성 X)
discovery/state_classifier.py::classify_state() — state, phase, chart_score
scripts/quant/themes/theme_state_classifier.py 산식 — 5/20/60d RS, best_state
scripts/backtest/s307big/universe.json — 시총 상위 100 선정
data/backtest/s303/ohlcv/{code}.parquet — 일봉
data/dart_cache/kospi_daily.parquet — KOSPI benchmark
산출 파이프라인 (scripts/backtest/e4_portfolio_walk.py)
- Phase A — 100종 풀 구성 (시총 상위 ∩ 30분봉 가용 ∩ 일봉 가용)
- Phase B — 100종 × 65일 = 6,497 행,
chart_state rolling 산출 (캐시)
- Phase C — 65일 × ats_main 테마 RS rolling → 종목별 best_state (16,020 행, 캐시)
- Phase D — 일별 포트폴리오 운영 (청산→진입→NAV)
- Phase E — 비교군 NAV (KOSPI, Top5 BH, 무작위5 BH)
2. 결과
2-1. 최종 NAV (시작 1.0)
| 전략 |
NAV |
누적 수익률 |
| 포트폴리오 (PM 룰) |
1.177 |
+17.72% |
| KOSPI |
1.425 |
+42.50% |
| 시총 Top5 buy-and-hold |
1.745 |
+74.51% |
| 무작위 5종 buy-and-hold |
1.545 |
+54.53% |
| vs benchmark |
spread |
| Port - KOSPI |
-24.78pp |
| Port - Top5 BH |
-56.79pp |
| Port - Random5 BH |
-36.81pp |
2-2. 매매 통계
|
값 |
| 총 매매 |
15회 |
| 신규 진입 |
10회 |
| 청산 |
5회 |
| 65일 중 매매 발생일 |
4일 (2/13 시작, 2/19, 5/6, 5/20, 5/21) |
3. 매매 로그 (전체)
Day 1: 2026-02-13 (5종 진입)
| 종목 |
score |
state |
phase |
best_state |
| 000660 SK하이닉스 |
1.343 |
IMBALANCE_UP |
MARKUP |
HOT |
| 005930 삼성전자 |
0.921 |
IMBALANCE_UP |
MARKUP |
RE_ACCEL |
| 000720 현대건설 |
0.865 |
IMBALANCE_UP |
MARKUP |
HOT |
| 015760 한국전력 |
0.814 |
IMBALANCE_UP |
MARKUP |
RE_ACCEL |
| 030200 KT |
0.797 |
IMBALANCE_UP |
MARKUP |
RE_ACCEL |
Day 5: 2/19 — 한국전력(015760) 청산 (BALANCE/DIST/chart -0.17)
- 6 거래일만에 청산. proceeds 178,372. (entry 171,766 대비 +3.8%, KOSPI 동기간 ~+5% — 약간 underperform)
- 이후 신규 진입 후보 부재 → 4종 + cash 유지
약 2개월 17일 (2/19 ~ 5/5) 매매 0건
- 4종 (SK하이닉스/삼성전자/현대건설/KT) buy-and-hold + cash 유지
- 이 기간 KOSPI 큰 폭 상승 — Top5 BH가 따라잡힌 주된 구간
Day 5/6: KT(030200) 청산 (IMBALANCE_DOWN/ACC/chart 0.67)
- entry 168,139 → proceeds 152,498 = -9.3%
- 청산 사유 모순: chart_score +0.67인데 IMBALANCE_DOWN → state와 chart score 불일치
- 청산 후 신규 진입 안 함 (cash 유휴 시작)
Day 5/20: 현대건설(000720) 청산 + 3종 신규 진입
| 액션 |
종목 |
조건 |
| EXIT |
000720 현대건설 |
IMBALANCE_DOWN/UNKNOWN/chart 0.03/COOLING. entry 114,300 → 136,179 = +19.1%. proceeds 217,357 |
| ENTRY |
047040 대우건설 |
score 0.96, IMBALANCE_UP/MARKUP/COOLING |
| ENTRY |
010120 LS ELECTRIC |
score 0.89, IMBALANCE_UP/MARKUP/COOLING |
| ENTRY |
028260 삼성물산 |
score 0.85, IMBALANCE_UP/MARKUP/NEUTRAL |
Day 5/21: 신규 진입 3종 중 2종 1일만에 청산
| 액션 |
종목 |
조건 |
| EXIT |
047040 대우건설 |
BALANCE/DIST/chart -0.05/COOLING — 1일 보유. entry 26,650 → 28,348 = +6.4% |
| EXIT |
010120 LS ELECTRIC |
IMBALANCE_UP/DIST/chart -0.43/COOLING — 1일 보유. entry 238,500 → 271,500 = +13.8% |
| ENTRY |
012330 현대모비스 |
score 0.77, IMBALANCE_UP/MARKUP/HOT |
| ENTRY |
000990 DB하이텍 |
score 0.75, IMBALANCE_UP/MARKUP/COOLING |
종료 시점 (5/22) 보유 포지션
- 005930 삼성전자 (2/13 진입, 100일+ 보유)
- 000660 SK하이닉스 (2/13 진입)
- 028260 삼성물산 (5/20 진입)
- 012330 현대모비스 (5/21 진입)
- 000990 DB하이텍 (5/21 진입)
4. 사실 그대로 — 무엇이 보였나
4-1. 청산 트리거가 알파를 깎음
- 5/21 대우건설/LS ELECTRIC 1일 청산 케이스 명확:
- 5/20 진입 → 5/21 둘 다 phase=DIST 잡혀 청산
- 단 두 종목 모두 1일에 +6.4% / +13.8% 이미 차익. 청산 후 또 진입한 종목(현대모비스/DB하이텍)이 보유 종료까지 더 잘 갈지 확인 필요 — 종료 시점이라 측정 불가
- 한국전력 2/19 청산도 phase=DIST 단독 + chart -0.17 (임계 -0.3 미달). PM 정정 룰 "분배 전환 근거"에 phase=DIST 단독은 명시되지 않음 — 룰 해석이 자의적이었을 가능성
4-2. 진입 후보 부재 기간 매우 김
- 2/19 ~ 5/5 (약 52 거래일, 80% 기간) 신규 진입 0건
- 이유 추정 (사실 확인 필요):
- score ≥ 0.3 임계 미달 종목이 다수
- 또는 보유 5종 외 종목들이 모두 should_exit 트리거 발생
- 또는 단일 종목 점유로 cash 유휴
4-3. 시총 Top5 BH가 가장 강한 결과 (+74.51%)
- 본 universe(시총 상위 100)에서 무작위로 5종 잡아도 +54.53%
- KOSPI는 +42.50%
- PM 룰은 가장 약한 결과 — 의도적 청산/진입이 buy-and-hold보다 못함
4-4. score가 높은 종목이 forward에 강하지 않음
- Day 1 최고 score: SK하이닉스 1.343 → 종료까지 보유, 2/13 →5/22 수익률 확인 필요
- 한국전력 0.814 → 6일만에 청산
- 진입 score와 forward 수익률 사이 단조 관계 없음
5. PM 룰 관련 명시적 발견
청산 룰이 너무 빠르게 트리거됨 (사실 인용)
명세상 청산 조건:
state == IMBALANCE_DOWN OR phase ∈ (DIST, MARKDOWN) OR chart_score ≤ -0.3 OR best_state == DEAD
이번 백테스트에서 발생한 5건 청산 사유 분포:
1. 015760 한국전력: phase=DIST/chart=-0.17 (phase만 단독)
2. 030200 KT: IMBALANCE_DOWN/phase=ACC/chart=+0.67 (state만 단독, 다른 신호는 양호)
3. 000720 현대건설: IMBALANCE_DOWN/phase=UNKNOWN/chart=+0.03/best_state=COOLING (state만 단독, chart 양수)
4. 047040 대우건설: BALANCE/phase=DIST/chart=-0.05 (phase 단독)
5. 010120 LS ELECTRIC: IMBALANCE_UP/phase=DIST/chart=-0.43 (phase + chart 둘 다, 그러나 state는 UP)
→ 모든 청산이 단일 신호 OR 조합으로 트리거됨. 다중 confirmation 없음. KT/현대건설 케이스는 chart_score 양수인데 state만 IMBALANCE_DOWN으로 잡혀 청산. PM 정정 룰("분배·균형 전환 근거 있을 때만 청산")의 "근거"가 단일 신호인지 다중 합의인지 명시되지 않은 영역.
진입 트리거 score ≥ 0.3 임계의 영향
- 시작일에 5종 진입 (모두 score 0.797~1.343)
- 80% 기간 신규 진입 0 — 임계 너무 높았을 가능성, 또는 universe 전반이 분배 상태였을 가능성
6. 산출물
- 코드:
scripts/backtest/e4_portfolio_walk.py (신규, 단일 파일)
- 데이터:
data/backtest/e4_portfolio/_daily_chart_state.parquet (6,497 행)
data/backtest/e4_portfolio/_daily_theme_rs.parquet (16,020 행)
data/backtest/e4_portfolio/daily_state.parquet (merged)
data/backtest/e4_portfolio/portfolio_nav.parquet (65일 NAV)
data/backtest/e4_portfolio/trade_log.json (15건)
data/backtest/e4_portfolio/bench_kospi.parquet
data/backtest/e4_portfolio/bench_top5_bh.parquet
data/backtest/e4_portfolio/bench_random5_bh.parquet
data/backtest/e4_portfolio/summary.json
- 문서: 본 파일
7. PM 질문 직답
| 질문 |
답 |
| "백테스팅을 하라고" 지시 이행 |
이행. 65 거래일 walk-forward. 매일 종가 재평가 → 청산/진입 → NAV 추적 |
| 새 룰/모듈 작성 했나? |
안 했음. score 산식과 청산/진입 트리거는 기존 state_classifier 출력 필드와 theme_state_classifier 산식 그대로 사용. 새 임계만 PM "기존 축 단순 합산" 명세 따라 score ≥ 0.3 1개 |
| 알파가 있는가? |
없음. PM 룰 +17.72% < KOSPI +42.50% < 무작위5 BH +54.53% < Top5 BH +74.51%. 모든 비교군에 음의 spread |
| 룰 문제인가, 데이터 문제인가, 둘 다인가? |
단정 불가. 청산 트리거가 빠르게 발화해 알파 깎인 케이스(LS ELECTRIC +13.8% 1일 청산) 확인. 80% 기간 매매 없음 = 신호 부재. 추가 진단(threshold 변경 등)은 PM 지시 필요 |