fix(kis): REQ-038 현재가 tr_id 정정 + REL-003 확정 + dcy-008 kill-switch Discovery
## REQ-038 CHG-04/05: KIS 시세 tr_id 정정 (FHKST01010100)
- kis.py _tr_price(): TTTC8397R/VTTC8397R → FHKST01010100 (실전/모의 공용)
배포 전 실 KIS LIVE 진단에서 구 tr_id가 rt_cd=1 "없는 서비스 코드"로 거부됨을
확인 — stop-loss/take-profit이 LIVE에서 조용히 미발동하는 결함이었음.
FHKST01010100으로 정정 후 get_current_price('005930')=329000(rt_cd=0) 확인.
- test_kis_price.py: AC-7 테스트 정정 (FHKST01010100 공용 검증)
- REQ-038: CHG-04/05 Change Log 추가, Requirement/AC-7/Notes 정정
- BAT-011 implementation.md: LIVE 검증 및 tr_id 정정 이력 추가
- BAT-011 verification.md: AC-7 evidence를 CHG-04 정정 버전으로 갱신
- docs/srs/index.md: CHG-04/05 Change Log 항목 추가
## REL-003: core 자율 안전 매도 release 문서 생성
- docs/releases/rel-003_20260607_core-autonomous-safety-sell.md 신규 생성
(BAT-010 + BAT-011, app-service profile, Status: confirmed)
- docs/releases/index.md: REL-003 행 추가
## dcy-008: 비상 정지/강제 청산(kill-switch) Discovery 초안
- docs/discovery/dcy-008_20260607_emergency-stop-kill-switch.md 신규 생성
GitHub #3 기반; SlotService.sell() 재사용, 당일 전략 제외 day-scoped
FR 6개, NFR 3개, OQ 4개 (트리거 표면/영속 방식/수동 해제 결정 필요)
- docs/discovery/index.md: dcy-008 행 추가
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
5. baseline 3문서 정합(REQ-042).
|
||||
6. 전체 회귀: 단위 236 passed / 23 skipped, `ruff check` All passed.
|
||||
7. 인프라 게이트 포함(PG+RabbitMQ): 259 passed (마이그레이션 001~004 적용 + PG repo round-trip 포함).
|
||||
8. **배포 전 실 KIS LIVE 시세 검증(읽기 전용)** → REQ-038 시세 tr_id 결함 발견(`TTTC8397R` = "없는 서비스 코드", rt_cd=1). change-req REQ-038 CHG-04/05 로 `FHKST01010100`(실전/모의 공용) 정정 → `kis.py _tr_price` 수정 + `test_kis_price` AC-7 정정 → LIVE 재검증 `get_current_price('005930')=329000`(rt_cd=0). 단위 236 passed 재확인.
|
||||
|
||||
## Validation
|
||||
- 단위: `pytest -q --deselect ...::test_sandbox_token_and_submit` → 236 passed / 23 skipped.
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
| AC-4: 워치독 활성 → KIS 호출 0 | `test_watchdog_active_no_kis_call` (broker.calls == []) |
|
||||
| AC-5: 임계값 None → 제외 | `test_no_thresholds_excluded` |
|
||||
| AC-6: pending_sell 중복 미호출 | `test_idempotent_on_pending_sell` |
|
||||
| AC-7: 실전 TTTC8397R / 모의 VTTC8397R | `test_kis_price.py::test_tr_price_live_vs_mock` + `test_get_current_price_parses_stck_prpr` |
|
||||
| AC-7 (CHG-04): 시세 tr `FHKST01010100`(실전/모의 공용) + stck_prpr | `test_kis_price.py::test_tr_price_is_quotations_tr` + `test_get_current_price_parses_stck_prpr`; **실 KIS LIVE 검증**: `get_current_price('005930')=Decimal('329000')`(rt_cd=0). 구 `TTTC8397R` 는 LIVE rt_cd=1 "없는 서비스 코드" 로 폐기 |
|
||||
| AC-8: 시간당 호출 ≤ N×(3600/interval) | `test_quota_estimate`(N=3,interval=30→360) + `test_only_eligible_slots_polled`(워치독 만료+임계 슬롯만 poll) |
|
||||
| 가격 미수신 → skip | `test_price_unavailable_skips` |
|
||||
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
# 0. 문서 상태
|
||||
|
||||
- 상태: draft
|
||||
- 문서 ID: dcy-008_20260607_emergency-stop-kill-switch
|
||||
- 이슈명: 비상 정지/강제 청산(kill-switch) — portfolio/전역 슬롯 청산 + 당일 전략 제외
|
||||
- 작성 시각(KST): 2026-06-07
|
||||
- 마지막 갱신 시각(KST): 2026-06-07
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: 없음
|
||||
|
||||
# 1. 계획 상태 요약
|
||||
|
||||
- 연계 문서 현황:
|
||||
- 관련 참조 파일: GitHub #3 (feat: 비상 정지/강제 청산(kill-switch) — portfolio/전역 단위 슬롯 청산 + 당일 전략 제외)
|
||||
- 관련 TODO/메모: dcy-007 Out of Scope "운영자 수동 즉시 청산(kill-switch): 별도 Discovery(trex-core#3 기반)" 명시
|
||||
- 해석:
|
||||
- dcy-007(core 자율 안전 매도)이 자동 조건 기반 청산(워치독 만료 + 가격/시간 트리거)을 구현했지만, 운영자가 즉시 개입할 수 있는 비상 정지 수단은 범위에서 제외됐다. 기존 pause(REQ-032)는 신규 매매만 중단하고 현재 holding 포지션을 청산하지 않으므로 비상 시 실제 보호 수단이 되지 못한다.
|
||||
- 이번 Discovery는 (1) 운영자가 명령 한 번으로 portfolio/전역 단위 holding 슬롯을 즉시 청산하고, (2) 당일 전략 실행을 중단시키며, (3) 다음 거래일에 자동 복귀하는 비상 정지 기능의 범위·제약·오픈 질문을 정의한다.
|
||||
|
||||
# 2. 요구사항 판정 결과
|
||||
|
||||
- 판정: 신규
|
||||
- 근거
|
||||
- dcy-007이 명시적으로 "운영자 수동 즉시 청산(kill-switch): 별도 Discovery(trex-core#3 기반)"를 Out of Scope로 분리했다.
|
||||
- 기존 pause(REQ-032)는 holding 슬롯을 청산하지 않으며, day-scoped 제외도 지원하지 않는다. 기능 성격이 근본적으로 다르다.
|
||||
|
||||
# 3. 문제점의 요약
|
||||
|
||||
- 현재 상태
|
||||
- trex-core는 `pause/resume` RPC(REQ-032, `trex.pause.req`)로 신규 매매를 중단할 수 있으나, 현재 `holding` 슬롯을 즉시 매도하는 운영자 트리거가 없다(`trex_core/adapters/persistence/postgresql.py`, `PostgreSQLPauseStore`).
|
||||
- 자율 안전 매도(dcy-007, REL-003)는 워치독 만료 + 가격·시간 조건이 충족된 경우에만 청산하며, 운영자가 즉시 개입하는 비상 경로가 없다.
|
||||
- kill-switch를 위한 슬롯 청산 경로(`SlotService.sell()`, REQ-036/BAT-010)와 실행 인프라(outbox, execution worker)는 이미 구현되어 있다. 호출 진입점만 없는 상태다.
|
||||
- 기대 상태
|
||||
- 운영자가 RPC 메시지 또는 CLI 명령으로 즉시 모든(또는 특정 portfolio의) holding 슬롯을 청산하고 당일 전략 실행을 중단시킬 수 있다.
|
||||
- 당일 전략 제외는 다음 거래일 자동 복귀한다(day-scoped, pause와 달리 수동 resume 불필요).
|
||||
- `SlotService.sell()`(REQ-036)을 재사용해 기존 outbox 내구성·슬롯 상태 전이·ledger 기록 경로를 그대로 따른다.
|
||||
- 이해관계자
|
||||
- 운영자(Justin Ko): 실 KIS 브로커 연동 환경에서 이상 상황 발생 시 즉시 포지션 청산이 필요한 주요 사용자
|
||||
- trex-core 데몬 운영자: kill-switch 발동 후 데몬 상태·로그 모니터링 담당
|
||||
- 전략 모듈 개발자(orb-v1): kill-switch 발동 시 당일 추가 매수 의도가 거부됨을 인지해야 함
|
||||
- 제약사항
|
||||
- `SlotService.sell()`(REQ-036)은 HOLDING 슬롯만 대상이며, PENDING_SELL 슬롯에는 idempotent로 동작한다. PENDING_BUY 슬롯 처리는 별도 결정 필요(OQ-3).
|
||||
- 당일 제외 상태는 데몬 재시작 후에도 유지되어야 하므로 영속화가 필요하다(pause_store 패턴 참조).
|
||||
- trex-core sync+thread 런타임 — kill-switch 발동 시 다수 슬롯 순차 sell()이 execution worker FIFO 큐에 집중될 수 있다.
|
||||
|
||||
## 주요 사용자 시나리오
|
||||
|
||||
- 실 KIS 브로커 환경에서 예기치 않은 시장 급변이 발생한 경우 — 운영자가 kill-switch를 발동해 전체 holding 포지션을 즉시 청산하고 당일 추가 매수를 차단한다.
|
||||
- 특정 portfolio의 전략 오동작을 감지한 경우 — portfolio 단위로 kill-switch를 발동해 해당 portfolio 슬롯만 청산하고, 다른 portfolio는 정상 운행한다.
|
||||
- 데몬 점검 또는 긴급 배포 전 포지션 청산이 필요한 경우 — 수동 kill-switch로 전체 포지션을 먼저 닫고 데몬을 안전하게 중단한다.
|
||||
|
||||
# 4. 이번에 정의할 변경
|
||||
|
||||
- **kill-switch 트리거 진입점 추가**: 운영자가 RPC 메시지(`trex.pause.req` 확장 또는 신규 채널) 또는 CLI로 kill-switch를 발동하면, 대상 범위(portfolio/전역)의 모든 holding 슬롯에 `SlotService.sell()`을 순차 호출한다.
|
||||
- **day-scoped 전략 제외 기능 추가**: kill-switch 발동 후 대상 범위 자산을 당일 전략 실행에서 제외한다. snapshot RPC 응답의 masking 소스에 "kill-switch 당일 제외"를 추가한다. 다음 거래일 데몬 부팅 시 자동 복귀한다.
|
||||
- **당일 제외 상태 영속화**: kill-switch 당일 제외 상태를 데몬 재시작 후에도 유지하기 위해 DB에 영속한다(pause_store 확장 또는 별도 테이블 — OQ-2).
|
||||
|
||||
## 영향 범위
|
||||
|
||||
- 코드 영향:
|
||||
- `trex_core/domain/slot/service.py`: kill-switch 발동 시 복수 슬롯에 sell()을 순차 호출하는 진입점(메서드 또는 헬퍼)
|
||||
- `trex_core/application/messaging_handlers.py`: kill-switch 명령 처리 handler 추가(RPC 채널 결정 이후)
|
||||
- `trex_core/adapters/persistence/postgresql.py` 또는 신규 어댑터: day-scoped 제외 상태 영속 read/write
|
||||
- `trex_core/runtime/daemon.py`: kill-switch RPC consumer 스레드 추가 또는 기존 pause RPC 확장
|
||||
- `trex_core/application/portfolio.py` 또는 snapshot handler: masking 소스에 "kill-switch 당일 제외" 추가
|
||||
- 문서 영향:
|
||||
- `docs/interface-contract.md`: kill-switch RPC 메시지 계약(payload 구조, 대상 범위, 응답 구조)
|
||||
- `docs/runtime-flows.md`: kill-switch 발동 흐름(트리거 → 순차 sell() → day-scoped 제외) 추가
|
||||
- `docs/project-structure.md`: kill-switch 관련 신규 모듈/handler 반영
|
||||
- 운영 영향:
|
||||
- DB 스키마: day-scoped 제외 상태 영속 컬럼 또는 테이블 추가(additive)
|
||||
- 기존 pause RPC(trex.pause.req) 채널 확장 또는 신규 채널 추가(OQ-1)
|
||||
|
||||
# 5. 요구사항 목록
|
||||
|
||||
## 기능 요구사항
|
||||
|
||||
- FR-1: 운영자가 kill-switch 명령을 발행하면 대상 범위(portfolio_id 지정 또는 전역)의 모든 `holding` 슬롯에 `SlotService.sell(actor="kill-switch", reason="emergency_stop")`을 순차 호출해 즉시 청산을 제출한다.
|
||||
- FR-2: kill-switch 발동 후 대상 범위의 자산(또는 모든 자산)을 당일 거래일 전략 실행에서 제외한다. snapshot RPC 응답의 masking 소스에 "kill-switch 당일 제외" 항목을 추가한다.
|
||||
- FR-3: 당일 제외 상태는 데몬 재시작 후에도 유지된다. 다음 거래일 첫 부팅 시 자동으로 해제된다(day-scoped).
|
||||
- FR-4: portfolio 단위(특정 portfolio_id의 슬롯만)와 전역(모든 슬롯)을 구분해 지원한다.
|
||||
- FR-5: 이미 `pending_sell` 상태인 슬롯에 대해 sell()을 중복 호출하지 않는다(REQ-036 idempotency 적용).
|
||||
- FR-6: kill-switch 발동 이력(actor, 발동 시각, 대상 범위, 처리 슬롯 수)이 데몬 로그에 기록된다.
|
||||
|
||||
## 비기능 요구사항
|
||||
|
||||
- NFR-1: kill-switch 발동 후 holding 슬롯 전체에 대한 sell() 제출 완료까지 지연이 슬롯 수 N 기준 N × 100ms + 5s 이내이어야 한다(순차 처리 기준, 실 KIS 연동 outbox 전송 제외).
|
||||
- NFR-2: kill-switch 발동이 기존 자율 안전 매도 루프(dcy-007/REL-003)의 동작에 영향을 주지 않는다. 두 기능은 독립적으로 동작한다.
|
||||
- NFR-3: kill-switch 당일 제외 상태가 영속화되어 데몬 재시작 후에도 snapshot RPC masking이 유지된다.
|
||||
|
||||
## 범위 경계
|
||||
|
||||
- In Scope
|
||||
- holding 슬롯 즉시 청산 (portfolio/전역 단위, `SlotService.sell()` 재사용)
|
||||
- 당일 전략 실행 제외 + 다음 거래일 자동 복귀 (day-scoped, 영속 필요)
|
||||
- kill-switch 트리거 표면: RPC 메시지 (CLI는 REQ 단계에서 결정)
|
||||
- kill-switch 발동 후 당일 추가 매수 거부 (masking 소스 확장)
|
||||
- kill-switch 발동 이력 로깅
|
||||
- Out of Scope
|
||||
- 부분 수량 청산 (GitHub #5 별도 Discovery)
|
||||
- 전략 모듈 프로세스 강제 종료 (trex-core 범위 밖)
|
||||
- pending_buy 슬롯의 롤백 처리 (OQ-3 Deferred — REQ 단계에서 결정)
|
||||
- kill-switch 발동 후 자율 안전 매도(dcy-007) 비활성화
|
||||
- trailing stop 등 고급 종료 전략
|
||||
|
||||
# 6. 리스크/가정 목록
|
||||
|
||||
## 리스크
|
||||
|
||||
- R-1 (Medium): kill-switch 발동 시 다수 슬롯에 순차 sell()을 호출하면 execution worker FIFO 큐에 부하가 집중된다. 슬롯 수가 많을 경우 처리 지연이 발생할 수 있다.
|
||||
- 완화 방안: sell() 호출은 PENDING_SELL 전이만 수행하므로 빠르다(DB write 1회). execution worker는 기존 순차 처리 유지. 슬롯 수 10 미만 운영 환경 기준으로 NFR-1 허용 지연 설정.
|
||||
- R-2 (Medium): 당일 제외 상태 영속 방식 미결정(pause_store 확장 vs 별도 테이블). pause와 의미 충돌 시 마스킹 로직 복잡도 증가.
|
||||
- 완화 방안: OQ-2에서 방식을 결정한다. pause는 "resume 전까지 유지(persistent)"이고 kill-switch 제외는 "당일만(day-scoped)"이라 별도 컬럼 또는 테이블로 분리를 권장한다.
|
||||
- R-3 (Low): kill-switch 발동 중 브로커 오류로 일부 슬롯의 sell()이 실패하거나 지연될 수 있다.
|
||||
- 완화 방안: sell() → PENDING_SELL 전이 성공 시 기존 outbox retry 경로가 eventual consistency를 보장한다. 발동 이력(FR-6)으로 미처리 슬롯 추적 가능.
|
||||
- R-4 (Low): kill-switch와 자율 안전 매도(dcy-007)가 동시에 같은 슬롯에 sell()을 호출하면 중복 전이가 발생할 수 있다.
|
||||
- 완화 방안: REQ-036 idempotency가 PENDING_SELL 상태 중복 sell()을 무시한다. 코드 충돌 없음.
|
||||
|
||||
## 가정
|
||||
|
||||
- A-1: `SlotService.sell()`(REQ-036/BAT-010)이 이미 구현되어 있어 kill-switch가 직접 재사용할 수 있다.
|
||||
- A-2: 당일 전략 제외는 snapshot RPC 응답의 masking 소스 확장으로 구현한다. 기존 pause masking 코드 패턴(`portfolio.py`)을 참조한다.
|
||||
- A-3: kill-switch 발동 후에도 기존 자율 안전 매도 루프(dcy-007)는 독립적으로 동작한다.
|
||||
- A-4: 운영 환경의 holding 슬롯 수는 통상 10 미만이다. 대규모 병렬 청산 최적화는 Out of Scope.
|
||||
|
||||
## 오픈 질문
|
||||
|
||||
- OQ-1: kill-switch 트리거 표면 — 기존 `trex.pause.req` 채널에 `action: "kill_switch"` 메시지를 추가할지, 신규 채널(`trex.killswitch.req`)을 별도로 열지?
|
||||
- 상태: Open
|
||||
- 처리 방안: confirm-discovery 단계에서 Justin Ko가 결정. 채널 재사용 시 RabbitMQ 큐 추가 불필요하나 pause 핸들러 확장 필요; 신규 채널 시 명확한 관심사 분리.
|
||||
- 종료 조건: confirm-discovery 완료 전 Justin Ko 결정 또는 REQ 초안 단계에서 Deferred 처리.
|
||||
- OQ-2: 당일 제외 상태 영속 방식 — `PostgreSQLPauseStore`(pause_store)를 확장해 day-scoped 컬럼을 추가할지, 별도 테이블/어댑터를 신설할지?
|
||||
- 상태: Open
|
||||
- 처리 방안: confirm-discovery 단계에서 결정. pause_store 확장 시 DB 마이그레이션 1건; 별도 테이블 시 명확한 분리.
|
||||
- 종료 조건: confirm-discovery 완료 전 결정 또는 REQ 단계 Deferred.
|
||||
- OQ-3: kill-switch 발동 시 `pending_buy` 상태 슬롯 처리 — 현재 진행 중인 매수를 무시하고 HOLDING이 된 이후에 청산하도록 기다릴지, 즉시 롤백(PENDING_BUY→IDLE) 처리를 별도로 추가할지?
|
||||
- 상태: Deferred → REQ 단계
|
||||
- 처리 방안: PENDING_BUY 롤백은 execution 루프와 상호작용이 복잡하므로 REQ 단계에서 상세 설계. 기본 구현은 HOLDING 슬롯만 대상.
|
||||
- 종료 조건: REQ 초안 작성 시 pending_buy 범위 명시.
|
||||
- OQ-4: 전역 halt 수동 해제 지원 여부 — kill-switch 발동 후 같은 거래일 내에 운영자가 당일 제외를 해제하고 정상 매매를 재개할 수 있어야 하는가?
|
||||
- 상태: Open
|
||||
- 처리 방안: confirm-discovery 단계에서 Justin Ko 결정. 해제 지원 시 `resume_kill_switch` action 추가 필요.
|
||||
- 종료 조건: confirm-discovery 완료 전 Justin Ko 결정.
|
||||
|
||||
### 오픈 질문 처리 기준
|
||||
|
||||
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
|
||||
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
|
||||
|
||||
# 7. 초기 성공 기준
|
||||
|
||||
- S-1: kill-switch 명령 발동 후 모든 holding 슬롯에 PENDING_SELL 전이 제출이 완료된다(슬롯 수 N 기준 NFR-1 이내).
|
||||
- S-2: kill-switch 발동 후 다음 snapshot RPC 응답에서 대상 자산이 masking되어 buyable/held 뷰에 노출되지 않는다(당일 전략 제외 확인).
|
||||
- S-3: 데몬 재시작 후에도 kill-switch 당일 제외 상태가 유지되어, 당일 전략 실행이 차단된다.
|
||||
- S-4: 다음 거래일 데몬 부팅 시 kill-switch 당일 제외 상태가 자동 해제되어 정상 매매 대상이 된다.
|
||||
|
||||
## 측정 방식 및 데이터 출처
|
||||
|
||||
- S-1 측정 방식: kill-switch 발동 후 `slot_repo.list_slots(day)` 결과에서 PENDING_SELL 수 == 발동 전 HOLDING 수 확인 / 데이터 출처: 단위 테스트(mock repo) + 실 e2e 로그
|
||||
- S-2 측정 방식: kill-switch 발동 후 snapshot RPC 요청 → 응답 assets 목록에서 청산된 symbol 부재 확인 / 데이터 출처: 실 RabbitMQ e2e 테스트 또는 단위 테스트(masking 로직)
|
||||
- S-3 측정 방식: kill-switch 발동 → 데몬 재시작 → snapshot RPC → masking 지속 확인 / 데이터 출처: 통합 테스트 또는 manual e2e
|
||||
- S-4 측정 방식: 다음 거래일 날짜로 데몬 부팅 → snapshot RPC → 청산된 symbol이 buyable 뷰에 복귀 확인 / 데이터 출처: 통합 테스트(날짜 mock) 또는 manual e2e
|
||||
|
||||
# 8. REQ로 넘기기 전 확인 체크
|
||||
|
||||
- [ ] 범위(In Scope/Out of Scope) 확정
|
||||
- 기준: OQ-1(트리거 표면), OQ-2(영속 방식), OQ-4(수동 해제 여부) 결정 또는 Deferred 처리 완료
|
||||
- [ ] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- 기준: S-1~S-4 각각 단위 테스트 또는 e2e 근거 확정
|
||||
- [ ] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- 기준: R-1(큐 부하), R-2(영속 방식) 완화 방안 REQ 단계 인계 여부 확인
|
||||
- [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: OQ-1, OQ-2, OQ-4 결정 완료 또는 REQ Deferred 처리 완료 (OQ-3은 이미 Deferred)
|
||||
- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- 기준: Justin Ko 확인
|
||||
|
||||
# 9. 파일 처리 결과
|
||||
|
||||
- 처리 결과: 생성
|
||||
- 생성 경로: docs/discovery/dcy-008_20260607_emergency-stop-kill-switch.md
|
||||
- 참조 문서:
|
||||
- GitHub #3: feat: 비상 정지/강제 청산(kill-switch) — portfolio/전역 단위 슬롯 청산 + 당일 전략 제외
|
||||
- docs/discovery/dcy-007_20260606_core-autonomous-safety-sell.md (Out of Scope 분리 근거)
|
||||
- docs/srs/Interface/req-036_auto-sell-internal-path.md (SlotService.sell() 재사용)
|
||||
- docs/srs/Interface/req-032_runtime-pause-resume-control.md (pause 패턴 참조)
|
||||
|
||||
# 10. 사용자 결정 필요 항목 요약
|
||||
|
||||
## DECIDE
|
||||
|
||||
- {{DECIDE_TRIGGER_SURFACE}}: kill-switch 트리거 표면 — `trex.pause.req` 채널 확장(`action: "kill_switch"`) vs 신규 채널(`trex.killswitch.req`) (OQ-1)
|
||||
- {{DECIDE_PERSIST_METHOD}}: 당일 제외 상태 영속 방식 — pause_store 확장(컬럼 추가) vs 별도 테이블 신설 (OQ-2)
|
||||
- {{DECIDE_MANUAL_RESUME}}: 같은 거래일 내 kill-switch 당일 제외 수동 해제 지원 여부 (OQ-4)
|
||||
|
||||
## CONFIRM
|
||||
|
||||
- {{CONFIRM_SCOPE_PORTFOLIO_AND_GLOBAL}}: portfolio 단위와 전역 단위 둘 다 이번 Discovery 범위에 포함하는지, 혹은 전역만 우선 구현할지 확인
|
||||
- {{CONFIRM_PENDING_BUY_DEFERRED}}: pending_buy 슬롯 롤백은 REQ 단계로 Deferred 처리해도 무방한지 확인 (OQ-3)
|
||||
|
||||
## DATA
|
||||
|
||||
- {{DATA_SLOT_COUNT_UPPER_BOUND}}: 실 운영 환경의 최대 동시 holding 슬롯 수 (NFR-1 지연 허용치 산정 기준)
|
||||
|
||||
# 11. Discovery Freeze
|
||||
|
||||
## 섹션 참조
|
||||
|
||||
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
||||
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
||||
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
||||
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
||||
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||||
|
||||
## Freeze 플래그
|
||||
|
||||
- Handoff Decision: 보류
|
||||
- Handoff Rationale: 초기 Discovery 초안 생성 완료. OQ-1(트리거 표면), OQ-2(영속 방식), OQ-4(수동 해제 여부) 미결 — confirm-discovery에서 Justin Ko 결정 후 REQ 초안 작성 가능.
|
||||
- Ready for REQ Drafting: false
|
||||
- Confirmed By: TBD
|
||||
- Confirmed At (KST): TBD
|
||||
@@ -12,4 +12,5 @@
|
||||
| dcy-004_20260604_portfolio-hub-ledger-integration | 20260604 | portfolio-hub ledger 연동 — 체결을 외부 원장에 기록 | [Discovery](./dcy-004_20260604_portfolio-hub-ledger-integration.md) | confirmed |
|
||||
| dcy-005_20260604_portfolio-hub-asset-snapshot-api | 20260604 | portfolio-hub 종목 스냅샷 read API — 전략 모듈에 전략 실행 대상 자산 리스트 제공 | [Discovery](./dcy-005_20260604_portfolio-hub-asset-snapshot-api.md) | confirmed |
|
||||
| dcy-006_20260605_strategy-module-rabbitmq-transport | 20260605 | trex-core ↔ 전략 모듈 통신 transport — RabbitMQ 도입 (스냅샷 read + intent 제출 + 이벤트) | [Discovery](./dcy-006_20260605_strategy-module-rabbitmq-transport.md) | confirmed |
|
||||
| dcy-007_20260606_core-autonomous-safety-sell | 20260606 | core 자율 안전 매도 — stop-loss·시간 만기 자동 트리거 | [Discovery](./dcy-007_20260606_core-autonomous-safety-sell.md) | confirmed |
|
||||
| dcy-007_20260606_core-autonomous-safety-sell | 20260606 | core 자율 안전 매도 — stop-loss·시간 만기 자동 트리거 | [Discovery](./dcy-007_20260606_core-autonomous-safety-sell.md) | confirmed |
|
||||
| dcy-008_20260607_emergency-stop-kill-switch | 20260607 | 비상 정지/강제 청산(kill-switch) — portfolio/전역 슬롯 청산 + 당일 전략 제외 | [Discovery](./dcy-008_20260607_emergency-stop-kill-switch.md) | draft |
|
||||
@@ -10,3 +10,4 @@
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| REL-001 | app-service | feedback-captured | BAT-001~008 | Discovery: orb-v1 전략 모듈, 부분 매도(#5) / Change-req: REQ-030 옵션 D, 전략 필드 실데이터, 이벤트 publisher idle 견고성, paper execution_id Redis(#4) | [rel-001](rel-001_20260606_trex-core-slot-core-baseline.md) |
|
||||
| REL-002 | tooling | released | BAT-009 | Change-req: paper execution_id Redis(#4) | [rel-002](rel-002_20260606_paper-broker-env-tuning.md) |
|
||||
| REL-003 | app-service | confirmed | BAT-010, BAT-011 | dcy-007 (core 자율 안전 매도: watchdog·stop-loss·take-profit·forced_close·장 마감·update_thresholds RPC) | [rel-003](rel-003_20260607_core-autonomous-safety-sell.md) |
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# REL-003: core 자율 안전 매도 (watchdog + 가격 임계값 + forced_close / stop-loss·take-profit / 장 마감 청산 + update_thresholds RPC)
|
||||
|
||||
- Status: confirmed
|
||||
- Profile: app-service
|
||||
- Included Batch: BAT-010, BAT-011
|
||||
|
||||
## Scope
|
||||
- Included:
|
||||
- BAT-010 (REQ-034, REQ-035, REQ-036, REQ-041): safety-sell 기반 인프라 — per-slot 워치독 store(snapshot RPC 가 holding 슬롯 `last_seen_at` 갱신), 슬롯 `stop_loss_price`/`take_profit_price` 도메인 필드 + `004_add_price_thresholds.sql` additive 마이그레이션, `SlotService.sell()` 자율 매도 내부 경로(idempotent), safety-sell env 5종 파싱.
|
||||
- BAT-011 (REQ-037, REQ-038, REQ-039, REQ-040, REQ-042): safety-sell 트리거 레이어 — `trex-safety-sell` worker(7번째 스레드), forced_close_at 만기 자동 매도(REQ-037), stop-loss·take-profit KIS 현재가 polling(`TTTC8397R`/`VTTC8397R`, `stck_prpr`) + 트리거(REQ-038), 장 마감 강제 청산(`TREX_MARKET_CLOSE_LIQUIDATION_TIME`, REQ-039), `update_thresholds` intent RPC 액션(REQ-040), baseline 3문서 정합(REQ-042).
|
||||
- Excluded:
|
||||
- **REQ-030 AC-2/AC-3 (옵션 D)**: portfolio-hub#8 배포 대기로 status Approved 유지 — 본 release 범위 밖(REL-001 Excluded 와 동일 이월).
|
||||
- 가격 임계값 논리 유효성 검증(take_profit > stop_loss 등): dcy-007/REQ-035 범위 밖.
|
||||
- KIS 쿼터 초과 시 polling 간격 **동적 조정**: 본 release 는 상한 산정 + 경고 로그까지(REQ-038 AC-8). 동적 조정은 후속 후보.
|
||||
- 전략 모듈(orb-v1) 자체: 별도 Discovery/배포.
|
||||
|
||||
## Rollout Plan
|
||||
1. 사전 점검: PostgreSQL / RabbitMQ 도달성 확인. `.env` 의 broker 설정(`TREX_BROKER`, kis 시 `TREX_KIS_*` + `TREX_KIS_ALLOW_LIVE`) 확인. safety-sell env(아래)는 모두 optional — 미설정 시 기본값.
|
||||
2. DB 마이그레이션 적용: `004_add_price_thresholds.sql`(additive, `ADD COLUMN IF NOT EXISTS`). 데몬 boot `apply_migrations` 가 자동 적용하나 사전 적용 권장.
|
||||
3. 배포 대상 commit SHA(a30efa8 이상) 설치 후 데몬 재기동 → **7 워커 스레드**(execution / ledger / intent-rpc / snapshot-rpc / pause-rpc / snapshot-scheduler / **trex-safety-sell**) 기동 확인.
|
||||
4. safety-sell 튜닝 env(전부 optional):
|
||||
- `TREX_WATCHDOG_TIMEOUT`(기본 120s), `TREX_WATCHDOG_GRACE_PERIOD`(기본 60s) — 워치독 만료/부팅 grace.
|
||||
- `TREX_FORCED_CLOSE_CHECK_INTERVAL`(기본 60s), `TREX_STOP_LOSS_POLL_INTERVAL`(기본 30s) — worker 캐던스.
|
||||
- `TREX_MARKET_CLOSE_LIQUIDATION_TIME`(HH:MM KST, **기본 미설정 = 장 마감 청산 OFF**).
|
||||
5. **운영 안전 주의(중요)**: `broker=kis` LIVE 에서 safety-sell worker 는 **실제 매도 주문**을 낸다.
|
||||
- forced_close / stop-loss·take-profit 는 **워치독 만료(전략 모듈이 snapshot RPC 폴링을 멈춘) 슬롯에만** 작동한다 → `TREX_WATCHDOG_TIMEOUT` 을 전략 모듈의 정상 snapshot 폴링 주기보다 충분히 크게 설정해 정상 운용 슬롯 오발동을 방지할 것.
|
||||
- 장 마감 청산은 설정 시 **워치독 무관 전체 holding** 을 매도하므로 의도적으로만 설정.
|
||||
- stop-loss·take-profit·forced_close 는 해당 슬롯에 임계값/만기가 설정된 경우에만 작동(매수 intent 또는 `update_thresholds` 로 주입). 미설정 슬롯에는 무영향.
|
||||
|
||||
## Rollback Plan
|
||||
- 코드 롤백: BAT-010+011 단일 커밋(a30efa8) revert → safety-sell worker 제거(데몬 6 스레드로 회귀). 타 batch 비의존.
|
||||
- 데이터: `004_add_price_thresholds.sql` 는 additive(컬럼 추가)라 데이터 파괴 없음. 롤백 시 컬럼 보존(드롭 불필요).
|
||||
- 즉시 무력화(코드 롤백 없이): safety-sell 은 임계값/만기/장 마감 env 가 **설정된 경우에만** 매도한다. 임계값을 주입하지 않고 `TREX_MARKET_CLOSE_LIQUIDATION_TIME` 을 비우면 worker 는 사실상 inert. 긴급 시 데몬 정지.
|
||||
- 트리거 조건 예: 정상 운용 슬롯 오발동(워치독 timeout 과소 설정), KIS 현재가 polling 쿼터 초과, 예기치 않은 청산.
|
||||
|
||||
## Verification Checklist
|
||||
- 배포 전:
|
||||
- 단위 `pytest -q --deselect ...::test_sandbox_token_and_submit` → 236 passed / 23 skipped.
|
||||
- 인프라 게이트(PG+RabbitMQ) `TREX_RABBITMQ_TEST_URL=... TREX_POSTGRESQL_TEST_DSN=... pytest -q` → 259 passed(마이그레이션 001~004 적용 + PG repo round-trip 포함; BAT-010 R-2 해소).
|
||||
- `ruff check trex_core tests` → All checks passed.
|
||||
- 실데몬 e2e(paper): RabbitMQ 매수(forced_close_at = 매수+60s) → 워치독 만료 → `holding→pending_sell(forced_close_at/trex-safety-sell)→closed` 관찰(PASS).
|
||||
- **실 KIS LIVE 시세 검증(읽기 전용)**: `get_current_price('005930')=Decimal('329000')`(rt_cd=0, tr `FHKST01010100`). 배포 전 진단에서 REQ-038 구 tr_id(`TTTC8397R`) 결함 발견 → change-req REQ-038 CHG-04/05 로 정정·재검증 완료(REL-003 re-confirm 근거).
|
||||
- 배포 후:
|
||||
- 데몬 기동 + **7 워커 스레드**(trex-safety-sell 포함) + 큐/exchange/DLQ 선언 확인.
|
||||
- 마이그레이션 004 적용 확인(`daily_slot` 에 `stop_loss_price`/`take_profit_price` 컬럼 존재).
|
||||
- `update_thresholds` intent RPC: holding 슬롯 임계값 갱신 + 워치독 리셋 + non-holding `ignored` 응답.
|
||||
- **차기 거래일 실 KIS 관찰**: stop-loss·take-profit·forced_close·장 마감 트리거 중 최소 1건 실 체결까지 확인(휴장/시장 상황 의존).
|
||||
|
||||
## Release Execution Evidence
|
||||
- Release Status Transition Target: released
|
||||
|
||||
### Deployment Facts
|
||||
- Executed At:
|
||||
- <배포 시작 시각>
|
||||
- Completed At:
|
||||
- <배포 완료 시각>
|
||||
- Environment:
|
||||
- <배포 대상 환경>
|
||||
- Operator:
|
||||
- <배포 수행자>
|
||||
- Revision or Artifact:
|
||||
- <커밋 SHA 또는 배포 artifact 식별자 (≥ a30efa8)>
|
||||
- Commands or Procedure:
|
||||
- <실행 명령 또는 배포 절차 요약>
|
||||
|
||||
### Profile-Specific Checks
|
||||
- app-service:
|
||||
- Health Endpoint:
|
||||
- <HTTP health 없음(RabbitMQ transport) → 큐 declare readiness + 7 스레드 기동 확인>
|
||||
- Startup Log Fields:
|
||||
- <app/event/command/run_id + trex-safety-sell 스레드 기동 로그>
|
||||
- Request Log Fields:
|
||||
- <update_thresholds RPC correlation_id 확인>
|
||||
- Smoke or Manual Verification:
|
||||
- <마이그레이션 004 적용 + update_thresholds RPC + (차기 거래일) 실 KIS 트리거 관찰>
|
||||
|
||||
### Rollback Outcome
|
||||
- Rollback Required:
|
||||
- <yes 또는 no>
|
||||
- Rollback Trigger:
|
||||
- <발생 시 원인, 없으면 none>
|
||||
- Rollback Action:
|
||||
- <수행한 조치, 없으면 none>
|
||||
|
||||
### Release Decision Evidence
|
||||
- Released Decision:
|
||||
- <released 전환 여부>
|
||||
- Rationale:
|
||||
- <released 전환 판단 근거>
|
||||
- Recorded By:
|
||||
- <기록자>
|
||||
- Recorded At:
|
||||
- <기록 시각>
|
||||
|
||||
## Feedback Handoff
|
||||
- Observation Summary:
|
||||
- (배포 전, 2026-06-07) 실데몬 paper e2e — RabbitMQ 매수(forced_close_at +60s) → 워치독 자연 만료 → `trex-safety-sell` 가 `holding→pending_sell(forced_close_at)` 트리거 → 집행 루프 `pending_sell→closed`. 단위 236 + 인프라 게이트 259 passed, ruff clean.
|
||||
- (배포 전, 2026-06-07) 실 KIS LIVE 시세 검증에서 **REQ-038 tr_id 결함 포착** — 구 `TTTC8397R` 가 "없는 서비스 코드"(rt_cd=1)로 LIVE 손절/익절 silent 미발동. `FHKST01010100` 로 정정 후 `get_current_price=329000` 정상. mock/샌드박스로는 못 잡는 결함을 배포 전 LIVE 검증으로 포착(테스트 가치 입증).
|
||||
- Discovery Input:
|
||||
- <후속 discovery 후보>
|
||||
- REQ Input:
|
||||
- <후속 req 후보>
|
||||
- Change Request Input:
|
||||
- KIS 현재가 polling 쿼터 동적 조정(REQ-038 후속) — 워치독 만료 슬롯 급증 시 polling 간격 자동 확대.
|
||||
- 가격 임계값 논리 유효성 검증(take_profit > stop_loss 등) — 신규 후보.
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
## Requirement
|
||||
|
||||
- 데몬은 `TREX_STOP_LOSS_POLL_INTERVAL(초)` 주기로, `stop_loss_price` 또는 `take_profit_price`가 설정되고 **워치독이 만료된** `holding` 슬롯들의 현재가를 KIS `TTTC8397R`(실전) / `VTTC8397R`(모의) API로 polling해야 한다.
|
||||
- 데몬은 `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` 조건 충족 시 익절 매도를 제출해야 한다.
|
||||
@@ -28,7 +28,7 @@
|
||||
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`을 사용한다.
|
||||
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
|
||||
@@ -47,7 +47,7 @@
|
||||
## Notes
|
||||
|
||||
- KIS 현재가 polling 어댑터는 기존 `trex_core/adapters/broker/kis.py`에 `get_current_price(symbol)` 메서드로 추가하거나, 별도 모듈로 분리한다. batch planning에서 결정한다.
|
||||
- paper broker 모의 환경에서는 `VTTC8397R`를 사용하거나, stub 구현으로 현재가를 반환하는 방식을 batch에서 결정한다.
|
||||
- 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 참조.
|
||||
|
||||
@@ -153,3 +153,85 @@
|
||||
- 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 재확인 후 배포.
|
||||
|
||||
+3
-3
@@ -126,8 +126,8 @@
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-06-07 :: REQ-037, REQ-038, REQ-039, REQ-040, REQ-042 Approved → Implemented (confirm-req-implemented): BAT-011 verify 완료(신규 38 테스트 green, 단위 236 passed, 인프라 게이트 259 passed, ruff clean). SafetySellService(forced_close/SL·TP KIS polling/장 마감), `BrokerPort.get_current_price`(TTTC8397R/VTTC8397R), `SlotService.update_thresholds` + intent RPC 액션, `trex-safety-sell` 7번째 스레드, baseline 3문서 정합 확인.
|
||||
- 2026-06-07 :: REQ-038 CHG-20260607-05 Approved → Implemented (confirm-req-implemented): tr_id 정정(`FHKST01010100`) 구현·재검증 — kis.py 수정, AC-7 테스트 정정, 단위 236 passed/ruff clean, 실 KIS LIVE `get_current_price=329000`(rt_cd=0) 확인.
|
||||
- 2026-06-07 :: REQ-038 CHG-20260607-04 Implemented → Approved (change-req): 현재가 시세 tr_id 정정 `TTTC8397R`/`VTTC8397R` → `FHKST01010100`. 배포 전 실 KIS LIVE 진단에서 구 tr_id 가 "없는 서비스 코드"(rt_cd=1)로 거부 → LIVE 에서 손절/익절 silent 미발동 확인. 표준 시세 tr 정상(stck_prpr=329000). (change-req)
|
||||
- 2026-06-07 :: REQ-037, REQ-038, REQ-039, REQ-040, REQ-042 Approved → Implemented (confirm-req-implemented): BAT-011 verify 완료(신규 38 테스트 green, 단위 236 passed, 인프라 게이트 259 passed, ruff clean). SafetySellService(forced_close/SL·TP KIS polling/장 마감), `BrokerPort.get_current_price`, `SlotService.update_thresholds` + intent RPC 액션, `trex-safety-sell` 7번째 스레드, baseline 3문서 정합 확인. (※ REQ-038 AC-7 tr_id 는 CHG-04 로 후속 정정)
|
||||
- 2026-06-07 :: REQ-034, REQ-035, REQ-036, REQ-041 Approved → Implemented (confirm-req-implemented): BAT-010 verify 완료(32 신규 테스트 green, 95 회귀 passed). WatchdogStore, stop_loss/take_profit 도메인 필드, SQL 마이그레이션, SlotService.sell(), SafetySellConfig env vars 5종 구현 확인.
|
||||
- 2026-06-07 :: REQ-034~042 Proposed → Approved (confirm-req): 9개 전원 승인 게이트 통과. dcy-007(confirmed) 범위·결정과 모순 없음, 필수 필드·AC·Impacted Area·Change Log 완비 확인. (confirm-req)
|
||||
- 2026-06-07 :: REQ-034~042 생성 — dcy-007(core 자율 안전 매도: stop-loss·take-profit·시간 만기 자동 트리거) confirmed → 9개 REQ 초안. 워치독(034), 가격 임계값 필드(035), 자동 매도 경로(036), forced_close_at 루프(037), KIS 가격 polling(038), 장 마감 청산(039), update_thresholds RPC(040), env 설정(041), baseline 문서 정합(042). (draft-req)
|
||||
- 2026-06-06 :: REQ-010 CHG-20260606-02 — Status Approved → Implemented (confirm-req-implemented, 근거 BAT-009 release-candidate). AC-1~4 BAT-003 회귀 + AC-5 단위 2개·e2e(거부/지연/부분) evidence 충족. (confirm-req-implemented)
|
||||
|
||||
@@ -32,10 +32,13 @@ def _live_broker():
|
||||
base_url=_LIVE_URL, allow_live=True)
|
||||
|
||||
|
||||
def test_tr_price_live_vs_mock():
|
||||
"""AC-7: live uses TTTC8397R, mock uses VTTC8397R."""
|
||||
assert _live_broker()._tr_price() == "TTTC8397R"
|
||||
assert _mock_broker()._tr_price() == "VTTC8397R"
|
||||
def test_tr_price_is_quotations_tr():
|
||||
"""AC-7 (CHG-20260607-04): 주식현재가 시세 uses FHKST01010100, live/mock 공용.
|
||||
|
||||
The old TTTC8397R/VTTC8397R were rejected LIVE as "없는 서비스 코드".
|
||||
"""
|
||||
assert _live_broker()._tr_price() == "FHKST01010100"
|
||||
assert _mock_broker()._tr_price() == "FHKST01010100"
|
||||
|
||||
|
||||
def test_get_current_price_parses_stck_prpr(monkeypatch):
|
||||
@@ -53,7 +56,7 @@ def test_get_current_price_parses_stck_prpr(monkeypatch):
|
||||
monkeypatch.setattr(httpx, "get", fake_get)
|
||||
price = b.get_current_price("005930")
|
||||
assert price == Decimal("49000")
|
||||
assert captured["tr_id"] == "VTTC8397R"
|
||||
assert captured["tr_id"] == "FHKST01010100"
|
||||
assert captured["params"]["FID_INPUT_ISCD"] == "005930"
|
||||
|
||||
|
||||
|
||||
@@ -121,8 +121,13 @@ class KISBroker:
|
||||
return "TTTC0012U" if side == "buy" else "TTTC0011U"
|
||||
|
||||
def _tr_price(self) -> str:
|
||||
"""tr_id for current-price inquiry (REQ-038 AC-7)."""
|
||||
return "VTTC8397R" if self._mock else "TTTC8397R"
|
||||
"""tr_id for current-price inquiry (REQ-038 AC-7, CHG-20260607-04).
|
||||
|
||||
주식현재가 시세 uses FHKST01010100 for both live and 모의투자 — KIS
|
||||
does not provide separate VTS quote tr_ids (the old TTTC8397R/
|
||||
VTTC8397R were rejected LIVE as "없는 서비스 코드", rt_cd=1).
|
||||
"""
|
||||
return "FHKST01010100"
|
||||
|
||||
# --- BrokerPort ---
|
||||
def submit_order(self, req: OrderRequest) -> OrderAck:
|
||||
|
||||
Reference in New Issue
Block a user