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,49 @@
|
||||
# BAT-017 Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-017
|
||||
|
||||
## 설계 목표
|
||||
- KIS 외부 취소를 종료 상태로 정확히 매핑해 pending_sell 영구 갇힘(issue #9)을 자동 복구한다.
|
||||
- 부분 체결 후 취소를 정확 회계(과매도·이중기록 방지)하고, 미지 원인 갇힘을 Alert-only 워치독으로 가시화한다.
|
||||
|
||||
## 1. FillStatus 계약 확장 (REQ-012)
|
||||
- `FillStatus.cancelled: bool = False` 추가. `is_partial_cancel` 프로퍼티 = `cancelled and filled_qty > 0`.
|
||||
- 매핑 규칙:
|
||||
- 체결 0 + 외부 취소 → `state="cancelled"`(기존 `is_terminal_reject` True).
|
||||
- 부분 체결(0<ccld<ord) + 외부 취소 → `state="partially_filled"` + `cancelled=True`(is_terminal_reject False, is_partial_cancel True) + execution_id/fee/avg_price 운반.
|
||||
- 전량 체결 → `state="filled"`(취소 플래그 무시).
|
||||
|
||||
## 2. KIS poll_fill (REQ-012)
|
||||
- `inquire-daily-ccld` row 의 `cncl_yn` 을 파싱: `str(cncl_yn).strip().upper() == "Y"`. 미지/부재 → 비취소(보수적, R-1; stale 워치독이 backstop).
|
||||
- ccld_qty<=0: cancelled 면 `cancelled` 반환, 아니면 기존 `accepted`.
|
||||
- 0<ccld<ord: cancelled 면 execution_id(`kis-daily-ccld:{ord_dt}:{ord_gno_brno}:{odno}:{ccld_qty}`)+fee 합성 후 partial+cancelled 반환, 아니면 기존 partially_filled.
|
||||
- 전량 체결: 기존 filled 경로 불변.
|
||||
|
||||
## 3. 집행 처리 (REQ-013/018, OQ-1 A안)
|
||||
- `_poll` 분기 순서: `is_filled` → `is_partial_cancel` → `is_terminal_reject` → else(대기+stale 체크).
|
||||
- 전량 취소(`cancelled`, ccld 0): 기존 `_rollback` 재사용 — 매도 `pending_sell→holding`+clear_dispatch, 매수 `pending_buy→idle`. holding 진입 시 limit_price clear(dcy-012) → 재매도 시장가(A-1).
|
||||
- 부분 체결 후 취소 `_on_partial_cancel`:
|
||||
- 매도(PENDING_SELL): `remaining = slot.qty - filled`; `transit_slot(PENDING_SELL→HOLDING, qty=remaining)`(avg_price=None → cost basis 유지). 체결분(filled, 체결가) 원장 1회(`_record_ledger` 재사용).
|
||||
- 매수(PENDING_BUY): `transit_slot(PENDING_BUY→HOLDING, qty=filled, avg_price=fill)`. 체결분 원장 1회.
|
||||
- 공통: clear_dispatch + event emit. `r.filled += 1`, `_record_ledger` 가 `r.ledger_enqueued += 1`.
|
||||
- 멱등: `SlotStateMismatchError` 시 조용히 반환(다음 tick reconcile).
|
||||
|
||||
## 4. pending stale 워치독 (REQ-014, OQ-2 Alert-only)
|
||||
- ExecutionService `stale_pending_threshold_sec`(<=0/None=비활성). `_check_stale_pending(slot, now, r)`: `age = now - slot.updated_at`; `age >= threshold` 면 `r.stale_pending += 1` + WARN. **상태 불변**.
|
||||
- `slot.updated_at` 출처: mark_dispatched/transit 가 갱신 → "마지막 쓰기 이후 경과"가 곧 "pending 체류 시간" 근사.
|
||||
- config: `DaemonConfig.pending_stale_threshold_sec`(env `TREX_PENDING_STALE_THRESHOLD`, default 0). daemon 이 ExecutionService 에 주입.
|
||||
|
||||
## 5. 포지션 정합 연계 (REQ-031)
|
||||
- `_on_partial_cancel` 이 slot.qty 를 잔여 held 와 일치시켜 reconcile_positions 의 입력 정합을 유지. reconcile 코드 자체 변경 없음(연계 change-req).
|
||||
|
||||
## 6. baseline 문서 (REQ-015)
|
||||
- interface-contract: FillStatus.cancelled/is_partial_cancel + state별 집행 처리 + KIS cncl_yn 매핑.
|
||||
- runtime-flows: poll 단계 외부 취소(전량→롤백 / 부분→회계) + Alert-only stale 워치독 + limit_price clear 재매도.
|
||||
|
||||
## 자동 복구 성립 근거 (A-2)
|
||||
- `is_terminal_reject` 가 이미 `cancelled` 포함 + `_rollback` 이 pending_sell→holding 수행 → KIS 어댑터가 cancelled 만 정확히 반환하면 application 경로 재사용으로 복구(전량 취소 케이스는 코드 추가 없이 성립).
|
||||
|
||||
## 테스트 설계
|
||||
- `tests/adapters/broker/test_kis_cancel.py`(httpx stub): full cancel→cancelled, zero-fill non-cancel→accepted, partial+cancel→is_partial_cancel(execution_id 확인), full fill 회귀.
|
||||
- `tests/application/test_execution_cancel.py`(programmable broker): 전량 취소→holding 복귀+clear, 부분 취소→원장 1건+qty 차감+cost basis 유지, stale 워치독 WARN(상태 불변), 비활성 default.
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# BAT-017 Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-017
|
||||
|
||||
## 구현 요약
|
||||
- dcy-013 KIS 외부 취소 감지(issue #9) + 부분 체결 회계 + pending stale 워치독(Alert-only)을 전부 change-req 로 구현(신규 REQ 없음).
|
||||
|
||||
## 변경 내역
|
||||
|
||||
### REQ-012 — KIS 취소 감지 + FillStatus 확장
|
||||
- `trex_core/application/broker_port.py`: `FillStatus.cancelled: bool = False` + `is_partial_cancel`(=`cancelled and filled_qty > 0`).
|
||||
- `trex_core/adapters/broker/kis.py::poll_fill`: `cncl_yn` 파싱(`=="Y"`, 미지 값 보수적 비취소). 체결 0+취소 → `state="cancelled"`; 부분 체결+취소 → `state="partially_filled"` + `cancelled=True` + execution_id/fee 합성. 전량 체결 경로 불변.
|
||||
|
||||
### REQ-013/018 — 집행 부분 체결 회계
|
||||
- `trex_core/application/execution.py::_poll`: `is_filled` → `is_partial_cancel` → `is_terminal_reject` → else(대기+stale) 분기.
|
||||
- `_on_partial_cancel`: 매도 `PENDING_SELL→HOLDING qty=잔여`(cost basis 유지), 매수 `PENDING_BUY→HOLDING qty=체결분 avg_price=체결가`; `_record_ledger`(REQ-018 재사용)로 체결분 원장 1회 + clear_dispatch + event. `r.filled += 1`.
|
||||
- 전량 취소(`cancelled`, 체결 0)는 기존 `is_terminal_reject` → `_rollback`(holding 복귀 + clear_dispatch) 재사용.
|
||||
|
||||
### REQ-014 — pending stale 워치독 (Alert-only)
|
||||
- `execution.py`: `ExecutionService(stale_pending_threshold_sec=...)`(<=0/None 비활성), `_check_stale_pending`(age=now-updated_at, 초과 시 WARN + `r.stale_pending += 1`, 상태 불변), `ExecutionResult.stale_pending`.
|
||||
- `trex_core/runtime/config.py`: `DaemonConfig.pending_stale_threshold_sec`(env `TREX_PENDING_STALE_THRESHOLD`, default 0).
|
||||
- `trex_core/runtime/daemon.py`: ExecutionService 에 threshold 주입.
|
||||
|
||||
### REQ-031 — 포지션 정합 연계
|
||||
- `_on_partial_cancel` 이 slot.qty 를 잔여 held 와 일치시켜 정합 입력 유지(과매도 방지). reconcile_positions 코드 변경 없음.
|
||||
|
||||
### REQ-015 — baseline 문서
|
||||
- `docs/interface-contract.md`: FillStatus.cancelled/is_partial_cancel + state별 집행 처리 + KIS cncl_yn 매핑. Last Updated → BAT-017.
|
||||
- `docs/runtime-flows.md`: poll 단계 외부 취소(전량→롤백 / 부분→회계) + Alert-only stale 워치독. Last Updated → BAT-017.
|
||||
|
||||
## 테스트
|
||||
- 신규 `tests/adapters/broker/test_kis_cancel.py`(httpx stub): 4건 — full cancel→cancelled, zero-fill non-cancel→accepted, partial+cancel→is_partial_cancel(execution_id), full fill 회귀.
|
||||
- 신규 `tests/application/test_execution_cancel.py`(programmable broker): 4건 — 전량 취소→holding 복귀+clear, 부분 취소→sell 원장 1건+qty 6 잔여+cost basis 1000 유지, stale 워치독 WARN(상태 PENDING_SELL 불변), 비활성 default 무경고.
|
||||
|
||||
## 검증 실행
|
||||
- 단위: `TREX_BROKER=paper .venv/bin/python -m pytest -q` → `294 passed / 32 skipped`(`::test_sandbox_token_and_submit` deselect — LIVE `.env` 환경 의존, 본 batch 무관).
|
||||
- 인프라 게이트: `TREX_POSTGRESQL_TEST_DSN=...(trex_core_test) TREX_RABBITMQ_TEST_URL=... pytest -q` → `326 passed`.
|
||||
- `ruff check trex_core tests` → All checks passed.
|
||||
@@ -0,0 +1,36 @@
|
||||
# BAT-017: KIS 외부 취소 감지 + 부분 체결 회계 + pending stale 워치독 (issue #9)
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-013
|
||||
- Included REQ: REQ-012 (CHG-20260608-01), REQ-013 (CHG-20260608-02), REQ-018 (CHG-20260608-02), REQ-031 (CHG-20260608-01), REQ-014 (CHG-20260608-01), REQ-015 (CHG-20260608-02)
|
||||
|
||||
## Summary
|
||||
- dcy-013(KIS 외부 주문 취소 시 슬롯이 pending_sell 에 영구 갇힘 — cncl_yn 미파싱, issue #9)을 단일 standard batch 로 delivery 한다. 거버넌스는 dcy-013 OQ-3 결정대로 **전부 change-req, 신규 REQ 없음**(워치독 임계 env 는 REQ-014 에 흡수).
|
||||
- 핵심: KIS `poll_fill` 이 `inquire-daily-ccld` 의 `cncl_yn="Y"` 를 파싱해 외부 취소를 감지한다. 체결 0 취소 → `cancelled`(기존 롤백 경로로 holding 자동 복구), 부분 체결 후 취소 → `is_partial_cancel`(체결분 원장 1회 + qty 차감 + 잔여 holding 복귀, OQ-1 A안). 보강책으로 pending 주문 stale 워치독(OQ-2 Alert-only: WARN+카운터, 상태 미변경).
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- KIS `poll_fill` 의 `cncl_yn` 파싱 + `cancelled`/partial-cancel 매핑, `FillStatus.cancelled`/`is_partial_cancel` (REQ-012)
|
||||
- 집행 루프 부분 체결 회계 `_on_partial_cancel`(체결분 원장 + qty 차감 + holding 복귀), 전량 취소 롤백 재사용 (REQ-013)
|
||||
- 부분 체결분 원장 1회 기록(`_record_ledger` 재사용) (REQ-018)
|
||||
- 잔여 포지션 정합 연계(슬롯 qty ↔ 실 잔고, 과매도 방지) (REQ-031)
|
||||
- pending stale 워치독 Alert-only + `TREX_PENDING_STALE_THRESHOLD` env (REQ-014)
|
||||
- baseline 문서(interface-contract/runtime-flows) 정합 (REQ-015)
|
||||
- Out of Scope:
|
||||
- buy 경로·paper 브로커 동작 변경(paper 는 이미 취소 정상 처리)
|
||||
- 매도 가격 정책(dcy-012/BAT-016 에서 처리 — 본 주기는 취소 *감지*만)
|
||||
- 워치독 auto-remediation(타임아웃 시 자동 롤백/취소) — 정상 slow fill·지정가 대기 상호작용·KIS 취소 능력 필요로 후속 분리
|
||||
- 과거 갇힘 슬롯의 일괄 보정(운영 절차)
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- Source discovery 단일(dcy-013). OQ-1(A안 정확 회계)·OQ-2(워치독 Alert-only)·OQ-3(전부 change-req) Answered.
|
||||
- 자동 회복 성립 근거: `FillStatus.is_terminal_reject` 가 이미 `cancelled` 를 포함하고 `_rollback` 이 `pending_sell→holding` 을 수행(A-2) → KIS 어댑터가 취소만 정확히 반환하면 application 경로 재사용으로 복구.
|
||||
- dcy-012(BAT-016) 가정 충족: holding 진입 시 `limit_price` clear → 재매도 시장가(A-1).
|
||||
- R-1(KIS 취소 필드 규약): `cncl_yn` 가정. 실 응답 확인은 운영/검증 단계 — 미지 값 보수 처리 + stale 워치독(FR-3) backstop.
|
||||
@@ -0,0 +1,51 @@
|
||||
# BAT-017 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-017
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-012 (Integration, change-req): KIS `poll_fill` `cncl_yn` 파싱 + `cancelled`/partial-cancel 매핑, `FillStatus.cancelled`/`is_partial_cancel`.
|
||||
- REQ-013 (Integration, change-req): 집행 루프 `_on_partial_cancel`(체결분 원장 + qty 차감 + holding 복귀), 전량 취소 롤백 재사용.
|
||||
- REQ-018 (Integration, change-req): 부분 체결분 원장 1회 기록(`_record_ledger` 재사용).
|
||||
- REQ-031 (Exception, change-req): 잔여 포지션 정합 연계(슬롯 qty ↔ 실 잔고).
|
||||
- REQ-014 (Exception, change-req): pending stale 워치독 Alert-only + `TREX_PENDING_STALE_THRESHOLD`.
|
||||
- REQ-015 (Documentation, change-req): interface-contract/runtime-flows 정합.
|
||||
|
||||
## Delivery Plan
|
||||
1. REQ-012: `broker_port.py` FillStatus.cancelled + is_partial_cancel; `kis.py::poll_fill` cncl_yn 파싱(full cancel/partial cancel 매핑 + execution_id).
|
||||
2. REQ-013/018: `execution.py::_poll` is_partial_cancel 분기 + `_on_partial_cancel`(원장 1회 + qty 차감 + holding). 전량 취소는 기존 is_terminal_reject 롤백.
|
||||
3. REQ-014: `_check_stale_pending` + ExecutionResult.stale_pending + ExecutionService threshold; `config.py` `pending_stale_threshold_sec`/env; `daemon.py` 배선.
|
||||
4. REQ-031: 부분 회계가 잔여 qty 정합 유지(연계, reconcile 코드 무변경) 확인.
|
||||
5. REQ-015: interface-contract/runtime-flows 정합.
|
||||
6. 신규 테스트(test_kis_cancel, test_execution_cancel) + 전체 회귀 + ruff + 인프라 게이트.
|
||||
|
||||
## Dependencies
|
||||
- Internal:
|
||||
- dcy-003/BAT-004 broker execution(poll/rollback/REQ-012/013/014) — 변경 대상.
|
||||
- dcy-004/BAT-006 fill→ledger(REQ-018) — 부분 체결분 기록.
|
||||
- dcy-005/BAT-008 reconcile_positions(REQ-031) — 잔여 정합 연계.
|
||||
- dcy-012/BAT-016 — holding 진입 시 limit_price clear(A-1, 재매도 시장가).
|
||||
- External:
|
||||
- KIS `inquire-daily-ccld` cncl_yn 필드(R-1, 실 응답 확인은 운영/검증).
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: (a) 부분 체결 후 취소 회계(A안)는 FillStatus 계약 확장 + 집행 분기 + 원장 기록 + 슬롯 qty/cost basis 정합까지 교차 — 매도/매수 각각의 qty·avg_price 처리 규칙을 설계에서 못박지 않으면 과매도/이중기록(R-3) 위험. (b) 전량 취소 자동 복구가 기존 is_terminal_reject 롤백 재사용으로 성립하는지(A-2) 설계에서 확인. (c) stale 워치독은 Alert-only 경계(상태 미변경)·임계 env·타임스탬프 출처(slot.updated_at)를 설계에서 고정. → design.md 동일 턴 scaffold.
|
||||
|
||||
## Milestones
|
||||
1. M1 — REQ-012 FillStatus.cancelled + KIS cncl_yn 매핑, test_kis_cancel green
|
||||
2. M2 — REQ-013/018 부분 체결 회계 + 전량 취소 롤백, test_execution_cancel green
|
||||
3. M3 — REQ-014 stale 워치독 + config/daemon 배선
|
||||
4. M4 — REQ-031 잔여 정합 확인 + REQ-015 문서 정합
|
||||
5. M5 — 전체 회귀 + ruff + 인프라 게이트
|
||||
|
||||
## Risks
|
||||
- R-1 (High): KIS 실제 취소 표현(cncl_yn 필드/값)이 가정과 다르면 취소를 여전히 accepted 로 오판. 대응: 미지 값 보수 처리 + stale 워치독(FR-3) backstop. 실 응답 확인은 운영/검증 단계.
|
||||
- R-2 (Medium): 외부 취소→롤백→재매도 반복 시 safety-sell 무한 재매도. 대응: dcy-010(REQ-052) per-slot 쿨다운 확인, 필요 시 cancelled 도 쿨다운 트리거 포함(후속).
|
||||
- R-3 (Medium): 부분 체결 후 단순 롤백 시 과매도. 대응: A안(체결분 원장 + qty 차감 + 잔여 holding)으로 구조적 차단, cost basis 유지.
|
||||
|
||||
## Notes
|
||||
- 1차 게이트: 전체 회귀 green.
|
||||
- KIS 어댑터 단위는 httpx stub(fixture)로 cncl_yn 매핑 검증; 집행 회계는 programmable broker 로 FillStatus 주입 검증.
|
||||
- 실 KIS 외부 취소 e2e 는 실계좌 세션 필요로 비차단 이월(운영 검증).
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
# BAT-017 Verification
|
||||
|
||||
- Status: verified
|
||||
- Batch: BAT-017
|
||||
|
||||
## Acceptance Mapping
|
||||
|
||||
### REQ-012 (KIS 취소 감지, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| cncl_yn → cancelled (체결 0) | tests/adapters/broker/test_kis_cancel.py::test_full_external_cancel_maps_to_cancelled |
|
||||
| 비취소 zero-fill → accepted | ::test_zero_fill_not_cancelled_stays_accepted |
|
||||
| 부분 체결 + 취소 → is_partial_cancel + execution_id | ::test_partial_fill_then_cancel_flags_partial_cancel |
|
||||
| 전량 체결 회귀 | ::test_full_fill_unaffected_by_cancel_flag |
|
||||
|
||||
### REQ-013 (집행 부분 체결 회계, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| 전량 외부 취소 → holding 복귀 + clear (영구 갇힘 0) | tests/application/test_execution_cancel.py::test_full_external_cancel_returns_slot_to_holding |
|
||||
| 부분 취소 → qty 차감 + holding 잔여 + cost basis 유지 | ::test_partial_fill_then_cancel_records_and_keeps_remainder |
|
||||
| 기존 filled/reject 회귀 | tests/application/test_execution.py 전체 |
|
||||
|
||||
### REQ-018 (부분 체결분 원장 1회, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| 부분 취소 시 sell 원장 1건(qty=체결분, price=체결가) | test_execution_cancel.py::test_partial_fill_then_cancel_records_and_keeps_remainder |
|
||||
|
||||
### REQ-031 (포지션 정합 연계, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| 부분 취소 후 slot.qty = 잔여 held (과매도 0) | test_execution_cancel.py::test_partial_fill_then_cancel_records_and_keeps_remainder (qty==6) |
|
||||
| reconcile 로직 불변 회귀 | tests/application/test_reconciliation.py 전체 |
|
||||
|
||||
### REQ-014 (pending stale 워치독 Alert-only, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| 임계 초과 pending → WARN + 카운터, 상태 미변경 | test_execution_cancel.py::test_stale_pending_watchdog_warns_without_state_change |
|
||||
| 비활성(default) → 무경고 | ::test_stale_watchdog_disabled_by_default |
|
||||
|
||||
### REQ-015 (baseline 문서, change-req)
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| interface-contract/runtime-flows 정합 | 문서 diff(BAT-017) |
|
||||
|
||||
## Evidence
|
||||
- Test:
|
||||
- 단위 전체 회귀: `294 passed / 32 skipped`(신규 KIS 취소 4 + 집행 취소/워치독 4 포함; `::test_sandbox_token_and_submit` deselect).
|
||||
- 인프라 게이트(PG trex_core_test + RabbitMQ): `326 passed`.
|
||||
- `ruff check trex_core tests` → All checks passed.
|
||||
- Logs: `294 passed, 32 skipped, 1 deselected` / `326 passed, 1 deselected ~15.9s`.
|
||||
|
||||
## 성공 기준(dcy-013) 대응
|
||||
- S-1(외부 취소 → cancelled 감지 → holding 복귀, 영구 갇힘 0): test_full_external_cancel_maps_to_cancelled + test_full_external_cancel_returns_slot_to_holding.
|
||||
- S-2(죽은 주문 추가 폴링 없음): 취소 감지 후 clear_dispatch 로 broker_order_id 비워져 list_dispatched_pending 에서 제외(폴링 중단) — test_full_external_cancel_returns_slot_to_holding(broker_order_id is None).
|
||||
- S-3(부분 체결분 원장 1회 + 잔여 정합, 과매도/이중기록 0): test_partial_fill_then_cancel_records_and_keeps_remainder(sell 1건 + qty 6 + cost basis 1000).
|
||||
- S-4(임계 초과 pending stale 가시화, 상태 미변경): test_stale_pending_watchdog_warns_without_state_change.
|
||||
|
||||
## Carried-Over / 비차단
|
||||
- R-1(KIS `cncl_yn` 실제 필드/값 규약): `cncl_yn="Y"` 가정으로 구현, 실 응답 확인은 운영/검증 단계. 미지 값 보수 처리 + stale 워치독(FR-3) backstop 으로 무한 갇힘 가시화 안전망 확보.
|
||||
- 실 KIS 외부 취소 full-stack e2e: 실계좌 세션 필요로 이월(운영 검증, 비차단).
|
||||
- R-2(외부 취소→재매도 반복 루프): dcy-010 per-slot 쿨다운과 연계, cancelled 쿨다운 트리거 포함은 후속.
|
||||
- 워치독 auto-remediation: 후속 분리(OQ-2 Alert-only 결정).
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues:
|
||||
- 없음. 전량 취소 자동 복구·부분 체결 회계·stale 워치독 모두 단위 green(326 passed 회귀). LIVE sandbox 토큰 deselect 는 `.env` 환경 의존(본 batch 무관). KIS cncl_yn 실 규약은 운영 검증 비차단(보수 처리 + 워치독 backstop).
|
||||
@@ -24,3 +24,4 @@
|
||||
| BAT-014 | batch-lite | release-candidate | REQ-052, REQ-053, REQ-054 | dcy-010 | [bat-014](./bat-014_20260608_safety-sell-cooldown/) |
|
||||
| BAT-015 | standard | release-candidate | REQ-007 (CHG-20260608-01), REQ-018 (CHG-20260608-01), REQ-021 (CHG-20260608-01), REQ-055 | dcy-011 | [bat-015](./bat-015_20260608_buy-intent-portfolio-id-contract/) |
|
||||
| BAT-016 | standard | release-candidate | REQ-002 (CHG-20260608-01), REQ-003 (CHG-20260608-01), REQ-006 (CHG-20260608-01), REQ-007 (CHG-20260608-02), REQ-013 (CHG-20260608-01), REQ-015 (CHG-20260608-01), REQ-036 (CHG-20260608-01) | dcy-012 | [bat-016](./bat-016_20260608_sell-pricing-limit-price-rename/) |
|
||||
| BAT-017 | standard | release-candidate | REQ-012 (CHG-20260608-01), REQ-013 (CHG-20260608-02), REQ-018 (CHG-20260608-02), REQ-031 (CHG-20260608-01), REQ-014 (CHG-20260608-01), REQ-015 (CHG-20260608-02) | dcy-013 | [bat-017](./bat-017_20260608_external-cancel-detection-stale-watchdog/) |
|
||||
|
||||
Reference in New Issue
Block a user