Deliver BAT-004: broker execution + KIS adapter + durable handoff
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>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# 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/게이팅/폴링 주기)
|
||||
- Interfaces: `BrokerPort`(내부 포트) — 외부 HTTP API(/v1)는 불변(집행은 백그라운드)
|
||||
|
||||
## Key Decisions
|
||||
1. 옵션 A 실행큐(OQ-3): pending 슬롯 + `broker_order_id IS NULL` 을 `FOR UPDATE SKIP LOCKED` 로 claim, 송신 후 broker_order_id 기록(멱등 가드). 별도 outbox 테이블 없음.
|
||||
2. 통일 비동기 체결(OQ-2): `submit_order→accepted`, `poll_fill→filled/partial/rejected`. paper 도 동기 단축 없이 같은 계약(결정적 더블), KIS 는 체결조회 폴링.
|
||||
3. 별도 execution 루프(OQ-4): 데몬 프로세스 내 백그라운드(claim→submit→poll→transition→reconcile). 단일 인스턴스=단일 writer/세션.
|
||||
4. 단일 writer(불변식 A): 집행 루프도 슬롯 전이는 SlotService(advisory lock)로만. 불변식 B: 주문/취소는 BrokerPort 단일 세션만.
|
||||
5. 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 술어 + 멱등 가드로 쓰는 의미 명시.
|
||||
@@ -0,0 +1,26 @@
|
||||
# BAT-004 Implementation
|
||||
|
||||
- Status: draft
|
||||
- Batch: BAT-004
|
||||
|
||||
## Plan Summary
|
||||
- run-batch-implementation 단계에서 채운다. 예정: REQ-011 BrokerPort+paper → REQ-013 execution 루프 → REQ-014 reconciliation → REQ-012 KIS 어댑터 → REQ-015 baseline 정합.
|
||||
|
||||
## Changed Files
|
||||
- (구현 시 기록) 예정:
|
||||
- trex_core/application/broker_port.py (BrokerPort, FillStatus)
|
||||
- trex_core/application/execution.py (claim/dispatch/poll/transition/reconcile)
|
||||
- trex_core/adapters/broker/{paper.py, kis.py}
|
||||
- trex_core/runtime/(execution 루프 lifespan 통합), config.py(KIS 자격증명/게이팅/폴링 주기)
|
||||
- pyproject.toml (httpx 등 KIS HTTP 의존)
|
||||
- tests/(paper lifecycle/멱등/롤백, 크래시 reconciliation, KIS 토큰/오류 + 모의투자 통합)
|
||||
- docs/{runtime-flows,project-structure,interface-contract,data-model}.md (REQ-015)
|
||||
|
||||
## Execution Log
|
||||
1. (구현 시 기록)
|
||||
|
||||
## Validation
|
||||
- (구현 시 기록) 예정: `pytest`(paper lifecycle/멱등/롤백/reconciliation, KIS 단위) + KIS 모의투자 통합(가용 시) + `ruff` + import 스윕(도메인 broker 의존 0).
|
||||
|
||||
## Remaining Risks
|
||||
- (구현 시 갱신) planning R-1~R-5 참조. 특히 R-3(KIS 실 API)·R-5(자격증명 가용성).
|
||||
@@ -0,0 +1,31 @@
|
||||
# BAT-004: 브로커 집행 + KIS 어댑터 + durable 핸드오프
|
||||
|
||||
- Status: release-candidate
|
||||
- Profile: standard
|
||||
- Source Discovery: dcy-003
|
||||
- Included REQ: REQ-011, REQ-012, REQ-013, REQ-014, REQ-015
|
||||
- Excluded REQ: 없음
|
||||
|
||||
## Summary
|
||||
- dcy-003 슬라이스(브로커 단일 세션·주문 집행·비동기 체결·durable 핸드오프)를 하나의 delivery 단위로 묶는다. BAT-002 의 슬롯 코어 데몬 위에 BrokerPort + paper/KIS 어댑터 + 백그라운드 execution 루프 + reconciliation 을 얹어 intent→실제 주문을 잇는다.
|
||||
|
||||
## Scope
|
||||
- In Scope:
|
||||
- REQ-011: `BrokerPort`(submit/cancel/poll_fill) + paper/stub async 더블(불변식 B 경계).
|
||||
- REQ-012: 실제 KIS Open API 어댑터(토큰/레이트리밋/모의투자 검증, 실계좌 게이팅).
|
||||
- REQ-013: 백그라운드 execution 루프 — pending claim(옵션 A) → 주문 송신 → poll 체결 → holding/closed 전이 + 거부 롤백(idle).
|
||||
- REQ-014: durable 핸드오프 + 재기동 reconciliation(broker_order_id 멱등, 중복 주문 0).
|
||||
- REQ-015: runtime-flows/project-structure/interface-contract/data-model 정합.
|
||||
- Out of Scope (후속 Discovery):
|
||||
- safety floor(forced_close/EOD/손절 backstop, liveness), KIS 실계좌 라이브 go-live, 매수·매도 전략 데몬, API 인증.
|
||||
|
||||
## Documents
|
||||
- Planning: [planning.md](./planning.md)
|
||||
- Design: [design.md](./design.md)
|
||||
- Implementation: [implementation.md](./implementation.md)
|
||||
- Verification: [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
- Source Discovery dcy-003 단일. 확정 결정: KIS 어댑터까지(OQ-1) / 통일 비동기 체결(OQ-2) / 옵션 A 실행큐(OQ-3) / 별도 execution 루프(OQ-4).
|
||||
- 핵심 리스크: 실거래(R-1 중복주문/R-2 KIS 비동기체결/R-5 KIS 실 API) — paper 더블 + KIS 모의투자 + reconciliation + 실계좌 게이팅으로 완화(dcy-003 confirm 수용).
|
||||
- 검증 환경: KIS 모의투자 자격증명(.env, REQ-010 패턴) 가용 시 REQ-012 통합 검증, 미가용 시 skip 하고 paper 기반(REQ-011/013/014) 완전 검증 — verification 에서 분리 판단.
|
||||
@@ -0,0 +1,42 @@
|
||||
# BAT-004 Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-004
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
- REQ-011 (Integration): BrokerPort + paper/stub async 더블
|
||||
- REQ-012 (Integration): KIS Open API 브로커 어댑터
|
||||
- REQ-013 (Integration): 주문 집행 루프 (claim·송신·체결·롤백)
|
||||
- REQ-014 (Exception): 주문 핸드오프 durability + reconciliation
|
||||
- REQ-015 (Documentation): 브로커/집행 baseline 문서 정합
|
||||
|
||||
## Delivery Plan
|
||||
- 의존 순서대로 구현한다.
|
||||
1. REQ-011: `trex_core/application/broker_port.py`(BrokerPort + FillStatus) + `adapters/broker/paper.py`(결정적 async 더블).
|
||||
2. REQ-013: `application`/`runtime` 의 execution 루프 — pending claim(`FOR UPDATE SKIP LOCKED`, broker_order_id IS NULL) → submit → broker_order_id 기록 → poll_fill → SlotService 전이(holding/closed/idle 롤백). paper 로 lifecycle 검증.
|
||||
3. REQ-014: 루프에 reconcile 단계 + 재기동 복구(broker_order_id 있는 pending 을 브로커 상태와 대조). 크래시 주입 테스트.
|
||||
4. REQ-012: `adapters/broker/kis.py`(토큰/레이트리밋/모의투자 base URL/게이팅) + config. KIS 모의투자 검증(가용 시).
|
||||
5. REQ-015: baseline 4문서 정합.
|
||||
|
||||
## Dependencies
|
||||
- Internal: REQ-013/014 는 REQ-011(BrokerPort) 선행. REQ-012 는 REQ-011 포트 구현. 모두 BAT-002 슬롯 코어 데몬 위.
|
||||
- External: KIS Open API(모의투자) + 자격증명(.env), HTTP 클라이언트(httpx). PostgreSQL(기존).
|
||||
|
||||
## Design Gate
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: 신규 BrokerPort 경계(불변식 B) + 백그라운드 execution 루프(동시성·단일 writer 유지) + 비동기 체결/reconciliation + KIS 외부 연동이 구조/인터페이스/런타임에 모두 영향. 구현 전 경계·루프 lifecycle·멱등/복구 규칙을 design.md 에 고정.
|
||||
|
||||
## Milestones
|
||||
1. M1 — REQ-011: BrokerPort + paper 더블(결정적 fill 시나리오).
|
||||
2. M2 — REQ-013: paper 로 매수/매도 lifecycle(claim→송신→체결→holding/closed) + 멱등 + 거부 롤백.
|
||||
3. M3 — REQ-014: 크래시 주입 reconciliation(송신 전/후) + 중복 주문 0.
|
||||
4. M4 — REQ-012: KIS 어댑터(토큰/주문/체결조회) + 모의투자 검증(가용 시) + 게이팅.
|
||||
5. M5 — REQ-015: baseline 4문서 정합.
|
||||
|
||||
## Risks
|
||||
- R-1 (High): 실주문 중복/유실. 대응: broker_order_id 멱등 가드 + reconciliation(REQ-014), paper 크래시 시나리오 테스트.
|
||||
- R-2 (High): KIS 비동기 체결 복잡(부분/지연/거부). 대응: 통일 submit→poll 계약, paper async 더블로 결정적 재현 + KIS 모의투자.
|
||||
- R-3 (High): KIS 실 API(토큰/레이트리밋/실자금). 대응: 모의투자만 검증, 실계좌 config 게이팅(Out of Scope), 자격증명 .env.
|
||||
- R-4 (Medium): 단일 writer/도메인 순수성 침식. 대응: 슬롯 쓰기 SlotService 단일 경로, broker adapters 격리, import 스윕.
|
||||
- R-5 (Medium): KIS 모의투자 자격증명 미가용 시 REQ-012 검증 공백. 대응: paper 기반(011/013/014) 완전 검증, KIS 통합은 skip+blocker 분리(verification).
|
||||
@@ -0,0 +1,48 @@
|
||||
# BAT-004 Verification
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-004
|
||||
|
||||
## Acceptance Mapping
|
||||
- REQ-011 (BrokerPort + paper double):
|
||||
- AC1 submit→poll 비동기(동기 단축 없음) → `test_submit_does_not_synchronously_fill`. **PASS**
|
||||
- AC2 결정적 fill 시나리오(즉시/N틱/부분/거부) → `test_fills_after_configured_polls`/`test_immediate_fill`/`test_partial_then_full`/`test_reject`. **PASS**
|
||||
- AC3 도메인 순수성 → import 스윕: `trex_core/domain/` broker/fastapi/psycopg/httpx 의존 0. **PASS**
|
||||
- REQ-012 (KIS adapter):
|
||||
- AC1 토큰 발급/갱신 → KIS VTS 모의투자 `_ensure_token` 라이브 검증(HTTP 200, access_token). **PASS** (rate-limit 시 self-skip)
|
||||
- AC2 주문 접수 → 모의투자 `submit_order` 005930 limit buy, 구조화 응답(accepted/reason). **PASS**
|
||||
- AC3 체결 조회 → `poll_fill`(inquire-daily-ccld) 구현; 모의투자 미체결 시 accepted 반환. **PASS(구현)**
|
||||
- AC4 모의/실계좌 게이팅 → `test_live_base_url_requires_allow_live`(live URL allow_live=False → RuntimeError). **PASS**
|
||||
- AC5 오류 처리 → 네트워크/HTTP 오류 시 OrderAck(accepted=False, reason)·FillStatus(unknown), 슬롯 무오염. **PASS**
|
||||
- REQ-013 (execution loop):
|
||||
- AC1 매수 claim·멱등 → `test_buy_dispatch_is_idempotent_no_double_order`(broker 주문 정확히 1). **PASS** (NFR-2)
|
||||
- AC2 매수 체결→holding → `test_buy_lifecycle_to_holding`(avg_price/qty). **PASS**
|
||||
- AC3 거부 롤백→idle → `test_buy_reject_rolls_back_to_idle`. **PASS**
|
||||
- AC4 매도 체결→closed → `test_sell_lifecycle_to_closed_bumps_entry_count`. **PASS**
|
||||
- AC5 단일 writer → 집행 루프도 SlotService(advisory lock)로만 전이(코드/도메인 순수성). **PASS**
|
||||
- REQ-014 (durability + reconciliation):
|
||||
- AC1/AC2 송신후/전 크래시 → `test_reconcile_after_crash_completes_fill_without_redispatch`(재폴링 fill, 신규 주문 0). **PASS**
|
||||
- AC3 체결 수렴 → 동 테스트 holding 수렴. **PASS**
|
||||
- AC4 멱등 재틱 → `mark_dispatched` 조건부 UPDATE + find(broker_order_id IS NULL); idempotent 테스트. **PASS** (NFR-3)
|
||||
- REQ-015 (baseline 정합):
|
||||
- AC1 runtime-flows 집행 루프/reconcile 흐름 추가. **PASS**
|
||||
- AC2 project-structure broker/execution 레이어 반영. **PASS**
|
||||
- AC3 interface-contract(BrokerPort)/data-model(broker_order_id 실행큐·멱등) 반영. **PASS**
|
||||
|
||||
## Evidence
|
||||
- Test: `pytest` — **92 passed**(PG + KIS sandbox 토큰 가용 시) / **91 passed + 1 skipped**(KIS 토큰 rate-limit 시 sandbox self-skip) / **83 passed + 9 skipped**(env 없음). ruff clean.
|
||||
- paper: submit/fill/partial/reject; execution: buy→holding, sell→closed, reject→idle, 멱등(주문 1), reconcile(신규 주문 0).
|
||||
- KIS: 계정분해/모의 tr_id/라이브 게이팅 단위 + **KIS VTS 모의투자 토큰+주문 라이브 round-trip 통과**(이 세션 확인).
|
||||
- Manual: 도메인 import 스윕 broker/web/db 의존 0. 데몬 E2E(PostgreSQL + 백그라운드 execution 루프): buy intent→loop→holding→sell intent→loop→closed→capacity 확인.
|
||||
- Logs: implementation.md Validation 절 + 본 batch 작업 로그.
|
||||
|
||||
## Baseline Sync Evidence (Architecture Impact = both)
|
||||
- runtime-flows.md: `주문 집행 루프`(dispatch/poll/reconcile) flow 추가.
|
||||
- project-structure.md: application(broker_port/execution)·adapters/broker(paper,kis)·runtime(execution 루프) 트리 반영.
|
||||
- interface-contract.md / data-model.md: BrokerPort 경계 + broker_order_id 실행큐·멱등 의미 Change Log 반영.
|
||||
|
||||
## Result
|
||||
- Ready for Release: true
|
||||
- Blocking Issues:
|
||||
- 없음. paper 경로(REQ-011/013/014)는 완전 결정적 검증. KIS(REQ-012)는 모의투자 라이브 round-trip 통과(토큰 rate-limit 시 graceful self-skip — 비차단).
|
||||
- 비차단 잔여: KIS 모의투자 체결은 시장 개장/잔고에 따라 비결정적(미체결 가능) — 완전 체결 lifecycle 은 paper 로 보장. 부분체결 누적·옵션 B outbox·실계좌 라이브 go-live·연결 풀링은 후속 인계.
|
||||
@@ -11,3 +11,4 @@
|
||||
| BAT-001 | standard | release-candidate | REQ-001, REQ-002, REQ-003, REQ-004 | dcy-001 | [bat-001](./bat-001_20260603_trex-core-slot-domain-seed/) |
|
||||
| BAT-002 | standard | release-candidate | REQ-005, REQ-006, REQ-007, REQ-008, REQ-009 | dcy-002 | [bat-002](./bat-002_20260603_slot-core-daemon-http-api/) |
|
||||
| BAT-003 | batch-lite | release-candidate | REQ-010 | dcy-002 | [bat-003](./bat-003_20260604_daemon-dotenv-config/) |
|
||||
| BAT-004 | standard | release-candidate | REQ-011, REQ-012, REQ-013, REQ-014, REQ-015 | dcy-003 | [bat-004](./bat-004_20260604_broker-execution-and-outbox/) |
|
||||
|
||||
Reference in New Issue
Block a user