Files
trex-core/docs/discovery/dcy-007_20260606_core-autonomous-safety-sell.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

24 KiB
Raw Blame History

0. 문서 상태

  • 상태: confirmed
  • 문서 ID: dcy-007_20260606_core-autonomous-safety-sell
  • 이슈명: core 자율 안전 매도 — stop-loss·take-profit·시간 만기 자동 트리거
  • 작성 시각(KST): 2026-06-06
  • 마지막 갱신 시각(KST): 2026-06-07 (draft-req: REQ-034~042 생성)
  • 대체됨: 없음
  • 후속 Discovery 참조: 없음
  • 생성된 REQ 참조: REQ-034, REQ-035, REQ-036, REQ-037, REQ-038, REQ-039, REQ-040, REQ-041, REQ-042

1. 계획 상태 요약

  • 연계 문서 현황:
    • 관련 참조 파일: 없음
    • 관련 TODO/메모: 없음
  • 해석:
    • REL-001 은 forced_close_at 필드만 존재하고 만기 트리거 로직이 없는 상태로 baseline 을 확정했다. stop-loss 는 필드·코드 모두 없다. dcy-006 A-5 와 BAT-007 R-1(High) 에서 "미구현 상태로 운영 release 전 별도 트래킹 필요"로 명시됐다.
    • 이번 Discovery 는 (1) snapshot RPC 기반 per-slot 워치독으로 전략 모듈 활성 여부를 감지하고, (2) 워치독 만료 슬롯에 한해 forced_close_at 만기·stop-loss(손절)·take-profit(익절) 가격 조건을 자율 매도로 처리하며, (3) 장 마감 시각(TREX_MARKET_CLOSE_LIQUIDATION_TIME)에 전체 holding 슬롯을 청산하는 세 가지 기능의 범위·제약·오픈 질문을 정의한다.

2. 요구사항 판정 결과

  • 판정: 신규
  • 근거
    • dcy-001~006 어디에도 stop-loss, forced_close_at 자동 트리거, 워치독, 장 마감 강제 청산을 다루는 Discovery 가 없다.
    • dcy-006 A-5 는 "추후 구현" 가정으로 명시되었고, REL-001 excluded 로 확정됐다.
    • 기존 코드에 forced_close_at 필드는 존재하나 감지·실행 루프가 없어 기능 구현이 신규임이 명확하다(trex_core/domain/slot/models.py:34).

3. 문제점의 요약

  • 현재 상태
    • 슬롯 도메인에 forced_close_at: Optional[datetime] 필드가 존재하고 PostgreSQL 에 영속되지만, 만기 여부를 주기적으로 검사하거나 자동 매도를 제출하는 로직이 없다(trex_core/domain/slot/models.py:34).
    • stop-loss(손절 가격 임계값) 를 위한 필드·코드가 전혀 없다.
    • 전략 모듈이 활성인지 비활성인지를 데몬이 감지하는 메커니즘이 없다.
    • RabbitMQ 장애나 전략 모듈 방치 상황에서 열린 포지션이 자동으로 청산되지 않고 무기한 유지될 수 있다(BAT-007 R-1 High).
    • 장 마감 후에도 holding 슬롯이 자동 청산되지 않는다.
  • 기대 상태
    • snapshot RPC 응답 시 해당 슬롯들의 워치독 타임스탬프가 갱신되며, TREX_WATCHDOG_TIMEOUT 이상 갱신되지 않은 슬롯은 "전략 모듈 미감시" 상태로 판정된다.
    • 워치독 만료 슬롯에 한해 forced_close_at 만기·stop-loss(손절)·take-profit(익절) 가격 조건 체크를 수행하고, 조건 충족 시 내부 매도 intent 를 제출한다.
    • TREX_MARKET_CLOSE_LIQUIDATION_TIME(HH:MM KST) 이 설정된 경우 해당 시각에 전략 모듈 활성 여부와 무관하게 모든 holding 슬롯을 청산한다.
    • 자동 매도 intent 는 기존 execution worker 를 통해 실행돼 outbox 내구성·슬롯 상태 전이·ledger 기록을 모두 따른다.
  • 이해관계자
    • 문서 작성자(AI agent)
    • 리뷰어/승인자: Justin Ko
    • 운영 담당자: trex-core 데몬 운영자
    • 전략 모듈 개발자: snapshot RPC 호출 주기가 워치독 만료 임계값보다 짧아야 한다는 연동 계약 합의 필요
  • 제약사항
    • trex-core 는 sync+thread 런타임이며, 신규 trigger 루프는 기존 6 스레드 아키텍처를 과도하게 변경하지 않아야 한다.
    • forced_close_at 필드가 이미 DB 스키마·도메인·persistence 에 존재하므로 하위호환을 유지하면서 트리거만 추가해야 한다.
    • trex-core#3(강제 청산/kill-switch) 는 별도 Discovery 로 분리됨(OQ-2 Answered). 이번 Discovery 는 자동 조건 트리거(시간·가격·장 마감)만 다룬다.
    • 워치독은 in-memory 이며 데몬 재시작 시 초기화된다. 재시작 후 TREX_WATCHDOG_GRACE_PERIOD(기본 60초) 동안 만료 판정 유예(OQ-4 Answered).

주요 사용자 시나리오

  • 전략 모듈이 N시간 만기 매수 슬롯을 등록(forced_close_at = 오후 3시)한 뒤 프로세스를 종료한 경우 — 워치독이 만료되고, 데몬이 3시에 해당 슬롯을 감지해 자동 매도를 제출한다.
  • RabbitMQ 장애로 전략 모듈이 snapshot RPC 를 보내지 못하는 상황 — 워치독이 만료되고, stop-loss 조건 충족 시 데몬이 자동 매도를 제출한다.
  • 전략 모듈이 정상 동작 중인 슬롯 — 워치독이 주기적으로 갱신되어 만료되지 않으므로 forced_close_at / stop-loss 체크를 건너뛴다(전략 모듈 책임).
  • TREX_MARKET_CLOSE_LIQUIDATION_TIME=15:10 설정 시 — 오후 3시 10분에 전략 모듈 활성 여부와 무관하게 전체 holding 슬롯에 매도 intent 제출.
  • 운영자가 CLI 또는 RPC 를 통해 특정 슬롯에 즉시 청산(kill-switch)을 요청하는 경우 — 별도 Discovery(trex-core#3 기반)에서 다룬다. 이번 Discovery 범위 밖.

4. 이번에 정의할 변경

  • per-slot 워치독: snapshot RPC 응답 시 해당 슬롯들의 last_seen_at 타임스탬프를 in-memory 로 갱신. TREX_WATCHDOG_TIMEOUT(초, env) 경과 시 "미감시" 판정.
  • forced_close_at 만기 감지 루프: 워치독 만료 슬롯 중 forced_close_at <= now(KST) 인 슬롯에 내부 매도 intent 제출. TREX_FORCED_CLOSE_CHECK_INTERVAL(초, env) 주기.
  • stop-loss·take-profit 임계값 필드 추가 및 가격 체크: 슬롯 도메인에 stop_loss_price: Optional[Decimal]·take_profit_price: Optional[Decimal] 추가. 워치독 만료 슬롯에 한해 KIS TTTC8397R polling(stck_prpr), 조건 현재가 <= stop_loss_price OR 현재가 >= take_profit_price 시 매도 trigger. TREX_STOP_LOSS_POLL_INTERVAL(초, env) 주기(두 조건 공통 사용).
  • 임계값 갱신 RPC: trex.intent.req 채널에 action: "update_thresholds" 추가. holding 슬롯의 stop_loss_price·take_profit_price·forced_close_at 를 매수 이후에도 갱신 가능(각 필드는 독립적으로 null 가능). 수신 시 해당 슬롯의 워치독 last_seen_at 도 리셋(전략 모듈 활성 증명).
  • 장 마감 강제 청산: TREX_MARKET_CLOSE_LIQUIDATION_TIME(HH:MM KST, optional) 설정 시 해당 시각에 모든 holding 슬롯 매도. 미설정 시 동작 없음. 워치독 만료 여부 무관.
  • 자동 매도 내부 경로: SlotService.sell() 직접 호출 — RabbitMQ intent RPC 우회, transport 장애와 독립.

영향 범위

  • 코드 영향:
    • trex_core/domain/slot/models.py: stop_loss_price: Optional[Decimal], take_profit_price: Optional[Decimal] 필드 추가
    • trex_core/domain/slot/service.py: forced_close / stop-loss / take-profit 조건 평가 메서드; update_thresholds(slot_id, stop_loss_price, take_profit_price, forced_close_at) 메서드
    • trex_core/application/portfolio.py 또는 신규 모듈: per-slot 워치독 store (dict[slot_id, datetime], in-memory)
    • trex_core/application/messaging_handlers.py: (1) snapshot RPC handler — 응답 슬롯 last_seen_at 갱신; (2) intent RPC — stop_loss_price·take_profit_price 파싱; (3) update_thresholds action handler — stop_loss_price·take_profit_price·forced_close_at DB 갱신 + last_seen_at 리셋
    • trex_core/runtime/daemon.py: safety-sell worker 스레드 추가 — forced-close/stop-loss·take-profit(워치독 만료 슬롯) + 장 마감 청산(전체) 처리
    • trex_core/runtime/config.py: TREX_WATCHDOG_TIMEOUT(기본 120), TREX_WATCHDOG_GRACE_PERIOD(기본 60), TREX_FORCED_CLOSE_CHECK_INTERVAL(기본 60), TREX_STOP_LOSS_POLL_INTERVAL(기본 30), TREX_MARKET_CLOSE_LIQUIDATION_TIME(optional) env 파싱
    • trex_core/adapters/persistence/: stop_loss_price, take_profit_price 컬럼 추가 DB 마이그레이션
    • KIS 현재가 polling 어댑터 신규: TTTC8397R(실전) / VTTC8397R(모의) 분기, stck_prpr 파싱
    • trex_core/application/intents.py: IntentIn.stop_loss_price, IntentIn.take_profit_price 필드 추가
  • 문서 영향:
    • docs/interface-contract.md: intent RPC payload 에 stop_loss_price·take_profit_price·forced_close_at 필드, update_thresholds action 계약, snapshot RPC 가 워치독 갱신 신호임을 명시
    • docs/runtime-flows.md: 워치독 갱신 흐름, forced-close checker, stop-loss 가격 체크, 장 마감 청산 흐름 추가
    • docs/project-structure.md: 신규 모듈/어댑터 반영
  • 운영 영향:
    • DB 마이그레이션: stop_loss_price, take_profit_price 컬럼 추가(additive, 기존 슬롯 NULL 허용)
    • 신규 데몬 스레드: safety-sell worker (forced-close + stop-loss·take-profit + 장 마감 통합)
    • 전략 모듈 연동 계약: snapshot RPC 호출 주기 < TREX_WATCHDOG_TIMEOUT 이어야 오탐 없음

5. 요구사항 목록

기능 요구사항

  • FR-1: 데몬은 TREX_FORCED_CLOSE_CHECK_INTERVAL(초, 기본 60) 주기로 holding 상태이며 워치독이 만료된 슬롯 중 forced_close_at <= now(KST) 인 슬롯을 감지해 내부 매도 intent 를 제출한다.
  • FR-2: stop_loss_price 또는 take_profit_price 가 설정되고 워치독이 만료된 슬롯에 한해, 데몬은 TREX_STOP_LOSS_POLL_INTERVAL(초, 기본 30) 주기로 KIS TTTC8397R(stck_prpr) 를 polling 하고 현재가 <= stop_loss_price(손절) 또는 현재가 >= take_profit_price(익절) 조건 충족 시 내부 매도 intent 를 제출한다. 두 조건은 동일 polling 루프에서 평가한다.
  • FR-3: 자동 매도 intent 는 SlotService.sell() 내부 함수 직접 호출로 제출한다(RabbitMQ intent RPC 우회). 기존 outbox 내구성·슬롯 상태 전이·ledger 기록 경로를 따른다.
  • FR-4: 이미 pending_sell 이거나 자동 매도가 진행 중인 슬롯에 대해 중복 trigger 가 발생하지 않도록 idempotency 를 보장한다.
  • FR-5: snapshot RPC handler 는 응답에 포함된 슬롯들의 last_seen_at 타임스탬프를 in-memory 워치독 store 에 갱신한다. now() - last_seen_at > TREX_WATCHDOG_TIMEOUT 인 슬롯을 "워치독 만료"로 판정한다.
  • FR-6: TREX_MARKET_CLOSE_LIQUIDATION_TIME(HH:MM KST, optional) 이 설정된 경우 해당 시각에 워치독 만료 여부와 무관하게 모든 holding 슬롯에 내부 매도 intent 를 제출한다. 미설정 시 장 마감 청산 동작 없음.
  • FR-7: 전략 모듈은 trex.intent.req 채널에 action: "update_thresholds" 메시지를 발행해 holding 상태 슬롯의 stop_loss_price·take_profit_price·forced_close_at 를 갱신할 수 있다(각 필드는 독립적으로 null 가능). 데몬은 DB 를 업데이트하고 해당 슬롯의 워치독 last_seen_at 을 현재 시각으로 리셋한다. holding 이 아닌 슬롯에 대한 요청은 무시(또는 에러 응답)한다(OQ-6 Deferred → REQ AC).

비기능 요구사항

  • NFR-1: forced_close_at 만기 감지 지연은 TREX_FORCED_CLOSE_CHECK_INTERVAL 이내(기본 60초, env 로 조정 가능). DB/메모리 스캔만 수행하므로 KIS 쿼터 무관.
  • NFR-2: 자동 매도 실패(broker 오류) 시 기존 outbox retry 경로를 따르며, 슬롯 상태는 execution worker 결과에 따라 전이된다.
  • NFR-3: 가격 조건 polling(TTTC8397R) 주기는 TREX_STOP_LOSS_POLL_INTERVAL(초, 기본 30) env 로 설정한다(stop-loss·take-profit 공통). 호출 수 상한 = 워치독 만료 슬롯 중 stop_loss_price 또는 take_profit_price 설정 슬롯 수 × (3600 / TREX_STOP_LOSS_POLL_INTERVAL) 로 산정해 KIS 쿼터를 초과하지 않도록 REQ 단계에서 상한을 명시한다.
  • NFR-4: 전략 모듈의 snapshot RPC 호출 주기는 TREX_WATCHDOG_TIMEOUT(기본 120초) 보다 짧아야 한다. 이 조건이 위반되면 정상 동작 중인 슬롯에서도 워치독 오탐이 발생한다. REQ 에서 권장 관계식(snapshot_interval < TREX_WATCHDOG_TIMEOUT / 2, 즉 기본값 기준 60초 이내)을 명시한다.

범위 경계

  • In Scope
    • per-slot 워치독(snapshot RPC 기반 last_seen_at 갱신 + 만료 판정)
    • forced_close_at 만기 자동 매도(워치독 만료 슬롯만)
    • stop_loss_price·take_profit_price 필드 추가 및 가격 기반 자동 매도(워치독 만료 슬롯만, 동일 polling 루프)
    • 자동 매도 내부 경로(SlotService.sell() 직접 호출)
    • 장 마감 강제 청산(TREX_MARKET_CLOSE_LIQUIDATION_TIME, 워치독 무관)
    • DB 마이그레이션: stop_loss_price, take_profit_price 컬럼 추가
    • intent RPC payload 에 stop_loss_price, take_profit_price 설정 및 update_thresholds 갱신 API 계약 업데이트(forced_close_at 포함)
  • Out of Scope
    • 운영자 수동 즉시 청산(kill-switch): 별도 Discovery(trex-core#3 기반)
    • 부분 매도(일부 수량만 청산): REL-001 Feedback Handoff Discovery Input 에 등록된 별도 후보
    • trailing stop 등 고급 손절 전략: 단순 임계값 이하 감지만 구현
    • 전략 모듈 주도 stop-loss·take-profit 관리: "trex-core 자율" 구현만 대상

6. 리스크/가정 목록

리스크

  • R-1 (Low): stop-loss 가격 소스는 KIS TTTC8397R polling 으로 확정(OQ-1 Answered). 워치독 만료 슬롯에만 polling 하므로 전략 모듈 정상 동작 중에는 KIS 쿼터 소비 없음.
    • 완화 방안: 워치독 만료 슬롯 수를 기준으로 polling 호출 수를 산정(NFR-3). KIS 쿼터 초과 시 polling 간격을 자동 조정하는 AC 를 REQ 에 추가한다.
  • R-2 (Medium): 장 마감 강제 청산(TREX_MARKET_CLOSE_LIQUIDATION_TIME) 시 모든 holding 슬롯 매도 intent 가 동시에 폭증할 수 있다. execution worker 단일 순차 루프 지연 위험.
    • 완화 방안: FR-4(idempotency) 로 중복 방지. execution worker FIFO 큐 처리. 장 마감 전 전략 모듈이 이미 일부 매도했다면 holding 슬롯 수가 줄어 폭증 완화. REQ 에서 수용 기준 명시.
  • R-3 (Low): trex-core#3 kill-switch 가 별도 Discovery 로 분리됨. FR-3 내부 매도 경로를 kill-switch Discovery 에서 재사용할 수 있도록 별도 함수로 격리한다.
    • 완화 방안: FR-3 internal trigger 경로를 별도 함수로 격리해 후속 kill-switch Discovery 에서 재사용 가능하도록 설계한다.
  • R-4 (Low): 데몬 재시작 시 워치독 store(in-memory) 가 초기화된다. TREX_WATCHDOG_GRACE_PERIOD 동안 만료 판정을 유예해 오탐을 방지한다(OQ-4 Answered). grace period 중에는 forced_close_at / stop-loss 보호 공백이 발생하나, grace period 를 짧게 유지하면 허용 가능한 수준이다.
    • 완화 방안: TREX_WATCHDOG_GRACE_PERIOD 기본값 60초로 확정됨(A-4). grace period 중에도 FR-6(장 마감 청산)은 동작함을 명시한다.

가정

  • A-1: 자동 매도 intent 는 SlotService.sell() 내부 함수 직접 호출로 제출한다. RabbitMQ intent RPC 를 경유하지 않는다. (확정 — RabbitMQ 장애와 무관하게 동작해야 하는 "transport 독립" 요건에 부합.)
  • A-2: forced_close_at 이 설정된 슬롯은 전략 모듈이 의도적으로 만기 청산을 요청한 것이므로, 만기 시점 데몬 자율 매도는 정책적으로 허용된다.
  • A-3: stop-loss·take-profit 가격 소스는 KIS API TTTC8397R(실전) / VTTC8397R(모의) polling 으로 결정됐다(OQ-1 Answered). 평가 기준가는 stck_prpr(현재가, 직전 체결가). 트리거 조건: 현재가 <= stop_loss_price(손절) 또는 현재가 >= take_profit_price(익절). 동일 polling 루프에서 평가.
  • A-4: 워치독 store 는 in-memory dict[slot_id, datetime] 이며 Postgres 에 영속하지 않는다. 데몬 재시작 시 초기화. 재시작 후 TREX_WATCHDOG_GRACE_PERIOD(기본 60초, env) 동안 워치독 만료 판정을 유예해 전략 모듈 재연결 시간을 확보한다. 만료 임계값은 TREX_WATCHDOG_TIMEOUT(기본 120초, env)(OQ-4 Answered — Grace period).

오픈 질문

  • OQ-1: stop-loss 를 위한 실시간 가격 소스를 어떻게 조달하는가?

    • 상태: Answered
    • 결정: KIS API polling(TTTC8397R 실전 / VTTC8397R 모의), 평가 기준가 stck_prpr(현재가). 트리거 조건: 현재가 <= stop_loss_price(손절) 또는 현재가 >= take_profit_price(익절).
    • 본문 반영: FR-2, NFR-3, A-3, 코드 영향(KIS 현재가 polling 어댑터).
  • OQ-2: trex-core#3(강제 청산/kill-switch) 이 이번 Discovery 에 포함되는가, 별도 Discovery 인가?

    • 상태: Answered
    • 결정: 별도 Discovery 로 분리. 이번 dcy-007 은 자동 조건 트리거(시간 만기·가격 임계값·장 마감)만 다룬다.
    • 본문 반영: Out of Scope(kill-switch 분리 확정), R-3(Low 하향), 제약사항, 사용자 시나리오.
  • OQ-3: forced_close_at checker 폴링 간격을 env 로 설정 가능하게 하는가, 고정값으로 하는가?

    • 상태: Answered
    • 결정: forced_close_at 체크와 stop-loss 가격 polling 을 별도 간격으로 분리하며 둘 다 env 로 설정 가능. TREX_FORCED_CLOSE_CHECK_INTERVAL(기본 60초) / TREX_STOP_LOSS_POLL_INTERVAL(기본 30초).
    • 본문 반영: FR-1, FR-2, NFR-1, NFR-3, 코드 영향(config.py + daemon.py).
  • OQ-4: 데몬 재시작 시 워치독이 초기화된 직후 safety-sell 체크를 어떻게 처리하는가?

    • 상태: Answered
    • 결정: 후보 A — Grace period. 재시작 후 TREX_WATCHDOG_GRACE_PERIOD(기본 60초, env) 동안 워치독 만료 판정을 유예. TREX_WATCHDOG_TIMEOUT 기본값 120초.
    • 본문 반영: A-4(가정 확정 + 기본값), NFR-4(기본값 명시), R-4(Low 하향), 코드 영향(config.py 기본값), DECIDE(OQ-4 완료).
  • OQ-5: update_thresholds 가 forced_close_at 도 갱신 대상에 포함하는가?

    • 상태: Answered
    • 결정: 포함. update_thresholds 는 stop_loss_price·take_profit_price·forced_close_at 세 필드를 모두 갱신 대상으로 한다(각 필드는 독립적으로 null 가능).
    • 본문 반영: FR-7(갱신 대상 확장), 코드 영향(service.py update_thresholds() 시그니처, messaging_handlers.py 파싱), S-6(성공 기준 갱신).
  • OQ-6: update_thresholds 요청이 holding 이 아닌 슬롯에 오면 무시(silent)인가, 에러 응답인가?

    • 상태: Deferred — REQ AC 단계에서 결정. 에러 응답 시 RPC reply 구조 필요, 무시 시 간단. Freeze 를 막지 않는다.
    • 처리 방안: REQ 초안 작성 시 AC 에 명시.
    • 종료 조건: REQ 초안의 FR-7 AC 에 반영되면 Answered.

오픈 질문 처리 기준

  • Open: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
  • Answered: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
  • Deferred: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
  • Dropped: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
  • 처리 원칙 1: 질문만 남기지 말고 반드시 상태, 처리 방안, 종료 조건을 함께 적는다.
  • 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
  • 처리 원칙 3: High 영향 질문은 Deferred 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.

7. 초기 성공 기준

  • S-1: 워치독이 만료된 슬롯 중 forced_close_at <= now() 인 슬롯에 자동 매도 intent 가 제출되고 슬롯이 idle 로 전이된다.
  • S-2: 워치독이 만료된 슬롯에서 현재가 <= stop_loss_price(손절) 또는 현재가 >= take_profit_price(익절) 조건 충족 시 자동 매도 intent 가 제출되고 슬롯이 idle 로 전이된다.
  • S-3: 이미 pending_sell 인 슬롯에 대해 자동 매도 checker 가 추가 intent 를 제출하지 않는다(idempotency).
  • S-4: 워치독이 활성(last_seen_at 갱신 중)인 슬롯은 forced_close_at / stop-loss / take-profit 체크를 건너뛴다.
  • S-5: TREX_MARKET_CLOSE_LIQUIDATION_TIME 설정 시 해당 시각에 워치독 상태와 무관하게 모든 holding 슬롯에 매도 intent 가 제출된다. 미설정 시 동작 없음.
  • S-6: update_thresholds 메시지 수신 시 holding 슬롯의 stop_loss_price·take_profit_price·forced_close_at 가 DB 에 갱신되고, 해당 슬롯의 워치독 last_seen_at 이 리셋된다. holding 이 아닌 슬롯 요청은 무시된다.

측정 방식 및 데이터 출처

  • S-1 측정 방식: 단위 테스트 — 워치독 만료 + forced_close_at = now() - 1s 슬롯에 checker 1회 수행 후 SlotState 전이 확인 / 데이터 출처: pytest 결과
  • S-2 측정 방식: 단위 테스트(mock KIS polling) — 워치독 만료 + 손절 조건(임계값 이하) / 익절 조건(임계값 이상) 각각 주입 후 매도 trigger 확인 / 데이터 출처: pytest 결과
  • S-3 측정 방식: 단위 테스트 — pending_sell 슬롯에 checker 수행 시 추가 intent 미제출 확인 / 데이터 출처: pytest 결과
  • S-4 측정 방식: 단위 테스트 — 워치독 활성 슬롯에 checker 수행 시 intent 미제출 확인 / 데이터 출처: pytest 결과
  • S-5 측정 방식: 단위 테스트 — 장 마감 시각 mock 주입 후 워치독 활성/만료 슬롯 모두 매도 intent 제출 확인 / 데이터 출처: pytest 결과
  • S-6 측정 방식: 단위 테스트 — update_thresholds 수신 후 DB 갱신 + last_seen_at 리셋 확인; pending_buy 슬롯 요청 시 무시 확인 / 데이터 출처: pytest 결과

8. REQ로 넘기기 전 확인 체크

  • 범위(In Scope/Out of Scope) 확정
    • 기준: OQ-2(kill-switch 포함 여부) 결정 후 범위 경계 확정. → 완료: kill-switch 별도 Discovery 분리 확정.
  • 성공 지표의 측정 방식/데이터 출처 확정
    • 기준: OQ-1(가격 소스) 결정 후 S-2 측정 방식 확정. → 완료: KIS TTTC8397R mock 단위 테스트.
  • High 영향 리스크 우선순위 및 해소 책임자 지정
    • 기준: R-1(High) — OQ-1 Answered 로 해소. 잔여 R-2(Medium)·R-4(Low) → REQ 단계 AC 로 완화.
  • 모든 오픈 질문(OQ) Open 상태 없음 확인
    • 기준: OQ 목록에 Open 상태 항목이 0개이거나, 잔여 항목 전부 Deferred 처리 완료. → 완료: OQ-5 Answered, OQ-6 Deferred. 잔여 Open 항목 없음.
  • Freeze 확정 담당자(CONFIRMED_BY) 지정
    • 기준: Justin Ko 가 Freeze 승인.

9. 파일 처리 결과

  • 처리 결과: 생성
  • 생성 경로: docs/discovery/dcy-007_20260606_core-autonomous-safety-sell.md
  • 참조 문서:
    • docs/discovery/dcy-006_20260605_strategy-module-rabbitmq-transport.md (A-5 가정 참조)
    • docs/batches/bat-007_20260605_strategy-module-rabbitmq-transport/index.md (R-1 High 참조)
    • docs/releases/rel-001_20260606_trex-core-slot-core-baseline.md (Excluded 확인)
    • trex-core#3 (강제 청산/kill-switch GitHub issue — OQ-2 범위 근거)

10. 사용자 결정 필요 항목 요약

DECIDE

  • OQ-1: Answered — KIS API polling(TTTC8397R), 현재가(stck_prpr), 트리거 현재가 <= stop_loss_price.
  • OQ-2: Answered — kill-switch 는 별도 Discovery(trex-core#3 기반)로 분리.
  • OQ-4: Answered — Grace period. TREX_WATCHDOG_GRACE_PERIOD(초, env) 동안 만료 판정 유예.
  • OQ-5: Answered — update_thresholds 는 stop_loss_price·take_profit_price·forced_close_at 세 필드를 모두 갱신 대상으로 한다.

CONFIRM

  • OQ-3: Answered — TREX_FORCED_CLOSE_CHECK_INTERVAL(60초) / TREX_STOP_LOSS_POLL_INTERVAL(30초) 별도 env.
  • A-1: 확정 — SlotService.sell() 내부 함수 직접 호출, RabbitMQ intent RPC 우회. transport 독립 요건 부합.

DATA

  • trex-core#3 GitHub issue 상세 내용: kill-switch 범위와 구현 방향 확인(OQ-2 resolved, 후속 Discovery 작성 시 참조).

11. Discovery Freeze

섹션 참조

Freeze 플래그

  • Handoff Decision: 승인
  • Handoff Rationale: OQ-5 Answered, OQ-6 Deferred. 모든 Open 항목 해소. FR-7 범위 확정(forced_close_at 포함).
  • Ready for REQ Drafting: true
  • Confirmed By: Justin Ko
  • Confirmed At (KST): 2026-06-07