docs(req): dcy-008 kill-switch confirm-discovery + REQ-043~047 (Approved)
dcy-008(비상 정지/kill-switch) OQ-2(별도 테이블/store)·OQ-4(수동 해제 resume_kill_switch) 결정 → confirm-discovery(confirmed) → draft-req → confirm-req(5개 Approved). - REQ-043 (Interface): trex.pause.req → trex.control.req 제어 채널 + action dispatch (OQ-1) - REQ-044 (Data): day-scoped killswitch_exclusion 테이블 + store + 005 마이그레이션 (OQ-2) - REQ-045 (Interface): kill_switch 실행 — 범위별 holding 순차 sell() + idempotency + 이력 (NFR-1/2 흡수) - REQ-046 (Interface): 당일 제외 masking + 다음 거래일 자동복귀 + resume_kill_switch (OQ-4, NFR-3 흡수) - REQ-047 (Documentation): baseline 3문서 정합 OQ-3(pending_buy 롤백)은 REQ Deferred. srs index Next ID REQ-048, Register +5, 요약 갱신. dcy-008 생성된 REQ 참조 기록. dcy-009(멀티 인스턴스 RabbitMQ, draft)는 별도 주제로 제외. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
# 0. 문서 상태
|
||||
|
||||
- 상태: draft
|
||||
- 상태: confirmed
|
||||
- 문서 ID: dcy-008_20260607_emergency-stop-kill-switch
|
||||
- 이슈명: 비상 정지/강제 청산(kill-switch) — portfolio/전역 슬롯 청산 + 당일 전략 제외
|
||||
- 작성 시각(KST): 2026-06-07
|
||||
- 마지막 갱신 시각(KST): 2026-06-07
|
||||
- 마지막 갱신 시각(KST): 2026-06-07 11:11 (draft-req: REQ-043~047 생성)
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: 없음
|
||||
- 생성된 REQ 참조: REQ-043, REQ-044, REQ-045, REQ-046, REQ-047
|
||||
|
||||
# 1. 계획 상태 요약
|
||||
|
||||
@@ -52,17 +52,18 @@
|
||||
|
||||
# 4. 이번에 정의할 변경
|
||||
|
||||
- **kill-switch 트리거 진입점 추가**: 운영자가 RPC 메시지(`trex.pause.req` 확장 또는 신규 채널) 또는 CLI로 kill-switch를 발동하면, 대상 범위(portfolio/전역)의 모든 holding 슬롯에 `SlotService.sell()`을 순차 호출한다.
|
||||
- **kill-switch 트리거 진입점 추가**: 운영자가 RPC 메시지(`trex.control.req`, `action: "kill_switch"`) 또는 CLI로 kill-switch를 발동하면, 대상 범위(portfolio/전역)의 모든 holding 슬롯에 `SlotService.sell()`을 순차 호출한다. 기존 `trex.pause.req`를 `trex.control.req`로 개념 확장하고 action 필드 기반 dispatch로 구현한다(새 큐 불필요).
|
||||
- **day-scoped 전략 제외 기능 추가**: kill-switch 발동 후 대상 범위 자산을 당일 전략 실행에서 제외한다. snapshot RPC 응답의 masking 소스에 "kill-switch 당일 제외"를 추가한다. 다음 거래일 데몬 부팅 시 자동 복귀한다.
|
||||
- **당일 제외 상태 영속화**: kill-switch 당일 제외 상태를 데몬 재시작 후에도 유지하기 위해 DB에 영속한다(pause_store 확장 또는 별도 테이블 — OQ-2).
|
||||
- **당일 제외 상태 영속화**: kill-switch 당일 제외 상태를 데몬 재시작 후에도 유지하기 위해 DB에 영속한다. **별도 테이블/store 어댑터를 신설**한다(OQ-2 Answered) — `snapshot_pause`(영구 pause)와 분리하고 `trading_date` 키로 day-scoped 표현. 다음 거래일 부팅 시 자동 무효.
|
||||
- **수동 해제(resume) 지원**: 운영자가 같은 거래일 내에 kill-switch 당일 제외를 해제할 수 있도록 `trex.control.req` 에 `action: "resume_kill_switch"`(범위: portfolio/전역)를 추가한다(OQ-4 Answered). 해당 범위의 day-scoped 제외 row 를 삭제해 정상 매매를 재개한다. 단, 이미 청산된 슬롯은 되돌리지 않는다(매도 비가역).
|
||||
|
||||
## 영향 범위
|
||||
|
||||
- 코드 영향:
|
||||
- `trex_core/domain/slot/service.py`: kill-switch 발동 시 복수 슬롯에 sell()을 순차 호출하는 진입점(메서드 또는 헬퍼)
|
||||
- `trex_core/application/messaging_handlers.py`: kill-switch 명령 처리 handler 추가(RPC 채널 결정 이후)
|
||||
- `trex_core/application/messaging_handlers.py`: `trex.control.req` action dispatch — kill-switch handler 추가
|
||||
- `trex_core/adapters/persistence/postgresql.py` 또는 신규 어댑터: day-scoped 제외 상태 영속 read/write
|
||||
- `trex_core/runtime/daemon.py`: kill-switch RPC consumer 스레드 추가 또는 기존 pause RPC 확장
|
||||
- `trex_core/runtime/daemon.py`: pause consumer를 control consumer(`trex.control.req`)로 확장 — action 기반 dispatch
|
||||
- `trex_core/application/portfolio.py` 또는 snapshot handler: masking 소스에 "kill-switch 당일 제외" 추가
|
||||
- 문서 영향:
|
||||
- `docs/interface-contract.md`: kill-switch RPC 메시지 계약(payload 구조, 대상 범위, 응답 구조)
|
||||
@@ -70,7 +71,7 @@
|
||||
- `docs/project-structure.md`: kill-switch 관련 신규 모듈/handler 반영
|
||||
- 운영 영향:
|
||||
- DB 스키마: day-scoped 제외 상태 영속 컬럼 또는 테이블 추가(additive)
|
||||
- 기존 pause RPC(trex.pause.req) 채널 확장 또는 신규 채널 추가(OQ-1)
|
||||
- `trex.pause.req` → `trex.control.req` 개념 확장. kill-switch는 `action: "kill_switch"` 필드로 dispatch(새 큐 불필요). daemon.py의 pause consumer 스레드를 control consumer로 교체.
|
||||
|
||||
# 5. 요구사항 목록
|
||||
|
||||
@@ -94,7 +95,7 @@
|
||||
- In Scope
|
||||
- holding 슬롯 즉시 청산 (portfolio/전역 단위, `SlotService.sell()` 재사용)
|
||||
- 당일 전략 실행 제외 + 다음 거래일 자동 복귀 (day-scoped, 영속 필요)
|
||||
- kill-switch 트리거 표면: RPC 메시지 (CLI는 REQ 단계에서 결정)
|
||||
- kill-switch 트리거 표면: `trex.control.req` RPC, `action: "kill_switch"` 메시지 (CLI는 REQ 단계에서 결정)
|
||||
- kill-switch 발동 후 당일 추가 매수 거부 (masking 소스 확장)
|
||||
- kill-switch 발동 이력 로깅
|
||||
- Out of Scope
|
||||
@@ -110,8 +111,7 @@
|
||||
|
||||
- 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-2 (해소): 당일 제외 상태 영속 방식 — **별도 테이블/store 신설로 결정**(OQ-2 Answered). pause는 "resume 전까지 유지(persistent)", kill-switch 제외는 "당일만(day-scoped, `trading_date` 키)"으로 테이블 분리 → 의미 충돌 없음, 기존 pause 마스킹 코드 무영향.
|
||||
- R-3 (Low): kill-switch 발동 중 브로커 오류로 일부 슬롯의 sell()이 실패하거나 지연될 수 있다.
|
||||
- 완화 방안: sell() → PENDING_SELL 전이 성공 시 기존 outbox retry 경로가 eventual consistency를 보장한다. 발동 이력(FR-6)으로 미처리 슬롯 추적 가능.
|
||||
- R-4 (Low): kill-switch와 자율 안전 매도(dcy-007)가 동시에 같은 슬롯에 sell()을 호출하면 중복 전이가 발생할 수 있다.
|
||||
@@ -127,21 +127,23 @@
|
||||
## 오픈 질문
|
||||
|
||||
- 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 처리.
|
||||
- 상태: Answered
|
||||
- 결정: `trex.pause.req` → `trex.control.req` 개념 확장. kill-switch는 `action: "kill_switch"` 필드로 라우팅. 새 큐 불필요. 기능마다 채널을 추가하면 consumer 스레드/커넥션이 기능 수에 비례해 증가하므로 단일 제어 채널 + action discriminator 패턴을 채택한다.
|
||||
- 반영 위치: # 4 변경 정의, # 4 영향 범위 운영 영향, # 5 범위 경계 In Scope
|
||||
- OQ-2: 당일 제외 상태 영속 방식 — `PostgreSQLPauseStore`(pause_store)를 확장해 day-scoped 컬럼을 추가할지, 별도 테이블/어댑터를 신설할지?
|
||||
- 상태: Open
|
||||
- 처리 방안: confirm-discovery 단계에서 결정. pause_store 확장 시 DB 마이그레이션 1건; 별도 테이블 시 명확한 분리.
|
||||
- 종료 조건: confirm-discovery 완료 전 결정 또는 REQ 단계 Deferred.
|
||||
- 상태: Answered
|
||||
- 결정: **별도 테이블/어댑터를 신설**한다. kill-switch 당일 제외는 `trading_date` 키로 day-scoped 표현이 자연스럽고, 기존 `snapshot_pause`(portfolio_id PK, resume까지 영구·날짜 없음)와 의미가 달라 같은 테이블에 섞으면 마스킹/만료 로직 복잡도가 올라간다(R-2). 신규 테이블(예: `killswitch_exclusion(portfolio_id, trading_date, actor, ...)`) + 전용 store 어댑터로 관심사를 분리하고, snapshot 마스킹은 `mask = pending | warning | pause | killswitch_today`로 합성한다. 기존 pause 코드는 무영향.
|
||||
- 반영 위치: # 4 변경 정의(당일 제외 상태 영속화), # 4 영향 범위(신규 테이블/마이그레이션 + store 어댑터), # 6 R-2 완화, # 5 범위 경계
|
||||
- 종료 조건: 충족(REQ 단계에서 정확한 테이블 컬럼/만료 기준 = trading_date 경과 자동 무효 상세화).
|
||||
- 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 결정.
|
||||
- 상태: Answered
|
||||
- 결정: **수동 해제를 지원한다.** 운영자가 같은 거래일 내에 kill-switch 당일 제외를 해제하고 정상 매매를 재개할 수 있어야 한다. `trex.control.req` 에 `action: "resume_kill_switch"`(범위: portfolio/전역) 를 추가하고, 해당 범위의 day-scoped 제외 row(OQ-2 신규 테이블)를 삭제(또는 무효화)한다. 해제는 "당일 제외"만 풀며 이미 청산된 슬롯을 되돌리지 않는다(매도는 비가역).
|
||||
- 반영 위치: # 4 변경 정의(수동 해제 action), # 4 영향 범위(control RPC handler), # 5 범위 경계 In Scope, # 10 DECIDE
|
||||
- 종료 조건: 충족(REQ 단계에서 `resume_kill_switch` payload/응답 + 권한/감사 상세화).
|
||||
|
||||
### 오픈 질문 처리 기준
|
||||
|
||||
@@ -164,16 +166,16 @@
|
||||
|
||||
# 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 확인
|
||||
- [x] 범위(In Scope/Out of Scope) 확정
|
||||
- OQ-1/2/4 Answered, OQ-3 REQ Deferred. scope = portfolio+전역 둘 다(FR-4). 확정.
|
||||
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- S-1~S-4 각각 단위 테스트/e2e 근거 명시(# 7 측정 방식). 확정.
|
||||
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- R-1(큐 부하) 완화 = 순차 sell()·N<10 가정으로 NFR-1 인계, R-2(영속 방식) 해소(별도 테이블). REQ 단계 인계.
|
||||
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- OQ-1/2/4 Answered, OQ-3 Deferred → `Open` 없음.
|
||||
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- Confirmed By = Justin.
|
||||
|
||||
# 9. 파일 처리 결과
|
||||
|
||||
@@ -189,18 +191,17 @@
|
||||
|
||||
## 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)
|
||||
- ✅ DECIDE_PERSIST_METHOD (OQ-2, Answered): 당일 제외 상태 영속 방식 = **별도 테이블/store 신설**(`snapshot_pause`와 분리, `trading_date` day-scoped).
|
||||
- ✅ DECIDE_MANUAL_RESUME (OQ-4, Answered): 수동 해제 **지원** — `trex.control.req action: "resume_kill_switch"` 추가, day-scoped 제외 해제(청산된 슬롯은 비가역).
|
||||
|
||||
## CONFIRM
|
||||
|
||||
- {{CONFIRM_SCOPE_PORTFOLIO_AND_GLOBAL}}: portfolio 단위와 전역 단위 둘 다 이번 Discovery 범위에 포함하는지, 혹은 전역만 우선 구현할지 확인
|
||||
- {{CONFIRM_PENDING_BUY_DEFERRED}}: pending_buy 슬롯 롤백은 REQ 단계로 Deferred 처리해도 무방한지 확인 (OQ-3)
|
||||
- ✅ CONFIRM_SCOPE_PORTFOLIO_AND_GLOBAL: **portfolio 단위 + 전역 단위 둘 다 포함**. 본문 FR-4("portfolio 단위와 전역 단위 둘 다 구분해 지원") + In Scope("portfolio/전역 단위") + 이슈명에서 이미 확정 — 동일하게 유지.
|
||||
- ✅ CONFIRM_PENDING_BUY_DEFERRED: **REQ 단계 Deferred로 처리(동의)**. OQ-3(Deferred → REQ 단계) + Out of Scope("pending_buy 슬롯의 롤백 처리 — OQ-3 Deferred")에 비차단으로 명시됨. 기본 구현은 HOLDING 슬롯만 대상.
|
||||
|
||||
## DATA
|
||||
|
||||
- {{DATA_SLOT_COUNT_UPPER_BOUND}}: 실 운영 환경의 최대 동시 holding 슬롯 수 (NFR-1 지연 허용치 산정 기준)
|
||||
- (비차단, REQ 단계 계측) DATA_SLOT_COUNT_UPPER_BOUND: 실 운영 환경의 최대 동시 holding 슬롯 수. A-4 가정(통상 10 미만)으로 NFR-1(N×100ms+5s) 산정 충분. 실측 상한은 REQ acceptance/verification 단계에서 확정 — Discovery confirm 차단 아님.
|
||||
|
||||
# 11. Discovery Freeze
|
||||
|
||||
@@ -214,8 +215,8 @@
|
||||
|
||||
## 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
|
||||
- Handoff Decision: 승인
|
||||
- Handoff Rationale: OQ-1(trex.control.req+action), OQ-2(별도 테이블/store 신설), OQ-4(수동 해제=resume_kill_switch) Answered, OQ-3(pending_buy) REQ Deferred. CONFIRM(scope=portfolio+전역=FR-4, pending_buy=REQ Deferred) 본문 근거로 확정, DATA(slot 수 상한)는 REQ 단계 계측(비차단). 코드 사실관계(SlotService.sell REQ-036, trex.pause.req) 일치, baseline 모순 없음. REQ 초안 작성 가능.
|
||||
- Ready for REQ Drafting: true
|
||||
- Confirmed By: Justin
|
||||
- Confirmed At (KST): 2026-06-07 11:11
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
| 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-008_20260607_emergency-stop-kill-switch | 20260607 | 비상 정지/강제 청산(kill-switch) — portfolio/전역 슬롯 청산 + 당일 전략 제외 | [Discovery](./dcy-008_20260607_emergency-stop-kill-switch.md) | draft |
|
||||
| dcy-008_20260607_emergency-stop-kill-switch | 20260607 | 비상 정지/강제 청산(kill-switch) — portfolio/전역 슬롯 청산 + 당일 전략 제외 | [Discovery](./dcy-008_20260607_emergency-stop-kill-switch.md) | confirmed |
|
||||
@@ -0,0 +1,116 @@
|
||||
# req-044: kill-switch day-scoped 제외 store + 테이블 + 마이그레이션
|
||||
|
||||
- Status: Approved
|
||||
- Type: Data
|
||||
- Priority: High
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
|
||||
kill-switch 발동 시 대상 범위(portfolio/전역)를 **당일(day-scoped) 전략 실행에서 제외**하는 상태를 데몬 재시작 후에도 유지하기 위해 영속화한다. 기존 `snapshot_pause`(portfolio_id PK, resume 전까지 영구, 날짜 없음)와 의미가 다르므로(당일만, `trading_date` 키), **별도 테이블 + 전용 store 어댑터를 신설**한다(dcy-008 OQ-2 Answered). 다음 거래일 부팅 시 자동 무효된다.
|
||||
|
||||
## Requirement
|
||||
|
||||
- 신규 테이블(예: `killswitch_exclusion`)을 추가하는 additive 마이그레이션을 작성한다. 최소 컬럼: `portfolio_id`(전역 제외 표현 포함), `trading_date`, `actor`, `created_at`.
|
||||
- 전역(global) 제외 표현 방식(예: `portfolio_id = '*'` sentinel 또는 별도 scope 컬럼)은 구현 시 결정한다(Notes).
|
||||
- PK 또는 unique 제약은 `(portfolio_id, trading_date)` 로 동일 거래일 중복 제외를 멱등 처리한다.
|
||||
- 전용 store 어댑터(예: `PostgreSQLKillSwitchStore`)가 아래를 제공한다: `exclude(portfolio_id|global, trading_date, actor)`, `clear(portfolio_id|global, trading_date)`(수동 해제, REQ-046), `list_excluded(trading_date) -> set`.
|
||||
- `list_excluded(trading_date)` 는 **해당 거래일** 의 제외 항목만 반환한다. 다른 날짜 row 는 자동으로 결과에서 제외된다(day-scoped). 과거 row 정리(삭제)는 선택이며 조회가 trading_date 로 필터링되므로 정합에 영향 없다.
|
||||
- 기존 `snapshot_pause` / `PostgreSQLPauseStore` 는 변경하지 않는다(관심사 분리).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given 마이그레이션 적용, When 스키마를 확인하면, Then `killswitch_exclusion` 테이블이 `(portfolio_id, trading_date, actor, created_at)` 컬럼과 `(portfolio_id, trading_date)` 멱등 제약으로 존재한다.
|
||||
2. Given 마이그레이션을 2회 적용, When 재적용하면, Then `IF NOT EXISTS` 패턴으로 오류 없이 멱등하다(기존 데이터 보존).
|
||||
3. Given `exclude("pf-A", D)`, When `list_excluded(D)`, Then `{"pf-A"}` 를 포함한다.
|
||||
4. Given `exclude("pf-A", D)`, When `list_excluded(D+1)`(다음 거래일), Then `pf-A` 가 포함되지 않는다(day-scoped 자동 무효).
|
||||
5. Given 같은 `(pf-A, D)` 로 `exclude` 2회, When 호출하면, Then 멱등(중복 row 없음, 오류 없음).
|
||||
6. Given `exclude("pf-A", D)` 후 `clear("pf-A", D)`, When `list_excluded(D)`, Then `pf-A` 가 더 이상 포함되지 않는다(수동 해제, REQ-046).
|
||||
7. Given 전역 제외, When `exclude` 후 `list_excluded(D)`, Then 전역 sentinel 이 결과에 포함되어 호출자가 전체 제외로 해석할 수 있다.
|
||||
|
||||
## Impacted Area
|
||||
|
||||
- Docs:
|
||||
- `docs/project-structure.md` — 신규 테이블/store 모듈 반영 (REQ-047 처리)
|
||||
- Modules:
|
||||
- `trex_core/adapters/persistence/sql/005_create_killswitch_exclusion.sql` (신규, additive)
|
||||
- `trex_core/adapters/portfolio/killswitch_store.py` 또는 동등 위치 — `PostgreSQLKillSwitchStore`
|
||||
- Tests:
|
||||
- store CRUD + day-scoped 조회 + 멱등 + 전역 sentinel 단위/통합 테스트(PG 게이트)
|
||||
|
||||
## Notes
|
||||
|
||||
- dcy-008 OQ-2 Answered: 별도 테이블/store 신설(snapshot_pause 와 분리).
|
||||
- 전역 제외 표현(`portfolio_id='*'` vs scope enum 컬럼)은 구현 결정 — masking 소비처(REQ-046)와 계약 일치만 보장하면 됨.
|
||||
- `trading_date` 기준: dcy-008 의 day-scoped 의미 = "발동 거래일". 거래일 경계(UTC vs KST)는 데몬의 기존 trading_date 관례(UTC date)를 따르되 REQ-045/046 와 일치시킨다.
|
||||
- BAT-010 R-2(마이그레이션 실 PG round-trip) 선례에 따라 verification 에서 실 PG 적용 확인.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260607-01
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- dcy-008(confirmed) → REQ-044 초안 생성.
|
||||
|
||||
#### Intent
|
||||
|
||||
- 당일 제외 영속(OQ-2 Answered: 별도 테이블)을 Data REQ 로 정규화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Added: AC-1~7 전체
|
||||
|
||||
#### Impacted Area Delta
|
||||
|
||||
- Docs: project-structure.md (REQ-047)
|
||||
- Modules: sql/005_create_killswitch_exclusion.sql, killswitch_store.py
|
||||
- Tests: store CRUD + day-scoped + 멱등
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: none (미할당)
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 로 승인.
|
||||
|
||||
### CHG-20260607-02
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- Proposed → Approved (confirm-req).
|
||||
|
||||
#### Intent
|
||||
|
||||
- 승인 게이트 통과: 필수 필드·구체 AC·Impacted Area·Change Log 완비. dcy-008(confirmed) OQ-2 결정(별도 테이블) 반영. 전역 sentinel 표현은 구현 결정으로 승인 blocker 아님.
|
||||
|
||||
#### 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 / draft-batch 로 delivery 편성.
|
||||
@@ -0,0 +1,113 @@
|
||||
# req-047: kill-switch baseline 문서 정합
|
||||
|
||||
- Status: Approved
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
|
||||
dcy-008(비상 정지/kill-switch) 기능(REQ-043~046)의 구현 완료 후 `docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/project-structure.md` 세 baseline 문서를 최신 아키텍처와 정합시킨다.
|
||||
|
||||
## Requirement
|
||||
|
||||
- `docs/interface-contract.md` 에 아래를 반영한다:
|
||||
- `trex.pause.req` → `trex.control.req` 제어 채널 확장 + action 목록(`pause`/`resume`/`list`/`kill_switch`/`resume_kill_switch`) (REQ-043)
|
||||
- `kill_switch` payload(`scope`, `portfolio_id?`)/응답(`liquidated`) 계약 (REQ-045)
|
||||
- `resume_kill_switch` payload/응답 계약 (REQ-046)
|
||||
- snapshot masking 소스에 "kill-switch 당일 제외" 추가 명시 (REQ-046)
|
||||
- `docs/runtime-flows.md` 에 아래 흐름을 추가한다:
|
||||
- kill-switch 발동 흐름(control RPC → 범위별 holding 순차 sell() → 당일 제외 표시) (REQ-045)
|
||||
- 당일 제외 masking / 다음 거래일 자동 복귀 / 수동 resume 흐름 (REQ-046)
|
||||
- `docs/project-structure.md` 에 아래를 반영한다:
|
||||
- control consumer(`trex.control.req`) 로의 전환 (REQ-043)
|
||||
- 신규 `killswitch_exclusion` 테이블 + `005_create_killswitch_exclusion.sql` + kill-switch store 모듈 (REQ-044)
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given `docs/interface-contract.md` 를 읽을 때, When 제어 채널을 찾으면, Then `trex.control.req` + action 목록(kill_switch/resume_kill_switch 포함)이 명시되어 있다.
|
||||
2. Given `docs/interface-contract.md` 를 읽을 때, When kill_switch/resume_kill_switch 를 찾으면, Then payload(scope/portfolio_id)와 응답 구조가 명시되어 있다.
|
||||
3. Given `docs/runtime-flows.md` 를 읽을 때, When kill-switch 관련 흐름을 찾으면, Then 발동(순차 sell()+당일 제외)·masking·자동 복귀·수동 resume 흐름이 서술되어 있다.
|
||||
4. Given `docs/project-structure.md` 를 읽을 때, When 제어 채널/저장소를 찾으면, Then control consumer 전환 + `killswitch_exclusion` 테이블/store + `005_*.sql` 이 반영되어 있다.
|
||||
|
||||
## Impacted Area
|
||||
|
||||
- Docs:
|
||||
- `docs/interface-contract.md`
|
||||
- `docs/runtime-flows.md`
|
||||
- `docs/project-structure.md`
|
||||
- Modules: 없음 (문서 전용)
|
||||
- Tests: 없음
|
||||
|
||||
## Notes
|
||||
|
||||
- 이 REQ 는 REQ-043~046 구현 완료 후 batch verification 단계에서 최신 구현 상태를 반영해 수행한다(REQ-042 선례).
|
||||
- baseline 문서가 없는 갭이 아니라 기존 baseline 갱신이므로 `bootstrap-baseline` 선행 불필요.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260607-01
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- dcy-008(confirmed) → REQ-047 초안 생성.
|
||||
|
||||
#### Intent
|
||||
|
||||
- kill-switch 구현 후 세 baseline 문서 정합을 Documentation REQ 로 정규화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Added: AC-1~4 전체
|
||||
|
||||
#### Impacted Area Delta
|
||||
|
||||
- Docs: interface-contract.md, runtime-flows.md, project-structure.md
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: none (미할당)
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 로 승인 (REQ-043~046 구현 batch 이후 연계).
|
||||
|
||||
### CHG-20260607-02
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- Proposed → Approved (confirm-req).
|
||||
|
||||
#### Intent
|
||||
|
||||
- 승인 게이트 통과: 필수 필드·구체 AC(1~4)·Impacted Area·Change Log 완비. baseline 정합 문서 REQ로 REQ-043~046 구현 후 verification 단계 수행 예정. 범위와 모순 없음.
|
||||
|
||||
#### 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 / draft-batch 로 delivery 편성(REQ-043~046 이후 연계).
|
||||
@@ -0,0 +1,115 @@
|
||||
# req-043: trex.control.req 제어 채널 + action dispatch
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
|
||||
기존 `trex.pause.req` 단일 목적 큐를 `trex.control.req` 제어 채널로 개념 확장하고, payload `action` 필드 기반 dispatch로 운영자 제어 명령을 단일 채널에서 처리한다. 기능마다 큐/consumer 스레드를 늘리지 않기 위한 단일 제어 채널 + action discriminator 패턴(dcy-008 OQ-1 Answered). 기존 pause/resume/list 동작을 보존하면서 kill-switch(REQ-045)와 resume_kill_switch(REQ-046) 라우팅 진입점을 추가한다.
|
||||
|
||||
## Requirement
|
||||
|
||||
- 데몬은 단일 제어 큐 `trex.control.req` 를 소비하고 메시지 payload 의 `action` 필드로 핸들러를 분기한다.
|
||||
- 기존 제어 action(`pause`/`resume`/`list`, REQ-032)의 동작·응답 계약을 그대로 유지한다.
|
||||
- 신규 action 라우팅을 추가한다: `kill_switch`(REQ-045), `resume_kill_switch`(REQ-046). 본 REQ는 라우팅·메시지 계약 골격만 정의하고 실제 청산/해제 동작은 각 REQ에서 정의한다.
|
||||
- 알 수 없는 `action` 은 `{status: "error", reason: "unknown_action:<action>"}` 으로 응답한다.
|
||||
- 기존 `trex.pause.req` consumer 스레드를 control consumer(`trex.control.req`)로 교체한다. 데몬 워커 스레드 총수는 불변(7개 유지).
|
||||
- 하위호환: `trex.pause.req` 를 사용하던 기존 클라이언트 호환 방식(별칭 큐 유지 vs `trex.control.req` 단일화)은 배포 영향이 있으므로 Notes 에 결정 필요로 남긴다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given `trex.control.req` 에 `{action: "pause", portfolio_id}` 발행, When 처리되면, Then 기존 REQ-032 pause 동작과 동일하게 `snapshot_pause` 에 반영되고 `{status: "ok", paused[], action}` 응답을 반환한다.
|
||||
2. Given `{action: "resume", portfolio_id}` / `{action: "list"}`, When 처리되면, Then 각각 기존 resume/list 동작과 동일한 응답을 반환한다.
|
||||
3. Given `{action: "kill_switch", ...}`, When 처리되면, Then kill-switch 핸들러(REQ-045)로 라우팅된다.
|
||||
4. Given `{action: "resume_kill_switch", ...}`, When 처리되면, Then resume 핸들러(REQ-046)로 라우팅된다.
|
||||
5. Given `{action: "bogus"}`, When 처리되면, Then `{status: "error", reason: "unknown_action:bogus"}` 를 반환한다.
|
||||
6. Given 데몬 기동, When 워커 스레드를 확인하면, Then pause consumer 가 control consumer 로 교체되어 총 워커 스레드 수가 7로 불변이다.
|
||||
|
||||
## Impacted Area
|
||||
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — 제어 채널 계약(`trex.control.req` + action 목록) (REQ-047 처리)
|
||||
- Modules:
|
||||
- `trex_core/adapters/messaging/topology.py` — `CONTROL_REQ_QUEUE = "trex.control.req"` 추가(또는 `PAUSE_REQ_QUEUE` 대체)
|
||||
- `trex_core/application/messaging_handlers.py` — `make_control_rpc_handler`(action dispatch) 신설 또는 `make_pause_rpc_handler` 확장
|
||||
- `trex_core/runtime/daemon.py` — pause consumer → control consumer 교체
|
||||
- Tests:
|
||||
- control dispatch 단위 테스트(pause/resume/list 회귀 + kill_switch/resume_kill_switch 라우팅 + unknown action)
|
||||
|
||||
## Notes
|
||||
|
||||
- dcy-008 OQ-1 Answered: 단일 제어 채널 + action discriminator. 새 큐 추가 없음.
|
||||
- 하위호환 결정 필요(배포): 기존 `trex.pause.req` 토픽 이름을 유지(별칭)할지, `trex.control.req` 로 단일화하고 전략/운영 클라이언트를 함께 갱신할지 — batch planning/release 단계에서 확정. 현재 CLI pause/resume 는 store 직접 호출이라 영향 없음(RPC 경로만 대상).
|
||||
- kill_switch/resume_kill_switch 의 payload 상세(scope/portfolio_id)는 REQ-045/046 에서 정의.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260607-01
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- dcy-008(confirmed) → REQ-043 초안 생성.
|
||||
|
||||
#### Intent
|
||||
|
||||
- kill-switch 트리거 표면(OQ-1 Answered)을 단일 제어 채널 Interface REQ 로 정규화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Added: AC-1~6 전체
|
||||
|
||||
#### Impacted Area Delta
|
||||
|
||||
- Docs: interface-contract.md (REQ-047)
|
||||
- Modules: topology.py, messaging_handlers.py, daemon.py
|
||||
- Tests: control dispatch 단위
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: none (미할당)
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 로 승인.
|
||||
|
||||
### CHG-20260607-02
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- Proposed → Approved (confirm-req).
|
||||
|
||||
#### Intent
|
||||
|
||||
- 승인 게이트 통과: 필수 필드·구체 AC·Impacted Area·Change Log 완비. dcy-008(confirmed) 범위·결정(OQ-1/2/4 Answered, OQ-3 Deferred)과 모순 없음. 하위호환(큐 rename)은 batch/release 단계 비핵심 운영 결정으로 승인 blocker 아님.
|
||||
|
||||
#### 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 / draft-batch 로 delivery 편성.
|
||||
@@ -0,0 +1,122 @@
|
||||
# req-045: kill-switch 실행 — 범위별 holding 즉시 청산 + 이력 로깅
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
|
||||
운영자가 `trex.control.req` 에 `action: "kill_switch"` 를 발행하면 대상 범위(portfolio 단위 또는 전역)의 모든 `holding` 슬롯을 즉시 청산한다. 기존 `SlotService.sell()`(REQ-036)을 재사용해 순차 매도를 제출하고, 같은 발동에서 대상 범위를 당일 제외(REQ-044/046)로 표시한다. 자율 안전 매도(dcy-007)와 독립적으로 동작하며, 발동 이력을 로깅한다(dcy-008 FR-1/4/5/6, NFR-1/2).
|
||||
|
||||
## Requirement
|
||||
|
||||
- `action: "kill_switch"` payload: `{action, scope: "portfolio"|"global", portfolio_id?(scope=portfolio 시 필수), actor?}`.
|
||||
- 발동 시 대상 범위의 모든 `holding` 슬롯에 `SlotService.sell(actor="kill-switch", reason="emergency_stop")` 을 순차 호출한다.
|
||||
- scope=portfolio: 해당 `portfolio_id` 의 holding 슬롯만.
|
||||
- scope=global: 당일 전체 holding 슬롯.
|
||||
- 이미 `pending_sell` 인 슬롯은 중복 sell 하지 않는다(REQ-036 idempotency).
|
||||
- 발동과 동시에 대상 범위를 당일 제외로 표시한다(REQ-044 store 의 `exclude(...)` 호출). masking 동작 자체는 REQ-046.
|
||||
- 발동 이력(actor, 발동 시각, scope, portfolio_id, 처리 슬롯 수)을 데몬 로그에 1건 기록한다.
|
||||
- 응답: `{status: "ok", scope, portfolio_id?, liquidated: <처리 슬롯 수>}`. scope=portfolio 인데 `portfolio_id` 누락 시 `{status: "error", reason: "portfolio_id_required"}`.
|
||||
- kill-switch 실행은 자율 안전 매도 루프(dcy-007)와 상태/스레드를 공유하지 않으며 서로 영향을 주지 않는다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given holding 슬롯 N개(전역), When `{action:"kill_switch", scope:"global"}` 처리, Then N개 모두 `holding→pending_sell`(actor=kill-switch, reason=emergency_stop) 전이되고 응답 `liquidated=N`.
|
||||
2. Given portfolio `pf-A` 에 holding 2개 + `pf-B` 에 holding 1개, When `{action:"kill_switch", scope:"portfolio", portfolio_id:"pf-A"}`, Then `pf-A` 2개만 청산되고 `pf-B` 는 holding 유지.
|
||||
3. Given 대상 중 이미 `pending_sell` 인 슬롯, When kill_switch 처리, Then 그 슬롯에 중복 sell 호출이 없다(REQ-036 idempotency, transition_log 중복 없음).
|
||||
4. Given scope=portfolio 인데 `portfolio_id` 누락, When 처리, Then `{status:"error", reason:"portfolio_id_required"}`.
|
||||
5. Given kill_switch 발동, When 처리 완료, Then 대상 범위가 REQ-044 store 에 당일 제외로 기록된다(`list_excluded(today)` 에 포함).
|
||||
6. Given kill_switch 발동, When 로그를 확인, Then actor/발동 시각/scope/portfolio_id/처리 슬롯 수를 담은 이력 1건이 기록된다.
|
||||
7. Given 자율 안전 매도 루프(dcy-007)가 동작 중, When kill_switch 가 발동, Then 두 경로가 독립적으로 동작하며(REQ-036 idempotency 로 충돌 없음) safety-sell 루프가 중단/오류 없이 유지된다.
|
||||
8. Given holding N개, When kill_switch 처리, Then 전체 sell() 제출 완료까지 지연이 N×100ms + 5s 이내이다(순차, outbox 전송 제외; A-4 N<10 가정).
|
||||
|
||||
## Impacted Area
|
||||
|
||||
- Docs:
|
||||
- `docs/runtime-flows.md` — kill-switch 발동 흐름(트리거→순차 sell()→당일 제외 표시) (REQ-047 처리)
|
||||
- `docs/interface-contract.md` — kill_switch payload/응답 계약 (REQ-047 처리)
|
||||
- Modules:
|
||||
- `trex_core/application/messaging_handlers.py` — kill_switch handler(REQ-043 dispatch 하위)
|
||||
- `trex_core/domain/slot/service.py` 또는 신규 application 서비스 — 범위별 holding 조회 + 순차 sell() 진입점
|
||||
- REQ-044 store 연동(당일 제외 표시)
|
||||
- Tests:
|
||||
- kill_switch handler 단위 테스트(전역/portfolio/idempotency/portfolio_id 누락/이력 로깅/독립성)
|
||||
|
||||
## Notes
|
||||
|
||||
- NFR-1(지연)·NFR-2(dcy-007 독립)는 본 REQ AC-8/AC-7 로 흡수(별도 REQ 미승격 — 독립 backlog 가치 낮음, dcy-008 정합).
|
||||
- pending_buy 슬롯 처리는 dcy-008 OQ-3 Deferred → 기본 구현은 HOLDING 만 대상. pending_buy 롤백은 후속(REQ 추가 또는 change-req).
|
||||
- R-1(큐 부하): sell()은 PENDING_SELL 전이(DB write 1회)로 빠름. execution worker 순차 처리 유지.
|
||||
- R-3(브로커 오류): sell()→PENDING_SELL 성공 시 기존 outbox retry 가 eventual consistency 보장.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260607-01
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- dcy-008(confirmed) → REQ-045 초안 생성.
|
||||
|
||||
#### Intent
|
||||
|
||||
- kill-switch 즉시 청산(FR-1/4/5/6)과 NFR-1/2 흡수를 Interface REQ 로 정규화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Added: AC-1~8 전체
|
||||
|
||||
#### Impacted Area Delta
|
||||
|
||||
- Docs: runtime-flows.md, interface-contract.md (REQ-047)
|
||||
- Modules: messaging_handlers.py, service.py(또는 신규 서비스), REQ-044 store 연동
|
||||
- Tests: kill_switch handler 단위
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: none (미할당)
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 로 승인.
|
||||
|
||||
### CHG-20260607-02
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- Proposed → Approved (confirm-req).
|
||||
|
||||
#### Intent
|
||||
|
||||
- 승인 게이트 통과: 필수 필드·구체 AC(1~8)·Impacted Area·Change Log 완비. dcy-008(confirmed) 범위와 모순 없음. NFR-1/2 흡수, OQ-3(pending_buy) REQ Deferred 는 비핵심 후속으로 승인 blocker 아님(기본 구현 HOLDING 한정).
|
||||
|
||||
#### 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 / draft-batch 로 delivery 편성.
|
||||
@@ -0,0 +1,121 @@
|
||||
# req-046: kill-switch 당일 제외 masking + 자동 해제 + 수동 resume
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: project maintainers
|
||||
|
||||
## Intent
|
||||
|
||||
kill-switch 발동(REQ-045)으로 표시된 당일 제외 상태를 snapshot RPC 응답에 반영해 대상 자산을 당일 전략 실행에서 제외한다(추가 매수 차단). 제외는 day-scoped 라 다음 거래일 자동 복귀하며(REQ-044 `trading_date` 키), 운영자는 같은 거래일 내 `action: "resume_kill_switch"` 로 수동 해제할 수 있다(dcy-008 FR-2/3, NFR-3, OQ-4 Answered).
|
||||
|
||||
## Requirement
|
||||
|
||||
- snapshot RPC 응답 산출 시 masking 소스에 "kill-switch 당일 제외"(REQ-044 `list_excluded(today)`)를 추가한다: `mask = pending | warning | pause | killswitch_today`.
|
||||
- scope=portfolio 제외는 해당 `portfolio_id` 자산만, 전역 제외는 당일 전체 자산을 마스킹한다.
|
||||
- 마스킹된 자산은 buyable/held 뷰에 노출되지 않아 당일 추가 매수 의도가 거부된다.
|
||||
- 당일 제외는 `trading_date` 기준 day-scoped 다. 다음 거래일 첫 조회/부팅 시 `list_excluded(다음 날)` 에 포함되지 않아 자동 복귀한다(별도 해제 동작 불필요).
|
||||
- 영속성: 데몬 재시작 후에도 같은 거래일이면 masking 이 유지된다(REQ-044 영속, NFR-3).
|
||||
- 수동 해제: `action: "resume_kill_switch"` payload `{action, scope, portfolio_id?, actor?}` 처리 시 대상 범위의 당일 제외를 해제(REQ-044 `clear(...)`)해 다음 조회부터 정상 매매 대상으로 복귀시킨다.
|
||||
- resume 은 **당일 제외(매매 차단)만 해제**하며 이미 청산(매도)된 슬롯을 되돌리지 않는다(매도 비가역).
|
||||
- 응답: `{status: "ok", scope, portfolio_id?}`. scope=portfolio 인데 `portfolio_id` 누락 시 `{status:"error", reason:"portfolio_id_required"}`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Given `pf-A` 가 당일 제외(REQ-044)로 표시됨, When snapshot RPC(view=buyable/all) 처리, Then `pf-A` 자산이 응답 assets 에서 마스킹되어 노출되지 않는다.
|
||||
2. Given 전역 당일 제외 표시, When snapshot RPC 처리, Then 당일 전체 자산이 마스킹된다.
|
||||
3. Given 당일 제외 표시 후, When 당일 추가 매수 intent 가 해당 자산에 들어오면, Then buyable 마스킹으로 거부/비노출된다.
|
||||
4. Given `pf-A` 당일(D) 제외, When 거래일 D+1 로 snapshot RPC 처리, Then `pf-A` 가 다시 노출된다(자동 복귀, 별도 resume 불필요).
|
||||
5. Given `pf-A` 제외 후 데몬 재시작(같은 거래일 D), When snapshot RPC 처리, Then 여전히 마스킹된다(영속, NFR-3).
|
||||
6. Given `pf-A` 당일 제외 상태, When `{action:"resume_kill_switch", scope:"portfolio", portfolio_id:"pf-A"}` 처리, Then 다음 snapshot RPC 부터 `pf-A` 가 정상 노출되고 응답 `{status:"ok"}`.
|
||||
7. Given resume_kill_switch scope=portfolio 인데 `portfolio_id` 누락, When 처리, Then `{status:"error", reason:"portfolio_id_required"}`.
|
||||
8. Given resume_kill_switch 처리, When 이미 청산된 슬롯을 확인, Then 슬롯 상태(pending_sell/closed)는 되돌려지지 않는다(매도 비가역).
|
||||
|
||||
## Impacted Area
|
||||
|
||||
- Docs:
|
||||
- `docs/interface-contract.md` — masking 소스에 kill-switch 당일 제외 + resume_kill_switch 계약 (REQ-047 처리)
|
||||
- `docs/runtime-flows.md` — 당일 제외 masking / 자동 복귀 / 수동 resume 흐름 (REQ-047 처리)
|
||||
- Modules:
|
||||
- `trex_core/application/portfolio.py`(또는 snapshot envelope builder) — masking 소스에 `list_excluded(today)` 추가
|
||||
- `trex_core/application/messaging_handlers.py` — `resume_kill_switch` handler(REQ-043 dispatch 하위) + snapshot 핸들러에 killswitch store 주입
|
||||
- REQ-044 store 연동(`list_excluded`/`clear`)
|
||||
- Tests:
|
||||
- masking 단위 테스트(portfolio/전역/추가매수 거부) + day-scoped 자동 복귀(날짜 mock) + 재시작 영속 + resume 단위
|
||||
|
||||
## Notes
|
||||
|
||||
- dcy-008 OQ-4 Answered: 수동 해제 지원(`resume_kill_switch`). 자동 복귀(day-scoped)와 병행.
|
||||
- NFR-3(영속 후 재시작 masking 유지)는 본 REQ AC-5 로 흡수.
|
||||
- 전역 제외 sentinel 해석은 REQ-044 store 계약을 따른다.
|
||||
- masking 합성은 기존 pause masking 패턴(`portfolio.py`)을 재사용(A-2).
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260607-01
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- dcy-008(confirmed) → REQ-046 초안 생성.
|
||||
|
||||
#### Intent
|
||||
|
||||
- 당일 제외 masking/자동 복귀/수동 resume(FR-2/3, OQ-4, NFR-3 흡수)을 Interface REQ 로 정규화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
|
||||
- Added: AC-1~8 전체
|
||||
|
||||
#### Impacted Area Delta
|
||||
|
||||
- Docs: interface-contract.md, runtime-flows.md (REQ-047)
|
||||
- Modules: portfolio.py, messaging_handlers.py, REQ-044 store 연동
|
||||
- Tests: masking + day-scoped + 영속 + resume 단위
|
||||
|
||||
#### Delivery Trace Delta
|
||||
|
||||
- Batches: none (미할당)
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 로 승인.
|
||||
|
||||
### CHG-20260607-02
|
||||
|
||||
- Date: 2026-06-07
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
|
||||
- Proposed → Approved (confirm-req).
|
||||
|
||||
#### Intent
|
||||
|
||||
- 승인 게이트 통과: 필수 필드·구체 AC(1~8)·Impacted Area·Change Log 완비. dcy-008(confirmed) OQ-4 결정(resume_kill_switch) 반영, NFR-3 흡수. 범위·결정과 모순 없음.
|
||||
|
||||
#### 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 / draft-batch 로 delivery 편성.
|
||||
+8
-3
@@ -74,7 +74,7 @@
|
||||
|
||||
## Next Requirement ID
|
||||
|
||||
- Current: `REQ-043`
|
||||
- Current: `REQ-048`
|
||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||
|
||||
## Requirement Register
|
||||
@@ -123,11 +123,16 @@
|
||||
| REQ-040 | update_thresholds intent RPC 액션 | Interface | Implemented | High | project maintainers | [req-040](./Interface/req-040_update-thresholds-intent-rpc.md) |
|
||||
| REQ-041 | safety-sell 환경 설정 — 5종 env var | Configuration | Implemented | High | project maintainers | [req-041](./Configuration/req-041_safety-sell-env-config.md) |
|
||||
| REQ-042 | safety-sell baseline 문서 정합 | Documentation | Implemented | Medium | project maintainers | [req-042](./Documentation/req-042_safety-sell-baseline-alignment.md) |
|
||||
| REQ-043 | trex.control.req 제어 채널 + action dispatch | Interface | Approved | High | project maintainers | [req-043](./Interface/req-043_control-channel-action-dispatch.md) |
|
||||
| REQ-044 | kill-switch day-scoped 제외 store + 마이그레이션 | Data | Approved | High | project maintainers | [req-044](./Data/req-044_killswitch-exclusion-store-and-migration.md) |
|
||||
| REQ-045 | kill-switch 실행 — 범위별 holding 즉시 청산 + 이력 | Interface | Approved | High | project maintainers | [req-045](./Interface/req-045_kill-switch-execution.md) |
|
||||
| REQ-046 | kill-switch 당일 제외 masking + 자동해제 + resume | Interface | Approved | High | project maintainers | [req-046](./Interface/req-046_killswitch-day-exclusion-masking-and-resume.md) |
|
||||
| REQ-047 | kill-switch baseline 문서 정합 | Documentation | Approved | Medium | project maintainers | [req-047](./Documentation/req-047_killswitch-baseline-alignment.md) |
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-06-07 :: REQ-043~047 Proposed → Approved (confirm-req): kill-switch 5개 전원 승인 게이트 통과. dcy-008(confirmed) 범위·결정(OQ-1/2/4 Answered, OQ-3 Deferred)과 모순 없음, 필수 필드·구체 AC·Impacted Area·Change Log 완비. 큐 rename 호환·전역 sentinel·pending_buy 는 비핵심 후속으로 blocker 아님. (confirm-req)
|
||||
- 2026-06-07 :: REQ-043~047 생성 (draft-req): dcy-008(비상 정지/kill-switch, confirmed) → 5개 REQ 초안. 제어 채널 `trex.control.req`+action dispatch(043), day-scoped 제외 store+마이그레이션(044), kill-switch 실행=범위별 holding 순차 sell()+이력(045), 당일 제외 masking+자동복귀+resume_kill_switch(046), baseline 정합(047). NFR-1/2/3 은 045/046 AC 로 흡수, OQ-3(pending_buy) REQ Deferred. (draft-req)
|
||||
- 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)
|
||||
|
||||
Reference in New Issue
Block a user