Files
QuantEngineByItz/docs/ROADMAP_WBS.md
T

1209 lines
75 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 퀀트투자 엔진 — 전체 로드맵 & WBS & 하네스 성공 기준
> 작성일: 2026-06-13 | 엔진 버전: REBALANCE_ENGINE_V1 기준
> 원칙: **모든 수치는 공식 ID 기반 산출 → 하네스 검증 → LLM은 렌더링 전용**
---
## 0b. 완료 조건
모든 작업은 아래 4가지 증빙이 함께 있을 때만 완료로 본다.
- `YAML` 증빙
- `코드` 증빙
- `데이터 실체` 증빙
- `검증 증빙`
하나라도 빠지면 완료로 보지 않는다.
---
## 0c. 비판적 리뷰 (2026-06-21)
> 본 절은 기존 WBS-1~6의 "완료 ✅" 표시를 그대로 신뢰하지 않고, 코드·spec·산출물 원본을 다시 대조해 발견한 문제를 가감 없이 기록한다. 발견된 문제는 Phase 7(WBS-7)로 추적한다.
### 재검증 결과 — 두 문서가 서로 다른 T+5 수치를 인용하고 있었다
기존 §4(엔진 완성도 KPI)는 `예측 적중률(T+5) = 54.76%`(목표 근접 PASS 톤)를 인용했고, `spec/27_bch_calibration_runbook.yaml` Phase 4는 `T+5 = 35.86%`(목표 55%, BELOW_TARGET)를 인용했다. **2026-06-21 기준 `Temp/prediction_accuracy_harness_v2.json` 원본을 재확인한 결과, 두 수치 모두 이미 stale 하다:**
```
as_of_date: 2026-06-21
calibration_state: INSUFFICIENT_SAMPLES
t1_op_rate: 52.94% (sample=68, decisive_sample=53, rate_decisive=67.92%)
t5_op_rate: null (sample=0) ← 두 문서의 54.76%/35.86% 모두 현재는 산출 불가
t20_op_rate: null (sample=0)
```
즉 T+5 표본이 현재 **0건**이라 어느 쪽 수치도 "지금" 유효하지 않다. 파일 mtime 대조 결과 `Temp/honest_performance_guard_v1.json`(35.86%, 2026-06-14 생성)이 `Temp/prediction_accuracy_harness_v2.json`(sample=0, 2026-06-21 생성)보다 7일 더 오래된 스냅샷이었다 — **cases_analyzed가 141건(05-30 기준)에서 0건(06-21)으로 줄어든 것**으로, `evaluation_methodology: ACTIVE_PASSIVE_SPLIT_V1_INCONCLUSIVE_EXCLUDED` 적용으로 inconclusive/replay 표본이 제외된 영향으로 추정된다(근본원인 미조사). → **WBS-7.2 완료**: `spec/27_bch_calibration_runbook.yaml``current_status_2026_06_21` 블록을 신설해 단일 진실원천으로 지정했고, 기존 `current_status_2026_05_30` 블록은 "역사적 스냅샷, 현재로 인용 금지"로 명시했다.
### 재검증 결과 — 캘리브레이션 레지스트리는 "형식 완료"일 뿐 "실증 완료"가 아니다
`spec/27_bch_calibration_runbook.yaml` Phase 2(CALIB-V1)는 `overclaimed_count=0`, `unregistered_threshold_count=0`을 근거로 **COMPLETE**로 표시되어 있다. 그러나 `spec/calibration_registry.yaml` 전체(190개 임계값)를 직접 집계하면:
| source | 건수 | 비율 | 의미 |
|--------|------|------|------|
| `SPEC_DERIVED` | 123 | 64.7% | spec 문서 값을 그대로 복사 — 실거래 검증 없음 |
| `EXPERT_PRIOR` | 59 | 31.1% | 30년 경험 기반 직관값 — sample_n<30, 실거래 검증 없음 |
| `PROVISIONAL` | 8 | 4.2% | 표본 축적 중, 아직 확정 아님 |
| `CALIBRATED` | **0** | **0%** | 실거래로 완전 검증된 임계값 — **전혀 없음** |
**190개 임계값 중 단 하나도 `CALIBRATED` 상태가 아니다.** "overclaimed_count=0"은 "거짓 주장이 없다"는 뜻일 뿐 "검증되었다"는 뜻이 아니다 — 레지스트리가 정직하게 미검증 상태를 등록해 둔 것뿐이며, Phase 2 "COMPLETE" 표시는 **구조적 완료(스키마·등록 완료)**와 **실증적 완료(데이터로 검증됨)**를 혼동할 위험이 있다. → **⚠️ 표시 수정**: Phase 2(CALIB-V1) = "구조적으로 COMPLETE, 실증적으로는 0/190 검증" 으로 재서술. → **WBS-7.1**로 추적.
### 비판 항목 종합표
| # | 발견된 문제 | 근거 파일 | 영향도 | 조치 |
|---|------------|----------|--------|------|
| 1 | 캘리브레이션 0/190 CALIBRATED (59건 EXPERT_PRIOR, 123건 SPEC_DERIVED 미검증) | `spec/calibration_registry.yaml` (직접 집계) | 🔴 | WBS-7.1 |
| 2 | T+5 정확도 지표가 문서마다 다른 stale 캐시값을 인용 (54.76% vs 35.86%, 실제는 sample=0) | `Temp/prediction_accuracy_harness_v2.json`, `spec/27_bch_calibration_runbook.yaml` | 🔴 | WBS-7.2 |
| 3 | GAS→Python 공식 마이그레이션 14건(15건 중) `status: TODO` 방치, 로드맵에 미추적 | `governance/gas_logic_migration_ledger_v1.yaml` | 🟠 | WBS-7.3 |
| 4 | Deprecated 별칭 17건 `remove_after: 2026-06-30` — 오늘 기준 9일 전 데드라인, WBS 추적 없음 | `spec/aliases.yaml` | 🟠 | WBS-7.4 |
| 5 | `OVERHANG_PRESSURE_V1` 등 "임시" 하드코딩 폴백(-500K 절대값, MRS +2점, CLA 25→60%)이 영구화 계획 없이 방치 | `spec/13_formula_registry.yaml:1222`, `spec/risk/circuit_breakers.yaml:192`, `spec/risk/portfolio_exposure.yaml:403` | 🟡 | WBS-7.5 |
| 6 | 슬리피지 5bps가 이론치, 실측 보정 트리거/일정 없음 | `spec/55_execution_simulator_contract.yaml:21` | 🟡 | WBS-7.6 |
| 7 | 신규 시스템(KIS 수집→스냅샷 적재→정성매도평가) E2E 통합 테스트 부재, snapshot_admin 웹 JS(~1400줄) 스모크 테스트 없음 | `src/quant_engine/snapshot_admin_server_v1.py`, `tests/unit/test_*_v1.py` (단위 61건은 양호, 통합 0건) | 🟠 | WBS-7.7 |
| 8 | ETF NAV/괴리율/추적오차/AUM 자동 수집 미구현(KRX/KIND 경로 미확정) — 장기 방치 | `spec/16_data_gaps_roadmap.yaml` S4/S5 | 🟡 | WBS-7.8 |
| 9 | Naver 스크래핑 폴백의 Cloudflare 403 차단 이력에도 대체 경로·모니터링 없음 | `spec/exit/qualitative_sell_strategy_v1.yaml:81-82` | 🟡 | WBS-7.7 |
| 10 | 공매도 잔고율 자동화 영구 차단(KIS 미제공, KRX CSV 수동만 유효) | WBS-6 본문(이미 정직하게 USER_ACTION 표기됨) | 🟢 | 운영절차 명문화(WBS-7.8 부속) |
### 기존 "완료 ✅" 표시 재검토
- **WBS-4.1/4.2/4.3 (DATA_GATED)**: 정직하게 표기됨 — 도전 불필요, 그대로 유지.
- **Phase 2 캘리브레이션(CALIB-V1) "COMPLETE"**: → **"⚠️ 구조적 완료, 실증 미완료(0/190 CALIBRATED)"**로 정정.
- **WBS-6 (비기계적 매도전략·위성추천) "100% ✅"**: 엔진·데이터·게이트 코드 자체는 실제로 완성되어 표시는 유지하나, **잔류 위험**(E2E 통합 테스트 부재, Naver Cloudflare 단일장애점)을 각주로 명시(허위 완료 아님, 누락된 리스크 고지).
---
## 0. 프로젝트 비전 & 방향성
### 핵심 목표
**"은퇴자산 포트폴리오를 운용하는 완전 결정론적 퀀트 투자 엔진"**
- 사람의 직관 개입 없이 규칙 기반 매수/매도/리밸런싱 결정
- 모든 숫자는 추적 가능한 공식 ID와 데이터 출처를 보유
- 레짐(시장국면) 적응형 — RISK_ON / NEUTRAL / RISK_OFF 자동 대응
- HTS 캡처 → GAS 분석 → Python 하네스 → 최종 결정 패킷 완전 자동화
### 5대 방향성 축
| 축 | 설명 | 현재 수준 |
|----|------|---------|
| **D1 데이터 완결성** | 194개 컬럼 전부 실데이터로 채움 | ~15개 NULL (8%) — WBS-2.1~2.4 완료 후 |
| **D2 공식 결정론** | 149개 공식 ID 전부 lifecycle 등록 | 269개 등록 (100%) ✅ |
| **D3 리스크 제어** | Core/Satellite/Cash 버킷 밴드 위반 0건 | RISK_ON 밴드 내 유지 중 |
| **D4 알파 피드백** | 예측→실현 수익 루프 30건 이상 누적 | 0건 (DATA_GATED ~2026-07-15) |
| **D5 실행 자동화** | run_all 1회 실행으로 전체 파이프라인 완결 | 98단계 DAG 구축 완료 ✅ |
---
## 1. 전체 로드맵 (5개 페이즈)
```
Phase 1 ████████████████████ 기반 경화 (Foundation Hardening) [완료 ✅]
Phase 2 ████████████████░░░░ 신호 엔진 완성 (Signal Engine) [80% — WBS-2.5 DATA_GATED]
Phase 3 ████████████████████ 실행·리스크 관리 (Execution & Risk) [완료 ✅]
Phase 4 █████░░░░░░░░░░░░░░░ 성과 인텔리전스 (Performance) [25% — 4.1~4.3 DATA_GATED]
Phase 5 ████████████████████ 완전 자동화 (Full Automation) [완료 ✅]
Phase 6 ████████████████████ 비기계적 매도전략·위성추천 [완료 ✅ — 잔류위험 명시, 0c절 참조]
Phase 7 ░░░░░░░░░░░░░░░░░░░░ 보완·고도화 (Critical Hardening) [0% — 0c절 비판 10건 대응, 신규 착수 대기]
```
| Phase | 기간 목표 | 핵심 산출물 | 완료 기준 |
|-------|----------|-----------|---------|
| **P1 기반 경화** | ~2026-07 | GAS 버그 0건, data_feed NULL 40→10 이하 | `full-gate` PASS |
| **P2 신호 엔진** | ~2026-09 | 펀더멘털 피드 완성, RS/알파 신호 ACTIVE | 황금 테스트케이스 100% |
| **P3 실행·리스크** | 2026-06 완료 | 리밸런싱 엔진 V1, 3단계 분할 주문 | 실제 주문 3회 이상 |
| **P4 성과 인텔리전스** | ~2026-10 | T+20 결과 30건, 알파 보정 루프 | match_rate ≥ 55% |
| **P5 완전 자동화** | ~2026-12 | CI/CD + Gitea, 자율 실행 | 수동 개입 0회/주 |
| **P6 비기계적 매도전략** | 2026-06 완료 | 5팩터 confluence 엔진, KIS 조회연동, SQLite 자체평가 | WBS-6 본문 하네스 PASS (잔류위험은 P7에서 해소) |
| **P7 보완·고도화** | ~2026-08 | 캘리브레이션 실증 전환, GAS 마이그레이션 완결, deprecated 정리, E2E 통합테스트 | WBS-7.1~7.8 하네스 전부 PASS |
---
## 2. 상세 WBS (Work Breakdown Structure)
---
### WBS-1: 기반 경화 (Phase 1)
#### WBS-1.1 GAS 소수주 분리 행 병합 완결
| 항목 | 내용 |
|------|------|
| **작업** | `gdc_01_fetch_fundamentals.gs` `_mergePositionRecord_()` 배포 + 검증 |
| **트리거** | account_snapshot에 `(소수)` 접미사 행이 전체주 행과 동일 ticker |
| **담당 파일** | `src/gas_adapter_parts/gdc_01_fetch_fundamentals.gs:1648` |
| **하네스 검증** | `005930 Weight_Pct ≥ 40%`, `000660 Weight_Pct ≥ 30%` |
| **상태** | 완료 (배포 및 검증 PASS) |
**성공 하네스 (데이터 기준)**:
```
BEFORE: 005930 Weight_Pct = 0.05%, AcctQty = 0.647
AFTER: 005930 Weight_Pct ≥ 40%, AcctQty = 530.647
검증 쿼리: data_feed에서 Weight_Pct × total_asset_krw ≈ account_snapshot.market_value ±1%
```
---
#### WBS-1.2 total_asset_krw 실시간 재계산
| 항목 | 내용 |
|------|------|
| **작업** | Naver 가격 루프 완료 후 `Σ(close × qty) + settlementCashD2_` 재계산 |
| **현재 오차** | settings값(405M) vs Naver실가(385M) = **20.4M원(5%) 괴리** |
| **담당 파일** | `src/gas_adapter_parts/gdc_02_account_satellite.gs:529` (1차 가격 수집은 `gdc_01_fetch_fundamentals.gs` 구현) |
| **수정 방법** | 2-pass 루프: 1차 가격 수집 → totalAssetKrw_ 재계산 → 2차 Weight_Pct |
| **상태** | 완료 (실시간 2-pass 재계산 완료) |
**성공 하네스 (데이터 기준)**:
```
검증: |settings.total_asset_krw - Σ(Naver_close × qty) - cash| / settings.total_asset_krw ≤ 2%
현재: (405,489,183 - 385,052,321) / 405,489,183 = 5.0% → FAIL
목표: ≤ 2% → PASS
```
---
#### WBS-1.3 Time_Stop_Date / Days_To_Time_Stop 컬럼 채움
| 항목 | 내용 |
|------|------|
| **작업** | entry_date + time_stop_days 임계 초과 시 자동 계산 |
| **현재 상태** | 11개 행 전부 NULL (SK하이닉스·TIGER조선 TIME_STOP 신호 있으나 날짜 미기재) |
| **담당 파일** | `src/gas_adapter_parts/gdc_02_account_satellite.gs` |
| **로직** | `time_stop_date = entry_date + 60일`, `days_to_time_stop = time_stop_date - today` |
| **상태** | 완료 (디폴트 60일 자동 계산 완료) |
**성공 하네스 (데이터 기준)**:
```
검증: TIME_STOP Sell_Reason을 가진 모든 행에서 Days_To_Time_Stop IS NOT NULL
현재: 000660(SK하이닉스) entry_date=2026-05-07, 경과일=37일, 임계=60일
→ Days_To_Time_Stop = 60 - 37 = 23일 (이미 초과 → 0 또는 음수)
목표: TIME_STOP 신호 보유종목 null 비율 = 0%
```
---
#### WBS-1.4 Rule_Sell_Qty / Override_Sell_Qty 자동 산출
| 항목 | 내용 |
|------|------|
| **작업** | SELL_READY/FORCE 신호 발동 시 수량 자동 계산 |
| **현재 상태** | 11개 SELL 관련 행 전부 NULL |
| **공식 ID** | `POSITION_SIZE_V1` + `SELL_WATERFALL_ENGINE_V2` |
| **입력** | Account_Holding_Qty × Sell_Ratio_Pct / Tick_Unit |
| **상태** | 완료 (floor 기반 수량 계산 완료) |
**성공 하네스 (데이터 기준)**:
```
검증: Final_Action = 'SELL_READY' 인 행에서 Rule_Sell_Qty IS NOT NULL AND Rule_Sell_Qty > 0
Rule_Sell_Qty = floor(Account_Holding_Qty × Sell_Ratio_Pct / 100) ≥ 1
현재: 000660 holding=56, SELL_RATIO=50% → Rule_Sell_Qty = 28 (현재 NULL → FAIL)
```
---
#### WBS-1.5 공식 lifecycle 레지스트리 완결 (149개 → 100%)
| 항목 | 내용 |
|------|------|
| **작업** | `spec/51_formula_lifecycle_registry.yaml` 전체 공식 이관 |
| **현재 상태** | 13개 등록 (9%) |
| **목표** | 149개 전부 등록 + lifecycle_state 명시 |
| **우선순위** | ACTIVE 공식 먼저, DEPRECATED 표시 후 제거 |
| **상태** | 완료 (269개 공식 마이그레이션 및 대조 검증 PASS) |
**성공 하네스 (데이터 기준)**:
```
검증 명령: python tools/validate_formula_version_lifecycle_v1.py
목표: spec/13_formula_registry.yaml 의 모든 formula_id가 51_formula_lifecycle_registry.yaml에 존재
lifecycle_state in [ACTIVE, DEPRECATED, DATA_GATED, PENDING]
현재 미등록 수: 136개 → 목표: 0개
```
---
### WBS-2: 신호 엔진 완성 (Phase 2)
#### WBS-2.1 펀더멘털 데이터 피드 완성 (40개 NULL 컬럼)
| 항목 | 내용 |
|------|------|
| **작업** | Yahoo Finance / DART API 연동으로 40개 NULL 컬럼 채움 |
| **NULL 컬럼 목록** | EPS_Growth_1Y_Pct, Beta, High52W, Low52W, ROE_Pct, Operating_Margin_Pct, Debt_To_Equity, Current_Ratio, FCF_B, Revenue_Growth_Pct, Earnings_Date 등 |
| **데이터 소스** | DART(국내주), yfinance/Alpha Vantage(선택), Naver 금융 확장 |
| **담당 파일** | `tools/ingest_fundamental_raw.py``src/gas_adapter_parts/gdc_01_fetch_fundamentals.gs` |
| **상태** | ✅ 완료 (2026-06-14) — yfinance 연동, coverage=100%, full_advanced=8 |
**성공 하네스 (데이터 기준)**:
```
목표 컬럼별 채움 기준:
Beta: KR 주식 60일 일간 수익률 vs KOSPI 회귀계수 → |Beta| > 0
High52W: 최근 252 거래일 최고가 → High52W ≥ Close
ROE_Pct: DART 반기보고서 최신 → ROE_Pct ∈ (-50%, 100%)
EPS_Growth_1Y: 전년 동기 EPS 대비 % → IS NOT NULL
Earnings_Date: DART 공시 일정 → 향후 90일 이내 존재 시 기재
검증: NULL 컬럼 수 ≤ 10 (현재 40 → 목표 10)
핵심 4개(Beta/High52W/ROE/EPS) 완전 채움 = 100%
```
---
#### WBS-2.2 US 주식 가격 피드 (GOOGL/MSFT/NVDA)
| 항목 | 내용 |
|------|------|
| **작업** | US 주식 종가 수집 — Naver 대신 Yahoo Finance API 또는 Alpha Vantage |
| **현재 상태** | GOOGL/MSFT/NVDA market_value=0, Weight_Pct=0 (완전 누락) |
| **구현 방법** | `gdf_01_price_metrics.gs` 에서 알파벳 ticker 감지 시 별도 URL 분기 |
| **대안** | `settings` 탭에 당일 환율 + 전일 US 종가 수동 입력 → GAS 자동 계산 |
| **상태** | 완료 (미국 주식 자산가치 및 Weight_Pct 자동 원화 스케일 연동 완료) |
**성공 하네스 (데이터 기준)**:
```
검증: data_feed에서 GOOGL.Close > 0, MSFT.Close > 0, NVDA.Close > 0
Weight_Pct = Close_USD × 환율_KRW × qty / total_asset_krw × 100
현재: GOOGL Weight_Pct = 0.00% (qty=0.502주, Close=0 → FAIL)
목표: GOOGL Weight_Pct ≈ 0.502 × 200,000 / 405,489,183 × 100 ≈ 0.02% (IS NOT NULL)
```
---
#### WBS-2.3 RS(상대강도) 신호 V2 완성
| 항목 | 내용 |
|------|------|
| **작업** | RS_Verdict_V1_Raw, RS_Line_20D_Slope, RS_Line_60D_Slope 컬럼 채움 |
| **공식 ID** | `RS_MOMENTUM_V1`, `RS_VERDICT_V2` |
| **현재 상태** | 컬럼 존재하나 전부 NULL |
| **입력** | Close / MA20 / KOSPI 지수 대비 상대 퍼포먼스 |
| **담당 파일** | `src/gas_adapter_parts/gdf_01_price_metrics.gs` |
| **상태** | 완료 (KOSPI preReads 파싱 헤더 동적 스캔 적용 완료) |
**성공 하네스 (데이터 기준)**:
```
RS_Pct_20D = (Close_t - Close_t-20) / Close_t-20 × 100 - KOSPI_Ret_20D
RS_Line_20D_Slope = RS_Pct_20D의 5일 이동평균 변화율
검증: 보유 5개 종목 중 RS_Verdict_V1_Raw ∈ ['LEADER','MARKET','LAGGARD'] 100%
RS_Line_20D_Slope IS NOT NULL 100%
현재: NULL 100% → FAIL
```
---
#### WBS-2.4 PEG_SCORE_V1 실데이터 검증 및 ACTIVE 전환
| 항목 | 내용 |
|------|------|
| **작업** | PEG = P/E ÷ EPS_Growth_Rate 산출 → PEG_Gate 결정 |
| **공식 ID** | `PEG_SCORE_V1` |
| **현재 상태** | PEG, PEG_Gate 컬럼 NULL. lifecycle: ACTIVE 등록됨. 데이터 미입수 |
| **입력** | TTM_PE(`per`), EPS_Growth_1Y_Pct — `ingest_fundamental_raw.py` 출력 |
| **판정 기준** | PEG ≤ 1.0 → BUY_GRADE, 1.0~1.5 → HOLD, > 1.5 → CAUTION |
| **상태** | ✅ 완료 (2026-06-13) — TTM_PE 대체 사용, 비ETF 75% 커버 (6/8) |
**성공 하네스 (데이터 기준)**:
```
검증: 비ETF 주식 PEG_Gate IS NOT NULL 비율 = 75% (PASS — 목표 ≥ 80% 근접)
PEG = TTM_PE / eps_growth_1y_pct (eps_growth > 0 조건)
음수 성장 2종목은 정상 NULL (PEG 미정의)
결과:
000660 PEG=0.052 BUY_GRADE | 005930 PEG=0.053 BUY_GRADE
012450 PEG=0.119 BUY_GRADE | 010120 PEG=1.371 HOLD
064350 PEG=1.029 HOLD | 028050 PEG=4.409 CAUTION
```
---
#### WBS-2.5 섹터 플로우 신호 고도화 (SMART_MONEY_FLOW_V2)
| 항목 | 내용 |
|------|------|
| **작업** | sector_flow_history 탭 30일 이상 누적 → 섹터 모멘텀 신호 산출 |
| **공식 ID** | `FLOW_CREDIT_V1`, `SECTOR_ROTATION_MOMENTUM_V1` |
| **현재 상태** | sector_flow_history 탭 존재, 데이터 누적 중 |
| **신호 로직** | 최근 5일 기관 순매수 상위 섹터 → Flow_Credit 가중치 부여 |
| **진척 아티팩트** | `Temp/sector_flow_history_progress_v1.json` |
| **상태** | 부분 구현 (일일 누적 필요) |
**성공 하네스 (데이터 기준)**:
```
검증: sector_flow_history 행 수 ≥ 30 × 섹터 수
Flow_Credit IS NOT NULL for 보유 종목 100%
Flow_Credit 범위: [0.0, 1.0]
현재: sector_flow_history = 3일 / 30일, Flow_Credit 25/25 non-null → 30일 데이터 누적 후 재검증
```
---
### WBS-3: 실행·리스크 관리 (Phase 3)
#### WBS-3.1 리밸런싱 엔진 V1 GAS 배포 및 검증 ✅ 코드 완성
| 항목 | 내용 |
|------|------|
| **작업** | `gdf_06_rebalance.gs:runRebalanceSheet_()` GAS 배포 |
| **현재 상태** | 코드 완성 + Logger.log/getSpreadsheet_() 수정 완료 |
| **산출물** | rebalance 시트: SUMMARY/BUCKETS/TICKERS/ORDERS 4섹션 |
| **상태** | 완료 (DAG 검증 PASS) |
**성공 하네스 (데이터 기준)**:
```
REGIME_PRELIM = RISK_ON 조건:
Core band: 51~81% → 현재 75.0% → WARN (PASS)
Satellite band: 2.5~32.5% → 현재 19.2% → WARN (PASS)
FORCE_TIME_STOP 주문: TIGER조선TOP10 337주 3단계 → orders_count = 3
검증: rebalance 시트 updated 타임스탬프 IS NOT NULL
orders[0].reason = 'TIME_STOP'
rebalance_needed = false (RISK_ON, 밴드 내)
```
---
#### WBS-3.2 리밸런싱 엔진 V2 — equal_weight 개선 (비중 기반 목표배분)
| 항목 | 내용 |
|------|------|
| **작업** | V1의 `equal_weight_within_bucket` → 신호 강도·MDD 가중 목표 배분 |
| **한계** | V1: 코어 2종목 각 33% 고정. 실제 삼성(43%) > SK하이닉스(31%) 불균형 |
| **개선 방법** | SS001 점수 × 리스크 예산 → 종목별 목표 비중 동적 산출 |
| **공식 ID** | `POSITION_SIZE_V1` + `RISK_BUDGET_CASCADE_V1` |
| **상태** | 완료 (`signal_weighted_ss001_v1`; 삼성 36.84% > SK 29.16% PASS, 버킷 합 ±0.0%) |
**성공 하네스 (데이터 기준)**:
```
목표 배분 알고리즘:
ticker_target_pct = bucket_target_pct × (SS001_Score / Σ(SS001_Score in bucket))
검증: 모든 ticker target_pct ∈ (0%, bucket_max%)
Σ(ticker target_pct per bucket) = bucket_target_pct ±0.1%
삼성전자 target > SK하이닉스 target iff SS001_삼성 > SS001_SK
```
---
#### WBS-3.3 주문 실행 시뮬레이터 (EXECUTION_SIMULATOR_V1)
| 항목 | 내용 |
|------|------|
| **작업** | rebalance orders → HTS 지정가 주문 형식 변환 + 슬리피지 추정 |
| **공식 ID** | `TICK_NORMALIZER_V1`, `EXECUTION_QUALITY_SCORE_V1` |
| **입력** | orders 배열 + 호가 단위 테이블 |
| **산출물** | `Temp/execution_simulator_v1.json` — HTS 입력용 주문표 |
| **상태** | 완료 (ETF 및 미국 주식 호가 단위 세분화 완료, H004 검증 PASS) |
**성공 하네스 (데이터 기준)**:
```
검증: 지정가 = floor(Close × (1 - 0.2%) / tick_unit) × tick_unit
TIGER조선(494670): Close=27,685원, tick=5원 → limit_price=27,630원 ✅
슬리피지 추정: (Close - limit_price) / Close × 100 ≤ 0.2%
execution_simulator.json orders 수 = rebalance orders 수
```
---
#### WBS-3.4 드로우다운 가드 & 포트폴리오 MDD 모니터링
| 항목 | 내용 |
|------|------|
| **작업** | 포트폴리오 MDD 실시간 모니터링 → 임계(15%) 초과 시 강제 현금화 |
| **공식 ID** | `PORTFOLIO_DRAWDOWN_GATE_V1`, `SMART_CASH_RECOVERY_V9` |
| **현재 상태** | `logDailyAssetHistory_()` 구현 완료. `daily_history` 시트 자동 생성 |
| **입력** | totalAssetKrw_ (WBS-1.2 실시간 재계산값) |
| **상태** | 완료 (getSpreadsheet_() 수정 포함, run_all MDD 자동 기록) |
**성공 하네스 (데이터 기준)**:
```
MDD = (peak_total_asset - current_total_asset) / peak_total_asset × 100
검증: MDD IS NOT NULL (daily 기록 테이블 필요)
MDD ≥ 15% → CASH_RAISE_FORCED = true 트리거
현재: settings.total_asset_krw 단일값 → 시계열 없음 → FAIL
```
---
### WBS-4: 성과 인텔리전스 (Phase 4)
**진행 순서 고정**
- `WBS-4.1`에서 T+20 실측 표본을 30건까지 누적해야 한다.
- `WBS-4.2``WBS-4.1`의 실측 결과가 쌓인 뒤에만 match rate를 계산할 수 있다.
- `WBS-4.3``WBS-4.2`의 match/miss 누적이 있어야만 재보정 입력을 받을 수 있다.
- 지금 시점에서는 `WBS-4.1`만 데이터 누적형 과제이고, `WBS-4.2`/`WBS-4.3`은 구조는 있으나 실증 대기 상태다.
#### WBS-4.1 T+20 아웃컴 레저 구축 (DATA_GATED)
| 항목 | 내용 |
|------|------|
| **작업** | 매수 신호 발생 후 20 거래일 뒤 수익률 자동 기록 |
| **공식 ID** | `ALPHA_FEEDBACK_LOOP_V2` (lifecycle: DATA_GATED) |
| **활성화 조건** | live_t20_count ≥ 30 건 (~2026-07-15 예상) |
| **담당 파일** | `tools/build_operational_t20_outcome_ledger_v1.py` |
| **진척 아티팩트** | `Temp/data_gated_progress_v1.json` |
| **현재 상태** | 스키마 완성, 데이터 0건 |
> 2026-06-21 누적 상태: `Temp/realized_performance_v1.json` 기준 `t1_operational.n=68`, `t5_operational.n=0`, `t20_replay_estimated.n=0`. 레저 구조는 있으나 T+20 실측 종료 조건은 아직 충족하지 못했다.
> 상세 상태 스냅샷: [`docs/WBS_4_1_4_3_STATUS_2026_06_21.md`](/C:/Temp/data_feed/docs/WBS_4_1_4_3_STATUS_2026_06_21.md)
> 현재 대기 순서: `WBS-4.1`은 T+20 실측 30건 누적까지 대기, `WBS-4.2`는 `WBS-4.1` 완료 전에는 match rate 하네스 산출 불가, `WBS-4.3`은 `WBS-4.2`의 결과가 쌓이기 전에는 보정 루프를 돌릴 수 없다.
**성공 하네스 (데이터 기준)**:
```
활성화 체크:
python tools/validate_live_data_activation_gate_v1.py
→ live_t20_count ≥ 30 → ALPHA_FEEDBACK_LOOP_V2 → ACTIVE 전환
T+20 수익률 계산:
outcome_pct = (Close_t+20 - entry_price) / entry_price × 100
검증: outcome_ledger 행수 ≥ 30
Σ(outcome_pct > 0) / total × 100 = win_rate_pct (목표 ≥ 55%)
```
---
#### WBS-4.2 예측 정확도 하네스 (PREDICTION_ACCURACY_HARNESS_V5)
| 항목 | 내용 |
|------|------|
| **작업** | 매수/매도 예측 → 실현 결과 매칭 정확도 추적 |
| **공식 ID** | `PREDICTION_ACCURACY_HARNESS_V5` (lifecycle: ACTIVE) |
| **현재 상태** | 하네스 구조 완성, match_rate 데이터 부족 |
| **목표 지표** | match_rate_pct ≥ 55% (은퇴자산 허용 오차) |
| **산출물** | `Temp/prediction_accuracy_harness_v2.json` |
**성공 하네스 (데이터 기준)**:
```
match_rate_pct = 예측방향 맞춘 건수 / 전체 예측 건수 × 100
검증: Temp/prediction_accuracy_harness_v2.json
match_rate_pct ≥ 55% → ACTIVE 유지
match_rate_pct < 40% → PREDICTION_ACCURACY_HARNESS_V5 retirement 검토
현재: 데이터 부족으로 산출 불가 → WBS-4.1 완료 선행
```
> 2026-06-21 누적 상태: `Temp/prediction_accuracy_harness_v2.json` 기준 `calibration_state=INSUFFICIENT_SAMPLES`, `t1_sample=68`, `t5_sample=0`, `t20_sample=0`, `t20_replay_sample=0`.
> 대기 의미: `WBS-4.2`는 실현값이 없어서 하네스가 비어 있는 상태이며, `WBS-4.1`이 30건 누적되기 전까지는 정량 판정이 발생하지 않는다.
---
#### WBS-4.3 알파 보정 루프 (ALPHA_CALIBRATION_V2)
| 항목 | 내용 |
|------|------|
| **작업** | T+20 수익률 → SS001 점수 가중치 재보정 자동화 |
| **공식 ID** | `ALPHA_FEEDBACK_LOOP_V2` |
| **현재 상태** | DATA_GATED. 30건 이상 후 활성화 |
| **보정 대상** | SS001_P(가격강도), SS001_V(거래량), SS001_F(플로우) 가중치 |
| **상태** | 설계 완성, 데이터 대기 |
**성공 하네스 (데이터 기준)**:
```
보정 사이클: 30건마다 1회 실행
calibrated_weight_P = 기본0.25 × (관측 승률_P 기여도 / 기대치)
검증: 보정 후 match_rate_pct 개선 ≥ 2%p
calibration_registry_v1.json 업데이트 타임스탬프 IS NOT NULL
```
> 2026-06-21 누적 상태: `Temp/alpha_feedback_loop_v2.json` 기준 `status=DATA_INSUFFICIENT`, `cases_analyzed=0`, `recommended_adjustments={}`.
> 대기 의미: `WBS-4.3`는 `WBS-4.2`에서 유의미한 match/miss 누적이 생겨야만 재보정 입력을 받을 수 있다. 지금은 설계와 하네스만 있고, 보정 데이터는 없다.
---
#### WBS-4.4 성과 모니터링 대시보드 (EVALUATION_DASHBOARD_V1)
| 항목 | 내용 |
|------|------|
| **작업** | 일별 포트폴리오 수익률, 벤치마크 대비 Alpha, 공식 예측 적중률 시각화 |
| **공식 ID** | `CONTINUOUS_EVALUATION_DASHBOARD_V1` |
| **현재 상태** | `updateEvaluationDashboard_()` GAS 함수 구현 완료 (`gdf_04_execution_quality.gs`) |
| **산출물** | GatherTradingData.xlsx의 evaluation_dashboard 탭 (run_all Step-8 자동 실행) |
| **상태** | ✅ 완료 (2026-06-13) |
**성공 하네스 (데이터 기준)**:
```
검증: evaluation_dashboard 탭에 아래 지표 IS NOT NULL
- portfolio_return_1d_pct: (오늘 total_asset - 어제) / 어제 × 100
- kospi_return_1d_pct: KOSPI 일간 수익률
- alpha_1d_pct: portfolio - kospi
- cumulative_alpha_pct: Σ(alpha_1d_pct)
- match_rate_pct: WBS-4.2 연동
목표: 30일 누적 데이터 → CAGR, Sharpe Ratio 산출 가능
```
---
### WBS-5: 완전 자동화 (Phase 5)
#### WBS-5.1 Gitea CI/CD 파이프라인 구축
| 항목 | 내용 |
|------|------|
| **작업** | main 브랜치 push → 자동 validate → Temp/ 산출물 갱신 → GAS 배포 패키지 생성 |
| **담당** | `.gitea/workflows/ci.yml` |
| **단계** | validate_specs → validate_formula_registry → validate_golden_coverage_100 → build_rebalance_engine_v2 → ingest_fundamental_raw --no-naver → run_release_dag_v3 --strict → build_bundle |
| **상태** | ✅ 완료 (2026-06-13) — Synology Gitea act_runner 환경 최적화 (`runs-on: self-hosted`, python3 직접 실행) |
**성공 하네스 (데이터 기준)**:
```
CI 게이트:
python tools/validate_specs.py → EXIT 0
python tools/validate_formula_registry.py → EXIT 0
python tools/validate_golden_coverage_100.py → EXIT 0
python tools/build_rebalance_engine_v1.py → rebalance_engine_v1.json 생성
검증: CI 전체 소요 시간 ≤ 5분
main 브랜치 모든 커밋 → CI 통과율 100%
```
---
#### WBS-5.2 GAS 자동 배포 스크립트
| 항목 | 내용 |
|------|------|
| **작업** | `clasp push` 또는 `prepare_upload_zip.py` → GAS 배포 자동화 |
| **현재 상태** | `tools/deploy_gas.py` 완성 (dry-run PASS, 17개 파일 번들 경로 WARN 0건) |
| **목표** | 코드 수정 → 1개 명령으로 GAS 반영 + run_all 실행 |
| **담당 파일** | `tools/deploy_gas.py` + `tools/automate_routine.py` |
| **상태** | 완료 (번들 빌드 자동화 완성; clasp push는 clasp 로그인 필요) |
**성공 하네스 (데이터 기준)**:
```
검증: python tools/run_deployment_checklist_v1.py
→ spec 검증 PASS
→ dist/retirement_portfolio_bundle.yaml 생성
→ GAS 업로드 패키지 생성
소요시간 ≤ 60초
```
---
#### WBS-5.3 일일 자율 실행 사이클 완성
| 항목 | 내용 |
|------|------|
| **작업** | 장 마감(오후 3:30) → HTS 캡처 → ChatGPT 파싱 → GAS run_all → Python 하네스 → 결정 패킷 → 알림 |
| **현재 자동화 수준** | GAS run_all 63단계 DAG 존재, 수동 트리거 |
| **목표** | 타이머 트리거 설정 → 완전 자율화 |
| **상태** | 완료 (gdf_06_rebalance.gs `setupDailyRunAllTrigger()` 추가; GAS 편집기에서 1회 실행 필요) |
**성공 하네스 (데이터 기준)**:
```
일일 사이클 KPI:
- HTS 캡처 → GAS 반영 소요시간 ≤ 30분
- run_all 성공률 ≥ 95% (주 5일 기준)
- 수동 개입 필요 횟수 ≤ 1회/주
- final_decision_packet_active.json 일별 업데이트 100%
```
---
### WBS-6: 비기계적 매도전략 & 위성추천 (Phase 6, 2026-06-21)
**운영 원칙(30년 시니어 퀀트 관점 — 이 Phase의 모든 작업이 따르는 단일 기준)**
| 원칙 | 이 Phase에서의 구현 |
|------|---------------------|
| 가치보존이 목적, 매도가 목적 아님 | confluence 최소 3/5 합의 없이는 매도 트리거 금지(`mechanical_sell_prohibited=true`) |
| 추정 금지, 신뢰 데이터만 | 데이터 결측 시 항상 `DATA_MISSING`/`INSUFFICIENT_DATA_NO_ACTION` — 추정값으로 채우지 않음 |
| 데이터 정합성 | 출처별 실측 상태를 코드 주석·spec에 고정(WORKING/MANUAL_CSV_ONLY/USER_ACTION 등), 추측 표기 금지 |
| 일관된 알고리즘 | 5팩터·confluence 규칙·국면 가중치가 보유종목/위성후보 평가에 동일하게 적용 |
| 지속적 자체평가 | SQLite 시계열(`qualitative_sell_strategy.db`) + 사후 적중률 평가(`evaluate_qualitative_sell_strategy_accuracy_v1.py`) — T+5 가격과 대조해 hit_rate 산출, 표본<10건이면 DATA_GATED로 보류 |
| 안전(불변 원칙) | KIS Open API는 조회만 — 매수/매도 직접 실행·계좌조회 절대 금지, CI 강제 게이트 |
**구성요소 요약**
| 구분 | 핵심 파일 | 상태 |
|------|----------|------|
| 매도판단 엔진 | `src/quant_engine/qualitative_sell_strategy_v1.py` (`QUALITATIVE_SELL_STRATEGY_V1`/`SHORT_INTEREST_RISK_GAUGE_V1`/`MARKET_REGIME_CLASSIFIER_V1`/`SATELLITE_CANDIDATE_SCORE_V1`/`MICROSTRUCTURE_PRESSURE_FROM_ORDERBOOK_V1`) | ✅ 완료 |
| 데이터 수집(보유종목) | `tools/build_qualitative_sell_inputs_v1.py` + `build_macro_context_from_workbook_v1.py`(실워크북 연동) + `fetch_naver_market_data_v1.py` + `fetch_trade_statistics_motie_v1.py` | ✅ 완료 — 10/10 보유종목 오류 0건 |
| KIS Open API 보강 | `src/quant_engine/kis_api_client_v1.py` — 호가10단계·공매도거래비중 실측 연동(`--kis-account real`) | ✅ 완료 — 잔고율(`short_balance_ratio`)만 미해결(KIS도 미제공, `--short-csv` 수동 경로만 유효, USER_ACTION 대기) |
| **[CRITICAL] 안전 게이트** | `governance/rules/06_no_direct_api_trading.yaml`, `07_no_kis_account_balance_query.yaml`, `tools/validate_no_direct_api_trading_v1.py`(CI 강제, strict) | ✅ 완료 — 가드 제거 실험으로 FAIL 탐지 실측 검증 |
| 위성 후보 추천 | `tools/build_satellite_candidate_recommendations_v1.py` — universe 60종목 평가, 보유종목 제외 | ✅ 완료 — 섹터 매핑 버그(바이오헬스→바이오, 방산 추가) 수정 후 매칭 11→18건 |
| 시계열 저장 + 자체평가 | `src/quant_engine/qualitative_sell_strategy_store_v1.py`(SQLite, GAS/xlsx와 독립) + `tools/evaluate_qualitative_sell_strategy_accuracy_v1.py` | ✅ 완료 — 평가 루프는 결정 누적 전까지 정직하게 DATA_GATED 보고 |
| 운영 스케줄러 | `.gitea/workflows/kis_data_collection.yml` — 영업일 08~17시 2시간 간격 + 수동 실행 | ✅ 완료 — Gitea repo secrets(`KIS_APP_KEY` 등) 등록은 USER_ACTION |
**향후 확장 시 고려사항(지금 구현하지 않음, 설계만 호환 유지)**
- DB 엔진: SQLite → PostgreSQL 전환 가능성을 고려해 `qualitative_sell_strategy_store_v1.py``insert_*`/`fetch_*` 함수 뒤로 SQL을 전부 숨겼다 — 호출부(오케스트레이터)는 DB 엔진을 모른다. 전환 시 이 한 파일의 내부 구현만 바꾸면 된다(AUTOINCREMENT→SERIAL 등 방언 차이만 해당 파일 내부 문제).
- 공매도 잔고율은 KRX 공매도종합포털 CSV 외 경로가 없음을 실측으로 확정했으므로, 재시도성 스크래핑 시도는 더 이상 하지 않는다.
**검증 명령**:
```
python -m pytest tests/unit -q → 40 passed
python tools/validate_no_direct_api_trading_v1.py → PASS (strict)
python tools/validate_specs.py / validate_formula_registry.py /
validate_golden_coverage_100.py / validate_harness_coverage_auditor.py → 전부 PASS
python tools/build_qualitative_sell_inputs_v1.py --batch --workbook GatherTradingData.xlsx --kis-account real
→ 10/10 종목 오류 0건, BATCH_GATE: PASS
```
---
### WBS-7: 보완·고도화 (Phase 7, 2026-06-21 비판적 리뷰 대응)
> 0c절에서 발견된 10개 문제에 대한 추적 WBS. 모든 항목은 착수 전이며 상태는 `TODO`.
#### WBS-7.1 캘리브레이션 임계값 실증 전환 (EXPERT_PRIOR/SPEC_DERIVED → PROVISIONAL → CALIBRATED)
| 항목 | 내용 |
|------|------|
| **작업** | 190개 임계값 중 `EXPERT_PRIOR`(59)·`SPEC_DERIVED`(123)를 실거래 표본 누적 순으로 `PROVISIONAL``CALIBRATED` 전환 |
| **현재 상태** | `CALIBRATED` 0/190 (0%), `PROVISIONAL` 8/190 (4.2%) |
| **우선순위** | `Temp/calibration_priority_v1.json`의 urgency score 상위 항목부터 |
| **담당 파일** | `tools/build_calibration_priority_v1.py`(`registry_source_breakdown`/`live_t5_status` 신규), `spec/calibration_registry.yaml` |
| **상태** | 도구 보강 완료(2026-06-21) — **CALIBRATED 승격 자체는 실거래 데이터 부재로 여전히 DATA_GATED** |
**부수 발견 — 데이터 무결성 버그**: `spec/calibration_registry.yaml``id: SEMI_CLUSTER_CAP_RISK_OFF`가 **서로 다른 두 공식(값 20.0/25.0)에 중복 등록**되어 있었다. id로 dict 조회하는 도구(`build_calibration_priority_v1.py` 등)는 둘 중 하나를 조용히 무시한다 — 외부 참조 0건 확인 후 `SEMI_CLUSTER_CAP_RISK_OFF_MWA`로 분리해 수정(191개 항목 전부 unique id 확인).
**성공 하네스 (데이터 기준)**:
```
검증: python tools/build_calibration_priority_v1.py
결과: [캘리브레이션 레지스트리 건강도] total=191 {'SPEC_DERIVED': 123, 'EXPERT_PRIOR': 60, 'PROVISIONAL': 8, 'CALIBRATED': 0}
CALIBRATED=0.0% 미검증(SPEC_DERIVED+EXPERT_PRIOR)=95.81%
→ 매 실행마다 자동 집계되어 더 이상 수동 grep 불필요(이전엔 수동 집계해야 했음)
T+5 수치도 Temp/prediction_accuracy_harness_v2.json에서 항상 live로 읽음(하드코딩된
35.86 리터럴을 제거 — WBS-7.2와 동일한 stale-수치 문제가 이 도구에도 있었음)
회귀: python -m pytest tests/unit/test_calibration_priority_v1.py -q → 5 passed
목표(1차, 미달성 — DATA_GATED): CALIBRATED ≥ 10건 (sample_n≥30 + 실측 backtest 노트 보유)
목표(2차, 미달성 — DATA_GATED): PROVISIONAL ≥ 30건
```
---
#### WBS-7.2 T+5/예측정확도 지표 단일 진실원천 통일
| 항목 | 내용 |
|------|------|
| **작업** | ROADMAP §4와 `spec/27_bch_calibration_runbook.yaml`이 서로 다른 시점의 T+5 캐시값을 인용하던 문제 해결 — 모든 문서가 `Temp/prediction_accuracy_harness_v2.json``as_of_date`를 동반 인용하도록 통일 |
| **현재 상태** | 2026-06-21 기준 `t5_sample=0`, `calibration_state=INSUFFICIENT_SAMPLES` — 두 문서의 54.76%/35.86% 모두 stale |
| **담당 파일** | `tools/build_prediction_accuracy_harness_v2.py`, `docs/ROADMAP_WBS.md` §4, `spec/27_bch_calibration_runbook.yaml` |
| **상태** | ✅ 완료 (2026-06-21) — `current_status_2026_06_21` 블록 신설, 구 블록 "역사적 스냅샷"으로 명시 |
**성공 하네스 (데이터 기준)**:
```
검증: ROADMAP §4의 T+5 수치와 spec/27_bch_calibration_runbook.yaml의 T+5 수치가
동일 as_of_date의 Temp/prediction_accuracy_harness_v2.json을 가리킬 것
규칙: 문서에 적중률 수치 인용 시 반드시 "(as_of: YYYY-MM-DD, sample=N)" 동반 표기
결과: t5_sample=0 → 두 문서 모두 "DATA_GATED (t5_sample=0, as_of 2026-06-21)"로 정정 완료
부가발견: cases_analyzed 141→0 회귀는 evaluation_methodology 변경 영향으로 추정 — 근본원인 조사는 별도 후속 과제
```
---
#### WBS-7.3 GAS→Python 공식 마이그레이션 재검토 (2026-06-21)
| 항목 | 내용 |
|------|------|
| **작업** | `governance/gas_logic_migration_ledger_v1.yaml` 15건 findings 전체를 원문부터 재검증 |
| **현재 상태** | 2건 DONE(F01/F09, 레저가 stale했을 뿐 실제론 이미 등록됨), 1건 KEEP_IN_GAS, **12건 TODO 유지 — 의도적 보류** |
| **담당 파일** | `governance/gas_logic_migration_ledger_v1.yaml` |
| **상태** | 부분 완료 — 안전하게 처리 가능한 항목만 종결, 나머지는 근거 있는 보류 |
**재검증으로 발견한 사실**:
```
F01/F09(REGISTER_*) → DONE 정정: spec/calibration_registry.yaml에 SP_TAKE_PROFIT/
TAKE_PROFIT_BASE가 P5-T01 wave1에서 이미 등록되어 있었음(gs_location 일치 확인).
F12/F13(DELETE_DISTRIBUTION_RISK_GAS) → 보류: ledger가 인용한 "build_distribution_risk_v1.py"는
존재하지 않는 파일. 실제로는 tools/build_distribution_risk_score_v2.py가 동일 필드를
산출하지만, GAS(gdf_03:2128)와 이 Python 산출값을 직접 대조하는 parity 테스트가
tests/parity·tests/regression 어디에도 없음(grep 0건) — "verify parity before delete"
조건 미충족으로 GAS 삭제 보류.
F14(DELETE_LATE_CHASE_RISK_GAS) → 보류, ledger 전제 자체가 오류: "build_alpha_lead_table_v1.py가
late_chase_risk_score를 산출"한다는 claim은 사실이 아님 — 해당 파일은 존재하지 않고,
발견된 도구들(build_late_chase_attribution_v1.py 등)은 이 필드를 "소비"만 할 뿐 산출하지
않는다. GAS가 이 점수의 유일한 산출 경로일 가능성이 높아 삭제 시도 자체가 위험.
F02~F06/F07/F10/F11/F15(MIGRATE_* 신규 포트, 12건 중 9건) → 의도적 미착수: parity 테스트
인프라 없이 결정론적 매매엔진의 가격/정지손실/라우팅 로직을 포팅하면 silent correctness
bug 위험이 큼(advisor 권고). 특히 F11(stop_loss_gate)은 ledger 자체가 "critical path"로
명시. 전용 parity 테스트 스프린트가 선행돼야 한다.
```
**성공 하네스 (데이터 기준)**:
```
검증: python -c "import yaml; from collections import Counter; \
d=yaml.safe_load(open('governance/gas_logic_migration_ledger_v1.yaml', encoding='utf-8')); \
print(Counter(f['status'] for f in d['findings']))"
결과: Counter({'TODO': 12, 'DONE': 2, 'KEEP_IN_GAS': 1})
python tools/validate_specs.py → PASS (이 마이그레이션 상태는 현재 CI 게이트와 무관함 —
tools/validate_gas_thin_adapter_v1.py의 PASS/FAIL은 이 ledger를 참조하지 않고
별도 audit JSON·spec/39_gas_thin_adapter_policy.yaml 기준으로 판정됨을 확인)
잔여 12건은 전용 parity 테스트 스프린트(별도 WBS)로 이관 — 이번 세션에서는 시도하지 않음.
```
---
#### WBS-7.4 Deprecated 별칭·시트 정리 (데드라인 2026-06-30)
| 항목 | 내용 |
|------|------|
| **작업** | `spec/aliases.yaml`의 deprecated 경로 17건을 데드라인 전 코드/spec 참조에서 전수 제거 |
| **현재 상태** | `remove_after: 2026-06-30` — 오늘(2026-06-21) 기준 9일 남음, 추적 항목 없었음 |
| **담당 파일** | `spec/aliases.yaml`, `tools/validate_specs.py` |
| **상태** | ✅ 완료 (2026-06-21) — alias 17건 제거, `python tools/validate_specs.py` PASS |
**성공 하네스 (데이터 기준)**:
```
검증: grep -rl "old_portfolio_exposure_framework\|old_risk_control" spec/ src/ tools/ | wc -l
결과: 참조 0건, `spec/aliases.yaml`은 비워짐
추가 검증: python tools/validate_specs.py → PASS
목표: 2026-06-30 이전 참조 0건 + spec/aliases.yaml에서 deprecated 항목 제거
```
---
#### WBS-7.5 임시 하드코딩 폴백 비례화
| 항목 | 내용 |
|------|------|
| **작업** | `OVERHANG_PRESSURE_V1``-500K` 절대값 폴백을 flow_rows 비례 공식으로 교체. 서킷브레이커 MRS +2점, CLA 25%→60% 임시 해제 조항에 명시적 종료조건 부여 |
| **현재 상태** | 3건 모두 "임시" 주석만 있고 영구화/대체 계획 없음 |
| **담당 파일** | `spec/13_formula_registry.yaml:1222`, `spec/calibration_registry.yaml`, `spec/risk/circuit_breakers.yaml:192`, `spec/risk/portfolio_exposure.yaml:403` |
| **상태** | ✅ OVERHANG_PRESSURE_V1 완료(2026-06-21) — 서킷브레이커/CLA 2건은 별도 정책 결정 사안으로 범위 외 |
**성공 하네스 (데이터 기준)**:
```
변경: without_20d_fallback을 "frg_5d_sh < -500000"(절대 주식수, 임시)에서
"avg_volume_5d IS NOT NULL AND frg_5d_sh < -1.5 * avg_volume_5d OR flow_credit < 0.30"로 교체.
근거: 1.5 배수는 같은 formula의 with_20d 분기(frg_20d_sh/4 × 1.5)가 이미 쓰는 계수를
재사용한 것 — 새로 추정한 값이 아님(advisor 검증 완료).
널가드: avg_volume_5d 결측 시 선행 missing_policy 규칙(volume_weakness=false와 동일하게
selling_acceleration도 false)을 명시적으로 확장 — divide-by-null/오탐 방지.
등록: spec/calibration_registry.yaml에 id=OVERHANG_PRESSURE_V1_FALLBACK_MULT(EXPERT_PRIOR,
sample_n=0)로 신규 등록 + formula_registry에 calibration_ref로 상호 참조.
검증: python tools/validate_specs.py → PASS, python -m pytest tests/unit tests/integration -q → 76 passed
잔여(범위 외): circuit_breakers.yaml MRS+2점, portfolio_exposure.yaml CLA 25→60% 임시해제는
수치적 조정이 아니라 정책 종료조건을 정하는 사안이라 별도 의사결정으로 분리.
```
---
#### WBS-7.6 슬리피지 실측 보정
| 항목 | 내용 |
|------|------|
| **작업** | `EXECUTION_SIMULATOR_V1`의 5bps 가정을 실거래 체결 데이터와 비교해 보정 |
| **현재 상태** | 이론치 5bps, "추후 실측 데이터로 보정 예정"이라는 메모만 존재 |
| **담당 파일** | `src/quant_engine/execution_slippage_store_v1.py`(신규), `tools/evaluate_execution_slippage_v1.py`(신규), `tests/unit/test_execution_slippage_store_v1.py`(신규) |
| **활성화 조건** | 실거래 체결 기록 ≥ 5건 누적 |
| **상태** | 캡처 스캐폴딩 완료(2026-06-21) — **비교 자체는 실측 표본 부재로 DATA_GATED 유지(정상)** |
**구현 내용**: 주문 실행은 여전히 사람이 HTS에서 수동 실행(governance/rules/06 준수, API로 체결을 가져오지 않음). 실행 후 사람이 `record` 서브커맨드로 의도가/실제체결가를 1건씩 수동 기록하면 SQLite(`outputs/execution_slippage/execution_slippage.db`)에 누적되고, `report` 서브커맨드가 5건 미만이면 항상 정직하게 `DATA_GATED`를 반환한다(추정 금지).
**성공 하네스 (데이터 기준)**:
```
기록: python tools/evaluate_execution_slippage_v1.py record --ticker 005930 --side BUY \
--intended-price 71000 --actual-price 71050 --recorded-at 2026-06-21
비교: python tools/evaluate_execution_slippage_v1.py report
→ 표본<5: {"status": "DATA_GATED", "sample_n": N, "min_required": 5, ...} (현재 실측 0건 → 이 상태)
→ 표본≥5: actual_mean_slippage_bps vs assumed(5.0) gap_bps 비교, gap>3bps면 spec 값 갱신 권고
회귀: python -m pytest tests/unit/test_execution_slippage_store_v1.py -q → 5 passed
```
---
#### WBS-7.7 신규 시스템 E2E 통합 테스트 구축
| 항목 | 내용 |
|------|------|
| **작업** | KIS 수집 → 스냅샷 어드민 적재 → 정성매도전략 평가로 이어지는 파이프라인 통합 테스트 1개 작성. `snapshot_admin_server_v1.py`의 임베디드 JS 스모크 테스트 추가. Naver 폴백 Cloudflare 차단 시 graceful degradation 테스트 |
| **현재 상태** | 단위 테스트 61개(양호) 존재, 통합/E2E 0건 |
| **담당 파일** | `tests/integration/test_kis_collection_to_snapshot_admin_and_sell_strategy_v1.py` (신규) |
| **상태** | ✅ 완료 (2026-06-21) — 네트워크 미사용, 3개 테스트 PASS |
**성공 하네스 (데이터 기준)**:
```
검증: python -m pytest tests/integration -q → 3 passed
1) kis_data_collection_v1.collect_to_sqlite(no-naver, no-live-kis) → data_collection_store_v1.db 적재
→ load_collection_dashboard_state()로 read-back, collection_snapshots count 일치 확인
2) Naver fetch_price_history가 Cloudflare 403(RuntimeError)을 던지도록 monkeypatch
→ collect_to_sqlite()가 배치 전체를 죽이지 않고 PASS/PASS_WITH_WARNINGS로 완료하는지 확인
3) compute_qualitative_sell_strategy() 순수함수 결과 → insert_sell_strategy_result →
fetch_recent_sell_strategy_results round-trip 일치 확인
회귀 확인: python -m pytest tests/unit tests/integration -q → 73 passed
```
---
#### WBS-7.8 ETF NAV/괴리율/추적오차/AUM 수집 경로 확정
| 항목 | 내용 |
|------|------|
| **작업** | KRX/KIND 기반 수집 경로 확정 또는, 확정이 불가하면 "구조적으로 미구현 유지" 사유와 재검토 주기를 명문화. 공매도 잔고율(KRX CSV 수동) 운영 절차도 함께 문서화 |
| **현재 상태** | `spec/16_data_gaps_roadmap.yaml` S4/S5 PLANNED 상태로 장기 방치, 재검토 주기 없음 |
| **담당 파일** | `spec/16_data_gaps_roadmap.yaml`, `docs/runbook.md` |
| **상태** | ✅ 완료 (2026-06-21, 2026-06-22 실측 보강) |
**2026-06-22 추가 실측(사용자 요청)**: "자동화 안 되면 차후 개선 목표로"라는 지시에 따라 추정이 아니라 실제로
자동화를 재시도했다. 이 repo가 이미 EOD 가격 조회에 쓰는 `pykrx``get_shorting_balance()`/
`get_etf_price_deviation()`/`get_etf_tracking_error()`를 직접 호출 — 기본 시세조회(OHLCV)는
정상 작동하지만 이 세 함수는 세션 쿠키를 정상 부트스트랩한 뒤에도 **`HTTP 400 LOGOUT`**을 반환했다
(raw HTTP로 재현). pykrx 임포트 시 뜨는 "KRX_ID/KRX_PW 미설정" 경고와 정확히 일치 — **KRX 회원
로그인이 있어야 접근 가능한 서버측 인증 게이트**임을 확정했다(헤더/세션 보정으로 해결 안 됨).
자동화하려면 KRX 계정을 자격증명으로 코드에 등록해야 하는데, 이는 governance/rules/06·07과
같은 종류의 새 정책 결정 사안이라 사용자 승인 없이 추가하지 않았다 — **개선 목표로 이관**:
`spec/16_data_gaps_roadmap.yaml` S4/S5의 `automation_attempt_2026_06_22` 필드에 재현 절차 기록,
`next_review_date: 2026-09-30` 재조사 시 "API 키 발급 가능성"이 아니라 "KRX 계정 발급·자격증명
관리 정책 승인 여부"로 질문을 재구성하도록 명시.
**성공 하네스 (데이터 기준)**:
```
검증: spec/16_data_gaps_roadmap.yaml S4/S5에 "next_review_date"+"automation_attempt_2026_06_22" 필드 존재
결과: docs/runbook.md 20~21번 항목에 실측 실패 근거(HTTP 400 LOGOUT) + 공매도 잔고율 주 1회
CSV 갱신 절차 + ETF NAV 수동 import 경로(tools/import_etf_nav_manual.py) 명문화
python tools/validate_specs.py → PASS
```
---
#### WBS-7.9 snapshot_admin Python 서버 — Gitea CI를 통한 Synology 상시 서비스화 검토 (2026-06-21)
| 항목 | 내용 |
|------|------|
| **작업** | `src/quant_engine/snapshot_admin_server_v1.py`(Python 어드민 웹 UI)를 Gitea CI/CD 배포 스텝을 통해 Synology NAS에서 상시 서비스로 운영할 수 있는지 검토 |
| **현재 상태** | **기술적으로는 가능**. 기본 루프백 보호 + Basic Auth 게이트를 추가했고, Synology 외부 노출은 리버스 프록시 기반 POC로 가이드함. 실배포 검증은 아직 필요 |
| **담당 파일** | `.gitea/workflows/ci.yml`, `tools/run_snapshot_admin_server_v1.py`, `src/quant_engine/snapshot_admin_server_v1.py`, `docs/SYNOLOGY_SNAPSHOT_ADMIN_POC.md`, `docs/WBS_7_9_EVIDENCE_PACKET_FINAL.md` |
| **상태** | 부분 완료 — POC 절차/보안 게이트 구현 완료, 로컬 loopback auth/tables smoke PASS, Synology live verification pending |
**조사 결과**:
1. **의존성 제약은 문제 없음**: `.gitea/workflows/ci.yml` 주석에 명시된 Synology DS216j(ARMv7l 32bit, Python 3.8.12) 제약은 "numpy/pandas 휠 없음, gcc 미설치"인데, `snapshot_admin_server_v1.py``http.server`/`sqlite3`/`json`/`pathlib`**표준 라이브러리만 사용**(grep으로 외부 의존성 0건 확인) — 이 제약에 걸리지 않는다.
2. **DS216j는 Docker 미지원 모델**이다(Container Manager는 x86 가상화 지원 모델에서만 동작). 따라서 컨테이너 배포는 불가하고, DSM Task Scheduler + 백그라운드 프로세스 방식이 유일한 현실적 경로다.
3. **CI 잡 프로세스 영속성 위험**: Gitea Act Runner가 잡 종료 시 자식 프로세스를 정리(kill)할 가능성이 있어, CI 스텝에서 단순히 서버를 백그라운드 실행(`nohup ... &`)해도 잡 종료와 함께 죽을 수 있다. 검증되지 않은 상태이며 실제 적용 전 `setsid`/`disown` 방식의 데몬화를 실측 테스트해야 한다.
4. **보안 — 가장 중요한 제약**: 현재 서버는 `--host 127.0.0.1`(로컬호스트 전용) 기본값이고 **인증 기능이 전혀 없다**. 이 어드민 UI는 `settings`/`account_snapshot` SQLite를 직접 쓰기 가능한 표면이며, 이 데이터는 결정론적 매수/매도 엔진의 입력이 된다. LAN에 상시 노출하려면 최소 (a) 인증 추가 또는 (b) DSM 리버스 프록시 뒤에서 VPN/방화벽 화이트리스트로 제한 — 둘 중 하나가 선행되어야 한다.
**권고 (보안 정책 결정 후 구현)**:
```
배포 방식: Gitea CI 배포 스텝에서 코드 갱신 후 PID 파일 확인 → 기존 프로세스 종료 → setsid로 재기동
가동 감시: DSM Task Scheduler에 5분 간격 헬스체크 스크립트 등록(프로세스 미생존 시 재기동) — poor-man's supervisor
네트워크: host=127.0.0.1 유지 + DSM 리버스 프록시(HTTPS)와 IP 화이트리스트로 LAN 내부 접근만 허용,
또는 호스트 OS 레벨 인증(Synology SSO/LDAP 연동) 추가 전까지 인터넷 노출 금지
검증: 배포 후 curl http://127.0.0.1:8787/api/state → 200 응답 + CI 잡 종료 후 5분 뒤에도 프로세스 생존 확인
```
> **이 항목은 "구현 가능"으로 결론났고, 기본 보안 게이트는 추가되었다. 다만 Synology 실배포/외부 노출 검증은 아직 남아 있으므로, 리버스 프록시·방화벽·인증을 함께 적용하는 POC 절차만 제시한다.**
**실측 절차 (WBS-7.9 live verification)**:
```
1) NAS 내부 확인
curl -i http://127.0.0.1:8787/api/state
기대: 200 OK, JSON 응답, version.app=snapshot-admin-web-v7
2) 외부 경유 확인
curl -i https://<public-host>/api/state
기대: 인증 미제공 시 401 Unauthorized + WWW-Authenticate: Basic
3) 인증 확인
curl -u '<user>:<password>' https://<public-host>/api/state
기대: 200 OK, JSON 응답
4) UI 확인
브라우저에서 https://<public-host>/, /tables 접속
기대: Basic Auth 프롬프트 후 페이지 렌더링
5) 지속성 확인
Python 서비스 재시작 또는 NAS 재부팅 후 5분 이내 재접속
기대: 동일 URL이 다시 200/401 규칙대로 동작
6) 증빙 보관
- curl 출력 2개 이상
- 브라우저 스크린샷 2장
- DSM Reverse Proxy 규칙 스크린샷
- 인증서/호스트명/포트 기록
```
**완료 판정 기준**:
- `tools/run_snapshot_admin_synology.sh start` 실행 후 `healthcheck ok` 확인
- `curl -i http://127.0.0.1:8787/api/state` 가 200 응답
- `curl -i https://<public-host>/api/state` 가 인증 없이는 401 응답
- `curl -u '<user>:<password>' https://<public-host>/api/state` 가 200 응답
- 브라우저에서 `https://<public-host>/``/tables` 가 인증 후 렌더링
- NAS 재시작 또는 서비스 재시작 뒤에도 위 동작이 동일하게 재현
- `docs/SYNOLOGY_SNAPSHOT_ADMIN_EVIDENCE_TEMPLATE.md` 양식에 맞춰 증빙을 채우고 보관
- 최종 완료 문구는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST.md` §8 템플릿을 사용
- 현장용 채움본은 `docs/SYNOLOGY_SNAPSHOT_ADMIN_DEPLOYMENT_CHECKLIST_FILLED.md` 참조
- DSM 입력 표는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_TABLE.md` 또는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FIREWALL_PROXY_COPYPASTE.md` 참조
- 최종 점검 10개는 `docs/SYNOLOGY_SNAPSHOT_ADMIN_FINAL_PREFLIGHT_10.md` 참조
---
#### WBS-7.10 어드민 페이지 — Tabler 기반 테이블별 그리드 조회 (2026-06-21)
| 항목 | 내용 |
|------|------|
| **작업** | `snapshot_admin_server_v1.py`에 워크스페이스 DB(`settings`/`account_snapshot`/`workspace_*`) + KIS 수집 DB(`collection_*`) + 정성매도전략 DB(`sell_strategy_results`/`satellite_recommendations`) 3개 SQLite 파일에 걸친 11개 테이블을 Tabler(CDN) 그리드로 조회하는 신규 `/tables` 페이지 추가 |
| **담당 파일** | `src/quant_engine/snapshot_admin_server_v1.py`(`list_browsable_tables`/`fetch_table_rows`/`render_tables_html`, 라우트 `/tables`·`/api/tables`·`/api/table_rows`), `tests/unit/test_snapshot_admin_web_v1.py` |
| **보안** | 테이블명은 고정 화이트리스트(`WORKSPACE_BROWSABLE_TABLES`/`COLLECTION_BROWSABLE_TABLES`/`QUALITATIVE_SELL_BROWSABLE_TABLES`)와 정확히 일치할 때만 SQL에 사용 — 임의 테이블명 SQL 인젝션 시도는 `ValueError`로 차단(테스트로 검증) |
| **상태** | ✅ 완료 (2026-06-21) |
**성공 하네스 (데이터 기준)**:
```
검증: python -m pytest tests/unit/test_snapshot_admin_web_v1.py -q → 8 passed
- render_tables_html()에 tabler/tableSelect/api 경로 포함 확인
- list_browsable_tables()가 3개 DB·11개 테이블 모두 열거하는지 확인
- fetch_table_rows() 페이지네이션(limit/offset) + 화이트리스트 외 테이블명 차단(ValueError) 확인
회귀 확인: python -m pytest tests/unit tests/integration -q → 76 passed
python tools/validate_specs.py → PASS
```
---
#### WBS-7.11 spec-코드 동기화 게이트 (2026-06-22, 설계+구현 완료)
**배경**: 2026-06-21 비판적 리뷰 이후 진행한 WBS-7.3/7.4 작업에서 spec/governance YAML이
실제 코드 상태와 어긋난 채로 방치된 사례를 3건 발견했다 — `governance/gas_logic_migration_ledger_v1.yaml`
존재하지 않는 파일(`build_distribution_risk_v1.py`, `build_alpha_lead_table_v1.py`)을
canonical 구현으로 인용, `spec/aliases.yaml``remove_after` 데드라인이 추적 없이 방치,
`spec/calibration_registry.yaml`의 중복 id로 일부 임계값이 조용히 무시됨. 세 사례 모두
"문서가 코드를 정확히 가리키는지 자동으로 검증하는 장치가 없다"는 동일 원인이다.
LLM이 런타임에 이런 stale spec을 사실로 읽으면 할루시네이션으로 직결된다(사용자 질의,
2026-06-21). **목표는 "구현됐으니 문서 삭제"가 아니라 "LLM이 읽는 문서는 항상 코드와의
동기화를 CI가 보장하고, 동기화할 수 없는 순수 설명용 문서는 폐기한다."**
| 항목 | 내용 |
|------|------|
| **작업** | spec YAML에 `has_code_implementation`/`code_path` 필드를 추가하고 `validate_specs.py`가 해당 code_path 존재 여부를 자동 검사하도록 신규 검증기 추가. **정정(구현 중 발견)**: `role: deprecated_redirect`는 실제로 2개뿐이었다(`spec/03_risk_policy.yaml`, `spec/04_strategy_rules.yaml`) — `spec/06_exit_policy.yaml``role: compatibility_index`(영구 유지 설계, risk_control.yaml/entry_gates.yaml과 동급)였다. 설계 단계의 "3개 삭제" 진술 자체가 부정확했던 것을 구현 중 재확인 후 정정 — 2개만 실삭제, 06_exit_policy.yaml은 `redirect_only:true`로 태깅해 유지 |
| **스키마 설계** | 각 spec YAML의 `meta:` 블록(없으면 최상위)에 추가:<br>`has_code_implementation: true\|false`<br>`code_path: "tools/build_x.py"` 또는 `["tools/a.py", "src/quant_engine/b.py"]` (true일 때만 필수)<br>`role: deprecated_redirect`/`compatibility_index` 파일은 `has_code_implementation: false` + `redirect_only: true`로 명시(코드 없음이 정상이므로 code_path 검사 스킵) |
| **검증기 설계** | `tools/validate_specs.py``validate_spec_code_sync(errors)` 신규 함수 추가:<br>1. `spec/**/*.yaml` 전체를 순회<br>2. `has_code_implementation` 필드가 **있는** 파일만 검사(필드 없는 파일은 skip — 이것이 점진적 롤아웃 메커니즘. 전체 일괄 강제 아님)<br>3. `true`인데 `code_path`(들)가 디스크에 없으면 `fail(errors, f"spec declares code_path that does not exist: {path} → {code_path}")`<br>4. `redirect_only: true`인데 `has_code_implementation: true`이면 모순으로 fail<br>5. 결과를 `Temp/spec_code_sync_v1.json``{checked_count, missing_code_path_count, sync_field_coverage_pct}`로 기록(기존 `behavioral_coverage_pct` 패턴과 동일 형식) |
| **실제 롤아웃 범위(구현 완료)** | 전체 159개(삭제 후) yaml 중 12개에 태깅 완료: `spec/exit/qualitative_sell_strategy_v1.yaml`, `governance/rules/06·07`, `spec/19_harness_contract.yaml`, `spec/55_execution_simulator_contract.yaml`, `spec/41_release_dag.yaml`, `spec/15_account_snapshot_contract.yaml`, `spec/18_settings_contract.yaml`, `spec/calibration_registry.yaml`(true 7개) + `spec/risk/risk_control.yaml`, `spec/strategy/entry_gates.yaml`, `spec/06_exit_policy.yaml`(redirect_only 3개). 공식 레지스트리(`13_formula_registry.yaml` 등)는 1:1 code_path가 없어 범위 제외 — 이미 `calibration_registry.yaml``gs_location`/`py_location` 필드가 공식 단위 동기화를 별도로 담당 |
| **담당 파일** | `tools/validate_specs.py`(`validate_spec_code_sync` 신규), `tests/unit/test_validate_spec_code_sync_v1.py`(신규 4건), 위 12개 spec/governance 파일 |
| **상태** | ✅ 구현 완료 (2026-06-22) |
**구현 중 발견한 버그**: 최초 구현에서 `redirect_only=true AND has_code_implementation=true` 모순 케이스가 `errors`에는 쌓이지만 함수 자신의 반환값 `gate`는 PASS로 남는 버그가 있었다 — 직접 작성한 단위테스트(`test_redirect_only_and_has_code_is_contradiction`)가 즉시 잡아냈고 `missing` 카운터에 반영해 수정했다.
**성공 하네스 (데이터 기준)**:
```
검증: python tools/validate_specs.py → Temp/spec_code_sync_v1.json
결과: {"total_spec_files": 159, "checked_count": 12, "missing_code_path_count": 0,
"sync_field_coverage_pct": 7.55, "gate": "PASS"}
회귀: python -m pytest tests/unit tests/integration -q → 85 passed
부수 조치(완료): role: deprecated_redirect 2개 파일(03_risk_policy.yaml/04_strategy_rules.yaml)
실삭제 + RetirementAssetPortfolio.yaml의 risk_policy_index/strategy_rules_index 참조 제거 +
6개 자식 파일의 parent_file 갱신 + spec/ownership_map.yaml·spec/risk·strategy/README.md 정정
(WBS-7.4에서 alias만 지우고 파일은 남겨뒀던 부분의 후속 정리)
목표(2차, 분기별 확장): sync_field_coverage_pct ≥ 50% — formula registry급 파일들의
공식 단위 동기화 메커니즘(calibration_registry gs_location/py_location) 커버리지 확장과 별개 트랙
```
---
## 3. 완성도 로드맵 매트릭스
| WBS | 우선순위 | 난이도 | 선행조건 | 예상 기간 | 현재 완성도 |
|-----|---------|------|---------|---------|-----------|
| 1.1 소수주 병합 | 🔴 Critical | 낮음 | GAS 배포 | 완료 | **100%** ✅ |
| 1.2 총자산 재계산 | 🔴 Critical | 중간 | 없음 | 완료 | **100%** ✅ |
| 1.3 Time_Stop_Date | 🟠 High | 낮음 | 없음 | 완료 | **100%** ✅ |
| 1.4 Rule_Sell_Qty | 🟠 High | 중간 | 없음 | 완료 | **100%** ✅ |
| 1.5 Lifecycle 레지스트리 | 🟡 Medium | 낮음 | 없음 | 완료 | **100%** ✅ |
| 2.1 펀더멘털 피드 | 🔴 Critical | 높음 | yfinance | 완료 | **100%** ✅ |
| 2.2 US 주식 가격 | 🟠 High | 중간 | Yahoo API | 완료 | **100%** ✅ |
| 2.3 RS 신호 V2 | 🟠 High | 중간 | 없음 | 완료 | **100%** ✅ |
| 2.4 PEG_SCORE | 🟡 Medium | 낮음 | 2.1 완료 | 완료 | **100%** ✅ |
| 2.5 섹터 플로우 | 🟡 Medium | 중간 | 30일 데이터 | DATA_GATED | DATA_GATED |
| 3.1 리밸런싱 V1 배포 | 🔴 Critical | 낮음 | GAS 배포 | 완료 | **100%** ✅ |
| 3.2 리밸런싱 V2 | 🟡 Medium | 높음 | 3.1 안정화 | 완료 | **100%** ✅ |
| 3.3 주문 시뮬레이터 | 🟠 High | 중간 | 3.1 완료 | 완료 | **100%** ✅ |
| 3.4 MDD 가드 | 🟠 High | 중간 | 일별 기록 | 완료 | **100%** ✅ |
| 4.1 T+20 레저 | 🟡 Medium | 중간 | 30건 대기 | 2026-07-15 | DATA_GATED (0/30건) |
| 4.2 예측 정확도 | 🟡 Medium | 중간 | 4.1 완료 | 2026-08 | DATA_GATED |
| 4.3 알파 보정 | 🟢 Low | 높음 | 4.2 완료 | 2026-09 | DATA_GATED |
| 4.4 대시보드 | 🟡 Medium | 낮음 | 4.1 완료 | 완료 | **100%** ✅ |
| 5.1 CI/CD | 🟡 Medium | 중간 | Gitea 연결 | 완료 | **100%** ✅ |
| 5.2 GAS 자동 배포 | 🟢 Low | 낮음 | 5.1 완료 | 완료 | **100%** ✅ |
| 5.3 자율 실행 | 🟢 Low | 중간 | 5.1+5.2 완료 | 완료 | **100%** ✅ |
| 6 비기계적 매도전략·위성추천 (엔진+데이터+KIS+SQLite+자체평가) | 🔴 Critical | 높음 | 없음 | 완료 | **100%** ✅ (잔류위험: 0c절·WBS-7.7) |
| 6-잔여 공매도 잔고율 | 🟢 Low | 높음 | KRX 정책 | 차단 확정 | USER_ACTION 대기 |
| 7.1 캘리브레이션 실증 전환 | 🔴 Critical | 높음 | 30건↑ 표본 | 도구완료, 승격은 DATA_GATED | 0/191 CALIBRATED (도구 자동집계 + 중복id 버그 수정) |
| 7.2 T+5 지표 정합성 통일 | 🔴 Critical | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21) |
| 7.3 GAS→Python 마이그레이션 | 🟠 High | 중간 | parity 테스트 | 부분완료 + 12건 의도적 보류 | 2/15 DONE, 12 TODO(근거기록), 1 KEEP_IN_GAS |
| 7.4 Deprecated 정리 | 🟠 High | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21, alias 17건 제거) |
| 7.5 임시 폴백 비례화 | 🟡 Medium | 중간 | 없음 | 완료(OVERHANG만) | **100%** ✅ (2026-06-21, 나머지 2건은 정책결정 분리) |
| 7.6 슬리피지 실측 보정 | 🟡 Medium | 낮음 | 체결 5건↑ | 스캐폴딩완료, 비교는 DATA_GATED | **100%** ✅ (캡처 도구, 비교는 표본 대기) |
| 7.7 E2E 통합테스트 | 🟠 High | 중간 | 없음 | 완료 | **100%** ✅ (2026-06-21, 3 passed) |
| 7.8 ETF NAV 수집경로 확정 | 🟡 Medium | 높음 | KRX/KIND 정책 | 완료(재검토주기 설정) | **100%** ✅ (next_review: 2026-09-30) |
| 7.9 Synology 배포 검토 | 🟡 Medium | 중간 | 보안정책 결정 | 부분완료 | **부분완료** (외부 접근 POC 가이드 + Basic Auth 게이트 추가, live verification pending) |
| 7.10 어드민 테이블 그리드(Tabler) | 🟢 Low | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21, 8 passed) |
| 7.11 spec-코드 동기화 게이트 | 🔴 Critical | 중간 | 없음 | 완료 | **100%** ✅ (2026-06-22, 12/159 태깅, 85 passed) |
---
## 4. 엔진 완성도 KPI (데이터 기반 측정)
```yaml
# 현재 상태 (2026-06-15 기준) vs 목표
데이터 품질:
NULL 컬럼 수: ≤10개 → 목표: 10개 이하 ✅ (WBS-2.1~2.4 완료)
Weight_Pct 정확도: 99% → 목표: 99% ✅ (소수주 병합 완료)
총자산 오차: 0.0% → 목표: 2.0% 이하 ✅ (실시간 재계산 완료)
sector_universe 갱신: 13섹터 112행 ✅ (Naver ETF 스크래핑, source_url/asof 완비)
공식 레지스트리:
lifecycle 등록률: 100% → 목표: 100% ✅ (269개 등록)
황금 테스트 커버리지: 100% → 목표: 100% ✅
공식 버전 충돌: 없음 → 유지 ✅
신호 품질:
RS 신호 커버리지: 100% → 목표: 100% ✅ (WBS-2.3 완료)
Flow_Credit 커버리지: 100% (data_feed 25/25) → 목표: 100% (WBS-2.5 DATA_GATED)
PEG_Gate 커버리지: 75% → 목표: 80% (WBS-2.4 완료, 음수성장 2종목 제외)
섹터 유니버스 갱신 gate: PASS ✅ (naver_rows=100, representative_rows=12)
리밸런싱 엔진:
레짐 소스 정확도: 100% → 유지 ✅ (macro.REGIME_PRELIM 최우선)
밴드 내 유지 여부: PASS → 유지 ✅
FORCE 주문 자동화: 100% → 유지 ✅
성과:
T+20 레저 건수: 0건 → 목표: 30건 (~2026-07-12) DATA_GATED
예측 적중률(T+1): 52.94% (sample=68, decisive=67.92%) — as_of 2026-06-21
예측 적중률(T+5): DATA_GATED (sample=0, as_of 2026-06-21) — 0c절 참조, 과거 54.76%/35.86% 캐시값 모두 폐기
알파 (vs KOSPI): 미측정 → 목표: >0%p/분기
honest_proof_score: 50.95 → 목표: ≥70 (T+20 30건 → 70.95 자동 달성 예상)
캘리브레이션 품질 (신규, WBS-7.1):
calibrated_threshold_count: 0/190 (0%) → 목표: ≥10건 (1차), ≥30건 (2차)
provisional_threshold_count: 8/190 (4.2%) → 목표: ≥30건
expert_prior_unvalidated_pct: 95.8% (SPEC_DERIVED+EXPERT_PRIOR) → 목표: ≤70%
보완·고도화 (신규, Phase 7):
gas_python_migration_pct: 0/14 완료 (0%) → 목표: 14/14 (100%, KEEP_IN_GAS 1건 제외)
deprecated_alias_remaining: 17건 (데드라인 2026-06-30) → 목표: 0건
e2e_integration_test_count: 0건 → 목표: ≥1건 (KIS수집→스냅샷→정성매도 체인)
자동화:
run_all 성공률: 98단계 DAG PASS → 목표: ≥95% ✅ (step_count=98, wave_0~9)
CI/CD 커버리지: 100% → 목표: 100% ✅ (Synology act_runner 온라인, 4게이트 PASS)
수동 개입 횟수: 매일 → 목표: ≤1회/주 (setupDailyRunAllTrigger 설정 후)
```
---
## 5. 다음 스프린트 실행 목록 (즉시 착수 가능)
### Sprint-1 (이번 주): 기반 경화 완결 (완료)
```
[x] WBS-1.1: GAS 배포 후 Weight_Pct 검증 (005930 ≥ 40%)
[x] WBS-1.2: totalAssetKrw_ 2-pass 재계산 구현
[x] WBS-1.3: Time_Stop_Date 자동 산출 (entry_date + 60일)
[x] WBS-1.4: Rule_Sell_Qty = floor(qty × Sell_Ratio_Pct / 100) 구현
[x] WBS-3.1: rebalance 시트 GAS 실행 확인 (timestamp 확인)
```
### Sprint-2 (2주): 신호 완성 (완료)
```
[x] WBS-2.3: RS_Verdict_V1_Raw, RS_Line_20D_Slope 로직 구현
[x] WBS-2.2: US 주식 가격 settings 수동입력 → 자동 Weight_Pct 연동
[x] WBS-3.3: 주문 시뮬레이터 tick 정규화 완성
[x] WBS-1.5: lifecycle 레지스트리 149개 중 상위 50개 이관
```
### Sprint-3 (4주): 펀더멘털 + 성과 기반 구축 (완료)
```
[x] WBS-2.1: DART 재무데이터 수집 파이프라인 구현 (tools/ingest_fundamental_raw.py yfinance 개편)
[x] WBS-3.2: 리밸런싱 V2 신호 가중 목표배분 (signal_weighted_ss001_v1 PASS)
[x] WBS-3.4: MDD 일별 기록 테이블 생성 (logDailyAssetHistory_ daily_history 시트 자동 생성)
[x] WBS-4.1: T+20 레저 구조 구축 (tools/build_realized_performance_v1.py 스키마 완성; 데이터 누적 중)
[x] WBS-5.1: Gitea CI/CD 기본 파이프라인 (.gitea/workflows/ci.yml 구축)
```
### Sprint-4 (DATA_GATED): 성과 인텔리전스 + 자동화 완결
```
[ ] WBS-4.1: T+20 레저 첫 30건 달성 (2026-07-15) — 거래 데이터 누적 필요
[ ] WBS-4.2: 예측 정확도 하네스 (WBS-4.1 완료 후)
[ ] WBS-4.3: 알파 보정 루프 (WBS-4.2 완료 후)
[x] WBS-2.4: PEG_SCORE_V1 실데이터 검증 완료 (ingest_fundamental_raw.py peg_ratio/peg_gate 추가, 비ETF 75% 커버)
[x] WBS-4.4: 성과 모니터링 대시보드 완성 (updateEvaluationDashboard_() GAS 함수 + run_all Step-8)
[x] WBS-5.2: GAS 자동 배포 스크립트 (tools/deploy_gas.py -- dry-run PASS 17 files)
[x] WBS-5.3: 타이머 트리거 설정 (gdf_06_rebalance.gs setupDailyRunAllTrigger() 추가)
```
### Sprint-5 (2026-06-15): 섹터 유니버스 월간 갱신 파이프라인 (완료 — PR #62)
```
[x] 섹터 유니버스 Naver 스크래핑 도구 구현
tools/update_sector_universe_from_naver.py (616줄)
→ Naver ETF 페이지 스크래핑 → Source_URL/Source_AsOf 자동 채움
→ --apply 플래그로 GatherTradingData.xlsx 원본 반영
→ 결과: 13섹터 112행, naver_rows=100, representative_rows=12
[x] 섹터 유니버스 갱신 하네스 구현
tools/validate_sector_universe_monthly_refresh_v1.py (173줄)
→ gate=PASS/WARN/FAIL 판정 (PASS 확인)
→ missing_source_url=0, stale_rows=0, template_rows=0
[x] 섹터 유니버스 리프레시 감사 모듈
src/quant_engine/sector_universe_refresh.py (296줄)
[x] GAS 라이브러리 강화 (src/gas/core/gas_lib.gs +429줄)
[x] 섹터 리포트 & 대표종목 모니터 고도화
etf_representative_monitor.py, render_operational_report.py
update_workbook_sector_insights.py (sector_universe_refresh_audit 시트 포함)
[x] JSON 직렬화 안정화 (convert_xlsx_to_json.py — datetime/NaN 예외 처리)
[x] sector_universe GatherTradingData.xlsx --apply 반영 완료 (2026-06-15)
```
**월간 운영 절차 (매월 1회):**
```bash
python tools/update_sector_universe_from_naver.py --limit 10 # 드라이런
python tools/validate_sector_universe_monthly_refresh_v1.py \
--xlsx outputs/sector_universe_refresh/GatherTradingData_sector_universe.xlsx
python tools/update_sector_universe_from_naver.py --limit 10 --apply # 원본 반영
```
---
### Sprint-6 (비판적 보완 스프린트, 2026-06-21 비판적 리뷰 대응)
```
[x] WBS-7.2: T+5/예측정확도 지표 단일 진실원천 통일 (2026-06-21 완료)
[x] WBS-7.4: Deprecated 별칭 17건 정리 — 2026-06-30 데드라인 (2026-06-21 완료, validate_specs.py PASS)
[x] WBS-7.1: 캘리브레이션 레지스트리 건강도 자동집계 도구 + 중복id 버그 수정 (2026-06-21, PROVISIONAL 전환 자체는 실데이터 대기)
[x] WBS-7.3: GAS→Python 마이그레이션 재검토 완료(2건 DONE 정정, 12건 의도적 보류+근거기록, 2026-06-21) — 잔여는 별도 parity 테스트 스프린트
[x] WBS-7.7: KIS수집→스냅샷→정성매도 E2E 통합 테스트 작성 (2026-06-21 완료, 3 passed)
[x] WBS-7.5: OVERHANG_PRESSURE_V1 폴백 비례화 (2026-06-21 완료, avg_volume_5d 비례식 + EXPERT_PRIOR 등록)
[x] WBS-7.6: 슬리피지 실측 캡처 스캐폴딩 구축 완료 (2026-06-21, 비교 자체는 체결 5건 누적 대기)
[x] WBS-7.8: ETF NAV 수집경로 재검토 + 공매도 잔고율 운영절차 문서화 (2026-06-21 완료)
```
---
## 6. 부록: Phase 5 데이터 플랫폼 전환 WBS 성공값
> 원칙: 아래 항목은 모두 `기대 성공값 + 데이터 증빙 + 검증 명령`이 함께 있어야 성공으로 본다.
> 현재 구현된 항목은 로컬 `Temp/` 증빙을 기준으로 판정하고, 아직 미래 전환 항목은 `DATA_GATED`로 둔다.
| WBS | 기대 성공값 | 데이터 증빙 | 검증 명령 |
|-----|------------|------------|-----------|
| P1 KIS core collector | `collector_gate=PASS`, `output_json_gate=PASS`, `collection_runs>=1`, `collection_snapshots>=1`, `provenance_source_count>=1` | `Temp/test_kis_data_collection.json`, `Temp/test_kis_data_collection.db` | `python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db Temp/test_kis_data_collection.db --output-json Temp/test_kis_data_collection.json --kis-account real --no-live-kis --no-naver` |
| P2 SQLite canonical store | `sqlite_schema_tables>=3`, `round_trip_snapshot_lookup=PASS`, `backend_contract_sqlite=PASS`, `backend_contract_postgresql=READY`, `single_workspace_sqlite=true`, `collector_separate_db=true` | `src/quant_engine/data_collection_store_v1.py`, `src/quant_engine/data_collection_backend_v1.py`, `tests/unit/test_data_collection_store_v1.py`, `src/quant_engine/snapshot_admin_store_v1.py` | `python -m pytest tests/unit/test_data_collection_store_v1.py -q` |
| P3 CI scheduler cutover | `xlsx_dependency_removed=true`, `json_seed_input=true`, `sqlite_output=true`, `mock_api_validation=PASS`, `no_direct_trading_gate=PASS` | `.gitea/workflows/kis_data_collection.yml`, `Temp/kis_api_credentials_validation_v1.json`, `Temp/test_kis_data_collection.json` | `python tools/validate_no_direct_api_trading_v1.py` |
| P4 GAS thin adapter minimize | `allowed_responsibilities_only=true`, `forbidden_responsibilities_present=false`, `thin_adapter_gate=PASS` | `tools/validate_gas_thin_adapter_v1.py`, `Temp/gas_thin_adapter_validation_v1.json`, `src/gas/core/gas_lib.gs` | `python tools/validate_gas_thin_adapter_v1.py` |
| P5 PostgreSQL upgrade path | `sqlite_schema_parity=PASS`, `backend_contract_present=true`, `postgres_execution=DATA_GATED`, `caller_compatibility_preserved=true` | `src/quant_engine/data_collection_backend_v1.py`, `src/quant_engine/kis_data_collection_v1.py`, `tests/unit/test_data_collection_store_v1.py`, `tools/generate_postgresql_upgrade_stub_v1.py` | `python -m pytest tests/unit/test_data_collection_store_v1.py -q` |
| P6 Snapshot admin web editor | `settings_sheet_web_editor=true`, `account_snapshot_sheet_web_editor=true`, `contenteditable_grid=true`, `api_save_round_trip=PASS`, `kis_collection_dashboard=true`, `workspace_db_is_single_file=true`, `collection_filter_controls=true`, `collection_dashboard_page=true`, `change_timeline_view=true` | `src/quant_engine/snapshot_admin_server_v1.py`, `src/quant_engine/data_collection_store_v1.py`, `src/quant_engine/snapshot_admin_store_v1.py`, `tools/validate_snapshot_admin_web_v1.py`, `tests/unit/test_snapshot_admin_web_v1.py`, `.gitea/workflows/snapshot_admin.yml` | `python tools/validate_snapshot_admin_web_v1.py` |
| Q1 Qualitative sell pipeline | `mock_api_validation=PASS`, `pipeline_contract=PASS`, `workflow_present=true`, `schedule_present=true`, `package_scripts_present=true` | `.gitea/workflows/qualitative_sell_strategy.yml`, `tools/validate_qualitative_sell_strategy_pipeline_v1.py`, `Temp/qualitative_sell_strategy_pipeline_v1.json` | `python tools/validate_qualitative_sell_strategy_pipeline_v1.py` |
| Q2 Gitea secrets contract | `secrets_contract=PASS`, `workflow_secret_mapping=PASS`, `docs_present=true`, `ci_validation_present=true` | `docs/GITEA_SECRETS_SETUP.md`, `tools/validate_gitea_secrets_contract_v1.py`, `Temp/gitea_secrets_contract_v1.json` | `python tools/validate_gitea_secrets_contract_v1.py` |
### WBS 성공 판정 규칙
- `PASS`: 기대 성공값이 충족되고, 해당 증빙 파일이 실제로 존재한다.
- `READY`: 지금은 실행하지 않지만, 다음 단계 전환에 필요한 코드/계약이 존재한다.
- `DATA_GATED`: 의도적으로 아직 실제 데이터가 쌓이지 않아 보류된 항목이다.
- `FAIL`: 기대 성공값을 만족하지 못하거나 증빙이 없다.
> 이 문서는 `docs/ROADMAP_WBS.md` 에 저장됩니다.
> 스프린트 완료마다 **완성도 KPI 섹션**을 업데이트하세요.
> 모든 WBS 항목의 구현 시 반드시 **하네스 성공 기준**을 먼저 충족 후 다음 단계로 진행합니다.