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:
@@ -6,3 +6,18 @@
|
||||
4. Render reports from canonical data only.
|
||||
5. Package upload artifacts only after the full gate passes or the output is explicitly audit-only.
|
||||
6. Treat work as complete only when YAML, code, data artifacts, and validation evidence all exist together.
|
||||
7. For calibration maintenance, run `npm run ops:calibration-backlog` or the Gitea schedule in `.gitea/workflows/calibration_backlog.yml`.
|
||||
8. Promote a threshold to `PROVISIONAL` only when there is a recorded sample note and an explicit change note in `Temp/calibration_change_ledger_v4.json`.
|
||||
9. Promote a threshold to `CALIBRATED` only when `sample_n >= 30`, a backtest note exists, and the validator still reports `overclaimed_count == 0`.
|
||||
10. For human review, open `Temp/calibration_review_report_v1.md` after each backlog build.
|
||||
11. For approval signoff, open `Temp/calibration_approval_list_v1.md` and approve only `source=PROVISIONAL` rows unless a new provisional review is explicitly requested.
|
||||
12. For spreadsheet-like edits of `settings` and `account_snapshot`, run `npm run ops:snapshot-web` and validate with `npm run ops:snapshot-web-validate`.
|
||||
13. Treat the snapshot admin web UI as the canonical edit surface for SQLite-backed manual maintenance; export JSON only when CI or downstream tooling needs a file artifact.
|
||||
14. Keep `settings` and `account_snapshot` in the same workspace SQLite DB. Do not split them into separate files per sheet; use a separate SQLite DB only for the KIS collection pipeline.
|
||||
15. Use the `KIS Collection` panel in snapshot admin to inspect the latest SQLite collection run, report status, source counts, and recent errors before you touch the editor.
|
||||
16. Use the collection filter when you need to narrow runs, snapshots, or errors by ticker/source/status.
|
||||
17. Use the change log filter when you need to audit a specific domain, action, or target reference.
|
||||
18. Use `/collection` when you want the collection-only dashboard with raw JSON download.
|
||||
19. Use `Export approval packet` in the snapshot admin UI to write `Temp/snapshot_admin_approval_packet_v1.json` and `Temp/snapshot_admin_approval_packet_v1.md` for review handoff.
|
||||
20. Short balance ratio (`short_balance_ratio`) has no automatable path — confirmed 2026-06-22 by live-testing `pykrx.stock.get_shorting_balance()` (already used elsewhere in this repo for EOD prices), which returns `HTTP 400 LOGOUT` even with a properly bootstrapped session. This KRX "standard report" endpoint family requires actual KRX member login (`KRX_ID`/`KRX_PW`), unlike the basic OHLCV endpoints. Adding KRX login credentials is a new credential-management policy decision (same category as governance/rules/06-07) that requires explicit user approval — do not add it unilaterally. Until then, download the KRX 공매도종합포털 CSV weekly (every Monday before market open) and feed it via `--short-csv` to `build_qualitative_sell_inputs_v1.py`.
|
||||
21. ETF NAV/iNAV/괴리율/추적오차/AUM has no automatable path either — same 2026-06-22 test confirmed `pykrx.stock.get_etf_price_deviation()`/`get_etf_tracking_error()` also return `HTTP 400 LOGOUT` (same KRX member-login gate as item 20). See `spec/16_data_gaps_roadmap.yaml` S4/S5 `automation_attempt_2026_06_22` for the full reproduction. Until a KRX login policy decision is made, keep feeding `etf_nav_manual` via `tools/import_etf_nav_manual.py` from manually downloaded KRX/KIND/운용사 CSV exports.
|
||||
|
||||
@@ -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 표본 누적 후 자동 평가
|
||||
|
||||
Reference in New Issue
Block a user