# req-038: stop-loss·take-profit KIS 가격 polling + 트리거 - Status: Implemented - Type: Integration - Priority: High - Owner: project maintainers ## Intent 워치독이 만료된 슬롯에 한해 KIS API(`TTTC8397R` 실전 / `VTTC8397R` 모의)로 현재가를 주기적으로 polling하고, `현재가 <= stop_loss_price`(손절) 또는 `현재가 >= take_profit_price`(익절) 조건 충족 시 자동 매도를 제출한다. 전략 모듈이 정상 활성 중인 슬롯에는 개입하지 않는다(dcy-007 FR-2, NFR-3, A-3). ## Requirement - 데몬은 `TREX_STOP_LOSS_POLL_INTERVAL(초)` 주기로, `stop_loss_price` 또는 `take_profit_price`가 설정되고 **워치독이 만료된** `holding` 슬롯들의 현재가를 KIS `TTTC8397R`(실전) / `VTTC8397R`(모의) API로 polling해야 한다. - 현재가 평가 기준값은 `stck_prpr`(직전 체결가) 필드를 사용한다. - `현재가 <= stop_loss_price` 조건 충족 시 손절 매도를 제출해야 한다. - `현재가 >= take_profit_price` 조건 충족 시 익절 매도를 제출해야 한다. - 두 조건은 동일 polling 루프에서 평가한다. - 워치독 활성(만료되지 않은) 슬롯과 임계값 미설정(`None`) 슬롯은 polling 대상이 아니다. - KIS API 호출 상한은 `워치독 만료 대상 슬롯 수 × (3600 / TREX_STOP_LOSS_POLL_INTERVAL)`로 산정한다. 이 상한이 KIS 쿼터를 초과할 경우 polling 간격 조정 AC를 이 REQ의 Notes에 명시하고 REQ 단계에서 상한 경고를 구현한다. - 자동 매도 제출은 `SlotService.sell()`(REQ-036)을 사용한다. ## Acceptance Criteria 1. Given `holding`, 워치독 만료, `stop_loss_price=Decimal("50000")`, `take_profit_price=None`인 슬롯 S가 있고 KIS polling 결과 `stck_prpr=49000`이면, When 가격 체크가 수행되면, Then S에 대해 `SlotService.sell()`이 호출된다(손절 트리거). 2. Given `holding`, 워치독 만료, `stop_loss_price=None`, `take_profit_price=Decimal("80000")`인 슬롯 S가 있고 KIS polling 결과 `stck_prpr=81000`이면, When 가격 체크가 수행되면, Then S에 대해 `sell()`이 호출된다(익절 트리거). 3. Given `holding`, 워치독 만료, `stop_loss_price=Decimal("50000")`, `take_profit_price=Decimal("80000")`인 슬롯 S가 있고 `stck_prpr=65000`이면, When 가격 체크가 수행되면, Then `sell()`이 호출되지 않는다(두 조건 모두 미충족). 4. Given `holding`, 워치독 **활성**인 슬롯 S가 있을 때, When 가격 polling이 수행되면, Then S에 대한 KIS API 호출이 발생하지 않는다(워치독 보호). 5. Given `stop_loss_price=None`이고 `take_profit_price=None`인 슬롯 S가 있을 때, When polling이 수행되면, Then S는 polling 대상에서 제외된다. 6. Given 이미 `pending_sell` 상태인 슬롯 S에 조건이 충족될 때, When 가격 체크가 수행되면, Then 중복 `sell()` 호출이 발생하지 않는다(REQ-036 idempotency). 7. Given 실전 모드(`broker=kis`)일 때, When 가격 polling을 수행하면, Then `TTTC8397R` endpoint를 사용한다. 모의 모드(`broker=paper`)일 때는 `VTTC8397R`을 사용한다. 8. Given `TREX_STOP_LOSS_POLL_INTERVAL=30`이고 워치독 만료 대상 슬롯이 N개일 때, When 정상 동작 중이면, Then 1시간당 KIS API 호출 수가 `N × 120`회를 넘지 않는다(NFR-3 쿼터 산정). ## Impacted Area - Docs: - `docs/runtime-flows.md` — stop-loss·take-profit 가격 체크 흐름 추가 (REQ-042 참조) - `docs/interface-contract.md` — 현재가 polling 어댑터 계약 명시 (REQ-042 참조) - Modules: - 신규 `trex_core/adapters/broker/kis_price.py` 또는 `kis.py` 확장 — `TTTC8397R`/`VTTC8397R` 현재가 polling, `stck_prpr` 파싱 - `trex_core/runtime/daemon.py` — stop-loss·take-profit 가격 체크를 safety-sell worker에 통합 - `trex_core/domain/slot/service.py` — stop-loss·take-profit 조건 평가 메서드(또는 worker 내 인라인) - Tests: - KIS 현재가 polling 어댑터 단위 테스트 — `stck_prpr` 파싱 확인, 실전/모의 분기 확인 - 가격 체크 단위 테스트(mock polling) — 손절/익절/미충족/워치독 보호 시나리오 ## Notes - KIS 현재가 polling 어댑터는 기존 `trex_core/adapters/broker/kis.py`에 `get_current_price(symbol)` 메서드로 추가하거나, 별도 모듈로 분리한다. batch planning에서 결정한다. - paper broker 모의 환경에서는 `VTTC8397R`를 사용하거나, stub 구현으로 현재가를 반환하는 방식을 batch에서 결정한다. - KIS 쿼터 초과 방지: 워치독 만료 슬롯 수가 임계값을 넘으면 polling 간격 자동 조정 또는 경고 로그를 구현한다. 구체적인 쿼터 수치는 KIS API 문서 기준으로 batch 설계에서 확정한다. - `TREX_STOP_LOSS_POLL_INTERVAL` env var는 REQ-041 참조. ## Change Log ### CHG-20260607-01 - Date: 2026-06-07 - Author: Justin Ko #### Change Summary - dcy-007 confirmed → REQ-038 초안 생성 #### Intent - stop-loss·take-profit KIS 가격 polling + 트리거(FR-2)를 독립 Integration REQ로 정규화 #### Acceptance Criteria Delta - Added: AC-1~8 전체 #### Impacted Area Delta - Docs: runtime-flows.md, interface-contract.md (REQ-042에서 처리) - Modules: kis 현재가 polling 어댑터(신규/확장), daemon.py, service.py - Tests: polling 어댑터 단위 + 가격 체크 시나리오 단위 (mock) #### Delivery Trace Delta - Batches: none (미할당) - Releases: none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: suggest-batch-reqs 후 batch 배정 ### CHG-20260607-02 - Date: 2026-06-07 - Author: Justin Ko #### Change Summary - Proposed → Approved (confirm-req) #### Intent - 승인 게이트 통과: 필수 필드·AC·Impacted Area·Change Log 완비 확인. dcy-007(confirmed) 범위 및 결정 사항과 모순 없음. #### Acceptance Criteria Delta - (변경 없음) #### Impacted Area Delta - (변경 없음) #### Delivery Trace Delta - Batches: none (미할당) - Releases: none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: suggest-batch-reqs 후 batch 배정 ### CHG-20260607-03 - Date: 2026-06-07 - Author: Justin Ko #### Change Summary - Status Approved → Implemented (confirm-req-implemented, 근거 batch BAT-011 release-candidate). #### Intent - BAT-011 verification.md 의 AC↔evidence(KIS `get_current_price` TTTC8397R/VTTC8397R·`stck_prpr` 파싱 + 손절/익절/미충족/워치독 보호/쿼터 산정/가격 미수신 skip)로 AC-1~8 충족 → 구현 완료 동기화. #### Acceptance Criteria Delta - (변경 없음) #### Impacted Area Delta - (변경 없음) #### Delivery Trace Delta - Batches: BAT-011 (release-candidate) - Releases: none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current (Implemented) - Follow-up Action: draft-release 로 BAT-011 배포 편성.