docs: add dcy-007 safety sell discovery
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
# 0. 문서 상태
|
||||
|
||||
- 상태: draft
|
||||
- 문서 ID: dcy-007_20260606_core-autonomous-safety-sell
|
||||
- 이슈명: core 자율 안전 매도 — stop-loss·take-profit·시간 만기 자동 트리거
|
||||
- 작성 시각(KST): 2026-06-06
|
||||
- 마지막 갱신 시각(KST): 2026-06-06 (review-discovery 반영: M-1~2, m-1~7; OQ-5 Answered; OQ-6 Deferred)
|
||||
- 대체됨: 없음
|
||||
- 후속 Discovery 참조: 없음
|
||||
- 생성된 REQ 참조: 없음
|
||||
|
||||
# 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로 넘기기 전 확인 체크
|
||||
|
||||
- [x] 범위(In Scope/Out of Scope) 확정
|
||||
- 기준: OQ-2(kill-switch 포함 여부) 결정 후 범위 경계 확정. → **완료**: kill-switch 별도 Discovery 분리 확정.
|
||||
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||||
- 기준: OQ-1(가격 소스) 결정 후 S-2 측정 방식 확정. → **완료**: KIS `TTTC8397R` mock 단위 테스트.
|
||||
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||||
- 기준: R-1(High) — OQ-1 Answered 로 해소. 잔여 R-2(Medium)·R-4(Low) → REQ 단계 AC 로 완화.
|
||||
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: OQ 목록에 `Open` 상태 항목이 0개이거나, 잔여 항목 전부 `Deferred` 처리 완료. → **완료**: OQ-5 Answered, OQ-6 Deferred. 잔여 Open 항목 없음.
|
||||
- [x] 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. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
||||
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
||||
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
||||
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
||||
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||||
|
||||
## Freeze 플래그
|
||||
|
||||
- Handoff Decision: REQ Drafting 진행 가능
|
||||
- Handoff Rationale: OQ-5 Answered, OQ-6 Deferred. 모든 Open 항목 해소. FR-7 범위 확정(forced_close_at 포함).
|
||||
- Ready for REQ Drafting: true
|
||||
- Confirmed By: Justin
|
||||
- Confirmed At (KST): TBD
|
||||
@@ -11,4 +11,5 @@
|
||||
| dcy-003_20260604_broker-execution-and-outbox | 20260604 | 브로커 단일 세션 + 주문 집행 + durable outbox (불변식 B) | [Discovery](./dcy-003_20260604_broker-execution-and-outbox.md) | confirmed |
|
||||
| dcy-004_20260604_portfolio-hub-ledger-integration | 20260604 | portfolio-hub ledger 연동 — 체결을 외부 원장에 기록 | [Discovery](./dcy-004_20260604_portfolio-hub-ledger-integration.md) | confirmed |
|
||||
| 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-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) | draft |
|
||||
Reference in New Issue
Block a user