Files
trex-core/docs/srs/Integration/req-038_stop-loss-take-profit-kis-polling.md
T
genichinandClaude Opus 4.8 6d7bca1460 feat(safety-sell): dcy-017 워치독 소유권 가시화 — snapshot 상태 + 발동 로깅 (BAT-021)
전략 이탈로 슬롯이 내부 청산 루틴(워치독) 소유로 넘어간 상태가 무성이라
운영자가 인지할 수 없던 문제(issue #12)를 가시화한다.

거버넌스: 전부 change-req (신규 REQ 없음, dcy-017 OQ-3; Redis 미채택).
- REQ-034/021: WatchdogStore.last_seen + snapshot RPC 응답에 holding 슬롯
  워치독 상태(expired/last_seen) read-only 동봉(best-effort)
- REQ-032: trex-core slots 가 expired 슬롯을 WATCHDOG-OWNED(전략 이탈) 표시
- REQ-037/038/039: safety-sell 워치독 소유 진입(WARN)/이탈(INFO) + 발동
  (forced_close·stop_loss=WARN, take_profit·market_close=INFO) 로그 +
  in-process 카운터(SafetySellService.counters), 구조화 필드(slot_id/
  portfolio_id/reason/phase/instance_key). per-tick 스킵은 카운터만
- REQ-042: runtime-flows/interface-contract 정합

best-effort 격리(NFR-1): 모든 가시화는 예외 격리 — 워치독 판정·청산 불변.
dcy-016 억제 읽기로 관측이 워치독을 리셋하지 않음(비침습).

검증: 단위 335 passed/34 skipped, 인프라 게이트(PG+RabbitMQ) 369 passed, ruff clean.
신규 test_safety_sell_visibility(6) + snapshot watchdog(1) + CLI(1). BAT-021 rc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 17:10:26 +09:00

12 KiB
Raw Blame History

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 주식현재가 시세 API(/uapi/domestic-stock/v1/quotations/inquire-price, tr_id FHKST01010100, 실전/모의 공용)로 polling해야 한다. (CHG-20260607-04: 시세 tr_id 정정 — 구 TTTC8397R/VTTC8397R는 실 KIS 에서 "없는 서비스 코드".)
  • 현재가 평가 기준값은 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 KIS 주식현재가 시세 endpoint(/uapi/domestic-stock/v1/quotations/inquire-price)를 tr_id FHKST01010100(실전/모의 공용)로 호출하고 output.stck_prpr 를 파싱한다. 실 KIS LIVE 호출이 rt_cd=0 + stck_prpr 를 반환함을 확인한다(구 TTTC8397R 는 rt_cd=1 "없는 서비스 코드" 로 거부됨).
  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 모의 환경에서는 stub 구현(set_current_price/get_current_price)으로 현재가를 반환한다(KIS 시세 호출 없음). 실 KIS(broker=kis) 는 FHKST01010100 시세 tr 사용.
  • 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 배포 편성.

CHG-20260607-04

  • Date: 2026-06-07
  • Author: Justin Ko

Change Summary

  • 현재가 시세 tr_id 정정: TTTC8397R(실전)/VTTC8397R(모의) → FHKST01010100(주식현재가 시세, 실전/모의 공용). Status Implemented → Approved (AC-7 evidence 무효).

Intent

  • 배포 전 실 KIS LIVE 진단(읽기 전용 시세 호출)에서 구 tr_id 가 rt_cd=1 "없는 서비스 코드 입니다"(OPSQ0002) 로 거부됨을 확인. 이 경우 get_current_price 가 항상 None 을 반환 → stop-loss/take-profit 가 LIVE 에서 조용히 미발동. 표준 시세 tr FHKST01010100 는 rt_cd=0 + stck_prpr=329000(005930) 정상 수신. mock/샌드박스로는 잡히지 않는 결함이라 LIVE 검증으로 포착.

Acceptance Criteria Delta

  • Updated:
    • AC-7: 시세 endpoint/tr_id 를 FHKST01010100(실전/모의 공용)로 정정. 실 KIS LIVE rt_cd=0 + stck_prpr 수신 검증 추가.
  • Added:
    • 없음
  • Removed:
    • 없음 (TTTC8397R/VTTC8397R 분기 폐기)

Impacted Area Delta

  • Docs:
    • 없음 (REQ 본문 + 후속 baseline 은 verification 단계 갱신)
  • Modules:
    • trex_core/adapters/broker/kis.py (_tr_price → FHKST01010100, live/mock 분기 제거)
  • Tests:
    • tests/adapters/broker/test_kis_price.py (AC-7 tr_id 단언 정정)

Delivery Trace Delta

  • Batches: BAT-011 (release-candidate — AC-7 재구현/재검증)
  • Releases: REL-003 (confirmed — 본 정정 반영 후 재확인 필요)

Revalidation Impact

  • Reverification Needed: yes (AC-7 한정; AC-1~6/8 은 paper/단위 evidence 유효)
  • Existing Implementation Invalidated: yes (AC-7 — 구 tr_id 코드/테스트)
  • Status Recommendation: Implemented → Approved (AC-7 재검증 전까지)
  • Follow-up Action: kis.py 수정 + LIVE 시세 재검증 + 단위 정정 → confirm-req-implemented 로 Implemented 재전환 → REL-003 재확인.

Validation Plan

  • 실 KIS LIVE get_current_price('005930') → Decimal(약 329000) 반환 확인(읽기 전용).
  • test_kis_price.py AC-7: _tr_price() == FHKST01010100 단언 + stck_prpr 파싱.
  • 단위 전체 회귀 + ruff.

CHG-20260607-05

  • Date: 2026-06-07
  • Author: Justin Ko

Change Summary

  • Status Approved → Implemented (confirm-req-implemented, CHG-04 정정 재검증 완료).

Intent

  • CHG-04 의 tr_id 정정(FHKST01010100)을 구현·재검증: kis.py _tr_price() 수정, test_kis_price.py AC-7 정정(7 passed), 단위 전체 236 passed/ruff clean, 실 KIS LIVE get_current_price('005930')=Decimal('329000')(rt_cd=0) 확인. AC-7 evidence 복구 → 전체 AC 충족.

Acceptance Criteria Delta

  • (변경 없음)

Impacted Area Delta

  • (변경 없음)

Delivery Trace Delta

  • Batches: BAT-011 (release-candidate)
  • Releases: REL-003 (confirmed — 정정 반영 후 재확인)

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: keep-current (Implemented)
  • Follow-up Action: REL-003 재확인 후 배포.

CHG-20260608-01

  • Date: 2026-06-08
  • Author: Justin Ko

Change Summary

  • (change-req, dcy-017/issue #12) SL/TP 발동 가시화: stop_loss 발동=WARN, take_profit 발동=INFO(_log_fire) + 카운터(fire:stop_loss/fire:take_profit). 구조화 필드(slot_id/portfolio_id/reason/phase/instance_key). 판정·KIS polling 동작 불변(관측만). BAT-021 재구현·재검증 완료 → Implemented 유지.

Intent

  • 손절/익절 발동을 무성에서 가시화(OQ-2: stop_loss=WARN 주의사건, take_profit=INFO).

Acceptance Criteria Delta

  • Added:
    • stop_loss(WARN)/take_profit(INFO) 발동 로그 + 카운터.

Impacted Area Delta

  • Modules: trex_core/application/safety_sell.py(check_prices 발동 가시화)
  • Tests: tests/application/test_safety_sell_visibility.py::test_take_profit_fire_logs_info

Delivery Trace Delta

  • Batches: BAT-021
  • Releases: none

Revalidation Impact

  • Reverification Needed: yes (BAT-021 에서 완료)
  • Existing Implementation Invalidated: no (관측만 추가)
  • Status Recommendation: Implemented (change-req → BAT-021 재검증)
  • Follow-up Action: confirm-req-implemented(BAT-021).

Validation Plan

  • BAT-021: take_profit 발동 INFO + 카운터 단위.