fix(broker): dcy-013 KIS 외부 취소 감지 + 부분 체결 회계 + stale 워치독 (BAT-017)
KIS 계좌에서 직접 매도 주문을 취소하면 poll_fill 이 cncl_yn 을 파싱하지 않아 슬롯이 pending_sell 에 영구히 갇히고 죽은 주문을 무한 폴링하던 버그(issue #9) 정정. 거버넌스: 전부 change-req (신규 REQ 없음, dcy-013 OQ-3). - REQ-012: KIS poll_fill cncl_yn 파싱 → 외부 취소 감지. FillStatus.cancelled /is_partial_cancel 추가. 체결 0 취소→cancelled, 부분 체결 후 취소→partial+cancelled - REQ-013: 집행 _on_partial_cancel — 체결분 원장 1회 + qty 차감(매도 잔여/매수 체결분) + holding 복귀 (OQ-1 A안). 전량 취소는 기존 롤백 경로 재사용 - REQ-018: 부분 체결분 원장 1회 기록 (_record_ledger 재사용) - REQ-031: 잔여 포지션 정합 연계 (slot.qty ↔ 실 잔고, 과매도 방지) - REQ-014: pending stale 워치독 Alert-only (TREX_PENDING_STALE_THRESHOLD, WARN+카운터, 상태 미변경) — 취소 감지 누락 backstop - REQ-015: interface-contract/runtime-flows 정합 전량 취소는 holding 자동 복구(limit_price clear→재매도 시장가, dcy-012 연계). KIS cncl_yn 실 규약은 운영 검증(미지 값 보수 처리 + 워치독 backstop). 검증: 단위 294 passed/32 skipped, 인프라 게이트(PG+RabbitMQ) 326 passed, ruff clean. 신규 test_kis_cancel(httpx stub) + test_execution_cancel(programmable broker). BAT-017 release-candidate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
# 0. 문서 상태
|
||||
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
|
||||
|
||||
- 상태: confirmed
|
||||
- 문서 ID: dcy-013_20260608_kis-external-cancel-pending-sell-stuck
|
||||
- 이슈명: KIS 외부 주문 취소 시 슬롯이 pending_sell 에 영구히 갇힘 — cncl_yn 미파싱
|
||||
- 작성 시각(KST): 2026-06-08 13:29 KST
|
||||
- 마지막 갱신 시각(KST): 2026-06-08 16:30 KST
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: REQ-012/013/018/031/014/015 (전부 change-req, 신규 REQ 없음 — OQ-3). REQ-013/018/015=CHG-20260608-02, REQ-012/031/014=CHG-20260608-01. 워치독 임계 env 는 REQ-014 에 흡수. delivery: BAT-017
|
||||
|
||||
# 1. 계획 상태 요약
|
||||
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
|
||||
|
||||
- 연계 문서 현황:
|
||||
- 관련 점수 파일: 미생성
|
||||
- 관련 TODO/메모: GitHub issue #9 (fourblocks2020/trex-core), 관련 issue #10(dcy-012, 매도 가격 버그)
|
||||
- 해석:
|
||||
- 실제 KIS `poll_fill` 이 일별주문체결조회의 취소여부(`cncl_yn`)를 파싱하지 않아, 계좌에서 직접(trex-core 외부) 매도 주문을 취소하면 슬롯이 `pending_sell` 에 영구히 갇히고 데몬이 죽은 주문을 매 tick 무한 폴링하는 버그를 정식 SDLC 주기로 착수한다.
|
||||
- 이번 Discovery에서는 KIS 어댑터의 취소 감지 갭을 메우는 핵심 수정과, pending 주문의 stale 처리(워치독/타임아웃) 보강 범위를 판정한다. 이미 Implemented·released(REL-001/003) 된 집행/브로커 경로(dcy-003) 변경이므로, 거버넌스는 OQ-3 결정에 따라 기존 REQ(REQ-012/013/018/031/014/015) `change-req` 로 처리하고 신규 REQ 는 원칙적으로 생성하지 않는다(워치독 임계 env 는 조건부).
|
||||
|
||||
# 2. 요구사항 판정 결과
|
||||
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
|
||||
|
||||
- 판정: 신규
|
||||
- 근거
|
||||
- 기존 Discovery(dcy-001~dcy-012) 중 KIS 외부 취소 감지(`cncl_yn` 파싱)나 pending 주문 stale 워치독을 다룬 문서는 없다. dcy-003(broker execution+outbox)은 집행 루프·롤백 경로를 도입했고, dcy-012(issue #10)는 매도 *가격* 버그를 다뤘으나 `poll_fill` 의 취소 *감지* 갭은 명시적으로 범위 밖(후속 이슈)으로 분리했다.
|
||||
- 변경이 이미 Implemented·released 된 KIS 어댑터(`poll_fill`)와 집행 reconciliation 의미를 바꾸므로, 기존 Discovery 미세 조정이 아니라 독립 주기로 추적하는 것이 명확하다.
|
||||
|
||||
# 3. 문제점의 요약
|
||||
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
|
||||
|
||||
- 현재 상태
|
||||
- 취소를 종료 상태로 다루는 설계는 이미 존재한다 — `trex_core/application/broker_port.py` 의 `FillStatus.is_terminal_reject`(broker_port.py:63~64)는 `rejected`·`cancelled` 를 모두 포함하고, `_poll`(execution.py:113~)은 `is_terminal_reject` 면 `_rollback`(execution.py:198~)을 호출해 `PENDING_SELL → HOLDING` 으로 되돌리고 `clear_dispatch` 로 `broker_order_id` 를 비운다(execution.py:207~210).
|
||||
- 그러나 실제 KIS `poll_fill`(`trex_core/adapters/broker/kis.py:163~208`)은 취소여부(`cncl_yn`)를 파싱하지 않는다. 계좌에서 직접 취소하면 일별주문체결조회(`inquire-daily-ccld`)에 주문 row 는 남지만 체결수량(`tot_ccld_qty`)이 0이므로, `if ccld_qty <= 0: return FillStatus(state="accepted", ...)`(kis.py:191~192)로 빠져 **`accepted`(접수됨)** 로 판정된다. `_poll` 은 `accepted` 를 "다음 tick 대기"로 처리해 슬롯이 영구히 `pending_sell` 에 머문다.
|
||||
- 부분 체결 후 취소(`0 < ccld_qty < ord_qty`)도 같은 문제다 — `partially_filled`(kis.py:207~)로 판정되어 역시 "다음 tick 대기"로 처리되어 갇힌다.
|
||||
- paper 브로커는 취소를 올바르게 처리한다 — `trex_core/adapters/broker/paper.py:74~75` 에서 `o.cancelled` 면 `FillStatus(state="cancelled")` 를 반환한다. 즉 갭은 **실제 KIS 어댑터에만** 존재한다.
|
||||
- baseline 문서(`docs/project-structure.md`, `docs/runtime-flows.md`)는 존재하며 최신이다. 집행 폴/롤백은 기존 runtime flow 에 포함돼 있고, 이번 변경은 KIS 어댑터의 취소 감지 단계를 메운다.
|
||||
- 기대 상태
|
||||
- KIS `poll_fill` 이 외부 취소를 `FillStatus(state="cancelled")` 로 매핑하면, 기존 롤백 경로가 동작해 슬롯이 `HOLDING` 으로 복귀하고 `broker_order_id` 가 비워져 자동 회복된다. (dcy-012 구현 가정 — HOLDING 진입 시 `limit_price` 가 clear 되어 재매도는 시장가로 나가 체결성이 보장된다.)
|
||||
- 죽은 주문에 대한 무한 1초 폴링이 멈추고, 운영자 수동 개입 없이 자동 복구된다. 보강책으로 pending 주문이 비정상적으로 오래 머무는 경우 stale 처리(워치독/타임아웃)가 안전망이 된다.
|
||||
- 이해관계자
|
||||
- 문서 작성자(AI agent) — Discovery/REQ/Batch 산출물 작성
|
||||
- 리뷰어/승인자 — KIS 어댑터·reconciliation 변경 승인
|
||||
- 운영 담당자 — 외부 취소·갇힘 상황의 자동 복구로 수동 개입 부담 감소
|
||||
- 전략 모듈 — 슬롯이 자동 복구되어 일관된 상태를 관측
|
||||
- 제약사항
|
||||
- 이미 released(REL-001/003) 된 KIS 어댑터/집행 경로 변경이므로 ad-hoc 수정이 아니라 정식 SDLC(change-req 또는 신규 REQ) 경유가 필요하다.
|
||||
- KIS `inquire-daily-ccld` 응답의 실제 `cncl_yn`(또는 동등) 필드명·값 규약을 실데이터로 확인해야 정확히 매핑할 수 있다.
|
||||
- 부분 체결 후 취소는 체결분 원장 귀속과 잔여 포지션 정합을 함께 고려해야 한다(단순 롤백으로 과매도 위험).
|
||||
|
||||
## 주요 사용자 시나리오
|
||||
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
|
||||
|
||||
- 운영자가 KIS HTS/MTS 에서 trex-core 가 낸 매도 주문을 직접 취소한다 → core 가 다음 tick 에 취소를 감지해 슬롯을 `HOLDING` 으로 복귀시키고, 필요 시 재매도(시장가)한다.
|
||||
- 부분 체결 후 운영자가 잔량을 취소한다 → 체결분은 원장에 기록되고 잔여 포지션만 정합 처리되어 과매도가 발생하지 않는다.
|
||||
- 어떤 이유로 pending 주문이 비정상적으로 오래 머문다 → 워치독/타임아웃이 stale 로 감지해 reconciliation 또는 경고를 발생시킨다.
|
||||
|
||||
# 4. 이번에 정의할 변경
|
||||
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
|
||||
|
||||
- KIS `poll_fill` 이 `inquire-daily-ccld` 응답의 취소여부(`cncl_yn` 등)를 파싱해, 취소된 주문(체결 0 또는 부분 체결 후 취소)을 `FillStatus(state="cancelled")` 로 매핑한다 — 기존 `is_terminal_reject` → `_rollback` 경로가 동작하도록.
|
||||
- 부분 체결 후 취소(OQ-1 A안): 체결분 원장 기록 + 슬롯 qty 차감 + 잔여 HOLDING 복귀의 부분 체결 회계를 도입한다(과매도/원장 누락 방지).
|
||||
- (OQ-2 — Alert-only) pending 주문 stale 워치독을 도입한다 — 임계 초과 시 WARN/메트릭 가시화만, 상태 미변경(auto-remediation 은 후속).
|
||||
- 인터페이스/런타임 baseline 문서에 취소 감지·복구 흐름을 반영한다.
|
||||
|
||||
## 영향 범위
|
||||
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
|
||||
|
||||
- 코드 영향:
|
||||
- `trex_core/adapters/broker/kis.py` — `poll_fill`(163~208) 에 `cncl_yn` 파싱 + `state="cancelled"` 매핑(핵심 갭)
|
||||
- `trex_core/application/broker_port.py` — `FillStatus` 계약(필요 시 부분 체결+취소 표현 보강)
|
||||
- `trex_core/application/execution.py` — 완전 취소(체결 0)는 기존 `_poll`/`_rollback` 경로 자동 재사용. 부분 체결+취소(OQ-1 A안)는 체결분 원장 기록 + 슬롯 qty 차감 + 잔여 HOLDING 복귀의 부분 체결 회계 경로 신규(`_on_filled`/`_record_ledger` 가 전량 체결만 다루던 것을 부분에도 확장)
|
||||
- `trex_core/domain/slot/` — 슬롯 qty 부분 차감 후 HOLDING 복귀 전이(잔여 수량 갱신)
|
||||
- (워치독 채택 시) pending stale 감지 컴포넌트 — 집행 reconciliation 경로
|
||||
- 문서 영향:
|
||||
- `docs/interface-contract.md` — KIS poll/fill 상태 매핑(취소 포함) 반영
|
||||
- `docs/runtime-flows.md` — 외부 취소 감지 → 롤백 → 재매도/회복 흐름 반영
|
||||
- 운영 영향:
|
||||
- 외부 취소·갇힘의 자동 복구로 수동 개입·불필요 폴링 감소
|
||||
- (워치독) stale 임계·알람 운영 파라미터 추가 가능
|
||||
|
||||
# 5. 요구사항 목록
|
||||
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
|
||||
|
||||
## 기능 요구사항
|
||||
- FR-1: KIS `poll_fill` 은 `inquire-daily-ccld` 응답의 취소여부를 파싱해, 외부 취소된 주문(체결 0)을 `FillStatus(state="cancelled")` 로 매핑한다. 이로써 기존 `is_terminal_reject → _rollback` 경로가 동작해 슬롯이 `HOLDING` 으로 자동 복귀한다.
|
||||
- FR-2: 부분 체결 후 취소(`0 < ccld_qty < ord_qty`)를 감지해 (OQ-1 A안) ① 체결분(`ccld_qty`)을 원장에 1회 기록, ② 슬롯 qty 를 `ccld_qty` 만큼 차감, ③ 잔여 qty 로 `HOLDING` 복귀해 잔여분만 재매도 가능하게 한다. `avg_price` 는 유지하여 잔여 포지션 정합을 보장하고 과매도/원장 누락을 방지한다.
|
||||
- FR-3: (OQ-2 — Alert-only) pending 주문이 configurable 임계 시간을 초과해 머물면 stale 로 감지해 WARN 로그·메트릭으로 가시화한다. 슬롯/주문 상태는 변경하지 않는다(자동 롤백/취소는 후속). cncl_yn 매핑 오류(R-1) 등 미지 원인의 무한 갇힘을 silent 가 아니라 운영자에게 노출하는 안전망이다.
|
||||
- FR-4: `docs/interface-contract.md` 와 `docs/runtime-flows.md` 에 취소 감지·복구(및 워치독) 흐름을 반영한다.
|
||||
|
||||
## 비기능 요구사항
|
||||
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
|
||||
|
||||
- NFR-1: 자동 회복성 — 외부 취소된 슬롯이 운영자 개입 없이 다음 tick 내에 `HOLDING` 으로 복구되어 `pending_sell` 영구 갇힘이 발생하지 않아야 한다.
|
||||
- NFR-2: 자원 효율 — 죽은(취소된) 주문에 대한 무한 폴링이 중단되어 불필요한 KIS API 호출이 발생하지 않아야 한다.
|
||||
- NFR-3: 정합성 — 부분 체결 후 취소에서 체결분이 원장에 정확히 귀속되고 잔여 포지션이 실제 잔고와 일치해야 한다(과매도/이중매도 금지).
|
||||
|
||||
## 범위 경계
|
||||
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
|
||||
|
||||
- In Scope
|
||||
- KIS `poll_fill` 의 `cncl_yn` 파싱 + `cancelled` 매핑(핵심 갭)
|
||||
- 부분 체결 후 취소 처리(OQ-1 A안: 체결분 원장 기록 + qty 차감 + 잔여 HOLDING 복귀)
|
||||
- pending stale 워치독(OQ-2 Alert-only: WARN/메트릭 가시화)
|
||||
- 인터페이스/런타임 문서 갱신
|
||||
- Out of Scope
|
||||
- buy 경로·paper 브로커 동작 변경(paper 는 이미 정상)
|
||||
- 매도 가격 정책(dcy-012 에서 처리 — 본 주기는 취소 *감지*만)
|
||||
- 워치독 auto-remediation(타임아웃 시 자동 롤백/취소) — 정상 slow fill·지정가 대기 상호작용·KIS 취소 능력 필요로 후속 분리
|
||||
|
||||
# 6. 리스크/가정 목록
|
||||
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
|
||||
|
||||
## 리스크
|
||||
- R-1 (High): KIS `inquire-daily-ccld` 의 실제 취소 표현(필드명/값)이 문서 가정과 다르면 취소를 여전히 `accepted` 로 오판해 갇힘이 재현된다.
|
||||
- 완화 방안: 실 응답 샘플(또는 KIS 명세)로 `cncl_yn` 등 필드를 확정하고, 미지 응답은 `unknown` 으로 보수 처리하되 OQ-2 의 Alert-only 워치독으로 무한 갇힘을 가시화하는 안전망을 둔다.
|
||||
- R-2 (Medium): 외부 취소 → 롤백 → 재매도가 반복되면(운영자가 계속 취소) safety-sell 이 무한 재매도 루프를 돌 수 있다.
|
||||
- 완화 방안: dcy-010(REQ-052) 의 per-slot 재시도 쿨다운/영속 거부 경고가 재매도 빈도를 제어하는지 확인하고, 필요 시 cancelled 도 쿨다운 트리거에 포함한다.
|
||||
- R-3 (Medium, OQ-1 A안으로 완화): 부분 체결 후 취소를 단순 롤백하면 이미 팔린 수량을 다시 팔아 과매도가 날 수 있다.
|
||||
- 완화 방안: OQ-1 A안(체결분 원장 기록 + 슬롯 qty 차감 + 잔여 HOLDING 복귀)으로 잔여분만 재매도되어 과매도가 구조적으로 차단된다. 부분 체결 회계 정확성은 REQ-018(원장)·REQ-031(포지션 정합) 검증과 연계해 보장한다.
|
||||
|
||||
## 가정
|
||||
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
|
||||
|
||||
- A-1: dcy-012 구현이 완료되어 슬롯 가격 필드가 `limit_price` 로 통일되고 HOLDING 진입 시 clear 되므로, 취소 후 롤백된 슬롯의 재매도는 시장가로 나가 체결성이 보장된다.
|
||||
- A-2: `FillStatus.is_terminal_reject` 가 이미 `cancelled` 를 포함하고 `_rollback` 이 `PENDING_SELL → HOLDING` 을 수행하므로, KIS 어댑터가 `cancelled` 만 정확히 반환하면 application 경로 변경 없이 회복이 성립한다.
|
||||
- A-3: paper 브로커는 이미 취소를 올바르게 처리하므로 본 변경은 KIS 어댑터(+필요 시 부분취소/워치독)에 국한된다.
|
||||
|
||||
## 오픈 질문
|
||||
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
|
||||
|
||||
- OQ-1: 부분 체결 후 취소 처리 정책 — 체결분을 원장에 기록하고 잔여 수량만 정합(예: HOLDING 잔량 갱신) 처리하는가, 아니면 다른 규칙을 쓰는가?
|
||||
- 상태: Answered (2026-06-08, 사용자 결정 — A안: 한 번에 올바르게(정확 회계))
|
||||
- 결정: 부분 체결 후 취소 감지 시 ① 체결분(`ccld_qty`)을 원장에 1회 기록, ② 슬롯 qty 를 `ccld_qty` 만큼 차감, ③ 잔여 qty 로 `HOLDING` 복귀(재매도는 잔여분만)한다. `avg_price`(주당 원가)는 그대로 유지되어 잔여 포지션도 정합하며, 과매도·원장 누락을 동시에 방지한다. 단순 롤백(C)·격리 후속(B)은 미채택.
|
||||
- 반영 위치: FR-2, 4. 변경 항목, 영향 범위, R-3
|
||||
- OQ-2: pending stale 워치독/타임아웃을 이번 주기에 포함하는가, 후속으로 분리하는가? 포함 시 임계 기준은?
|
||||
- 상태: Answered (2026-06-08, 사용자 결정 — 포함, Alert-only)
|
||||
- 결정: pending 주문이 임계 시간을 초과해 머물면 stale 로 감지해 WARN 로그·메트릭으로 **가시화만** 하고 슬롯/주문 상태는 변경하지 않는다(Alert-only). cncl_yn 매핑이 틀려도(R-1) 무한 갇힘이 silent 가 아니라 운영자에게 노출되는 안전망. 타임아웃 시 자동 롤백/취소(auto-remediation)는 정상 slow fill·지정가 대기와의 상호작용 및 KIS 주문 취소 능력이 필요해 후속으로 분리. 임계는 configurable(시장가 매도는 짧게, 지정가 매도는 관대하게 또는 단일 보수적 임계).
|
||||
- 반영 위치: FR-3, 범위 경계, R-1 완화
|
||||
- OQ-3: 거버넌스 경로 — 기존 REQ(REQ-012 KIS 어댑터, REQ-014 reconciliation 등)의 change-req 로 처리할지, 신규 REQ 로 추적할지?
|
||||
- 상태: Answered (2026-06-08, 사용자 결정 — change-req 우선)
|
||||
- 결정: 기존 Implemented REQ 본문 수정(change-req)으로 처리한다. change-req 대상 — REQ-012(KIS 어댑터: `cncl_yn` 파싱 + `cancelled` 매핑), REQ-013(주문 집행 루프: 부분 체결 회계 — qty 차감 + 잔여 HOLDING 복귀), REQ-018(체결→원장 기록: 부분 체결분 1회 기록), REQ-031(포지션 정합: 잔여 포지션 정합 연계), REQ-014(주문 핸드오프 durability + reconciliation: pending stale 워치독 Alert-only), REQ-015(브로커/집행 baseline 문서 정합: interface-contract/runtime-flows 갱신). 원칙적으로 신규 REQ 는 생성하지 않는다 — 단 워치독 임계 env 설정이 별도 추적을 요하면 REQ 정제에서 소형 config 신규 REQ 를 둘 수 있다(조건부).
|
||||
- 반영 위치: 1. 거버넌스 해석, R-1 완화 방안, 8. 확인 체크
|
||||
|
||||
### 오픈 질문 처리 기준
|
||||
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
|
||||
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
|
||||
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
|
||||
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
|
||||
|
||||
# 7. 초기 성공 기준
|
||||
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
|
||||
|
||||
- S-1: 외부 취소된 매도 주문이 다음 tick 에 `cancelled` 로 감지되어 슬롯이 `HOLDING` 으로 복귀하고 `broker_order_id` 가 비워진다(영구 갇힘 0건).
|
||||
- S-2: 취소된(죽은) 주문에 대한 추가 KIS 폴링이 발생하지 않는다.
|
||||
- S-3: 부분 체결 후 취소 시 체결분이 원장에 1회 기록되고 잔여 포지션이 실제 잔고와 일치한다(과매도/이중기록 0건).
|
||||
- S-4: pending 주문이 임계 시간을 초과하면 워치독이 WARN/메트릭으로 가시화한다(미지 원인 무한 갇힘이 silent 로 남지 않음). 슬롯/주문 상태는 변경하지 않는다.
|
||||
|
||||
## 측정 방식 및 데이터 출처
|
||||
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
|
||||
|
||||
- S-1 측정 방식: KIS 어댑터 단위 테스트(취소 응답 fixture → `cancelled`) + 집행 통합 테스트(롤백→HOLDING) 통과 / 데이터 출처: `poll_fill` 테스트, 슬롯 상태 스냅샷, `inquire-daily-ccld` 응답 fixture
|
||||
- S-2 측정 방식: 취소 감지 후 해당 slot 의 poll 호출 중단 확인 / 데이터 출처: 폴링 호출 로그/카운터
|
||||
- S-3 측정 방식: 부분 체결+취소 시나리오 테스트에서 원장 1건·잔여 수량 정합 확인 / 데이터 출처: ledger_outbox/포지션 정합(REQ-031) 검증, 슬롯 qty
|
||||
- S-4 측정 방식: 임계 초과 pending 주입 테스트에서 WARN/메트릭 발생 및 상태 미변경 확인 / 데이터 출처: 워치독 테스트, 데몬 로그/메트릭, 슬롯 상태 스냅샷
|
||||
|
||||
# 8. REQ로 넘기기 전 확인 체크
|
||||
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
|
||||
|
||||
- [x] 범위(In Scope/Out of Scope) 확정
|
||||
- 기준: 부분취소 정책(OQ-1 A안)·워치독 포함 여부(OQ-2 Alert-only)·거버넌스(OQ-3) 모두 확정됨
|
||||
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- 기준: S-1~S-4 의 테스트/검증 출처가 실제 실행 가능한 형태로 지정됨
|
||||
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- 기준: R-1(취소 필드 규약 확인)의 change-req 대상(REQ-012/013/018/031/014/015)이 OQ-3 으로 확정됨. 해소 실무 책임자 배정·DATA(취소 필드 확인)는 REQ/batch planning 단계로 넘김(비차단).
|
||||
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: OQ-1~OQ-3 모두 `Answered`, `Open` 0개
|
||||
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- 기준: Confirmed By = Justin (사람 승인자 지정 완료)
|
||||
|
||||
# 9. 파일 처리 결과
|
||||
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
|
||||
|
||||
- 처리 결과: 생성
|
||||
- 생성 경로: docs/discovery/dcy-013_20260608_kis-external-cancel-pending-sell-stuck.md
|
||||
- 참조 문서:
|
||||
- GitHub issue #9 (fourblocks2020/trex-core) — KIS 외부 취소 시 pending_sell 갇힘
|
||||
- docs/discovery/dcy-003_20260604_broker-execution-and-outbox.md — 집행 폴/롤백 경로 도입 근거
|
||||
- docs/discovery/dcy-012_20260608_sell-order-pricing-entry-price-bug.md — 매도 가격 버그(같은 pending_sell 갇힘 증상의 다른 원인, A-1)
|
||||
- docs/discovery/dcy-010_20260607_safety-sell-persistent-rejection-backoff.md — per-slot 재시도 쿨다운(재매도 루프 완화, R-2)
|
||||
- trex_core/adapters/broker/kis.py:163-208 — `poll_fill` 취소 미파싱 갭
|
||||
|
||||
# 10. 사용자 결정 필요 항목 요약
|
||||
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
|
||||
|
||||
## DECIDE
|
||||
- (없음 — OQ-1/OQ-2 확정으로 해소)
|
||||
|
||||
> 결정 완료(2026-06-08): OQ-1 부분 체결 후 취소 = A안(체결분 원장 기록 + 슬롯 qty 차감 + 잔여 HOLDING 복귀, 정확 회계) / OQ-2 워치독 = 포함(Alert-only: WARN/메트릭 가시화, auto-remediation 후속). 결정 항목에서 제외됨.
|
||||
|
||||
## CONFIRM
|
||||
- 없음 — 거버넌스 경로는 OQ-3 으로 확정됨(REQ-012/013/018/031/014/015 change-req, 신규 REQ 원칙적 없음).
|
||||
|
||||
## DATA
|
||||
- (R-1 연계, REQ/delivery 단계 확인) KIS `inquire-daily-ccld` 응답의 실제 취소 표현(필드명 `cncl_yn` 등·값 규약). 구현/검증 단계에서 실 응답·KIS 명세로 확정할 데이터이며, 미지 응답은 `unknown` 보수 처리 + 워치독 가시화로 안전망이 있어 Discovery confirm 을 차단하지 않는다.
|
||||
|
||||
# 11. Discovery Freeze
|
||||
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
|
||||
|
||||
## 섹션 참조
|
||||
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
||||
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
||||
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
||||
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
||||
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||||
|
||||
## Freeze 플래그
|
||||
- Handoff Decision: 승인
|
||||
- Handoff Rationale: confirm-discovery 완료. 오픈 질문 OQ-1~OQ-3 모두 Answered (부분 체결 후 취소=A안 정확 회계 / 워치독=포함 Alert-only / 거버넌스=REQ-012/013/018/031/014/015 change-req, 신규 REQ 원칙적 없음). C 유형 플레이스홀더 0개, 저장소 사실관계 일치. DATA(KIS 취소 필드)는 REQ/delivery 단계 확인 비차단. REQ 초안 작성 인계 가능.
|
||||
- Ready for REQ Drafting: true
|
||||
- Confirmed By: Justin Ko
|
||||
- Confirmed At (KST): 2026-06-08 14:10 KST
|
||||
@@ -17,4 +17,5 @@
|
||||
| dcy-009_20260607_multi-instance-rabbitmq-topology | 20260607 | 인스턴스별 독립 RabbitMQ 채널 토폴로지 — trex-core 다중 인스턴스 운영을 위한 Topic Exchange + Fanout Exchange per-instance 큐 분리 | [Discovery](./dcy-009_20260607_multi-instance-rabbitmq-topology.md) | confirmed |
|
||||
| dcy-010_20260607_safety-sell-persistent-rejection-backoff | 20260607 | safety-sell 영속 거부 재시도 백오프 — 장 휴장·잔고 부족·규제 등 지속 거부 상황에서 KIS 주문 spam 방지를 위한 슬롯별 재시도 backoff/cooldown 정책 | [Discovery](./dcy-010_20260607_safety-sell-persistent-rejection-backoff.md) | confirmed |
|
||||
| dcy-011_20260608_buy-intent-portfolio-id-required-symbol-resolve | 20260608 | buy intent — portfolio_id를 필수 키로, symbol은 universe(SnapshotStore)에서 resolve (+ issue #11 portfolio_id 누락 체결 ledger silent skip 가시화 흡수) | [Discovery](./dcy-011_20260608_buy-intent-portfolio-id-required-symbol-resolve.md) | confirmed |
|
||||
| dcy-012_20260608_sell-order-pricing-entry-price-bug | 20260608 | 매도 주문이 현재가가 아닌 entry_price(매수가) 지정가로 나가는 버그 — sell dispatch 가격 정책 부재 (issue #10) | [Discovery](./dcy-012_20260608_sell-order-pricing-entry-price-bug.md) | confirmed |
|
||||
| dcy-012_20260608_sell-order-pricing-entry-price-bug | 20260608 | 매도 주문이 현재가가 아닌 entry_price(매수가) 지정가로 나가는 버그 — sell dispatch 가격 정책 부재 (issue #10) | [Discovery](./dcy-012_20260608_sell-order-pricing-entry-price-bug.md) | confirmed |
|
||||
| dcy-013_20260608_kis-external-cancel-pending-sell-stuck | 20260608 | KIS 외부 주문 취소 시 슬롯이 pending_sell 에 영구히 갇힘 — cncl_yn 미파싱 (issue #9) | [Discovery](./dcy-013_20260608_kis-external-cancel-pending-sell-stuck.md) | confirmed |
|
||||
Reference in New Issue
Block a user