5.3 KiB
5.3 KiB
BAT-012 Design
- Status: confirmed
- Batch: BAT-012
Architecture Summary
BAT-012 는 운영자 비상 정지(kill-switch)를 기존 인프라 위에 추가한다.
- 제어 채널 일원화 (
trex.control.req): 기존trex.pause.req단일 목적 큐를actiondiscriminator 기반 제어 채널로 확장.make_control_rpc_handler가 pause/resume/list(REQ-032 위임) + kill_switch(REQ-045) + resume_kill_switch(REQ-046)를 분기. daemon 의 pause consumer 스레드를 control consumer 로 교체(스레드 수 7 불변). - day-scoped 제외 store (
killswitch_exclusion+PostgreSQLKillSwitchStore):(portfolio_id, trading_date)키.snapshot_pause(영구 pause)와 분리.list_excluded(today)가 당일 항목만 반환 → day-scoped 자동 복귀. - kill_switch 실행: 범위별 holding 조회 → 순차
SlotService.sell()→ 당일 제외 표시 → 이력 로깅. dcy-007 safety-sell 과 독립(REQ-036 idempotency 로 충돌 무해). - masking 합성 + resume: snapshot envelope masking 에
killswitch_today추가.resume_kill_switch가 day-scoped 제외 row 삭제(청산된 슬롯은 비가역).
Changed Areas
- Docs:
docs/interface-contract.md— 제어 채널trex.control.req+ action 목록 + kill_switch/resume_kill_switch payload/응답 + masking 소스 (REQ-047)docs/runtime-flows.md— kill-switch 발동/masking/자동복귀/resume 흐름 (REQ-047)docs/project-structure.md— control consumer + killswitch_exclusion 테이블/store + 005 sql (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) + kill_switch / resume_kill_switch handlertrex_core/adapters/persistence/sql/005_create_killswitch_exclusion.sql(신규)trex_core/adapters/portfolio/killswitch_store.py—PostgreSQLKillSwitchStoretrex_core/application/portfolio.py(envelope builder) — masking 소스에list_excluded(today)추가trex_core/application/killswitch.py(신규, 후보) 또는domain/slot/service.py— 범위별 holding 순차 sell() 진입점trex_core/runtime/daemon.py— pause consumer → control consumer 교체 + killswitch_store 배선 + snapshot/control handler 주입
- Interfaces:
trex.control.reqaction 계약:pause|resume|list|kill_switch|resume_kill_switch- kill_switch payload
{action, scope: portfolio|global, portfolio_id?, actor?}→{status, scope, portfolio_id?, liquidated} - resume_kill_switch payload
{action, scope, portfolio_id?, actor?}→{status, scope, portfolio_id?} KillSwitchStore.exclude/clear/list_excluded(trading_date)
Key Decisions (초안 — 구현 전 확정 대상)
- 제어 큐 전환 방식:
trex.control.req단일 채널로 일원화. 기존trex.pause.req하위호환은 (a) 별칭 큐 동시 선언 유지 vs (b) control.req 단일화 + 클라이언트 갱신 중 택1 — release rollout 영향이라 확정 필요(R-1). 기본안: control.req 단일화(RPC pause 사용처는 전략/운영 한정, CLI 무영향). - 전역 제외 표현:
killswitch_exclusion.portfolio_id = '*'sentinel(기본안) vs scope 컬럼 분리. masking 소비처(REQ-046)와 계약만 일치하면 됨. 기본안:'*'sentinel +list_excluded가 sentinel 포함 시 전체 마스킹 신호로 반환. - kill_switch 실행 진입점: 신규
application/killswitch.py(범위별 holding 조회 + 순차 sell() + store exclude + 로깅)로 분리(기본안). domain service 는 단일 슬롯 책임 유지, 복수 슬롯 오케스트레이션은 application 계층. - trading_date 기준: 데몬 기존 관례(UTC date) 사용 — REQ-045 표시·REQ-046 조회·자동복귀 모두 동일 키. (safety-sell
_trading_date(now)와 일치) - dcy-007 독립성: kill_switch 는 safety-sell worker 스레드와 상태/락 공유 없음. 동일 슬롯 동시 sell() 은 REQ-036 PENDING_SELL idempotency 로 1회만 전이.
Edge Cases
- kill_switch scope=portfolio + portfolio_id 누락 →
{status:error, reason:portfolio_id_required}. - 대상 중 이미 pending_sell → sell() 건너뜀(idempotent), liquidated 카운트에서 제외 여부는 구현 결정(기본: 신규 전이만 카운트).
- resume_kill_switch 후 이미 청산된 슬롯 → 상태 불변(매도 비가역). resume 은 제외 row 만 삭제.
- 다음 거래일 →
list_excluded(D+1)에 전일 row 미포함 → 자동 복귀(별도 동작 불필요). - 데몬 재시작(같은 거래일) → store 영속 → masking 유지(NFR-3).
- unknown action →
{status:error, reason:unknown_action:<a>}.
Architecture Impact
- runtime-flows: 제어 채널 + kill-switch 4흐름 추가 — REQ-047 에서 문서화.
- project-structure: control consumer 전환 + 신규 store/테이블/sql — REQ-047 에서 문서화.
- interface-contract: pause→control 채널 계약 갱신 — REQ-047.
Reference Doc Update Plan
interface-contract.md: 제어 채널 action 목록 + kill_switch/resume_kill_switch 계약 + masking 소스 (REQ-047 AC-1/2)runtime-flows.md: 발동/masking/자동복귀/resume 흐름 (REQ-047 AC-3)project-structure.md: control consumer + killswitch_exclusion/store + 005 sql (REQ-047 AC-4)