1686 lines
60 KiB
Python
1686 lines
60 KiB
Python
import logging
|
|
import time
|
|
import sys
|
|
from typing import Optional, Tuple
|
|
|
|
import pandas as pd
|
|
|
|
sys.path.extend(['..', '.'])
|
|
import kis_auth as ka
|
|
|
|
# 로깅 설정
|
|
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
|
logger = logging.getLogger(__name__)
|
|
|
|
##############################################################################################
|
|
# [장내채권] 기본시세 > 장내채권 평균단가조회 [국내채권-158]
|
|
##############################################################################################
|
|
|
|
def avg_unit(
|
|
inqr_strt_dt: str, # 조회시작일자
|
|
inqr_end_dt: str, # 조회종료일자
|
|
pdno: str, # 상품번호
|
|
prdt_type_cd: str, # 상품유형코드
|
|
vrfc_kind_cd: str, # 검증종류코드
|
|
NK30: str = "", # 연속조회키30
|
|
FK100: str = "", # 연속조회검색조건100
|
|
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output1)
|
|
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output2)
|
|
dataframe3: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output3)
|
|
tr_cont: str = "",
|
|
depth: int = 0,
|
|
max_depth: int = 10
|
|
) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
|
|
"""
|
|
[장내채권] 기본시세
|
|
장내채권 평균단가조회[국내주식-158]
|
|
장내채권 평균단가조회 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
inqr_strt_dt (str): 조회 시작 일자 (예: '20230101')
|
|
inqr_end_dt (str): 조회 종료 일자 (예: '20230131')
|
|
pdno (str): 상품번호, 공백: 전체, 특정종목 조회시 : 종목코드
|
|
prdt_type_cd (str): 상품유형코드 (예: '302')
|
|
vrfc_kind_cd (str): 검증종류코드 (예: '00')
|
|
NK30 (str): 연속조회키30, 공백 허용
|
|
FK100 (str): 연속조회검색조건100, 공백 허용
|
|
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임 (output1)
|
|
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임 (output2)
|
|
dataframe3 (Optional[pd.DataFrame]): 누적 데이터프레임 (output3)
|
|
tr_cont (str): 연속 거래 여부
|
|
depth (int): 현재 재귀 깊이
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: 장내채권 평균단가조회 데이터
|
|
|
|
Example:
|
|
>>> df1, df2, df3 = avg_unit(
|
|
... inqr_strt_dt='20230101',
|
|
... inqr_end_dt='20230131',
|
|
... pdno='KR2033022D33',
|
|
... prdt_type_cd='302',
|
|
... vrfc_kind_cd='00',
|
|
... )
|
|
>>> print(df1)
|
|
>>> print(df2)
|
|
>>> print(df3)
|
|
"""
|
|
# 필수 파라미터 검증
|
|
if not inqr_strt_dt:
|
|
logger.error("inqr_strt_dt is required. (e.g. '20230101')")
|
|
raise ValueError("inqr_strt_dt is required. (e.g. '20230101')")
|
|
|
|
if not inqr_end_dt:
|
|
logger.error("inqr_end_dt is required. (e.g. '20230131')")
|
|
raise ValueError("inqr_end_dt is required. (e.g. '20230131')")
|
|
|
|
if not prdt_type_cd:
|
|
logger.error("prdt_type_cd is required. (e.g. '302')")
|
|
raise ValueError("prdt_type_cd is required. (e.g. '302')")
|
|
|
|
if not vrfc_kind_cd:
|
|
logger.error("vrfc_kind_cd is required. (e.g. '00')")
|
|
raise ValueError("vrfc_kind_cd is required. (e.g. '00')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return (
|
|
dataframe1 if dataframe1 is not None else pd.DataFrame(),
|
|
dataframe2 if dataframe2 is not None else pd.DataFrame(),
|
|
dataframe3 if dataframe3 is not None else pd.DataFrame()
|
|
)
|
|
|
|
tr_id = "CTPF2005R"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/quotations/avg-unit"
|
|
|
|
|
|
|
|
params = {
|
|
"INQR_STRT_DT": inqr_strt_dt,
|
|
"INQR_END_DT": inqr_end_dt,
|
|
"PDNO": pdno,
|
|
"PRDT_TYPE_CD": prdt_type_cd,
|
|
"VRFC_KIND_CD": vrfc_kind_cd,
|
|
"CTX_AREA_NK30": NK30,
|
|
"CTX_AREA_FK100": FK100,
|
|
}
|
|
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
# 연속조회 정보 업데이트
|
|
tr_cont = res.getHeader().tr_cont
|
|
NK30 = res.getBody().ctx_area_nk30
|
|
FK100 = res.getBody().ctx_area_fk100
|
|
|
|
# output1 데이터 처리
|
|
current_data1 = pd.DataFrame(res.getBody().output1)
|
|
if dataframe1 is not None:
|
|
dataframe1 = pd.concat([dataframe1, current_data1], ignore_index=True)
|
|
else:
|
|
dataframe1 = current_data1
|
|
|
|
# output2 데이터 처리
|
|
current_data2 = pd.DataFrame(res.getBody().output2)
|
|
if dataframe2 is not None:
|
|
dataframe2 = pd.concat([dataframe2, current_data2], ignore_index=True)
|
|
else:
|
|
dataframe2 = current_data2
|
|
|
|
# output3 데이터 처리
|
|
current_data3 = pd.DataFrame(res.getBody().output3)
|
|
if dataframe3 is not None:
|
|
dataframe3 = pd.concat([dataframe3, current_data3], ignore_index=True)
|
|
else:
|
|
dataframe3 = current_data3
|
|
|
|
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
|
logger.info("Call Next page...")
|
|
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
|
return avg_unit(
|
|
inqr_strt_dt,
|
|
inqr_end_dt,
|
|
pdno,
|
|
prdt_type_cd,
|
|
vrfc_kind_cd,
|
|
NK30,
|
|
FK100,
|
|
dataframe1,
|
|
dataframe2,
|
|
dataframe3,
|
|
"N",
|
|
depth + 1,
|
|
max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe1, dataframe2, dataframe3
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame(), pd.DataFrame(), pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 주문/계좌 > 장내채권 매수주문 [국내주식-124]
|
|
##############################################################################################
|
|
|
|
def buy(
|
|
cano: str,
|
|
acnt_prdt_cd: str,
|
|
pdno: str,
|
|
ord_qty2: str,
|
|
bond_ord_unpr: str,
|
|
samt_mket_ptci_yn: str,
|
|
bond_rtl_mket_yn: str,
|
|
idcr_stfno: str = "",
|
|
mgco_aptm_odno: str = "",
|
|
ord_svr_dvsn_cd: str = "",
|
|
ctac_tlno: str = ""
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 주문/계좌
|
|
장내채권 매수주문[국내주식-124]
|
|
장내채권 매수주문 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
cano (str): 종합계좌번호 (8자리)
|
|
acnt_prdt_cd (str): 계좌상품코드 (2자리)
|
|
pdno (str): 상품번호 (12자리)
|
|
ord_qty2 (str): 주문수량2 (19자리)
|
|
bond_ord_unpr (str): 채권주문단가 (182자리)
|
|
samt_mket_ptci_yn (str): 소액시장참여여부 ('Y' or 'N')
|
|
bond_rtl_mket_yn (str): 채권소매시장여부 ('Y' or 'N')
|
|
idcr_stfno (str, optional): 유치자직원번호 (6자리). Defaults to "".
|
|
mgco_aptm_odno (str, optional): 운용사지정주문번호 (12자리). Defaults to "".
|
|
ord_svr_dvsn_cd (str, optional): 주문서버구분코드. Defaults to "".
|
|
ctac_tlno (str, optional): 연락전화번호. Defaults to "".
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권 매수주문 데이터
|
|
|
|
Example:
|
|
>>> df = buy(
|
|
... cano=trenv.my_acct,
|
|
... acnt_prdt_cd=trenv.my_prod,
|
|
... pdno="KR1234567890",
|
|
... ord_qty2="10",
|
|
... bond_ord_unpr="10000",
|
|
... samt_mket_ptci_yn="N",
|
|
... bond_rtl_mket_yn="Y"
|
|
... )
|
|
>>> print(df)
|
|
"""
|
|
tr_id = "TTTC0952U"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/trading/buy"
|
|
|
|
|
|
|
|
params = {
|
|
"CANO": cano,
|
|
"ACNT_PRDT_CD": acnt_prdt_cd,
|
|
"PDNO": pdno,
|
|
"ORD_QTY2": ord_qty2,
|
|
"BOND_ORD_UNPR": bond_ord_unpr,
|
|
"SAMT_MKET_PTCI_YN": samt_mket_ptci_yn,
|
|
"BOND_RTL_MKET_YN": bond_rtl_mket_yn,
|
|
"IDCR_STFNO": idcr_stfno,
|
|
"MGCO_APTM_ODNO": mgco_aptm_odno,
|
|
"ORD_SVR_DVSN_CD": ord_svr_dvsn_cd,
|
|
"CTAC_TLNO": ctac_tlno,
|
|
}
|
|
|
|
res = ka._url_fetch(api_url=api_url,
|
|
ptr_id=tr_id,
|
|
tr_cont="",
|
|
params=params,
|
|
postFlag=True
|
|
)
|
|
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
dataframe = pd.DataFrame(output_data)
|
|
else:
|
|
dataframe = pd.DataFrame()
|
|
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 기본시세 > 장내채권현재가(호가) [국내주식-132]
|
|
##############################################################################################
|
|
|
|
def inquire_asking_price(
|
|
fid_cond_mrkt_div_code: str, # 시장 분류 코드
|
|
fid_input_iscd: str, # 채권종목코드
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 기본시세
|
|
장내채권현재가(호가)[국내주식-132]
|
|
장내채권현재가(호가) API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
fid_cond_mrkt_div_code (str): 시장 분류 코드 (B 입력)
|
|
fid_input_iscd (str): 채권종목코드 (ex KR2033022D33)
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임 (기본값: None)
|
|
depth (int): 현재 재귀 깊이 (기본값: 0)
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권현재가(호가) 데이터
|
|
|
|
Example:
|
|
>>> df = inquire_asking_price(fid_cond_mrkt_div_code="B", fid_input_iscd="KR2033022D33")
|
|
>>> print(df)
|
|
"""
|
|
# 필수 파라미터 검증
|
|
if not fid_cond_mrkt_div_code:
|
|
logger.error("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
raise ValueError("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
|
|
if not fid_input_iscd:
|
|
logger.error("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
raise ValueError("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "FHKBJ773401C0"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/quotations/inquire-asking-price"
|
|
|
|
|
|
|
|
params = {
|
|
"FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code,
|
|
"FID_INPUT_ISCD": fid_input_iscd,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
# 응답 데이터 처리
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
# 데이터프레임 병합
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
# 연속 거래 여부 확인
|
|
tr_cont = res.getHeader().tr_cont
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_asking_price(
|
|
fid_cond_mrkt_div_code,
|
|
fid_input_iscd,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
# API 에러 처리
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 주문/계좌 > 장내채권 잔고조회 [국내주식-198]
|
|
##############################################################################################
|
|
|
|
def inquire_balance(
|
|
cano: str, # 종합계좌번호
|
|
acnt_prdt_cd: str, # 계좌상품코드
|
|
inqr_cndt: str, # 조회조건
|
|
pdno: str, # 상품번호
|
|
buy_dt: str, # 매수일자
|
|
FK200: str = "", # 연속조회검색조건200
|
|
NK200: str = "", # 연속조회키200
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 주문/계좌
|
|
장내채권 잔고조회[국내주식-198]
|
|
장내채권 잔고조회 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
cano (str): 종합계좌번호
|
|
acnt_prdt_cd (str): 계좌상품코드
|
|
inqr_cndt (str): 조회조건 (00: 전체, 01: 상품번호단위)
|
|
pdno (str): 상품번호 (공백 허용)
|
|
buy_dt (str): 매수일자 (공백 허용)
|
|
FK200 (str): 연속조회검색조건200
|
|
NK200 (str): 연속조회키200
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
|
depth (int): 현재 재귀 깊이
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권 잔고조회 데이터
|
|
|
|
Example:
|
|
>>> df = inquire_balance(
|
|
... cano=trenv.my_acct,
|
|
... acnt_prdt_cd=trenv.my_prod,
|
|
... inqr_cndt='00',
|
|
... pdno='',
|
|
... buy_dt='',
|
|
... )
|
|
>>> print(df)
|
|
"""
|
|
# 로깅 설정
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# 필수 파라미터 검증
|
|
if not cano:
|
|
logger.error("cano is required. (e.g. '12345678')")
|
|
raise ValueError("cano is required. (e.g. '12345678')")
|
|
|
|
if not acnt_prdt_cd:
|
|
logger.error("acnt_prdt_cd is required. (e.g. '01')")
|
|
raise ValueError("acnt_prdt_cd is required. (e.g. '01')")
|
|
|
|
if not inqr_cndt:
|
|
logger.error("inqr_cndt is required. (e.g. '00')")
|
|
raise ValueError("inqr_cndt is required. (e.g. '00')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "CTSC8407R"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/trading/inquire-balance"
|
|
|
|
|
|
|
|
params = {
|
|
"CANO": cano,
|
|
"ACNT_PRDT_CD": acnt_prdt_cd,
|
|
"INQR_CNDT": inqr_cndt,
|
|
"PDNO": pdno,
|
|
"BUY_DT": buy_dt,
|
|
"CTX_AREA_FK200": FK200,
|
|
"CTX_AREA_NK200": NK200,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
tr_cont = res.getHeader().tr_cont
|
|
NK200 = res.getBody().ctx_area_nk200
|
|
FK200 = res.getBody().ctx_area_fk200
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_balance(
|
|
cano,
|
|
acnt_prdt_cd,
|
|
inqr_cndt,
|
|
pdno,
|
|
buy_dt,
|
|
FK200,
|
|
NK200,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 기본시세 > 장내채권현재가(체결) [국내주식-201]
|
|
##############################################################################################
|
|
|
|
def inquire_ccnl(
|
|
fid_cond_mrkt_div_code: str, # 조건시장분류코드
|
|
fid_input_iscd: str, # 입력종목코드
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 기본시세
|
|
장내채권현재가(체결)[국내주식-201]
|
|
장내채권현재가(체결) API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
fid_cond_mrkt_div_code (str): 조건시장분류코드 (예: 'B')
|
|
fid_input_iscd (str): 입력종목코드 (예: 'KR2033022D33')
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임 (기본값: None)
|
|
depth (int): 현재 재귀 깊이 (기본값: 0)
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권현재가(체결) 데이터
|
|
|
|
Example:
|
|
>>> df = inquire_ccnl('B', 'KR2033022D33')
|
|
>>> print(df)
|
|
"""
|
|
# 필수 파라미터 검증
|
|
if not fid_cond_mrkt_div_code:
|
|
logger.error("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
raise ValueError("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
|
|
if not fid_input_iscd:
|
|
logger.error("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
raise ValueError("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "FHKBJ773403C0"
|
|
|
|
# API 요청 파라미터 설정
|
|
|
|
api_url = "/uapi/domestic-bond/v1/quotations/inquire-ccnl"
|
|
|
|
|
|
params = {
|
|
"FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code,
|
|
"FID_INPUT_ISCD": fid_input_iscd,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
# API 응답 처리
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
# 데이터프레임 병합
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
# 연속 거래 여부 확인
|
|
tr_cont = res.getHeader().tr_cont
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_ccnl(
|
|
fid_cond_mrkt_div_code,
|
|
fid_input_iscd,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 주문/계좌 > 장내채권 일별체결조회 [국내주식-127]
|
|
##############################################################################################
|
|
|
|
def inquire_daily_ccld(
|
|
cano: str, # 종합계좌번호
|
|
acnt_prdt_cd: str, # 계좌상품코드
|
|
inqr_strt_dt: str, # 조회시작일자
|
|
inqr_end_dt: str, # 조회종료일자
|
|
sll_buy_dvsn_cd: str, # 매도매수구분코드
|
|
sort_sqn_dvsn: str, # 정렬순서구분
|
|
pdno: str, # 상품번호
|
|
nccs_yn: str, # 미체결여부
|
|
ctx_area_nk200: str, # 연속조회키200
|
|
ctx_area_fk200: str, # 연속조회검색조건200
|
|
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output1)
|
|
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output2)
|
|
tr_cont: str = "",
|
|
depth: int = 0,
|
|
max_depth: int = 10
|
|
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
"""
|
|
[장내채권] 주문/계좌
|
|
장내채권 주문체결내역[국내주식-127]
|
|
장내채권 주문체결내역 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
cano (str): 종합계좌번호
|
|
acnt_prdt_cd (str): 계좌상품코드
|
|
inqr_strt_dt (str): 조회시작일자 (1주일 이내)
|
|
inqr_end_dt (str): 조회종료일자 (조회 당일)
|
|
sll_buy_dvsn_cd (str): 매도매수구분코드 (%(전체), 01(매도), 02(매수))
|
|
sort_sqn_dvsn (str): 정렬순서구분 (01(주문순서), 02(주문역순))
|
|
pdno (str): 상품번호
|
|
nccs_yn (str): 미체결여부 (N(전체), C(체결), Y(미체결))
|
|
ctx_area_nk200 (str): 연속조회키200
|
|
ctx_area_fk200 (str): 연속조회검색조건200
|
|
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임 (output1)
|
|
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임 (output2)
|
|
tr_cont (str): 연속 거래 여부
|
|
depth (int): 현재 재귀 깊이
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Tuple[pd.DataFrame, pd.DataFrame]: 장내채권 주문체결내역 데이터
|
|
|
|
Example:
|
|
>>> df1, df2 = inquire_daily_ccld(
|
|
... cano=trenv.my_acct,
|
|
... acnt_prdt_cd=trenv.my_prod,
|
|
... inqr_strt_dt='20230101',
|
|
... inqr_end_dt='20230107',
|
|
... sll_buy_dvsn_cd='01',
|
|
... sort_sqn_dvsn='01',
|
|
... pdno='000000000001',
|
|
... nccs_yn='N',
|
|
... ctx_area_nk200='',
|
|
... ctx_area_fk200=''
|
|
... )
|
|
>>> print(df1)
|
|
>>> print(df2)
|
|
"""
|
|
# 필수 파라미터 검증
|
|
if not cano:
|
|
logger.error("cano is required. (e.g. '12345678')")
|
|
raise ValueError("cano is required. (e.g. '12345678')")
|
|
if not acnt_prdt_cd:
|
|
logger.error("acnt_prdt_cd is required. (e.g. '01')")
|
|
raise ValueError("acnt_prdt_cd is required. (e.g. '01')")
|
|
if not inqr_strt_dt:
|
|
logger.error("inqr_strt_dt is required. (e.g. '20230101')")
|
|
raise ValueError("inqr_strt_dt is required. (e.g. '20230101')")
|
|
if not inqr_end_dt:
|
|
logger.error("inqr_end_dt is required. (e.g. '20230107')")
|
|
raise ValueError("inqr_end_dt is required. (e.g. '20230107')")
|
|
if not sll_buy_dvsn_cd in ["%", "01", "02"]:
|
|
logger.error("sll_buy_dvsn_cd is required. (e.g. '01')")
|
|
raise ValueError("sll_buy_dvsn_cd is required. (e.g. '01')")
|
|
if not sort_sqn_dvsn in ["01", "02"]:
|
|
logger.error("sort_sqn_dvsn is required. (e.g. '01')")
|
|
raise ValueError("sort_sqn_dvsn is required. (e.g. '01')")
|
|
if not nccs_yn in ["N", "C", "Y"]:
|
|
logger.error("nccs_yn is required. (e.g. 'N')")
|
|
raise ValueError("nccs_yn is required. (e.g. 'N')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe1 if dataframe1 is not None else pd.DataFrame(), dataframe2 if dataframe2 is not None else pd.DataFrame()
|
|
|
|
tr_id = "CTSC8013R"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/trading/inquire-daily-ccld"
|
|
|
|
|
|
|
|
params = {
|
|
"CANO": cano,
|
|
"ACNT_PRDT_CD": acnt_prdt_cd,
|
|
"INQR_STRT_DT": inqr_strt_dt,
|
|
"INQR_END_DT": inqr_end_dt,
|
|
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd,
|
|
"SORT_SQN_DVSN": sort_sqn_dvsn,
|
|
"PDNO": pdno,
|
|
"NCCS_YN": nccs_yn,
|
|
"CTX_AREA_NK200": ctx_area_nk200,
|
|
"CTX_AREA_FK200": ctx_area_fk200,
|
|
}
|
|
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
# output1 처리
|
|
if hasattr(res.getBody(), 'output1'):
|
|
output_data = res.getBody().output1
|
|
if output_data:
|
|
# output1은 단일 객체, output2는 배열일 수 있음
|
|
if isinstance(output_data, list):
|
|
current_data1 = pd.DataFrame(output_data)
|
|
else:
|
|
# 단일 객체인 경우 리스트로 감싸서 DataFrame 생성
|
|
current_data1 = pd.DataFrame([output_data])
|
|
|
|
if dataframe1 is not None:
|
|
dataframe1 = pd.concat([dataframe1, current_data1], ignore_index=True)
|
|
else:
|
|
dataframe1 = current_data1
|
|
else:
|
|
if dataframe1 is None:
|
|
dataframe1 = pd.DataFrame()
|
|
else:
|
|
if dataframe1 is None:
|
|
dataframe1 = pd.DataFrame()
|
|
# output2 처리
|
|
if hasattr(res.getBody(), 'output2'):
|
|
output_data = res.getBody().output2
|
|
if output_data:
|
|
# output1은 단일 객체, output2는 배열일 수 있음
|
|
if isinstance(output_data, list):
|
|
current_data2 = pd.DataFrame(output_data)
|
|
else:
|
|
# 단일 객체인 경우 리스트로 감싸서 DataFrame 생성
|
|
current_data2 = pd.DataFrame([output_data])
|
|
|
|
if dataframe2 is not None:
|
|
dataframe2 = pd.concat([dataframe2, current_data2], ignore_index=True)
|
|
else:
|
|
dataframe2 = current_data2
|
|
else:
|
|
if dataframe2 is None:
|
|
dataframe2 = pd.DataFrame()
|
|
else:
|
|
if dataframe2 is None:
|
|
dataframe2 = pd.DataFrame()
|
|
tr_cont = res.getHeader().tr_cont
|
|
ctx_area_nk200 = res.getBody().ctx_area_nk200
|
|
ctx_area_fk200 = res.getBody().ctx_area_fk200
|
|
|
|
if tr_cont in ["M", "F"]:
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_daily_ccld(
|
|
cano,
|
|
acnt_prdt_cd,
|
|
inqr_strt_dt,
|
|
inqr_end_dt,
|
|
sll_buy_dvsn_cd,
|
|
sort_sqn_dvsn,
|
|
pdno,
|
|
nccs_yn,
|
|
ctx_area_nk200,
|
|
ctx_area_fk200,
|
|
"N", dataframe1, dataframe2, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe1, dataframe2
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame(), pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 기본시세 > 장내채권 기간별시세(일) [국내주식-159]
|
|
##############################################################################################
|
|
|
|
def inquire_daily_itemchartprice(
|
|
fid_cond_mrkt_div_code: str, # 조건 시장 구분 코드
|
|
fid_input_iscd: str, # 입력 종목코드
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 기본시세
|
|
장내채권 기간별시세(일)[국내주식-159]
|
|
장내채권 기간별시세(일) API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
fid_cond_mrkt_div_code (str): 조건 시장 구분 코드 (필수)
|
|
fid_input_iscd (str): 입력 종목코드 (필수)
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임 (기본값: None)
|
|
depth (int): 현재 재귀 깊이 (기본값: 0)
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권 기간별시세(일) 데이터
|
|
|
|
Example:
|
|
>>> df = inquire_daily_itemchartprice("B", "KR2033022D33")
|
|
>>> print(df)
|
|
"""
|
|
# 필수 파라미터 검증
|
|
if not fid_cond_mrkt_div_code:
|
|
logger.error("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
raise ValueError("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
|
|
if not fid_input_iscd:
|
|
logger.error("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
raise ValueError("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "FHKBJ773701C0"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/quotations/inquire-daily-itemchartprice"
|
|
|
|
|
|
|
|
params = {
|
|
"FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code,
|
|
"FID_INPUT_ISCD": fid_input_iscd,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
# 데이터프레임 병합
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
# 연속 거래 여부 확인
|
|
tr_cont = res.getHeader().tr_cont
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_daily_itemchartprice(
|
|
fid_cond_mrkt_div_code,
|
|
fid_input_iscd,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 기본시세 > 장내채권 일별시세 [국내주식-202]
|
|
##############################################################################################
|
|
|
|
def inquire_daily_price(
|
|
fid_cond_mrkt_div_code: str, # 조건시장분류코드
|
|
fid_input_iscd: str, # 입력종목코드
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 기본시세
|
|
장내채권현재가(일별)[국내주식-202]
|
|
장내채권현재가(일별) API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
fid_cond_mrkt_div_code (str): 조건시장분류코드 (예: 'B')
|
|
fid_input_iscd (str): 입력종목코드 (예: 'KR2033022D33')
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임 (기본값: None)
|
|
depth (int): 현재 재귀 깊이 (기본값: 0)
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권현재가(일별) 데이터
|
|
|
|
Example:
|
|
>>> df = inquire_daily_price('B', 'KR2033022D33')
|
|
>>> print(df)
|
|
"""
|
|
# 로깅 설정
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# 필수 파라미터 검증
|
|
if not fid_cond_mrkt_div_code:
|
|
logger.error("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
raise ValueError("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
|
|
if not fid_input_iscd:
|
|
logger.error("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
raise ValueError("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "FHKBJ773404C0"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/quotations/inquire-daily-price"
|
|
|
|
|
|
|
|
params = {
|
|
"FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code,
|
|
"FID_INPUT_ISCD": fid_input_iscd,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
# 응답 데이터 처리
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
# 데이터프레임 병합
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
# 연속 거래 여부 확인
|
|
tr_cont = res.getHeader().tr_cont
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_daily_price(
|
|
fid_cond_mrkt_div_code,
|
|
fid_input_iscd,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
# API 에러 처리
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 기본시세 > 장내채권현재가(시세) [국내주식-200]
|
|
##############################################################################################
|
|
|
|
def inquire_price(
|
|
fid_cond_mrkt_div_code: str, # 조건시장분류코드
|
|
fid_input_iscd: str, # 입력종목코드
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 기본시세
|
|
장내채권현재가(시세)[국내주식-200]
|
|
장내채권현재가(시세) API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
fid_cond_mrkt_div_code (str): 조건시장분류코드 (예: 'B')
|
|
fid_input_iscd (str): 입력종목코드 (예: 'KR2033022D33')
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임 (기본값: None)
|
|
depth (int): 현재 재귀 깊이 (기본값: 0)
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권현재가(시세) 데이터
|
|
|
|
Example:
|
|
>>> df = inquire_price('B', 'KR2033022D33')
|
|
>>> print(df)
|
|
"""
|
|
# 필수 파라미터 검증
|
|
if not fid_cond_mrkt_div_code:
|
|
logger.error("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
raise ValueError("fid_cond_mrkt_div_code is required. (e.g. 'B')")
|
|
|
|
if not fid_input_iscd:
|
|
logger.error("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
raise ValueError("fid_input_iscd is required. (e.g. 'KR2033022D33')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "FHKBJ773400C0"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/quotations/inquire-price"
|
|
|
|
|
|
|
|
params = {
|
|
"FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code,
|
|
"FID_INPUT_ISCD": fid_input_iscd,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
# 데이터프레임 병합
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
# 연속 거래 여부 확인
|
|
tr_cont = res.getHeader().tr_cont
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_price(
|
|
fid_cond_mrkt_div_code,
|
|
fid_input_iscd,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 주문/계좌 > 장내채권 매수가능조회 [국내주식-199]
|
|
##############################################################################################
|
|
|
|
def inquire_psbl_order(
|
|
cano: str, # 종합계좌번호
|
|
acnt_prdt_cd: str, # 계좌상품코드
|
|
pdno: str, # 상품번호
|
|
bond_ord_unpr: str, # 채권주문단가
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 주문/계좌
|
|
장내채권 매수가능조회[국내주식-199]
|
|
장내채권 매수가능조회 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
cano (str): 종합계좌번호 (필수)
|
|
acnt_prdt_cd (str): 계좌상품코드 (필수)
|
|
pdno (str): 채권종목코드(ex KR2033022D33)
|
|
bond_ord_unpr (str): 채권주문단가 (필수)
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
|
depth (int): 현재 재귀 깊이
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권 매수가능조회 데이터
|
|
|
|
Example:
|
|
>>> df = inquire_psbl_order("12345678", "01", "KR2033022D33", "1000")
|
|
>>> print(df)
|
|
"""
|
|
# 필수 파라미터 검증
|
|
if not cano:
|
|
logger.error("cano is required. (e.g. '1234567890')")
|
|
raise ValueError("cano is required. (e.g. '1234567890')")
|
|
|
|
if not acnt_prdt_cd:
|
|
logger.error("acnt_prdt_cd is required. (e.g. '01')")
|
|
raise ValueError("acnt_prdt_cd is required. (e.g. '01')")
|
|
|
|
if not pdno:
|
|
logger.error("pdno is required. (e.g. 'KR2033022D33')")
|
|
raise ValueError("pdno is required. (e.g. 'KR2033022D33')")
|
|
|
|
if not bond_ord_unpr:
|
|
logger.error("bond_ord_unpr is required. (e.g. '1000')")
|
|
raise ValueError("bond_ord_unpr is required. (e.g. '1000')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "TTTC8910R"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/trading/inquire-psbl-order"
|
|
|
|
|
|
|
|
params = {
|
|
"CANO": cano,
|
|
"ACNT_PRDT_CD": acnt_prdt_cd,
|
|
"PDNO": pdno,
|
|
"BOND_ORD_UNPR": bond_ord_unpr,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
# 응답 데이터 처리
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
# 데이터프레임 병합
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
# 연속 거래 여부 확인
|
|
tr_cont = res.getHeader().tr_cont
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_psbl_order(
|
|
cano,
|
|
acnt_prdt_cd,
|
|
pdno,
|
|
bond_ord_unpr,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
# API 에러 처리
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 주문/계좌 > 채권정정취소가능주문조회 [국내주식-126]
|
|
##############################################################################################
|
|
|
|
def inquire_psbl_rvsecncl(
|
|
cano: str, # 종합계좌번호
|
|
acnt_prdt_cd: str, # 계좌상품코드
|
|
ord_dt: str, # 주문일자
|
|
odno: str, # 주문번호
|
|
ctx_area_fk200: str, # 연속조회검색조건200
|
|
ctx_area_nk200: str, # 연속조회키200
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 주문/계좌
|
|
채권정정취소가능주문조회[국내주식-126]
|
|
채권정정취소가능주문조회 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
cano (str): 종합계좌번호 (예: '12345678')
|
|
acnt_prdt_cd (str): 계좌상품코드 (예: '01')
|
|
ord_dt (str): 주문일자 (예: '20230101')
|
|
odno (str): 주문번호 (예: '0000000001')
|
|
ctx_area_fk200 (str): 연속조회검색조건200 (예: '조건값')
|
|
ctx_area_nk200 (str): 연속조회키200 (예: '키값')
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
|
depth (int): 현재 재귀 깊이
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 채권정정취소가능주문조회 데이터
|
|
|
|
Example:
|
|
>>> df = inquire_psbl_rvsecncl(
|
|
... cano=trenv.my_acct,
|
|
... acnt_prdt_cd=trenv.my_prod,
|
|
... ord_dt='20230101',
|
|
... odno='0000000001',
|
|
... ctx_area_fk200='조건값',
|
|
... ctx_area_nk200='키값'
|
|
... )
|
|
>>> print(df)
|
|
"""
|
|
# 필수 파라미터 검증
|
|
if not cano:
|
|
logger.error("cano is required. (e.g. '12345678')")
|
|
raise ValueError("cano is required. (e.g. '12345678')")
|
|
if not acnt_prdt_cd:
|
|
logger.error("acnt_prdt_cd is required. (e.g. '01')")
|
|
raise ValueError("acnt_prdt_cd is required. (e.g. '01')")
|
|
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "CTSC8035R"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/trading/inquire-psbl-rvsecncl"
|
|
|
|
|
|
|
|
params = {
|
|
"CANO": cano,
|
|
"ACNT_PRDT_CD": acnt_prdt_cd,
|
|
"ORD_DT": ord_dt,
|
|
"ODNO": odno,
|
|
"CTX_AREA_FK200": ctx_area_fk200,
|
|
"CTX_AREA_NK200": ctx_area_nk200,
|
|
}
|
|
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
tr_cont = res.getHeader().tr_cont
|
|
ctx_area_nk200 = res.getBody().ctx_area_nk200
|
|
ctx_area_fk200 = res.getBody().ctx_area_fk200
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return inquire_psbl_rvsecncl(
|
|
cano,
|
|
acnt_prdt_cd,
|
|
ord_dt,
|
|
odno,
|
|
ctx_area_fk200,
|
|
ctx_area_nk200,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 기본시세 > 장내채권 발행정보 [국내주식-156]
|
|
##############################################################################################
|
|
|
|
def issue_info(
|
|
pdno: str, # 사용자권한정보
|
|
prdt_type_cd: str, # 거래소코드
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 기본시세
|
|
장내채권 발행정보[국내주식-156]
|
|
장내채권 발행정보 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
pdno (str): 채권 종목번호(ex. KR6449111CB8)
|
|
prdt_type_cd (str): Unique key(302)
|
|
tr_cont (str): 연속 거래 여부
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
|
depth (int): 현재 재귀 깊이
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권 발행정보 데이터
|
|
|
|
Example:
|
|
>>> df = issue_info("KR6449111CB8", "302")
|
|
>>> print(df)
|
|
"""
|
|
# 로깅 설정
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# 필수 파라미터 검증
|
|
if not pdno:
|
|
logger.error("pdno is required. (e.g. 'KR6449111CB8')")
|
|
raise ValueError("pdno is required. (e.g. 'KR6449111CB8')")
|
|
|
|
if not prdt_type_cd:
|
|
logger.error("prdt_type_cd is required. (e.g. '302')")
|
|
raise ValueError("prdt_type_cd is required. (e.g. '302')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
# API 호출 URL 및 거래 ID 설정
|
|
tr_id = "CTPF1101R"
|
|
|
|
# 요청 파라미터 설정
|
|
|
|
api_url = "/uapi/domestic-bond/v1/quotations/issue-info"
|
|
|
|
|
|
params = {
|
|
"PDNO": pdno,
|
|
"PRDT_TYPE_CD": prdt_type_cd,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
# API 호출 성공 여부 확인
|
|
if res.isOK():
|
|
# 응답 데이터 처리
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
# 데이터프레임 병합
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
# 연속 거래 여부 확인
|
|
tr_cont = res.getHeader().tr_cont
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return issue_info(
|
|
pdno,
|
|
prdt_type_cd,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
# API 호출 실패 시 에러 로그 출력
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 주문/계좌 > 장내채권 정정취소주문 [국내주식-125]
|
|
##############################################################################################
|
|
|
|
def order_rvsecncl(
|
|
cano: str,
|
|
acnt_prdt_cd: str,
|
|
pdno: str,
|
|
orgn_odno: str,
|
|
ord_qty2: str,
|
|
bond_ord_unpr: str,
|
|
qty_all_ord_yn: str,
|
|
rvse_cncl_dvsn_cd: str,
|
|
mgco_aptm_odno: str = "",
|
|
ord_svr_dvsn_cd: str = "0",
|
|
ctac_tlno: str = ""
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 주문/계좌
|
|
장내채권 정정취소주문[국내주식-125]
|
|
장내채권 정정취소주문 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
cano (str): 종합계좌번호
|
|
acnt_prdt_cd (str): 계좌상품코드
|
|
pdno (str): 상품번호
|
|
orgn_odno (str): 원주문번호
|
|
ord_qty2 (str): 주문수량2
|
|
bond_ord_unpr (str): 채권주문단가
|
|
qty_all_ord_yn (str): 잔량전부주문여부
|
|
rvse_cncl_dvsn_cd (str): 정정취소구분코드
|
|
mgco_aptm_odno (str, optional): 운용사지정주문번호. Defaults to "".
|
|
ord_svr_dvsn_cd (str, optional): 주문서버구분코드. Defaults to "0".
|
|
ctac_tlno (str, optional): 연락전화번호. Defaults to "".
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권 정정취소주문 데이터
|
|
|
|
Example:
|
|
>>> df = order_rvsecncl(
|
|
... cano=trenv.my_acct,
|
|
... acnt_prdt_cd=trenv.my_prod,
|
|
... pdno="KR6095572D81",
|
|
... orgn_odno="0000015402",
|
|
... ord_qty2="2",
|
|
... bond_ord_unpr="10460",
|
|
... qty_all_ord_yn="Y",
|
|
... rvse_cncl_dvsn_cd="01"
|
|
... )
|
|
>>> print(df)
|
|
"""
|
|
tr_id = "TTTC0953U"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/trading/order-rvsecncl"
|
|
|
|
|
|
|
|
params = {
|
|
"CANO": cano,
|
|
"ACNT_PRDT_CD": acnt_prdt_cd,
|
|
"PDNO": pdno,
|
|
"ORGN_ODNO": orgn_odno,
|
|
"ORD_QTY2": ord_qty2,
|
|
"BOND_ORD_UNPR": bond_ord_unpr,
|
|
"QTY_ALL_ORD_YN": qty_all_ord_yn,
|
|
"RVSE_CNCL_DVSN_CD": rvse_cncl_dvsn_cd,
|
|
"MGCO_APTM_ODNO": mgco_aptm_odno,
|
|
"ORD_SVR_DVSN_CD": ord_svr_dvsn_cd,
|
|
"CTAC_TLNO": ctac_tlno
|
|
}
|
|
|
|
res = ka._url_fetch(api_url=api_url,
|
|
ptr_id=tr_id,
|
|
tr_cont="",
|
|
params=params,
|
|
postFlag=True
|
|
)
|
|
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
dataframe = pd.DataFrame(output_data)
|
|
else:
|
|
dataframe = pd.DataFrame()
|
|
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 기본시세 > 장내채권 기본조회 [국내주식-129]
|
|
##############################################################################################
|
|
|
|
def search_bond_info(
|
|
pdno: str, # 상품번호
|
|
prdt_type_cd: str, # 상품유형코드
|
|
tr_cont: str = "", # 연속 거래 여부
|
|
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
|
depth: int = 0, # 현재 재귀 깊이
|
|
max_depth: int = 10 # 최대 재귀 깊이
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 기본시세
|
|
장내채권 기본조회[국내주식-129]
|
|
장내채권 기본조회 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
pdno (str): 상품번호 (필수)
|
|
prdt_type_cd (str): 상품유형코드 (필수)
|
|
tr_cont (str): 연속 거래 여부 (기본값: "")
|
|
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
|
depth (int): 현재 재귀 깊이
|
|
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권 기본조회 데이터
|
|
|
|
Example:
|
|
>>> df = search_bond_info("KR2033022D33", "302")
|
|
>>> print(df)
|
|
"""
|
|
# 로깅 설정
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# 필수 파라미터 검증
|
|
if not pdno:
|
|
logger.error("pdno is required. (e.g. 'KR2033022D33')")
|
|
raise ValueError("pdno is required. (e.g. 'KR2033022D33')")
|
|
|
|
if not prdt_type_cd:
|
|
logger.error("prdt_type_cd is required. (e.g. '302')")
|
|
raise ValueError("prdt_type_cd is required. (e.g. '302')")
|
|
|
|
# 최대 재귀 깊이 체크
|
|
if depth >= max_depth:
|
|
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
|
return dataframe if dataframe is not None else pd.DataFrame()
|
|
|
|
tr_id = "CTPF1114R"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/quotations/search-bond-info"
|
|
|
|
|
|
|
|
params = {
|
|
"PDNO": pdno,
|
|
"PRDT_TYPE_CD": prdt_type_cd,
|
|
}
|
|
|
|
# API 호출
|
|
res = ka._url_fetch(api_url, tr_id, tr_cont, params)
|
|
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
current_data = pd.DataFrame(output_data)
|
|
else:
|
|
current_data = pd.DataFrame()
|
|
|
|
if dataframe is not None:
|
|
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
|
else:
|
|
dataframe = current_data
|
|
|
|
tr_cont = res.getHeader().tr_cont
|
|
|
|
if tr_cont == "M":
|
|
logger.info("Calling next page...")
|
|
ka.smart_sleep()
|
|
return search_bond_info(
|
|
pdno,
|
|
prdt_type_cd,
|
|
"N", dataframe, depth + 1, max_depth
|
|
)
|
|
else:
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|
|
##############################################################################################
|
|
# [장내채권] 주문/계좌 > 장내채권 매도주문 [국내주식-123]
|
|
##############################################################################################
|
|
|
|
def sell(
|
|
cano: str,
|
|
acnt_prdt_cd: str,
|
|
ord_dvsn: str,
|
|
pdno: str,
|
|
ord_qty2: str,
|
|
bond_ord_unpr: str,
|
|
sprx_yn: str,
|
|
samt_mket_ptci_yn: str,
|
|
sll_agco_opps_sll_yn: str,
|
|
bond_rtl_mket_yn: str,
|
|
buy_dt: str = "",
|
|
buy_seq: str = "",
|
|
mgco_aptm_odno: str = "",
|
|
ord_svr_dvsn_cd: str = "0",
|
|
ctac_tlno: str = ""
|
|
) -> Optional[pd.DataFrame]:
|
|
"""
|
|
[장내채권] 주문/계좌
|
|
장내채권 매도주문[국내주식-123]
|
|
장내채권 매도주문 API를 호출하여 DataFrame으로 반환합니다.
|
|
|
|
Args:
|
|
cano (str): 종합계좌번호
|
|
acnt_prdt_cd (str): 계좌상품코드
|
|
ord_dvsn (str): 주문구분
|
|
pdno (str): 상품번호
|
|
ord_qty2 (str): 주문수량2
|
|
bond_ord_unpr (str): 채권주문단가
|
|
sprx_yn (str): 분리과세여부
|
|
samt_mket_ptci_yn (str): 소액시장참여여부
|
|
sll_agco_opps_sll_yn (str): 매도대행사반대매도여부
|
|
bond_rtl_mket_yn (str): 채권소매시장여부
|
|
buy_dt (str, optional): 매수일자. Defaults to "".
|
|
buy_seq (str, optional): 매수순번. Defaults to "".
|
|
mgco_aptm_odno (str, optional): 운용사지정주문번호. Defaults to "".
|
|
ord_svr_dvsn_cd (str, optional): 주문서버구분코드. Defaults to "0".
|
|
ctac_tlno (str, optional): 연락전화번호. Defaults to "".
|
|
|
|
Returns:
|
|
Optional[pd.DataFrame]: 장내채권 매도주문 데이터
|
|
|
|
Example:
|
|
>>> df = sell(
|
|
... cano=trenv.my_acct,
|
|
... acnt_prdt_cd=trenv.my_prod,
|
|
... ord_dvsn="01",
|
|
... pdno="KR6095572D81",
|
|
... ord_qty2="1",
|
|
... bond_ord_unpr="10000.0",
|
|
... sprx_yn="N",
|
|
... samt_mket_ptci_yn="N",
|
|
... sll_agco_opps_sll_yn="N",
|
|
... bond_rtl_mket_yn="N"
|
|
... )
|
|
>>> print(df)
|
|
"""
|
|
tr_id = "TTTC0958U"
|
|
|
|
|
|
api_url = "/uapi/domestic-bond/v1/trading/sell"
|
|
|
|
|
|
|
|
params = {
|
|
"CANO": cano,
|
|
"ACNT_PRDT_CD": acnt_prdt_cd,
|
|
"ORD_DVSN": ord_dvsn,
|
|
"PDNO": pdno,
|
|
"ORD_QTY2": ord_qty2,
|
|
"BOND_ORD_UNPR": bond_ord_unpr,
|
|
"SPRX_YN": sprx_yn,
|
|
"BUY_DT": buy_dt,
|
|
"BUY_SEQ": buy_seq,
|
|
"SAMT_MKET_PTCI_YN": samt_mket_ptci_yn,
|
|
"SLL_AGCO_OPPS_SLL_YN": sll_agco_opps_sll_yn,
|
|
"BOND_RTL_MKET_YN": bond_rtl_mket_yn,
|
|
"MGCO_APTM_ODNO": mgco_aptm_odno,
|
|
"ORD_SVR_DVSN_CD": ord_svr_dvsn_cd,
|
|
"CTAC_TLNO": ctac_tlno
|
|
}
|
|
|
|
res = ka._url_fetch(api_url=api_url,
|
|
ptr_id=tr_id,
|
|
tr_cont="",
|
|
params=params,
|
|
postFlag=True
|
|
)
|
|
|
|
if res.isOK():
|
|
if hasattr(res.getBody(), 'output'):
|
|
output_data = res.getBody().output
|
|
if not isinstance(output_data, list):
|
|
output_data = [output_data]
|
|
dataframe = pd.DataFrame(output_data)
|
|
else:
|
|
dataframe = pd.DataFrame()
|
|
|
|
logger.info("Data fetch complete.")
|
|
return dataframe
|
|
else:
|
|
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
|
res.printError(api_url)
|
|
return pd.DataFrame()
|
|
|