"백엔드_핵심_로직_구현_프론트엔드_연동_및_도커_배포_최적화_완료"

This commit is contained in:
2026-02-03 00:52:54 +09:00
parent ed8fc0943b
commit eeddc62089
32 changed files with 1287 additions and 318 deletions

View File

@@ -6,6 +6,7 @@ from app.db.database import SessionLocal
from app.db.models import ApiSettings
from sqlalchemy import select
from app.core.crypto import decrypt_str
import logging
class KisClient:
"""
@@ -15,16 +16,19 @@ class KisClient:
# Domestic URLs
URL_DOMESTIC_ORDER = "/uapi/domestic-stock/v1/trading/order-cash"
URL_DOMESTIC_MODIFY = "/uapi/domestic-stock/v1/trading/order-rvsecncl"
URL_DOMESTIC_PRICE = "/uapi/domestic-stock/v1/quotations/inquire-price"
URL_DOMESTIC_BALANCE = "/uapi/domestic-stock/v1/trading/inquire-balance"
# Overseas URLs
URL_OVERSEAS_ORDER = "/uapi/overseas-stock/v1/trading/order"
URL_OVERSEAS_MODIFY = "/uapi/overseas-stock/v1/trading/order-rvsecncl"
URL_OVERSEAS_PRICE = "/uapi/overseas-price/v1/quotations/price"
URL_OVERSEAS_BALANCE = "/uapi/overseas-stock/v1/trading/inquire-balance"
def __init__(self):
pass
self.logger = logging.getLogger(self.__class__.__name__)
async def _get_settings(self):
async with SessionLocal() as session:
@@ -60,6 +64,7 @@ class KisClient:
}
full_url = f"{base_url}{url_path}"
# self.logger.debug(f"API Calling: {method} {url_path} (TR_ID: {tr_id})")
async with httpx.AsyncClient() as client:
if method == "GET":
@@ -178,6 +183,7 @@ class KisClient:
"ORD_QTY": str(quantity),
"ORD_UNPR": str(int(price)), # Cash Order requires integer price string
}
self.logger.info(f"Ordering Domestic: {side} {code} {quantity}qty @ {price}")
return await self._call_api("POST", self.URL_DOMESTIC_ORDER, tr_id, data=data)
elif market == "Overseas":
@@ -197,4 +203,52 @@ class KisClient:
}
return await self._call_api("POST", self.URL_OVERSEAS_ORDER, tr_id, data=data)
async def modify_order(self, market: str, order_no: str, code: str, quantity: int, price: float, type: str = "00", cancel: bool = False) -> Dict:
"""
Cancel or Modify Order.
cancel=True -> Cancel
"""
settings = await self._get_settings()
acc_no_str = decrypt_str(settings.accountNumber)
if '-' in acc_no_str:
cano, prdt = acc_no_str.split('-')
else:
cano = acc_no_str[:8]
prdt = acc_no_str[8:]
if market == "Domestic":
# TR_ID: TTTC0803U (Modify/Cancel)
data = {
"CANO": cano,
"ACNT_PRDT_CD": prdt,
"KRX_FWDG_ORD_ORGNO": "", # Exchange Node? Usually empty or "00950"
"ORGN_ODNO": order_no,
"ORD_DVSN": type,
"RVSE_CNCL_DVSN_CD": "02" if cancel else "01", # 01: Modify, 02: Cancel
"ORD_QTY": str(quantity),
"ORD_UNPR": str(int(price)),
"QTY_ALL_ORD_YN": "Y" if quantity == 0 else "N", # 0 means cancel all?
}
# Note: KRX_FWDG_ORD_ORGNO is tricky. Usually 5 digit branch code. Defaulting to "" might fail.
# Using '06010' (Online) or leaving blank depending on API.
# KIS API Doc: "주문상태조회"에서 얻은 ORGNO 사용해야 함.
# For this impl, we assume user knows or simple default.
return await self._call_api("POST", self.URL_DOMESTIC_MODIFY, "TTTC0803U", data=data)
elif market == "Overseas":
# MCCL: TTTT1004U
data = {
"CANO": cano,
"ACNT_PRDT_CD": prdt,
"OVRS_EXCG_CD": "NASD",
"PDNO": code,
"ORGN_ODNO": order_no,
"RVSE_CNCL_DVSN_CD": "02" if cancel else "01",
"ORD_QTY": str(quantity),
"OVRS_ORD_UNPR": str(price),
}
return await self._call_api("POST", self.URL_OVERSEAS_MODIFY, "TTTT1004U", data=data)
kis_client = KisClient()