initial commit
This commit is contained in:
110
한국투자증권(API)/examples_llm/overseas_stock/algo_ordno/algo_ordno.py
Normal file
110
한국투자증권(API)/examples_llm/overseas_stock/algo_ordno/algo_ordno.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 지정가주문번호조회 [해외주식-071]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/algo-ordno"
|
||||
|
||||
def algo_ordno(
|
||||
cano: str, # [필수] 종합계좌번호
|
||||
acnt_prdt_cd: str, # [필수] 계좌상품코드 (ex. 01)
|
||||
trad_dt: str, # [필수] 거래일자
|
||||
FK200: str = "", # 연속조회검색조건200
|
||||
NK200: str = "", # 연속조회키200
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
TWAP, VWAP 주문에 대한 주문번호를 조회하는 API
|
||||
|
||||
Args:
|
||||
cano (str): [필수] 종합계좌번호
|
||||
acnt_prdt_cd (str): [필수] 계좌상품코드 (ex. 01)
|
||||
trad_dt (str): [필수] 거래일자
|
||||
FK200 (str): 연속조회검색조건200
|
||||
NK200 (str): 연속조회키200
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외주식 지정가주문번호 데이터
|
||||
|
||||
Example:
|
||||
>>> df = algo_ordno(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, trad_dt="20250619")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if cano == "":
|
||||
raise ValueError("cano is required")
|
||||
|
||||
if acnt_prdt_cd == "":
|
||||
raise ValueError("acnt_prdt_cd is required (e.g. '01')")
|
||||
|
||||
if trad_dt == "":
|
||||
raise ValueError("trad_dt is required")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe is None:
|
||||
return pd.DataFrame()
|
||||
else:
|
||||
return dataframe
|
||||
|
||||
tr_id = "TTTS6058R" # 해외주식 지정가주문번호조회
|
||||
|
||||
params = {
|
||||
"CANO": cano, # 종합계좌번호
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd, # 계좌상품코드
|
||||
"TRAD_DT": trad_dt, # 거래일자
|
||||
"CTX_AREA_FK200": FK200, # 연속조회검색조건200
|
||||
"CTX_AREA_NK200": NK200 # 연속조회키200
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output)
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
FK200 = res.getBody().ctx_area_fk200
|
||||
NK200 = res.getBody().ctx_area_nk200
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return algo_ordno(
|
||||
cano, acnt_prdt_cd, trad_dt, FK200, NK200, "N", dataframe, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from algo_ordno import algo_ordno
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 지정가주문번호조회 [해외주식-071]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'ODNO': '주문번호',
|
||||
'TRAD_DVSN_NAME': '매매구분명',
|
||||
'PDNO': '상품번호',
|
||||
'ITEM_NAME': '종목명',
|
||||
'FT_ORD_QTY': 'FT주문수량',
|
||||
'FT_ORD_UNPR3': 'FT주문단가',
|
||||
'SPLT_BUY_ATTR_NAME': '분할매수속성명',
|
||||
'FT_CCLD_QTY': 'FT체결수량',
|
||||
'ORD_GNO_BRNO': '주문채번지점번호'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 지정가주문번호조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 지정가주문번호조회 API를 호출하여 결과를 출력합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# case1 조회
|
||||
logging.info("=== case1 조회 ===")
|
||||
try:
|
||||
result = algo_ordno(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, trad_dt="20250619")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logging.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 실시간시세 > 해외주식 실시간호가[실시간-021]
|
||||
##############################################################################################
|
||||
|
||||
def asking_price(
|
||||
tr_type: str,
|
||||
tr_key: str,
|
||||
) -> tuple[dict, list[str]]:
|
||||
"""
|
||||
해외주식 실시간호가를 이용하여 미국시세 실시간 1호가(매수/매도) 시세가 무료로 제공됩니다. (미국은 유료시세 제공 X)
|
||||
|
||||
아시아 국가의 경우, HTS(efriend Plus) [7781] 시세신청(실시간) 화면에서 유료 서비스 신청 시,
|
||||
"해외주식 실시간호가 HDFSASP0" 을 이용하여 아시아국가 유료시세(실시간호가)를 받아보실 수 있습니다. (24.11.29 반영)
|
||||
(아시아 국가 무료시세는 "해외주식 지연호가(아시아) HDFSASP1" 를 이용하시기 바랍니다.)
|
||||
|
||||
Args:
|
||||
tr_type (str): [필수] 등록/해제
|
||||
tr_key (str): [필수] 종목코드
|
||||
|
||||
Returns:
|
||||
message (dict): 메시지 데이터
|
||||
columns (list[str]): 컬럼 정보
|
||||
|
||||
Example:
|
||||
>>> msg, columns = asking_price("1", "DNASAAPL")
|
||||
>>> print(msg, columns)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if tr_type == "":
|
||||
raise ValueError("tr_type is required")
|
||||
|
||||
if tr_key == "":
|
||||
raise ValueError("tr_key is required")
|
||||
|
||||
tr_id = "HDFSASP0"
|
||||
|
||||
params = {
|
||||
"tr_key": tr_key,
|
||||
}
|
||||
|
||||
msg = ka.data_fetch(tr_id, tr_type, params)
|
||||
|
||||
columns = [
|
||||
"symb",
|
||||
"zdiv",
|
||||
"xymd",
|
||||
"xhms",
|
||||
"kymd",
|
||||
"khms",
|
||||
"bvol",
|
||||
"avol",
|
||||
"bdvl",
|
||||
"advl",
|
||||
"pbid1",
|
||||
"pask1",
|
||||
"vbid1",
|
||||
"vask1",
|
||||
"dbid1",
|
||||
"dask1"
|
||||
]
|
||||
|
||||
return msg, columns
|
||||
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from asking_price import asking_price
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 실시간시세 > 해외주식 실시간호가[실시간-021]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
"symb": "종목코드",
|
||||
"zdiv": "소숫점자리수",
|
||||
"xymd": "현지일자",
|
||||
"xhms": "현지시간",
|
||||
"kymd": "한국일자",
|
||||
"khms": "한국시간",
|
||||
"bvol": "매수총잔량",
|
||||
"avol": "매도총잔량",
|
||||
"bdvl": "매수총잔량대비",
|
||||
"advl": "매도총잔량대비",
|
||||
"pbid1": "매수호가1",
|
||||
"pask1": "매도호가1",
|
||||
"vbid1": "매수잔량1",
|
||||
"vask1": "매도잔량1",
|
||||
"dbid1": "매수잔량대비1",
|
||||
"dask1": "매도잔량대비1"
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 실시간호가
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
ka.auth_ws()
|
||||
|
||||
# 인증(auth_ws()) 이후에 선언
|
||||
kws = ka.KISWebSocket(api_url="/tryitout")
|
||||
|
||||
# 조회
|
||||
kws.subscribe(request=asking_price, data=["RBAQAAPL"])
|
||||
|
||||
# 결과 표시
|
||||
def on_result(ws, tr_id: str, result: pd.DataFrame, data_map: dict):
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
kws.start(on_result=on_result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외속보(제목) [해외주식-055]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/brknews-title"
|
||||
|
||||
def brknews_title(
|
||||
fid_news_ofer_entp_code: str, # [필수] 뉴스제공업체코드 (ex. 0:전체조회)
|
||||
fid_cond_scr_div_code: str, # [필수] 조건화면분류코드 (ex. 11801)
|
||||
fid_cond_mrkt_cls_code: str = "", # 조건시장구분코드
|
||||
fid_input_iscd: str = "", # 입력종목코드
|
||||
fid_titl_cntt: str = "", # 제목내용
|
||||
fid_input_date_1: str = "", # 입력날짜1
|
||||
fid_input_hour_1: str = "", # 입력시간1
|
||||
fid_rank_sort_cls_code: str = "", # 순위정렬구분코드
|
||||
fid_input_srno: str = "" # 입력일련번호
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외속보(제목) API입니다.
|
||||
한국투자 HTS(eFriend Plus) > [7704] 해외속보 화면 의 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
|
||||
|
||||
최대 100건까지 조회 가능합니다.
|
||||
|
||||
Args:
|
||||
fid_news_ofer_entp_code (str): [필수] 뉴스제공업체코드 (ex. 0:전체조회)
|
||||
fid_cond_scr_div_code (str): [필수] 조건화면분류코드 (ex. 11801)
|
||||
fid_cond_mrkt_cls_code (str): 조건시장구분코드
|
||||
fid_input_iscd (str): 입력종목코드
|
||||
fid_titl_cntt (str): 제목내용
|
||||
fid_input_date_1 (str): 입력날짜1
|
||||
fid_input_hour_1 (str): 입력시간1
|
||||
fid_rank_sort_cls_code (str): 순위정렬구분코드
|
||||
fid_input_srno (str): 입력일련번호
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외속보(제목) 데이터
|
||||
|
||||
Example:
|
||||
>>> df = brknews_title("0", "11801")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if fid_news_ofer_entp_code == "":
|
||||
raise ValueError("fid_news_ofer_entp_code is required (e.g. '0')")
|
||||
|
||||
if fid_cond_scr_div_code == "":
|
||||
raise ValueError("fid_cond_scr_div_code is required (e.g. '11801')")
|
||||
|
||||
tr_id = "FHKST01011801"
|
||||
|
||||
params = {
|
||||
"FID_NEWS_OFER_ENTP_CODE": fid_news_ofer_entp_code,
|
||||
"FID_COND_SCR_DIV_CODE": fid_cond_scr_div_code,
|
||||
"FID_COND_MRKT_CLS_CODE": fid_cond_mrkt_cls_code,
|
||||
"FID_INPUT_ISCD": fid_input_iscd,
|
||||
"FID_TITL_CNTT": fid_titl_cntt,
|
||||
"FID_INPUT_DATE_1": fid_input_date_1,
|
||||
"FID_INPUT_HOUR_1": fid_input_hour_1,
|
||||
"FID_RANK_SORT_CLS_CODE": fid_rank_sort_cls_code,
|
||||
"FID_INPUT_SRNO": fid_input_srno
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, "", params)
|
||||
|
||||
if res.isOK():
|
||||
return pd.DataFrame(res.getBody().output)
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from brknews_title import brknews_title
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외속보(제목) [해외주식-055]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'cntt_usiq_srno': '내용조회용일련번호',
|
||||
'news_ofer_entp_code': '뉴스제공업체코드',
|
||||
'data_dt': '작성일자',
|
||||
'data_tm': '작성시간',
|
||||
'hts_pbnt_titl_cntt': 'HTS공시제목내용',
|
||||
'news_lrdv_code': '뉴스대구분',
|
||||
'dorg': '자료원',
|
||||
'iscd1': '종목코드1',
|
||||
'iscd2': '종목코드2',
|
||||
'iscd3': '종목코드3',
|
||||
'iscd4': '종목코드4',
|
||||
'iscd5': '종목코드5',
|
||||
'iscd6': '종목코드6',
|
||||
'iscd7': '종목코드7',
|
||||
'iscd8': '종목코드8',
|
||||
'iscd9': '종목코드9',
|
||||
'iscd10': '종목코드10',
|
||||
'kor_isnm1': '한글종목명1',
|
||||
'kor_isnm2': '한글종목명2',
|
||||
'kor_isnm3': '한글종목명3',
|
||||
'kor_isnm4': '한글종목명4',
|
||||
'kor_isnm5': '한글종목명5',
|
||||
'kor_isnm6': '한글종목명6',
|
||||
'kor_isnm7': '한글종목명7',
|
||||
'kor_isnm8': '한글종목명8',
|
||||
'kor_isnm9': '한글종목명9',
|
||||
'kor_isnm10': '한글종목명10'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외속보(제목) 조회 테스트 함수
|
||||
|
||||
이 함수는 해외속보(제목) API를 호출하여 결과를 출력합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# case1 조회
|
||||
logging.info("=== case1 조회 ===")
|
||||
try:
|
||||
result = brknews_title(fid_news_ofer_entp_code="0", fid_cond_scr_div_code="11801")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logging.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 실시간시세 > 해외주식 실시간체결통보[실시간-009]
|
||||
##############################################################################################
|
||||
|
||||
def ccnl_notice(
|
||||
tr_type: str,
|
||||
tr_key: str,
|
||||
env_dv: str,
|
||||
) -> tuple[dict, list[str]]:
|
||||
"""
|
||||
해외주식 실시간체결통보 API입니다.
|
||||
|
||||
Args:
|
||||
tr_type (str): [필수] 등록/해제
|
||||
tr_key (str): [필수] 종목코드
|
||||
env_dv (str): [필수] 실전모의구분 (ex. real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
message (dict): 메시지 데이터
|
||||
columns (list[str]): 컬럼 정보
|
||||
|
||||
Example:
|
||||
>>> msg, columns = ccnl_notice("1", trenv.my_htsid, "real")
|
||||
>>> print(msg, columns)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if tr_type == "":
|
||||
raise ValueError("tr_type is required")
|
||||
|
||||
if tr_key == "":
|
||||
raise ValueError("tr_key is required")
|
||||
|
||||
if env_dv == "":
|
||||
raise ValueError("env_dv is required (e.g. 'real', 'demo')")
|
||||
|
||||
# tr_id 구분
|
||||
if env_dv == "real":
|
||||
tr_id = "H0GSCNI0"
|
||||
elif env_dv == "demo":
|
||||
tr_id = "H0GSCNI9"
|
||||
else:
|
||||
raise ValueError("env_dv can only be real or demo")
|
||||
|
||||
params = {
|
||||
"tr_key": tr_key,
|
||||
}
|
||||
|
||||
msg = ka.data_fetch(tr_id, tr_type, params)
|
||||
|
||||
columns = [
|
||||
"CUST_ID",
|
||||
"ACNT_NO",
|
||||
"ODER_NO",
|
||||
"OODER_NO",
|
||||
"SELN_BYOV_CLS",
|
||||
"RCTF_CLS",
|
||||
"ODER_KIND2",
|
||||
"STCK_SHRN_ISCD",
|
||||
"CNTG_QTY",
|
||||
"CNTG_UNPR",
|
||||
"STCK_CNTG_HOUR",
|
||||
"RFUS_YN",
|
||||
"CNTG_YN",
|
||||
"ACPT_YN",
|
||||
"BRNC_NO",
|
||||
"ODER_QTY",
|
||||
"ACNT_NAME",
|
||||
"CNTG_ISNM",
|
||||
"ODER_COND",
|
||||
"DEBT_GB",
|
||||
"DEBT_DATE",
|
||||
"START_TM",
|
||||
"END_TM",
|
||||
"TM_DIV_TP",
|
||||
"CNTG_UNPR12"
|
||||
]
|
||||
|
||||
return msg, columns
|
||||
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from ccnl_notice import ccnl_notice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 실시간시세 > 해외주식 실시간체결통보[실시간-009]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
"CUST_ID": "고객 ID",
|
||||
"ACNT_NO": "계좌번호",
|
||||
"ODER_NO": "주문번호",
|
||||
"OODER_NO": "원주문번호",
|
||||
"SELN_BYOV_CLS": "매도매수구분",
|
||||
"RCTF_CLS": "정정구분",
|
||||
"ODER_KIND2": "주문종류2",
|
||||
"STCK_SHRN_ISCD": "주식 단축 종목코드",
|
||||
"CNTG_QTY": "체결수량",
|
||||
"CNTG_UNPR": "체결단가",
|
||||
"STCK_CNTG_HOUR": "주식 체결 시간",
|
||||
"RFUS_YN": "거부여부",
|
||||
"CNTG_YN": "체결여부",
|
||||
"ACPT_YN": "접수여부",
|
||||
"BRNC_NO": "지점번호",
|
||||
"ODER_QTY": "주문 수량",
|
||||
"ACNT_NAME": "계좌명",
|
||||
"CNTG_ISNM": "체결종목명",
|
||||
"ODER_COND": "해외종목구분",
|
||||
"DEBT_GB": "담보유형코드",
|
||||
"DEBT_DATE": "담보대출일자",
|
||||
"START_TM": "분할매수/매도 시작시간",
|
||||
"END_TM": "분할매수/매도 종료시간",
|
||||
"TM_DIV_TP": "시간분할타입유형",
|
||||
"CNTG_UNPR": "체결단가12"
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 실시간체결통보
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
ka.auth_ws()
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# 인증(auth_ws()) 이후에 선언
|
||||
kws = ka.KISWebSocket(api_url="/tryitout")
|
||||
|
||||
# 조회
|
||||
kws.subscribe(request=ccnl_notice, data=[trenv.my_htsid], kwargs={"env_dv": "real"})
|
||||
|
||||
# 결과 표시
|
||||
def on_result(ws, tr_id: str, result: pd.DataFrame, data_map: dict):
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
kws.start(on_result=on_result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,95 @@
|
||||
"""
|
||||
Created on 20250101
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from colable_by_company import colable_by_company
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 당사 해외주식담보대출 가능 종목 [해외주식-051]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'pdno': '상품번호',
|
||||
'ovrs_item_name': '해외종목명',
|
||||
'loan_rt': '대출비율',
|
||||
'mgge_mntn_rt': '담보유지비율',
|
||||
'mgge_ensu_rt': '담보확보비율',
|
||||
'loan_exec_psbl_yn': '대출실행가능여부',
|
||||
'stff_name': '직원명',
|
||||
'erlm_dt': '등록일자',
|
||||
'tr_mket_name': '거래시장명',
|
||||
'crcy_cd': '통화코드',
|
||||
'natn_kor_name': '국가한글명',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'loan_psbl_item_num': '대출가능종목수'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
당사 해외주식담보대출 가능 종목 조회 테스트 함수
|
||||
|
||||
이 함수는 당사 해외주식담보대출 가능 종목 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 AMD 종목을 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# 한줄함수호출 -> 컬럼명 한글 변환 -> 숫자형 컬럼 소수점표시 -> 결과 (case1)
|
||||
logger.info("=== case1 조회 ===")
|
||||
try:
|
||||
result1, result2 = colable_by_company(pdno="AMD", natn_cd="840", inqr_sqn_dvsn="01")
|
||||
except ValueError as e:
|
||||
logger.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logger.info("=== output1 데이터 ===")
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("결과:")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logger.info("=== output2 데이터 ===")
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("결과:")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,153 @@
|
||||
"""
|
||||
Created on 20250101
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 당사 해외주식담보대출 가능 종목 [해외주식-051]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/colable-by-company"
|
||||
|
||||
def colable_by_company(
|
||||
pdno: str, # 상품번호
|
||||
natn_cd: str, # 국가코드
|
||||
inqr_sqn_dvsn: str, # 조회순서구분
|
||||
prdt_type_cd: str = "", # 상품유형코드
|
||||
inqr_strt_dt: str = "", # 조회시작일자
|
||||
inqr_end_dt: str = "", # 조회종료일자
|
||||
inqr_dvsn: str = "", # 조회구분
|
||||
rt_dvsn_cd: str = "", # 비율구분코드
|
||||
rt: str = "", # 비율
|
||||
loan_psbl_yn: str = "", # 대출가능여부
|
||||
FK100: str = "", # 연속조회검색조건100
|
||||
NK100: str = "", # 연속조회키100
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output1)
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output2)
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
당사 해외주식담보대출 가능 종목 API입니다.
|
||||
한국투자 HTS(eFriend Plus) > [0497] 당사 해외주식담보대출 가능 종목 화면 의 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
|
||||
|
||||
한 번의 호출에 20건까지 조회가 가능하며 다음조회가 불가하기에, PDNO에 데이터 확인하고자 하는 종목코드를 입력하여 단건조회용으로 사용하시기 바랍니다.
|
||||
|
||||
Args:
|
||||
pdno (str): [필수] 상품번호 (ex. AMD)
|
||||
natn_cd (str): [필수] 국가코드 (ex. 840:미국,344:홍콩,156:중국)
|
||||
inqr_sqn_dvsn (str): [필수] 조회순서구분 (ex. 01:이름순,02:코드순)
|
||||
prdt_type_cd (str): 상품유형코드
|
||||
inqr_strt_dt (str): 조회시작일자
|
||||
inqr_end_dt (str): 조회종료일자
|
||||
inqr_dvsn (str): 조회구분
|
||||
rt_dvsn_cd (str): 비율구분코드
|
||||
rt (str): 비율
|
||||
loan_psbl_yn (str): 대출가능여부
|
||||
FK100 (str): 연속조회검색조건100
|
||||
NK100 (str): 연속조회키100
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임 (output1)
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임 (output2)
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1 데이터, output2 데이터)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = colable_by_company(pdno="AMD", natn_cd="840", inqr_sqn_dvsn="01")
|
||||
>>> print(df1) # output1 데이터
|
||||
>>> print(df2) # output2 데이터
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if pdno == "":
|
||||
raise ValueError("pdno is required (e.g. 'AMD')")
|
||||
|
||||
if natn_cd == "":
|
||||
raise ValueError("natn_cd is required (e.g. '840:미국,344:홍콩,156:중국')")
|
||||
|
||||
if inqr_sqn_dvsn == "":
|
||||
raise ValueError("inqr_sqn_dvsn is required (e.g. '01:이름순,02:코드순')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "CTLN4050R"
|
||||
|
||||
params = {
|
||||
"PDNO": pdno,
|
||||
"NATN_CD": natn_cd,
|
||||
"INQR_SQN_DVSN": inqr_sqn_dvsn,
|
||||
"PRDT_TYPE_CD": prdt_type_cd,
|
||||
"INQR_STRT_DT": inqr_strt_dt,
|
||||
"INQR_END_DT": inqr_end_dt,
|
||||
"INQR_DVSN": inqr_dvsn,
|
||||
"RT_DVSN_CD": rt_dvsn_cd,
|
||||
"RT": rt,
|
||||
"LOAN_PSBL_YN": loan_psbl_yn,
|
||||
"CTX_AREA_FK100": FK100,
|
||||
"CTX_AREA_NK100": NK100
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리 (array)
|
||||
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 처리 (object)
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
FK100 = res.getBody().ctx_area_fk100
|
||||
NK100 = res.getBody().ctx_area_nk100
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return colable_by_company(
|
||||
pdno, natn_cd, inqr_sqn_dvsn, prdt_type_cd, inqr_strt_dt, inqr_end_dt,
|
||||
inqr_dvsn, rt_dvsn_cd, rt, loan_psbl_yn, FK100, NK100, "N",
|
||||
dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
@@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from countries_holiday import countries_holiday
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외결제일자조회[해외주식-017]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'prdt_type_cd': '상품유형코드',
|
||||
'tr_natn_cd': '거래국가코드',
|
||||
'natn_eng_abrv_cd': '국가영문약어코드',
|
||||
'tr_mket_cd': '거래시장코드',
|
||||
'tr_mket_name': '거래시장명',
|
||||
'acpl_sttl_dt': '현지결제일자',
|
||||
'dmst_sttl_dt': '국내결제일자'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외결제일자조회[해외주식-017]
|
||||
|
||||
해외결제일자조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- trad_dt (str): 기준일자 (기준일자(YYYYMMDD))
|
||||
- ctx_area_nk (str): 연속조회키 (공백으로 입력)
|
||||
- ctx_area_fk (str): 연속조회검색조건 (공백으로 입력)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외결제일자조회 결과
|
||||
|
||||
Example:
|
||||
>>> df = countries_holiday(trad_dt="20250131", NK="", FK="")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외결제일자조회")
|
||||
result = countries_holiday(
|
||||
trad_dt="20250131", # 기준일자
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외결제일자조회 결과 ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,114 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-26
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외결제일자조회[해외주식-017]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/quotations/countries-holiday"
|
||||
|
||||
def countries_holiday(
|
||||
trad_dt: str, # 기준일자
|
||||
NK: str = "", # 연속조회키
|
||||
FK: str = "", # 연속조회검색조건
|
||||
tr_cont: str = "", # 연속 거래 여부
|
||||
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
||||
depth: int = 0, # 현재 재귀 깊이
|
||||
max_depth: int = 10 # 최대 재귀 깊이
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외결제일자조회[해외주식-017]
|
||||
해외결제일자조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
trad_dt (str): 기준일자(YYYYMMDD)
|
||||
ctx_area_nk (str): 공백으로 입력
|
||||
ctx_area_fk (str): 공백으로 입력
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외결제일자조회 데이터
|
||||
|
||||
Example:
|
||||
>>> df = countries_holiday("20250131", "", "")
|
||||
>>> print(df)
|
||||
"""
|
||||
# 필수 파라미터 검증
|
||||
if not trad_dt:
|
||||
logger.error("trad_dt is required. (e.g. '20250131')")
|
||||
raise ValueError("trad_dt is required. (e.g. '20250131')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "CTOS5011R"
|
||||
|
||||
params = {
|
||||
"TRAD_DT": trad_dt,
|
||||
"CTX_AREA_NK": NK,
|
||||
"CTX_AREA_FK": FK,
|
||||
}
|
||||
|
||||
# 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
|
||||
NK = res.getBody().ctx_area_nk
|
||||
FK = res.getBody().ctx_area_fk
|
||||
|
||||
if tr_cont == "M":
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return countries_holiday(
|
||||
trad_dt,
|
||||
NK,
|
||||
FK,
|
||||
"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()
|
||||
@@ -0,0 +1,140 @@
|
||||
"""
|
||||
Created on 2025-06-26
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from dailyprice import dailyprice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 기간별시세[v1_해외주식-010]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'rsym': '실시간조회종목코드',
|
||||
'zdiv': '소수점자리수',
|
||||
'nrec': '전일종가',
|
||||
'xymd': '일자(YYYYMMDD)',
|
||||
'clos': '종가',
|
||||
'sign': '대비기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'open': '시가',
|
||||
'high': '고가',
|
||||
'low': '저가',
|
||||
'tvol': '거래량',
|
||||
'tamt': '거래대금',
|
||||
'pbid': '매수호가',
|
||||
'vbid': '매수호가잔량',
|
||||
'pask': '매도호가',
|
||||
'vask': '매도호가잔량'
|
||||
}
|
||||
NUMERIC_COLUMNS = ['대비', '등락율', '시가', '고가', '저가', '거래량', '거래대금', '매수호가', '매도호가', '매수호가잔량', '매도호가잔량']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 기간별시세[v1_해외주식-010]
|
||||
|
||||
해외주식 기간별시세 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- auth (str): 사용자권한정보 ("" (Null 값 설정))
|
||||
- excd (str): 거래소코드 (HKS : 홍콩 NYS : 뉴욕 NAS : 나스닥 AMS : 아멕스 TSE : 도쿄 SHS : 상해 SZS : 심천 SHI : 상해지수 SZI : 심천지수 HSX : 호치민 HNX : 하노이)
|
||||
- symb (str): 종목코드 (종목코드 (ex. TSLA))
|
||||
- gubn (str): 일/주/월구분 (0 : 일 1 : 주 2 : 월)
|
||||
- bymd (str): 조회기준일자 (조회기준일자(YYYYMMDD) ※ 공란 설정 시, 기준일 오늘 날짜로 설정)
|
||||
- modp (str): 수정주가반영여부 (0 : 미반영 1 : 반영)
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 기간별시세 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = dailyprice(auth="", excd="NAS", symb="TSLA", gubn="0", bymd="", modp="1", env_dv="real") # 실전투자
|
||||
>>> df1, df2 = dailyprice(auth="", excd="NAS", symb="TSLA", gubn="0", bymd="", modp="1", env_dv="demo") # 모의투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외주식 기간별시세 (%s)", "실전투자" if env_dv == "real" else "모의투자")
|
||||
result1, result2 = dailyprice(
|
||||
auth="",
|
||||
excd="NAS",
|
||||
symb="TSLA",
|
||||
gubn="2",
|
||||
bymd="",
|
||||
modp="1",
|
||||
env_dv=env_dv,
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
results = [result1, result2]
|
||||
if all(result is None or result.empty for result in results):
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
|
||||
# output1 결과 처리
|
||||
logger.info("=== output1 조회 ===")
|
||||
if not result1.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
logger.info("output1 결과:")
|
||||
print(result1)
|
||||
else:
|
||||
logger.info("output1 데이터가 없습니다.")
|
||||
|
||||
# output2 결과 처리
|
||||
logger.info("=== output2 조회 ===")
|
||||
if not result2.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
logger.info("output2 결과:")
|
||||
print(result2)
|
||||
else:
|
||||
logger.info("output2 데이터가 없습니다.")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
177
한국투자증권(API)/examples_llm/overseas_stock/dailyprice/dailyprice.py
Normal file
177
한국투자증권(API)/examples_llm/overseas_stock/dailyprice/dailyprice.py
Normal file
@@ -0,0 +1,177 @@
|
||||
"""
|
||||
Created on 2025-06-26
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 기간별시세[v1_해외주식-010]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/dailyprice"
|
||||
|
||||
def dailyprice(
|
||||
auth: str, # 사용자권한정보
|
||||
excd: str, # 거래소코드
|
||||
symb: str, # 종목코드
|
||||
gubn: str, # 일/주/월구분
|
||||
bymd: str, # 조회기준일자
|
||||
modp: str, # 수정주가반영여부
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 기간별시세[v1_해외주식-010]
|
||||
해외주식 기간별시세 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
auth (str): 사용자권한정보 (예: "")
|
||||
excd (str): 거래소코드 (예: "NAS")
|
||||
symb (str): 종목코드 (예: "TSLA")
|
||||
gubn (str): 일/주/월구분 (예: "0")
|
||||
bymd (str): 조회기준일자(YYYYMMDD) (예: "20230101")
|
||||
modp (str): 수정주가반영여부 (예: "0")
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
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 = dailyprice("auth_token", "NAS", "TSLA", "0", "20230101", "0", "")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# 로깅 설정
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if not excd:
|
||||
logger.error("excd is required. (e.g. 'NAS')")
|
||||
raise ValueError("excd is required. (e.g. 'NAS')")
|
||||
if not symb:
|
||||
logger.error("symb is required. (e.g. 'TSLA')")
|
||||
raise ValueError("symb is required. (e.g. 'TSLA')")
|
||||
if not gubn:
|
||||
logger.error("gubn is required. (e.g. '0')")
|
||||
raise ValueError("gubn is required. (e.g. '0')")
|
||||
if not modp:
|
||||
logger.error("modp is required. (e.g. '0')")
|
||||
raise ValueError("modp is required. (e.g. '0')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 설정 (모의투자 지원 로직)
|
||||
if env_dv == "real" or env_dv == "demo":
|
||||
tr_id = "HHDFS76240000" # 실전/모의투자 공통 TR ID
|
||||
else:
|
||||
logger.error("env_dv can only be 'real' or 'demo'")
|
||||
raise ValueError("env_dv can only be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"AUTH": auth,
|
||||
"EXCD": excd,
|
||||
"SYMB": symb,
|
||||
"GUBN": gubn,
|
||||
"BYMD": bymd,
|
||||
"MODP": modp,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return dailyprice(
|
||||
auth,
|
||||
excd,
|
||||
symb,
|
||||
gubn,
|
||||
bymd,
|
||||
modp,
|
||||
env_dv,
|
||||
dataframe1,
|
||||
dataframe2,
|
||||
"N",
|
||||
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()
|
||||
@@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from daytime_order import daytime_order
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 미국주간주문 [v1_해외주식-026]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'KRX_FWDG_ORD_ORGNO': '한국거래소전송주문조직번호',
|
||||
'ODNO': '주문번호',
|
||||
'ORD_TMD': '주문시각'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 미국주간주문[v1_해외주식-026]
|
||||
|
||||
해외주식 미국주간주문 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (NASD:나스닥 / NYSE:뉴욕 / AMEX:아멕스)
|
||||
- pdno (str): 상품번호 (종목코드)
|
||||
- ord_qty (str): 주문수량 (해외거래소 별 최소 주문수량 및 주문단위 확인 필요)
|
||||
- ovrs_ord_unpr (str): 해외주문단가 (소수점 포함, 1주당 가격 * 시장가의 경우 1주당 가격을 공란으로 비우지 않음 "0"으로 입력)
|
||||
- ctac_tlno (str): 연락전화번호 (" ")
|
||||
- mgco_aptm_odno (str): 운용사지정주문번호 (" ")
|
||||
- ord_svr_dvsn_cd (str): 주문서버구분코드 ("0")
|
||||
- ord_dvsn (str): 주문구분 ([미국 매수/매도 주문] 00 : 지정가 * 주간거래는 지정가만 가능)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 미국주간주문 결과
|
||||
|
||||
Example:
|
||||
>>> df = daytime_order(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, order_dv="buy", ovrs_excg_cd="NASD", pdno="AAPL", ord_qty="10", ovrs_ord_unpr="150.50", ctac_tlno="", mgco_aptm_odno="", ord_svr_dvsn_cd="0", ord_dvsn="00")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = daytime_order(
|
||||
order_dv="buy", # 주문구분
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
pdno="AAPL", # 상품번호
|
||||
ord_qty="10", # 주문수량
|
||||
ovrs_ord_unpr="0.8", # 해외주문단가
|
||||
ctac_tlno="", # 연락전화번호
|
||||
mgco_aptm_odno="", # 운용사지정주문번호
|
||||
ord_svr_dvsn_cd="0", # 주문서버구분코드
|
||||
ord_dvsn="00", # 주문구분
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 미국주간주문 결과 ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,145 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 미국주간주문 [v1_해외주식-026]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/daytime-order"
|
||||
|
||||
def daytime_order(
|
||||
order_dv: str, # 주문구분 buy(매수) / sell(매도)
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
pdno: str, # 상품번호
|
||||
ord_qty: str, # 주문수량
|
||||
ovrs_ord_unpr: str, # 해외주문단가
|
||||
ctac_tlno: str, # 연락전화번호
|
||||
mgco_aptm_odno: str, # 운용사지정주문번호
|
||||
ord_svr_dvsn_cd: str, # 주문서버구분코드
|
||||
ord_dvsn: str, # 주문구분
|
||||
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 미국주간주문[v1_해외주식-026]
|
||||
해외주식 미국주간주문 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
order_dv (str): 주문구분 buy(매수) / sell(매도)
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_excg_cd (str): NASD:나스닥 / NYSE:뉴욕 / AMEX:아멕스
|
||||
pdno (str): 종목코드
|
||||
ord_qty (str): 해외거래소 별 최소 주문수량 및 주문단위 확인 필요
|
||||
ovrs_ord_unpr (str): 소수점 포함, 1주당 가격 * 시장가의 경우 1주당 가격을 공란으로 비우지 않음 "0"으로 입력
|
||||
ctac_tlno (str): " "
|
||||
mgco_aptm_odno (str): " "
|
||||
ord_svr_dvsn_cd (str): "0"
|
||||
ord_dvsn (str): [미국 매수/매도 주문] 00 : 지정가 * 주간거래는 지정가만 가능
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 미국주간주문 데이터
|
||||
|
||||
Example:
|
||||
>>> df = daytime_order(
|
||||
... order_dv="buy",
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_excg_cd="NASD",
|
||||
... pdno="AAPL",
|
||||
... ord_qty="10",
|
||||
... ovrs_ord_unpr="150.00",
|
||||
... ctac_tlno="01012345678",
|
||||
... mgco_aptm_odno="",
|
||||
... ord_svr_dvsn_cd="0",
|
||||
... ord_dvsn="00"
|
||||
... )
|
||||
>>> 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 not ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
if not pdno:
|
||||
logger.error("pdno is required. (e.g. 'AAPL')")
|
||||
raise ValueError("pdno is required. (e.g. 'AAPL')")
|
||||
if not ord_qty:
|
||||
logger.error("ord_qty is required. (e.g. '10')")
|
||||
raise ValueError("ord_qty is required. (e.g. '10')")
|
||||
if not ovrs_ord_unpr:
|
||||
logger.error("ovrs_ord_unpr is required. (e.g. '150.00')")
|
||||
raise ValueError("ovrs_ord_unpr is required. (e.g. '150.00')")
|
||||
if not ord_svr_dvsn_cd:
|
||||
logger.error("ord_svr_dvsn_cd is required. (e.g. '0')")
|
||||
raise ValueError("ord_svr_dvsn_cd is required. (e.g. '0')")
|
||||
if not ord_dvsn:
|
||||
logger.error("ord_dvsn is required. (e.g. '00')")
|
||||
raise ValueError("ord_dvsn is required. (e.g. '00')")
|
||||
|
||||
if order_dv == "buy":
|
||||
tr_id = "TTTS6036U"
|
||||
elif order_dv == "sell":
|
||||
tr_id = "TTTS6037U"
|
||||
else:
|
||||
logger.error("Invalid order_dv. (e.g. 'buy' or 'sell')")
|
||||
raise ValueError("Invalid order_dv. (e.g. 'buy' or 'sell')")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"PDNO": pdno,
|
||||
"ORD_QTY": ord_qty,
|
||||
"OVRS_ORD_UNPR": ovrs_ord_unpr,
|
||||
"CTAC_TLNO": ctac_tlno,
|
||||
"MGCO_APTM_ODNO": mgco_aptm_odno,
|
||||
"ORD_SVR_DVSN_CD": ord_svr_dvsn_cd,
|
||||
"ORD_DVSN": ord_dvsn,
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from daytime_order_rvsecncl import daytime_order_rvsecncl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 미국주간정정취소 [v1_해외주식-027]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'Output1': '응답상세',
|
||||
'KRX_FWDG_ORD_ORGNO': '한국거래소전송주문조직번호',
|
||||
'ODNO': '주문번호',
|
||||
'ORD_TMD': '주문시각'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 미국주간정정취소[v1_해외주식-027]
|
||||
|
||||
해외주식 미국주간정정취소 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (NASD:나스닥 / NYSE:뉴욕 / AMEX:아멕스)
|
||||
- pdno (str): 상품번호 (종목코드)
|
||||
- orgn_odno (str): 원주문번호 ('정정 또는 취소할 원주문번호(매매 TR의 주문번호) - 해외주식 주문체결내역api (/uapi/overseas-stock/v1/trading/inquire-nccs)에서 odno(주문번호) 참조')
|
||||
- rvse_cncl_dvsn_cd (str): 정정취소구분코드 ('01 : 정정 02 : 취소')
|
||||
- ord_qty (str): 주문수량 ()
|
||||
- ovrs_ord_unpr (str): 해외주문단가 (소수점 포함, 1주당 가격)
|
||||
- ctac_tlno (str): 연락전화번호 (" ")
|
||||
- mgco_aptm_odno (str): 운용사지정주문번호 (" ")
|
||||
- ord_svr_dvsn_cd (str): 주문서버구분코드 ("0")
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 미국주간정정취소 결과
|
||||
|
||||
Example:
|
||||
>>> df = daytime_order_rvsecncl(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_excg_cd="NASD", pdno="AAPL", orgn_odno="1234567890", rvse_cncl_dvsn_cd="01", ord_qty="10", ovrs_ord_unpr="150.25", ctac_tlno="", mgco_aptm_odno="", ord_svr_dvsn_cd="0")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = daytime_order_rvsecncl(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
pdno="AMZN", # 상품번호
|
||||
orgn_odno="0000034439", # 원주문번호
|
||||
rvse_cncl_dvsn_cd="02", # 정정취소구분코드
|
||||
ord_qty="111", # 주문수량
|
||||
ovrs_ord_unpr="0", # 해외주문단가
|
||||
ctac_tlno="", # 연락전화번호
|
||||
mgco_aptm_odno="", # 운용사지정주문번호
|
||||
ord_svr_dvsn_cd="0" # 주문서버구분코드
|
||||
)
|
||||
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 미국주간정정취소 결과 ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,144 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 미국주간정정취소 [v1_해외주식-027]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/daytime-order-rvsecncl"
|
||||
|
||||
def daytime_order_rvsecncl(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
pdno: str, # 상품번호
|
||||
orgn_odno: str, # 원주문번호
|
||||
rvse_cncl_dvsn_cd: str, # 정정취소구분코드
|
||||
ord_qty: str, # 주문수량
|
||||
ovrs_ord_unpr: str, # 해외주문단가
|
||||
ctac_tlno: str, # 연락전화번호
|
||||
mgco_aptm_odno: str, # 운용사지정주문번호
|
||||
ord_svr_dvsn_cd: str, # 주문서버구분코드
|
||||
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 미국주간정정취소[v1_해외주식-027]
|
||||
해외주식 미국주간정정취소 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_excg_cd (str): NASD:나스닥 / NYSE:뉴욕 / AMEX:아멕스
|
||||
pdno (str): 종목코드
|
||||
orgn_odno (str): 정정 또는 취소할 원주문번호
|
||||
rvse_cncl_dvsn_cd (str): 01 : 정정, 02 : 취소
|
||||
ord_qty (str): 주문수량
|
||||
ovrs_ord_unpr (str): 소수점 포함, 1주당 가격
|
||||
ctac_tlno (str): 연락전화번호
|
||||
mgco_aptm_odno (str): 운용사지정주문번호
|
||||
ord_svr_dvsn_cd (str): 주문서버구분코드
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 미국주간정정취소 데이터
|
||||
|
||||
Example:
|
||||
>>> df = daytime_order_rvsecncl(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_excg_cd="NASD",
|
||||
... pdno="AAPL",
|
||||
... orgn_odno="1234567890",
|
||||
... rvse_cncl_dvsn_cd="01",
|
||||
... ord_qty="100",
|
||||
... ovrs_ord_unpr="150.00",
|
||||
... ctac_tlno="01012345678",
|
||||
... mgco_aptm_odno="000000000001",
|
||||
... ord_svr_dvsn_cd="0"
|
||||
... )
|
||||
>>> 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 not ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
if not pdno:
|
||||
logger.error("pdno is required. (e.g. 'AAPL')")
|
||||
raise ValueError("pdno is required. (e.g. 'AAPL')")
|
||||
if not orgn_odno:
|
||||
logger.error("orgn_odno is required. (e.g. '1234567890')")
|
||||
raise ValueError("orgn_odno is required. (e.g. '1234567890')")
|
||||
if rvse_cncl_dvsn_cd not in ["01", "02"]:
|
||||
logger.error("rvse_cncl_dvsn_cd is required. (e.g. '01' or '02')")
|
||||
raise ValueError("rvse_cncl_dvsn_cd is required. (e.g. '01' or '02')")
|
||||
if not ord_qty:
|
||||
logger.error("ord_qty is required. (e.g. '100')")
|
||||
raise ValueError("ord_qty is required. (e.g. '100')")
|
||||
if not ovrs_ord_unpr:
|
||||
logger.error("ovrs_ord_unpr is required. (e.g. '150.00')")
|
||||
raise ValueError("ovrs_ord_unpr is required. (e.g. '150.00')")
|
||||
if not ord_svr_dvsn_cd:
|
||||
logger.error("ord_svr_dvsn_cd is required. (e.g. '0')")
|
||||
raise ValueError("ord_svr_dvsn_cd is required. (e.g. '0')")
|
||||
|
||||
tr_id = "TTTS6038U"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"PDNO": pdno,
|
||||
"ORGN_ODNO": orgn_odno,
|
||||
"RVSE_CNCL_DVSN_CD": rvse_cncl_dvsn_cd,
|
||||
"ORD_QTY": ord_qty,
|
||||
"OVRS_ORD_UNPR": ovrs_ord_unpr,
|
||||
"CTAC_TLNO": ctac_tlno,
|
||||
"MGCO_APTM_ODNO": mgco_aptm_odno,
|
||||
"ORD_SVR_DVSN_CD": ord_svr_dvsn_cd,
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from delayed_asking_price_asia import delayed_asking_price_asia
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 실시간시세 > 해외주식 지연호가(아시아)[실시간-008]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
"symb": "종목코드",
|
||||
"zdiv": "소숫점자리수",
|
||||
"xymd": "현지일자",
|
||||
"xhms": "현지시간",
|
||||
"kymd": "한국일자",
|
||||
"khms": "한국시간",
|
||||
"bvol": "매수총잔량",
|
||||
"avol": "매도총잔량",
|
||||
"bdvl": "매수총잔량대비",
|
||||
"advl": "매도총잔량대비",
|
||||
"pbid1": "매수호가1",
|
||||
"pask1": "매도호가1",
|
||||
"vbid1": "매수잔량1",
|
||||
"vask1": "매도잔량1",
|
||||
"dbid1": "매수잔량대비1",
|
||||
"dask1": "매도잔량대비1"
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = ["매수호가1", "매도호가1", "매수잔량1", "매도잔량1"]
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 지연호가(아시아) 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 지연호가(아시아) API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 DHKS00003을 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
ka.auth_ws()
|
||||
|
||||
# 인증(auth_ws()) 이후에 선언
|
||||
kws = ka.KISWebSocket(api_url="/tryitout")
|
||||
|
||||
# 조회
|
||||
kws.subscribe(request=delayed_asking_price_asia, data=["DHKS00003"])
|
||||
|
||||
# 결과 표시
|
||||
def on_result(ws, tr_id: str, result: pd.DataFrame, data_map: dict):
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
kws.start(on_result=on_result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 실시간시세 > 해외주식 지연호가(아시아)[실시간-008]
|
||||
##############################################################################################
|
||||
|
||||
def delayed_asking_price_asia(
|
||||
tr_type: str,
|
||||
tr_key: str,
|
||||
) -> tuple[dict, list[str]]:
|
||||
"""
|
||||
해외주식 지연호가(아시아)의 경우 아시아 무료시세(지연호가)가 제공됩니다.
|
||||
|
||||
HTS(efriend Plus) [7781] 시세신청(실시간) 화면에서 유료 서비스 신청 시,
|
||||
"해외주식 실시간호가 HDFSASP0" 을 이용하여 아시아국가 유료시세(실시간호가)를 받아보실 수 있습니다. (24.11.29 반영)
|
||||
|
||||
※ 지연시세 지연시간 : 홍콩, 베트남, 중국, 일본 - 15분지연
|
||||
|
||||
Args:
|
||||
tr_type (str): [필수] 등록/해제
|
||||
tr_key (str): [필수] 종목코드
|
||||
|
||||
Returns:
|
||||
message (dict): 메시지 데이터
|
||||
columns (list[str]): 컬럼 정보
|
||||
|
||||
Example:
|
||||
>>> msg, columns = delayed_asking_price_asia("1", "DHKS00003")
|
||||
>>> print(msg, columns)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if tr_type == "":
|
||||
raise ValueError("tr_type is required")
|
||||
|
||||
if tr_key == "":
|
||||
raise ValueError("tr_key is required")
|
||||
|
||||
tr_id = "HDFSASP1"
|
||||
|
||||
params = {
|
||||
"tr_key": tr_key,
|
||||
}
|
||||
|
||||
msg = ka.data_fetch(tr_id, tr_type, params)
|
||||
|
||||
columns = [
|
||||
"symb",
|
||||
"zdiv",
|
||||
"xymd",
|
||||
"xhms",
|
||||
"kymd",
|
||||
"khms",
|
||||
"bvol",
|
||||
"avol",
|
||||
"bdvl",
|
||||
"advl",
|
||||
"pbid1",
|
||||
"pask1",
|
||||
"vbid1",
|
||||
"vask1",
|
||||
"dbid1",
|
||||
"dask1"
|
||||
]
|
||||
|
||||
return msg, columns
|
||||
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from delayed_ccnl import delayed_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 실시간시세 > 해외주식 실시간지연체결가[실시간-007]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
"SYMB": "종목코드",
|
||||
"ZDIV": "수수점자리수",
|
||||
"TYMD": "현지영업일자",
|
||||
"XYMD": "현지일자",
|
||||
"XHMS": "현지시간",
|
||||
"KYMD": "한국일자",
|
||||
"KHMS": "한국시간",
|
||||
"OPEN": "시가",
|
||||
"HIGH": "고가",
|
||||
"LOW": "저가",
|
||||
"LAST": "현재가",
|
||||
"SIGN": "대비구분",
|
||||
"DIFF": "전일대비",
|
||||
"RATE": "등락율",
|
||||
"PBID": "매수호가",
|
||||
"PASK": "매도호가",
|
||||
"VBID": "매수잔량",
|
||||
"VASK": "매도잔량",
|
||||
"EVOL": "체결량",
|
||||
"TVOL": "거래량",
|
||||
"TAMT": "거래대금",
|
||||
"BIVL": "매도체결량",
|
||||
"ASVL": "매수체결량",
|
||||
"STRN": "체결강도",
|
||||
"MTYP": "시장구분"
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = ["시가", "고가", "저가", "현재가", "전일대비", "등락율", "매수호가", "매도호가", "매수잔량", "매도잔량"]
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 실시간지연체결가
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
ka.auth_ws()
|
||||
|
||||
# 인증(auth_ws()) 이후에 선언
|
||||
kws = ka.KISWebSocket(api_url="/tryitout")
|
||||
|
||||
# 조회
|
||||
kws.subscribe(request=delayed_ccnl, data=["DHKS00003"])
|
||||
|
||||
# 결과 표시
|
||||
def on_result(ws, tr_id: str, result: pd.DataFrame, data_map: dict):
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
kws.start(on_result=on_result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 실시간시세 > 해외주식 실시간지연체결가[실시간-007]
|
||||
##############################################################################################
|
||||
|
||||
def delayed_ccnl(
|
||||
tr_type: str,
|
||||
tr_key: str,
|
||||
) -> tuple[dict, list[str]]:
|
||||
"""
|
||||
해외주식 실시간지연체결가의 경우 기본적으로 무료시세(지연체결가)가 제공되며,
|
||||
HTS(efriend Plus) [7781] 시세신청(실시간) 화면에서 유료 서비스 신청 시 API로도 유료시세(실시간체결가)를 받아보실 수 있습니다. (24.11.29 반영)
|
||||
|
||||
※ 지연시세 지연시간 : 미국 - 실시간무료(0분지연) / 홍콩, 베트남, 중국, 일본 - 15분지연 (중국은 실시간시세 신청 시 무료실시간시세 제공)
|
||||
미국의 경우 0분지연시세로 제공되나, 장중 당일 시가는 상이할 수 있으며, 익일 정정 표시됩니다.
|
||||
|
||||
해당 API로 미국주간거래(10:00~16:00) 시세 조회도 가능합니다.
|
||||
※ 미국주간거래 실시간 조회 시, 맨 앞자리(R), tr_key 중 시장구분 값을 다음과 같이 입력 → 나스닥: BAQ, 뉴욕: BAY, 아멕스: BAA
|
||||
|
||||
Args:
|
||||
tr_type (str): [필수] 등록/해제
|
||||
tr_key (str): [필수] 종목코드
|
||||
|
||||
Returns:
|
||||
message (dict): 메시지 데이터
|
||||
columns (list[str]): 컬럼 정보
|
||||
|
||||
Example:
|
||||
>>> msg, columns = delayed_ccnl("1", "DNASAAPL")
|
||||
>>> print(msg, columns)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if tr_type == "":
|
||||
raise ValueError("tr_type is required")
|
||||
|
||||
if tr_key == "":
|
||||
raise ValueError("tr_key is required")
|
||||
|
||||
tr_id = "HDFSCNT0"
|
||||
|
||||
params = {
|
||||
"tr_key": tr_key,
|
||||
}
|
||||
|
||||
msg = ka.data_fetch(tr_id, tr_type, params)
|
||||
|
||||
columns = [
|
||||
"SYMB",
|
||||
"ZDIV",
|
||||
"TYMD",
|
||||
"XYMD",
|
||||
"XHMS",
|
||||
"KYMD",
|
||||
"KHMS",
|
||||
"OPEN",
|
||||
"HIGH",
|
||||
"LOW",
|
||||
"LAST",
|
||||
"SIGN",
|
||||
"DIFF",
|
||||
"RATE",
|
||||
"PBID",
|
||||
"PASK",
|
||||
"VBID",
|
||||
"VASK",
|
||||
"EVOL",
|
||||
"TVOL",
|
||||
"TAMT",
|
||||
"BIVL",
|
||||
"ASVL",
|
||||
"STRN",
|
||||
"MTYP"
|
||||
]
|
||||
|
||||
return msg, columns
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Created on 2025-06-26
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from foreign_margin import foreign_margin
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 - 해외증거금 통화별조회 [해외주식-035]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'natn_name': '국가명',
|
||||
'frcr_dncl_amt1': '외화예수금액',
|
||||
'ustl_buy_amt': '미결제매수금액',
|
||||
'ustl_sll_amt': '미결제매도금액',
|
||||
'frcr_rcvb_amt': '외화미수금액',
|
||||
'frcr_mgn_amt': '외화증거금액',
|
||||
'frcr_gnrl_ord_psbl_amt': '외화일반주문가능금액',
|
||||
'frcr_ord_psbl_amt1': '외화주문가능금액',
|
||||
'itgr_ord_psbl_amt': '통합주문가능금액',
|
||||
'bass_exrt': '기준환율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = [
|
||||
'외화예수금액', '미결제매수금액', '미결제매도금액', '외화미수금액', '외화증거금액',
|
||||
'외화일반주문가능금액', '외화주문가능금액', '통합주문가능금액', '기준환율'
|
||||
]
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외증거금 통화별조회[해외주식-035]
|
||||
|
||||
해외증거금 통화별조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 ()
|
||||
- acnt_prdt_cd (str): 계좌상품코드 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외증거금 통화별조회 결과
|
||||
|
||||
Example:
|
||||
>>> df = foreign_margin(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod)
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = foreign_margin(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외증거금 통화별조회 결과 ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
Created on 2025-06-26
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 - 해외증거금 통화별조회 [해외주식-035]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/foreign-margin"
|
||||
|
||||
def foreign_margin(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
tr_cont: str = "", # 연속 거래 여부
|
||||
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
||||
depth: int = 0, # 현재 재귀 깊이
|
||||
max_depth: int = 10 # 최대 재귀 깊이 (기본값: 10)
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외증거금 통화별조회[해외주식-035]
|
||||
해외증거금 통화별조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 종합계좌번호 (필수)
|
||||
acnt_prdt_cd (str): 계좌상품코드 (필수)
|
||||
tr_cont (str): 연속 거래 여부 (기본값: "")
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외증거금 통화별조회 데이터
|
||||
|
||||
Example:
|
||||
>>> df = foreign_margin("12345678", "01")
|
||||
>>> 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 = "TTTC2101R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
}
|
||||
|
||||
# API 호출
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=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 in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return foreign_margin(
|
||||
cano,
|
||||
acnt_prdt_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()
|
||||
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from industry_price import industry_price
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 업종별코드조회[해외주식-049]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'nrec': 'RecordCount',
|
||||
'icod': '업종코드',
|
||||
'name': '업종명'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 업종별코드조회 테스트 함수
|
||||
이 함수는 해외주식 업종별코드조회 API를 호출하여 결과를 출력합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# case1: EXCD=NAS
|
||||
logging.info("=== case1: EXCD=NAS ===")
|
||||
try:
|
||||
result1, result2 = industry_price(excd="NAS")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s", str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("=== output1 결과 ===")
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 컬럼명 한글 변환 및 숫자형 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("=== output2 결과 ===")
|
||||
logging.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 컬럼명 한글 변환 및 숫자형 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 업종별코드조회[해외주식-049]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/industry-price"
|
||||
|
||||
def industry_price(
|
||||
excd: str, # [필수] 거래소명
|
||||
auth: str = "", # 사용자권한정보
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
해외주식 업종별코드조회 API입니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
auth (str): 사용자권한정보
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1, output2) 데이터
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = industry_price(excd="NAS")
|
||||
>>> print(df1, df2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS', 'NAS', 'AMS', 'HKS', 'SHS', 'SZS', 'HSX', 'HNX', 'TSE')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76370100"
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"AUTH": auth
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리 (object 타입)
|
||||
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 처리 (array 타입)
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return industry_price(
|
||||
excd, auth, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from industry_theme import industry_theme
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 업종별시세[해외주식-048]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 한글 변환
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'crec': '현재조회종목수',
|
||||
'trec': '전체조회종목수',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'vask': '매도잔량',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'vbid': '매수잔량',
|
||||
'seqn': '순위',
|
||||
'ename': '영문종목명',
|
||||
'e_ordyn': '매매가능'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 업종별시세 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 업종별시세 API를 호출하여 결과를 출력합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# case1 조회
|
||||
logger.info("=== case1 조회 ===")
|
||||
try:
|
||||
result1, result2 = industry_theme(excd="NAS", icod="010", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logger.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logger.info("output1 사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# output1 컬럼명 한글 변환 및 데이터 출력
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# output1 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("output1 결과:")
|
||||
print(result1)
|
||||
|
||||
logger.info("output2 사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# output2 컬럼명 한글 변환 및 데이터 출력
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# output2 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("output2 결과:")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 업종별시세[해외주식-048]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/industry-theme"
|
||||
|
||||
def industry_theme(
|
||||
excd: str, # [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
icod: str, # [필수] 업종코드
|
||||
vol_rang: str, # [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth: str = "", # 사용자권한정보
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
해외주식 업종별시세 API입니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
icod (str): [필수] 업종코드
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth (str): 사용자권한정보
|
||||
keyb (str): NEXT KEY BUFF
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1 데이터, output2 데이터)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = industry_theme(excd="NAS", icod="010", vol_rang="0")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NAS')")
|
||||
|
||||
if icod == "":
|
||||
raise ValueError("icod is required")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76370000"
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"ICOD": icod,
|
||||
"VOL_RANG": vol_rang,
|
||||
"AUTH": auth,
|
||||
"KEYB": keyb
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 데이터 처리
|
||||
current_data1 = pd.DataFrame(res.getBody().output1, index=[0])
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return industry_theme(
|
||||
excd, icod, vol_rang, auth, keyb, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from inquire_algo_ccnl import inquire_algo_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 지정가체결내역조회 [해외주식-070]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'CCLD_SEQ': '체결순번',
|
||||
'CCLD_BTWN': '체결시간',
|
||||
'PDNO': '상품번호',
|
||||
'ITEM_NAME': '종목명',
|
||||
'FT_CCLD_QTY': 'FT체결수량',
|
||||
'FT_CCLD_UNPR3': 'FT체결단가',
|
||||
'FT_CCLD_AMT3': 'FT체결금액',
|
||||
'ODNO': '주문번호',
|
||||
'TRAD_DVSN_NAME': '매매구분명',
|
||||
'FT_ORD_QTY': 'FT주문수량',
|
||||
'FT_ORD_UNPR3': 'FT주문단가',
|
||||
'ORD_TMD': '주문시각',
|
||||
'SPLT_BUY_ATTR_NAME': '분할매수속성명',
|
||||
'TR_CRCY': '거래통화',
|
||||
'CCLD_CNT': '체결건수'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 지정가체결내역조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 지정가체결내역조회 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 case1의 파라미터를 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result, result3 = inquire_algo_ccnl(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod,)
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output 결과 처리
|
||||
logging.info("=== output 결과 ===")
|
||||
logging.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
# output3 결과 처리
|
||||
logging.info("=== output3 결과 ===")
|
||||
logging.info("사용 가능한 컬럼: %s", result3.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result3 = result3.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result3.columns:
|
||||
result3[col] = pd.to_numeric(result3[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output3):")
|
||||
print(result3)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 지정가체결내역조회 [해외주식-070]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-algo-ccnl"
|
||||
|
||||
def inquire_algo_ccnl(
|
||||
cano: str, # [필수] 계좌번호
|
||||
acnt_prdt_cd: str, # [필수] 계좌상품코드 (ex. 01)
|
||||
ord_dt: str = "", # 주문일자
|
||||
ord_gno_brno: str = "", # 주문채번지점번호
|
||||
odno: str = "", # 주문번호 (ex. 지정가주문번호 TTTC6058R에서 조회된 주문번호 입력)
|
||||
ttlz_icld_yn: str = "", # 집계포함여부
|
||||
NK200: str = "", # 연속조회키200
|
||||
FK200: str = "", # 연속조회조건200
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
||||
dataframe3: Optional[pd.DataFrame] = None, # 누적 데이터프레임3
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
해외주식 TWAP, VWAP 주문에 대한 체결내역 조회 API로 지정가 주문번호조회 API를 수행 후 조회해야합니다
|
||||
|
||||
Args:
|
||||
cano (str): [필수] 계좌번호
|
||||
acnt_prdt_cd (str): [필수] 계좌상품코드 (ex. 01)
|
||||
ord_dt (str): 주문일자
|
||||
ord_gno_brno (str): 주문채번지점번호
|
||||
odno (str): 주문번호 (ex. 지정가주문번호 TTTC6058R에서 조회된 주문번호 입력)
|
||||
ttlz_icld_yn (str): 집계포함여부
|
||||
NK200 (str): 연속조회키200
|
||||
FK200 (str): 연속조회조건200
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
dataframe3 (Optional[pd.DataFrame]): 누적 데이터프레임3
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output, output3) 체결내역 데이터
|
||||
|
||||
Example:
|
||||
>>> result, result3 = inquire_algo_ccnl(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod)
|
||||
>>> print(result)
|
||||
>>> print(result3)
|
||||
"""
|
||||
|
||||
if cano == "":
|
||||
raise ValueError("cano is required")
|
||||
|
||||
if acnt_prdt_cd == "":
|
||||
raise ValueError("acnt_prdt_cd is required")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe is None:
|
||||
dataframe = pd.DataFrame()
|
||||
if dataframe3 is None:
|
||||
dataframe3 = pd.DataFrame()
|
||||
return dataframe, dataframe3
|
||||
|
||||
tr_id = "TTTS6059R" # 해외주식 지정가체결내역조회
|
||||
|
||||
params = {
|
||||
"CANO": cano, # 계좌번호
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd, # 계좌상품코드
|
||||
"ORD_DT": ord_dt, # 주문일자
|
||||
"ORD_GNO_BRNO": ord_gno_brno, # 주문채번지점번호
|
||||
"ODNO": odno, # 주문번호
|
||||
"TTLZ_ICLD_YN": ttlz_icld_yn, # 집계포함여부
|
||||
"CTX_AREA_NK200": NK200, # 연속조회키200
|
||||
"CTX_AREA_FK200": FK200 # 연속조회조건200
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output)
|
||||
current_data3 = pd.DataFrame(res.getBody().output3)
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
if dataframe3 is not None:
|
||||
dataframe3 = pd.concat([dataframe3, current_data3], ignore_index=True)
|
||||
else:
|
||||
dataframe3 = current_data3
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
NK200 = res.getBody().ctx_area_nk200
|
||||
FK200 = res.getBody().ctx_area_fk200
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return inquire_algo_ccnl(
|
||||
cano, acnt_prdt_cd, ord_dt, ord_gno_brno, odno, ttlz_icld_yn,
|
||||
NK200, FK200, "N", dataframe, dataframe3, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe, dataframe3
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-03
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_asking_price import inquire_asking_price
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 현재가 1호가[해외주식-033]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'rsym': '실시간조회종목코드',
|
||||
'zdiv': '소수점자리수',
|
||||
'curr': '통화',
|
||||
'base': '전일종가',
|
||||
'open': '시가',
|
||||
'high': '고가',
|
||||
'low': '저가',
|
||||
'last': '현재가',
|
||||
'dymd': '호가일자',
|
||||
'dhms': '호가시간',
|
||||
'bvol': '매수호가총잔량',
|
||||
'avol': '매도호가총잔량',
|
||||
'bdvl': '매수호가총잔량대비',
|
||||
'advl': '매도호가총잔량대비',
|
||||
'code': '종목코드',
|
||||
'ropen': '시가율',
|
||||
'rhigh': '고가율',
|
||||
'rlow': '저가율',
|
||||
'rclose': '현재가율',
|
||||
'pbid1': '매수호가가격1',
|
||||
'pask1': '매도호가가격1',
|
||||
'vbid1': '매수호가잔량1',
|
||||
'vask1': '매도호가잔량1',
|
||||
'dbid1': '매수호가대비1',
|
||||
'dask1': '매도호가대비1',
|
||||
'vstm': 'VCMStart시간',
|
||||
'vetm': 'VCMEnd시간',
|
||||
'csbp': 'CAS/VCM기준가',
|
||||
'cshi': 'CAS/VCMHighprice',
|
||||
'cslo': 'CAS/VCMLowprice',
|
||||
'iep': 'IEP',
|
||||
'iev': 'IEV'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['소수점자리수', '시가율', '고가율', '저가율', '현재가율', '매수호가가격1', '매도호가가격1', '매수호가잔량1',
|
||||
'매도호가잔량1', '매수호가대비1', '매도호가대비1', 'CAS/VCM기준가', 'CAS/VCMHighprice', 'CAS/VCMLowprice', 'IEP', 'IEV']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 현재가 1호가[해외주식-033]
|
||||
|
||||
해외주식 현재가 1호가 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- auth (str): 사용자권한정보 (공백)
|
||||
- excd (str): 거래소코드 (NYS : 뉴욕 NAS : 나스닥 AMS : 아멕스 HKS : 홍콩 SHS : 상해 SZS : 심천 HSX : 호치민 HNX : 하노이 TSE : 도쿄 BAY : 뉴욕(주간) BAQ : 나스닥(주간) BAA : 아멕스(주간))
|
||||
- symb (str): 종목코드 (종목코드 예)TSLA)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 현재가 1호가 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2, df3 = inquire_asking_price(auth="", excd="NAS", symb="TSLA")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외주식 현재가 1호가")
|
||||
result1, result2, result3 = inquire_asking_price(
|
||||
auth="", # 사용자권한정보
|
||||
excd="NAS", # 거래소코드
|
||||
symb="TSLA", # 종목코드
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
results = [result1, result2, result3]
|
||||
if all(result is None or result.empty for result in results):
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
if result1 is not None and not result1.empty:
|
||||
logger.info("=== output1 결과 ===")
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result1.columns.tolist())
|
||||
|
||||
# output1 결과 처리
|
||||
logger.info("=== output1 조회 ===")
|
||||
if not result1.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("조회된 데이터 건수: %d", len(result1))
|
||||
print("=== output1 ===")
|
||||
print(result1)
|
||||
|
||||
# output2 결과 처리
|
||||
logger.info("=== output2 조회 ===")
|
||||
if result2 is not None and not result2.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("조회된 데이터 건수: %d", len(result2))
|
||||
print("=== output2 ===")
|
||||
print(result2)
|
||||
|
||||
# output3 결과 처리
|
||||
logger.info("=== output3 조회 ===")
|
||||
if result3 is not None and not result3.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result3.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환
|
||||
result3 = result3.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result3.columns:
|
||||
result3[col] = pd.to_numeric(result3[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("조회된 데이터 건수: %d", len(result3))
|
||||
print("=== output3 ===")
|
||||
print(result3)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,165 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-03
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 현재가 1호가[해외주식-033]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/inquire-asking-price"
|
||||
|
||||
def inquire_asking_price(
|
||||
auth: str, # 사용자권한정보
|
||||
excd: str, # 거래소코드
|
||||
symb: str, # 종목코드
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 현재가 1호가[해외주식-033]
|
||||
해외주식 현재가 1호가 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
auth (str): 사용자권한정보
|
||||
excd (str): 거래소코드 (예: NYS, NAS, AMS, 등)
|
||||
symb (str): 종목코드 (예: TSLA)
|
||||
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]: 해외주식 현재가 1호가 데이터
|
||||
|
||||
Example:
|
||||
>>> df1, df2, df3 = inquire_asking_price(auth="your_auth_token", excd="NAS", symb="TSLA")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
>>> print(df3)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not excd:
|
||||
logger.error("excd is required. (e.g. 'NAS')")
|
||||
raise ValueError("excd is required. (e.g. 'NAS')")
|
||||
if not symb:
|
||||
logger.error("symb is required. (e.g. 'TSLA')")
|
||||
raise ValueError("symb is required. (e.g. 'TSLA')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFS76200100"
|
||||
|
||||
params = {
|
||||
"AUTH": auth,
|
||||
"EXCD": excd,
|
||||
"SYMB": symb,
|
||||
}
|
||||
|
||||
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:
|
||||
if isinstance(output_data, list):
|
||||
current_data1 = pd.DataFrame(output_data)
|
||||
else:
|
||||
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:
|
||||
if isinstance(output_data, list):
|
||||
current_data2 = pd.DataFrame(output_data)
|
||||
else:
|
||||
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()
|
||||
|
||||
# output3 처리
|
||||
if hasattr(res.getBody(), 'output3'):
|
||||
output_data = res.getBody().output3
|
||||
if output_data:
|
||||
if isinstance(output_data, list):
|
||||
current_data3 = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data3 = pd.DataFrame([output_data])
|
||||
|
||||
if dataframe3 is not None:
|
||||
dataframe3 = pd.concat([dataframe3, current_data3], ignore_index=True)
|
||||
else:
|
||||
dataframe3 = current_data3
|
||||
else:
|
||||
if dataframe3 is None:
|
||||
dataframe3 = pd.DataFrame()
|
||||
else:
|
||||
if dataframe3 is None:
|
||||
dataframe3 = pd.DataFrame()
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_asking_price(
|
||||
auth,
|
||||
excd,
|
||||
symb,
|
||||
"N", dataframe1, dataframe2, dataframe3, 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()
|
||||
@@ -0,0 +1,134 @@
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_balance import inquire_balance
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 잔고 [v1_해외주식-006]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'prdt_type_cd': '상품유형코드',
|
||||
'ovrs_pdno': '해외상품번호',
|
||||
'frcr_evlu_pfls_amt': '외화평가손익금액',
|
||||
'evlu_pfls_rt': '평가손익율',
|
||||
'pchs_avg_pric': '매입평균가격',
|
||||
'ovrs_cblc_qty': '해외잔고수량',
|
||||
'ord_psbl_qty': '주문가능수량',
|
||||
'frcr_pchs_amt1': '외화매입금액1',
|
||||
'ovrs_stck_evlu_amt': '해외주식평가금액',
|
||||
'now_pric2': '현재가격2',
|
||||
'tr_crcy_cd': '거래통화코드',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'loan_type_cd': '대출유형코드',
|
||||
'loan_dt': '대출일자',
|
||||
'expd_dt': '만기일자',
|
||||
'frcr_pchs_amt1': '외화매입금액1',
|
||||
'ovrs_rlzt_pfls_amt': '해외실현손익금액',
|
||||
'ovrs_tot_pfls': '해외총손익',
|
||||
'rlzt_erng_rt': '실현수익율',
|
||||
'tot_evlu_pfls_amt': '총평가손익금액',
|
||||
'tot_pftrt': '총수익률',
|
||||
'frcr_buy_amt_smtl1': '외화매수금액합계1',
|
||||
'ovrs_rlzt_pfls_amt2': '해외실현손익금액2',
|
||||
'frcr_buy_amt_smtl2': '외화매수금액합계2'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 잔고[해외주식-006]
|
||||
|
||||
해외주식 잔고 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 ()
|
||||
- acnt_prdt_cd (str): 계좌상품코드 ()
|
||||
- ovrs_excg_cd (str): 해외거래소코드 ()
|
||||
- tr_crcy_cd (str): 거래통화코드 ()
|
||||
- FK200 (str): 연속조회검색조건200 ()
|
||||
- NK200 (str): 연속조회키200 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 잔고 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_balance(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_excg_cd="NASD", tr_crcy_cd="USD")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2 = inquire_balance(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
tr_crcy_cd="USD", # 거래통화코드
|
||||
FK200="", # 연속조회검색조건200
|
||||
NK200="", # 연속조회키200
|
||||
)
|
||||
|
||||
# output1 결과 처리
|
||||
logging.info("=== output1 결과 ===")
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result1)
|
||||
|
||||
# output3 결과 처리
|
||||
logging.info("=== output2 결과 ===")
|
||||
logging.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,181 @@
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 잔고 [v1_해외주식-006]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-balance"
|
||||
|
||||
def inquire_balance(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
tr_crcy_cd: str, # 거래통화코드
|
||||
FK200: str = "", # 연속조회검색조건200
|
||||
NK200: str = "", # 연속조회키200
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 잔고[v1_해외주식-006]
|
||||
해외주식 잔고 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_excg_cd (str): [모의] NASD : 나스닥 NYSE : 뉴욕 AMEX : 아멕스 [실전] NASD : 미국전체 NAS : 나스닥 NYSE : 뉴욕 AMEX : 아멕스 [모의/실전 공통] SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민
|
||||
tr_crcy_cd (str): USD : 미국달러 HKD : 홍콩달러 CNY : 중국위안화 JPY : 일본엔화 VND : 베트남동
|
||||
FK200 (str): 공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK200값 : 다음페이지 조회시(2번째부터)
|
||||
NK200 (str): 공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK200값 : 다음페이지 조회시(2번째부터)
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
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_balance(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_excg_cd="NASD",
|
||||
... tr_crcy_cd="USD",
|
||||
... FK200="",
|
||||
... NK200=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '810XXXXX')")
|
||||
raise ValueError("cano is required. (e.g. '810XXXXX')")
|
||||
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 ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
if not tr_crcy_cd:
|
||||
logger.error("tr_crcy_cd is required. (e.g. 'USD')")
|
||||
raise ValueError("tr_crcy_cd is required. (e.g. 'USD')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 설정 (모의투자 지원 로직)
|
||||
if env_dv == "real":
|
||||
tr_id = "TTTS3012R" # 실전투자용 TR ID
|
||||
elif env_dv == "demo":
|
||||
tr_id = "VTTS3012R" # 모의투자용 TR ID
|
||||
else:
|
||||
raise ValueError("env_dv can only be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"TR_CRCY_CD": tr_crcy_cd,
|
||||
"CTX_AREA_FK200": FK200,
|
||||
"CTX_AREA_NK200": NK200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=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, FK200, NK200 = res.getHeader().tr_cont, res.getBody().ctx_area_fk200, res.getBody().ctx_area_nk200
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_balance(
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
ovrs_excg_cd,
|
||||
tr_crcy_cd,
|
||||
FK200,
|
||||
NK200,
|
||||
env_dv,
|
||||
dataframe1,
|
||||
dataframe2,
|
||||
"N",
|
||||
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()
|
||||
@@ -0,0 +1,158 @@
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_ccnl import inquire_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 주문체결내역 [v1_해외주식-007]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'ord_dt': '주문일자',
|
||||
'ord_gno_brno': '주문채번지점번호',
|
||||
'odno': '주문번호',
|
||||
'orgn_odno': '원주문번호',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'sll_buy_dvsn_cd_name': '매도매수구분코드명',
|
||||
'rvse_cncl_dvsn': '정정취소구분',
|
||||
'rvse_cncl_dvsn_name': '정정취소구분명',
|
||||
'pdno': '상품번호',
|
||||
'prdt_name': '상품명',
|
||||
'ft_ord_qty': 'FT주문수량',
|
||||
'ft_ord_unpr3': 'FT주문단가3',
|
||||
'ft_ccld_qty': 'FT체결수량',
|
||||
'ft_ccld_unpr3': 'FT체결단가3',
|
||||
'ft_ccld_amt3': 'FT체결금액3',
|
||||
'nccs_qty': '미체결수량',
|
||||
'prcs_stat_name': '처리상태명',
|
||||
'rjct_rson': '거부사유',
|
||||
'rjct_rson_name': '거부사유명',
|
||||
'ord_tmd': '주문시각',
|
||||
'tr_mket_name': '거래시장명',
|
||||
'tr_crcy_cd': '거래통화코드',
|
||||
'tr_natn': '거래국가',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'tr_natn_name': '거래국가명',
|
||||
'dmst_ord_dt': '국내주문일자',
|
||||
'thco_ord_tmd': '당사주문시각',
|
||||
'loan_type_cd': '대출유형코드',
|
||||
'loan_dt': '대출일자',
|
||||
'mdia_dvsn_name': '매체구분명',
|
||||
'usa_amk_exts_rqst_yn': '미국애프터마켓연장신청여부',
|
||||
'splt_buy_attr_name': '분할매수/매도속성명'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 주문체결내역[v1_해외주식-007]
|
||||
|
||||
해외주식 주문체결내역 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- pdno (str): 상품번호 (전종목일 경우 "%" 입력 ※ 모의투자계좌의 경우 ""(전체 조회)만 가능)
|
||||
- ord_strt_dt (str): 주문시작일자 (YYYYMMDD 형식 (현지시각 기준))
|
||||
- ord_end_dt (str): 주문종료일자 (YYYYMMDD 형식 (현지시각 기준))
|
||||
- sll_buy_dvsn (str): 매도매수구분 (00 : 전체 01 : 매도 02 : 매수 ※ 모의투자계좌의 경우 "00"(전체 조회)만 가능)
|
||||
- ccld_nccs_dvsn (str): 체결미체결구분 (00 : 전체 01 : 체결 02 : 미체결 ※ 모의투자계좌의 경우 "00"(전체 조회)만 가능)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (전종목일 경우 "%" 입력 NASD : 미국시장 전체(나스닥, 뉴욕, 아멕스) NYSE : 뉴욕 AMEX : 아멕스 SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민 ※ 모의투자계좌의 경우 ""(전체 조회)만 가능)
|
||||
- sort_sqn (str): 정렬순서 (DS : 정순 AS : 역순 ※ 모의투자계좌의 경우 정렬순서 사용불가(Default : DS(정순)))
|
||||
- ord_dt (str): 주문일자 ("" (Null 값 설정))
|
||||
- ord_gno_brno (str): 주문채번지점번호 ("" (Null 값 설정))
|
||||
- odno (str): 주문번호 ("" (Null 값 설정) ※ 주문번호로 검색 불가능합니다. 반드시 ""(Null 값 설정) 바랍니다.)
|
||||
- NK200 (str): 연속조회키200 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK200값 : 다음페이지 조회시(2번째부터))
|
||||
- FK200 (str): 연속조회검색조건200 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK200값 : 다음페이지 조회시(2번째부터))
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 주문체결내역 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_ccnl(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, pdno="%", ord_strt_dt="20250101", ord_end_dt="20250131", sll_buy_dvsn="00", ccld_nccs_dvsn="00", ovrs_excg_cd="NASD", sort_sqn="DS", ord_dt="", ord_gno_brno="", odno="", env_dv="real") # 실전투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = inquire_ccnl(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
pdno="%", # 상품번호
|
||||
ord_strt_dt="20250601", # 주문시작일자
|
||||
ord_end_dt="20250630", # 주문종료일자
|
||||
sll_buy_dvsn="00", # 매도매수구분
|
||||
ccld_nccs_dvsn="00", # 체결미체결구분
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
sort_sqn="DS", # 정렬순서
|
||||
env_dv="real", # 실전모의구분
|
||||
ord_dt="",
|
||||
ord_gno_brno="",
|
||||
odno="",
|
||||
)
|
||||
|
||||
if result is None:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 주문체결내역 결과 (%s) ===", "실전투자" if env_dv == "real" else "모의투자")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 주문체결내역 [v1_해외주식-007]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-ccnl"
|
||||
|
||||
|
||||
def inquire_ccnl(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
pdno: str, # 상품번호
|
||||
ord_strt_dt: str, # 주문시작일자
|
||||
ord_end_dt: str, # 주문종료일자
|
||||
sll_buy_dvsn: str, # 매도매수구분
|
||||
ccld_nccs_dvsn: str, # 체결미체결구분
|
||||
sort_sqn: str, # 정렬순서
|
||||
ord_dt: str, # 주문일자
|
||||
ord_gno_brno: str, # 주문채번지점번호
|
||||
odno: str, # 주문번호
|
||||
ovrs_excg_cd: str = "", # 해외거래소코드
|
||||
NK200: str = "", # 연속조회키200
|
||||
FK200: str = "", # 연속조회검색조건200
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 주문체결내역[v1_해외주식-007]
|
||||
해외주식 주문체결내역 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
pdno (str): 전종목일 경우 "%" 입력 ※ 모의투자계좌의 경우 ""(전체 조회)만 가능
|
||||
ord_strt_dt (str): YYYYMMDD 형식 (현지시각 기준)
|
||||
ord_end_dt (str): YYYYMMDD 형식 (현지시각 기준)
|
||||
sll_buy_dvsn (str): 00 : 전체 01 : 매도 02 : 매수 ※ 모의투자계좌의 경우 "00"(전체 조회)만 가능
|
||||
ccld_nccs_dvsn (str): 00 : 전체 01 : 체결 02 : 미체결 ※ 모의투자계좌의 경우 "00"(전체 조회)만 가능
|
||||
ovrs_excg_cd (str): 전종목일 경우 "%" 입력 NASD : 미국시장 전체(나스닥, 뉴욕, 아멕스) NYSE : 뉴욕 AMEX : 아멕스 SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민 ※ 모의투자계좌의 경우 ""(전체 조회)만 가능
|
||||
sort_sqn (str): DS : 정순 AS : 역순 ※ 모의투자계좌의 경우 정렬순서 사용불가(Default : DS(정순))
|
||||
ord_dt (str): "" (Null 값 설정)
|
||||
ord_gno_brno (str): "" (Null 값 설정)
|
||||
odno (str): "" (Null 값 설정) ※ 주문번호로 검색 불가능합니다. 반드시 ""(Null 값 설정) 바랍니다.
|
||||
NK200 (str): 공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK200값 : 다음페이지 조회시(2번째부터)
|
||||
FK200 (str): 공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK200값 : 다음페이지 조회시(2번째부터)
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 주문체결내역 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_ccnl(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... pdno="",
|
||||
... ord_strt_dt="20250601",
|
||||
... ord_end_dt="20250630",
|
||||
... sll_buy_dvsn="00",
|
||||
... ccld_nccs_dvsn="00",
|
||||
... ovrs_excg_cd="%",
|
||||
... sort_sqn="DS",
|
||||
... ord_dt="",
|
||||
... ord_gno_brno="02111",
|
||||
... odno="",
|
||||
... NK200="",
|
||||
... FK200=""
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '810XXXXX')")
|
||||
raise ValueError("cano is required. (e.g. '810XXXXX')")
|
||||
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 ord_strt_dt:
|
||||
logger.error("ord_strt_dt is required. (e.g. '20211027')")
|
||||
raise ValueError("ord_strt_dt is required. (e.g. '20211027')")
|
||||
if not ord_end_dt:
|
||||
logger.error("ord_end_dt is required. (e.g. '20211027')")
|
||||
raise ValueError("ord_end_dt is required. (e.g. '20211027')")
|
||||
if not sll_buy_dvsn:
|
||||
logger.error("sll_buy_dvsn is required. (e.g. '00')")
|
||||
raise ValueError("sll_buy_dvsn is required. (e.g. '00')")
|
||||
if not ccld_nccs_dvsn:
|
||||
logger.error("ccld_nccs_dvsn is required. (e.g. '00')")
|
||||
raise ValueError("ccld_nccs_dvsn is required. (e.g. '00')")
|
||||
if not sort_sqn:
|
||||
logger.error("sort_sqn is required. (e.g. 'DS')")
|
||||
raise ValueError("sort_sqn is required. (e.g. 'DS')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 설정 (모의투자 지원 로직)
|
||||
if env_dv == "real":
|
||||
tr_id = "TTTS3035R" # 실전투자용 TR ID
|
||||
elif env_dv == "demo":
|
||||
tr_id = "VTTS3035R" # 모의투자용 TR ID
|
||||
else:
|
||||
raise ValueError("env_dv can only be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"PDNO": pdno,
|
||||
"ORD_STRT_DT": ord_strt_dt,
|
||||
"ORD_END_DT": ord_end_dt,
|
||||
"SLL_BUY_DVSN": sll_buy_dvsn,
|
||||
"CCLD_NCCS_DVSN": ccld_nccs_dvsn,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"SORT_SQN": sort_sqn,
|
||||
"ORD_DT": ord_dt,
|
||||
"ORD_GNO_BRNO": ord_gno_brno,
|
||||
"ODNO": odno,
|
||||
"CTX_AREA_NK200": NK200,
|
||||
"CTX_AREA_FK200": FK200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=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, NK200, FK200 = res.getHeader().tr_cont, res.getBody().ctx_area_nk200, res.getBody().ctx_area_fk200
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_ccnl(
|
||||
cano=cano,
|
||||
acnt_prdt_cd=acnt_prdt_cd,
|
||||
pdno=pdno,
|
||||
ord_strt_dt=ord_strt_dt,
|
||||
ord_end_dt=ord_end_dt,
|
||||
sll_buy_dvsn=sll_buy_dvsn,
|
||||
ccld_nccs_dvsn=ccld_nccs_dvsn,
|
||||
ovrs_excg_cd=ovrs_excg_cd,
|
||||
sort_sqn=sort_sqn,
|
||||
ord_dt=ord_dt,
|
||||
ord_gno_brno=ord_gno_brno,
|
||||
odno=odno,
|
||||
NK200=NK200,
|
||||
FK200=FK200,
|
||||
env_dv=env_dv,
|
||||
tr_cont="N",
|
||||
dataframe=dataframe,
|
||||
depth=depth + 1,
|
||||
max_depth=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()
|
||||
@@ -0,0 +1,145 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_daily_chartprice import inquire_daily_chartprice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 종목_지수_환율기간별시세(일_주_월_년)[v1_해외주식-012]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'ovrs_nmix_prdy_vrss': '전일 대비',
|
||||
'prdy_vrss_sign': '전일 대비 부호',
|
||||
'prdy_ctrt': '전일 대비율',
|
||||
'ovrs_nmix_prdy_clpr': '전일 종가',
|
||||
'acml_vol': '누적 거래량',
|
||||
'hts_kor_isnm': 'HTS 한글 종목명',
|
||||
'ovrs_nmix_prpr': '현재가',
|
||||
'stck_shrn_iscd': '단축 종목코드',
|
||||
'prdy_vol': '전일 거래량',
|
||||
'ovrs_prod_oprc': '시가',
|
||||
'ovrs_prod_hgpr': '최고가',
|
||||
'ovrs_prod_lwpr': '최저가',
|
||||
'stck_bsop_date': '영업 일자',
|
||||
'ovrs_nmix_prpr': '현재가',
|
||||
'ovrs_nmix_oprc': '시가',
|
||||
'ovrs_nmix_hgpr': '최고가',
|
||||
'ovrs_nmix_lwpr': '최저가',
|
||||
'acml_vol': '누적 거래량',
|
||||
'mod_yn': '변경 여부'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['전일 대비', '전일 대비율', '전일 종가', '누적 거래량', '현재가', '시가', '최고가', '최저가']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 종목_지수_환율기간별시세(일_주_월_년)[v1_해외주식-012]
|
||||
|
||||
해외주식 종목_지수_환율기간별시세(일_주_월_년) 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- fid_cond_mrkt_div_code (str): FID 조건 시장 분류 코드 (N: 해외지수, X 환율, I: 국채, S:금선물)
|
||||
- fid_input_iscd (str): FID 입력 종목코드 (종목코드 ※ 해외주식 마스터 코드 참조 (포럼 > FAQ > 종목정보 다운로드(해외) > 해외지수) ※ 해당 API로 미국주식 조회 시, 다우30, 나스닥100, S&P500 종목만 조회 가능합니다. 더 많은 미국주식 종목 시세를 이용할 시에는, 해외주식기간별시세 API 사용 부탁드립니다.)
|
||||
- fid_input_date_1 (str): FID 입력 날짜1 (시작일자(YYYYMMDD))
|
||||
- fid_input_date_2 (str): FID 입력 날짜2 (종료일자(YYYYMMDD))
|
||||
- fid_period_div_code (str): FID 기간 분류 코드 (D:일, W:주, M:월, Y:년)
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 종목_지수_환율기간별시세(일_주_월_년) 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_daily_chartprice(fid_cond_mrkt_div_code="N", fid_input_iscd=".DJI", fid_input_date_1="20250101", fid_input_date_2="20250131", fid_period_div_code="D", env_dv="real") # 실전투자
|
||||
>>> df1, df2 = inquire_daily_chartprice(fid_cond_mrkt_div_code="N", fid_input_iscd=".DJI", fid_input_date_1="20250101", fid_input_date_2="20250131", fid_period_div_code="D", env_dv="demo") # 모의투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
|
||||
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외주식 종목_지수_환율기간별시세(일_주_월_년) (%s)", "실전투자" if env_dv == "real" else "모의투자")
|
||||
result1, result2 = inquire_daily_chartprice(
|
||||
fid_cond_mrkt_div_code="N",
|
||||
fid_input_iscd="QQQ",
|
||||
fid_input_date_1="20250101",
|
||||
fid_input_date_2="20250131",
|
||||
fid_period_div_code="D",
|
||||
env_dv=env_dv,
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
results = [result1, result2]
|
||||
if all(result is None or result.empty for result in results):
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
|
||||
# output1 결과 처리
|
||||
logger.info("=== output1 조회 ===")
|
||||
if not result1.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
logger.info("output1 결과:")
|
||||
print(result1)
|
||||
else:
|
||||
logger.info("output1 데이터가 없습니다.")
|
||||
|
||||
# output2 결과 처리
|
||||
logger.info("=== output2 조회 ===")
|
||||
if not result2.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
logger.info("output2 결과:")
|
||||
print(result2)
|
||||
else:
|
||||
logger.info("output2 데이터가 없습니다.")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,175 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 종목_지수_환율기간별시세(일_주_월_년)[v1_해외주식-012]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/inquire-daily-chartprice"
|
||||
|
||||
def inquire_daily_chartprice(
|
||||
fid_cond_mrkt_div_code: str, # FID 조건 시장 분류 코드
|
||||
fid_input_iscd: str, # FID 입력 종목코드
|
||||
fid_input_date_1: str, # FID 입력 날짜1
|
||||
fid_input_date_2: str, # FID 입력 날짜2
|
||||
fid_period_div_code: str, # FID 기간 분류 코드
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 종목_지수_환율기간별시세(일_주_월_년)[v1_해외주식-012]
|
||||
해외주식 종목_지수_환율기간별시세(일_주_월_년) API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
fid_cond_mrkt_div_code (str): N: 해외지수, X 환율, I: 국채, S:금선물
|
||||
fid_input_iscd (str): 종목코드 ※ 해외주식 마스터 코드 참조 (포럼 > FAQ > 종목정보 다운로드(해외) > 해외지수) ※ 해당 API로 미국주식 조회 시, 다우30, 나스닥100, S&P500 종목만 조회 가능합니다. 더 많은 미국주식 종목 시세를 이용할 시에는, 해외주식기간별시세 API 사용 부탁드립니다.
|
||||
fid_input_date_1 (str): 시작일자(YYYYMMDD)
|
||||
fid_input_date_2 (str): 종료일자(YYYYMMDD)
|
||||
fid_period_div_code (str): D:일, W:주, M:월, Y:년
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
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_chartprice(
|
||||
... fid_cond_mrkt_div_code="N",
|
||||
... fid_input_iscd=".DJI",
|
||||
... fid_input_date_1="20220401",
|
||||
... fid_input_date_2="20220613",
|
||||
... fid_period_div_code="D",
|
||||
... env_dv="real"
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not fid_cond_mrkt_div_code:
|
||||
logger.error("fid_cond_mrkt_div_code is required. (e.g. 'N')")
|
||||
raise ValueError("fid_cond_mrkt_div_code is required. (e.g. 'N')")
|
||||
if not fid_input_iscd:
|
||||
logger.error("fid_input_iscd is required. (e.g. '.DJI')")
|
||||
raise ValueError("fid_input_iscd is required. (e.g. '.DJI')")
|
||||
if not fid_input_date_1:
|
||||
logger.error("fid_input_date_1 is required. (e.g. '20220401')")
|
||||
raise ValueError("fid_input_date_1 is required. (e.g. '20220401')")
|
||||
if not fid_input_date_2:
|
||||
logger.error("fid_input_date_2 is required. (e.g. '20220613')")
|
||||
raise ValueError("fid_input_date_2 is required. (e.g. '20220613')")
|
||||
if not fid_period_div_code:
|
||||
logger.error("fid_period_div_code is required. (e.g. 'D')")
|
||||
raise ValueError("fid_period_div_code is required. (e.g. 'D')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 설정 (모의투자 지원 로직)
|
||||
if env_dv == "real" or env_dv == "demo":
|
||||
tr_id = "FHKST03030100" # 실전투자용 TR ID
|
||||
else:
|
||||
raise ValueError("env_dv can only be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code,
|
||||
"FID_INPUT_ISCD": fid_input_iscd,
|
||||
"FID_INPUT_DATE_1": fid_input_date_1,
|
||||
"FID_INPUT_DATE_2": fid_input_date_2,
|
||||
"FID_PERIOD_DIV_CODE": fid_period_div_code,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_daily_chartprice(
|
||||
fid_cond_mrkt_div_code,
|
||||
fid_input_iscd,
|
||||
fid_input_date_1,
|
||||
fid_input_date_2,
|
||||
fid_period_div_code,
|
||||
env_dv,
|
||||
"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()
|
||||
@@ -0,0 +1,131 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_nccs import inquire_nccs
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 미체결내역 [v1_해외주식-005]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'ord_dt': '주문일자',
|
||||
'ord_gno_brno': '주문채번지점번호',
|
||||
'odno': '주문번호',
|
||||
'orgn_odno': '원주문번호',
|
||||
'pdno': '상품번호',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'rvse_cncl_dvsn_cd': '정정취소구분코드',
|
||||
'rjct_rson': '거부사유',
|
||||
'ord_tmd': '주문시각',
|
||||
'tr_crcy_cd': '거래통화코드',
|
||||
'natn_cd': '국가코드',
|
||||
'ft_ord_qty': 'FT주문수량',
|
||||
'ft_ccld_qty': 'FT체결수량',
|
||||
'nccs_qty': '미체결수량',
|
||||
'ft_ord_unpr3': 'FT주문단가3',
|
||||
'ft_ccld_unpr3': 'FT체결단가3',
|
||||
'ft_ccld_amt3': 'FT체결금액3',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'loan_type_cd': '대출유형코드',
|
||||
'loan_dt': '대출일자',
|
||||
'usa_amk_exts_rqst_yn': '미국애프터마켓연장신청여부',
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 미체결내역[v1_해외주식-005]
|
||||
|
||||
해외주식 미체결내역 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (NASD : 나스닥 NYSE : 뉴욕 AMEX : 아멕스 SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민 * NASD 인 경우만 미국전체로 조회되며 나머지 거래소 코드는 해당 거래소만 조회됨 * 공백 입력 시 다음조회가 불가능하므로, 반드시 거래소코드 입력해야 함)
|
||||
- sort_sqn (str): 정렬순서 (DS : 정순 그외 : 역순 [header tr_id: TTTS3018R] ""(공란))
|
||||
- FK200 (str): 연속조회검색조건200 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK200값 : 다음페이지 조회시(2번째부터))
|
||||
- NK200 (str): 연속조회키200 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK200값 : 다음페이지 조회시(2번째부터))
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 미체결내역 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_nccs(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_excg_cd="NASD", sort_sqn="DS", FK200="", NK200="", env_dv="real") # 실전투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = inquire_nccs(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
sort_sqn="DS", # 정렬순서
|
||||
FK200="", # 연속조회검색조건200
|
||||
NK200="", # 연속조회키200
|
||||
env_dv="real", # 실전모의구분
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 미체결내역 결과 ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 미체결내역 [v1_해외주식-005]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-nccs"
|
||||
|
||||
def inquire_nccs(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
sort_sqn: str, # 정렬순서
|
||||
FK200: str, # 연속조회검색조건200
|
||||
NK200: str, # 연속조회키200
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 미체결내역[v1_해외주식-005]
|
||||
해외주식 미체결내역 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_excg_cd (str): NASD : 나스닥 NYSE : 뉴욕 AMEX : 아멕스 SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민 * NASD 인 경우만 미국전체로 조회되며 나머지 거래소 코드는 해당 거래소만 조회됨 * 공백 입력 시 다음조회가 불가능하므로, 반드시 거래소코드 입력해야 함
|
||||
sort_sqn (str): DS : 정순 그외 : 역순 [header tr_id: TTTS3018R] ""(공란)
|
||||
FK200 (str): 공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK200값 : 다음페이지 조회시(2번째부터)
|
||||
NK200 (str): 공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK200값 : 다음페이지 조회시(2번째부터)
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 미체결내역 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_nccs(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_excg_cd="NYSE",
|
||||
... sort_sqn="DS",
|
||||
... FK200="",
|
||||
... NK200=""
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '810XXXXX')")
|
||||
raise ValueError("cano is required. (e.g. '810XXXXX')")
|
||||
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 ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NYSE')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NYSE')")
|
||||
if not sort_sqn:
|
||||
logger.error("sort_sqn is required. (e.g. 'DS')")
|
||||
raise ValueError("sort_sqn is required. (e.g. 'DS')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "TTTS3018R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"SORT_SQN": sort_sqn,
|
||||
"CTX_AREA_FK200": FK200,
|
||||
"CTX_AREA_NK200": NK200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=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, NK200, FK200 = res.getHeader().tr_cont, res.getBody().ctx_area_nk200, res.getBody().ctx_area_fk200
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_nccs(
|
||||
cano=cano,
|
||||
acnt_prdt_cd=acnt_prdt_cd,
|
||||
ovrs_excg_cd=ovrs_excg_cd,
|
||||
sort_sqn=sort_sqn,
|
||||
FK200=FK200,
|
||||
NK200=NK200,
|
||||
env_dv=env_dv,
|
||||
tr_cont="N",
|
||||
dataframe=dataframe,
|
||||
depth=depth + 1,
|
||||
max_depth=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)
|
||||
# 이미 수집된 데이터가 있으면 그것을 반환, 없으면 빈 DataFrame 반환
|
||||
if dataframe is not None and not dataframe.empty:
|
||||
logger.info("Returning already collected data due to API error.")
|
||||
return dataframe
|
||||
else:
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,185 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_paymt_stdr_balance import inquire_paymt_stdr_balance
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 결제기준잔고 [해외주식-064]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'pdno': '상품번호',
|
||||
'prdt_name': '상품명',
|
||||
'cblc_qty13': '잔고수량13',
|
||||
'ord_psbl_qty1': '주문가능수량1',
|
||||
'avg_unpr3': '평균단가3',
|
||||
'ovrs_now_pric1': '해외현재가격1',
|
||||
'frcr_pchs_amt': '외화매입금액',
|
||||
'frcr_evlu_amt2': '외화평가금액2',
|
||||
'evlu_pfls_amt2': '평가손익금액2',
|
||||
'bass_exrt': '기준환율',
|
||||
'oprt_dtl_dtime': '조작상세일시',
|
||||
'buy_crcy_cd': '매수통화코드',
|
||||
'thdt_sll_ccld_qty1': '당일매도체결수량1',
|
||||
'thdt_buy_ccld_qty1': '당일매수체결수량1',
|
||||
'evlu_pfls_rt1': '평가손익율1',
|
||||
'tr_mket_name': '거래시장명',
|
||||
'natn_kor_name': '국가한글명',
|
||||
'std_pdno': '표준상품번호',
|
||||
'mgge_qty': '담보수량',
|
||||
'loan_rmnd': '대출잔액',
|
||||
'prdt_type_cd': '상품유형코드',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'scts_dvsn_name': '유가증권구분명',
|
||||
'ldng_cblc_qty': '대여잔고수량',
|
||||
'crcy_cd': '통화코드',
|
||||
'crcy_cd_name': '통화코드명',
|
||||
'frcr_dncl_amt_2': '외화예수금액2',
|
||||
'frst_bltn_exrt': '최초고시환율',
|
||||
'frcr_evlu_amt2': '외화평가금액2',
|
||||
'pchs_amt_smtl_amt': '매입금액합계금액',
|
||||
'tot_evlu_pfls_amt': '총평가손익금액',
|
||||
'evlu_erng_rt1': '평가수익율1',
|
||||
'tot_dncl_amt': '총예수금액',
|
||||
'wcrc_evlu_amt_smtl': '원화평가금액합계',
|
||||
'tot_asst_amt2': '총자산금액2',
|
||||
'frcr_cblc_wcrc_evlu_amt_smtl': '외화잔고원화평가금액합계',
|
||||
'tot_loan_amt': '총대출금액',
|
||||
'tot_ldng_evlu_amt': '총대여평가금액'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = [
|
||||
'잔고수량13', '주문가능수량1', '평균단가3', '해외현재가격1', '외화매입금액',
|
||||
'외화평가금액2', '평가손익금액2', '기준환율', '당일매도체결수량1', '당일매수체결수량1',
|
||||
'평가손익율1', '담보수량', '대출잔액', '대여잔고수량', '외화예수금액2',
|
||||
'최초고시환율', '매입금액합계금액', '총평가손익금액', '평가수익율1', '총예수금액',
|
||||
'원화평가금액합계', '총자산금액2', '외화잔고원화평가금액합계', '총대출금액', '총대여평가금액'
|
||||
]
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 결제기준잔고[해외주식-064]
|
||||
|
||||
해외주식 결제기준잔고 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 ()
|
||||
- acnt_prdt_cd (str): 계좌상품코드 ()
|
||||
- bass_dt (str): 기준일자 ()
|
||||
- wcrc_frcr_dvsn_cd (str): 원화외화구분코드 (01(원화기준),02(외화기준))
|
||||
- inqr_dvsn_cd (str): 조회구분코드 (00(전체), 01(일반), 02(미니스탁))
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 결제기준잔고 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2, df3 = inquire_paymt_stdr_balance(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, bass_dt="20250630", wcrc_frcr_dvsn_cd="01", inqr_dvsn_cd="00")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2, result3 = inquire_paymt_stdr_balance(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
bass_dt="20250630", # 기준일자
|
||||
wcrc_frcr_dvsn_cd="01", # 원화외화구분코드
|
||||
inqr_dvsn_cd="00", # 조회구분코드
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
results = [result1, result2, result3]
|
||||
if all(result is None or result.empty for result in results):
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
|
||||
# output1 결과 처리
|
||||
logger.info("=== output1 조회 ===")
|
||||
if not result1.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("output1 결과:")
|
||||
print(result1)
|
||||
else:
|
||||
logger.info("output1 데이터가 없습니다.")
|
||||
|
||||
# output2 결과 처리
|
||||
logger.info("=== output2 조회 ===")
|
||||
if not result2.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("output2 결과:")
|
||||
print(result2)
|
||||
else:
|
||||
logger.info("output2 데이터가 없습니다.")
|
||||
|
||||
# output3 결과 처리
|
||||
logger.info("=== output3 조회 ===")
|
||||
if not result3.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result3.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result3 = result3.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result3.columns:
|
||||
result3[col] = pd.to_numeric(result3[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("output3 결과:")
|
||||
print(result3)
|
||||
else:
|
||||
logger.info("output3 데이터가 없습니다.")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,200 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 결제기준잔고 [해외주식-064]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-paymt-stdr-balance"
|
||||
|
||||
def inquire_paymt_stdr_balance(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
bass_dt: str, # 기준일자
|
||||
wcrc_frcr_dvsn_cd: str, # 원화외화구분코드
|
||||
inqr_dvsn_cd: str, # 조회구분코드
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 결제기준잔고[해외주식-064]
|
||||
해외주식 결제기준잔고 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 종합계좌번호
|
||||
acnt_prdt_cd (str): 계좌상품코드
|
||||
bass_dt (str): 기준일자
|
||||
wcrc_frcr_dvsn_cd (str): 원화외화구분코드 (01: 원화기준, 02: 외화기준)
|
||||
inqr_dvsn_cd (str): 조회구분코드 (00: 전체, 01: 일반, 02: 미니스탁)
|
||||
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 = inquire_paymt_stdr_balance(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... bass_dt="20230630",
|
||||
... wcrc_frcr_dvsn_cd="01",
|
||||
... inqr_dvsn_cd="00"
|
||||
... )
|
||||
>>> 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 bass_dt:
|
||||
logger.error("bass_dt is required. (e.g. '20230630')")
|
||||
raise ValueError("bass_dt is required. (e.g. '20230630')")
|
||||
if not wcrc_frcr_dvsn_cd:
|
||||
logger.error("wcrc_frcr_dvsn_cd is required. (e.g. '01')")
|
||||
raise ValueError("wcrc_frcr_dvsn_cd is required. (e.g. '01')")
|
||||
if not inqr_dvsn_cd:
|
||||
logger.error("inqr_dvsn_cd is required. (e.g. '00')")
|
||||
raise ValueError("inqr_dvsn_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 = "CTRP6010R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"BASS_DT": bass_dt,
|
||||
"WCRC_FRCR_DVSN_CD": wcrc_frcr_dvsn_cd,
|
||||
"INQR_DVSN_CD": inqr_dvsn_cd,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=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()
|
||||
# output3 처리
|
||||
if hasattr(res.getBody(), 'output3'):
|
||||
output_data = res.getBody().output3
|
||||
if output_data:
|
||||
# output1은 단일 객체, output2는 배열일 수 있음
|
||||
if isinstance(output_data, list):
|
||||
current_data3 = pd.DataFrame(output_data)
|
||||
else:
|
||||
# 단일 객체인 경우 리스트로 감싸서 DataFrame 생성
|
||||
current_data3 = pd.DataFrame([output_data])
|
||||
|
||||
if dataframe3 is not None:
|
||||
dataframe3 = pd.concat([dataframe3, current_data3], ignore_index=True)
|
||||
else:
|
||||
dataframe3 = current_data3
|
||||
else:
|
||||
if dataframe3 is None:
|
||||
dataframe3 = pd.DataFrame()
|
||||
else:
|
||||
if dataframe3 is None:
|
||||
dataframe3 = pd.DataFrame()
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_paymt_stdr_balance(
|
||||
cano=cano,
|
||||
acnt_prdt_cd=acnt_prdt_cd,
|
||||
bass_dt=bass_dt,
|
||||
wcrc_frcr_dvsn_cd=wcrc_frcr_dvsn_cd,
|
||||
inqr_dvsn_cd=inqr_dvsn_cd,
|
||||
dataframe1=dataframe1,
|
||||
dataframe2=dataframe2,
|
||||
dataframe3=dataframe3,
|
||||
tr_cont="N",
|
||||
depth=depth + 1,
|
||||
max_depth=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)
|
||||
# 이미 수집된 데이터가 있으면 그것을 반환, 없으면 빈 DataFrame 반환
|
||||
if dataframe1 is not None and not dataframe1.empty:
|
||||
logger.info("Returning already collected data due to API error.")
|
||||
return dataframe1, dataframe2 if dataframe2 is not None else pd.DataFrame(), dataframe3 if dataframe3 is not None else pd.DataFrame()
|
||||
else:
|
||||
return pd.DataFrame(), pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,156 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_period_profit import inquire_period_profit
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 기간손익 [v1_해외주식-032]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'trad_day': '매매일',
|
||||
'ovrs_pdno': '해외상품번호',
|
||||
'slcl_qty': '매도청산수량',
|
||||
'pchs_avg_pric': '매입평균가격',
|
||||
'frcr_pchs_amt1': '외화매입금액1',
|
||||
'avg_sll_unpr': '평균매도단가',
|
||||
'frcr_sll_amt_smtl1': '외화매도금액합계1',
|
||||
'stck_sll_tlex': '주식매도제비용',
|
||||
'ovrs_rlzt_pfls_amt': '해외실현손익금액',
|
||||
'pftrt': '수익률',
|
||||
'exrt': '환율',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'frst_bltn_exrt': '최초고시환율',
|
||||
'stck_sll_amt_smtl': '주식매도금액합계',
|
||||
'stck_buy_amt_smtl': '주식매수금액합계',
|
||||
'smtl_fee1': '합계수수료1',
|
||||
'excc_dfrm_amt': '정산지급금액',
|
||||
'ovrs_rlzt_pfls_tot_amt': '해외실현손익총금액',
|
||||
'tot_pftrt': '총수익률',
|
||||
'bass_dt': '기준일자',
|
||||
'exrt': '환율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = [
|
||||
'slcl_qty', 'pchs_avg_pric', 'frcr_pchs_amt1', 'avg_sll_unpr', 'frcr_sll_amt_smtl1',
|
||||
'stck_sll_tlex', 'ovrs_rlzt_pfls_amt', 'pftrt', 'exrt', 'frst_bltn_exrt',
|
||||
'stck_sll_amt_smtl', 'stck_buy_amt_smtl', 'smtl_fee1', 'excc_dfrm_amt',
|
||||
'ovrs_rlzt_pfls_tot_amt', 'tot_pftrt'
|
||||
]
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 기간손익[v1_해외주식-032]
|
||||
|
||||
해외주식 기간손익 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (공란 : 전체, NASD : 미국, SEHK : 홍콩, SHAA : 중국, TKSE : 일본, HASE : 베트남)
|
||||
- natn_cd (str): 국가코드 (공란(Default))
|
||||
- crcy_cd (str): 통화코드 (공란 : 전체 USD : 미국달러, HKD : 홍콩달러, CNY : 중국위안화, JPY : 일본엔화, VND : 베트남동)
|
||||
- pdno (str): 상품번호 (공란 : 전체)
|
||||
- inqr_strt_dt (str): 조회시작일자 (YYYYMMDD)
|
||||
- inqr_end_dt (str): 조회종료일자 (YYYYMMDD)
|
||||
- wcrc_frcr_dvsn_cd (str): 원화외화구분코드 (01 : 외화, 02 : 원화)
|
||||
- FK200 (str): 연속조회검색조건200 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK200값 : 다음페이지 조회시(2번째부터))
|
||||
- NK200 (str): 연속조회키200 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK200값 : 다음페이지 조회시(2번째부터))
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 기간손익 결과 (output1: 기간손익 목록, output2: 기간손익 요약)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_period_profit(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_excg_cd="NASD", natn_cd="", crcy_cd="USD", pdno="", inqr_strt_dt="20230101", inqr_end_dt="20231231", wcrc_frcr_dvsn_cd="01", FK200="", NK200="")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2 = inquire_period_profit(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
natn_cd="", # 국가코드
|
||||
crcy_cd="USD", # 통화코드
|
||||
pdno="", # 상품번호
|
||||
inqr_strt_dt="20240601", # 조회시작일자
|
||||
inqr_end_dt="20240630", # 조회종료일자
|
||||
wcrc_frcr_dvsn_cd="01", # 원화외화구분코드
|
||||
FK200="", # 연속조회검색조건200
|
||||
NK200="" # 연속조회키200
|
||||
)
|
||||
|
||||
if result1 is None or result1.empty:
|
||||
logger.warning("조회된 기간손익 목록 데이터가 없습니다.")
|
||||
else:
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록 (output1):")
|
||||
logger.info(result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 기간손익 목록 (output1) ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result1))
|
||||
print(result1)
|
||||
|
||||
if result2 is None or result2.empty:
|
||||
logger.warning("조회된 기간손익 요약 데이터가 없습니다.")
|
||||
else:
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록 (output2):")
|
||||
logger.info(result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 기간손익 요약 (output2) ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result2))
|
||||
print(result2)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,214 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 기간손익 [v1_해외주식-032]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-period-profit"
|
||||
|
||||
def inquire_period_profit(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
natn_cd: str, # 국가코드
|
||||
crcy_cd: str, # 통화코드
|
||||
pdno: str, # 상품번호
|
||||
inqr_strt_dt: str, # 조회시작일자
|
||||
inqr_end_dt: str, # 조회종료일자
|
||||
wcrc_frcr_dvsn_cd: str, # 원화외화구분코드
|
||||
FK200: str, # 연속조회검색조건200
|
||||
NK200: 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]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 기간손익[v1_해외주식-032]
|
||||
해외주식 기간손익 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_excg_cd (str): 공란 : 전체, NASD : 미국, SEHK : 홍콩, SHAA : 중국, TKSE : 일본, HASE : 베트남
|
||||
natn_cd (str): 공란(Default)
|
||||
crcy_cd (str): 공란 : 전체 USD : 미국달러, HKD : 홍콩달러, CNY : 중국위안화, JPY : 일본엔화, VND : 베트남동
|
||||
pdno (str): 공란 : 전체
|
||||
inqr_strt_dt (str): YYYYMMDD
|
||||
inqr_end_dt (str): YYYYMMDD
|
||||
wcrc_frcr_dvsn_cd (str): 01 : 외화, 02 : 원화
|
||||
FK200 (str): 연속조회검색조건200
|
||||
NK200 (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_period_profit(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_excg_cd="NASD",
|
||||
... natn_cd="",
|
||||
... crcy_cd="USD",
|
||||
... pdno="",
|
||||
... inqr_strt_dt="20230101",
|
||||
... inqr_end_dt="20231231",
|
||||
... wcrc_frcr_dvsn_cd="01",
|
||||
... FK200="",
|
||||
... NK200=""
|
||||
... )
|
||||
>>> 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 ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
if not crcy_cd:
|
||||
logger.error("crcy_cd is required. (e.g. 'USD')")
|
||||
raise ValueError("crcy_cd is required. (e.g. 'USD')")
|
||||
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. '20231231')")
|
||||
raise ValueError("inqr_end_dt is required. (e.g. '20231231')")
|
||||
if not wcrc_frcr_dvsn_cd:
|
||||
logger.error("wcrc_frcr_dvsn_cd is required. (e.g. '01')")
|
||||
raise ValueError("wcrc_frcr_dvsn_cd is required. (e.g. '01')")
|
||||
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "TTTS3039R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"NATN_CD": natn_cd,
|
||||
"CRCY_CD": crcy_cd,
|
||||
"PDNO": pdno,
|
||||
"INQR_STRT_DT": inqr_strt_dt,
|
||||
"INQR_END_DT": inqr_end_dt,
|
||||
"WCRC_FRCR_DVSN_CD": wcrc_frcr_dvsn_cd,
|
||||
"CTX_AREA_FK200": FK200,
|
||||
"CTX_AREA_NK200": NK200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=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, NK200, FK200 = res.getHeader().tr_cont, res.getBody().ctx_area_nk200, res.getBody().ctx_area_fk200
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_period_profit(
|
||||
cano=cano,
|
||||
acnt_prdt_cd=acnt_prdt_cd,
|
||||
ovrs_excg_cd=ovrs_excg_cd,
|
||||
natn_cd=natn_cd,
|
||||
crcy_cd=crcy_cd,
|
||||
pdno=pdno,
|
||||
inqr_strt_dt=inqr_strt_dt,
|
||||
inqr_end_dt=inqr_end_dt,
|
||||
wcrc_frcr_dvsn_cd=wcrc_frcr_dvsn_cd,
|
||||
FK200=FK200,
|
||||
NK200=NK200,
|
||||
dataframe1=dataframe1,
|
||||
dataframe2=dataframe2,
|
||||
tr_cont="N",
|
||||
depth=depth + 1,
|
||||
max_depth=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)
|
||||
# 이미 수집된 데이터가 있으면 그것을 반환, 없으면 빈 DataFrame 반환
|
||||
if dataframe1 is not None and not dataframe1.empty:
|
||||
logger.info("Returning already collected data due to API error.")
|
||||
return dataframe1, dataframe2 if dataframe2 is not None else pd.DataFrame()
|
||||
else:
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_period_trans import inquire_period_trans
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 일별거래내역 [해외주식-063]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'trad_dt': '매매일자',
|
||||
'sttl_dt': '결제일자',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'sll_buy_dvsn_name': '',
|
||||
'pdno': '상품번호',
|
||||
'ovrs_item_name': '',
|
||||
'ccld_qty': '체결수량',
|
||||
'amt_unit_ccld_qty': '금액단위체결수량',
|
||||
'ft_ccld_unpr2': 'FT체결단가2',
|
||||
'ovrs_stck_ccld_unpr': '해외주식체결단가',
|
||||
'tr_frcr_amt2': '거래외화금액2',
|
||||
'tr_amt': '거래금액',
|
||||
'frcr_excc_amt_1': '외화정산금액1',
|
||||
'wcrc_excc_amt': '원화정산금액',
|
||||
'dmst_frcr_fee1': '국내외화수수료1',
|
||||
'frcr_fee1': '외화수수료1',
|
||||
'dmst_wcrc_fee': '국내원화수수료',
|
||||
'ovrs_wcrc_fee': '해외원화수수료',
|
||||
'crcy_cd': '통화코드',
|
||||
'std_pdno': '표준상품번호',
|
||||
'erlm_exrt': '등록환율',
|
||||
'loan_dvsn_cd': '대출구분코드',
|
||||
'loan_dvsn_name': '',
|
||||
'frcr_buy_amt_smtl': '외화매수금액합계',
|
||||
'frcr_sll_amt_smtl': '외화매도금액합계',
|
||||
'dmst_fee_smtl': '국내수수료합계',
|
||||
'ovrs_fee_smtl': '해외수수료합계'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = [
|
||||
'ccld_qty', 'amt_unit_ccld_qty', 'ft_ccld_unpr2', 'ovrs_stck_ccld_unpr', 'tr_frcr_amt2',
|
||||
'tr_amt', 'frcr_excc_amt_1', 'wcrc_excc_amt', 'dmst_frcr_fee1', 'frcr_fee1',
|
||||
'dmst_wcrc_fee', 'ovrs_wcrc_fee', 'erlm_exrt', 'frcr_buy_amt_smtl', 'frcr_sll_amt_smtl',
|
||||
'dmst_fee_smtl', 'ovrs_fee_smtl'
|
||||
]
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 일별거래내역[해외주식-063]
|
||||
|
||||
해외주식 일별거래내역 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- erlm_strt_dt (str): 등록시작일자 (예: 20240420)
|
||||
- erlm_end_dt (str): 등록종료일자 (예: 20240520)
|
||||
- ovrs_excg_cd (str): 해외거래소코드
|
||||
- pdno (str): 상품번호
|
||||
- sll_buy_dvsn_cd (str): 매도매수구분코드 (00: 전체, 01: 매도, 02: 매수)
|
||||
- loan_dvsn_cd (str): 대출구분코드
|
||||
- FK100 (str): 연속조회검색조건100 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK100값 : 다음페이지 조회시(2번째부터))
|
||||
- NK100 (str): 연속조회키100 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK100값 : 다음페이지 조회시(2번째부터))
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 일별거래내역 결과 (output1: 거래내역 목록, output2: 거래내역 요약)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_period_trans(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, erlm_strt_dt="20240420", erlm_end_dt="20240520", ovrs_excg_cd="NAS", pdno="", sll_buy_dvsn_cd="00", loan_dvsn_cd="", FK100="", NK100="")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2 = inquire_period_trans(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
erlm_strt_dt="20240601", # 등록시작일자
|
||||
erlm_end_dt="20240630", # 등록종료일자
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
pdno="", # 상품번호
|
||||
sll_buy_dvsn_cd="00", # 매도매수구분코드
|
||||
loan_dvsn_cd="", # 대출구분코드
|
||||
FK100="", # 연속조회검색조건100
|
||||
NK100="" # 연속조회키100
|
||||
)
|
||||
|
||||
if result1 is None or result1.empty:
|
||||
logger.warning("조회된 거래내역 목록 데이터가 없습니다.")
|
||||
else:
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록 (output1):")
|
||||
logger.info(result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 일별거래내역 목록 (output1) ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result1))
|
||||
print(result1)
|
||||
|
||||
if result2 is None or result2.empty:
|
||||
logger.warning("조회된 거래내역 요약 데이터가 없습니다.")
|
||||
else:
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록 (output2):")
|
||||
logger.info(result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 일별거래내역 요약 (output2) ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result2))
|
||||
print(result2)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,206 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 일별거래내역 [해외주식-063]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-period-trans"
|
||||
|
||||
def inquire_period_trans(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
erlm_strt_dt: str, # 등록시작일자
|
||||
erlm_end_dt: str, # 등록종료일자
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
pdno: str, # 상품번호
|
||||
sll_buy_dvsn_cd: str, # 매도매수구분코드
|
||||
loan_dvsn_cd: str, # 대출구분코드
|
||||
FK100: str, # 연속조회검색조건100
|
||||
NK100: str, # 연속조회키100
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 일별거래내역[해외주식-063]
|
||||
해외주식 일별거래내역 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 종합계좌번호
|
||||
acnt_prdt_cd (str): 계좌상품코드
|
||||
erlm_strt_dt (str): 등록시작일자 (예: 20240420)
|
||||
erlm_end_dt (str): 등록종료일자 (예: 20240520)
|
||||
ovrs_excg_cd (str): 해외거래소코드
|
||||
pdno (str): 상품번호
|
||||
sll_buy_dvsn_cd (str): 매도매수구분코드 (00: 전체, 01: 매도, 02: 매수)
|
||||
loan_dvsn_cd (str): 대출구분코드
|
||||
FK100 (str): 연속조회검색조건100
|
||||
NK100 (str): 연속조회키100
|
||||
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_period_trans(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... erlm_strt_dt="20240420",
|
||||
... erlm_end_dt="20240520",
|
||||
... ovrs_excg_cd="NAS",
|
||||
... pdno="",
|
||||
... sll_buy_dvsn_cd="00",
|
||||
... loan_dvsn_cd="",
|
||||
... FK100="",
|
||||
... NK100=""
|
||||
... )
|
||||
>>> 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 erlm_strt_dt:
|
||||
logger.error("erlm_strt_dt is required. (e.g. '20240420')")
|
||||
raise ValueError("erlm_strt_dt is required. (e.g. '20240420')")
|
||||
if not erlm_end_dt:
|
||||
logger.error("erlm_end_dt is required. (e.g. '20240520')")
|
||||
raise ValueError("erlm_end_dt is required. (e.g. '20240520')")
|
||||
if not ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NAS')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NAS')")
|
||||
if not sll_buy_dvsn_cd:
|
||||
logger.error("sll_buy_dvsn_cd is required. (e.g. '00')")
|
||||
raise ValueError("sll_buy_dvsn_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()
|
||||
|
||||
tr_id = "CTOS4001R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"ERLM_STRT_DT": erlm_strt_dt,
|
||||
"ERLM_END_DT": erlm_end_dt,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"PDNO": pdno,
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd,
|
||||
"LOAN_DVSN_CD": loan_dvsn_cd,
|
||||
"CTX_AREA_FK100": FK100,
|
||||
"CTX_AREA_NK100": NK100,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=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, NK100, FK100 = res.getHeader().tr_cont, res.getBody().ctx_area_nk100, res.getBody().ctx_area_fk100
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_period_trans(
|
||||
cano=cano,
|
||||
acnt_prdt_cd=acnt_prdt_cd,
|
||||
erlm_strt_dt=erlm_strt_dt,
|
||||
erlm_end_dt=erlm_end_dt,
|
||||
ovrs_excg_cd=ovrs_excg_cd,
|
||||
pdno=pdno,
|
||||
sll_buy_dvsn_cd=sll_buy_dvsn_cd,
|
||||
loan_dvsn_cd=loan_dvsn_cd,
|
||||
FK100=FK100,
|
||||
NK100=NK100,
|
||||
dataframe1=dataframe1,
|
||||
dataframe2=dataframe2,
|
||||
tr_cont="N",
|
||||
depth=depth + 1,
|
||||
max_depth=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)
|
||||
# 이미 수집된 데이터가 있으면 그것을 반환, 없으면 빈 DataFrame 반환
|
||||
if dataframe1 is not None and not dataframe1.empty:
|
||||
logger.info("Returning already collected data due to API error.")
|
||||
return dataframe1, dataframe2 if dataframe2 is not None else pd.DataFrame()
|
||||
else:
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,223 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_present_balance import inquire_present_balance
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 체결기준현재잔고 [v1_해외주식-008]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'cblc_qty13': '잔고수량13',
|
||||
'thdt_buy_ccld_qty1': '당일매수체결수량1',
|
||||
'thdt_sll_ccld_qty1': '당일매도체결수량1',
|
||||
'ccld_qty_smtl1': '체결수량합계1',
|
||||
'ord_psbl_qty1': '주문가능수량1',
|
||||
'frcr_pchs_amt': '외화매입금액',
|
||||
'frcr_evlu_amt2': '외화평가금액2',
|
||||
'evlu_pfls_amt2': '평가손익금액2',
|
||||
'evlu_pfls_rt1': '평가손익율1',
|
||||
'pdno': '상품번호',
|
||||
'bass_exrt': '기준환율',
|
||||
'buy_crcy_cd': '매수통화코드',
|
||||
'ovrs_now_pric1': '해외현재가격1',
|
||||
'avg_unpr3': '평균단가3',
|
||||
'tr_mket_name': '거래시장명',
|
||||
'natn_kor_name': '국가한글명',
|
||||
'pchs_rmnd_wcrc_amt': '매입잔액원화금액',
|
||||
'thdt_buy_ccld_frcr_amt': '당일매수체결외화금액',
|
||||
'thdt_sll_ccld_frcr_amt': '당일매도체결외화금액',
|
||||
'unit_amt': '단위금액',
|
||||
'std_pdno': '표준상품번호',
|
||||
'prdt_type_cd': '상품유형코드',
|
||||
'loan_rmnd': '대출잔액',
|
||||
'loan_dt': '대출일자',
|
||||
'loan_expd_dt': '대출만기일자',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'item_lnkg_excg_cd': '종목연동거래소코드',
|
||||
'crcy_cd': '통화코드',
|
||||
'frcr_buy_amt_smtl': '외화매수금액합계',
|
||||
'frcr_sll_amt_smtl': '외화매도금액합계',
|
||||
'frcr_dncl_amt_2': '외화예수금액2',
|
||||
'frst_bltn_exrt': '최초고시환율',
|
||||
'frcr_buy_mgn_amt': '외화매수증거금액',
|
||||
'frcr_etc_mgna': '외화기타증거금',
|
||||
'frcr_drwg_psbl_amt_1': '외화출금가능금액1',
|
||||
'frcr_evlu_amt2': '출금가능원화금액',
|
||||
'acpl_cstd_crcy_yn': '현지보관통화여부',
|
||||
'nxdy_frcr_drwg_psbl_amt': '익일외화출금가능금액',
|
||||
'output3': '응답상세3',
|
||||
'pchs_amt_smtl': '매입금액합계',
|
||||
'evlu_amt_smtl': '평가금액합계',
|
||||
'evlu_pfls_amt_smtl': '평가손익금액합계',
|
||||
'dncl_amt': '예수금액',
|
||||
'cma_evlu_amt': 'CMA평가금액',
|
||||
'tot_dncl_amt': '총예수금액',
|
||||
'etc_mgna': '기타증거금',
|
||||
'wdrw_psbl_tot_amt': '인출가능총금액',
|
||||
'frcr_evlu_tota': '외화평가총액',
|
||||
'evlu_erng_rt1': '평가수익율1',
|
||||
'pchs_amt_smtl_amt': '매입금액합계금액',
|
||||
'evlu_amt_smtl_amt': '평가금액합계금액',
|
||||
'tot_evlu_pfls_amt': '총평가손익금액',
|
||||
'tot_asst_amt': '총자산금액',
|
||||
'buy_mgn_amt': '매수증거금액',
|
||||
'mgna_tota': '증거금총액',
|
||||
'frcr_use_psbl_amt': '외화사용가능금액',
|
||||
'ustl_sll_amt_smtl': '미결제매도금액합계',
|
||||
'ustl_buy_amt_smtl': '미결제매수금액합계',
|
||||
'tot_frcr_cblc_smtl': '총외화잔고합계',
|
||||
'tot_loan_amt': '총대출금액'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = [
|
||||
'cblc_qty13', 'thdt_buy_ccld_qty1', 'thdt_sll_ccld_qty1', 'ccld_qty_smtl1', 'ord_psbl_qty1',
|
||||
'frcr_pchs_amt', 'frcr_evlu_amt2', 'evlu_pfls_amt2', 'evlu_pfls_rt1', 'bass_exrt',
|
||||
'ovrs_now_pric1', 'avg_unpr3', 'pchs_rmnd_wcrc_amt', 'thdt_buy_ccld_frcr_amt', 'thdt_sll_ccld_frcr_amt',
|
||||
'unit_amt', 'loan_rmnd', 'frcr_buy_amt_smtl', 'frcr_sll_amt_smtl', 'frcr_dncl_amt_2',
|
||||
'frst_bltn_exrt', 'frcr_buy_mgn_amt', 'frcr_etc_mgna', 'frcr_drwg_psbl_amt_1', 'frcr_evlu_amt2',
|
||||
'nxdy_frcr_drwg_psbl_amt', 'pchs_amt_smtl', 'evlu_amt_smtl', 'evlu_pfls_amt_smtl', 'dncl_amt',
|
||||
'cma_evlu_amt', 'tot_dncl_amt', 'etc_mgna', 'wdrw_psbl_tot_amt', 'frcr_evlu_tota',
|
||||
'evlu_erng_rt1', 'pchs_amt_smtl_amt', 'evlu_amt_smtl_amt', 'tot_evlu_pfls_amt', 'tot_asst_amt',
|
||||
'buy_mgn_amt', 'mgna_tota', 'frcr_use_psbl_amt', 'ustl_sll_amt_smtl', 'ustl_buy_amt_smtl',
|
||||
'tot_frcr_cblc_smtl', 'tot_loan_amt'
|
||||
]
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 체결기준현재잔고[v1_해외주식-008]
|
||||
|
||||
해외주식 체결기준현재잔고 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- wcrc_frcr_dvsn_cd (str): 원화외화구분코드 (01 : 원화 02 : 외화)
|
||||
- natn_cd (str): 국가코드 (000 전체 840 미국 344 홍콩 156 중국 392 일본 704 베트남)
|
||||
- tr_mket_cd (str): 거래시장코드 ([Request body NATN_CD 000 설정] 00 : 전체 [Request body NATN_CD 840 설정] 00 : 전체 01 : 나스닥(NASD) 02 : 뉴욕거래소(NYSE) 03 : 미국(PINK SHEETS) 04 : 미국(OTCBB) 05 : 아멕스(AMEX) [Request body NATN_CD 156 설정] 00 : 전체 01 : 상해B 02 : 심천B 03 : 상해A 04 : 심천A [Request body NATN_CD 392 설정] 01 : 일본 [Request body NATN_CD 704 설정] 01 : 하노이거래 02 : 호치민거래소 [Request body NATN_CD 344 설정] 01 : 홍콩 02 : 홍콩CNY 03 : 홍콩USD)
|
||||
- inqr_dvsn_cd (str): 조회구분코드 (00 : 전체 01 : 일반해외주식 02 : 미니스탁)
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 체결기준현재잔고 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2, df3 = inquire_present_balance(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, wcrc_frcr_dvsn_cd="02", natn_cd="000", tr_mket_cd="00", inqr_dvsn_cd="00", env_dv="real") # 실전투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2, result3 = inquire_present_balance(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
wcrc_frcr_dvsn_cd="02", # 원화외화구분코드
|
||||
natn_cd="000", # 국가코드
|
||||
tr_mket_cd="00", # 거래시장코드
|
||||
inqr_dvsn_cd="00", # 조회구분코드
|
||||
env_dv=env_dv, # 실전모의구분
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
results = [result1, result2, result3]
|
||||
if all(result is None or result.empty for result in results):
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
|
||||
# output1 결과 처리
|
||||
logger.info("=== output1 조회 ===")
|
||||
if not result1.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("output1 결과:")
|
||||
print(result1)
|
||||
else:
|
||||
logger.info("output1 데이터가 없습니다.")
|
||||
|
||||
# output2 결과 처리
|
||||
logger.info("=== output2 조회 ===")
|
||||
if not result2.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("output2 결과:")
|
||||
print(result2)
|
||||
else:
|
||||
logger.info("output2 데이터가 없습니다.")
|
||||
|
||||
# output3 결과 처리
|
||||
logger.info("=== output3 조회 ===")
|
||||
if not result3.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result3.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result3 = result3.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result3.columns:
|
||||
result3[col] = pd.to_numeric(result3[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("output3 결과:")
|
||||
print(result3)
|
||||
else:
|
||||
logger.info("output3 데이터가 없습니다.")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,215 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 체결기준현재잔고 [v1_해외주식-008]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-present-balance"
|
||||
|
||||
def inquire_present_balance(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
wcrc_frcr_dvsn_cd: str, # 원화외화구분코드
|
||||
natn_cd: str, # 국가코드
|
||||
tr_mket_cd: str, # 거래시장코드
|
||||
inqr_dvsn_cd: str, # 조회구분코드
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 체결기준현재잔고[v1_해외주식-008]
|
||||
해외주식 체결기준현재잔고 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
wcrc_frcr_dvsn_cd (str): 01 : 원화 02 : 외화
|
||||
natn_cd (str): 000 전체 840 미국 344 홍콩 156 중국 392 일본 704 베트남
|
||||
tr_mket_cd (str): [Request body NATN_CD 000 설정] 00 : 전체 [Request body NATN_CD 840 설정] 00 : 전체 01 : 나스닥(NASD) 02 : 뉴욕거래소(NYSE) 03 : 미국(PINK SHEETS) 04 : 미국(OTCBB) 05 : 아멕스(AMEX) [Request body NATN_CD 156 설정] 00 : 전체 01 : 상해B 02 : 심천B 03 : 상해A 04 : 심천A [Request body NATN_CD 392 설정] 01 : 일본 [Request body NATN_CD 704 설정] 01 : 하노이거래 02 : 호치민거래소 [Request body NATN_CD 344 설정] 01 : 홍콩 02 : 홍콩CNY 03 : 홍콩USD
|
||||
inqr_dvsn_cd (str): 00 : 전체 01 : 일반해외주식 02 : 미니스탁
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
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 = inquire_present_balance(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... wcrc_frcr_dvsn_cd="01",
|
||||
... natn_cd="000",
|
||||
... tr_mket_cd="00",
|
||||
... inqr_dvsn_cd="00"
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
>>> print(df3)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '810XXXXX')")
|
||||
raise ValueError("cano is required. (e.g. '810XXXXX')")
|
||||
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 wcrc_frcr_dvsn_cd:
|
||||
logger.error("wcrc_frcr_dvsn_cd is required. (e.g. '01')")
|
||||
raise ValueError("wcrc_frcr_dvsn_cd is required. (e.g. '01')")
|
||||
if not natn_cd:
|
||||
logger.error("natn_cd is required. (e.g. '000')")
|
||||
raise ValueError("natn_cd is required. (e.g. '000')")
|
||||
if not tr_mket_cd:
|
||||
logger.error("tr_mket_cd is required. (e.g. '00')")
|
||||
raise ValueError("tr_mket_cd is required. (e.g. '00')")
|
||||
if not inqr_dvsn_cd:
|
||||
logger.error("inqr_dvsn_cd is required. (e.g. '00')")
|
||||
raise ValueError("inqr_dvsn_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 설정 (모의투자 지원 로직)
|
||||
if env_dv == "real":
|
||||
tr_id = "CTRP6504R" # 실전투자용 TR ID
|
||||
elif env_dv == "demo":
|
||||
tr_id = "VTRP6504R" # 모의투자용 TR ID
|
||||
else:
|
||||
raise ValueError("env_dv can only be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"WCRC_FRCR_DVSN_CD": wcrc_frcr_dvsn_cd,
|
||||
"NATN_CD": natn_cd,
|
||||
"TR_MKET_CD": tr_mket_cd,
|
||||
"INQR_DVSN_CD": inqr_dvsn_cd,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리
|
||||
if hasattr(res.getBody(), 'output1'):
|
||||
output_data = res.getBody().output1
|
||||
if output_data:
|
||||
if isinstance(output_data, list):
|
||||
current_data1 = pd.DataFrame(output_data)
|
||||
else:
|
||||
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:
|
||||
if isinstance(output_data, list):
|
||||
current_data2 = pd.DataFrame(output_data)
|
||||
else:
|
||||
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()
|
||||
|
||||
# output3 처리
|
||||
if hasattr(res.getBody(), 'output3'):
|
||||
output_data = res.getBody().output3
|
||||
if output_data:
|
||||
if isinstance(output_data, list):
|
||||
current_data3 = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data3 = pd.DataFrame([output_data])
|
||||
|
||||
if dataframe3 is not None:
|
||||
dataframe3 = pd.concat([dataframe3, current_data3], ignore_index=True)
|
||||
else:
|
||||
dataframe3 = current_data3
|
||||
else:
|
||||
if dataframe3 is None:
|
||||
dataframe3 = pd.DataFrame()
|
||||
else:
|
||||
if dataframe3 is None:
|
||||
dataframe3 = pd.DataFrame()
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_present_balance(
|
||||
cano=cano,
|
||||
acnt_prdt_cd=acnt_prdt_cd,
|
||||
wcrc_frcr_dvsn_cd=wcrc_frcr_dvsn_cd,
|
||||
natn_cd=natn_cd,
|
||||
tr_mket_cd=tr_mket_cd,
|
||||
inqr_dvsn_cd=inqr_dvsn_cd,
|
||||
env_dv=env_dv,
|
||||
dataframe1=dataframe1,
|
||||
dataframe2=dataframe2,
|
||||
dataframe3=dataframe3,
|
||||
tr_cont="N",
|
||||
depth=depth + 1,
|
||||
max_depth=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)
|
||||
# 이미 수집된 데이터가 있으면 그것을 반환, 없으면 빈 DataFrame 반환
|
||||
if dataframe1 is not None and not dataframe1.empty:
|
||||
logger.info("Returning already collected data due to API error.")
|
||||
return dataframe1, dataframe2 if dataframe2 is not None else pd.DataFrame(), dataframe3 if dataframe3 is not None else pd.DataFrame()
|
||||
else:
|
||||
return pd.DataFrame(), pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,119 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_psamount import inquire_psamount
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 매수가능금액조회 [v1_해외주식-014]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'tr_crcy_cd': '거래통화코드',
|
||||
'ord_psbl_frcr_amt': '주문가능외화금액',
|
||||
'sll_ruse_psbl_amt': '매도재사용가능금액',
|
||||
'ovrs_ord_psbl_amt': '해외주문가능금액',
|
||||
'max_ord_psbl_qty': '최대주문가능수량',
|
||||
'echm_af_ord_psbl_amt': '환전이후주문가능금액',
|
||||
'echm_af_ord_psbl_qty': '환전이후주문가능수량',
|
||||
'ord_psbl_qty': '주문가능수량',
|
||||
'exrt': '환율',
|
||||
'frcr_ord_psbl_amt1': '외화주문가능금액1',
|
||||
'ovrs_max_ord_psbl_qty': '해외최대주문가능수량'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 매수가능금액조회[v1_해외주식-014]
|
||||
|
||||
해외주식 매수가능금액조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (NASD : 나스닥 / NYSE : 뉴욕 / AMEX : 아멕스 SEHK : 홍콩 / SHAA : 중국상해 / SZAA : 중국심천 TKSE : 일본 / HASE : 하노이거래소 / VNSE : 호치민거래소)
|
||||
- ovrs_ord_unpr (str): 해외주문단가 (해외주문단가 (23.8) 정수부분 23자리, 소수부분 8자리)
|
||||
- item_cd (str): 종목코드 (종목코드)
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 매수가능금액조회 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_psamount(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_excg_cd="NASD", ovrs_ord_unpr="1.4", item_cd="QQQ", env_dv="real") # 실전투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = inquire_psamount(
|
||||
cano=trenv.my_acct,
|
||||
acnt_prdt_cd=trenv.my_prod,
|
||||
ovrs_excg_cd="NASD",
|
||||
ovrs_ord_unpr="1.4",
|
||||
item_cd="QQQ",
|
||||
env_dv="real",
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 매수가능금액조회 결과 (%s) ===", "실전투자" if env_dv == "real" else "모의투자")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,147 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 매수가능금액조회 [v1_해외주식-014]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/inquire-psamount"
|
||||
|
||||
def inquire_psamount(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
ovrs_ord_unpr: str, # 해외주문단가
|
||||
item_cd: str, # 종목코드
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 매수가능금액조회[v1_해외주식-014]
|
||||
해외주식 매수가능금액조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_excg_cd (str): NASD : 나스닥 / NYSE : 뉴욕 / AMEX : 아멕스 SEHK : 홍콩 / SHAA : 중국상해 / SZAA : 중국심천 TKSE : 일본 / HASE : 하노이거래소 / VNSE : 호치민거래소
|
||||
ovrs_ord_unpr (str): 해외주문단가 (23.8) 정수부분 23자리, 소수부분 8자리
|
||||
item_cd (str): 종목코드
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 매수가능금액조회 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_psamount(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_excg_cd="NASD",
|
||||
... ovrs_ord_unpr="1.4",
|
||||
... item_cd="QQQ"
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '81019777')")
|
||||
raise ValueError("cano is required. (e.g. '81019777')")
|
||||
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 ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
if not ovrs_ord_unpr:
|
||||
logger.error("ovrs_ord_unpr is required. (e.g. '1.4')")
|
||||
raise ValueError("ovrs_ord_unpr is required. (e.g. '1.4')")
|
||||
if not item_cd:
|
||||
logger.error("item_cd is required. (e.g. 'QQQ')")
|
||||
raise ValueError("item_cd is required. (e.g. 'QQQ')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 설정 (모의투자 지원 로직)
|
||||
if env_dv == "real":
|
||||
tr_id = "TTTS3007R" # 실전투자용 TR ID
|
||||
elif env_dv == "demo":
|
||||
tr_id = "VTTS3007R" # 모의투자용 TR ID
|
||||
else:
|
||||
raise ValueError("env_dv can only be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"OVRS_ORD_UNPR": ovrs_ord_unpr,
|
||||
"ITEM_CD": item_cd,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(api_url=API_URL, ptr_id=tr_id, tr_cont=tr_cont, params=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_psamount(
|
||||
cano=cano,
|
||||
acnt_prdt_cd=acnt_prdt_cd,
|
||||
ovrs_excg_cd=ovrs_excg_cd,
|
||||
ovrs_ord_unpr=ovrs_ord_unpr,
|
||||
item_cd=item_cd,
|
||||
env_dv=env_dv,
|
||||
tr_cont="N",
|
||||
dataframe=dataframe,
|
||||
depth=depth + 1,
|
||||
max_depth=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()
|
||||
@@ -0,0 +1,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-27
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_search import inquire_search
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식조건검색[v1_해외주식-015]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'crec': '현재조회종목수',
|
||||
'trec': '전체조회종목수',
|
||||
'nrec': 'Record Count',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'last': '현재가',
|
||||
'shar': '발행주식',
|
||||
'valx': '시가총액',
|
||||
'plow': '저가',
|
||||
'phigh': '고가',
|
||||
'popen': '시가',
|
||||
'tvol': '거래량',
|
||||
'rate': '등락율',
|
||||
'diff': '대비',
|
||||
'sign': '기호',
|
||||
'avol': '거래대금',
|
||||
'eps': 'EPS',
|
||||
'per': 'PER',
|
||||
'rank': '순위',
|
||||
'e_ordyn': '매매가능'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['소수점자리수', '현재가', '발행주식', '시가총액', '저가', '고가', '시가', '거래량', '등락율', '대비', '기호', '거래대금', 'EPS', 'PER', '순위', '매매가능']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식조건검색[v1_해외주식-015]
|
||||
|
||||
해외주식조건검색 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- auth (str): 사용자권한정보 ("" (Null 값 설정))
|
||||
- excd (str): 거래소코드 (NYS : 뉴욕, NAS : 나스닥, AMS : 아멕스 HKS : 홍콩, SHS : 상해 , SZS : 심천 HSX : 호치민, HNX : 하노이 TSE : 도쿄)
|
||||
- co_yn_pricecur (str): 현재가선택조건 (해당조건 사용시(1), 미사용시 필수항목아님)
|
||||
- co_st_pricecur (str): 현재가시작범위가 (단위: 각국통화(JPY, USD, HKD, CNY, VND))
|
||||
- co_en_pricecur (str): 현재가끝범위가 (단위: 각국통화(JPY, USD, HKD, CNY, VND))
|
||||
- co_yn_rate (str): 등락율선택조건 (해당조건 사용시(1), 미사용시 필수항목아님)
|
||||
- co_st_rate (str): 등락율시작율 (%)
|
||||
- co_en_rate (str): 등락율끝율 (%)
|
||||
- co_yn_valx (str): 시가총액선택조건 (해당조건 사용시(1), 미사용시 필수항목아님)
|
||||
- co_st_valx (str): 시가총액시작액 (단위: 천)
|
||||
- co_en_valx (str): 시가총액끝액 (단위: 천)
|
||||
- co_yn_shar (str): 발행주식수선택조건 (해당조건 사용시(1), 미사용시 필수항목아님)
|
||||
- co_st_shar (str): 발행주식시작수 (단위: 천)
|
||||
- co_en_shar (str): 발행주식끝수 (단위: 천)
|
||||
- co_yn_volume (str): 거래량선택조건 (해당조건 사용시(1), 미사용시 필수항목아님)
|
||||
- co_st_volume (str): 거래량시작량 (단위: 주)
|
||||
- co_en_volume (str): 거래량끝량 (단위: 주)
|
||||
- co_yn_amt (str): 거래대금선택조건 (해당조건 사용시(1), 미사용시 필수항목아님)
|
||||
- co_st_amt (str): 거래대금시작금 (단위: 천)
|
||||
- co_en_amt (str): 거래대금끝금 (단위: 천)
|
||||
- co_yn_eps (str): EPS선택조건 (해당조건 사용시(1), 미사용시 필수항목아님)
|
||||
- co_st_eps (str): EPS시작 ()
|
||||
- co_en_eps (str): EPS끝 ()
|
||||
- co_yn_per (str): PER선택조건 (해당조건 사용시(1), 미사용시 필수항목아님)
|
||||
- co_st_per (str): PER시작 ()
|
||||
- co_en_per (str): PER끝 ()
|
||||
- keyb (str): NEXT KEY BUFF ("" 공백 입력)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식조건검색 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_search(auth="", excd="NYS", co_yn_pricecur="1", co_st_pricecur="100", co_en_pricecur="200", co_yn_rate="1", co_st_rate="5", co_en_rate="10", co_yn_valx="1", co_st_valx="1000", co_en_valx="5000", co_yn_shar="1", co_st_shar="100", co_en_shar="500", co_yn_volume="1", co_st_volume="1000", co_en_volume="5000", co_yn_amt="1", co_st_amt="1000", co_en_amt="5000", co_yn_eps="1", co_st_eps="1", co_en_eps="5", co_yn_per="1", co_st_per="10", co_en_per="20", keyb="")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외주식조건검색")
|
||||
result1, result2 = inquire_search(
|
||||
auth = "",
|
||||
excd = "NAS",
|
||||
co_yn_pricecur = "1",
|
||||
co_st_pricecur = "160",
|
||||
co_en_pricecur = "170",
|
||||
co_yn_rate = "",
|
||||
co_st_rate = "",
|
||||
co_en_rate = "",
|
||||
co_yn_valx = "",
|
||||
co_st_valx = "",
|
||||
co_en_valx = "",
|
||||
co_yn_shar = "",
|
||||
co_st_shar = "",
|
||||
co_en_shar = "",
|
||||
co_yn_volume = "",
|
||||
co_st_volume = "",
|
||||
co_en_volume = "",
|
||||
co_yn_amt = "",
|
||||
co_st_amt = "",
|
||||
co_en_amt = "",
|
||||
co_yn_eps = "",
|
||||
co_st_eps = "",
|
||||
co_en_eps = "",
|
||||
co_yn_per = "",
|
||||
co_st_per = "",
|
||||
co_en_per = "",
|
||||
keyb = "",
|
||||
)
|
||||
# 결과 확인
|
||||
results = [result1, result2]
|
||||
if all(result is None or result.empty for result in results):
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
|
||||
# output1 결과 처리
|
||||
logger.info("=== output1 조회 ===")
|
||||
if not result1.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
logger.info("output1 결과:")
|
||||
print(result1)
|
||||
else:
|
||||
logger.info("output1 데이터가 없습니다.")
|
||||
|
||||
# output2 결과 처리
|
||||
logger.info("=== output2 조회 ===")
|
||||
if not result2.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
logger.info("output2 결과:")
|
||||
print(result2)
|
||||
else:
|
||||
logger.info("output2 데이터가 없습니다.")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,243 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-27
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식조건검색[v1_해외주식-015]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/inquire-search"
|
||||
|
||||
def inquire_search(
|
||||
auth: str, # 사용자권한정보
|
||||
excd: str, # 거래소코드
|
||||
co_yn_pricecur: str, # 현재가선택조건
|
||||
co_st_pricecur: str, # 현재가시작범위가
|
||||
co_en_pricecur: str, # 현재가끝범위가
|
||||
co_yn_rate: str, # 등락율선택조건
|
||||
co_st_rate: str, # 등락율시작율
|
||||
co_en_rate: str, # 등락율끝율
|
||||
co_yn_valx: str, # 시가총액선택조건
|
||||
co_st_valx: str, # 시가총액시작액
|
||||
co_en_valx: str, # 시가총액끝액
|
||||
co_yn_shar: str, # 발행주식수선택조건
|
||||
co_st_shar: str, # 발행주식시작수
|
||||
co_en_shar: str, # 발행주식끝수
|
||||
co_yn_volume: str, # 거래량선택조건
|
||||
co_st_volume: str, # 거래량시작량
|
||||
co_en_volume: str, # 거래량끝량
|
||||
co_yn_amt: str, # 거래대금선택조건
|
||||
co_st_amt: str, # 거래대금시작금
|
||||
co_en_amt: str, # 거래대금끝금
|
||||
co_yn_eps: str, # EPS선택조건
|
||||
co_st_eps: str, # EPS시작
|
||||
co_en_eps: str, # EPS끝
|
||||
co_yn_per: str, # PER선택조건
|
||||
co_st_per: str, # PER시작
|
||||
co_en_per: str, # PER끝
|
||||
keyb: str, # NEXT KEY BUFF
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output)
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output1)
|
||||
tr_cont: str = "",
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식조건검색[v1_해외주식-015]
|
||||
해외주식조건검색 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
auth (str): "" (Null 값 설정)
|
||||
excd (str): NYS : 뉴욕, NAS : 나스닥, AMS : 아멕스 HKS : 홍콩, SHS : 상해 , SZS : 심천 HSX : 호치민, HNX : 하노이 TSE : 도쿄
|
||||
co_yn_pricecur (str): 해당조건 사용시(1), 미사용시 필수항목아님
|
||||
co_st_pricecur (str): 단위: 각국통화(JPY, USD, HKD, CNY, VND)
|
||||
co_en_pricecur (str): 단위: 각국통화(JPY, USD, HKD, CNY, VND)
|
||||
co_yn_rate (str): 해당조건 사용시(1), 미사용시 필수항목아님
|
||||
co_st_rate (str): %
|
||||
co_en_rate (str): %
|
||||
co_yn_valx (str): 해당조건 사용시(1), 미사용시 필수항목아님
|
||||
co_st_valx (str): 단위: 천
|
||||
co_en_valx (str): 단위: 천
|
||||
co_yn_shar (str): 해당조건 사용시(1), 미사용시 필수항목아님
|
||||
co_st_shar (str): 단위: 천
|
||||
co_en_shar (str): 단위: 천
|
||||
co_yn_volume (str): 해당조건 사용시(1), 미사용시 필수항목아님
|
||||
co_st_volume (str): 단위: 주
|
||||
co_en_volume (str): 단위: 주
|
||||
co_yn_amt (str): 해당조건 사용시(1), 미사용시 필수항목아님
|
||||
co_st_amt (str): 단위: 천
|
||||
co_en_amt (str): 단위: 천
|
||||
co_yn_eps (str): 해당조건 사용시(1), 미사용시 필수항목아님
|
||||
co_st_eps (str):
|
||||
co_en_eps (str):
|
||||
co_yn_per (str): 해당조건 사용시(1), 미사용시 필수항목아님
|
||||
co_st_per (str):
|
||||
co_en_per (str):
|
||||
keyb (str): "" 공백 입력
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임 (output)
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임 (output1)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: 해외주식조건검색 데이터
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_search(
|
||||
... auth="", excd="NAS", co_yn_pricecur="1", co_st_pricecur="160", co_en_pricecur="161",
|
||||
... co_yn_rate="", co_st_rate="", co_en_rate="", co_yn_valx="", co_st_valx="", co_en_valx="",
|
||||
... co_yn_shar="", co_st_shar="", co_en_shar="", co_yn_volume="", co_st_volume="", co_en_volume="",
|
||||
... co_yn_amt="", co_st_amt="", co_en_amt="", co_yn_eps="", co_st_eps="", co_en_eps="",
|
||||
... co_yn_per="", co_st_per="", co_en_per="", keyb=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not excd:
|
||||
logger.error("excd is required. (e.g. 'NAS')")
|
||||
raise ValueError("excd is required. (e.g. 'NAS')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFS76410000"
|
||||
|
||||
params = {
|
||||
"AUTH": auth,
|
||||
"EXCD": excd,
|
||||
"CO_YN_PRICECUR": co_yn_pricecur,
|
||||
"CO_ST_PRICECUR": co_st_pricecur,
|
||||
"CO_EN_PRICECUR": co_en_pricecur,
|
||||
"CO_YN_RATE": co_yn_rate,
|
||||
"CO_ST_RATE": co_st_rate,
|
||||
"CO_EN_RATE": co_en_rate,
|
||||
"CO_YN_VALX": co_yn_valx,
|
||||
"CO_ST_VALX": co_st_valx,
|
||||
"CO_EN_VALX": co_en_valx,
|
||||
"CO_YN_SHAR": co_yn_shar,
|
||||
"CO_ST_SHAR": co_st_shar,
|
||||
"CO_EN_SHAR": co_en_shar,
|
||||
"CO_YN_VOLUME": co_yn_volume,
|
||||
"CO_ST_VOLUME": co_st_volume,
|
||||
"CO_EN_VOLUME": co_en_volume,
|
||||
"CO_YN_AMT": co_yn_amt,
|
||||
"CO_ST_AMT": co_st_amt,
|
||||
"CO_EN_AMT": co_en_amt,
|
||||
"CO_YN_EPS": co_yn_eps,
|
||||
"CO_ST_EPS": co_st_eps,
|
||||
"CO_EN_EPS": co_en_eps,
|
||||
"CO_YN_PER": co_yn_per,
|
||||
"CO_ST_PER": co_st_per,
|
||||
"CO_EN_PER": co_en_per,
|
||||
"KEYB": keyb,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_search(
|
||||
auth,
|
||||
excd,
|
||||
co_yn_pricecur,
|
||||
co_st_pricecur,
|
||||
co_en_pricecur,
|
||||
co_yn_rate,
|
||||
co_st_rate,
|
||||
co_en_rate,
|
||||
co_yn_valx,
|
||||
co_st_valx,
|
||||
co_en_valx,
|
||||
co_yn_shar,
|
||||
co_st_shar,
|
||||
co_en_shar,
|
||||
co_yn_volume,
|
||||
co_st_volume,
|
||||
co_en_volume,
|
||||
co_yn_amt,
|
||||
co_st_amt,
|
||||
co_en_amt,
|
||||
co_yn_eps,
|
||||
co_st_eps,
|
||||
co_en_eps,
|
||||
co_yn_per,
|
||||
co_st_per,
|
||||
co_en_per,
|
||||
keyb,
|
||||
"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()
|
||||
@@ -0,0 +1,137 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_time_indexchartprice import inquire_time_indexchartprice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외지수분봉조회[v1_해외주식-031]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'ovrs_nmix_prdy_vrss': '해외 지수 전일 대비',
|
||||
'prdy_vrss_sign': '전일 대비 부호',
|
||||
'hts_kor_isnm': 'HTS 한글 종목명',
|
||||
'prdy_ctrt': '전일 대비율',
|
||||
'ovrs_nmix_prdy_clpr': '해외 지수 전일 종가',
|
||||
'acml_vol': '누적 거래량',
|
||||
'ovrs_nmix_prpr': '해외 지수 현재가',
|
||||
'stck_shrn_iscd': '주식 단축 종목코드',
|
||||
'ovrs_prod_oprc': '해외 상품 시가2',
|
||||
'ovrs_prod_hgpr': '해외 상품 최고가',
|
||||
'ovrs_prod_lwpr': '해외 상품 최저가',
|
||||
'stck_bsop_date': '주식 영업 일자',
|
||||
'stck_cntg_hour': '주식 체결 시간',
|
||||
'optn_prpr': '옵션 현재가',
|
||||
'optn_oprc': '옵션 시가2',
|
||||
'optn_hgpr': '옵션 최고가',
|
||||
'optn_lwpr': '옵션 최저가',
|
||||
'cntg_vol': '체결 거래량'
|
||||
}
|
||||
NUMERIC_COLUMNS = ['해외 지수 전일 대비', '전일 대비율', '해외 지수 전일 종가', '누적 거래량', '해외 지수 현재가', '해외 상품 시가2', '해외 상품 최고가', '해외 상품 최저가',
|
||||
'체결 거래량', '옵션 현재가', '옵션 시가2', '옵션 최고가', '옵션 최저가']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외지수분봉조회[v1_해외주식-031]
|
||||
|
||||
해외지수분봉조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- fid_cond_mrkt_div_code (str): 조건 시장 분류 코드 (N 해외지수 X 환율 KX 원화환율)
|
||||
- fid_input_iscd (str): 입력 종목코드 (종목번호(ex. TSLA))
|
||||
- fid_hour_cls_code (str): 시간 구분 코드 (0: 정규장, 1: 시간외)
|
||||
- fid_pw_data_incu_yn (str): 과거 데이터 포함 여부 (Y/N)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외지수분봉조회 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_time_indexchartprice(fid_cond_mrkt_div_code="N", fid_input_iscd="TSLA", fid_hour_cls_code="0", fid_pw_data_incu_yn="Y")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
# 해외지수분봉조회 파라미터 설정
|
||||
logger.info("API 파라미터 설정 중...")
|
||||
fid_cond_mrkt_div_code = "N" # 조건 시장 분류 코드
|
||||
fid_input_iscd = "SPX" # 입력 종목코드
|
||||
fid_hour_cls_code = "0" # 시간 구분 코드
|
||||
fid_pw_data_incu_yn = "Y" # 과거 데이터 포함 여부
|
||||
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외지수분봉조회")
|
||||
result1, result2 = inquire_time_indexchartprice(
|
||||
fid_cond_mrkt_div_code = "N",
|
||||
fid_input_iscd = "SPX",
|
||||
fid_hour_cls_code = "0",
|
||||
fid_pw_data_incu_yn = "Y",
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
results = [result1, result2]
|
||||
if all(result is None or result.empty for result in results):
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
|
||||
# output1 결과 처리
|
||||
logger.info("=== output1 조회 ===")
|
||||
if not result1.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
logger.info("output1 결과:")
|
||||
print(result1)
|
||||
else:
|
||||
logger.info("output1 데이터가 없습니다.")
|
||||
|
||||
# output2 결과 처리
|
||||
logger.info("=== output2 조회 ===")
|
||||
if not result2.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
logger.info("output2 결과:")
|
||||
print(result2)
|
||||
else:
|
||||
logger.info("output2 데이터가 없습니다.")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,157 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외지수분봉조회[v1_해외주식-031]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/inquire-time-indexchartprice"
|
||||
|
||||
def inquire_time_indexchartprice(
|
||||
fid_cond_mrkt_div_code: str, # 조건 시장 분류 코드
|
||||
fid_input_iscd: str, # 입력 종목코드
|
||||
fid_hour_cls_code: str, # 시간 구분 코드
|
||||
fid_pw_data_incu_yn: str, # 과거 데이터 포함 여부
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외지수분봉조회[v1_해외주식-031]
|
||||
해외지수분봉조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
fid_cond_mrkt_div_code (str): N 해외지수 X 환율 KX 원화환율
|
||||
fid_input_iscd (str): 종목번호(ex. TSLA)
|
||||
fid_hour_cls_code (str): 0: 정규장, 1: 시간외
|
||||
fid_pw_data_incu_yn (str): Y/N
|
||||
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_time_indexchartprice(
|
||||
... fid_cond_mrkt_div_code="N",
|
||||
... fid_input_iscd="SPX",
|
||||
... fid_hour_cls_code="0",
|
||||
... fid_pw_data_incu_yn="Y"
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not fid_cond_mrkt_div_code:
|
||||
logger.error("fid_cond_mrkt_div_code is required. (e.g. 'N')")
|
||||
raise ValueError("fid_cond_mrkt_div_code is required. (e.g. 'N')")
|
||||
if not fid_input_iscd:
|
||||
logger.error("fid_input_iscd is required. (e.g. 'SPX')")
|
||||
raise ValueError("fid_input_iscd is required. (e.g. 'SPX')")
|
||||
if not fid_hour_cls_code:
|
||||
logger.error("fid_hour_cls_code is required. (e.g. '0')")
|
||||
raise ValueError("fid_hour_cls_code is required. (e.g. '0')")
|
||||
if not fid_pw_data_incu_yn:
|
||||
logger.error("fid_pw_data_incu_yn is required. (e.g. 'Y')")
|
||||
raise ValueError("fid_pw_data_incu_yn is required. (e.g. 'Y')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "FHKST03030200"
|
||||
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code,
|
||||
"FID_INPUT_ISCD": fid_input_iscd,
|
||||
"FID_HOUR_CLS_CODE": fid_hour_cls_code,
|
||||
"FID_PW_DATA_INCU_YN": fid_pw_data_incu_yn,
|
||||
}
|
||||
|
||||
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:
|
||||
if isinstance(output_data, list):
|
||||
current_data1 = pd.DataFrame(output_data)
|
||||
else:
|
||||
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:
|
||||
if isinstance(output_data, list):
|
||||
current_data2 = pd.DataFrame(output_data)
|
||||
else:
|
||||
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
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_time_indexchartprice(
|
||||
fid_cond_mrkt_div_code,
|
||||
fid_input_iscd,
|
||||
fid_hour_cls_code,
|
||||
fid_pw_data_incu_yn,
|
||||
"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()
|
||||
@@ -0,0 +1,143 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_time_itemchartprice import inquire_time_itemchartprice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식분봉조회[v1_해외주식-030]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'rsym': '실시간종목코드',
|
||||
'zdiv': '소수점자리수',
|
||||
'stim': '장시작현지시간',
|
||||
'etim': '장종료현지시간',
|
||||
'sktm': '장시작한국시간',
|
||||
'ektm': '장종료한국시간',
|
||||
'next': '다음가능여부',
|
||||
'more': '추가데이타여부',
|
||||
'nrec': '레코드갯수',
|
||||
'tymd': '현지영업일자',
|
||||
'xymd': '현지기준일자',
|
||||
'xhms': '현지기준시간',
|
||||
'kymd': '한국기준일자',
|
||||
'khms': '한국기준시간',
|
||||
'open': '시가',
|
||||
'high': '고가',
|
||||
'low': '저가',
|
||||
'last': '종가',
|
||||
'evol': '체결량',
|
||||
'eamt': '체결대금'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['소수점자리수', '시가', '고가', '저가', '종가', '체결량', '체결대금']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식분봉조회[v1_해외주식-030]
|
||||
|
||||
해외주식분봉조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- auth (str): 사용자권한정보 ("" 공백으로 입력)
|
||||
- excd (str): 거래소코드 (NYS : 뉴욕 NAS : 나스닥 AMS : 아멕스 HKS : 홍콩 SHS : 상해 SZS : 심천 HSX : 호치민 HNX : 하노이 TSE : 도쿄 ※ 주간거래는 최대 1일치 분봉만 조회 가능 BAY : 뉴욕(주간) BAQ : 나스닥(주간) BAA : 아멕스(주간))
|
||||
- symb (str): 종목코드 (종목코드(ex. TSLA))
|
||||
- nmin (str): 분갭 (분단위(1: 1분봉, 2: 2분봉, ...))
|
||||
- pinc (str): 전일포함여부 (0:당일 1:전일포함 ※ 다음조회 시 반드시 "1"로 입력)
|
||||
- next (str): 다음여부 (처음조회 시, "" 공백 입력 다음조회 시, "1" 입력)
|
||||
- nrec (str): 요청갯수 (레코드요청갯수 (최대 120))
|
||||
- fill (str): 미체결채움구분 ("" 공백으로 입력)
|
||||
- keyb (str): NEXT KEY BUFF (처음 조회 시, "" 공백 입력 다음 조회 시, 이전 조회 결과의 마지막 분봉 데이터를 이용하여, 1분 전 혹은 n분 전의 시간을 입력 (형식: YYYYMMDDHHMMSS, ex. 20241014140100))
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식분봉조회 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_time_itemchartprice(auth="", excd="NAS", symb="TSLA", nmin="5", pinc="1", next="", nrec="120", fill="", keyb="")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외주식분봉조회")
|
||||
result1, result2 = inquire_time_itemchartprice(
|
||||
auth="",
|
||||
excd="NAS",
|
||||
symb="TSLA",
|
||||
nmin="5",
|
||||
pinc="1",
|
||||
next="",
|
||||
nrec="120",
|
||||
fill="",
|
||||
keyb="",
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
results = [result1, result2]
|
||||
if all(result is None or result.empty for result in results):
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
|
||||
# output1 결과 처리
|
||||
logger.info("=== output1 조회 ===")
|
||||
if not result1.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
logger.info("output1 결과:")
|
||||
print(result1)
|
||||
else:
|
||||
logger.info("output1 데이터가 없습니다.")
|
||||
|
||||
# output2 결과 처리
|
||||
logger.info("=== output2 조회 ===")
|
||||
if not result2.empty:
|
||||
logger.info("사용 가능한 컬럼: %s", result2.columns.tolist())
|
||||
|
||||
# 통합 컬럼명 한글 변환 (필요한 컬럼만 자동 매핑됨)
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
logger.info("output2 결과:")
|
||||
print(result2)
|
||||
else:
|
||||
logger.info("output2 데이터가 없습니다.")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,177 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식분봉조회[v1_해외주식-030]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/inquire-time-itemchartprice"
|
||||
|
||||
def inquire_time_itemchartprice(
|
||||
auth: str, # 사용자권한정보
|
||||
excd: str, # 거래소코드
|
||||
symb: str, # 종목코드
|
||||
nmin: str, # 분갭
|
||||
pinc: str, # 전일포함여부
|
||||
next: str, # 다음여부
|
||||
nrec: str, # 요청갯수
|
||||
fill: str, # 미체결채움구분
|
||||
keyb: str, # NEXT KEY BUFF
|
||||
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]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식분봉조회[v1_해외주식-030]
|
||||
해외주식분봉조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
auth (str): "" 공백으로 입력
|
||||
excd (str): NYS : 뉴욕 NAS : 나스닥 AMS : 아멕스 HKS : 홍콩 SHS : 상해 SZS : 심천 HSX : 호치민 HNX : 하노이 TSE : 도쿄 ※ 주간거래는 최대 1일치 분봉만 조회 가능 BAY : 뉴욕(주간) BAQ : 나스닥(주간) BAA : 아멕스(주간)
|
||||
symb (str): 종목코드(ex. TSLA)
|
||||
nmin (str): 분단위(1: 1분봉, 2: 2분봉, ...)
|
||||
pinc (str): 0:당일 1:전일포함 ※ 다음조회 시 반드시 "1"로 입력
|
||||
next (str): 처음조회 시, "" 공백 입력 다음조회 시, "1" 입력
|
||||
nrec (str): 레코드요청갯수 (최대 120)
|
||||
fill (str): "" 공백으로 입력
|
||||
keyb (str): 처음 조회 시, "" 공백 입력 다음 조회 시, 이전 조회 결과의 마지막 분봉 데이터를 이용하여, 1분 전 혹은 n분 전의 시간을 입력 (형식: YYYYMMDDHHMMSS, ex. 20241014140100)
|
||||
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_time_itemchartprice(
|
||||
... auth="", excd="NAS", symb="TSLA", nmin="5", pinc="1", next="1", nrec="120", fill="", keyb=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not excd:
|
||||
logger.error("excd is required. (e.g. 'NAS')")
|
||||
raise ValueError("excd is required. (e.g. 'NAS')")
|
||||
if not symb:
|
||||
logger.error("symb is required. (e.g. 'TSLA')")
|
||||
raise ValueError("symb is required. (e.g. 'TSLA')")
|
||||
if not nmin:
|
||||
logger.error("nmin is required. (e.g. '5')")
|
||||
raise ValueError("nmin is required. (e.g. '5')")
|
||||
if not pinc:
|
||||
logger.error("pinc is required. (e.g. '1')")
|
||||
raise ValueError("pinc is required. (e.g. '1')")
|
||||
if not nrec or int(nrec)>120:
|
||||
logger.error("nrec is required. (e.g. '120', 최대120개)")
|
||||
raise ValueError("nrec is required. (e.g. '120', 최대120개)")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFS76950200"
|
||||
|
||||
params = {
|
||||
"AUTH": auth,
|
||||
"EXCD": excd,
|
||||
"SYMB": symb,
|
||||
"NMIN": nmin,
|
||||
"PINC": pinc,
|
||||
"NEXT": next,
|
||||
"NREC": nrec,
|
||||
"FILL": fill,
|
||||
"KEYB": keyb,
|
||||
}
|
||||
|
||||
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:
|
||||
if isinstance(output_data, list):
|
||||
current_data1 = pd.DataFrame(output_data)
|
||||
else:
|
||||
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:
|
||||
if isinstance(output_data, list):
|
||||
current_data2 = pd.DataFrame(output_data)
|
||||
else:
|
||||
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
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_time_itemchartprice(
|
||||
auth,
|
||||
excd,
|
||||
symb,
|
||||
nmin,
|
||||
pinc,
|
||||
next,
|
||||
nrec,
|
||||
fill,
|
||||
keyb,
|
||||
"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()
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Created on 20250114
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from market_cap import market_cap
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 시가총액순위[해외주식-047]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'crec': '현재조회종목수',
|
||||
'trec': '전체조회종목수',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'shar': '상장주식수',
|
||||
'tomv': '시가총액',
|
||||
'grav': '비중',
|
||||
'rank': '순위',
|
||||
'ename': '영문종목명',
|
||||
'e_ordyn': '매매가능',
|
||||
'tamt': '거래대금',
|
||||
'mcap': '시가총액'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 시가총액순위 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 시가총액순위 API를 호출하여 결과를 출력합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = market_cap(excd="SZS", vol_rang="1")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("사용 가능한 컬럼(output1): %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("사용 가능한 컬럼(output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
112
한국투자증권(API)/examples_llm/overseas_stock/market_cap/market_cap.py
Normal file
112
한국투자증권(API)/examples_llm/overseas_stock/market_cap/market_cap.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
Created on 20250114
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 시가총액순위[해외주식-047]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/market-cap"
|
||||
|
||||
def market_cap(
|
||||
excd: str, # 거래소명
|
||||
vol_rang: str, # 거래량조건
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
auth: str = "", # 사용자권한정보
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임 output1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임 output2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
해외주식 시가총액순위 조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
keyb (str): NEXT KEY BUFF (ex. "")
|
||||
auth (str): 사용자권한정보 (ex. "")
|
||||
tr_cont (str): 연속거래여부 (ex. "")
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임 output1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임 output2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: 시가총액순위 데이터 (output1, output2)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = market_cap(excd="SZS", vol_rang="1")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None or dataframe2 is None:
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
else:
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76350100" # 해외주식 시가총액순위
|
||||
|
||||
params = {
|
||||
"EXCD": excd, # 거래소명
|
||||
"VOL_RANG": vol_rang, # 거래량조건
|
||||
"KEYB": keyb, # NEXT KEY BUFF
|
||||
"AUTH": auth, # 사용자권한정보
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리
|
||||
current_data1 = pd.DataFrame(res.getBody().output1, index=[0])
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return market_cap(
|
||||
excd, vol_rang, keyb, auth, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
Created on 20250112
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from new_highlow import new_highlow
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 신고/신저가[해외주식-042]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'n_base': '기준가',
|
||||
'n_diff': '기준가대비',
|
||||
'n_rate': '기준가대비율',
|
||||
'ename': '영문종목명',
|
||||
'e_ordyn': '매매가능',
|
||||
'tamt': '거래대금',
|
||||
'nhgh': '신고가',
|
||||
'nlow': '신저가',
|
||||
'rank': '순위'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 신고/신저가 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 신고/신저가 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 AMS 거래소의 신고가 정보를 조회합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = new_highlow(excd="AMS", mixn="0", vol_rang="0", gubn="1", gubn2="1")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("사용 가능한 컬럼 (output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,134 @@
|
||||
"""
|
||||
Created on 20250112
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 신고/신저가[해외주식-042]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/new-highlow"
|
||||
|
||||
def new_highlow(
|
||||
excd: str, # [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
mixn: str, # [필수] N분전콤보값 (ex. 0:1분전, 1:2분전, 2:3분전, 3:5분전, 4:10분전, 5:15분전, 6:20분전, 7:30분전, 8:60분전, 9:120분전)
|
||||
vol_rang: str, # [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
gubn: str, # [필수] 신고/신저 구분 (ex. 0:신저,1:신고)
|
||||
gubn2: str, # [필수] 일시돌파/돌파 구분 (ex. 0:일시돌파0, 1:돌파유지1)
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
auth: str = "", # 사용자권한정보
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임 output1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임 output2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 시세분석 > 해외주식 신고/신저가[해외주식-042]
|
||||
해외주식 신고/신저가 정보를 조회하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
mixn (str): [필수] N분전콤보값 (ex. 0:1분전, 1:2분전, 2:3분전, 3:5분전, 4:10분전, 5:15분전, 6:20분전, 7:30분전, 8:60분전, 9:120분전)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
gubn (str): [필수] 신고/신저 구분 (ex. 0:신저,1:신고)
|
||||
gubn2 (str): [필수] 일시돌파/돌파 구분 (ex. 0:일시돌파0, 1:돌파유지1)
|
||||
keyb (str): NEXT KEY BUFF
|
||||
auth (str): 사용자권한정보
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임 output1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임 output2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1 데이터, output2 데이터)
|
||||
|
||||
Example:
|
||||
>>> output1, output2 = new_highlow(excd="AMS", mixn="0", vol_rang="0", gubn="1", gubn2="1")
|
||||
>>> print(output1)
|
||||
>>> print(output2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS')")
|
||||
|
||||
if mixn == "":
|
||||
raise ValueError("mixn is required (e.g. '0')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0')")
|
||||
|
||||
if gubn == "":
|
||||
raise ValueError("gubn is required (e.g. '1')")
|
||||
|
||||
if gubn2 == "":
|
||||
raise ValueError("gubn2 is required (e.g. '1')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76300000" # 해외주식 신고/신저가
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"MIXN": mixn,
|
||||
"VOL_RANG": vol_rang,
|
||||
"GUBN": gubn,
|
||||
"GUBN2": gubn2,
|
||||
"KEYB": keyb,
|
||||
"AUTH": auth
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리 (object 타입)
|
||||
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 처리 (array 타입)
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return new_highlow(
|
||||
excd, mixn, vol_rang, gubn, gubn2, keyb, auth, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from news_title import news_title
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외뉴스종합(제목) [해외주식-053]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'info_gb': '뉴스구분',
|
||||
'news_key': '뉴스키',
|
||||
'data_dt': '조회일자',
|
||||
'data_tm': '조회시간',
|
||||
'class_cd': '중분류',
|
||||
'class_name': '중분류명',
|
||||
'source': '자료원',
|
||||
'nation_cd': '국가코드',
|
||||
'exchange_cd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'symb_name': '종목명',
|
||||
'title': '제목'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외뉴스종합 테스트 함수
|
||||
"""
|
||||
try:
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = news_title(
|
||||
info_gb="",
|
||||
class_cd="",
|
||||
nation_cd="",
|
||||
exchange_cd="",
|
||||
symb="",
|
||||
data_dt="",
|
||||
data_tm="",
|
||||
cts=""
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외뉴스종합 결과 ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
110
한국투자증권(API)/examples_llm/overseas_stock/news_title/news_title.py
Normal file
110
한국투자증권(API)/examples_llm/overseas_stock/news_title/news_title.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외뉴스종합(제목) [해외주식-053]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/news-title"
|
||||
|
||||
def news_title(
|
||||
info_gb: str = "", # [필수] 뉴스구분
|
||||
class_cd: str = "", # [필수] 중분류
|
||||
nation_cd: str = "", # [필수] 국가코드 (ex. 공백:전체, CN:중국, HK:홍콩, US:미국)
|
||||
exchange_cd: str = "", # [필수] 거래소코드
|
||||
symb: str = "", # [필수] 종목코드
|
||||
data_dt: str = "", # [필수] 조회일자
|
||||
data_tm: str = "", # [필수] 조회시간
|
||||
cts: str = "", # [필수] 다음키
|
||||
tr_cont: str = "", # [필수] 연속거래여부
|
||||
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외뉴스종합(제목) API입니다.
|
||||
한국투자 HTS(eFriend Plus) > [7702] 해외뉴스종합 화면의 "우측 상단 뉴스목록" 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
|
||||
|
||||
Args:
|
||||
info_gb (str): [필수] 뉴스구분
|
||||
class_cd (str): [필수] 중분류
|
||||
nation_cd (str): [필수] 국가코드 (ex. 공백:전체, CN:중국, HK:홍콩, US:미국)
|
||||
exchange_cd (str): [필수] 거래소코드
|
||||
symb (str): [필수] 종목코드
|
||||
data_dt (str): [필수] 조회일자
|
||||
data_tm (str): [필수] 조회시간
|
||||
cts (str): [필수] 다음키
|
||||
tr_cont (str): [필수] 연속거래여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외뉴스종합(제목) 데이터
|
||||
|
||||
Example:
|
||||
>>> df = news_title()
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe is None:
|
||||
return pd.DataFrame()
|
||||
else:
|
||||
return dataframe
|
||||
|
||||
tr_id = "HHPSTH60100C1" # 해외뉴스종합(제목)
|
||||
|
||||
params = {
|
||||
"INFO_GB": info_gb, # 뉴스구분
|
||||
"CLASS_CD": class_cd, # 중분류
|
||||
"NATION_CD": nation_cd, # 국가코드
|
||||
"EXCHANGE_CD": exchange_cd, # 거래소코드
|
||||
"SYMB": symb, # 종목코드
|
||||
"DATA_DT": data_dt, # 조회일자
|
||||
"DATA_TM": data_tm, # 조회시간
|
||||
"CTS": cts # 다음키
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().outblock1)
|
||||
|
||||
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 in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return news_title(
|
||||
info_gb, class_cd, nation_cd, exchange_cd, symb, data_dt, data_tm, cts, "N", dataframe, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
122
한국투자증권(API)/examples_llm/overseas_stock/order/chk_order.py
Normal file
122
한국투자증권(API)/examples_llm/overseas_stock/order/chk_order.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from order import order
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 주문 [v1_해외주식-001]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'KRX_FWDG_ORD_ORGNO': '한국거래소전송주문조직번호',
|
||||
'ODNO': '주문번호',
|
||||
'ORD_TMD': '주문시각'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 주문[v1_해외주식-001]
|
||||
|
||||
해외주식 주문 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (NASD : 나스닥 NYSE : 뉴욕 AMEX : 아멕스 SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민)
|
||||
- pdno (str): 상품번호 (종목코드)
|
||||
- ord_qty (str): 주문수량 (주문수량 (해외거래소 별 최소 주문수량 및 주문단위 확인 필요))
|
||||
- ovrs_ord_unpr (str): 해외주문단가 (1주당 가격 * 시장가의 경우 1주당 가격을 공란으로 비우지 않음 "0"으로 입력)
|
||||
- ord_dv (str): 주문구분 (buy: 매수, sell: 매도)
|
||||
- ctac_tlno (str): 연락전화번호 ()
|
||||
- mgco_aptm_odno (str): 운용사지정주문번호 ()
|
||||
- ord_svr_dvsn_cd (str): 주문서버구분코드 ("0"(Default))
|
||||
- ord_dvsn (str): 주문구분 ([Header tr_id TTTT1002U(미국 매수 주문)] 00 : 지정가 32 : LOO(장개시지정가) 34 : LOC(장마감지정가) * 모의투자 VTTT1002U(미국 매수 주문)로는 00:지정가만 가능 [Header tr_id TTTT1006U(미국 매도 주문)] 00 : 지정가 31 : MOO(장개시시장가) 32 : LOO(장개시지정가) 33 : MOC(장마감시장가) 34 : LOC(장마감지정가) * 모의투자 VTTT1006U(미국 매도 주문)로는 00:지정가만 가능 [Header tr_id TTTS1001U(홍콩 매도 주문)] 00 : 지정가 50 : 단주지정가 * 모의투자 VTTS1001U(홍콩 매도 주문)로는 00:지정가만 가능 [그외 tr_id] 제거)
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 주문 결과
|
||||
|
||||
Example:
|
||||
>>> df = order(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_excg_cd="NASD", pdno="AAPL", ord_qty="10", ovrs_ord_unpr="150.00", ord_dv="sell", ctac_tlno="", mgco_aptm_odno="", ord_svr_dvsn_cd="0", ord_dvsn="00", env_dv="real") # 실전투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = order(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
pdno="AAPL", # 상품번호
|
||||
ord_qty="10", # 주문수량
|
||||
ovrs_ord_unpr="200", # 해외주문단가
|
||||
ord_dv="sell", # 주문구분
|
||||
ctac_tlno="", # 연락전화번호
|
||||
mgco_aptm_odno="", # 운용사지정주문번호
|
||||
ord_svr_dvsn_cd="0", # 주문서버구분코드
|
||||
ord_dvsn="00", # 주문구분
|
||||
env_dv="real", # 실전모의구분
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 주문 결과 (%s) ===", "실전투자" if env_dv == "real" else "모의투자")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
191
한국투자증권(API)/examples_llm/overseas_stock/order/order.py
Normal file
191
한국투자증권(API)/examples_llm/overseas_stock/order/order.py
Normal file
@@ -0,0 +1,191 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 주문 [v1_해외주식-001]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/order"
|
||||
|
||||
def order(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
pdno: str, # 상품번호
|
||||
ord_qty: str, # 주문수량
|
||||
ovrs_ord_unpr: str, # 해외주문단가
|
||||
ord_dv: str, # 주문구분 (buy: 매수, sell: 매도)
|
||||
ctac_tlno: str, # 연락전화번호
|
||||
mgco_aptm_odno: str, # 운용사지정주문번호
|
||||
ord_svr_dvsn_cd: str, # 주문서버구분코드
|
||||
ord_dvsn: str, # 주문구분
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 주문[v1_해외주식-001]
|
||||
해외주식 주문 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_excg_cd (str): NASD : 나스닥 NYSE : 뉴욕 AMEX : 아멕스 SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민
|
||||
pdno (str): 종목코드
|
||||
ord_qty (str): 주문수량 (해외거래소 별 최소 주문수량 및 주문단위 확인 필요)
|
||||
ovrs_ord_unpr (str): 1주당 가격 * 시장가의 경우 1주당 가격을 공란으로 비우지 않음 "0"으로 입력
|
||||
ord_dv (str): 주문구분 (buy: 매수, sell: 매도)
|
||||
ctac_tlno (str):
|
||||
mgco_aptm_odno (str):
|
||||
ord_svr_dvsn_cd (str): "0"(Default)
|
||||
ord_dvsn (str): [Header tr_id TTTT1002U(미국 매수 주문)] 00 : 지정가 32 : LOO(장개시지정가) 34 : LOC(장마감지정가) * 모의투자 VTTT1002U(미국 매수 주문)로는 00:지정가만 가능 [Header tr_id TTTT1006U(미국 매도 주문)] 00 : 지정가 31 : MOO(장개시시장가) 32 : LOO(장개시지정가) 33 : MOC(장마감시장가) 34 : LOC(장마감지정가) * 모의투자 VTTT1006U(미국 매도 주문)로는 00:지정가만 가능 [Header tr_id TTTS1001U(홍콩 매도 주문)] 00 : 지정가 50 : 단주지정가 * 모의투자 VTTS1001U(홍콩 매도 주문)로는 00:지정가만 가능 [그외 tr_id] 제거
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 주문 데이터
|
||||
|
||||
Example:
|
||||
>>> df = order(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_excg_cd="NASD",
|
||||
... pdno="AAPL",
|
||||
... ord_qty="1",
|
||||
... ovrs_ord_unpr="145.00",
|
||||
... ord_dv="buy",
|
||||
... ctac_tlno="",
|
||||
... mgco_aptm_odno="",
|
||||
... ord_svr_dvsn_cd="0",
|
||||
... ord_dvsn="00",
|
||||
... env_dv="real"
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '810XXXXX')")
|
||||
raise ValueError("cano is required. (e.g. '810XXXXX')")
|
||||
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 ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NASD')")
|
||||
if not pdno:
|
||||
logger.error("pdno is required. (e.g. 'AAPL')")
|
||||
raise ValueError("pdno is required. (e.g. 'AAPL')")
|
||||
if not ord_qty:
|
||||
logger.error("ord_qty is required. (e.g. '1')")
|
||||
raise ValueError("ord_qty is required. (e.g. '1')")
|
||||
if not ovrs_ord_unpr:
|
||||
logger.error("ovrs_ord_unpr is required. (e.g. '145.00')")
|
||||
raise ValueError("ovrs_ord_unpr is required. (e.g. '145.00')")
|
||||
if not ord_dv:
|
||||
logger.error("ord_dv is required. (e.g. 'buy' or 'sell')")
|
||||
raise ValueError("ord_dv is required. (e.g. 'buy' or 'sell')")
|
||||
if not ord_svr_dvsn_cd:
|
||||
logger.error("ord_svr_dvsn_cd is required. (e.g. '0')")
|
||||
raise ValueError("ord_svr_dvsn_cd is required. (e.g. '0')")
|
||||
if not ord_dvsn:
|
||||
logger.error("ord_dvsn is required. (e.g. '00')")
|
||||
raise ValueError("ord_dvsn is required. (e.g. '00')")
|
||||
|
||||
# TR ID 설정 (매수/매도 및 거래소별)
|
||||
if ord_dv == "buy":
|
||||
if ovrs_excg_cd in ("NASD", "NYSE", "AMEX"):
|
||||
tr_id = "TTTT1002U" # 미국 매수 주문 [모의투자] VTTT1002U
|
||||
elif ovrs_excg_cd == "SEHK":
|
||||
tr_id = "TTTS1002U" # 홍콩 매수 주문 [모의투자] VTTS1002U
|
||||
elif ovrs_excg_cd == "SHAA":
|
||||
tr_id = "TTTS0202U" # 중국상해 매수 주문 [모의투자] VTTS0202U
|
||||
elif ovrs_excg_cd == "SZAA":
|
||||
tr_id = "TTTS0305U" # 중국심천 매수 주문 [모의투자] VTTS0305U
|
||||
elif ovrs_excg_cd == "TKSE":
|
||||
tr_id = "TTTS0308U" # 일본 매수 주문 [모의투자] VTTS0308U
|
||||
elif ovrs_excg_cd in ("HASE", "VNSE"):
|
||||
tr_id = "TTTS0311U" # 베트남(하노이,호치민) 매수 주문 [모의투자] VTTS0311U
|
||||
else:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NASD', 'NYSE', 'AMEX', 'SEHK', 'SHAA', 'SZAA', 'TKSE', 'HASE', 'VNSE')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NASD', 'NYSE', 'AMEX', 'SEHK', 'SHAA', 'SZAA', 'TKSE', 'HASE', 'VNSE')")
|
||||
sll_type = ""
|
||||
elif ord_dv == "sell":
|
||||
if ovrs_excg_cd in ("NASD", "NYSE", "AMEX"):
|
||||
tr_id = "TTTT1006U" # 미국 매도 주문 [모의투자] VTTT1006U
|
||||
elif ovrs_excg_cd == "SEHK":
|
||||
tr_id = "TTTS1001U" # 홍콩 매도 주문 [모의투자] VTTS1001U
|
||||
elif ovrs_excg_cd == "SHAA":
|
||||
tr_id = "TTTS1005U" # 중국상해 매도 주문 [모의투자] VTTS1005U
|
||||
elif ovrs_excg_cd == "SZAA":
|
||||
tr_id = "TTTS0304U" # 중국심천 매도 주문 [모의투자] VTTS0304U
|
||||
elif ovrs_excg_cd == "TKSE":
|
||||
tr_id = "TTTS0307U" # 일본 매도 주문 [모의투자] VTTS0307U
|
||||
elif ovrs_excg_cd in ("HASE", "VNSE"):
|
||||
tr_id = "TTTS0310U" # 베트남(하노이,호치민) 매도 주문 [모의투자] VTTS0310U
|
||||
else:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NASD', 'NYSE', 'AMEX', 'SEHK', 'SHAA', 'SZAA', 'TKSE', 'HASE', 'VNSE')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NASD', 'NYSE', 'AMEX', 'SEHK', 'SHAA', 'SZAA', 'TKSE', 'HASE', 'VNSE')")
|
||||
sll_type = "00"
|
||||
else:
|
||||
logger.error("ord_dv is required. (e.g. 'buy' or 'sell')")
|
||||
raise ValueError("ord_dv is required. (e.g. 'buy' or 'sell')")
|
||||
|
||||
# 모의투자인 경우 TR ID 앞에 V 붙이기
|
||||
if env_dv == "demo":
|
||||
tr_id = "V" + tr_id[1:]
|
||||
elif env_dv != "real":
|
||||
logger.error("env_dv can only be 'real' or 'demo'")
|
||||
raise ValueError("env_dv can only be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"PDNO": pdno,
|
||||
"ORD_QTY": ord_qty,
|
||||
"OVRS_ORD_UNPR": ovrs_ord_unpr,
|
||||
"CTAC_TLNO": ctac_tlno,
|
||||
"MGCO_APTM_ODNO": mgco_aptm_odno,
|
||||
"SLL_TYPE": sll_type,
|
||||
"ORD_SVR_DVSN_CD": ord_svr_dvsn_cd,
|
||||
"ORD_DVSN": ord_dvsn,
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from order_resv import order_resv
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 예약주문접수[v1_해외주식-002]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'ODNO': '한국거래소전송주문조직번호',
|
||||
'RSVN_ORD_RCIT_DT': '예약주문접수일자',
|
||||
'OVRS_RSVN_ODNO': '해외예약주문번호'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 예약주문접수 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 예약주문접수 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 TSLA 주식을 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result = order_resv(env_dv="real", ord_dv="usBuy", cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod,
|
||||
pdno="TSLA", ovrs_excg_cd="NASD", ft_ord_qty="1", ft_ord_unpr3="900")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logging.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
194
한국투자증권(API)/examples_llm/overseas_stock/order_resv/order_resv.py
Normal file
194
한국투자증권(API)/examples_llm/overseas_stock/order_resv/order_resv.py
Normal file
@@ -0,0 +1,194 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 예약주문접수[v1_해외주식-002]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/order-resv"
|
||||
|
||||
def order_resv(
|
||||
env_dv: str, # [필수] 실전모의구분 (ex. real:실전, demo:모의)
|
||||
ord_dv: str, # [필수] 매도매수구분 (ex. usBuy:미국매수, usSell:미국매도, asia:아시아)
|
||||
cano: str, # [필수] 종합계좌번호 (ex. 12345678)
|
||||
acnt_prdt_cd: str, # [필수] 계좌상품코드 (ex. 01)
|
||||
pdno: str, # [필수] 상품번호
|
||||
ovrs_excg_cd: str, # [필수] 해외거래소코드 (ex. NASD:나스닥, NYSE:뉴욕, AMEX:아멕스, SEHK:홍콩, SHAA:상해, SZAA:심천, TKSE:일본, HASE:하노이, VNSE:호치민)
|
||||
ft_ord_qty: str, # [필수] FT주문수량
|
||||
ft_ord_unpr3: str, # [필수] FT주문단가3
|
||||
sll_buy_dvsn_cd: Optional[str] = "", # 매도매수구분코드 (ex. 아시아인경우만 사용, 01:매도,02:매수)
|
||||
rvse_cncl_dvsn_cd: Optional[str] = "", # 정정취소구분코드 (ex. 아시아인경우만 사용, 00:매도/매수)
|
||||
prdt_type_cd: Optional[str] = "", # 상품유형코드 (ex. 아시아인경우만 사용)
|
||||
ord_svr_dvsn_cd: Optional[str] = "", # 주문서버구분코드 (ex. 0)
|
||||
rsvn_ord_rcit_dt: Optional[str] = "", # 예약주문접수일자 (ex. 아시아인경우만 사용)
|
||||
ord_dvsn: Optional[str] = "", # 주문구분 (ex. 미국 매수/매도인 경우만 사용)
|
||||
ovrs_rsvn_odno: Optional[str] = "", # 해외예약주문번호 (ex. 아이사인 경우만 사용)
|
||||
algo_ord_tmd_dvsn_cd: Optional[str] = "" # 알고리즘주문시간구분코드 (ex. TWAP, VWAP 주문에서만 사용, 02로 고정)
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
미국거래소 운영시간 외 미국주식을 예약 매매하기 위한 API입니다.
|
||||
|
||||
* 해외주식 서비스 신청 후 이용 가능합니다. (아래 링크 3번 해외증권 거래신청 참고)
|
||||
https://securities.koreainvestment.com/main/bond/research/_static/TF03ca010001.jsp
|
||||
|
||||
※ POST API의 경우 BODY값의 key값들을 대문자로 작성하셔야 합니다.
|
||||
(EX. "CANO" : "12345678", "ACNT_PRDT_CD": "01",...)
|
||||
|
||||
* 아래 각 국가의 시장별 예약주문 접수 가능 시간을 확인하시길 바랍니다.
|
||||
|
||||
미국 예약주문 접수시간
|
||||
1) 10:00 ~ 23:20 / 10:00 ~ 22:20 (서머타임 시)
|
||||
2) 주문제한 : 16:30 ~ 16:45 경까지 (사유 : 시스템 정산작업시간)
|
||||
3) 23:30 정규장으로 주문 전송 (서머타임 시 22:30 정규장 주문 전송)
|
||||
4) 미국 거래소 운영시간(한국시간 기준) : 23:30 ~ 06:00 (썸머타임 적용 시 22:30 ~ 05:00)
|
||||
|
||||
홍콩 예약주문 접수시간
|
||||
1) 09:00 ~ 10:20 접수, 10:30 주문전송
|
||||
2) 10:40 ~ 13:50 접수, 14:00 주문전송
|
||||
|
||||
중국 예약주문 접수시간
|
||||
1) 09:00 ~ 10:20 접수, 10:30 주문전송
|
||||
2) 10:40 ~ 13:50 접수, 14:00 주문전송
|
||||
|
||||
일본 예약주문 접수시간
|
||||
1) 09:10 ~ 12:20 까지 접수, 12:30 주문전송
|
||||
|
||||
베트남 예약주문 접수시간
|
||||
1) 09:00 ~ 11:00 까지 접수, 11:15 주문전송
|
||||
2) 11:20 ~ 14:50 까지 접수, 15:00 주문전송
|
||||
|
||||
* 예약주문 유의사항
|
||||
1) 예약주문 유효기간 : 당일
|
||||
- 미국장 마감 후, 미체결주문은 자동취소
|
||||
- 미국휴장 시, 익 영업일로 이전
|
||||
(미국예약주문화면에서 취소 가능)
|
||||
2) 증거금 및 잔고보유 : 체크 안함
|
||||
3) 주문전송 불가사유
|
||||
- 매수증거금 부족: 수수료 포함 매수금액부족, 환전, 시세이용료 출금, 인출에 의한 증거금 부족
|
||||
- 기타 매수증거금 부족, 매도가능수량 부족, 주권변경 등 권리발생으로 인한 주문불가사유 발생
|
||||
4) 지정가주문만 가능
|
||||
* 단 미국 예약매도주문(TTTT3016U)의 경우, MOO(장개시시장가)로 주문 접수 가능
|
||||
|
||||
Args:
|
||||
env_dv (str): [필수] 실전모의구분 (ex. real:실전, demo:모의)
|
||||
ord_dv (str): [필수] 매도매수구분 (ex. usBuy:미국매수, usSell:미국매도, asia:아시아)
|
||||
cano (str): [필수] 종합계좌번호 (ex. 12345678)
|
||||
acnt_prdt_cd (str): [필수] 계좌상품코드 (ex. 01)
|
||||
pdno (str): [필수] 상품번호
|
||||
ovrs_excg_cd (str): [필수] 해외거래소코드 (ex. NASD:나스닥, NYSE:뉴욕, AMEX:아멕스, SEHK:홍콩, SHAA:상해, SZAA:심천, TKSE:일본, HASE:하노이, VNSE:호치민)
|
||||
ft_ord_qty (str): [필수] FT주문수량
|
||||
ft_ord_unpr3 (str): [필수] FT주문단가3
|
||||
sll_buy_dvsn_cd (Optional[str]): 매도매수구분코드 (ex. 아시아인경우만 사용, 01:매도,02:매수)
|
||||
rvse_cncl_dvsn_cd (Optional[str]): 정정취소구분코드 (ex. 아시아인경우만 사용, 00:매도/매수)
|
||||
prdt_type_cd (Optional[str]): 상품유형코드 (ex. 아시아인경우만 사용)
|
||||
ord_svr_dvsn_cd (Optional[str]): 주문서버구분코드 (ex. 0)
|
||||
rsvn_ord_rcit_dt (Optional[str]): 예약주문접수일자 (ex. 아시아인경우만 사용)
|
||||
ord_dvsn (Optional[str]): 주문구분 (ex. 미국 매수/매도인 경우만 사용)
|
||||
ovrs_rsvn_odno (Optional[str]): 해외예약주문번호 (ex. 아이사인 경우만 사용)
|
||||
algo_ord_tmd_dvsn_cd (Optional[str]): 알고리즘주문시간구분코드 (ex. TWAP, VWAP 주문에서만 사용, 02로 고정)
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외주식 예약주문접수 결과 데이터
|
||||
|
||||
Example:
|
||||
>>> df = order_resv(env_dv="real", ord_dv="usBuy", cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, pdno="TSLA", ovrs_excg_cd="NASD", ft_ord_qty="1", ft_ord_unpr3="900")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if env_dv == "":
|
||||
raise ValueError("env_dv is required (e.g. 'real' or 'demo')")
|
||||
|
||||
if ord_dv == "":
|
||||
raise ValueError("ord_dv is required (e.g. 'usBuy', 'usSell', 'asia')")
|
||||
|
||||
if cano == "":
|
||||
raise ValueError("cano is required (e.g. '12345678')")
|
||||
|
||||
if acnt_prdt_cd == "":
|
||||
raise ValueError("acnt_prdt_cd is required (e.g. '01')")
|
||||
|
||||
if pdno == "":
|
||||
raise ValueError("pdno is required")
|
||||
|
||||
if ovrs_excg_cd == "":
|
||||
raise ValueError("ovrs_excg_cd is required (e.g. 'NASD', 'NYSE', 'AMEX', 'SEHK', 'SHAA', 'SZAA', 'TKSE', 'HASE', 'VNSE')")
|
||||
|
||||
if ft_ord_qty == "":
|
||||
raise ValueError("ft_ord_qty is required")
|
||||
|
||||
if ft_ord_unpr3 == "":
|
||||
raise ValueError("ft_ord_unpr3 is required")
|
||||
|
||||
# tr_id 설정
|
||||
if env_dv == "real":
|
||||
if ord_dv == "usBuy":
|
||||
tr_id = "TTTT3014U"
|
||||
elif ord_dv == "usSell":
|
||||
tr_id = "TTTT3016U"
|
||||
elif ord_dv == "asia":
|
||||
tr_id = "TTTS3013U"
|
||||
else:
|
||||
raise ValueError("ord_dv can only be 'usBuy', 'usSell' or 'asia'")
|
||||
elif env_dv == "demo":
|
||||
if ord_dv == "usBuy":
|
||||
tr_id = "VTTT3014U"
|
||||
elif ord_dv == "usSell":
|
||||
tr_id = "VTTT3016U"
|
||||
elif ord_dv == "asia":
|
||||
tr_id = "VTTS3013U"
|
||||
else:
|
||||
raise ValueError("ord_dv can only be 'usBuy', 'usSell' or 'asia'")
|
||||
else:
|
||||
raise ValueError("env_dv is required (e.g. 'real' or 'demo')")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"PDNO": pdno,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"FT_ORD_QTY": ft_ord_qty,
|
||||
"FT_ORD_UNPR3": ft_ord_unpr3
|
||||
}
|
||||
|
||||
# 옵션 파라미터 추가
|
||||
if sll_buy_dvsn_cd:
|
||||
params["SLL_BUY_DVSN_CD"] = sll_buy_dvsn_cd
|
||||
if rvse_cncl_dvsn_cd:
|
||||
params["RVSE_CNCL_DVSN_CD"] = rvse_cncl_dvsn_cd
|
||||
if prdt_type_cd:
|
||||
params["PRDT_TYPE_CD"] = prdt_type_cd
|
||||
if ord_svr_dvsn_cd:
|
||||
params["ORD_SVR_DVSN_CD"] = ord_svr_dvsn_cd
|
||||
if rsvn_ord_rcit_dt:
|
||||
params["RSVN_ORD_RCIT_DT"] = rsvn_ord_rcit_dt
|
||||
if ord_dvsn:
|
||||
params["ORD_DVSN"] = ord_dvsn
|
||||
if ovrs_rsvn_odno:
|
||||
params["OVRS_RSVN_ODNO"] = ovrs_rsvn_odno
|
||||
if algo_ord_tmd_dvsn_cd:
|
||||
params["ALGO_ORD_TMD_DVSN_CD"] = algo_ord_tmd_dvsn_cd
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, "", params, postFlag=True)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output, index=[0])
|
||||
logging.info("Data fetch complete.")
|
||||
return current_data
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from order_resv_ccnl import order_resv_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 예약주문접수취소[v1_해외주식-004]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'OVRS_RSVN_ODNO': '해외예약주문번호'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 예약주문접수취소 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 예약주문접수취소 API를 호출하여 결과를 출력합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result = order_resv_ccnl(
|
||||
env_dv="real",
|
||||
nat_dv="us",
|
||||
cano=trenv.my_acct,
|
||||
acnt_prdt_cd=trenv.my_prod,
|
||||
rsvn_ord_rcit_dt="20250610",
|
||||
ovrs_rsvn_odno="0030008244"
|
||||
)
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logging.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 예약주문접수취소[v1_해외주식-004]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/order-resv-ccnl"
|
||||
|
||||
def order_resv_ccnl(
|
||||
env_dv: str, # [필수] 실전모의구분 (ex. real:실전, demo:모의)
|
||||
nat_dv: str, # [필수] 국가구분 (ex. us:미국)
|
||||
cano: str, # [필수] 종합계좌번호 (ex. 12345678)
|
||||
acnt_prdt_cd: str, # [필수] 계좌상품코드 (ex. 01)
|
||||
rsvn_ord_rcit_dt: str, # [필수] 해외주문접수일자
|
||||
ovrs_rsvn_odno: str # [필수] 해외예약주문번호 (ex. 해외주식_예약주문접수 API Output ODNO(주문번호) 참고)
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
접수된 미국주식 예약주문을 취소하기 위한 API입니다.
|
||||
(해외주식 예약주문접수 시 Return 받은 ODNO를 참고하여 API를 호출하세요.)
|
||||
|
||||
* 해외주식 서비스 신청 후 이용 가능합니다. (아래 링크 3번 해외증권 거래신청 참고)
|
||||
https://securities.koreainvestment.com/main/bond/research/_static/TF03ca010001.jsp
|
||||
|
||||
※ POST API의 경우 BODY값의 key값들을 대문자로 작성하셔야 합니다.
|
||||
(EX. "CANO" : "12345678", "ACNT_PRDT_CD": "01",...)
|
||||
|
||||
Args:
|
||||
env_dv (str): [필수] 실전모의구분 (ex. real:실전, demo:모의)
|
||||
nat_dv (str): [필수] 국가구분 (ex. us:미국)
|
||||
cano (str): [필수] 종합계좌번호 (ex. 12345678)
|
||||
acnt_prdt_cd (str): [필수] 계좌상품코드 (ex. 01)
|
||||
rsvn_ord_rcit_dt (str): [필수] 해외주문접수일자
|
||||
ovrs_rsvn_odno (str): [필수] 해외예약주문번호 (ex. 해외주식_예약주문접수 API Output ODNO(주문번호) 참고)
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외주식 예약주문접수취소 결과 데이터
|
||||
|
||||
Example:
|
||||
>>> df = order_resv_ccnl(env_dv="real", nat_dv="us", cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, rsvn_ord_rcit_dt="20220810", ovrs_rsvn_odno="0030008244")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if env_dv == "":
|
||||
raise ValueError("env_dv is required (e.g. 'real' or 'demo')")
|
||||
|
||||
if nat_dv == "":
|
||||
raise ValueError("nat_dv is required (e.g. 'us')")
|
||||
|
||||
if cano == "":
|
||||
raise ValueError("cano is required (e.g. '12345678')")
|
||||
|
||||
if acnt_prdt_cd == "":
|
||||
raise ValueError("acnt_prdt_cd is required (e.g. '01')")
|
||||
|
||||
if rsvn_ord_rcit_dt == "":
|
||||
raise ValueError("rsvn_ord_rcit_dt is required")
|
||||
|
||||
if ovrs_rsvn_odno == "":
|
||||
raise ValueError("ovrs_rsvn_odno is required")
|
||||
|
||||
# tr_id 설정
|
||||
if env_dv == "real":
|
||||
if nat_dv == "us":
|
||||
tr_id = "TTTT3017U"
|
||||
else:
|
||||
raise ValueError("nat_dv can only be 'us'")
|
||||
elif env_dv == "demo":
|
||||
if nat_dv == "us":
|
||||
tr_id = "VTTT3017U"
|
||||
else:
|
||||
raise ValueError("nat_dv can only be 'us'")
|
||||
else:
|
||||
raise ValueError("env_dv is required (e.g. 'real' or 'demo')")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"RSVN_ORD_RCIT_DT": rsvn_ord_rcit_dt,
|
||||
"OVRS_RSVN_ODNO": ovrs_rsvn_odno
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, "", params, postFlag=True)
|
||||
|
||||
if res.isOK():
|
||||
# output은 object 자료형이므로 DataFrame으로 변환
|
||||
current_data = pd.DataFrame([res.getBody().output])
|
||||
logging.info("Data fetch complete.")
|
||||
return current_data
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,131 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 20250601
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from order_resv_list import order_resv_list
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 예약주문조회[v1_해외주식-013]
|
||||
##############################################################################################
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'cncl_yn': '취소여부',
|
||||
'rsvn_ord_rcit_dt': '예약주문접수일자',
|
||||
'ovrs_rsvn_odno': '해외예약주문번호',
|
||||
'ord_dt': '주문일자',
|
||||
'ord_gno_brno': '주문채번지점번호',
|
||||
'odno': '주문번호',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'sll_buy_dvsn_cd_name': '매도매수구분명',
|
||||
'ovrs_rsvn_ord_stat_cd': '해외예약주문상태코드',
|
||||
'ovrs_rsvn_ord_stat_cd_name': '해외예약주문상태코드명',
|
||||
'pdno': '상품번호',
|
||||
'prdt_type_cd': '상품유형코드',
|
||||
'prdt_name': '상품명',
|
||||
'ord_rcit_tmd': '주문접수시각',
|
||||
'ord_fwdg_tmd': '주문전송시각',
|
||||
'tr_dvsn_name': '거래구분명',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'tr_mket_name': '거래시장명',
|
||||
'ord_stfno': '주문직원번호',
|
||||
'ft_ord_qty': 'FT주문수량',
|
||||
'ft_ord_unpr3': 'FT주문단가3',
|
||||
'ft_ccld_qty': 'FT체결수량',
|
||||
'nprc_rson_text': '미처리사유내용',
|
||||
'splt_buy_attr_name': '분할매수속성명'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = [
|
||||
'FT주문수량', 'FT주문단가', 'FT체결수량'
|
||||
]
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 예약주문조회[v1_해외주식-013]
|
||||
|
||||
해외주식 예약주문조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- nat_dv (str): 국가구분 (us: 미국)
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- inqr_strt_dt (str): 조회시작일자 (YYYYMMDD)
|
||||
- inqr_end_dt (str): 조회종료일자 (YYYYMMDD)
|
||||
- inqr_dvsn_cd (str): 조회구분코드 (00: 전체, 01: 예약, 02: 체결, 03: 거부)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (NASD: 나스닥, NYSE: 뉴욕, AMEX: 아멕스, SEHK: 홍콩, SHAA: 중국상해, SZAA: 중국심천, TKSE: 일본, HASE: 베트남 하노이, VNSE: 베트남 호치민)
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 예약주문조회 결과
|
||||
|
||||
Example:
|
||||
>>> df = order_resv_list(nat_dv="us", cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, inqr_strt_dt="20220809", inqr_end_dt="20220830", inqr_dvsn_cd="00", ovrs_excg_cd="NASD") # 실전투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = order_resv_list(
|
||||
nat_dv="us", # 국가구분
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
inqr_strt_dt="20220809", # 조회시작일자
|
||||
inqr_end_dt="20220830", # 조회종료일자
|
||||
inqr_dvsn_cd="00", # 조회구분코드
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce')
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 예약주문조회 결과 (실전투자) ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 예약주문조회[v1_해외주식-013]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/order-resv-list"
|
||||
|
||||
def order_resv_list(
|
||||
nat_dv: str, # 국가구분코드
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
inqr_strt_dt: str, # 조회시작일자
|
||||
inqr_end_dt: str, # 조회종료일자
|
||||
inqr_dvsn_cd: str, # 조회구분코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
prdt_type_cd: str = "", # 상품유형코드
|
||||
FK200: str = "", # 연속조회검색조건200
|
||||
NK200: str = "", # 연속조회키200
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외주식 예약주문 조회 API입니다.
|
||||
※ 모의투자는 사용 불가합니다.
|
||||
|
||||
* 해외주식 서비스 신청 후 이용 가능합니다. (아래 링크 3번 해외증권 거래신청 참고)
|
||||
https://securities.koreainvestment.com/main/bond/research/_static/TF03ca010001.jsp
|
||||
|
||||
Args:
|
||||
nat_dv (str): [필수] 국가구분코드 (ex. us:미국, asia:아시아)
|
||||
cano (str): [필수] 종합계좌번호 (ex. 12345678)
|
||||
acnt_prdt_cd (str): [필수] 계좌상품코드 (ex. 01)
|
||||
inqr_strt_dt (str): [필수] 조회시작일자 (ex. 20250101)
|
||||
inqr_end_dt (str): [필수] 조회종료일자 (ex. 20251231)
|
||||
inqr_dvsn_cd (str): [필수] 조회구분코드 (ex. 00:전체, 01:일반해외주식, 02:미니스탁)
|
||||
ovrs_excg_cd (str): [필수] 해외거래소코드 (ex. NASD:나스닥, NYSE:뉴욕, AMEX:아멕스, SEHK:홍콩, SHAA:상해, SZAA:심천, TKSE:일본, HASE:하노이, VNSE:호치민)
|
||||
prdt_type_cd (str): 상품유형코드
|
||||
FK200 (str): 연속조회검색조건200
|
||||
NK200 (str): 연속조회키200
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외주식 예약주문조회 데이터
|
||||
|
||||
Example:
|
||||
>>> df = order_resv_list(nat_dv="us", cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, inqr_strt_dt="20250101", inqr_end_dt="20251231", inqr_dvsn_cd="00", ovrs_excg_cd="NASD")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if nat_dv == "":
|
||||
raise ValueError("nat_dv is required (e.g. 'us' or 'asia')")
|
||||
|
||||
if cano == "":
|
||||
raise ValueError("cano is required (e.g. '12345678')")
|
||||
|
||||
if acnt_prdt_cd == "":
|
||||
raise ValueError("acnt_prdt_cd is required (e.g. '01')")
|
||||
|
||||
if inqr_strt_dt == "":
|
||||
raise ValueError("inqr_strt_dt is required (e.g. '20250101')")
|
||||
|
||||
if inqr_end_dt == "":
|
||||
raise ValueError("inqr_end_dt is required (e.g. '20251231')")
|
||||
|
||||
if inqr_dvsn_cd == "":
|
||||
raise ValueError("inqr_dvsn_cd is required (e.g. '00')")
|
||||
|
||||
if ovrs_excg_cd == "":
|
||||
raise ValueError("ovrs_excg_cd is required (e.g. 'NASD')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe is None:
|
||||
return pd.DataFrame()
|
||||
else:
|
||||
return dataframe
|
||||
|
||||
# tr_id 설정
|
||||
if nat_dv == "us":
|
||||
tr_id = "TTTT3039R"
|
||||
elif nat_dv == "asia":
|
||||
tr_id = "TTTS3014R"
|
||||
else:
|
||||
raise ValueError("nat_dv can only be 'us' or 'asia'")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"INQR_STRT_DT": inqr_strt_dt,
|
||||
"INQR_END_DT": inqr_end_dt,
|
||||
"INQR_DVSN_CD": inqr_dvsn_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"PRDT_TYPE_CD": prdt_type_cd,
|
||||
"CTX_AREA_FK200": FK200,
|
||||
"CTX_AREA_NK200": NK200
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output)
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
FK200 = res.getBody().ctx_area_fk200
|
||||
NK200 = res.getBody().ctx_area_nk200
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return order_resv_list(
|
||||
nat_dv, cano, acnt_prdt_cd, inqr_strt_dt, inqr_end_dt,
|
||||
inqr_dvsn_cd, ovrs_excg_cd, prdt_type_cd, FK200, NK200,
|
||||
"N", dataframe, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from order_rvsecncl import order_rvsecncl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 정정취소주문[v1_해외주식-003]
|
||||
################################################################################
|
||||
|
||||
# 컬럼명 매핑 (한글 변환용)
|
||||
COLUMN_MAPPING = {
|
||||
'KRX_FWDG_ORD_ORGNO': '한국거래소전송주문조직번호',
|
||||
'ODNO': '주문번호',
|
||||
'ORD_TMD': '주문시각'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의 (소수점 처리용)
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 정정취소주문[v1_해외주식-003]
|
||||
|
||||
해외주식 정정취소주문 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_excg_cd (str): 해외거래소코드 (NASD : 나스닥 NYSE : 뉴욕 AMEX : 아멕스 SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민)
|
||||
- pdno (str): 상품번호 ()
|
||||
- orgn_odno (str): 원주문번호 (정정 또는 취소할 원주문번호 (해외주식_주문 API ouput ODNO or 해외주식 미체결내역 API output ODNO 참고))
|
||||
- rvse_cncl_dvsn_cd (str): 정정취소구분코드 (01 : 정정 02 : 취소)
|
||||
- ord_qty (str): 주문수량 ()
|
||||
- ovrs_ord_unpr (str): 해외주문단가 (취소주문 시, "0" 입력)
|
||||
- mgco_aptm_odno (str): 운용사지정주문번호 ()
|
||||
- ord_svr_dvsn_cd (str): 주문서버구분코드 ("0"(Default))
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 정정취소주문 결과
|
||||
|
||||
Example:
|
||||
>>> df = order_rvsecncl(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_excg_cd="NYSE", pdno="BA", orgn_odno="1234567890", rvse_cncl_dvsn_cd="01", ord_qty="100", ovrs_ord_unpr="0", mgco_aptm_odno="", ord_svr_dvsn_cd="0", env_dv="real") # 실전투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급 (모의투자 지원 로직)
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
trenv = ka.getTREnv()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = order_rvsecncl(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
ovrs_excg_cd="NASD", # 해외거래소코드
|
||||
pdno="AMZN", # 상품번호
|
||||
orgn_odno="0030132992", # 원주문번호
|
||||
rvse_cncl_dvsn_cd="01", # 정정취소구분코드
|
||||
ord_qty="100", # 주문수량
|
||||
ovrs_ord_unpr="99", # 해외주문단가
|
||||
mgco_aptm_odno="", # 운용사지정주문번호
|
||||
ord_svr_dvsn_cd="0", # 주문서버구분코드
|
||||
env_dv="real", # 실전모의구분
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 변환 (object -> float64)
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce')
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 정정취소주문 결과 (%s) ===", "실전투자" if env_dv == "real" else "모의투자")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,143 @@
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
################################################################################
|
||||
# [해외주식] 주문/계좌 > 해외주식 정정취소주문[v1_해외주식-003]
|
||||
################################################################################
|
||||
|
||||
API_URL = "/uapi/overseas-stock/v1/trading/order-rvsecncl"
|
||||
|
||||
def order_rvsecncl(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_excg_cd: str, # 해외거래소코드
|
||||
pdno: str, # 상품번호
|
||||
orgn_odno: str, # 원주문번호
|
||||
rvse_cncl_dvsn_cd: str, # 정정취소구분코드
|
||||
ord_qty: str, # 주문수량
|
||||
ovrs_ord_unpr: str, # 해외주문단가
|
||||
mgco_aptm_odno: str, # 운용사지정주문번호
|
||||
ord_svr_dvsn_cd: str, # 주문서버구분코드
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 주문/계좌
|
||||
해외주식 정정취소주문[v1_해외주식-003]
|
||||
해외주식 정정취소주문 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_excg_cd (str): NASD : 나스닥 NYSE : 뉴욕 AMEX : 아멕스 SEHK : 홍콩 SHAA : 중국상해 SZAA : 중국심천 TKSE : 일본 HASE : 베트남 하노이 VNSE : 베트남 호치민
|
||||
pdno (str): 상품번호
|
||||
orgn_odno (str): 정정 또는 취소할 원주문번호 (해외주식_주문 API ouput ODNO or 해외주식 미체결내역 API output ODNO 참고)
|
||||
rvse_cncl_dvsn_cd (str): 01 : 정정 02 : 취소
|
||||
ord_qty (str): 주문수량
|
||||
ovrs_ord_unpr (str): 취소주문 시, "0" 입력
|
||||
mgco_aptm_odno (str): 운용사지정주문번호
|
||||
ord_svr_dvsn_cd (str): "0"(Default)
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 정정취소주문 데이터
|
||||
|
||||
Example:
|
||||
>>> df = order_rvsecncl(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_excg_cd="NYSE",
|
||||
... pdno="BA",
|
||||
... orgn_odno="30135009",
|
||||
... rvse_cncl_dvsn_cd="01",
|
||||
... ord_qty="1",
|
||||
... ovrs_ord_unpr="226.00",
|
||||
... mgco_aptm_odno="",
|
||||
... ord_svr_dvsn_cd="0",
|
||||
... env_dv="real"
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '810XXXXX')")
|
||||
raise ValueError("cano is required. (e.g. '810XXXXX')")
|
||||
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 ovrs_excg_cd:
|
||||
logger.error("ovrs_excg_cd is required. (e.g. 'NYSE')")
|
||||
raise ValueError("ovrs_excg_cd is required. (e.g. 'NYSE')")
|
||||
if not pdno:
|
||||
logger.error("pdno is required. (e.g. 'BA')")
|
||||
raise ValueError("pdno is required. (e.g. 'BA')")
|
||||
if not orgn_odno:
|
||||
logger.error("orgn_odno is required. (e.g. '30135009')")
|
||||
raise ValueError("orgn_odno is required. (e.g. '30135009')")
|
||||
if not rvse_cncl_dvsn_cd:
|
||||
logger.error("rvse_cncl_dvsn_cd is required. (e.g. '01')")
|
||||
raise ValueError("rvse_cncl_dvsn_cd is required. (e.g. '01')")
|
||||
if not ord_qty:
|
||||
logger.error("ord_qty is required. (e.g. '1')")
|
||||
raise ValueError("ord_qty is required. (e.g. '1')")
|
||||
if not ovrs_ord_unpr:
|
||||
logger.error("ovrs_ord_unpr is required. (e.g. '226.00')")
|
||||
raise ValueError("ovrs_ord_unpr is required. (e.g. '226.00')")
|
||||
|
||||
# TR ID 설정 (모의투자 지원 로직)
|
||||
if env_dv == "real":
|
||||
tr_id = "TTTT1004U" # 실전투자용 TR ID
|
||||
elif env_dv == "demo":
|
||||
tr_id = "VTTT1004U" # 모의투자용 TR ID
|
||||
else:
|
||||
raise ValueError("env_dv can only be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_EXCG_CD": ovrs_excg_cd,
|
||||
"PDNO": pdno,
|
||||
"ORGN_ODNO": orgn_odno,
|
||||
"RVSE_CNCL_DVSN_CD": rvse_cncl_dvsn_cd,
|
||||
"ORD_QTY": ord_qty,
|
||||
"OVRS_ORD_UNPR": ovrs_ord_unpr,
|
||||
"MGCO_APTM_ODNO": mgco_aptm_odno,
|
||||
"ORD_SVR_DVSN_CD": ord_svr_dvsn_cd,
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from period_rights import period_rights
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 기간별권리조회 [해외주식-052]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'bass_dt': '기준일자',
|
||||
'rght_type_cd': '권리유형코드',
|
||||
'pdno': '상품번호',
|
||||
'prdt_name': '상품명',
|
||||
'prdt_type_cd': '상품유형코드',
|
||||
'std_pdno': '표준상품번호',
|
||||
'acpl_bass_dt': '현지기준일자',
|
||||
'sbsc_strt_dt': '청약시작일자',
|
||||
'sbsc_end_dt': '청약종료일자',
|
||||
'cash_alct_rt': '현금배정비율',
|
||||
'stck_alct_rt': '주식배정비율',
|
||||
'crcy_cd': '통화코드',
|
||||
'crcy_cd2': '통화코드2',
|
||||
'crcy_cd3': '통화코드3',
|
||||
'crcy_cd4': '통화코드4',
|
||||
'alct_frcr_unpr': '배정외화단가',
|
||||
'stkp_dvdn_frcr_amt2': '주당배당외화금액2',
|
||||
'stkp_dvdn_frcr_amt3': '주당배당외화금액3',
|
||||
'stkp_dvdn_frcr_amt4': '주당배당외화금액4',
|
||||
'dfnt_yn': '확정여부'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['현금배정비율', '주식배정비율', '배정외화단가', '주당배당외화금액2', '주당배당외화금액3', '주당배당외화금액4']
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 기간별권리조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 기간별권리조회 API를 호출하여 결과를 출력합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# case1 조회
|
||||
logger.info("=== case1 조회 ===")
|
||||
try:
|
||||
result = period_rights(
|
||||
rght_type_cd="%%",
|
||||
inqr_dvsn_cd="02",
|
||||
inqr_strt_dt="20240417",
|
||||
inqr_end_dt="20240417"
|
||||
)
|
||||
except ValueError as e:
|
||||
logger.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logger.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
# 컬럼명 한글 변환 및 데이터 출력
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logger.info("결과:")
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 기간별권리조회 [해외주식-052]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/period-rights"
|
||||
|
||||
def period_rights(
|
||||
rght_type_cd: str, # 권리유형코드
|
||||
inqr_dvsn_cd: str, # 조회구분코드
|
||||
inqr_strt_dt: str, # 조회시작일자
|
||||
inqr_end_dt: str, # 조회종료일자
|
||||
pdno: str = "", # 상품번호
|
||||
prdt_type_cd: str = "", # 상품유형코드
|
||||
NK50: str = "", # 연속조회키50
|
||||
FK50: str = "", # 연속조회검색조건50
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외주식 기간별권리조회 API입니다.
|
||||
한국투자 HTS(eFriend Plus) > [7520] 기간별해외증권권리조회 화면을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
|
||||
|
||||
※ 확정여부가 '예정'으로 표시되는 경우는 권리정보가 변경될 수 있으니 참고자료로만 활용하시기 바랍니다.
|
||||
|
||||
Args:
|
||||
rght_type_cd (str): [필수] 권리유형코드 (%%:전체, 01:유상, 02:무상, 03:배당, 11:합병,14:액면분할, 15:액면병합, 17:감자, 54:WR청구,61:원리금상환, 71:WR소멸, 74:배당옵션, 75:특별배당, 76:ISINCODE변경, 77:실권주청약)
|
||||
inqr_dvsn_cd (str): [필수] 조회구분코드 (02:현지기준일, 03:청약시작일, 04:청약종료일)
|
||||
inqr_strt_dt (str): [필수] 조회시작일자 (20250101)
|
||||
inqr_end_dt (str): [필수] 조회종료일자 (20250131)
|
||||
pdno (str): 상품번호
|
||||
prdt_type_cd (str): 상품유형코드
|
||||
NK50 (str): 연속조회키50
|
||||
FK50 (str): 연속조회검색조건50
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외주식 기간별권리조회 데이터
|
||||
|
||||
Example:
|
||||
>>> df = period_rights("%%", "02", "20240417", "20240417")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if rght_type_cd == "":
|
||||
raise ValueError("rght_type_cd is required (e.g. '%%:전체, 01:유상, 02:무상, 03:배당, 11:합병,14:액면분할, 15:액면병합, 17:감자, 54:WR청구,61:원리금상환, 71:WR소멸, 74:배당옵션, 75:특별배당, 76:ISINCODE변경, 77:실권주청약')")
|
||||
|
||||
if inqr_dvsn_cd == "":
|
||||
raise ValueError("inqr_dvsn_cd is required (e.g. '02:현지기준일, 03:청약시작일, 04:청약종료일')")
|
||||
|
||||
if inqr_strt_dt == "":
|
||||
raise ValueError("inqr_strt_dt is required (e.g. '20250101')")
|
||||
|
||||
if inqr_end_dt == "":
|
||||
raise ValueError("inqr_end_dt is required (e.g. '20250131')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe is None:
|
||||
return pd.DataFrame()
|
||||
else:
|
||||
return dataframe
|
||||
|
||||
tr_id = "CTRGT011R" # 해외주식 기간별권리조회
|
||||
|
||||
params = {
|
||||
"RGHT_TYPE_CD": rght_type_cd, # 권리유형코드
|
||||
"INQR_DVSN_CD": inqr_dvsn_cd, # 조회구분코드
|
||||
"INQR_STRT_DT": inqr_strt_dt, # 조회시작일자
|
||||
"INQR_END_DT": inqr_end_dt, # 조회종료일자
|
||||
"PDNO": pdno, # 상품번호
|
||||
"PRDT_TYPE_CD": prdt_type_cd, # 상품유형코드
|
||||
"CTX_AREA_NK50": NK50, # 연속조회키50
|
||||
"CTX_AREA_FK50": FK50 # 연속조회검색조건50
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output)
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
NK50 = res.getBody().ctx_area_nk50
|
||||
FK50 = res.getBody().ctx_area_fk50
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return period_rights(
|
||||
rght_type_cd, inqr_dvsn_cd, inqr_strt_dt, inqr_end_dt,
|
||||
pdno, prdt_type_cd, NK50, FK50, "N", dataframe, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
112
한국투자증권(API)/examples_llm/overseas_stock/price/chk_price.py
Normal file
112
한국투자증권(API)/examples_llm/overseas_stock/price/chk_price.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
Created on 2025-06-26
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from price import price
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 현재체결가[v1_해외주식-009]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'rsym': '실시간조회종목코드',
|
||||
'zdiv': '소수점자리수',
|
||||
'base': '전일종가',
|
||||
'pvol': '전일거래량',
|
||||
'last': '현재가',
|
||||
'sign': '대비기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'tamt': '거래대금',
|
||||
'ordy': '매수가능여부'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['소수점자리수', '전일종가', '전일거래량', '현재가', '대비', '등락율', '거래량', '거래대금']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 현재체결가[v1_해외주식-009]
|
||||
|
||||
해외주식 현재체결가 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- auth (str): 사용자권한정보 ("" (Null 값 설정))
|
||||
- excd (str): 거래소코드 (HKS : 홍콩 NYS : 뉴욕 NAS : 나스닥 AMS : 아멕스 TSE : 도쿄 SHS : 상해 SZS : 심천 SHI : 상해지수 SZI : 심천지수 HSX : 호치민 HNX : 하노이 BAY : 뉴욕(주간) BAQ : 나스닥(주간) BAA : 아멕스(주간))
|
||||
- symb (str): 종목코드 ()
|
||||
- env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 현재체결가 결과
|
||||
|
||||
Example:
|
||||
>>> df = price(auth="", excd="NAS", symb="AAPL", env_dv="real") # 실전투자
|
||||
>>> df = price(auth="", excd="NAS", symb="AAPL", env_dv="demo") # 모의투자
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 실전/모의투자 선택 (모의투자 지원 로직)
|
||||
env_dv = "real" # "real": 실전투자, "demo": 모의투자
|
||||
logger.info("투자 환경: %s", "실전투자" if env_dv == "real" else "모의투자")
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
if env_dv == "real":
|
||||
ka.auth(svr='prod') # 실전투자용 토큰
|
||||
elif env_dv == "demo":
|
||||
ka.auth(svr='vps') # 모의투자용 토큰
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외주식 현재체결가 (%s)", "실전투자" if env_dv == "real" else "모의투자")
|
||||
result = price(
|
||||
auth="",
|
||||
excd="NAS",
|
||||
symb="AAPL",
|
||||
env_dv="real"
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 현재체결가 결과 (%s) ===", "실전투자" if env_dv == "real" else "모의투자")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
127
한국투자증권(API)/examples_llm/overseas_stock/price/price.py
Normal file
127
한국투자증권(API)/examples_llm/overseas_stock/price/price.py
Normal file
@@ -0,0 +1,127 @@
|
||||
# [해외주식] 기본시세 - 해외주식 현재체결가
|
||||
# Generated by KIS API Generator (Single API Mode)
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-26
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 현재체결가[v1_해외주식-009]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/price"
|
||||
|
||||
def price(
|
||||
auth: str, # 사용자권한정보
|
||||
excd: str, # 거래소코드
|
||||
symb: str, # 종목코드
|
||||
env_dv: str = "real", # 실전모의구분
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 현재체결가[v1_해외주식-009]
|
||||
해외주식 현재체결가 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
auth (str): 사용자권한정보
|
||||
excd (str): 거래소코드 (예: "NAS")
|
||||
symb (str): 종목코드 (예: "AAPL")
|
||||
env_dv (str): 실전모의구분 (real:실전, demo:모의)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 현재체결가 데이터
|
||||
|
||||
Example:
|
||||
>>> df = price("", "NAS", "AAPL")
|
||||
>>> print(df)
|
||||
"""
|
||||
# 로깅 설정
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if not excd:
|
||||
logger.error("excd is required. (e.g. 'NAS')")
|
||||
raise ValueError("excd is required. (e.g. 'NAS')")
|
||||
|
||||
if not symb:
|
||||
logger.error("symb is required. (e.g. 'AAPL')")
|
||||
raise ValueError("symb is required. (e.g. 'AAPL')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 설정 (모의투자 지원 로직)
|
||||
if env_dv == "real" or env_dv == "demo":
|
||||
tr_id = "HHDFS00000300" # 실전투자, 모의투자 공통 TR ID
|
||||
else:
|
||||
logger.error("Invalid env_dv value: %s. Must be 'real' or 'demo'.", env_dv)
|
||||
raise ValueError("env_dv must be 'real' or 'demo'")
|
||||
|
||||
params = {
|
||||
"AUTH": auth,
|
||||
"EXCD": excd,
|
||||
"SYMB": symb,
|
||||
}
|
||||
|
||||
# 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 price(
|
||||
auth,
|
||||
excd,
|
||||
symb,
|
||||
env_dv,
|
||||
"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()
|
||||
@@ -0,0 +1,134 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from price_detail import price_detail
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 현재가상세[v1_해외주식-029]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'rsym': '실시간조회종목코드',
|
||||
'pvol': '전일거래량',
|
||||
'open': '시가',
|
||||
'high': '고가',
|
||||
'low': '저가',
|
||||
'last': '현재가',
|
||||
'base': '전일종가',
|
||||
'tomv': '시가총액',
|
||||
'pamt': '전일거래대금',
|
||||
'uplp': '상한가',
|
||||
'dnlp': '하한가',
|
||||
'h52p': '52주최고가',
|
||||
'h52d': '52주최고일자',
|
||||
'l52p': '52주최저가',
|
||||
'l52d': '52주최저일자',
|
||||
'perx': 'PER',
|
||||
'pbrx': 'PBR',
|
||||
'epsx': 'EPS',
|
||||
'bpsx': 'BPS',
|
||||
'shar': '상장주수',
|
||||
'mcap': '자본금',
|
||||
'curr': '통화',
|
||||
'zdiv': '소수점자리수',
|
||||
'vnit': '매매단위',
|
||||
't_xprc': '원환산당일가격',
|
||||
't_xdif': '원환산당일대비',
|
||||
't_xrat': '원환산당일등락',
|
||||
'p_xprc': '원환산전일가격',
|
||||
'p_xdif': '원환산전일대비',
|
||||
'p_xrat': '원환산전일등락',
|
||||
't_rate': '당일환율',
|
||||
'p_rate': '전일환율',
|
||||
't_xsgn': '원환산당일기호',
|
||||
'p_xsng': '원환산전일기호',
|
||||
'e_ordyn': '거래가능여부',
|
||||
'e_hogau': '호가단위',
|
||||
'e_icod': '업종(섹터)',
|
||||
'e_parp': '액면가',
|
||||
'tvol': '거래량',
|
||||
'tamt': '거래대금',
|
||||
'etyp_nm': 'ETP 분류명'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['소수점자리수', '전일거래량', '시가', '고가', '저가', '현재가', '전일종가', '시가총액', '전일거래대금', '상한가', '하한가', '52주최고가',
|
||||
'52주최저가', 'PER', 'PBR', 'EPS', 'BPS', '원환산당일가격', '원환산당일대비', '원환산당일등락', '원환산전일가격', '원환산전일대비', '원환산전일등락', '당일환율', '전일환율', '액면가', '거래량', '거래대금']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 현재가상세[v1_해외주식-029]
|
||||
|
||||
해외주식 현재가상세 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- auth (str): 사용자권한정보 ()
|
||||
- excd (str): 거래소명 (HKS : 홍콩 NYS : 뉴욕 NAS : 나스닥 AMS : 아멕스 TSE : 도쿄 SHS : 상해 SZS : 심천 SHI : 상해지수 SZI : 심천지수 HSX : 호치민 HNX : 하노이 BAY : 뉴욕(주간) BAQ : 나스닥(주간) BAA : 아멕스(주간))
|
||||
- symb (str): 종목코드 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 현재가상세 결과
|
||||
|
||||
Example:
|
||||
>>> df = price_detail(auth="", excd="NAS", symb="TSLA")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = price_detail(
|
||||
auth="", # 사용자권한정보
|
||||
excd="NAS", # 거래소명
|
||||
symb="TSLA", # 종목코드
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 현재가상세 결과 ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 현재가상세[v1_해외주식-029]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/price-detail"
|
||||
|
||||
def price_detail(
|
||||
auth: str, # 사용자권한정보
|
||||
excd: str, # 거래소명
|
||||
symb: str, # 종목코드
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 현재가상세[v1_해외주식-029]
|
||||
해외주식 현재가상세 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
auth (str): 사용자권한정보
|
||||
excd (str): 거래소명 (예: HKS, NYS, NAS, AMS, TSE, SHS, SZS, SHI, SZI, HSX, HNX, BAY, BAQ, BAA)
|
||||
symb (str): 종목코드
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 현재가상세 데이터
|
||||
|
||||
Example:
|
||||
>>> df = price_detail(auth="your_auth_token", excd="NAS", symb="TSLA")
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not excd:
|
||||
logger.error("excd is required. (e.g. 'NAS')")
|
||||
raise ValueError("excd is required. (e.g. 'NAS')")
|
||||
if not symb:
|
||||
logger.error("symb is required. (e.g. 'TSLA')")
|
||||
raise ValueError("symb is required. (e.g. 'TSLA')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFS76200200"
|
||||
|
||||
params = {
|
||||
"AUTH": auth,
|
||||
"EXCD": excd,
|
||||
"SYMB": symb,
|
||||
}
|
||||
|
||||
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 price_detail(
|
||||
auth,
|
||||
excd,
|
||||
symb,
|
||||
"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()
|
||||
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from price_fluct import price_fluct
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 가격급등락[해외주식-038]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'knam': '종목명',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'n_base': '기준가격',
|
||||
'n_diff': '기준가격대비',
|
||||
'n_rate': '기준가격대비율',
|
||||
'enam': '영문종목명',
|
||||
'e_ordyn': '매매가능',
|
||||
'tamt': '거래대금',
|
||||
'n_last': '기준현재가'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 가격급등락 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 가격급등락 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 나스닥 급락 1분전 전체 조건을 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = price_fluct(excd="NAS", gubn="0", mixn="0", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 결과 처리
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 결과 처리
|
||||
logging.info("사용 가능한 컬럼 (output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,126 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 가격급등락[해외주식-038]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/price-fluct"
|
||||
|
||||
def price_fluct(
|
||||
excd: str, # [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
gubn: str, # [필수] 급등/급락구분 (ex. 0:급락, 1:급등)
|
||||
mixn: str, # [필수] N분전콤보값 (ex. 0:1분전, 1:2분전, 2:3분전, 3:5분전, 4:10분전, 5:15분전, 6:20분전, 7:30분전, 8:60분전, 9:120분전)
|
||||
vol_rang: str, # [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
auth: str = "", # 사용자권한정보
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 시세분석 > 해외주식 가격급등락[해외주식-038]
|
||||
해외주식 가격급등락 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
gubn (str): [필수] 급등/급락구분 (ex. 0:급락, 1:급등)
|
||||
mixn (str): [필수] N분전콤보값 (ex. 0:1분전, 1:2분전, 2:3분전, 3:5분전, 4:10분전, 5:15분전, 6:20분전, 7:30분전, 8:60분전, 9:120분전)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
keyb (str): NEXT KEY BUFF
|
||||
auth (str): 사용자권한정보
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: 해외주식 가격급등락 데이터 (output1, output2)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = price_fluct(excd="NAS", gubn="0", mixn="0", vol_rang="0")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NAS')")
|
||||
|
||||
if gubn == "":
|
||||
raise ValueError("gubn is required (e.g. '0' or '1')")
|
||||
|
||||
if mixn == "":
|
||||
raise ValueError("mixn is required (e.g. '0')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76260000" # 해외주식 가격급등락
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"GUBN": gubn,
|
||||
"MIXN": mixn,
|
||||
"VOL_RANG": vol_rang,
|
||||
"KEYB": keyb,
|
||||
"AUTH": auth
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리
|
||||
current_data1 = pd.DataFrame(res.getBody().output1, index=[0])
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return price_fluct(
|
||||
excd, gubn, mixn, vol_rang, keyb, auth, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from quot_inquire_ccnl import quot_inquire_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 체결추이[해외주식-037]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'vpow': '체결강도',
|
||||
'evol': '체결량',
|
||||
'khms': '한국기준시간',
|
||||
'tvol': '거래량',
|
||||
'last': '체결가',
|
||||
'mtyp': '시장구분',
|
||||
'sign': '기호',
|
||||
'pbid': '매수호가',
|
||||
'diff': '대비',
|
||||
'pask': '매도호가',
|
||||
'rate': '등락율'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['체결강도', '체결량', '거래량', '대비', '등락율', '매수호가', '매도호가']
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 체결추이 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 체결추이 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 TSLA를 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# case1 조회
|
||||
logging.info("=== case1 조회 ===")
|
||||
try:
|
||||
result = quot_inquire_ccnl(excd="NAS", tday="0", symb="TSLA")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logging.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 기본시세 > 해외주식 체결추이[해외주식-037]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/inquire-ccnl"
|
||||
|
||||
def quot_inquire_ccnl(
|
||||
excd: str, # [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
tday: str, # [필수] 당일전일구분 (ex. 0:전일, 1:당일)
|
||||
symb: str, # [필수] 종목코드 (ex. 해외종목코드)
|
||||
auth: str = "", # 사용자권한정보
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe: Optional[pd.DataFrame] = None, # 누적 데이터프레임
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외주식 체결추이 API입니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
tday (str): [필수] 당일전일구분 (ex. 0:전일, 1:당일)
|
||||
symb (str): [필수] 종목코드 (ex. 해외종목코드)
|
||||
auth (str): 사용자권한정보
|
||||
keyb (str): NEXT KEY BUFF
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외주식 체결추이 데이터
|
||||
|
||||
Example:
|
||||
>>> df = quot_inquire_ccnl(excd="NAS", tday="0", symb="TSLA")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NAS')")
|
||||
|
||||
if tday == "":
|
||||
raise ValueError("tday is required (e.g. '0' or '1')")
|
||||
|
||||
if symb == "":
|
||||
raise ValueError("symb is required (e.g. 'TSLA')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe is None:
|
||||
return pd.DataFrame()
|
||||
else:
|
||||
return dataframe
|
||||
|
||||
tr_id = "HHDFS76200300"
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"TDAY": tday,
|
||||
"SYMB": symb,
|
||||
"AUTH": auth,
|
||||
"KEYB": keyb
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output1)
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
keyb = res.getBody().keyb if hasattr(res.getBody(), 'keyb') else ""
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return quot_inquire_ccnl(
|
||||
excd, tday, symb, auth, keyb, "N", dataframe, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Created on 20250113
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from rights_by_ice import rights_by_ice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 권리종합 [해외주식-050]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'anno_dt': 'ICE공시일',
|
||||
'ca_title': '권리유형',
|
||||
'div_lock_dt': '배당락일',
|
||||
'pay_dt': '지급일',
|
||||
'record_dt': '기준일',
|
||||
'validity_dt': '효력일자',
|
||||
'local_end_dt': '현지지시마감일',
|
||||
'lock_dt': '권리락일',
|
||||
'delist_dt': '상장폐지일',
|
||||
'redempt_dt': '상환일자',
|
||||
'early_redempt_dt': '조기상환일자',
|
||||
'effective_dt': '적용일'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 권리종합 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 권리종합 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 NVDL(US) 종목을 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# case1 조회
|
||||
logging.info("=== case1 조회 ===")
|
||||
try:
|
||||
result = rights_by_ice(ncod="US", symb="NVDL")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logging.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
Created on 20250113
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 권리종합 [해외주식-050]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/rights-by-ice"
|
||||
|
||||
def rights_by_ice(
|
||||
ncod: str, # 국가코드 (CN:중국,HK:홍콩,US:미국,JP:일본,VN:베트남)
|
||||
symb: str, # 종목코드
|
||||
st_ymd: str = "", # 일자시작일 (미입력시 3개월전)
|
||||
ed_ymd: str = "" # 일자종료일 (미입력시 3개월후)
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외주식 권리종합 API입니다.
|
||||
한국투자 HTS(eFriend Plus) > [7833] 해외주식 권리(ICE제공) 화면의 "전체" 탭 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
|
||||
|
||||
※ 조회기간 기준일 입력시 참고 - 상환: 상환일자, 조기상환: 조기상환일자, 티커변경: 적용일, 그 외: 발표일
|
||||
|
||||
Args:
|
||||
ncod (str): [필수] 국가코드 (ex. CN:중국,HK:홍콩,US:미국,JP:일본,VN:베트남)
|
||||
symb (str): [필수] 종목코드
|
||||
st_ymd (str): 일자시작일 (ex. 미입력시 3개월전)
|
||||
ed_ymd (str): 일자종료일 (ex. 미입력시 3개월후)
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외주식 권리종합 정보
|
||||
|
||||
Raises:
|
||||
ValueError: 필수 파라미터가 누락되었을 때
|
||||
|
||||
Example:
|
||||
>>> df = rights_by_ice("US", "NVDL")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if ncod == "":
|
||||
raise ValueError("ncod is required (e.g. 'CN:중국,HK:홍콩,US:미국,JP:일본,VN:베트남')")
|
||||
|
||||
if symb == "":
|
||||
raise ValueError("symb is required")
|
||||
|
||||
tr_id = "HHDFS78330900"
|
||||
|
||||
params = {
|
||||
"NCOD": ncod, # 국가코드
|
||||
"SYMB": symb, # 종목코드
|
||||
"ST_YMD": st_ymd, # 일자시작일
|
||||
"ED_YMD": ed_ymd # 일자종료일
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, "", params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output1)
|
||||
logging.info("Data fetch complete.")
|
||||
return current_data
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from search_info import search_info
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 상품기본정보[v1_해외주식-034]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'std_pdno': '표준상품번호',
|
||||
'prdt_eng_name': '상품영문명',
|
||||
'natn_cd': '국가코드',
|
||||
'natn_name': '국가명',
|
||||
'tr_mket_cd': '거래시장코드',
|
||||
'tr_mket_name': '거래시장명',
|
||||
'ovrs_excg_cd': '해외거래소코드',
|
||||
'ovrs_excg_name': '해외거래소명',
|
||||
'tr_crcy_cd': '거래통화코드',
|
||||
'ovrs_papr': '해외액면가',
|
||||
'crcy_name': '통화명',
|
||||
'ovrs_stck_dvsn_cd': '해외주식구분코드',
|
||||
'prdt_clsf_cd': '상품분류코드',
|
||||
'prdt_clsf_name': '상품분류명',
|
||||
'sll_unit_qty': '매도단위수량',
|
||||
'buy_unit_qty': '매수단위수량',
|
||||
'tr_unit_amt': '거래단위금액',
|
||||
'lstg_stck_num': '상장주식수',
|
||||
'lstg_dt': '상장일자',
|
||||
'ovrs_stck_tr_stop_dvsn_cd': '해외주식거래정지구분코드',
|
||||
'lstg_abol_item_yn': '상장폐지종목여부',
|
||||
'ovrs_stck_prdt_grp_no': '해외주식상품그룹번호',
|
||||
'lstg_yn': '상장여부',
|
||||
'tax_levy_yn': '세금징수여부',
|
||||
'ovrs_stck_erlm_rosn_cd': '해외주식등록사유코드',
|
||||
'ovrs_stck_hist_rght_dvsn_cd': '해외주식이력권리구분코드',
|
||||
'chng_bf_pdno': '변경전상품번호',
|
||||
'prdt_type_cd_2': '상품유형코드2',
|
||||
'ovrs_item_name': '해외종목명',
|
||||
'sedol_no': 'SEDOL번호',
|
||||
'blbg_tckr_text': '블름버그티커내용',
|
||||
'ovrs_stck_etf_risk_drtp_cd': '해외주식ETF위험지표코드',
|
||||
'etp_chas_erng_rt_dbnb': 'ETP추적수익율배수',
|
||||
'istt_usge_isin_cd': '기관용도ISIN코드',
|
||||
'mint_svc_yn': 'MINT서비스여부',
|
||||
'mint_svc_yn_chng_dt': 'MINT서비스여부변경일자',
|
||||
'prdt_name': '상품명',
|
||||
'lei_cd': 'LEI코드',
|
||||
'ovrs_stck_stop_rson_cd': '해외주식정지사유코드',
|
||||
'lstg_abol_dt': '상장폐지일자',
|
||||
'mini_stk_tr_stat_dvsn_cd': '미니스탁거래상태구분코드',
|
||||
'mint_frst_svc_erlm_dt': 'MINT최초서비스등록일자',
|
||||
'mint_dcpt_trad_psbl_yn': 'MINT소수점매매가능여부',
|
||||
'mint_fnum_trad_psbl_yn': 'MINT정수매매가능여부',
|
||||
'mint_cblc_cvsn_ipsb_yn': 'MINT잔고전환불가여부',
|
||||
'ptp_item_yn': 'PTP종목여부',
|
||||
'ptp_item_trfx_exmt_yn': 'PTP종목양도세면제여부',
|
||||
'ptp_item_trfx_exmt_strt_dt': 'PTP종목양도세면제시작일자',
|
||||
'ptp_item_trfx_exmt_end_dt': 'PTP종목양도세면제종료일자',
|
||||
'dtm_tr_psbl_yn': '주간거래가능여부',
|
||||
'sdrf_stop_ecls_yn': '급등락정지제외여부',
|
||||
'sdrf_stop_ecls_erlm_dt': '급등락정지제외등록일자',
|
||||
'memo_text1': '메모내용1',
|
||||
'ovrs_now_pric1': '해외현재가격1',
|
||||
'last_rcvg_dtime': '최종수신일시'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['해외액면가', '매도단위수량', '매수단위수량', '거래단위금액', '상장주식수', 'ETP추적수익율배수', '해외현재가격1']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 상품기본정보[v1_해외주식-034]
|
||||
|
||||
해외주식 상품기본정보 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- prdt_type_cd (str): 상품유형코드 (512 미국 나스닥 / 513 미국 뉴욕 / 529 미국 아멕스 515 일본 501 홍콩 / 543 홍콩CNY / 558 홍콩USD 507 베트남 하노이 / 508 베트남 호치민 551 중국 상해A / 552 중국 심천A)
|
||||
- pdno (str): 상품번호 (예) AAPL (애플))
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외주식 상품기본정보 결과
|
||||
|
||||
Example:
|
||||
>>> df = search_info(prdt_type_cd="512", pdno="AAPL")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
logger.info("토큰 발급 중...")
|
||||
ka.auth()
|
||||
logger.info("토큰 발급 완료")
|
||||
|
||||
# 해외주식 상품기본정보 파라미터 설정
|
||||
logger.info("API 파라미터 설정 중...")
|
||||
prdt_type_cd = "512" # 상품유형코드
|
||||
pdno = "AAPL" # 상품번호
|
||||
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외주식 상품기본정보")
|
||||
result = search_info(
|
||||
prdt_type_cd=prdt_type_cd, # 상품유형코드
|
||||
pdno=pdno, # 상품번호
|
||||
)
|
||||
|
||||
if result is None or result.empty:
|
||||
logger.warning("조회된 데이터가 없습니다.")
|
||||
return
|
||||
|
||||
# 컬럼명 출력
|
||||
logger.info("사용 가능한 컬럼 목록:")
|
||||
logger.info(result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 처리
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result.columns:
|
||||
result[col] = pd.to_numeric(result[col], errors='coerce')
|
||||
|
||||
# 결과 출력
|
||||
logger.info("=== 해외주식 상품기본정보 결과 ===")
|
||||
logger.info("조회된 데이터 건수: %d", len(result))
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("에러 발생: %s", str(e))
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,109 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-06-30
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
import sys
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 상품기본정보[v1_해외주식-034]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-price/v1/quotations/search-info"
|
||||
|
||||
|
||||
def search_info(
|
||||
prdt_type_cd: str, # 상품유형코드
|
||||
pdno: str, # 상품번호
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 기본시세
|
||||
해외주식 상품기본정보[v1_해외주식-034]
|
||||
해외주식 상품기본정보 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
prdt_type_cd (str): 512 미국 나스닥 / 513 미국 뉴욕 / 529 미국 아멕스 515 일본 501 홍콩 / 543 홍콩CNY / 558 홍콩USD 507 베트남 하노이 / 508 베트남 호치민 551 중국 상해A / 552 중국 심천A
|
||||
pdno (str): 예) AAPL (애플)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외주식 상품기본정보 데이터
|
||||
|
||||
Example:
|
||||
>>> df = search_info(prdt_type_cd="512", pdno="AAPL")
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not prdt_type_cd:
|
||||
logger.error("prdt_type_cd is required. (e.g. '512')")
|
||||
raise ValueError("prdt_type_cd is required. (e.g. '512')")
|
||||
if not pdno:
|
||||
logger.error("pdno is required. (e.g. 'AAPL')")
|
||||
raise ValueError("pdno is required. (e.g. 'AAPL')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "CTPF1702R"
|
||||
|
||||
params = {
|
||||
"PRDT_TYPE_CD": prdt_type_cd,
|
||||
"PDNO": pdno,
|
||||
}
|
||||
|
||||
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_info(
|
||||
prdt_type_cd,
|
||||
pdno,
|
||||
"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()
|
||||
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from trade_growth import trade_growth
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래증가율순위[해외주식-045]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'crec': '현재조회종목수',
|
||||
'trec': '전체조회종목수',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'tvol': '거래량',
|
||||
'n_tvol': '평균거래량',
|
||||
'n_rate': '증가율',
|
||||
'rank': '순위',
|
||||
'ename': '영문종목명',
|
||||
'e_ordyn': '매매가능',
|
||||
'tamt': '거래대금',
|
||||
'n_diff': '증가량'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 거래증가율순위 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 거래증가율순위 API를 호출하여 결과를 출력합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = trade_growth(excd="NAS", nday="0", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("사용 가능한 컬럼(output1): %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("사용 가능한 컬럼(output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래증가율순위[해외주식-045]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/trade-growth"
|
||||
|
||||
def trade_growth(
|
||||
excd: str, # [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday: str, # [필수] N일자값 (ex. 0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년)
|
||||
vol_rang: str, # [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth: str = "", # 사용자권한정보
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 기본시세 > 해외주식 거래증가율순위[해외주식-045]
|
||||
해외주식 거래증가율순위 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday (str): [필수] N일자값 (ex. 0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth (str): 사용자권한정보
|
||||
keyb (str): NEXT KEY BUFF
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1, output2) 데이터프레임 튜플
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = trade_growth(excd="NAS", nday="0", vol_rang="0")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS')")
|
||||
|
||||
if nday == "":
|
||||
raise ValueError("nday is required (e.g. '0')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76330000" # 해외주식 거래증가율순위
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"NDAY": nday,
|
||||
"VOL_RANG": vol_rang,
|
||||
"AUTH": auth,
|
||||
"KEYB": keyb
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리 (object)
|
||||
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 처리 (array)
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return trade_growth(
|
||||
excd, nday, vol_rang, auth, keyb, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from trade_pbmn import trade_pbmn
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래대금순위[해외주식-044]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'crec': '현재조회종목수',
|
||||
'trec': '전체조회종목수',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'tvol': '거래량',
|
||||
'tamt': '거래대금',
|
||||
'a_tamt': '평균거래대금',
|
||||
'rank': '순위',
|
||||
'ename': '영문종목명',
|
||||
'e_ordyn': '매매가능'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 거래대금순위 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 거래대금순위 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 나스닥 거래소를 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = trade_pbmn(excd="NAS", nday="0", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("사용 가능한 컬럼 (output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
127
한국투자증권(API)/examples_llm/overseas_stock/trade_pbmn/trade_pbmn.py
Normal file
127
한국투자증권(API)/examples_llm/overseas_stock/trade_pbmn/trade_pbmn.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래대금순위[해외주식-044]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/trade-pbmn"
|
||||
|
||||
def trade_pbmn(
|
||||
excd: str, # [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday: str, # [필수] N일자값 (ex. 0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년)
|
||||
vol_rang: str, # [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth: str = "", # 사용자권한정보
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
prc1: str = "", # 현재가 필터범위 시작
|
||||
prc2: str = "", # 현재가 필터범위 끝
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
해외주식 거래대금순위 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday (str): [필수] N일자값 (ex. 0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth (str): 사용자권한정보
|
||||
keyb (str): NEXT KEY BUFF
|
||||
prc1 (str): 현재가 필터범위 시작
|
||||
prc2 (str): 현재가 필터범위 끝
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: 거래대금순위 데이터 (output1, output2)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = trade_pbmn(excd="NAS", nday="0", vol_rang="0")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄')")
|
||||
|
||||
if nday == "":
|
||||
raise ValueError("nday is required (e.g. '0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None or dataframe2 is None:
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
else:
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76320010" # 해외주식 거래대금순위
|
||||
|
||||
params = {
|
||||
"EXCD": excd, # 거래소명
|
||||
"NDAY": nday, # N일자값
|
||||
"VOL_RANG": vol_rang, # 거래량조건
|
||||
"AUTH": auth, # 사용자권한정보
|
||||
"KEYB": keyb, # NEXT KEY BUFF
|
||||
"PRC1": prc1, # 현재가 필터범위 시작
|
||||
"PRC2": prc2, # 현재가 필터범위 끝
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리 (object 타입)
|
||||
current_data1 = pd.DataFrame([res.getBody().output1])
|
||||
|
||||
# output2 처리 (array 타입)
|
||||
current_data2 = pd.DataFrame(res.getBody().output2)
|
||||
|
||||
if dataframe1 is not None:
|
||||
dataframe1 = pd.concat([dataframe1, current_data1], ignore_index=True)
|
||||
else:
|
||||
dataframe1 = current_data1
|
||||
|
||||
if dataframe2 is not None:
|
||||
dataframe2 = pd.concat([dataframe2, current_data2], ignore_index=True)
|
||||
else:
|
||||
dataframe2 = current_data2
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
keyb = res.getBody().keyb if hasattr(res.getBody(), 'keyb') else ""
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return trade_pbmn(
|
||||
excd, nday, vol_rang, auth, keyb, prc1, prc2, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from trade_turnover import trade_turnover
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래회전율순위[해외주식-046]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'crec': '현재조회종목수',
|
||||
'trec': '전체조회종목수',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'n_tvol': '평균거래량',
|
||||
'shar': '상장주식수',
|
||||
'tover': '회전율',
|
||||
'rank': '순위',
|
||||
'ename': '영문종목명',
|
||||
'e_ordyn': '매매가능',
|
||||
'tamt': '거래대금',
|
||||
'trat': '회전율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 거래회전율순위 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 거래회전율순위 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 상해거래소 데이터를 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = trade_turnover(excd="SHS", nday="0", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 결과 처리
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 결과 처리
|
||||
logging.info("사용 가능한 컬럼 (output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래회전율순위[해외주식-046]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/trade-turnover"
|
||||
|
||||
def trade_turnover(
|
||||
excd: str, # 거래소명
|
||||
nday: str, # N분전콤보값
|
||||
vol_rang: str, # 거래량조건
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
auth: str = "", # 사용자권한정보
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output1)
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임 (output2)
|
||||
depth: int = 0, # 내부 재귀 깊이 (자동 관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 시세분석 > 해외주식 거래회전율순위[해외주식-046]
|
||||
해외주식 거래회전율순위 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday (str): [필수] N분전콤보값 (ex. 0:당일, 1:2일전, 2:3일전, 3:5일전, 4:10일전, 5:20일전, 6:30일전, 7:60일전, 8:120일전, 9:1년전)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
keyb (str): NEXT KEY BUFF
|
||||
auth (str): 사용자권한정보
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임 (output1)
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임 (output2)
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1, output2) 해외주식 거래회전율순위 데이터
|
||||
|
||||
Example:
|
||||
>>> result1, result2 = trade_turnover(excd="SHS", nday="0", vol_rang="0")
|
||||
>>> print(result1)
|
||||
>>> print(result2)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄')")
|
||||
|
||||
if nday == "":
|
||||
raise ValueError("nday is required (e.g. '0:당일, 1:2일전, 2:3일전, 3:5일전, 4:10일전, 5:20일전, 6:30일전, 7:60일전, 8:120일전, 9:1년전')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상')")
|
||||
|
||||
# 재귀 깊이 제한 확인
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76340000" # 해외주식 거래회전율순위
|
||||
|
||||
params = {
|
||||
"EXCD": excd, # 거래소명
|
||||
"NDAY": nday, # N분전콤보값
|
||||
"VOL_RANG": vol_rang, # 거래량조건
|
||||
"KEYB": keyb, # NEXT KEY BUFF
|
||||
"AUTH": auth # 사용자권한정보
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# 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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return trade_turnover(
|
||||
excd, nday, vol_rang, keyb, auth, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from trade_vol import trade_vol
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래량순위[해외주식-043]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'crec': '현재조회종목수',
|
||||
'trec': '전체조회종목수',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'tvol': '거래량',
|
||||
'tamt': '거래대금',
|
||||
'a_tvol': '평균거래량',
|
||||
'rank': '순위',
|
||||
'ename': '영문종목명',
|
||||
'e_ordyn': '매매가능'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 거래량순위 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 거래량순위 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 하노이 거래소(HNX), 당일(0), 전체(0)를 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = trade_vol(excd="HNX", nday="0", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("사용 가능한 컬럼 (output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
127
한국투자증권(API)/examples_llm/overseas_stock/trade_vol/trade_vol.py
Normal file
127
한국투자증권(API)/examples_llm/overseas_stock/trade_vol/trade_vol.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래량순위[해외주식-043]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/trade-vol"
|
||||
|
||||
def trade_vol(
|
||||
excd: str, # 거래소명
|
||||
nday: str, # N분전콤보값
|
||||
vol_rang: str, # 거래량조건
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
auth: str = "", # 사용자권한정보
|
||||
prc1: str = "", # 가격 필터 시작
|
||||
prc2: str = "", # 가격 필터 종료
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 시세분석 > 해외주식 거래량순위[해외주식-043]
|
||||
해외주식 거래량순위 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday (str): [필수] N분전콤보값 (ex. 0:당일, 1:2일전, 2:3일전, 3:5일전, 4:10일전, 5:20일전, 6:30일전, 7:60일전, 8:120일전, 9:1년전)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
keyb (str): NEXT KEY BUFF (ex. "")
|
||||
auth (str): 사용자권한정보 (ex. "")
|
||||
prc1 (str): 가격 필터 시작 (ex. "")
|
||||
prc2 (str): 가격 필터 종료 (ex. "")
|
||||
tr_cont (str): 연속거래여부 (ex. "")
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: 해외주식 거래량순위 데이터 (output1, output2)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = trade_vol(excd="NYS", nday="0", vol_rang="0")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄')")
|
||||
|
||||
if nday == "":
|
||||
raise ValueError("nday is required (e.g. '0:당일, 1:2일전, 2:3일전, 3:5일전, 4:10일전, 5:20일전, 6:30일전, 7:60일전, 8:120일전, 9:1년전')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76310010" # 해외주식 거래량순위
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"NDAY": nday,
|
||||
"VOL_RANG": vol_rang,
|
||||
"KEYB": keyb,
|
||||
"AUTH": auth,
|
||||
"PRC1": prc1,
|
||||
"PRC2": prc2
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리
|
||||
current_data1 = pd.DataFrame(res.getBody().output1, index=[0])
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return trade_vol(
|
||||
excd, nday, vol_rang, keyb, auth, prc1, prc2, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from updown_rate import updown_rate
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 등락율순위[해외주식-041]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태정보',
|
||||
'crec': '현재조회종목수',
|
||||
'trec': '전체조회종목수',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'n_base': '기준가격',
|
||||
'n_diff': '기준가격대비',
|
||||
'n_rate': '기준가격대비율',
|
||||
'rank': '순위',
|
||||
'ename': '영문종목명',
|
||||
'e_ordyn': '매매가능',
|
||||
'tamt': '거래대금'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 등락율순위 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 등락율순위 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 상해거래소 급등 당일 전체를 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = updown_rate(excd="SHS", gubn="1", nday="0", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("사용 가능한 컬럼 (output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 상승률/하락률[해외주식-041]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/updown-rate"
|
||||
|
||||
def updown_rate(
|
||||
excd: str, # [필수] 거래소명
|
||||
nday: str, # [필수] N일자값
|
||||
gubn: str, # [필수] 상승율/하락율 구분
|
||||
vol_rang: str, # [필수] 거래량조건
|
||||
auth: str = "", # 사용자권한정보
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
해외주식 상승률/하락률 순위를 조회합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday (str): [필수] N일자값 (ex. 0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년)
|
||||
gubn (str): [필수] 상승율/하락율 구분 (ex. 0:하락율, 1:상승율)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth (str): 사용자권한정보
|
||||
keyb (str): NEXT KEY BUFF
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: 상승률/하락률 순위 데이터
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = updown_rate(excd="NYS", nday="0", gubn="1", vol_rang="0")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄')")
|
||||
|
||||
if nday == "":
|
||||
raise ValueError("nday is required (e.g. '0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년')")
|
||||
|
||||
if gubn == "":
|
||||
raise ValueError("gubn is required (e.g. '0:하락율, 1:상승율')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None and dataframe2 is None:
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
else:
|
||||
return dataframe1 if dataframe1 is not None else pd.DataFrame(), dataframe2 if dataframe2 is not None else pd.DataFrame()
|
||||
|
||||
tr_id = "HHDFS76290000"
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"NDAY": nday,
|
||||
"GUBN": gubn,
|
||||
"VOL_RANG": vol_rang,
|
||||
"AUTH": auth,
|
||||
"KEYB": keyb
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# 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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return updown_rate(
|
||||
excd, nday, gubn, vol_rang, auth, keyb, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from volume_power import volume_power
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 매수체결강도상위[해외주식-040]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'knam': '종목명',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'tpow': '당일체결강도',
|
||||
'powx': '체결강도',
|
||||
'enam': '영문종목명',
|
||||
'e_ordyn': '매매가능',
|
||||
'tamt': '거래대금',
|
||||
'bivl': '매도체결량',
|
||||
'asvl': '매수체결량',
|
||||
'strn': '체결강도'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 매수체결강도상위 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 매수체결강도상위 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 홍콩거래소(HKS)를 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = volume_power(excd="HKS", nday="0", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("사용 가능한 컬럼 (output1): %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output1):")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("사용 가능한 컬럼 (output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 매수체결강도상위[해외주식-040]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/volume-power"
|
||||
|
||||
def volume_power(
|
||||
excd: str, # [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday: str, # [필수] N일자값 (ex. 0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년)
|
||||
vol_rang: str, # [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth: str = "", # 사용자권한정보
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # output1 누적 데이터프레임
|
||||
dataframe2: Optional[pd.DataFrame] = None, # output2 누적 데이터프레임
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 시세분석 > 해외주식 매수체결강도상위[해외주식-040]
|
||||
|
||||
해외주식 매수 체결강도 상위 종목을 조회합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
nday (str): [필수] N일자값 (ex. 0:당일, 1:2일, 2:3일, 3:5일, 4:10일, 5:20일전, 6:30일, 7:60일, 8:120일, 9:1년)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
auth (str): 사용자권한정보
|
||||
keyb (str): NEXT KEY BUFF
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): output1 누적 데이터프레임
|
||||
dataframe2 (Optional[pd.DataFrame]): output2 누적 데이터프레임
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1 데이터, output2 데이터)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = volume_power(excd="HKS", nday="0", vol_rang="0")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'HKS')")
|
||||
|
||||
if nday == "":
|
||||
raise ValueError("nday is required (e.g. '0')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76280000"
|
||||
|
||||
params = {
|
||||
"EXCD": excd,
|
||||
"NDAY": nday,
|
||||
"VOL_RANG": vol_rang,
|
||||
"AUTH": auth,
|
||||
"KEYB": keyb
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리 (object)
|
||||
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 처리 (array)
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return volume_power(
|
||||
excd, nday, vol_rang, auth, keyb, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from volume_surge import volume_surge
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래량급증[해외주식-039]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼 매핑 정의
|
||||
COLUMN_MAPPING = {
|
||||
'zdiv': '소수점자리수',
|
||||
'stat': '거래상태',
|
||||
'nrec': 'RecordCount',
|
||||
'rsym': '실시간조회심볼',
|
||||
'excd': '거래소코드',
|
||||
'symb': '종목코드',
|
||||
'knam': '종목명',
|
||||
'name': '종목명',
|
||||
'last': '현재가',
|
||||
'sign': '기호',
|
||||
'diff': '대비',
|
||||
'rate': '등락율',
|
||||
'tvol': '거래량',
|
||||
'pask': '매도호가',
|
||||
'pbid': '매수호가',
|
||||
'n_tvol': '기준거래량',
|
||||
'n_diff': '증가량',
|
||||
'n_rate': '증가율',
|
||||
'enam': '영문종목명',
|
||||
'e_ordyn': '매매가능',
|
||||
'tamt': '거래대금',
|
||||
'trat': '거래량비율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼 정의
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외주식 거래량급증 조회 테스트 함수
|
||||
|
||||
이 함수는 해외주식 거래량급증 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 뉴욕거래소(NYS) 데이터를 사용합니다.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 인증 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logging.info("API 호출")
|
||||
try:
|
||||
result1, result2 = volume_surge(excd="NYS", mixn="0", vol_rang="0")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 처리
|
||||
logging.info("사용 가능한 컬럼: %s", result1.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result1 = result1.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result1.columns:
|
||||
result1[col] = pd.to_numeric(result1[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과:")
|
||||
print(result1)
|
||||
|
||||
# output2 처리
|
||||
logging.info("사용 가능한 컬럼 (output2): %s", result2.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result2 = result2.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시
|
||||
for col in NUMERIC_COLUMNS:
|
||||
if col in result2.columns:
|
||||
result2[col] = pd.to_numeric(result2[col], errors='coerce').round(2)
|
||||
|
||||
logging.info("결과(output2):")
|
||||
print(result2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외주식] 시세분석 > 해외주식 거래량급증[해외주식-039]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-stock/v1/ranking/volume-surge"
|
||||
|
||||
def volume_surge(
|
||||
excd: str, # [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
mixn: str, # [필수] N분전콤보값 (ex. 0:1분전, 1:2분전, 2:3분전, 3:5분전, 4:10분전, 5:15분전, 6:20분전, 7:30분전, 8:60분전, 9:120분전)
|
||||
vol_rang: str, # [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
keyb: str = "", # NEXT KEY BUFF
|
||||
auth: str = "", # 사용자권한정보
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
[해외주식] 시세분석 > 해외주식 거래량급증[해외주식-039]
|
||||
해외주식 거래량급증 정보를 조회합니다.
|
||||
|
||||
Args:
|
||||
excd (str): [필수] 거래소명 (ex. NYS:뉴욕, NAS:나스닥, AMS:아멕스, HKS:홍콩, SHS:상해, SZS:심천, HSX:호치민, HNX:하노이, TSE:도쿄)
|
||||
mixn (str): [필수] N분전콤보값 (ex. 0:1분전, 1:2분전, 2:3분전, 3:5분전, 4:10분전, 5:15분전, 6:20분전, 7:30분전, 8:60분전, 9:120분전)
|
||||
vol_rang (str): [필수] 거래량조건 (ex. 0:전체, 1:1백주이상, 2:1천주이상, 3:1만주이상, 4:10만주이상, 5:100만주이상, 6:1000만주이상)
|
||||
keyb (str): NEXT KEY BUFF
|
||||
auth (str): 사용자권한정보
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1 데이터, output2 데이터)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = volume_surge(excd="NYS", mixn="0", vol_rang="0")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
if excd == "":
|
||||
raise ValueError("excd is required (e.g. 'NYS')")
|
||||
|
||||
if mixn == "":
|
||||
raise ValueError("mixn is required (e.g. '0')")
|
||||
|
||||
if vol_rang == "":
|
||||
raise ValueError("vol_rang is required (e.g. '0')")
|
||||
|
||||
if depth > max_depth:
|
||||
logging.warning("Max recursive depth reached.")
|
||||
if dataframe1 is None:
|
||||
dataframe1 = pd.DataFrame()
|
||||
if dataframe2 is None:
|
||||
dataframe2 = pd.DataFrame()
|
||||
return dataframe1, dataframe2
|
||||
|
||||
tr_id = "HHDFS76270000" # 해외주식 거래량급증
|
||||
|
||||
params = {
|
||||
"EXCD": excd, # 거래소명
|
||||
"MIXN": mixn, # N분전콤보값
|
||||
"VOL_RANG": vol_rang, # 거래량조건
|
||||
"KEYB": keyb, # NEXT KEY BUFF
|
||||
"AUTH": auth # 사용자권한정보
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output1 처리
|
||||
current_data1 = pd.DataFrame(res.getBody().output1, index=[0])
|
||||
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
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return volume_surge(
|
||||
excd, mixn, vol_rang, keyb, auth, "N", dataframe1, dataframe2, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logging.info("Data fetch complete.")
|
||||
return dataframe1, dataframe2
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
Reference in New Issue
Block a user