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:
@@ -172,6 +172,10 @@ class KISBroker:
|
||||
"ORD_DVSN": req.order_type,
|
||||
"ORD_QTY": str(req.qty),
|
||||
"ORD_UNPR": str(req.limit_price or 0),
|
||||
# REQ-118: always send EXCG_ID_DVSN_CD from req.venue (default "KRX").
|
||||
# Previously omitted (server defaulted to KRX); explicit is more robust.
|
||||
# Real-account NXT order confirmed rt_cd=0 on 2026-07-06.
|
||||
"EXCG_ID_DVSN_CD": req.venue,
|
||||
}
|
||||
try:
|
||||
resp = httpx.post(
|
||||
@@ -197,6 +201,9 @@ class KISBroker:
|
||||
cano, product = self._account()
|
||||
odno = broker_order_id.split(":")[-1]
|
||||
today = datetime.now(tz=_KST).strftime("%Y%m%d")
|
||||
# REQ-118 AC-4 OPEN ITEM: TTTC8001R NXT 체결 포함 여부 미확인 (웹 접근 불가 환경,
|
||||
# KIS 개발자 센터 문서 조회 불가). 보수 구현: 기존 TR 유지. KRX 경로 동작 절대 불변.
|
||||
# 향후 NXT 체결 미포함 실측 확인 시 TTTC0081R(EXCG=ALL) 전환 별도 batch 처리.
|
||||
tr_id = "VTTC8001R" if self._mock else "TTTC8001R"
|
||||
params = {
|
||||
"CANO": cano, "ACNT_PRDT_CD": product,
|
||||
@@ -311,14 +318,16 @@ class KISBroker:
|
||||
out.append(BalanceItem(symbol=symbol, hldg_qty=qty))
|
||||
return out
|
||||
|
||||
def get_current_price(self, symbol: str) -> Optional[Decimal]:
|
||||
"""Current traded price `stck_prpr` via KIS inquire-price (REQ-038).
|
||||
def get_current_price(self, symbol: str, market: str = "J") -> Optional[Decimal]:
|
||||
"""Current traded price `stck_prpr` via KIS inquire-price (REQ-038, REQ-118).
|
||||
|
||||
tr_id: `TTTC8397R` (live) / `VTTC8397R` (mock) per REQ-038 AC-7.
|
||||
tr_id: `FHKST01010100` per REQ-038 AC-7 (live and mock share the same tr_id).
|
||||
market: KIS FID_COND_MRKT_DIV_CODE (REQ-118). Default "J" (KRX equities)
|
||||
preserves backward compatibility for existing safety-sell callers.
|
||||
Returns None on network/parse error or missing price so the
|
||||
safety-sell worker skips the slot this tick (no false trigger).
|
||||
"""
|
||||
params = {"FID_COND_MRKT_DIV_CODE": "J", "FID_INPUT_ISCD": symbol}
|
||||
params = {"FID_COND_MRKT_DIV_CODE": market, "FID_INPUT_ISCD": symbol}
|
||||
try:
|
||||
resp = httpx.get(
|
||||
f"{self._base_url}/uapi/domestic-stock/v1/quotations/inquire-price",
|
||||
@@ -338,7 +347,13 @@ class KISBroker:
|
||||
except (InvalidOperation, ValueError):
|
||||
return None
|
||||
|
||||
def cancel_order(self, broker_order_id: str, *, side: str, symbol: str, qty: int) -> bool:
|
||||
def cancel_order(self, broker_order_id: str, *, side: str, symbol: str, qty: int, venue: str = "KRX") -> bool:
|
||||
"""Cancel a previously submitted order (REQ-118).
|
||||
|
||||
venue: exchange venue of the original order. Must match the EXCG_ID_DVSN_CD
|
||||
used at submit time. Default "KRX" preserves backward compatibility.
|
||||
Real-account NXT cancel (EXCG=NXT) confirmed rt_cd=0 on 2026-07-06.
|
||||
"""
|
||||
cano, product = self._account()
|
||||
parts = broker_order_id.split(":")
|
||||
orgno, odno = (parts[0], parts[1]) if len(parts) == 2 else ("", parts[-1])
|
||||
@@ -347,6 +362,8 @@ class KISBroker:
|
||||
"KRX_FWDG_ORD_ORGNO": orgno, "ORGN_ODNO": odno,
|
||||
"ORD_DVSN": "00", "RVSE_CNCL_DVSN_CD": "02",
|
||||
"ORD_QTY": str(qty), "ORD_UNPR": "0", "QTY_ALL_ORD_YN": "Y",
|
||||
# REQ-118: cancel must use the same EXCG_ID_DVSN_CD as the original submit.
|
||||
"EXCG_ID_DVSN_CD": venue,
|
||||
}
|
||||
tr_id = "VTTC0013U" if self._mock else "TTTC0013U"
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user