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>
24 KiB
24 KiB
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슬롯을 청산하는 세 가지 기능의 범위·제약·오픈 질문을 정의한다.
- REL-001 은
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 기록을 모두 따른다.
- snapshot RPC 응답 시 해당 슬롯들의 워치독 타임스탬프가 갱신되며,
- 이해관계자
- 문서 작성자(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]추가. 워치독 만료 슬롯에 한해 KISTTTC8397Rpolling(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_thresholdsaction handler —stop_loss_price·take_profit_price·forced_close_atDB 갱신 +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_thresholdsaction 계약, 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이어야 오탐 없음
- DB 마이그레이션:
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) 주기로 KISTTTC8397R(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포함)
- per-slot 워치독(snapshot RPC 기반
- 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
TTTC8397Rpolling 으로 확정(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 에서 수용 기준 명시.
- 완화 방안: FR-4(idempotency) 로 중복 방지. execution worker FIFO 큐 처리. 장 마감 전 전략 모듈이 이미 일부 매도했다면
- 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.pyupdate_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
TTTC8397Rmock 단위 테스트.
- 기준: OQ-1(가격 소스) 결정 후 S-2 측정 방식 확정. → 완료: KIS
- 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 항목 없음.
- 기준: OQ 목록에
- 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
섹션 참조
- 범위: # 5. 요구사항 목록 > 범위 경계
- 리스크/오픈 질문: # 6. 리스크/가정 목록
- 성공 기준: # 7. 초기 성공 기준
- 인계 전 확인: # 8. REQ로 넘기기 전 확인 체크
- 사용자 결정 필요: # 10. 사용자 결정 필요 항목 요약
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