feat(broker): KIS venue parameterization — OrderRequest.venue + EXCG_ID_DVSN_CD (REQ-117/118, BAT-056)

- OrderRequest.venue: str = "KRX" additive 추가 (기존 위치 인수 순서 불변)
- BrokerPort.cancel_order: venue: str = "KRX" 파라미터 추가
- BrokerPort.get_current_price: market: str = "J" 파라미터 추가
- KISBroker.submit_order: EXCG_ID_DVSN_CD=req.venue 항상 명시 전송
- KISBroker.cancel_order: EXCG_ID_DVSN_CD=venue 항상 명시 전송
- KISBroker.get_current_price: FID_COND_MRKT_DIV_CODE=market 파라미터화
- PaperBroker: cancel_order/get_current_price 시그니처 정합 (venue/market 수신·무시)
- poll_fill TR 유지 (TTTC8001R) — NXT 체결 포함 여부 미확인 open item (코드 주석 기록)
- tests/adapters/broker/test_venue.py: REQ-117 AC-1~5 + REQ-118 AC-1~8 신규 17개
- docs: interface-contract.md OrderRequest/BrokerPort 섹션 + change log 추가
- docs: runtime-flows.md venue 전달 경로 명시 + change log 추가

결과: 794 passed, 47 skipped (기존 777+47 대비 신규 17개 추가 전부 통과)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
2026-07-06 23:24:51 +09:00
co-authored by Claude Fable 5
parent dc6d7a943b
commit ee3dc83a93
9 changed files with 638 additions and 11 deletions
@@ -0,0 +1,57 @@
# BAT-056: KIS Broker Venue Parameterization (NXT/KRX)
- ID: bat-056
- Slug: kis-broker-venue
- Status: In Progress
- Date: 2026-07-06
- Owner: trex-core-dev
- Risk: HIGH (라이브 주문 경로 — worktree+PR+QC(opus) 플로)
## REQs
| REQ | Title | Status |
| --- | --- | --- |
| REQ-117 | OrderRequest venue additive 필드 | Approved |
| REQ-118 | KIS 브로커 어댑터 venue 파라미터화 (EXCG_ID_DVSN_CD) | Approved |
## Scope
- `trex_core/application/broker_port.py` — `OrderRequest.venue` 필드 additive 추가
- `trex_core/adapters/broker/kis.py` — submit_order, cancel_order, poll_fill, get_current_price 변경
- `trex_core/adapters/broker/paper.py` — cancel_order 시그니처 정합
- `tests/adapters/broker/` — 신규 venue 테스트 + 기존 회귀
- `docs/interface-contract.md` — OrderRequest venue 필드 + EXCG 명세 업데이트
- `docs/runtime-flows.md` — venue 전달 경로 명시
## Out of Scope
- AMQP wire 계약 변경 (intent 메시지 venue 전달은 tvr 팬-인 batch 별도 범위)
- orb/tsl 소비 경로 (intent, slots wire) 변경
- SOR 동작 지원
- VTS NXT 검증 (단위 테스트 + 실전 소액 스모크 별도)
## Additive Constraint
venue 미지정(기본 KRX) 경로에서 기존 orb/tsl 동작이 byte-동일 유지:
- submit_order: `EXCG_ID_DVSN_CD="KRX"` 추가만 다름 (기존 미전송 → KRX 명시)
- cancel_order: `EXCG_ID_DVSN_CD="KRX"` 추가만 다름
- poll_fill: TR 유지(TTTC8001R/VTTC8001R) — NXT 체결 포함 여부 미확인(open item)
- get_current_price: market 기본값 "J" 유지
## AC Coverage
| AC | REQ | 대상 테스트 위치 |
| --- | --- | --- |
| REQ-117 AC-1 | venue 기본값 KRX | tests/adapters/broker/test_venue.py |
| REQ-117 AC-2 | NXT 지정 | tests/adapters/broker/test_venue.py |
| REQ-117 AC-3 | 기존 회귀 | 전체 pytest |
| REQ-117 AC-4 | 위치 인수 순서 불변 | tests/adapters/broker/test_venue.py |
| REQ-117 AC-5 | paper broker venue 무영향 | tests/adapters/broker/test_venue.py |
| REQ-118 AC-1 | submit KRX EXCG 포함 | tests/adapters/broker/test_venue.py |
| REQ-118 AC-2 | submit NXT EXCG 포함 | tests/adapters/broker/test_venue.py |
| REQ-118 AC-3 | cancel NXT EXCG 포함 | tests/adapters/broker/test_venue.py |
| REQ-118 AC-4 | poll_fill TR 미확인 open item | design.md + 코드 주석 |
| REQ-118 AC-5 | get_current_price 기본 J | tests/adapters/broker/test_venue.py |
| REQ-118 AC-6 | get_current_price UN | tests/adapters/broker/test_venue.py |
| REQ-118 AC-7 | 기존 회귀 (EXCG 추가만 다름) | tests/adapters/broker/test_venue.py |
| REQ-118 AC-8 | paper cancel_order 시그니처 정합 | tests/adapters/broker/test_venue.py |
@@ -0,0 +1,86 @@
# BAT-056 Design
## 1. OrderRequest.venue 필드 (REQ-117)
### 결정
`OrderRequest` frozen dataclass의 **마지막 위치**에 `venue: str = "KRX"` 추가.
```python
@dataclass(frozen=True)
class OrderRequest:
slot_id: str
side: str
symbol: str
qty: int
limit_price: Optional[int] = None
order_type: str = "00"
venue: str = "KRX" # additive (REQ-117): KRX | NXT | SOR
```
- 기존 위치 인수(`slot_id`, `side`, `symbol`, `qty`, `limit_price`, `order_type`) 순서 불변.
- venue 기본값 "KRX"로 기존 orb/tsl 미지정 호출 자동 KRX 적용.
- "SOR" 정의하되 현재 운영 비권고 (OQ-1 잔여, REQ-117 Notes 참조).
## 2. cancel_order 시그니처 venue 파라미터 (REQ-118)
### 결정: 시그니처 venue 파라미터 추가 방식
대안 검토:
- **A안(채택)**: `cancel_order(broker_order_id, *, side, symbol, qty, venue="KRX")` — 호출자가 원주문 venue를 명시 전달. 명시적이고 기본값으로 하위 호환.
- B안: execution context에서 venue 보존 — 더 복잡, 현재 OrderAck에 venue 저장 필요.
A안 채택 이유: 단순성, 기본값 하위 호환, BrokerPort 프로토콜에서 명시적 의미론 확립.
BrokerPort 프로토콜:
```python
def cancel_order(self, broker_order_id: str, *, side: str, symbol: str, qty: int, venue: str = "KRX") -> bool:
```
KISBroker.cancel_order body에 `"EXCG_ID_DVSN_CD": venue` 추가.
PaperBroker.cancel_order: 시그니처만 정합, venue 무시.
## 3. poll_fill TR 선택 — open item (REQ-118 AC-4)
### 현황
- 구 TR `TTTC8001R` (mock: `VTTC8001R`): 현재 사용 중. NXT 체결 포함 여부 **미확인**.
- 신 TR `TTTC0081R`: EXCG=ALL 또는 venue 지정으로 NXT 체결 조회 가능(KIS 개발자 센터 문서 기준).
### 확인 시도 결과
웹 접근 불가 환경 — KIS 개발자 센터 문서 직접 조회 불가. 코드/기존 문서 내 관련 근거 없음.
### 결정: 보수 구현 (기존 TR 유지)
- **기존 TR(`TTTC8001R`/`VTTC8001R`) 유지** — KRX 경로 동작 절대 불변.
- 코드 주석에 open item 명시.
- verification에 open item 기록: "NXT 체결 조회는 실측 확인 필요(운영 스모크)".
### 향후 전환 조건 및 방법 (미구현, 운영 협의 후)
- 조건: NXT 체결 주문 발생 후 TTTC8001R 응답에 NXT 체결 미포함 확인(실측).
- 방법: `poll_fill` 파라미터 `venue` 추가 + venue="NXT"이면 TTTC0081R 사용.
- mock(VTS) 대응 주의: VTS TTTC0081R 지원 여부 미확인 → mock일 때 구 TR fallback 고려.
- 이 전환은 별도 batch로 처리한다(REQ-118 AC-4 follow-up).
## 4. get_current_price market 파라미터 (REQ-118 AC-5,6)
```python
def get_current_price(self, symbol: str, market: str = "J") -> Optional[Decimal]:
```
- `FID_COND_MRKT_DIV_CODE` → `market` 값 사용.
- 기본값 "J" → 기존 safety-sell 경로 무영향.
- "UN" 지정 시 나스닥/해외 시세 조회 가능(NXT 연장세션 활용 예정).
BrokerPort 프로토콜:
```python
def get_current_price(self, symbol: str, market: str = "J") -> Optional[Decimal]:
```
## 5. 실측 근거 (design 인용)
- 2026-07-06: 실전 계좌 NXT 주문(TTTC0012U + EXCG=NXT) rt_cd=0, 취소(TTTC0013U + EXCG=NXT) rt_cd=0·잔여 0건 확인.
- 토큰 재요청 1분 rate-limit 확인. 기존 kis.py 토큰 캐시 방식 유지 — 신 TR 전환 시에도 캐시 재사용 경로 유지 필요.
## 6. additive 검증 전략
- venue 미지정 submit_order: 기존 대비 `EXCG_ID_DVSN_CD="KRX"` 필드 추가만 다름 → fixture로 body 캡처 검사(AC-7).
- 기존 `cancel_order` 호출자: keyword-only 파라미터이므로 venue 없이 호출 시 기본값 KRX 자동 적용 → 기존 테스트 회귀 없음.
- 기존 `get_current_price` 호출자: 파라미터 미지정 → market="J" 자동 적용 → 기존 safety-sell 경로 무영향.
@@ -0,0 +1,62 @@
# BAT-056 Planning
## Task Breakdown
### T-1: OrderRequest.venue 필드 추가 (REQ-117)
- 파일: `trex_core/application/broker_port.py`
- 작업: `OrderRequest` frozen dataclass에 `venue: str = "KRX"` 필드 추가 (마지막 위치)
- 제약: 기존 위치 인수 순서(`slot_id`, `side`, `symbol`, `qty`, `limit_price`, `order_type`) 불변
### T-2: BrokerPort.cancel_order 시그니처 venue 파라미터 추가 (REQ-118)
- 파일: `trex_core/application/broker_port.py`
- 작업: `cancel_order` 프로토콜 시그니처에 `venue: str = "KRX"` 파라미터 추가
- 설계 결정: 시그니처 파라미터 추가 방식 채택(호출자 보존 방식 대비 명시적, 기본값으로 하위 호환)
### T-3: KISBroker.submit_order EXCG 명시 전송 (REQ-118 AC-1,2)
- 파일: `trex_core/adapters/broker/kis.py`
- 작업: body에 `"EXCG_ID_DVSN_CD": req.venue` 항상 추가
### T-4: KISBroker.cancel_order venue 파라미터 + EXCG 전송 (REQ-118 AC-3,8)
- 파일: `trex_core/adapters/broker/kis.py`
- 작업: 시그니처에 `venue: str = "KRX"` 추가, body에 `"EXCG_ID_DVSN_CD": venue` 추가
### T-5: KISBroker.poll_fill TR 유지 + open item 기록 (REQ-118 AC-4)
- 파일: `trex_core/adapters/broker/kis.py`
- 작업: 구 TR(TTTC8001R/VTTC8001R) 유지, 코드 주석에 NXT 체결 포함 여부 미확인 명시
- 이유: 웹 접근 불가 환경 — KIS 개발자 센터 문서 확인 불가. 보수 원칙: 기존 TR 유지, open item으로 기록
### T-6: KISBroker.get_current_price market 파라미터 추가 (REQ-118 AC-5,6)
- 파일: `trex_core/adapters/broker/kis.py`
- 작업: 시그니처에 `market: str = "J"` 추가, `FID_COND_MRKT_DIV_CODE` → market 값 사용
### T-7: PaperBroker.cancel_order 시그니처 정합 (REQ-118 AC-8, REQ-117 AC-5)
- 파일: `trex_core/adapters/broker/paper.py`
- 작업: cancel_order 시그니처에 `venue: str = "KRX"` 추가, 내부 로직 무변경(venue 무시)
### T-8: 신규 테스트 작성 (REQ-117 AC-1~5, REQ-118 AC-1~8)
- 파일: `tests/adapters/broker/test_venue.py` (신규)
- 작업: 13개 AC 커버 테스트
### T-9: 기존 테스트 회귀 확인 (REQ-117 AC-3, REQ-118 AC-7)
- 기존 테스트가 `OrderRequest` venue 미지정 경로를 통과하는지 확인
- 기존 submit_order fixture가 EXCG 필드 추가로 인한 페이로드 변화 감지 확인
### T-10: 문서 업데이트
- `docs/interface-contract.md` — OrderRequest venue 필드 추가
- `docs/runtime-flows.md` — venue 전달 경로 명시
## Dependency Order
T-1 → T-2 → T-3, T-4, T-5, T-6 (병렬) → T-7 → T-8 → T-9 → T-10
## Risk Points
1. `cancel_order` 시그니처 변경: BrokerPort 프로토콜 + KIS + paper 3개 파일 정합 필요
2. 기존 테스트 중 `cancel_order` 호출하는 코드 — venue 파라미터 없이 호출해도 기본값 KRX로 동작해야 함
3. `get_current_price` safety-sell 호출 경로: 기본값 "J" 유지로 무영향 확인 필요
## Estimation
- 구현: T-1~T-7 → 약 1시간
- 테스트: T-8~T-9 → 약 30분
- 문서: T-10 → 약 20분