feat(rel-006): dcy-022/023 + BAT-032~034 — 실행 정책·qty 검증·allocated_capital 제거 통합
BAT-032 (dcy-022): timeout_sec/slippage_pct 실행 정책, 미체결 자동 취소, 보호 지정가, `trex-core cancel` 백스톱. 마이그레이션 012. BAT-033: `trex-core enter --qty` 필수 검증 — 미지정 시 RPC 전 즉시 오류. BAT-034 (dcy-023): allocated_capital 죽은 필드 전 경로 제거 (domain·intent· persistence·snapshot·CLI). 마이그레이션 013(비가역 DROP). 외부 발신자 unknown key 무시 수신 의미론 적용. REL-006 confirmed: BAT-026~034 통합 릴리즈, pre-release smoke 증거 포함. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# BAT-032 Design
|
||||
|
||||
- Status: approved
|
||||
- Batch: BAT-032
|
||||
|
||||
## 설계 결정
|
||||
|
||||
### D1: 실행 정책 필드를 슬롯 컬럼으로 저장 (timeout_sec, slippage_pct)
|
||||
|
||||
- 결정: `Slot` 모델에 `timeout_sec: Optional[int]`, `slippage_pct: Optional[float]` 컬럼 추가. PENDING_* 진입 시 set, 탈출(→HOLDING/→INACTIVE) 시 clear.
|
||||
- 근거: 집행 루프가 dispatch/poll 시 슬롯만 읽으면 되어 별도 캐시/in-memory 구조 불필요. limit_price 와 동일한 패턴(set on pending entry, cleared on exit).
|
||||
- 대안: in-memory dict 에 별도 저장 → 재시작 시 유실, 재기동 reconciliation 에서 timeout 추적 불가. 기각.
|
||||
|
||||
### D2: updated_at 을 dispatch 시각 proxy 로 사용 (기존 stale watchdog 방법 계승)
|
||||
|
||||
- 결정: timeout 경과 시간 = `(now - slot.updated_at).total_seconds()`. mark_dispatched 가 updated_at 을 갱신하므로 dispatch 시각과 일치.
|
||||
- 근거: `_check_stale_pending`(REQ-014) 이 이미 동일 방법 사용 → 일관성, 추가 필드 불필요.
|
||||
- 한계: PENDING_* 상태에서 `update_thresholds` 가 호출되면 updated_at 이 리셋될 수 있으나, `update_thresholds`는 HOLDING 슬롯에만 작동(REQ-040) → 실질적 충돌 없음.
|
||||
|
||||
### D3: cancel_order best-effort, 즉시 롤백 없음 (race 안전)
|
||||
|
||||
- 결정: `_try_timeout_cancel` 에서 cancel_order 송신만 하고 슬롯 상태를 변경하지 않는다. 다음 poll 틱에서 is_terminal_reject/is_partial_cancel 로 수렴.
|
||||
- 근거: 취소-체결 race(cancel 수락 직전 체결) 시 다음 poll 이 is_filled 경로로 정상 수렴 → 임의 롤백 없음. dcy-013 부분 취소 선례 재사용.
|
||||
|
||||
### D4: cancel CLI → control RPC → ExecutionService.cancel_pending_slot
|
||||
|
||||
- 결정: CLI는 `trex.control.req` 채널로 `{"action": "cancel_pending", "slot_id": ...}` 를 전송. 제어 핸들러가 `ExecutionService.cancel_pending_slot(slot_id)` 를 호출해 cancel_order 를 송신. 슬롯 전이 없음.
|
||||
- 근거: 기존 kill_switch·pause 와 동일한 경로(단일 writer 불변식 보존). execution_service 를 control 핸들러에 주입하는 방식으로 커플링 최소화.
|
||||
- 대안: intent RPC 채널 재사용 → intent 계약(buy/sell)과 의미 혼용. 기각.
|
||||
|
||||
## 컴포넌트 다이어그램 (텍스트)
|
||||
|
||||
```
|
||||
CLI: trex-core cancel <slot_id>
|
||||
→ control RPC (trex.control.req) cancel_pending
|
||||
→ make_control_rpc_handler (execution_service.cancel_pending_slot)
|
||||
→ broker.cancel_order(broker_order_id)
|
||||
→ [다음 tick _poll] is_terminal_reject / is_partial_cancel
|
||||
→ rollback / partial-preserve (기존 경로)
|
||||
```
|
||||
|
||||
```
|
||||
Intent: {intent_type: "buy", timeout_sec: 60, slippage_pct: 0.1}
|
||||
→ messaging_handlers (파싱 → BuyIntent)
|
||||
→ IntentService._admit_buy (effective_timeout 해결 → transit_slot)
|
||||
→ SlotService (slot.timeout_sec=60, slot.slippage_pct=0.1 set)
|
||||
→ [_dispatch] slippage 실효 지정가 → OrderRequest.limit_price
|
||||
→ [_poll] timeout 초과 → _try_timeout_cancel → cancel_order
|
||||
→ [다음 tick] is_terminal_reject → rollback
|
||||
```
|
||||
Reference in New Issue
Block a user