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