Files
trex-core/docs/batches/bat-010_20260607_safety-sell-foundation/planning.md
T
genichinandClaude Opus 4.8 a30efa8f3b feat(safety-sell): dcy-007 core 자율 안전 매도 — BAT-010 기반 + BAT-011 트리거
dcy-007(core 자율 안전 매도) 전체 delivery. BAT-010 기반 인프라가 미커밋
상태였고 BAT-011 트리거 레이어와 공유 파일에서 섞여 있어 한 커밋으로 묶음.

BAT-010 (REQ-034/035/036/041, release-candidate):
- WatchdogStore(application/watchdog.py) + snapshot RPC 워치독 갱신
- Slot stop_loss_price/take_profit_price 필드 + 004_add_price_thresholds.sql
- SlotService.sell() 자율 매도 내부 경로 + idempotency
- SafetySellConfig env 5종 파싱

BAT-011 (REQ-037/038/039/040/042, release-candidate):
- SafetySellService(application/safety_sell.py): forced_close 만기 / stop-loss·
  take-profit KIS polling / 장 마감 강제 청산 + trex-safety-sell 7번째 스레드
- BrokerPort.get_current_price + KIS inquire-price(TTTC8397R/VTTC8397R, stck_prpr)
  + paper stub
- SlotService.update_thresholds() + intent RPC update_thresholds 액션
  (부분 갱신/null 해제, 워치독 last_seen_at 리셋)
- baseline 3문서 정합(interface-contract/runtime-flows/project-structure)

거버넌스: REQ-034~042 Implemented 동기화, BAT-010/011 release-candidate,
dcy-007 discovery 포함.

검증: 신규 38 테스트 green, 단위 236 passed/23 skipped,
인프라 게이트(PG+RabbitMQ) 259 passed(마이그레이션 001~004 round-trip 포함),
ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 08:32:30 +09:00

3.7 KiB

BAT-010 Planning

  • Status: confirmed
  • Batch: BAT-010
  • Profile: standard

Included REQ

  • REQ-041 (Configuration): safety-sell 환경 변수 5종 파싱 — TREX_WATCHDOG_TIMEOUT(s, 기본 120), TREX_WATCHDOG_GRACE_PERIOD(s, 기본 60), TREX_FORCED_CLOSE_CHECK_INTERVAL(s, 기본 60), TREX_STOP_LOSS_POLL_INTERVAL(s, 기본 30), TREX_MARKET_CLOSE_LIQUIDATION_TIME(HH:MM KST, optional)
  • REQ-035 (Data): stop_loss_price · take_profit_price Optional[Decimal] 도메인 필드 + PostgreSQL additive 마이그레이션
  • REQ-034 (Interface): per-slot 워치독 — WatchdogStore(dict[slot_id, datetime]) + snapshot RPC handler 확장 + grace period
  • REQ-036 (Interface): SlotService.sell(slot_id, actor, reason) — holding→pending_sell 전이 + idempotency

Delivery Plan

  1. REQ-041: runtime/config.py에 safety-sell env vars 5종 파싱 추가 (DaemonConfig 확장). 파싱 단위 테스트.
  2. REQ-035: domain/slot/models.py에 두 필드 추가 → adapters/persistence/postgresql.py read/write 포함 → SQL 마이그레이션 스크립트 작성 → application/intents.py + messaging_handlers.py payload 파싱 추가. 필드 저장·조회 round-trip 테스트 + 마이그레이션 멱등성 테스트.
  3. REQ-034: application/watchdog.py 신규 작성 (WatchdogStore class, grace period 판정) → make_snapshot_rpc_handler 확장 (응답 슬롯 last_seen_at 갱신). 워치독 판정 단위 테스트 (만료/비만료/grace period 시나리오).
  4. REQ-036: domain/slot/service.py에 sell(slot_id, actor, reason) 추가 (transit_slot 기반, idempotency). 단위 테스트 3종(holding→pending_sell, pending_sell 중복 호출, idle 오류).
  5. 전체 회귀 테스트 green + ruff lint clean.

Dependencies

  • Internal:
    • BAT-007/REQ-022: make_snapshot_rpc_handler 기존 구현 — 확장 대상
    • BAT-008/REQ-027: SlotService 기존 구조 + transit_slot 패턴 — sell() 기반
    • BAT-004/REQ-011~012: PaperBroker execution 경로 — sell() 통합 검증 대상
  • External:
    • 없음 (KIS API 호출 없음, 신규 daemon thread 없음)

Design Gate

  • Design Required Before Implementation: yes
  • Reason: WatchdogStore는 신규 in-memory 컴포넌트로 snapshot RPC handler thread에서 write, BAT-011 트리거 루프 thread에서 read가 발생한다. 동시성 모델(GIL 의존 vs. explicit lock)과 모듈 배치(watchdog.py 분리 vs. portfolio.py 확장)를 설계 단계에서 확정해야 BAT-011 트리거 루프 설계 시 인터페이스 충돌이 없다. SlotService.sell()과 transit_slot advisory lock 통합 방식도 설계에서 명시한다.

Milestones

  1. M1 — REQ-041: env vars 파싱 + unit test green
  2. M2 — REQ-035: 도메인 필드 + 마이그레이션 + persistence round-trip 테스트 green
  3. M3 — REQ-034: WatchdogStore + snapshot RPC handler 확장 + 워치독 판정 unit test green
  4. M4 — REQ-036: SlotService.sell() + idempotency unit test green (3종)
  5. M5 — 전체 회귀 + ruff lint clean

Risks

  • R-1 (Medium): WatchdogStore 동시성 — BAT-011 트리거 루프가 watchdog store를 read하는 시점에 snapshot RPC handler가 동시 write할 수 있음. 대응: Python GIL 보장 하에 단순 dict 사용; 별도 threading.Lock 회피. 단위 테스트에서 판정 로직 고정.
  • R-2 (Medium): DB 마이그레이션 기존 row 영향. 대응: NULL 허용 additive 컬럼 추가 전용(ALTER TABLE … ADD COLUMN IF NOT EXISTS). 기존 row NULL 유지. migration 멱등성 테스트.
  • R-3 (Low): SlotService.sell() advisory lock 통합 — transit_slot 기존 advisory lock과 충돌 가능성. 대응: 기존 transit_slot() 코드 경로를 최대한 재사용하고 신규 로직을 최소화한다.