# 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= 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.