docs(proofline): approve KIS balance pagination spec
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: "dcy-0001"
|
||||
status: confirmed
|
||||
---
|
||||
|
||||
# KIS 잔고조회 페이지네이션 완전성
|
||||
|
||||
## Problem
|
||||
|
||||
`trex-core`의 `KISBroker.get_balance()`는 KIS 국내주식 잔고조회 응답을 한 번만 요청하고 첫 페이지의 `output1`만 반환한다. 실전 계좌는 호출당 최대 50건, 모의 계좌는 최대 20건을 반환하므로 보유 row가 이 한도를 넘으면 성공 응답이어도 후속 페이지의 종목이 누락된 불완전한 broker snapshot이 만들어진다.
|
||||
|
||||
이 snapshot을 사용하는 두 reconciliation 경로는 서로 독립적으로 잘못된 결과를 낼 수 있다. `reconcile_positions()`는 portfolio-hub 수량과 누락된 broker 수량을 비교해 `case_code=C` 경고를 만들 수 있고, `reconcile_core_holdings()`는 holding 종목을 broker flat으로 오인해 슬롯을 `error`로 격리할 수 있다. 문제는 reconciliation 정책 자체가 아니라 두 소비자에게 전달되는 KIS 잔고 snapshot의 완전성이다.
|
||||
|
||||
기대 상태는 KIS가 지시하는 모든 연속 페이지를 성공적으로 읽고 병합한 경우에만 완전한 잔고를 반환하고, 페이지 체인이 불완전하거나 진행하지 않으면 부분 잔고를 정상 결과로 반환하지 않는 것이다.
|
||||
|
||||
## Evidence
|
||||
|
||||
- GitHub issue #53, `https://github.com/fourblocks2020/trex-core/issues/53` (OPEN, 2026-08-06T14:54:08Z 갱신)은 운영 계좌에서 첫 페이지 `output1=50`, 후속 페이지 `output1=9`를 관찰했고, 두 번째 페이지의 7개 양수 보유 종목이 첫 페이지 전용 구현에서 누락됐다고 기록한다. 두 요청 모두 HTTP `200`, `rt_cd=0`이어서 인증·권한·KIS 업무 오류와 구별된다.
|
||||
- `trex_core/adapters/broker/kis.py:626-689`의 `KISBroker.get_balance()`는 최초 query에 빈 `CTX_AREA_FK100`/`CTX_AREA_NK100`을 설정한 뒤 `httpx.get()`을 한 번만 호출하고 그 응답의 `output1`만 변환해 반환한다. 응답의 `ctx_area_fk100`, `ctx_area_nk100`과 response header `tr_cont`를 읽는 경로가 없다.
|
||||
- 저장소의 KIS 계약 문서 `docs/broker/kis/rest/국내주식/주문/계좌/015_v1_국내주식-006_주식잔고조회.md:18-20,38,59-60,68,78-80`은 실전 최대 50건·모의 최대 20건, 최초 요청의 빈 `tr_cont`, 후속 요청의 `tr_cont=N`, 이전 응답 continuation key 재사용, response header `tr_cont`의 `F/M`(다음 데이터 있음) 및 `D/E`(마지막 데이터)를 명시한다.
|
||||
- 이슈 원문의 “첫 요청에 `tr_cont=F`”와 “`KIOK0510` 등으로 마지막 페이지 판정”은 위 계약과 다르다. 최초 요청은 공백/미지정이어야 하고, 후속 페이지 존재와 종료의 정본은 `msg_cd`가 아니라 response header `tr_cont`다. `KIOK0500`/`KIOK0510`은 운영 관찰 증거로만 취급한다.
|
||||
- `trex_core/application/reconciliation.py:100-199`의 `reconcile_core_holdings()`는 반환된 balance map에 holding 종목이 없으면 `broker_qty=0`으로 판단해 `holding → error`를 수행한다. `trex_core/application/reconciliation.py:202-280`의 `reconcile_positions()`는 별도로 portfolio-hub 수량과 broker 수량을 비교하고 불일치 시 `case_code=C` 경고를 기록한다.
|
||||
- 현재 tree `bdf9761b2d64166abfcf1ab5699800c37c076f04`에서 `.venv/bin/python -m pytest -q tests/adapters/broker/test_kis.py tests/application/test_holding_divergence.py tests/application/test_reconciliation.py`를 실행한 결과는 `33 passed, 1 skipped`였다. 기존 `get_balance()` 테스트는 실패 응답, 정상 빈 응답, 토큰 발급 실패를 다루지만 다중 페이지, continuation 진행, 중간 페이지 실패는 검증하지 않는다.
|
||||
|
||||
## Scope
|
||||
|
||||
- AC admission 분류는 새로운 장기 active 동작을 정의하는 `create`다. 현재 ProofLine AC inventory는 비어 있고, 이 Line은 KIS 잔고 snapshot 완전성에 대한 독립 PASS/FAIL 계약을 새로 만든다.
|
||||
- `KISBroker.get_balance()`는 최초 요청에서 continuation key를 비우고 `tr_cont`를 공백/미지정으로 유지하며, response header `tr_cont`가 `F` 또는 `M`이면 직전 응답의 `ctx_area_fk100`/`ctx_area_nk100`을 다음 query에 전달하고 후속 요청 header에 `tr_cont=N`을 사용한다.
|
||||
- response header `tr_cont`가 `D` 또는 `E`일 때 페이지 순회를 종료한다. `msg_cd` 값은 진단 정보일 뿐 pagination 제어 계약으로 사용하지 않는다.
|
||||
- 모든 성공 페이지의 `output1`을 읽고 동일 종목의 양수 `hldg_qty`를 종목별로 합산하며, 기존의 0수량 row 제외 동작을 유지한다.
|
||||
- 최초 페이지를 포함한 어느 페이지에서든 HTTP, KIS `rt_cd`, JSON shape, row shape 또는 continuation 계약이 실패하면 이미 모은 부분 결과를 반환하지 않고 기존의 정제된 `BrokerBalanceError` 경계로 실패한다.
|
||||
- response header가 다음 페이지를 요구하지만 continuation key가 비어 있거나 이전과 동일해 진행하지 않는 경우 무한 반복하지 않고 fail-closed 한다.
|
||||
- 단일 페이지, 실전/모의 page-size와 무관한 synthetic 다중 페이지, 종목별 병합, 중간 페이지 실패, 빈/반복 continuation key, 정상 빈 잔고를 단위 테스트로 검증한다.
|
||||
- 다중 페이지 결과를 사용하는 기존 reconciliation에서 후속 페이지 보유 종목이 `broker_qty=0`으로 누락되지 않는 회귀 경계를 검증한다.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- portfolio-hub 저장소 또는 API 변경
|
||||
- `case_code=B/C` 분류, warning persistence, `holding → error`를 포함한 reconciliation 정책 변경
|
||||
- KIS 인증정보, token cache, broker provider 또는 account ownership projection 변경
|
||||
- 잔고조회 외 주문·체결·시세 API의 pagination 일반화
|
||||
- `INQR_DVSN`, 보유수량 0 row 제외 등 이 Line과 무관한 기존 KIS query·정규화 정책 변경
|
||||
- 운영계좌에 대한 주문, 자동 슬롯 복구, 기존 warning/error 상태의 데이터 수정
|
||||
- 배포, release 또는 운영환경 검증
|
||||
|
||||
## Risks and Unknowns
|
||||
|
||||
### Risks
|
||||
|
||||
- 첫 페이지가 성공한 뒤 후속 페이지가 실패했을 때 부분 snapshot을 반환하면 현재 결함이 다른 형태로 유지된다. 완화책은 전체 페이지 체인이 성공한 경우에만 결과를 반환하는 atomic read 경계다.
|
||||
- KIS가 “다음 데이터 있음”을 표시하면서 빈 key나 동일 key를 반복하면 무한 호출이 발생할 수 있다. 완화책은 key 존재성과 진행성을 검증하고 위반 시 fail-closed 하는 것이다.
|
||||
- response body의 `msg_cd`를 종료 조건으로 고정하면 KIS 메시지 코드 변화에 취약하다. 완화책은 저장소에 기록된 공식 계약대로 response header `tr_cont`만 pagination 제어에 사용하고 메시지 코드는 진단용으로 한정하는 것이다.
|
||||
- 동일 종목이 여러 row 또는 페이지에 걸쳐 나타날 수 있으므로 마지막 row 덮어쓰기는 수량을 축소할 수 있다. 완화책은 유효한 양수 수량을 종목별로 합산하고 해당 경계를 테스트하는 것이다.
|
||||
Reference in New Issue
Block a user