"백엔드_핵심_로직_구현_프론트엔드_연동_및_도커_배포_최적화_완료"
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user