매도(sell) 주문이 현재가가 아닌 매수가(entry_price) 지정가로 나가던 버그(issue #10) 정정. 슬롯의 매수 전용 entry_price 를 side 무관 "현재 pending 주문 지정가" limit_price 로 내부 rename 하고, pending 이탈(→holding/→closed) 시 clear 해 매수 지정가가 매도로 누수되는 경로를 구조적으로 제거. 거버넌스: 전부 change-req (신규 REQ 없음, dcy-012 OQ-3). - REQ-002: Slot entry_price→limit_price rename + clear-on-exit 생애주기 - REQ-006: daily_slot 컬럼 rename 마이그레이션 006 (idempotent) + persistence 매핑 - REQ-013: _dispatch 가 slot.limit_price 단일 필드로 가격/주문유형 결정 - REQ-007: SellIntent/sell payload optional limit_price + _admit_sell 운반 - REQ-036: SlotService.sell optional 가격 → 자율 매도(safety-sell/kill-switch) 시장가 - REQ-003: 컬럼 계약/드리프트 가드 limit_price 반영 - REQ-015: runtime-flows/interface-contract/data-model 정합 매도 정책(OQ-1 하이브리드): 가격 지정→지정가("00"), 미지정→시장가("01"). 자율 매도는 가격 미전달 → 항상 시장가 (손절 미체결 pending_sell 갇힘 방지, issue #9 연계). 검증: 단위 286 passed/32 skipped, 인프라 게이트(PG+RabbitMQ) 318 passed, ruff clean. 신규 test_sell_pricing(recording broker)로 지정가 매수 슬롯의 매도가 매수가 재사용 안 함을 직접 검증. BAT-016 release-candidate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.2 KiB
6.2 KiB
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()을 통해 제출된 매도는 기존executionworker의transit_slot(holding→pending_sell)+ broker dispatch + ledger 기록 경로를 재사용해야 한다.
Acceptance Criteria
- Given
holding상태의 슬롯 S가 있을 때, WhenSlotService.sell(S, actor="safety-sell", reason="stop_loss")를 호출하면, Then S의 상태가pending_sell로 전이된다. - Given
pending_sell상태의 슬롯 S가 있을 때, Whensell(S, ...)를 다시 호출하면, Then 슬롯 상태가 변경되지 않고 중복 매도 intent가 제출되지 않는다. - Given
idle상태의 슬롯 S가 있을 때, Whensell(S, ...)를 호출하면, Then 오류(예외 또는 rejected 응답)가 반환되고 슬롯 상태는 변경되지 않는다. - Given
sell()호출 성공 시, When execution worker가 이후 처리하면, Then 매도 주문이 broker에 제출되고 outbox 및 ledger 경로가 기존 intent RPC 경로와 동일하게 작동한다. - 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에 optionallimit_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 전이 단위.