# req-036: 자동 매도 내부 경로 — SlotService.sell + idempotency - Status: Implemented - Type: Interface - Priority: High - Owner: project maintainers ## Intent 자율 안전 매도 트리거가 RabbitMQ intent RPC를 거치지 않고 직접 슬롯 매도를 제출할 수 있도록 `SlotService.sell()` 내부 메서드를 추가한다. RabbitMQ 장애와 독립적으로 동작해야 하는 transport-independent 매도 경로다. 이미 `pending_sell` 상태인 슬롯에 대한 중복 트리거를 방지하는 idempotency도 함께 보장한다(dcy-007 FR-3, FR-4, A-1). ## Requirement - `SlotService`는 `sell(slot_id, actor, reason)` 메서드를 제공해야 한다. 이 메서드는 `holding` 슬롯을 `pending_sell`로 전이시키고 기존 outbox 내구성·execution 루프 경로를 따른다. - `sell()`은 내부 함수 직접 호출이며 RabbitMQ intent RPC를 거치지 않는다. - 대상 슬롯이 이미 `pending_sell` 상태이면 `sell()`은 추가 전이 없이 조용히 반환하거나 멱등 응답을 반환해야 한다(중복 트리거 방지). - 대상 슬롯이 `holding` 이 아니고 `pending_sell`도 아닌 상태이면 적절한 예외 또는 오류 응답을 반환한다. - `sell()`을 통해 제출된 매도는 기존 `execution` worker의 `transit_slot(holding→pending_sell)` + broker dispatch + ledger 기록 경로를 재사용해야 한다. ## Acceptance Criteria 1. Given `holding` 상태의 슬롯 S가 있을 때, When `SlotService.sell(S, actor="safety-sell", reason="stop_loss")` 를 호출하면, Then S의 상태가 `pending_sell`로 전이된다. 2. Given `pending_sell` 상태의 슬롯 S가 있을 때, When `sell(S, ...)` 를 다시 호출하면, Then 슬롯 상태가 변경되지 않고 중복 매도 intent가 제출되지 않는다. 3. Given `idle` 상태의 슬롯 S가 있을 때, When `sell(S, ...)` 를 호출하면, Then 오류(예외 또는 rejected 응답)가 반환되고 슬롯 상태는 변경되지 않는다. 4. Given `sell()` 호출 성공 시, When execution worker가 이후 처리하면, Then 매도 주문이 broker에 제출되고 outbox 및 ledger 경로가 기존 intent RPC 경로와 동일하게 작동한다. 5. Given RabbitMQ transport가 불가용 상태일 때, When `sell()` 을 호출하면, Then transport 상태와 무관하게 `holding→pending_sell` 전이가 성공한다. ## Impacted Area - Docs: 없음 (런타임 흐름 변경은 REQ-037~039에서 발생, baseline 문서 갱신은 REQ-042 참조) - Modules: - `trex_core/domain/slot/service.py` — `SlotService.sell(slot_id, actor, reason)` 메서드 추가 - Tests: - `SlotService.sell()` 단위 테스트 — `holding→pending_sell` 전이 확인 - idempotency 테스트 — `pending_sell` 슬롯 중복 호출 시 무전이 확인 - 잘못된 상태(`idle`) 슬롯 호출 시 오류 반환 확인 ## Notes - `sell()`은 `transit_slot(slot_id, from_state=HOLDING, to_state=PENDING_SELL, ...)` 패턴 위에 구현한다. 기존 advisory lock + Postgres commit 보장이 그대로 적용된다. - NFR-2(자동 매도 실패 시 outbox retry): broker 오류 발생 시 execution worker의 기존 retry 경로를 따른다. 별도 retry 로직 불필요. - `actor` 값은 트리거 구분을 위해 `"safety-sell"`, `"forced-close"`, `"market-close"` 등으로 구체화하는 것을 권장한다. 정확한 값은 batch 설계에서 결정한다. - 이 메서드는 dcy-007 FR-3의 "내부 자동 매도 trigger 경로"를 표준화한다. 이후 kill-switch Discovery(trex-core#3)에서도 재사용 가능하도록 격리한다. ## Change Log ### CHG-20260607-01 - Date: 2026-06-07 - Author: Justin Ko #### Change Summary - dcy-007 confirmed → REQ-036 초안 생성 #### Intent - 자동 매도 내부 경로(FR-3, FR-4)를 독립 Interface REQ로 정규화 #### Acceptance Criteria Delta - Added: AC-1~5 전체 #### Impacted Area Delta - Modules: service.py (`sell()` 메서드 추가) - Tests: 단위 테스트 3종 #### Delivery Trace Delta - Batches: none (미할당) - Releases: none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: suggest-batch-reqs 후 batch 배정 ### CHG-20260607-02 - Date: 2026-06-07 - Author: Justin Ko #### Change Summary - Proposed → Approved (confirm-req) #### Intent - 승인 게이트 통과: 필수 필드·AC·Impacted Area·Change Log 완비 확인. dcy-007(confirmed) 범위 및 결정 사항과 모순 없음. #### Acceptance Criteria Delta - (변경 없음) #### Impacted Area Delta - (변경 없음) #### Delivery Trace Delta - Batches: none (미할당) - Releases: none #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: suggest-batch-reqs 후 batch 배정 ### CHG-20260608-01 - Date: 2026-06-08 - Author: Justin Ko #### Change Summary - (change-req, dcy-012) `SlotService.sell` 에 optional `limit_price` 파라미터(default None) 추가. 자율 매도(safety-sell/kill-switch)는 가격 미전달 → 시장가로 즉시 체결(미체결 pending_sell 갇힘 방지), 재량 경로만 지정가 전달 가능. BAT-016 재구현·재검증 완료 → Implemented 유지. #### Intent - 자동 매도 내부 경로가 매수 지정가에 걸리지 않고 시장가로 체결되도록(issue #10/#9 연계), 가격 파라미터를 optional 로 추가하되 기본값 None 으로 자율 경로 체결성을 보장. #### Acceptance Criteria Delta - Updated: - `SlotService.sell(..., limit_price=None)` — 미전달 시 시장가(슬롯 limit_price None), 전달 시 지정가. #### Impacted Area Delta - Modules: `trex_core/domain/slot/service.py`(`sell`) - Tests: tests/application/test_sell_pricing.py::test_safety_sell_dispatches_at_market #### Delivery Trace Delta - Batches: BAT-016 - Releases: none #### Revalidation Impact - Reverification Needed: yes (BAT-016 에서 완료) - Existing Implementation Invalidated: no (시그니처 확장, 기존 호출 default None 동일 동작) - Status Recommendation: Implemented (change-req → BAT-016 재검증) - Follow-up Action: confirm-req-implemented(BAT-016). #### Validation Plan - BAT-016: svc.sell 가격 미전달 → 시장가 + closed 전이 단위.