feat: add portfolio snapshot read layer

This commit is contained in:
2026-06-06 09:33:00 +09:00
parent 56e977a7ad
commit ca757009f5
51 changed files with 3565 additions and 51 deletions
+38
View File
@@ -16,6 +16,7 @@ from typing import Optional
import httpx
from trex_core.application.broker_port import (
BalanceItem,
FillStatus,
OrderAck,
OrderRequest,
@@ -196,6 +197,43 @@ class KISBroker:
return FillStatus(state="partially_filled", filled_qty=ccld_qty,
avg_price=float(avg) if avg else None, raw=row)
def get_balance(self) -> list[BalanceItem]:
"""KIS 주식잔고조회 (`TTTC8434R`/`VTTC8434R`).
Aggregates `output1[*].pdno` + `hldg_qty`, drops zero-qty rows
(D-2 leftover, Design Decision 7). Network/credential errors map to
an empty list (caller treats as no broker positions).
"""
cano, product = self._account()
tr_id = "VTTC8434R" if self._mock else "TTTC8434R"
params = {
"CANO": cano, "ACNT_PRDT_CD": product,
"AFHR_FLPR_YN": "N", "OFL_YN": "",
"INQR_DVSN": "02", "UNPR_DVSN": "01",
"FUND_STTL_ICLD_YN": "N", "FNCG_AMT_AUTO_RDPT_YN": "N",
"PRCS_DVSN": "01",
"CTX_AREA_FK100": "", "CTX_AREA_NK100": "",
}
try:
resp = httpx.get(
f"{self._base_url}/uapi/domestic-stock/v1/trading/inquire-balance",
headers=self._headers(tr_id), params=params, timeout=self._timeout,
)
payload = resp.json()
except (httpx.HTTPError, ValueError):
return []
if str(payload.get("rt_cd")) != "0":
return []
rows = payload.get("output1") or []
out: list[BalanceItem] = []
for row in rows:
symbol = str(row.get("pdno") or "").strip()
qty = _safe_int(row.get("hldg_qty")) or 0
if not symbol or qty <= 0:
continue
out.append(BalanceItem(symbol=symbol, hldg_qty=qty))
return out
def cancel_order(self, broker_order_id: str, *, side: str, symbol: str, qty: int) -> bool:
cano, product = self._account()
parts = broker_order_id.split(":")