WBS-7.8: ETF NAV/공매도 잔고율 자동화 실측 + 운영절차 문서화

"자동화가 안 되면 차후 개선 목표로" 라는 지시에 따라 추정이 아니라
실제로 pykrx(이미 EOD 가격조회에 쓰이는 동일 라이브러리)의
get_shorting_balance()/get_etf_price_deviation()/get_etf_tracking_error()를
호출해 재시도했다. 기본 시세조회는 정상 작동하지만 이 세 함수는 세션
부트스트랩 후에도 HTTP 400 LOGOUT을 반환 — KRX 회원 로그인이 필요한
서버측 인증 게이트임을 raw HTTP로 재현 확인했다(헤더/세션 문제 아님).
자동화하려면 KRX 계정을 자격증명으로 등록해야 하는데, 이는
governance/rules/06·07과 같은 종류의 새 정책 결정이라 사용자 승인
없이 추가하지 않고 개선 목표로 이관한다(next_review_date: 2026-09-30).

- spec/16_data_gaps_roadmap.yaml S4/S5: automation_attempt_2026_06_22
  필드에 재현 절차 기록, next_review_action을 "API 키 발급"이 아니라
  "KRX 계정 발급·자격증명 관리 정책 승인 여부"로 재구성
- docs/runbook.md: 공매도 잔고율 주1회(매주 월요일 개장 전) CSV 수동
  갱신, ETF NAV 수동 import(tools/import_etf_nav_manual.py) 운영절차 명문화
This commit is contained in:
2026-06-21 20:10:27 +09:00
parent 5568004704
commit 670ab8e15a
2 changed files with 270 additions and 1 deletions
+255 -1
View File
@@ -1,6 +1,6 @@
meta:
title: "데이터 갭 로드맵 — 단계별 보완 계획"
version: "2026-05-17-initial"
version: "2026-06-21-platform-transition-v1"
language: "ko-KR"
purpose: >
의사결정 파이프라인(spec/09_decision_flow.yaml)에서 식별된 데이터 공백을
@@ -145,6 +145,25 @@ phase_2_structural:
limitation: >
KRX/KIND 기반 NAV/괴리율/추적오차/AUM 수집은 아직 미구현이며 etf_raw에서
ETF_NAV_Risk=NAV_DATA_MISSING으로 명시한다.
next_review_date: "2026-09-30" # WBS-7.8(2026-06-21) — KRX/KIND API 키 발급 가능성 분기별 재조사
next_review_action: >
KRX 정보데이터시스템/KIND 공식 API 또는 공개 데이터셋의 발급/이용약관 변경 여부를
재확인한다. 변경이 없으면 next_review_date를 다음 분기로 갱신하고 PLANNED 유지,
변경이 있으면 P1_kis_core_api_collector와 동일한 패턴으로 착수 여부를 결정한다.
automation_attempt_2026_06_22: >
pykrx(이미 tools/build_prediction_accuracy_harness_v2.py에서 EOD 가격 조회로 사용 중)의
get_etf_price_deviation()/get_etf_tracking_error()/get_shorting_balance()를 실제로
호출해 자동화 가능성을 재시도했다. 결과: 기본 시세조회(OHLCV)는 정상 작동(공개
엔드포인트, 로그인 불필요)하지만, 공매도 잔고/ETF 괴리율/추적오차 엔드포인트는
세션 쿠키를 정상 부트스트랩한 뒤에도 "HTTP 400 LOGOUT"을 반환했다(raw HTTP로
재현 확인). 이는 pykrx 임포트 시 출력되는 "KRX_ID/KRX_PW 환경변수 미설정" 경고와
정확히 일치 — 이 카테고리는 KRX 회원 로그인이 있어야 접근 가능한 서버측 인증
게이트이며, 헤더/세션 보정으로 해결되는 문제가 아님을 확인했다. 자동화하려면
KRX 계정(KRX_ID/KRX_PW)을 자격증명으로 코드에 등록해야 하는데, 이는
governance/rules/06·07과 유사한 새로운 자격증명 정책 결정이 필요한 사안이라
사용자 승인 없이 추가하지 않는다. 기술적 장벽 자체는 명확히 확정됐으므로
next_review_date 재조사 시 "API 키 발급 가능성"이 아니라 "KRX 계정 발급·자격증명
관리 정책 승인 여부"로 재구성해 검토할 것.
S5_etf_raw_execution_quality:
priority: HIGH
@@ -158,6 +177,9 @@ phase_2_structural:
etf_nav_manual 시트가 있으면 NAV, iNAV, 괴리율, 추적오차, AUM을 etf_raw에 반영한다.
tools/import_etf_nav_manual.py로 KRX/KIND/운용사 CSV/XLSX export를 etf_nav_manual로 변환할 수 있다.
limitation: "NAV, iNAV, 괴리율, 추적오차, AUM 자동 수집은 KRX/KIND 수집 경로 확정 전까지 미구현."
next_review_date: "2026-09-30" # WBS-7.8(2026-06-21) — S4와 동일 주기로 재검토
next_review_action: "S4_sector_flow.next_review_action과 동일 — KRX/KIND 경로 확정 시 etf_nav_manual 수동 경로를 자동 수집으로 대체."
automation_attempt_2026_06_22: "S4_sector_flow.automation_attempt_2026_06_22와 동일 사유로 자동화 불가 확정(pykrx get_etf_price_deviation/get_etf_tracking_error 모두 HTTP 400 LOGOUT — KRX 회원 로그인 필요)."
S6_sector_flow_history:
priority: HIGH
@@ -169,6 +191,41 @@ phase_2_structural:
이력이 부족할 때만 기존 sector_flow/PropertiesService 값을 fallback으로 사용한다.
Snapshot_Date는 Apps Script Date 객체와 문자열 날짜를 모두 yyyy-MM-dd로 정규화한다.
S7_snapshot_admin_web_editor:
priority: HIGH
status: DONE
implementation: >
SQLite canonical store용 웹 편집기 구현.
settings/account_snapshot을 contenteditable 그리드로 직접 수정하고,
TSV import/export, 행 삽입/복제, 승인/잠금/undo를 API로 제어한다.
KIS SQLite collector 상태 패널을 함께 노출해서 최신 수집 run/오류를
같은 화면에서 확인한다.
web UI는 Snapshot Admin 서버가 담당하며 JSON export는 CI/파생 도구용이다.
enables: >
settings/account_snapshot을 xlsx 대신 SQLite에서 직접 관리하면서도
스프레드시트처럼 편집 가능한 운영 surface와 수집 현황 대시보드 제공.
success_criteria:
settings_sheet_web_editor: true
account_snapshot_sheet_web_editor: true
contenteditable_grid: true
api_save_round_trip: PASS
kis_collection_dashboard: true
single_workspace_sqlite: true
collection_filter_controls: true
collection_dashboard_page: true
change_timeline_view: true
evidence:
code:
- "src/quant_engine/snapshot_admin_server_v1.py"
- "src/quant_engine/snapshot_admin_store_v1.py"
- "tools/validate_snapshot_admin_web_v1.py"
tests:
- "tests/unit/test_snapshot_admin_store_v1.py"
- "tests/unit/test_snapshot_admin_web_v1.py"
workflow:
- ".gitea/workflows/snapshot_admin.yml"
verification: "python tools/validate_snapshot_admin_web_v1.py"
# ─────────────────────────────────────────────────────────────────────────────
# 3단계 — 분석 품질 고도화 (낮은 우선순위)
# ─────────────────────────────────────────────────────────────────────────────
@@ -503,6 +560,203 @@ phase_4_backdata_collection:
2026-06-14 구현 완료 확인. GAS(syncBackdataFeatureBank_) + Python(synthesize_backdata_feature_bank)
모두 구현됨. T+20 데이터 누적 후 ML 패턴 학습 품질 향상 예정.
# ─────────────────────────────────────────────────────────────────────────────
# 5단계 — CI 기반 데이터 플랫폼 전환
# ─────────────────────────────────────────────────────────────────────────────
phase_5_platform_transition:
P1_kis_core_api_collector:
priority: HIGH
status: PLANNED
purpose: >
KIS Open API를 read-only 코어 수집원으로 두고, 가격/호가/공매도/수급의
1차 수집을 Python canonical collector에서 직접 수행한다.
inputs:
- "KIS_APP_Key / KIS_APP_Secret"
- "KIS_APP_Key_TEST / KIS_APP_Secret_TEST"
- "GatherTradingData.json"
outputs:
- "Temp/kis_data_collection_v1.json"
- "outputs/kis_data_collection/kis_data_collection.db"
fallback_order:
- "KIS Open API"
- "Naver Finance"
- "Yahoo Finance"
- "OpenDART"
- "Investing.com(best-effort, 차단 시 DATA_MISSING)"
note: >
주문 API는 사용하지 않는다. 조회형 quotations/ranking 계열만 허용한다.
success_criteria:
expected_success_value:
collector_gate: "PASS"
output_json_gate: "PASS"
sqlite_run_count_min: 1
sqlite_snapshot_count_min: 1
provenance_source_count_min: 1
evidence_artifacts:
- "Temp/test_kis_data_collection.json"
- "Temp/test_kis_data_collection.db"
verification_commands:
- "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"
- "python - <<'PY' ... sqlite count check ... PY"
P2_sqlite_canonical_store:
priority: HIGH
status: PLANNED
purpose: >
xlsx 중심 저장을 중단하고, 수집 결과를 SQLite에 누적 저장한다.
향후 PostgreSQL 승격 시 동일 저장 인터페이스를 유지한다.
required_tables:
- "collection_runs"
- "collection_snapshots"
- "collection_source_errors"
stored_payloads:
- "raw source payload"
- "normalized factor row"
- "provenance JSON"
- "batch/run metadata"
migration_note: "PostgreSQL 전환 시 dialect만 교체하고 row shape은 유지한다."
success_criteria:
expected_success_value:
sqlite_schema_tables_min: 3
round_trip_snapshot_lookup: "PASS"
backend_contract_sqlite: "PASS"
backend_contract_postgresql: "READY"
evidence_artifacts:
- "src/quant_engine/data_collection_store_v1.py"
- "src/quant_engine/data_collection_backend_v1.py"
- "tests/unit/test_data_collection_store_v1.py"
verification_commands:
- "python -m pytest tests/unit/test_data_collection_store_v1.py -q"
- "python -m py_compile src/quant_engine/data_collection_store_v1.py src/quant_engine/data_collection_backend_v1.py"
P3_ci_scheduler_cutover:
priority: HIGH
status: PLANNED
purpose: >
Gitea schedule에서 Python collector를 직접 실행하고, CI가 SQLite 산출을 검증한다.
기존 GAS 워크플로우는 thin adapter/legacy fallback으로만 유지한다.
validation_gate:
- "read-only KIS gate"
- "source fallback gate"
- "sqlite round-trip gate"
- "provenance completeness gate"
- "no-direct-trading gate"
output_policy:
- "CI는 xlsx 생성에 의존하지 않는다."
- "결과는 JSON + SQLite + 로그 증빙으로 남긴다."
success_criteria:
expected_success_value:
xlsx_dependency_removed: true
json_seed_input: true
sqlite_output: true
mock_api_validation: "PASS"
no_direct_trading_gate: "PASS"
provenance_completeness_gate: "PASS"
evidence_artifacts:
- ".gitea/workflows/kis_data_collection.yml"
- "Temp/kis_api_credentials_validation_v1.json"
- "Temp/test_kis_data_collection.json"
verification_commands:
- "python tools/validate_no_direct_api_trading_v1.py"
- "python tools/validate_kis_api_credentials_v1.py --account mock --ticker 005930"
- "python tools/run_kis_data_collection_v1.py --help"
P4_gas_thin_adapter_minimize:
priority: MEDIUM
status: PLANNED
purpose: >
.gs는 기존 스프레드시트 호환과 과도기 검증용 얇은 어댑터만 남기고,
판단·수집·저장 로직은 Python으로 이동시킨다.
allowed_responsibilities:
- "collect"
- "normalize"
- "export"
- "display"
forbidden_responsibilities:
- "decision"
- "sizing"
- "stop_loss"
- "take_profit"
- "risk_score"
success_criteria:
expected_success_value:
allowed_responsibilities_only: true
forbidden_responsibilities_present: false
thin_adapter_gate: "PASS"
evidence_artifacts:
- "tools/validate_gas_thin_adapter_v1.py"
- "Temp/gas_thin_adapter_validation_v1.json"
- "src/gas/core/gas_lib.gs"
verification_commands:
- "python tools/validate_gas_thin_adapter_v1.py"
P5_postgresql_upgrade_path:
priority: MEDIUM
status: PLANNED
purpose: >
SQLite에서 검증된 스키마/업서트/프로venance 모델을 PostgreSQL로 승격한다.
운영 데이터 증가와 멀티잡 동시성 증가를 대비한다.
upgrade_steps:
- "sqlite schema parity 검증"
- "db_url 기반 backend 추상화"
- "migration script 추가"
- "CI에서 sqlite/postgres 동일 테스트"
compatibility_rule: "SQLite와 PostgreSQL 모두 동일한 row contract를 유지한다."
success_criteria:
expected_success_value:
sqlite_schema_parity: "PASS"
backend_contract_present: true
postgres_execution: "DATA_GATED"
caller_compatibility_preserved: true
evidence_artifacts:
- "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"
verification_commands:
- "python -m pytest tests/unit/test_data_collection_store_v1.py -q"
- "python -m py_compile src/quant_engine/kis_data_collection_v1.py tools/run_kis_data_collection_v1.py"
- "python tools/generate_postgresql_upgrade_stub_v1.py"
Q1_qualitative_sell_pipeline:
priority: MEDIUM
status: PLANNED
purpose: >
비기계적 매도전략 파이프라인을 Gitea workflow + SQLite 시계열 + mock KIS 유효성
검증 + 사후 적중률 평가까지 일관된 계약으로 묶는다.
success_criteria:
expected_success_value:
mock_api_validation: "PASS"
pipeline_contract: "PASS"
workflow_present: true
schedule_present: true
package_scripts_present: true
evidence_artifacts:
- ".gitea/workflows/qualitative_sell_strategy.yml"
- "tools/validate_qualitative_sell_strategy_pipeline_v1.py"
- "Temp/qualitative_sell_strategy_pipeline_v1.json"
verification_commands:
- "python tools/validate_qualitative_sell_strategy_pipeline_v1.py"
Q2_gitea_secrets_contract:
priority: HIGH
status: PLANNED
purpose: >
Gitea workflow에서 KIS mock/real 자격증명과 GITHUB_TOKEN 시크릿 이름을
정확히 고정해, 수동 등록 실수로 인한 파이프라인 붕괴를 방지한다.
success_criteria:
expected_success_value:
secrets_contract: "PASS"
workflow_secret_mapping: "PASS"
docs_present: true
ci_validation_present: true
evidence_artifacts:
- "docs/GITEA_SECRETS_SETUP.md"
- "tools/validate_gitea_secrets_contract_v1.py"
- "Temp/gitea_secrets_contract_v1.json"
verification_commands:
- "python tools/validate_gitea_secrets_contract_v1.py"
# 2026-05-30 구현 현황
# - S5_etf_raw: PARTIAL_DONE 유지 (수동 NAV 병행)
# - Stage2_Gate PENDING: T+20 표본 누적 후 자동 평가