Implement REQ-011~015 (dcy-003): the order-execution layer on top of the BAT-002 slot-core daemon, connecting intents to real orders. - application: BrokerPort (submit/poll_fill/cancel) + ExecutionService (claim -> submit -> poll -> transition -> rollback -> reconcile) - adapters/broker: PaperBroker (deterministic async double) and KISBroker (KIS Open API: token, order-cash, inquire-daily-ccld, cancel; VTS sandbox tr_ids; live account gated behind allow_live) - persistence: option-A execution queue (find_pending_for_dispatch FOR UPDATE SKIP LOCKED, mark_dispatched idempotency guard, clear_dispatch) - runtime: background execution loop in the daemon lifespan + startup reconcile; config selects paper|kis Verification (real KIS VTS 모의투자 + PostgreSQL): 92 passed, ruff clean, domain import sweep shows zero broker/web/db deps, buy->holding / sell->closed / reject->idle, idempotent dispatch (exactly one order), reconcile after crash creates no duplicate order, KIS sandbox token+submit round-trip, and a daemon E2E through the background loop. Align the four baseline docs. BAT-004 -> release-candidate; REQ-011~015 -> Implemented. KIS live-account go-live, partial-fill accrual, option-B outbox, connection pooling, and the safety floor remain deferred to follow-up Discoveries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.3 KiB
3.3 KiB
BAT-004 Design
- Status: confirmed
- Batch: BAT-004
Architecture Summary
- BAT-002 onion(domain/application/adapters/runtime) 위에 집행을 추가한다. application 에
BrokerPort(submit/cancel/poll_fill)와 execution 유스케이스, adapters/broker 에 paper/stub + KIS 어댑터, runtime 에 백그라운드 execution 루프(데몬 lifecycle 에서 기동/종료). 코어가 단일 브로커 세션(불변식 B) + 단일 슬롯 writer(불변식 A)를 모두 소유. intent API 는 pending 전이까지만, 집행은 루프가 비동기.
Changed Areas
- Docs: runtime-flows(집행/체결/reconcile 흐름), project-structure(broker/execution 레이어), interface-contract(BrokerPort 경계), data-model(broker_order_id=실행큐/멱등) — REQ-015
- Modules:
- application:
BrokerPort+FillStatus, execution 유스케이스(claim/dispatch/poll/transition/reconcile) - adapters/broker: paper(결정적 async 더블), kis(KIS Open API)
- runtime: execution 루프(lifespan 백그라운드), config(KIS 자격증명/base URL/게이팅/폴링 주기)
- application:
- Interfaces:
BrokerPort(내부 포트) — 외부 HTTP API(/v1)는 불변(집행은 백그라운드)
Key Decisions
- 옵션 A 실행큐(OQ-3): pending 슬롯 +
broker_order_id IS NULL을FOR UPDATE SKIP LOCKED로 claim, 송신 후 broker_order_id 기록(멱등 가드). 별도 outbox 테이블 없음. - 통일 비동기 체결(OQ-2):
submit_order→accepted,poll_fill→filled/partial/rejected. paper 도 동기 단축 없이 같은 계약(결정적 더블), KIS 는 체결조회 폴링. - 별도 execution 루프(OQ-4): 데몬 프로세스 내 백그라운드(claim→submit→poll→transition→reconcile). 단일 인스턴스=단일 writer/세션.
- 단일 writer(불변식 A): 집행 루프도 슬롯 전이는 SlotService(advisory lock)로만. 불변식 B: 주문/취소는 BrokerPort 단일 세션만.
- KIS 안전(R-3): 기본 모의투자, 실계좌는 config 게이팅. 자격증명 .env(REQ-010 패턴). 도메인 순수성(NFR-2): broker/HTTP 의존은 adapters/runtime 에만.
Edge Cases
- 송신 후 broker_order_id 기록 전 크래시 → reconciliation 으로 브로커 상태 대조(중복 주문 방지).
- 부분체결 → 슬라이스는 완전체결 기준 전이(부분체결 누적/분할주문 본격화는 옵션 B 승격 후속).
- KIS 토큰 만료/레이트리밋 → 어댑터 내 갱신/백오프, 실패 시 슬롯 전이 안 함(다음 틱 재시도).
- 거부/취소 → pending_buy→idle 롤백(점유 초기화), pending_sell→holding 복귀(매도 거부).
- KIS 자격증명 미가용 → 통합테스트 skip, paper 경로는 완전 동작.
Architecture Impact
- both (project-structure + runtime-flows). interface-contract(BrokerPort)·data-model(broker_order_id 의미) 도 갱신.
Reference Doc Update Plan
- runtime-flows.md: 집행 루프(claim→submit→poll→transition)·reconcile·KIS 흐름 추가(REQ-015).
- project-structure.md: application(BrokerPort/execution)·adapters/broker(paper,kis)·runtime(execution 루프) 레이어 반영.
- interface-contract.md:
BrokerPort내부 포트 계약(submit/cancel/poll_fill) + KIS 어댑터 노트. - data-model.md:
broker_order_id를 실행큐 claim 술어 + 멱등 가드로 쓰는 의미 명시.