initial commit
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션]실시간시세 > 해외선물옵션 실시간호가[실시간-018]
|
||||
##############################################################################################
|
||||
|
||||
def asking_price(
|
||||
tr_type: str,
|
||||
tr_key: str,
|
||||
) -> tuple[dict, list[str]]:
|
||||
"""
|
||||
[해외선물옵션]실시간시세 > 해외선물옵션 실시간호가[실시간-018]
|
||||
|
||||
※ CME, SGX 실시간시세 유료시세 신청 필수 (KIS포털 > FAQ > 자주 묻는 질문 > 해외선물옵션 API 유료시세 신청방법(CME, SGX 거래소))
|
||||
- CME, SGX 거래소 실시간시세는 유료시세 신청 후 이용하시는 모든 계좌에 대해서 접근토큰발급 API 호출하셔야 하며,
|
||||
접근토큰발급 이후 2시간 이내로 신청정보가 동기화되어 유료시세 수신이 가능해집니다.
|
||||
- CME, SGX 거래소 종목은 유료시세 신청되어 있지 않으면 실시간시세 종목등록이 불가하며,
|
||||
등록 시도 시 "SUBSCRIBE ERROR : mci send failed" 에러가 발생합니다.
|
||||
|
||||
(중요) 해외선물옵션시세 출력값을 해석하실 때 ffcode.mst(해외선물종목마스터 파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
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 = "HDFFF010"
|
||||
|
||||
params = {
|
||||
"tr_key": tr_key,
|
||||
}
|
||||
|
||||
msg = ka.data_fetch(tr_id, tr_type, params)
|
||||
|
||||
columns = [
|
||||
"series_cd",
|
||||
"recv_date",
|
||||
"recv_time",
|
||||
"prev_price",
|
||||
"bid_qntt_1",
|
||||
"bid_num_1",
|
||||
"bid_price_1",
|
||||
"ask_qntt_1",
|
||||
"ask_num_1",
|
||||
"ask_price_1",
|
||||
"bid_qntt_2",
|
||||
"bid_num_2",
|
||||
"bid_price_2",
|
||||
"ask_qntt_2",
|
||||
"ask_num_2",
|
||||
"ask_price_2",
|
||||
"bid_qntt_3",
|
||||
"bid_num_3",
|
||||
"bid_price_3",
|
||||
"ask_qntt_3",
|
||||
"ask_num_3",
|
||||
"ask_price_3",
|
||||
"bid_qntt_4",
|
||||
"bid_num_4",
|
||||
"bid_price_4",
|
||||
"ask_qntt_4",
|
||||
"ask_num_4",
|
||||
"ask_price_4",
|
||||
"bid_qntt_5",
|
||||
"bid_num_5",
|
||||
"bid_price_5",
|
||||
"ask_qntt_5",
|
||||
"ask_num_5",
|
||||
"ask_price_5",
|
||||
"sttl_price"
|
||||
]
|
||||
|
||||
return msg, columns
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
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)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션]실시간시세 > 해외선물옵션 실시간호가[실시간-018]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
"series_cd": "종목코드",
|
||||
"recv_date": "수신일자",
|
||||
"recv_time": "수신시각",
|
||||
"prev_price": "전일종가",
|
||||
"bid_qntt_1": "매수1수량",
|
||||
"bid_num_1": "매수1번호",
|
||||
"bid_price_1": "매수1호가",
|
||||
"ask_qntt_1": "매도1수량",
|
||||
"ask_num_1": "매도1번호",
|
||||
"ask_price_1": "매도1호가",
|
||||
"bid_qntt_2": "매수2수량",
|
||||
"bid_num_2": "매수2번호",
|
||||
"bid_price_2": "매수2호가",
|
||||
"ask_qntt_2": "매도2수량",
|
||||
"ask_num_2": "매도2번호",
|
||||
"ask_price_2": "매도2호가",
|
||||
"bid_qntt_3": "매수3수량",
|
||||
"bid_num_3": "매수3번호",
|
||||
"bid_price_3": "매수3호가",
|
||||
"ask_qntt_3": "매도3수량",
|
||||
"ask_num_3": "매도3번호",
|
||||
"ask_price_3": "매도3호가",
|
||||
"bid_qntt_4": "매수4수량",
|
||||
"bid_num_4": "매수4번호",
|
||||
"bid_price_4": "매수4호가",
|
||||
"ask_qntt_4": "매도4수량",
|
||||
"ask_num_4": "매도4번호",
|
||||
"ask_price_4": "매도4호가",
|
||||
"bid_qntt_5": "매수5수량",
|
||||
"bid_num_5": "매수5번호",
|
||||
"bid_price_5": "매수5호가",
|
||||
"ask_qntt_5": "매도5수량",
|
||||
"ask_num_5": "매도5번호",
|
||||
"ask_price_5": "매도5호가",
|
||||
"sttl_price": "전일정산가"
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ["전일종가", "매수1수량", "매수1호가", "매도1수량", "매도1호가",
|
||||
"매수2수량", "매수2호가", "매도2수량", "매도2호가",
|
||||
"매수3수량", "매수3호가", "매도3수량", "매도3호가",
|
||||
"매수4수량", "매수4호가", "매도4수량", "매도4호가",
|
||||
"매수5수량", "매수5호가", "매도5수량", "매도5호가", "전일정산가"]
|
||||
|
||||
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=["SPIU25"])
|
||||
|
||||
# 결과 표시
|
||||
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()
|
||||
85
한국투자증권(API)/examples_llm/overseas_futureoption/ccnl/ccnl.py
Normal file
85
한국투자증권(API)/examples_llm/overseas_futureoption/ccnl/ccnl.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션]실시간시세 > 해외선물옵션 실시간체결가[실시간-017]
|
||||
##############################################################################################
|
||||
|
||||
def ccnl(
|
||||
tr_type: str,
|
||||
tr_key: str,
|
||||
) -> tuple[dict, list[str]]:
|
||||
"""
|
||||
※ CME, SGX 실시간시세 유료시세 신청 필수 (KIS포털 > FAQ > 자주 묻는 질문 > 해외선물옵션 API 유료시세 신청방법(CME, SGX 거래소))
|
||||
- CME, SGX 거래소 실시간시세는 유료시세 신청 후 이용하시는 모든 계좌에 대해서 접근토큰발급 API 호출하셔야 하며,
|
||||
접근토큰발급 이후 2시간 이내로 신청정보가 동기화되어 유료시세 수신이 가능해집니다.
|
||||
- CME, SGX 거래소 종목은 유료시세 신청되어 있지 않으면 실시간시세 종목등록이 불가하며,
|
||||
등록 시도 시 "SUBSCRIBE ERROR : mci send failed" 에러가 발생합니다.
|
||||
|
||||
Args:
|
||||
tr_type (str): [필수] 등록/해제
|
||||
tr_key (str): [필수] 종목코드
|
||||
|
||||
Returns:
|
||||
message (dict): 메시지 데이터
|
||||
columns (list[str]): 컬럼 정보
|
||||
|
||||
Example:
|
||||
>>> msg, columns = 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 = "HDFFF020"
|
||||
|
||||
params = {
|
||||
"tr_key": tr_key,
|
||||
}
|
||||
|
||||
msg = ka.data_fetch(tr_id, tr_type, params)
|
||||
|
||||
columns = [
|
||||
"series_cd",
|
||||
"bsns_date",
|
||||
"mrkt_open_date",
|
||||
"mrkt_open_time",
|
||||
"mrkt_close_date",
|
||||
"mrkt_close_time",
|
||||
"prev_price",
|
||||
"recv_date",
|
||||
"recv_time",
|
||||
"active_flag",
|
||||
"last_price",
|
||||
"last_qntt",
|
||||
"prev_diff_price",
|
||||
"prev_diff_rate",
|
||||
"open_price",
|
||||
"high_price",
|
||||
"low_price",
|
||||
"vol",
|
||||
"prev_sign",
|
||||
"quotsign",
|
||||
"recv_time2",
|
||||
"psttl_price",
|
||||
"psttl_sign",
|
||||
"psttl_diff_price",
|
||||
"psttl_diff_rate"
|
||||
]
|
||||
|
||||
return msg, columns
|
||||
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from ccnl import ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션]실시간시세 > 해외선물옵션 실시간체결가[실시간-017]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
"series_cd": "종목코드",
|
||||
"bsns_date": "영업일자",
|
||||
"mrkt_open_date": "장개시일자",
|
||||
"mrkt_open_time": "장개시시각",
|
||||
"mrkt_close_date": "장종료일자",
|
||||
"mrkt_close_time": "장종료시각",
|
||||
"prev_price": "전일종가",
|
||||
"recv_date": "수신일자",
|
||||
"recv_time": "수신시각",
|
||||
"active_flag": "본장_전산장구분",
|
||||
"last_price": "체결가격",
|
||||
"last_qntt": "체결수량",
|
||||
"prev_diff_price": "전일대비가",
|
||||
"prev_diff_rate": "등락률",
|
||||
"open_price": "시가",
|
||||
"high_price": "고가",
|
||||
"low_price": "저가",
|
||||
"vol": "누적거래량",
|
||||
"prev_sign": "전일대비부호",
|
||||
"quotsign": "체결구분",
|
||||
"recv_time2": "수신시각2 만분의일초",
|
||||
"psttl_price": "전일정산가",
|
||||
"psttl_sign": "전일정산가대비",
|
||||
"psttl_diff_price": "전일정산가대비가격",
|
||||
"psttl_diff_rate": "전일정산가대비율"
|
||||
}
|
||||
|
||||
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=ccnl, data=["1OZQ25"])
|
||||
|
||||
# 결과 표시
|
||||
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)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션]실시간시세 > 해외선물옵션 실시간체결내역통보[실시간-020]
|
||||
##############################################################################################
|
||||
|
||||
def ccnl_notice(
|
||||
tr_type: str,
|
||||
tr_key: str,
|
||||
) -> tuple[dict, list[str]]:
|
||||
"""
|
||||
해외선물옵션 실시간체결내역통보 API입니다.
|
||||
|
||||
Args:
|
||||
tr_type (str): [필수] 등록/해제
|
||||
tr_key (str): [필수] 종목코드
|
||||
|
||||
Returns:
|
||||
message (dict): 메시지 데이터
|
||||
columns (list[str]): 컬럼 정보
|
||||
|
||||
Example:
|
||||
>>> msg, columns = ccnl_notice("1", trenv.my_htsid)
|
||||
>>> print(msg, columns)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if tr_type == "":
|
||||
raise ValueError("tr_type is required")
|
||||
|
||||
if tr_key == "":
|
||||
raise ValueError("tr_key is required")
|
||||
|
||||
tr_id = "HDFFF2C0"
|
||||
|
||||
params = {
|
||||
"tr_key": tr_key,
|
||||
}
|
||||
|
||||
msg = ka.data_fetch(tr_id, tr_type, params)
|
||||
|
||||
columns = [
|
||||
"acct_no",
|
||||
"ord_dt",
|
||||
"odno",
|
||||
"orgn_ord_dt",
|
||||
"orgn_odno",
|
||||
"series",
|
||||
"rvse_cncl_dvsn_cd",
|
||||
"sll_buy_dvsn_cd",
|
||||
"cplx_ord_dvsn_cd",
|
||||
"prce_tp",
|
||||
"fm_excg_rcit_dvsn_cd",
|
||||
"ord_qty",
|
||||
"fm_lmt_pric",
|
||||
"fm_stop_ord_pric",
|
||||
"tot_ccld_qty",
|
||||
"tot_ccld_uv",
|
||||
"ord_remq",
|
||||
"fm_ord_grp_dt",
|
||||
"ord_grp_stno",
|
||||
"ord_dtl_dtime",
|
||||
"oprt_dtl_dtime",
|
||||
"work_empl",
|
||||
"ccld_dt",
|
||||
"ccno",
|
||||
"api_ccno",
|
||||
"ccld_qty",
|
||||
"fm_ccld_pric",
|
||||
"crcy_cd",
|
||||
"trst_fee",
|
||||
"ord_mdia_online_yn",
|
||||
"fm_ccld_amt",
|
||||
"fuop_item_dvsn_cd"
|
||||
]
|
||||
|
||||
return msg, columns
|
||||
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
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)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션]실시간시세 > 해외선물옵션 실시간체결내역통보[실시간-020]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
"acct_no": "계좌번호",
|
||||
"ord_dt": "주문일자",
|
||||
"odno": "주문번호",
|
||||
"orgn_ord_dt": "원주문일자",
|
||||
"orgn_odno": "원주문번호",
|
||||
"series": "종목명",
|
||||
"rvse_cncl_dvsn_cd": "정정취소구분코드",
|
||||
"sll_buy_dvsn_cd": "매도매수구분코드",
|
||||
"cplx_ord_dvsn_cd": "복합주문구분코드",
|
||||
"prce_tp": "가격구분코드",
|
||||
"fm_excg_rcit_dvsn_cd": "FM거래소접수구분코드",
|
||||
"ord_qty": "주문수량",
|
||||
"fm_lmt_pric": "FMLIMIT가격",
|
||||
"fm_stop_ord_pric": "FMSTOP주문가격",
|
||||
"tot_ccld_qty": "총체결수량",
|
||||
"tot_ccld_uv": "총체결단가",
|
||||
"ord_remq": "잔량",
|
||||
"fm_ord_grp_dt": "FM주문그룹일자",
|
||||
"ord_grp_stno": "주문그룹번호",
|
||||
"ord_dtl_dtime": "주문상세일시",
|
||||
"oprt_dtl_dtime": "조작상세일시",
|
||||
"work_empl": "주문자",
|
||||
"ccld_dt": "체결일자",
|
||||
"ccno": "체결번호",
|
||||
"api_ccno": "API 체결번호",
|
||||
"ccld_qty": "체결수량",
|
||||
"fm_ccld_pric": "FM체결가격",
|
||||
"crcy_cd": "통화코드",
|
||||
"trst_fee": "위탁수수료",
|
||||
"ord_mdia_online_yn": "주문매체온라인여부",
|
||||
"fm_ccld_amt": "FM체결금액",
|
||||
"fuop_item_dvsn_cd": "선물옵션종목구분코드"
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ["주문수량", "FMLIMIT가격", "FMSTOP주문가격", "총체결수량", "총체결단가", "잔량",
|
||||
"체결수량", "FM체결가격", "위탁수수료", "FM체결금액"]
|
||||
|
||||
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])
|
||||
|
||||
# 결과 표시
|
||||
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,137 @@
|
||||
# -*- 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 daily_ccnl import daily_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(일간) [해외선물-018]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'tret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시각',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['자료개수', 'N틱최종개수', '시가', '고가', '저가', '체결가격', '체결수량', '누적거래수량',
|
||||
'전일대비가격', '전일대비율']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 체결추이(일간)[해외선물-018]
|
||||
|
||||
해외선물 체결추이(일간) 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목코드 (예) 6AM24)
|
||||
- exch_cd (str): 거래소코드 (예) CME)
|
||||
- start_date_time (str): 조회시작일시 (공백)
|
||||
- close_date_time (str): 조회종료일시 (예) 20240402)
|
||||
- qry_tp (str): 조회구분 (Q : 최초조회시 , P : 다음키(INDEX_KEY) 입력하여 조회시)
|
||||
- qry_cnt (str): 요청개수 (예) 30 (최대 40))
|
||||
- qry_gap (str): 묶음개수 (공백 (분만 사용))
|
||||
- index_key (str): 이전조회KEY (공백)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물 체결추이(일간) 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = daily_ccnl(srs_cd="6AM24", exch_cd="CME", start_date_time="", close_date_time="20240402", qry_tp="Q", qry_cnt="30", qry_gap="", index_key="")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2 = daily_ccnl(
|
||||
srs_cd="DXM24",
|
||||
exch_cd="ICE",
|
||||
start_date_time="",
|
||||
close_date_time="20250630",
|
||||
qry_tp="Q",
|
||||
qry_cnt="30",
|
||||
qry_gap="",
|
||||
index_key=""
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
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,186 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(일간) [해외선물-018]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/daily-ccnl"
|
||||
|
||||
def daily_ccnl(
|
||||
srs_cd: str, # 종목코드
|
||||
exch_cd: str, # 거래소코드
|
||||
start_date_time: str, # 조회시작일시
|
||||
close_date_time: str, # 조회종료일시
|
||||
qry_tp: str, # 조회구분
|
||||
qry_cnt: str, # 요청개수
|
||||
qry_gap: str, # 묶음개수
|
||||
index_key: str, # 이전조회KEY
|
||||
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]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 체결추이(일간)[해외선물-018]
|
||||
해외선물 체결추이(일간) API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): 종목코드 (예: 6AM24)
|
||||
exch_cd (str): 거래소코드 (예: CME)
|
||||
start_date_time (str): 조회시작일시 (공백)
|
||||
close_date_time (str): 조회종료일시 (예: 20240402)
|
||||
qry_tp (str): 조회구분 (Q: 최초조회시, P: 다음키(INDEX_KEY) 입력하여 조회시)
|
||||
qry_cnt (str): 요청개수 (예: 30, 최대 40)
|
||||
qry_gap (str): 묶음개수 (공백, 분만 사용)
|
||||
index_key (str): 이전조회KEY (공백)
|
||||
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 = daily_ccnl(
|
||||
... srs_cd="6AM24",
|
||||
... exch_cd="CME",
|
||||
... start_date_time="",
|
||||
... close_date_time="20240402",
|
||||
... qry_tp="Q",
|
||||
... qry_cnt="30",
|
||||
... qry_gap="",
|
||||
... index_key=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. '6AM24')")
|
||||
raise ValueError("srs_cd is required. (e.g. '6AM24')")
|
||||
if not exch_cd:
|
||||
logger.error("exch_cd is required. (e.g. 'CME')")
|
||||
raise ValueError("exch_cd is required. (e.g. 'CME')")
|
||||
if not close_date_time:
|
||||
logger.error("close_date_time is required. (e.g. '20240402')")
|
||||
raise ValueError("close_date_time is required. (e.g. '20240402')")
|
||||
if not qry_tp:
|
||||
logger.error("qry_tp is required. (e.g. 'Q')")
|
||||
raise ValueError("qry_tp is required. (e.g. 'Q')")
|
||||
if not qry_cnt:
|
||||
logger.error("qry_cnt is required. (e.g. '30')")
|
||||
raise ValueError("qry_cnt is required. (e.g. '30')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFC55020100"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
"EXCH_CD": exch_cd,
|
||||
"START_DATE_TIME": start_date_time,
|
||||
"CLOSE_DATE_TIME": close_date_time,
|
||||
"QRY_TP": qry_tp,
|
||||
"QRY_CNT": qry_cnt,
|
||||
"QRY_GAP": qry_gap,
|
||||
"INDEX_KEY": index_key,
|
||||
}
|
||||
|
||||
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 daily_ccnl(
|
||||
srs_cd,
|
||||
exch_cd,
|
||||
start_date_time,
|
||||
close_date_time,
|
||||
qry_tp,
|
||||
qry_cnt,
|
||||
qry_gap,
|
||||
index_key,
|
||||
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,124 @@
|
||||
# -*- 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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 호가 [해외선물-031]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'lowp_rice': '저가',
|
||||
'last_price': '현재가',
|
||||
'prev_price': '전일종가',
|
||||
'vol': '거래량',
|
||||
'prev_diff_price': '전일대비가',
|
||||
'prev_diff_rate': '전일대비율',
|
||||
'quot_date': '호가수신일자',
|
||||
'quot_time': '호가수신시각',
|
||||
'bid_qntt': '매수수량',
|
||||
'bid_num': '매수번호',
|
||||
'bid_price': '매수호가',
|
||||
'ask_qntt': '매도수량',
|
||||
'ask_num': '매도번호',
|
||||
'ask_price': '매도호가'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['시가', '고가', '저가', '현재가', '전일종가', '거래량', '전일대비가', '전일대비율',
|
||||
'매수수량', '매수호가', '매도수량', '매도호가']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 호가[해외선물-031]
|
||||
|
||||
해외선물 호가 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목명 (종목코드)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물 호가 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_asking_price(srs_cd="ESZ23")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2 = inquire_asking_price(srs_cd="ESZ25")
|
||||
|
||||
# 결과 확인
|
||||
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,133 @@
|
||||
# -*- 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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 호가 [해외선물-031]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/inquire-asking-price"
|
||||
|
||||
def inquire_asking_price(
|
||||
srs_cd: 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]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 호가[해외선물-031]
|
||||
해외선물 호가 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): 종목코드
|
||||
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_asking_price(srs_cd="EXAMPLE_SRS_CD")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. 'EXAMPLE_SRS_CD')")
|
||||
raise ValueError("srs_cd is required. (e.g. 'EXAMPLE_SRS_CD')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFC86000000"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
}
|
||||
|
||||
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_asking_price(
|
||||
srs_cd,
|
||||
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,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_ccld import inquire_ccld
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 당일주문내역조회 [v1_해외선물-004]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'ord_dt': '주문일자',
|
||||
'odno': '주문번호',
|
||||
'orgn_ord_dt': '원주문일자',
|
||||
'orgn_odno': '원주문번호',
|
||||
'ovrs_futr_fx_pdno': '해외선물FX상품번호',
|
||||
'rcit_dvsn_cd': '접수구분코드',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'trad_stgy_dvsn_cd': '매매전략구분코드',
|
||||
'bass_pric_type_cd': '기준가격유형코드',
|
||||
'ord_stat_cd': '주문상태코드',
|
||||
'fm_ord_qty': 'FM주문수량',
|
||||
'fm_ord_pric': 'FM주문가격',
|
||||
'fm_stop_ord_pric': 'FMSTOP주문가격',
|
||||
'rsvn_dvsn': '예약구분',
|
||||
'fm_ccld_qty': 'FM체결수량',
|
||||
'fm_ccld_pric': 'FM체결가격',
|
||||
'fm_ord_rmn_qty': 'FM주문잔여수량',
|
||||
'ord_grp_name': '주문그룹명',
|
||||
'erlm_dtl_dtime': '등록상세일시',
|
||||
'ccld_dtl_dtime': '체결상세일시',
|
||||
'ord_stfno': '주문직원번호',
|
||||
'rmks1': '비고1',
|
||||
'new_lqd_dvsn_cd': '신규청산구분코드',
|
||||
'fm_lqd_lmt_ord_pric': 'FM청산LIMIT주문가격',
|
||||
'fm_lqd_stop_pric': 'FM청산STOP가격',
|
||||
'ccld_cndt_cd': '체결조건코드',
|
||||
'noti_vald_dt': '게시유효일자',
|
||||
'acnt_type_cd': '계좌유형코드',
|
||||
'fuop_dvsn': '선물옵션구분'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['FM주문수량', 'FM주문가격', 'FMSTOP주문가격', 'FM체결수량', 'FM체결가격', 'FM주문잔여수량',
|
||||
'FM청산LIMIT주문가격', 'FM청산STOP가격']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 당일주문내역조회[v1_해외선물-004]
|
||||
|
||||
해외선물옵션 당일주문내역조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ccld_nccs_dvsn (str): 체결미체결구분 (01:전체 / 02:체결 / 03:미체결)
|
||||
- sll_buy_dvsn_cd (str): 매도매수구분코드 (%%:전체 / 01:매도 / 02:매수)
|
||||
- fuop_dvsn (str): 선물옵션구분 (00:전체 / 01:선물 / 02:옵션)
|
||||
- ctx_area_fk200 (str): 연속조회검색조건200 ()
|
||||
- ctx_area_nk200 (str): 연속조회키200 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 당일주문내역조회 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_ccld(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ccld_nccs_dvsn="01", sll_buy_dvsn_cd="%%", fuop_dvsn="00", ctx_area_fk200="", ctx_area_nk200="")
|
||||
"""
|
||||
try:
|
||||
# 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 호출
|
||||
logger.info("API 호출")
|
||||
result = inquire_ccld(
|
||||
cano=trenv.my_acct,
|
||||
acnt_prdt_cd=trenv.my_prod,
|
||||
ccld_nccs_dvsn="01",
|
||||
sll_buy_dvsn_cd="%%",
|
||||
fuop_dvsn="00",
|
||||
ctx_area_fk200="",
|
||||
ctx_area_nk200=""
|
||||
)
|
||||
|
||||
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-07-02
|
||||
|
||||
"""
|
||||
|
||||
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_해외선물-004]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/inquire-ccld"
|
||||
|
||||
def inquire_ccld(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ccld_nccs_dvsn: str, # 체결미체결구분
|
||||
sll_buy_dvsn_cd: str, # 매도매수구분코드
|
||||
fuop_dvsn: str, # 선물옵션구분
|
||||
ctx_area_fk200: str, # 연속조회검색조건200
|
||||
ctx_area_nk200: str, # 연속조회키200
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 당일주문내역조회[v1_해외선물-004]
|
||||
해외선물옵션 당일주문내역조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ccld_nccs_dvsn (str): 01:전체 / 02:체결 / 03:미체결
|
||||
sll_buy_dvsn_cd (str): %%:전체 / 01:매도 / 02:매수
|
||||
fuop_dvsn (str): 00:전체 / 01:선물 / 02:옵션
|
||||
ctx_area_fk200 (str): 연속조회검색조건200
|
||||
ctx_area_nk200 (str): 연속조회키200
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 당일주문내역조회 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_ccld(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ccld_nccs_dvsn="01",
|
||||
... sll_buy_dvsn_cd="01",
|
||||
... fuop_dvsn="00",
|
||||
... ctx_area_fk200="",
|
||||
... ctx_area_nk200=""
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '80012345')")
|
||||
raise ValueError("cano is required. (e.g. '80012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not ccld_nccs_dvsn:
|
||||
logger.error("ccld_nccs_dvsn is required. (e.g. '01')")
|
||||
raise ValueError("ccld_nccs_dvsn is required. (e.g. '01')")
|
||||
if not sll_buy_dvsn_cd:
|
||||
logger.error("sll_buy_dvsn_cd is required. (e.g. '01')")
|
||||
raise ValueError("sll_buy_dvsn_cd is required. (e.g. '01')")
|
||||
if not fuop_dvsn:
|
||||
logger.error("fuop_dvsn is required. (e.g. '00')")
|
||||
raise ValueError("fuop_dvsn is required. (e.g. '00')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
if depth >= max_depth:
|
||||
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
||||
return dataframe if dataframe is not None else pd.DataFrame()
|
||||
|
||||
tr_id = "OTFM3116R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"CCLD_NCCS_DVSN": ccld_nccs_dvsn,
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd,
|
||||
"FUOP_DVSN": fuop_dvsn,
|
||||
"CTX_AREA_FK200": ctx_area_fk200,
|
||||
"CTX_AREA_NK200": ctx_area_nk200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output'):
|
||||
output_data = res.getBody().output
|
||||
if not isinstance(output_data, list):
|
||||
output_data = [output_data]
|
||||
current_data = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data = pd.DataFrame()
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont == "M":
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_ccld(
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
ccld_nccs_dvsn,
|
||||
sll_buy_dvsn_cd,
|
||||
fuop_dvsn,
|
||||
ctx_area_fk200,
|
||||
ctx_area_nk200,
|
||||
"N", dataframe, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logger.info("Data fetch complete.")
|
||||
return dataframe
|
||||
else:
|
||||
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
||||
res.printError(API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from inquire_daily_ccld import inquire_daily_ccld
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 일별체결내역[해외선물-011]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'fm_tot_ccld_qty': 'FM총체결수량',
|
||||
'fm_tot_futr_agrm_amt': 'FM총선물약정금액',
|
||||
'fm_tot_opt_agrm_amt': 'FM총옵션약정금액',
|
||||
'fm_fee_smtl': 'FM수수료합계',
|
||||
'dt': '일자',
|
||||
'ccno': '체결번호',
|
||||
'ovrs_futr_fx_pdno': '해외선물FX상품번호',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'fm_ccld_qty': 'FM체결수량',
|
||||
'fm_ccld_amt': 'FM체결금액',
|
||||
'fm_futr_ccld_amt': 'FM선물체결금액',
|
||||
'fm_opt_ccld_amt': 'FM옵션체결금액',
|
||||
'crcy_cd': '통화코드',
|
||||
'fm_fee': 'FM수수료',
|
||||
'fm_futr_pure_agrm_amt': 'FM선물순약정금액',
|
||||
'fm_opt_pure_agrm_amt': 'FM옵션순약정금액',
|
||||
'ccld_dtl_dtime': '체결상세일시',
|
||||
'ord_dt': '주문일자',
|
||||
'odno': '주문번호',
|
||||
'ord_mdia_dvsn_name': '주문매체구분명'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['FM총체결수량', 'FM총선물약정금액', 'FM총옵션약정금액', 'FM수수료합계', 'FM체결수량', 'FM체결금액', 'FM선물체결금액',
|
||||
'FM옵션체결금액', 'FM수수료', 'FM선물순약정금액', 'FM옵션순약정금액']
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 일별 체결내역[해외선물-011]
|
||||
|
||||
해외선물옵션 일별 체결내역 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- strt_dt (str): 시작일자 (시작일자(YYYYMMDD))
|
||||
- end_dt (str): 종료일자 (종료일자(YYYYMMDD))
|
||||
- fuop_dvsn_cd (str): 선물옵션구분코드 (00:전체 / 01:선물 / 02:옵션)
|
||||
- fm_pdgr_cd (str): FM상품군코드 (공란(Default))
|
||||
- crcy_cd (str): 통화코드 (%%% : 전체 TUS: TOT_USD / TKR: TOT_KRW KRW: 한국 / USD: 미국 EUR: EUR / HKD: 홍콩 CNY: 중국 / JPY: 일본 VND: 베트남)
|
||||
- fm_item_ftng_yn (str): FM종목합산여부 ("N"(Default))
|
||||
- sll_buy_dvsn_cd (str): 매도매수구분코드 (%%: 전체 / 01 : 매도 / 02 : 매수)
|
||||
- ctx_area_fk200 (str): 연속조회검색조건200 ()
|
||||
- ctx_area_nk200 (str): 연속조회키200 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 일별 체결내역 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_daily_ccld(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, strt_dt="20250101", end_dt="20250131", fuop_dvsn_cd="00", fm_pdgr_cd="", crcy_cd="%%%", fm_item_ftng_yn="N", sll_buy_dvsn_cd="%%", ctx_area_fk200="", ctx_area_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_daily_ccld(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
strt_dt="20250601", # 시작일자
|
||||
end_dt="20250702", # 종료일자
|
||||
fuop_dvsn_cd="00", # 선물옵션구분코드
|
||||
fm_pdgr_cd="", # FM상품군코드
|
||||
crcy_cd="%%%", # 통화코드
|
||||
fm_item_ftng_yn="N", # FM종목합산여부
|
||||
sll_buy_dvsn_cd="%%", # 매도매수구분코드
|
||||
ctx_area_fk200="", # 연속조회검색조건200
|
||||
ctx_area_nk200="", # 연속조회키200
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
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,206 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 일별체결내역[해외선물-011]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/inquire-daily-ccld"
|
||||
|
||||
def inquire_daily_ccld(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
strt_dt: str, # 시작일자
|
||||
end_dt: str, # 종료일자
|
||||
fuop_dvsn_cd: str, # 선물옵션구분코드
|
||||
fm_pdgr_cd: str, # FM상품군코드
|
||||
crcy_cd: str, # 통화코드
|
||||
fm_item_ftng_yn: str, # FM종목합산여부
|
||||
sll_buy_dvsn_cd: str, # 매도매수구분코드
|
||||
ctx_area_fk200: str, # 연속조회검색조건200
|
||||
ctx_area_nk200: str, # 연속조회키200
|
||||
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]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 일별 체결내역[해외선물-011]
|
||||
해외선물옵션 일별 체결내역 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
strt_dt (str): 시작일자(YYYYMMDD)
|
||||
end_dt (str): 종료일자(YYYYMMDD)
|
||||
fuop_dvsn_cd (str): 00:전체 / 01:선물 / 02:옵션
|
||||
fm_pdgr_cd (str): 공란(Default)
|
||||
crcy_cd (str): %%% : 전체 TUS: TOT_USD / TKR: TOT_KRW KRW: 한국 / USD: 미국 EUR: EUR / HKD: 홍콩 CNY: 중국 / JPY: 일본 VND: 베트남
|
||||
fm_item_ftng_yn (str): "N"(Default)
|
||||
sll_buy_dvsn_cd (str): %%: 전체 / 01 : 매도 / 02 : 매수
|
||||
ctx_area_fk200 (str): 연속조회검색조건200
|
||||
ctx_area_nk200 (str): 연속조회키200
|
||||
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_daily_ccld(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... strt_dt="20221010",
|
||||
... end_dt="20221216",
|
||||
... fuop_dvsn_cd="00",
|
||||
... fm_pdgr_cd="",
|
||||
... crcy_cd="%%%",
|
||||
... fm_item_ftng_yn="N",
|
||||
... sll_buy_dvsn_cd="%%",
|
||||
... ctx_area_fk200="",
|
||||
... ctx_area_nk200=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '80012345')")
|
||||
raise ValueError("cano is required. (e.g. '80012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not strt_dt:
|
||||
logger.error("strt_dt is required. (e.g. '20221010')")
|
||||
raise ValueError("strt_dt is required. (e.g. '20221010')")
|
||||
if not end_dt:
|
||||
logger.error("end_dt is required. (e.g. '20221216')")
|
||||
raise ValueError("end_dt is required. (e.g. '20221216')")
|
||||
if fuop_dvsn_cd not in ['00', '01', '02']:
|
||||
logger.error("fuop_dvsn_cd is required. (e.g. '00', '01', '02')")
|
||||
raise ValueError("fuop_dvsn_cd is required. (e.g. '00', '01', '02')")
|
||||
if not crcy_cd:
|
||||
logger.error("crcy_cd is required. (e.g. '%%%',KRW, USD, EUR..)")
|
||||
raise ValueError("crcy_cd is required. (e.g. '%%%',KRW, USD, EUR..)")
|
||||
if not fm_item_ftng_yn:
|
||||
logger.error("fm_item_ftng_yn is required. (e.g. 'N')")
|
||||
raise ValueError("fm_item_ftng_yn is required. (e.g. 'N')")
|
||||
if not sll_buy_dvsn_cd:
|
||||
logger.error("sll_buy_dvsn_cd is required. (e.g. '%%')")
|
||||
raise ValueError("sll_buy_dvsn_cd is required. (e.g. '%%')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "OTFM3122R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"STRT_DT": strt_dt,
|
||||
"END_DT": end_dt,
|
||||
"FUOP_DVSN_CD": fuop_dvsn_cd,
|
||||
"FM_PDGR_CD": fm_pdgr_cd,
|
||||
"CRCY_CD": crcy_cd,
|
||||
"FM_ITEM_FTNG_YN": fm_item_ftng_yn,
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd,
|
||||
"CTX_AREA_FK200": ctx_area_fk200,
|
||||
"CTX_AREA_NK200": ctx_area_nk200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# output 처리
|
||||
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()
|
||||
# output1 처리
|
||||
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, ctx_area_fk200, ctx_area_nk200 = res.getHeader().tr_cont, res.getBody().ctx_area_fk200, res.getBody().ctx_area_fk200
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_daily_ccld(
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
strt_dt,
|
||||
end_dt,
|
||||
fuop_dvsn_cd,
|
||||
fm_pdgr_cd,
|
||||
crcy_cd,
|
||||
fm_item_ftng_yn,
|
||||
sll_buy_dvsn_cd,
|
||||
ctx_area_fk200,
|
||||
ctx_area_nk200,
|
||||
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,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-03
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from inquire_daily_order import inquire_daily_order
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 일별 주문내역 [해외선물-013]
|
||||
##############################################################################################
|
||||
|
||||
COLUMN_MAPPING = {
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'dt': '일자',
|
||||
'ord_dt': '주문일자',
|
||||
'odno': '주문번호',
|
||||
'orgn_ord_dt': '원주문일자',
|
||||
'orgn_odno': '원주문번호',
|
||||
'ovrs_futr_fx_pdno': '해외선물FX상품번호',
|
||||
'rvse_cncl_dvsn_cd': '정정취소구분코드',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'cplx_ord_dvsn_cd': '복합주문구분코드',
|
||||
'pric_dvsn_cd': '가격구분코드',
|
||||
'rcit_dvsn_cd': '접수구분코드',
|
||||
'fm_ord_qty': 'FM주문수량',
|
||||
'fm_ord_pric': 'FM주문가격',
|
||||
'fm_stop_ord_pric': 'FMSTOP주문가격',
|
||||
'ecis_rsvn_ord_yn': '행사예약주문여부',
|
||||
'fm_ccld_qty': 'FM체결수량',
|
||||
'fm_ccld_pric': 'FM체결가격',
|
||||
'fm_ord_rmn_qty': 'FM주문잔여수량',
|
||||
'ord_grp_name': '주문그룹명',
|
||||
'rcit_dtl_dtime': '접수상세일시',
|
||||
'ccld_dtl_dtime': '체결상세일시',
|
||||
'ordr_emp_no': '주문자사원번호',
|
||||
'rjct_rson_name': '거부사유명',
|
||||
'ccld_cndt_cd': '체결조건코드',
|
||||
'trad_end_dt': '매매종료일자'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['FM주문수량', 'FM주문가격', 'FMSTOP주문가격', 'FM체결수량', 'FM체결가격', 'FM주문잔여수량']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 일별 주문내역[해외선물-013]
|
||||
|
||||
해외선물옵션 일별 주문내역 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- strt_dt (str): 시작일자 ()
|
||||
- end_dt (str): 종료일자 ()
|
||||
- fm_pdgr_cd (str): FM상품군코드 ()
|
||||
- ccld_nccs_dvsn (str): 체결미체결구분 (01:전체 / 02:체결 / 03:미체결)
|
||||
- sll_buy_dvsn_cd (str): 매도매수구분코드 (%%전체 / 01 : 매도 / 02 : 매수)
|
||||
- fuop_dvsn (str): 선물옵션구분 (00:전체 / 01:선물 / 02:옵션)
|
||||
- ctx_area_fk200 (str): 연속조회검색조건200 ()
|
||||
- ctx_area_nk200 (str): 연속조회키200 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 일별 주문내역 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_daily_order(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, strt_dt="20250601", end_dt="20250703", fm_pdgr_cd="", ccld_nccs_dvsn="01", sll_buy_dvsn_cd="%%", fuop_dvsn="00", ctx_area_fk200="", ctx_area_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 호출 시작: 해외선물옵션 일별 주문내역")
|
||||
result = inquire_daily_order(
|
||||
cano=trenv.my_acct, # 종합계좌번호 (자동 설정)
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
strt_dt="20250601", # 시작일자
|
||||
end_dt="20250703", # 종료일자
|
||||
fm_pdgr_cd="", # FM상품군코드
|
||||
ccld_nccs_dvsn="01", # 체결미체결구분
|
||||
sll_buy_dvsn_cd="%%", # 매도매수구분코드
|
||||
fuop_dvsn="00", # 선물옵션구분
|
||||
ctx_area_fk200="", # 연속조회검색조건200
|
||||
ctx_area_nk200="" # 연속조회키200
|
||||
)
|
||||
|
||||
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,166 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-03
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 일별 주문내역 [해외선물-013]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/inquire-daily-order"
|
||||
|
||||
def inquire_daily_order(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
strt_dt: str, # 시작일자
|
||||
end_dt: str, # 종료일자
|
||||
fm_pdgr_cd: str, # FM상품군코드
|
||||
ccld_nccs_dvsn: str, # 체결미체결구분
|
||||
sll_buy_dvsn_cd: str, # 매도매수구분코드
|
||||
fuop_dvsn: str, # 선물옵션구분
|
||||
ctx_area_fk200: str, # 연속조회검색조건200
|
||||
ctx_area_nk200: str, # 연속조회키200
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 일별 주문내역[해외선물-013]
|
||||
해외선물옵션 일별 주문내역 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
strt_dt (str): 시작일자 (YYYYMMDD)
|
||||
end_dt (str): 종료일자 (YYYYMMDD)
|
||||
fm_pdgr_cd (str): FM상품군코드
|
||||
ccld_nccs_dvsn (str): 체결미체결구분 (01:전체 / 02:체결 / 03:미체결)
|
||||
sll_buy_dvsn_cd (str): 매도매수구분코드 (%%전체 / 01:매도 / 02:매수)
|
||||
fuop_dvsn (str): 선물옵션구분 (00:전체 / 01:선물 / 02:옵션)
|
||||
ctx_area_fk200 (str): 연속조회검색조건200
|
||||
ctx_area_nk200 (str): 연속조회키200
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 일별 주문내역 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_daily_order(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... strt_dt="20220101",
|
||||
... end_dt="20221214",
|
||||
... fm_pdgr_cd="",
|
||||
... ccld_nccs_dvsn="01",
|
||||
... sll_buy_dvsn_cd="%%",
|
||||
... fuop_dvsn="00",
|
||||
... ctx_area_fk200="",
|
||||
... ctx_area_nk200=""
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '12345678')")
|
||||
raise ValueError("cano is required. (e.g. '12345678')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not strt_dt:
|
||||
logger.error("strt_dt is required. (e.g. '20220101')")
|
||||
raise ValueError("strt_dt is required. (e.g. '20220101')")
|
||||
if not end_dt:
|
||||
logger.error("end_dt is required. (e.g. '20221214')")
|
||||
raise ValueError("end_dt is required. (e.g. '20221214')")
|
||||
if not ccld_nccs_dvsn:
|
||||
logger.error("ccld_nccs_dvsn is required. (e.g. '01')")
|
||||
raise ValueError("ccld_nccs_dvsn is required. (e.g. '01')")
|
||||
if not sll_buy_dvsn_cd:
|
||||
logger.error("sll_buy_dvsn_cd is required. (e.g. '%%')")
|
||||
raise ValueError("sll_buy_dvsn_cd is required. (e.g. '%%')")
|
||||
if not fuop_dvsn:
|
||||
logger.error("fuop_dvsn is required. (e.g. '00')")
|
||||
raise ValueError("fuop_dvsn is required. (e.g. '00')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
if depth >= max_depth:
|
||||
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
||||
return dataframe if dataframe is not None else pd.DataFrame()
|
||||
|
||||
tr_id = "OTFM3120R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"STRT_DT": strt_dt,
|
||||
"END_DT": end_dt,
|
||||
"FM_PDGR_CD": fm_pdgr_cd,
|
||||
"CCLD_NCCS_DVSN": ccld_nccs_dvsn,
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd,
|
||||
"FUOP_DVSN": fuop_dvsn,
|
||||
"CTX_AREA_FK200": ctx_area_fk200,
|
||||
"CTX_AREA_NK200": ctx_area_nk200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output'):
|
||||
output_data = res.getBody().output
|
||||
if not isinstance(output_data, list):
|
||||
output_data = [output_data]
|
||||
current_data = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data = pd.DataFrame()
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont == "M":
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_daily_order(
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
strt_dt,
|
||||
end_dt,
|
||||
fm_pdgr_cd,
|
||||
ccld_nccs_dvsn,
|
||||
sll_buy_dvsn_cd,
|
||||
fuop_dvsn,
|
||||
ctx_area_fk200,
|
||||
ctx_area_nk200,
|
||||
"N", dataframe, depth + 1, max_depth
|
||||
)
|
||||
else:
|
||||
logger.info("Data fetch complete.")
|
||||
return dataframe
|
||||
else:
|
||||
logger.error("API call failed: %s - %s", res.getErrorCode(), res.getErrorMessage())
|
||||
res.printError(API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,119 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-03
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from inquire_deposit import inquire_deposit
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 예수금현황 [해외선물-012]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'fm_nxdy_dncl_amt': 'FM익일예수금액',
|
||||
'fm_tot_asst_evlu_amt': 'FM총자산평가금액',
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'crcy_cd': '통화코드',
|
||||
'resp_dt': '응답일자',
|
||||
'fm_dnca_rmnd': 'FM예수금잔액',
|
||||
'fm_lqd_pfls_amt': 'FM청산손익금액',
|
||||
'fm_fee': 'FM수수료',
|
||||
'fm_fuop_evlu_pfls_amt': 'FM선물옵션평가손익금액',
|
||||
'fm_rcvb_amt': 'FM미수금액',
|
||||
'fm_brkg_mgn_amt': 'FM위탁증거금액',
|
||||
'fm_mntn_mgn_amt': 'FM유지증거금액',
|
||||
'fm_add_mgn_amt': 'FM추가증거금액',
|
||||
'fm_risk_rt': 'FM위험율',
|
||||
'fm_ord_psbl_amt': 'FM주문가능금액',
|
||||
'fm_drwg_psbl_amt': 'FM출금가능금액',
|
||||
'fm_echm_rqrm_amt': 'FM환전요청금액',
|
||||
'fm_drwg_prar_amt': 'FM출금예정금액',
|
||||
'fm_opt_tr_chgs': 'FM옵션거래대금',
|
||||
'fm_opt_icld_asst_evlu_amt': 'FM옵션포함자산평가금액',
|
||||
'fm_opt_evlu_amt': 'FM옵션평가금액',
|
||||
'fm_crcy_sbst_amt': 'FM통화대용금액',
|
||||
'fm_crcy_sbst_use_amt': 'FM통화대용사용금액',
|
||||
'fm_crcy_sbst_stup_amt': 'FM통화대용설정금액'
|
||||
}
|
||||
NUMERIC_COLUMNS = ['FM총자산평가금액', 'FM예수금잔액', 'FM청산손익금액', 'FM수수료', 'FM선물옵션평가손익금액', 'FM미수금액', 'FM위탁증거금액', 'FM유지증거금액', 'FM추가증거금액',
|
||||
'FM위험율', 'FM주문가능금액', 'FM출금가능금액', 'FM환전요청금액', 'FM출금예정금액', 'FM옵션거래대금', 'FM옵션포함자산평가금액', 'FM옵션평가금액', 'FM통화대용금액', 'FM통화대용사용금액', 'FM통화대용설정금액']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 예수금현황[해외선물-012]
|
||||
|
||||
해외선물옵션 예수금현황 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- crcy_cd (str): 통화코드 (TUS: TOT_USD / TKR: TOT_KRW KRW: 한국 / USD: 미국 EUR: EUR / HKD: 홍콩 CNY: 중국 / JPY: 일본 VND: 베트남)
|
||||
- inqr_dt (str): 조회일자 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 예수금현황 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_deposit(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, crcy_cd="TUS", inqr_dt="20250630")
|
||||
"""
|
||||
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 = inquire_deposit(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
crcy_cd="TUS", # 통화코드
|
||||
inqr_dt="20250630", # 조회일자
|
||||
)
|
||||
|
||||
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,127 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-03
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 예수금현황 [해외선물-012]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/inquire-deposit"
|
||||
|
||||
def inquire_deposit(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
crcy_cd: str, # 통화코드
|
||||
inqr_dt: str, # 조회일자
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 예수금현황[해외선물-012]
|
||||
해외선물옵션 예수금현황 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
crcy_cd (str): TUS: TOT_USD / TKR: TOT_KRW KRW: 한국 / USD: 미국 EUR: EUR / HKD: 홍콩 CNY: 중국 / JPY: 일본 VND: 베트남
|
||||
inqr_dt (str): 조회일자 (YYYYMMDD 형식)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 예수금현황 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_deposit(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... crcy_cd="KRW",
|
||||
... inqr_dt="20221214"
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '80012345')")
|
||||
raise ValueError("cano is required. (e.g. '80012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not crcy_cd:
|
||||
logger.error("crcy_cd is required. (e.g. 'KRW')")
|
||||
raise ValueError("crcy_cd is required. (e.g. 'KRW')")
|
||||
if not inqr_dt:
|
||||
logger.error("inqr_dt is required. (e.g. '20221214')")
|
||||
raise ValueError("inqr_dt is required. (e.g. '20221214')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "OTFM1411R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"CRCY_CD": crcy_cd,
|
||||
"INQR_DT": inqr_dt,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output'):
|
||||
output_data = res.getBody().output
|
||||
if not isinstance(output_data, list):
|
||||
output_data = [output_data]
|
||||
current_data = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data = pd.DataFrame()
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont == "M":
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_deposit(
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
crcy_cd,
|
||||
inqr_dt,
|
||||
"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,155 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from inquire_period_ccld import inquire_period_ccld
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 기간계좌손익 일별 [해외선물-010]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'crcy_cd': '통화코드',
|
||||
'fm_buy_qty': 'FM매수수량',
|
||||
'fm_sll_qty': 'FM매도수량',
|
||||
'fm_lqd_pfls_amt': 'FM청산손익금액',
|
||||
'fm_fee': 'FM수수료',
|
||||
'fm_net_pfls_amt': 'FM순손익금액',
|
||||
'fm_ustl_buy_qty': 'FM미결제매수수량',
|
||||
'fm_ustl_sll_qty': 'FM미결제매도수량',
|
||||
'fm_ustl_evlu_pfls_amt': 'FM미결제평가손익금액',
|
||||
'fm_ustl_evlu_pfls_amt2': 'FM미결제평가손익금액2',
|
||||
'fm_ustl_evlu_pfls_icdc_amt': 'FM미결제평가손익증감금액',
|
||||
'fm_ustl_agrm_amt': 'FM미결제약정금액',
|
||||
'fm_opt_lqd_amt': 'FM옵션청산금액',
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'ovrs_futr_fx_pdno': '해외선물FX상품번호',
|
||||
'crcy_cd': '통화코드',
|
||||
'fm_buy_qty': 'FM매수수량',
|
||||
'fm_sll_qty': 'FM매도수량',
|
||||
'fm_lqd_pfls_amt': 'FM청산손익금액',
|
||||
'fm_fee': 'FM수수료',
|
||||
'fm_net_pfls_amt': 'FM순손익금액',
|
||||
'fm_ustl_buy_qty': 'FM미결제매수수량',
|
||||
'fm_ustl_sll_qty': 'FM미결제매도수량',
|
||||
'fm_ustl_evlu_pfls_amt': 'FM미결제평가손익금액',
|
||||
'fm_ustl_evlu_pfls_amt2': 'FM미결제평가손익금액2',
|
||||
'fm_ustl_evlu_pfls_icdc_amt': 'FM미결제평가손익증감금액',
|
||||
'fm_ccld_avg_pric': 'FM체결평균가격',
|
||||
'fm_ustl_agrm_amt': 'FM미결제약정금액',
|
||||
'fm_opt_lqd_amt': 'FM옵션청산금액'
|
||||
}
|
||||
NUMERIC_COLUMNS = ['FM매수수량', 'FM매도수량', 'FM청산손익금액', 'FM수수료', 'FM순손익금액', 'FM미결제매수수량', 'FM미결제매도수량', 'FM미결제평가손익금액',
|
||||
'FM미결제평가손익금액2', 'FM미결제평가손익증감금액', 'FM미결제약정금액', 'FM옵션청산금액', 'FM체결평균가격']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 기간계좌손익 일별[해외선물-010]
|
||||
|
||||
해외선물옵션 기간계좌손익 일별 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- inqr_term_from_dt (str): 조회기간FROM일자 ()
|
||||
- inqr_term_to_dt (str): 조회기간TO일자 ()
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- crcy_cd (str): 통화코드 ('%%% : 전체 TUS: TOT_USD / TKR: TOT_KRW KRW: 한국 / USD: 미국 EUR: EUR / HKD: 홍콩 CNY: 중국 / JPY: 일본')
|
||||
- whol_trsl_yn (str): 전체환산여부 (N)
|
||||
- fuop_dvsn (str): 선물옵션구분 (00:전체 / 01:선물 / 02:옵션)
|
||||
- ctx_area_fk200 (str): 연속조회검색조건200 ()
|
||||
- ctx_area_nk200 (str): 연속조회키200 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 기간계좌손익 일별 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_period_ccld(inqr_term_from_dt="20250601", inqr_term_to_dt="20250630", cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, crcy_cd="%%%", whol_trsl_yn="N", fuop_dvsn="00", ctx_area_fk200="", ctx_area_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_ccld(
|
||||
inqr_term_from_dt="20250601", # 조회기간FROM일자
|
||||
inqr_term_to_dt="20250630", # 조회기간TO일자
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
crcy_cd="%%%", # 통화코드
|
||||
whol_trsl_yn="N", # 전체환산여부
|
||||
fuop_dvsn="00", # 선물옵션구분
|
||||
ctx_area_fk200="", # 연속조회검색조건200
|
||||
ctx_area_nk200="", # 연속조회키200
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
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,193 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 기간계좌손익 일별 [해외선물-010]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/inquire-period-ccld"
|
||||
|
||||
def inquire_period_ccld(
|
||||
inqr_term_from_dt: str, # 조회기간FROM일자
|
||||
inqr_term_to_dt: str, # 조회기간TO일자
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
crcy_cd: str, # 통화코드
|
||||
whol_trsl_yn: str, # 전체환산여부
|
||||
fuop_dvsn: str, # 선물옵션구분
|
||||
ctx_area_fk200: str, # 연속조회검색조건200
|
||||
ctx_area_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]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 기간계좌손익 일별[해외선물-010]
|
||||
해외선물옵션 기간계좌손익 일별 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
inqr_term_from_dt (str): 조회기간FROM일자
|
||||
inqr_term_to_dt (str): 조회기간TO일자
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
crcy_cd (str): '%%% : 전체 TUS: TOT_USD / TKR: TOT_KRW KRW: 한국 / USD: 미국 EUR: EUR / HKD: 홍콩 CNY: 중국 / JPY: 일본'
|
||||
whol_trsl_yn (str): 전체환산여부
|
||||
fuop_dvsn (str): 00:전체 / 01:선물 / 02:옵션
|
||||
ctx_area_fk200 (str): 연속조회검색조건200
|
||||
ctx_area_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_ccld(
|
||||
... inqr_term_from_dt="20250601",
|
||||
... inqr_term_to_dt="20250630",
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... crcy_cd="%%%",
|
||||
... whol_trsl_yn="N",
|
||||
... fuop_dvsn="00",
|
||||
... ctx_area_fk200="",
|
||||
... ctx_area_nk200=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not inqr_term_from_dt:
|
||||
logger.error("inqr_term_from_dt is required. (e.g. '20250601')")
|
||||
raise ValueError("inqr_term_from_dt is required. (e.g. '20250601')")
|
||||
if not inqr_term_to_dt:
|
||||
logger.error("inqr_term_to_dt is required. (e.g. '20250630')")
|
||||
raise ValueError("inqr_term_to_dt is required. (e.g. '20250630')")
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '80012345')")
|
||||
raise ValueError("cano is required. (e.g. '80012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not crcy_cd:
|
||||
logger.error("crcy_cd is required. (e.g. '%%%')")
|
||||
raise ValueError("crcy_cd is required. (e.g. '%%%')")
|
||||
if not whol_trsl_yn:
|
||||
logger.error("whol_trsl_yn is required. (e.g. 'N')")
|
||||
raise ValueError("whol_trsl_yn is required. (e.g. 'N')")
|
||||
if not fuop_dvsn:
|
||||
logger.error("fuop_dvsn is required. (e.g. '00')")
|
||||
raise ValueError("fuop_dvsn 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 = "OTFM3118R"
|
||||
|
||||
params = {
|
||||
"INQR_TERM_FROM_DT": inqr_term_from_dt,
|
||||
"INQR_TERM_TO_DT": inqr_term_to_dt,
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"CRCY_CD": crcy_cd,
|
||||
"WHOL_TRSL_YN": whol_trsl_yn,
|
||||
"FUOP_DVSN": fuop_dvsn,
|
||||
"CTX_AREA_FK200": ctx_area_fk200,
|
||||
"CTX_AREA_NK200": ctx_area_nk200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
# 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_period_ccld(
|
||||
inqr_term_from_dt,
|
||||
inqr_term_to_dt,
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
crcy_cd,
|
||||
whol_trsl_yn,
|
||||
fuop_dvsn,
|
||||
ctx_area_fk200,
|
||||
ctx_area_nk200,
|
||||
"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,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 기간계좌거래내역 [해외선물-014]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'bass_dt': '기준일자',
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'fm_ldgr_inog_seq': 'FM원장출납순번',
|
||||
'crcy_cd': '통화코드',
|
||||
'fm_iofw_amt': 'FM입출금액',
|
||||
'fm_fee': 'FM수수료',
|
||||
'fm_tax_amt': 'FM세금금액',
|
||||
'fm_sttl_amt': 'FM결제금액',
|
||||
'fm_bf_dncl_amt': 'FM이전예수금액',
|
||||
'fm_dncl_amt': 'FM예수금액',
|
||||
'fm_rcvb_occr_amt': 'FM미수발생금액',
|
||||
'fm_rcvb_pybk_amt': 'FM미수변제금액',
|
||||
'ovdu_int_pybk_amt': '연체이자변제금액',
|
||||
'rmks_text': '비고내용'
|
||||
}
|
||||
NUMERIC_COLUMNS = ['FM입출금액', 'FM수수료', 'FM세금금액', 'FM결제금액', 'FM이전예수금액', 'FM예수금액', 'FM미수발생금액', 'FM미수변제금액', '연체이자변제금액']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 기간계좌거래내역[해외선물-014]
|
||||
|
||||
해외선물옵션 기간계좌거래내역 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- inqr_term_from_dt (str): 조회기간FROM일자 ()
|
||||
- inqr_term_to_dt (str): 조회기간TO일자 ()
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- acnt_tr_type_cd (str): 계좌거래유형코드 (1: 전체, 2:입출금 , 3: 결제)
|
||||
- crcy_cd (str): 통화코드 ('%%% : 전체 TUS: TOT_USD / TKR: TOT_KRW KRW: 한국 / USD: 미국 EUR: EUR / HKD: 홍콩 CNY: 중국 / JPY: 일본 VND: 베트남 ')
|
||||
- ctx_area_fk100 (str): 연속조회검색조건100 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK100값 : 다음페이지 조회시(2번째부터))
|
||||
- ctx_area_nk100 (str): 연속조회키100 (공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK100값 : 다음페이지 조회시(2번째부터))
|
||||
- pwd_chk_yn (str): 비밀번호체크여부 (공란(Default))
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 기간계좌거래내역 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_period_trans(inqr_term_from_dt="20250101", inqr_term_to_dt="20250131", cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, acnt_tr_type_cd="1", crcy_cd="%%%", ctx_area_fk100="", ctx_area_nk100="", pwd_chk_yn="")
|
||||
"""
|
||||
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 = inquire_period_trans(
|
||||
inqr_term_from_dt="20250601", # 조회기간FROM일자
|
||||
inqr_term_to_dt="20250630", # 조회기간TO일자
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
acnt_tr_type_cd="1", # 계좌거래유형코드
|
||||
crcy_cd="%%%", # 통화코드
|
||||
ctx_area_fk100="", # 연속조회검색조건100
|
||||
ctx_area_nk100="", # 연속조회키100
|
||||
pwd_chk_yn="N", # 비밀번호체크여부
|
||||
)
|
||||
|
||||
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,157 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 기간계좌거래내역 [해외선물-014]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/inquire-period-trans"
|
||||
|
||||
def inquire_period_trans(
|
||||
inqr_term_from_dt: str, # 조회기간FROM일자
|
||||
inqr_term_to_dt: str, # 조회기간TO일자
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
acnt_tr_type_cd: str, # 계좌거래유형코드
|
||||
crcy_cd: str, # 통화코드
|
||||
ctx_area_fk100: str, # 연속조회검색조건100
|
||||
ctx_area_nk100: str, # 연속조회키100
|
||||
pwd_chk_yn: str, # 비밀번호체크여부
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 기간계좌거래내역[해외선물-014]
|
||||
해외선물옵션 기간계좌거래내역 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
inqr_term_from_dt (str): 조회기간FROM일자 (예: '20220101')
|
||||
inqr_term_to_dt (str): 조회기간TO일자 (예: '20221214')
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리 (예: '80012345')
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리 (예: '08')
|
||||
acnt_tr_type_cd (str): 계좌거래유형코드 (1: 전체, 2:입출금 , 3: 결제)
|
||||
crcy_cd (str): 통화코드 ('%%%': 전체, 'TUS': TOT_USD, 'TKR': TOT_KRW, 'KRW': 한국, 'USD': 미국, 'EUR': EUR, 'HKD': 홍콩, 'CNY': 중국, 'JPY': 일본, 'VND': 베트남)
|
||||
ctx_area_fk100 (str): 연속조회검색조건100
|
||||
ctx_area_nk100 (str): 연속조회키100
|
||||
pwd_chk_yn (str): 비밀번호체크여부
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 기간계좌거래내역 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_period_trans(
|
||||
... inqr_term_from_dt="20220101",
|
||||
... inqr_term_to_dt="20221214",
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... acnt_tr_type_cd="%%",
|
||||
... crcy_cd="%%%",
|
||||
... ctx_area_fk100="",
|
||||
... ctx_area_nk100="",
|
||||
... pwd_chk_yn=""
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not inqr_term_from_dt:
|
||||
logger.error("inqr_term_from_dt is required. (e.g. '20220101')")
|
||||
raise ValueError("inqr_term_from_dt is required. (e.g. '20220101')")
|
||||
if not inqr_term_to_dt:
|
||||
logger.error("inqr_term_to_dt is required. (e.g. '20221214')")
|
||||
raise ValueError("inqr_term_to_dt is required. (e.g. '20221214')")
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '80012345')")
|
||||
raise ValueError("cano is required. (e.g. '80012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not acnt_tr_type_cd:
|
||||
logger.error("acnt_tr_type_cd is required. (e.g. '%%')")
|
||||
raise ValueError("acnt_tr_type_cd is required. (e.g. '%%')")
|
||||
if not crcy_cd:
|
||||
logger.error("crcy_cd is required. (e.g. '%%%')")
|
||||
raise ValueError("crcy_cd is required. (e.g. '%%%')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "OTFM3114R"
|
||||
|
||||
params = {
|
||||
"INQR_TERM_FROM_DT": inqr_term_from_dt,
|
||||
"INQR_TERM_TO_DT": inqr_term_to_dt,
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"ACNT_TR_TYPE_CD": acnt_tr_type_cd,
|
||||
"CRCY_CD": crcy_cd,
|
||||
"CTX_AREA_FK100": ctx_area_fk100,
|
||||
"CTX_AREA_NK100": ctx_area_nk100,
|
||||
"PWD_CHK_YN": pwd_chk_yn,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output'):
|
||||
output_data = res.getBody().output
|
||||
if not isinstance(output_data, list):
|
||||
output_data = [output_data]
|
||||
current_data = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data = pd.DataFrame()
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont == "M":
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_period_trans(
|
||||
inqr_term_from_dt,
|
||||
inqr_term_to_dt,
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
acnt_tr_type_cd,
|
||||
crcy_cd,
|
||||
ctx_area_fk100,
|
||||
ctx_area_nk100,
|
||||
pwd_chk_yn,
|
||||
"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,125 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from inquire_price import inquire_price
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물종목현재가 [v1_해외선물-009]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'proc_date': '최종처리일자',
|
||||
'high_price': '고가',
|
||||
'proc_time': '최종처리시각',
|
||||
'open_price': '시가',
|
||||
'trst_mgn': '증거금',
|
||||
'low_price': '저가',
|
||||
'last_price': '현재가',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율',
|
||||
'bid_qntt': '매수1수량',
|
||||
'bid_price': '매수1호가',
|
||||
'ask_qntt': '매도1수량',
|
||||
'ask_price': '매도1호가',
|
||||
'prev_price': '전일종가',
|
||||
'exch_cd': '거래소코드',
|
||||
'crc_cd': '거래통화',
|
||||
'trd_fr_date': '상장일',
|
||||
'expr_date': '만기일',
|
||||
'trd_to_date': '최종거래일',
|
||||
'remn_cnt': '잔존일수',
|
||||
'last_qntt': '체결량',
|
||||
'tot_ask_qntt': '총매도잔량',
|
||||
'tot_bid_qntt': '총매수잔량',
|
||||
'tick_size': '틱사이즈',
|
||||
'open_date': '장개시일자',
|
||||
'open_time': '장개시시각',
|
||||
'close_date': '장종료일자',
|
||||
'close_time': '장종료시각',
|
||||
'sbsnsdate': '영업일자',
|
||||
'sttl_price': '정산가'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['고가', '시가', '저가', '현재가', '누적거래수량', '전일대비가격', '전일대비율', '매수1수량', '매수1호가', '매도1수량', '매도1호가',
|
||||
'전일종가', '증거금', '체결량', '총매도잔량', '총매수잔량', '틱사이즈', '정산가']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물종목현재가[v1_해외선물-009]
|
||||
|
||||
해외선물종목현재가 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목코드 (ex) BONU25 ※ 종목코드 "포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수선물" 참고)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물종목현재가 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_price(srs_cd="BONU25")
|
||||
"""
|
||||
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 파라미터 설정 중...")
|
||||
srs_cd = "BONU25" # 종목코드
|
||||
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출 시작: 해외선물종목현재가")
|
||||
result = inquire_price(
|
||||
srs_cd=srs_cd, # 종목코드
|
||||
)
|
||||
|
||||
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,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
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 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/inquire-price"
|
||||
|
||||
def inquire_price(
|
||||
srs_cd: str, # 종목코드
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물종목현재가[v1_해외선물-009]
|
||||
해외선물종목현재가 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): 종목코드 (예: CNHU24)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물종목현재가 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_price(srs_cd="CNHU24")
|
||||
>>> print(df)
|
||||
"""
|
||||
# 필수 파라미터 검증
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. 'CNHU24')")
|
||||
raise ValueError("srs_cd is required. (e.g. 'CNHU24')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFC55010000"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output1'):
|
||||
output_data = res.getBody().output1
|
||||
if not isinstance(output_data, list):
|
||||
output_data = [output_data]
|
||||
current_data = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data = pd.DataFrame()
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont == "M":
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_price(
|
||||
srs_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,107 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
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_해외선물-006]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'ovrs_futr_fx_pdno': '해외선물FX상품번호',
|
||||
'crcy_cd': '통화코드',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'fm_ustl_qty': 'FM미결제수량',
|
||||
'fm_lqd_psbl_qty': 'FM청산가능수량',
|
||||
'fm_new_ord_psbl_qty': 'FM신규주문가능수량',
|
||||
'fm_tot_ord_psbl_qty': 'FM총주문가능수량',
|
||||
'fm_mkpr_tot_ord_psbl_qty': 'FM시장가총주문가능수량'
|
||||
}
|
||||
NUMERIC_COLUMNS = ['FM미결제수량', 'FM청산가능수량', 'FM신규주문가능수량', 'FM총주문가능수량', 'FM시장가총주문가능수량']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 주문가능조회[v1_해외선물-006]
|
||||
|
||||
해외선물옵션 주문가능조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_futr_fx_pdno (str): 해외선물FX상품번호 ()
|
||||
- sll_buy_dvsn_cd (str): 매도매수구분코드 (01 : 매도 / 02 : 매수)
|
||||
- fm_ord_pric (str): FM주문가격 (N)
|
||||
- ecis_rsvn_ord_yn (str): 행사예약주문여부 (N)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 주문가능조회 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_psamount(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_futr_fx_pdno="6AU22", sll_buy_dvsn_cd="02", fm_ord_pric="", ecis_rsvn_ord_yn="")
|
||||
"""
|
||||
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 = inquire_psamount(
|
||||
cano=trenv.my_acct, # 종합계좌번호
|
||||
acnt_prdt_cd=trenv.my_prod, # 계좌상품코드
|
||||
ovrs_futr_fx_pdno="6AU22", # 해외선물FX상품번호
|
||||
sll_buy_dvsn_cd="02", # 매도매수구분코드
|
||||
fm_ord_pric="", # FM주문가격
|
||||
ecis_rsvn_ord_yn="", # 행사예약주문여부
|
||||
)
|
||||
|
||||
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,138 @@
|
||||
# -*- 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_해외선물-006]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/inquire-psamount"
|
||||
|
||||
def inquire_psamount(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_futr_fx_pdno: str, # 해외선물FX상품번호
|
||||
sll_buy_dvsn_cd: str, # 매도매수구분코드
|
||||
fm_ord_pric: str, # FM주문가격
|
||||
ecis_rsvn_ord_yn: str, # 행사예약주문여부
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 주문가능조회[v1_해외선물-006]
|
||||
해외선물옵션 주문가능조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_futr_fx_pdno (str): 해외선물FX상품번호
|
||||
sll_buy_dvsn_cd (str): 01 : 매도 / 02 : 매수
|
||||
fm_ord_pric (str): FM주문가격
|
||||
ecis_rsvn_ord_yn (str): 행사예약주문여부
|
||||
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_futr_fx_pdno="6AU22",
|
||||
... sll_buy_dvsn_cd="02",
|
||||
... fm_ord_pric="",
|
||||
... ecis_rsvn_ord_yn=""
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '80012345')")
|
||||
raise ValueError("cano is required. (e.g. '80012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not ovrs_futr_fx_pdno:
|
||||
logger.error("ovrs_futr_fx_pdno is required. (e.g. '6AU22')")
|
||||
raise ValueError("ovrs_futr_fx_pdno is required. (e.g. '6AU22')")
|
||||
if sll_buy_dvsn_cd not in ["01", "02"]:
|
||||
logger.error("sll_buy_dvsn_cd is required. (e.g. '01' or '02')")
|
||||
raise ValueError("sll_buy_dvsn_cd is required. (e.g. '01' or '02')")
|
||||
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "OTFM3304R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_FUTR_FX_PDNO": ovrs_futr_fx_pdno,
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd,
|
||||
"FM_ORD_PRIC": fm_ord_pric,
|
||||
"ECIS_RSVN_ORD_YN": ecis_rsvn_ord_yn,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output'):
|
||||
output_data = res.getBody().output
|
||||
if not isinstance(output_data, list):
|
||||
output_data = [output_data]
|
||||
current_data = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data = pd.DataFrame()
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont == "M":
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_psamount(
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
ovrs_futr_fx_pdno,
|
||||
sll_buy_dvsn_cd,
|
||||
fm_ord_pric,
|
||||
ecis_rsvn_ord_yn,
|
||||
dataframe, "N", 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,135 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from inquire_time_futurechartprice import inquire_time_futurechartprice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 분봉조회[해외선물-016]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'ret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시각',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
NUMERIC_COLUMNS = ['전일대비구분', '전일대비가격', '전일대비율', '시가', '고가', '저가', '체결가격', '체결수량', '누적거래수량']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 분봉조회[해외선물-016]
|
||||
|
||||
해외선물 분봉조회 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목코드 (ex) BONU25 ※ 종목코드 "포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수선물" 참고)
|
||||
- exch_cd (str): 거래소코드 (EUREX)
|
||||
- start_date_time (str): 조회시작일시 (공백)
|
||||
- close_date_time (str): 조회종료일시 (ex) 20230823)
|
||||
- qry_tp (str): 조회구분 (Q : 최초조회시 , P : 다음키(INDEX_KEY) 입력하여 조회시)
|
||||
- qry_cnt (str): 요청개수 (120 (조회갯수))
|
||||
- qry_gap (str): 묶음개수 (5 (분간격))
|
||||
- index_key (str): 이전조회KEY (다음조회(QRY_TP를 P로 입력) 시, 이전 호출의 "output1 > index_key" 기입하여 조회)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물 분봉조회 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_time_futurechartprice(srs_cd="BONU25", exch_cd="EUREX", start_date_time="20250101", close_date_time="20250702", qry_tp="", qry_cnt="120", qry_gap="5", index_key="")
|
||||
"""
|
||||
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_futurechartprice(
|
||||
srs_cd="BONU25", # 종목코드
|
||||
exch_cd="EUREX", # 거래소코드
|
||||
start_date_time="20250101", # 조회시작일시
|
||||
close_date_time="20250701", # 조회종료일시
|
||||
qry_tp="Q", # 조회구분
|
||||
qry_cnt="120", # 요청개수
|
||||
qry_gap="1", # 묶음개수
|
||||
index_key="", # 이전조회KEY
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
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,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 분봉조회[해외선물-016]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/inquire-time-futurechartprice"
|
||||
|
||||
def inquire_time_futurechartprice(
|
||||
srs_cd: str, # 종목코드
|
||||
exch_cd: str, # 거래소코드
|
||||
start_date_time: str, # 조회시작일시
|
||||
close_date_time: str, # 조회종료일시
|
||||
qry_tp: str, # 조회구분
|
||||
qry_cnt: str, # 요청개수
|
||||
qry_gap: str, # 묶음개수
|
||||
index_key: str, # 이전조회KEY
|
||||
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]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 분봉조회[해외선물-016]
|
||||
해외선물 분봉조회 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): ex) CNHU24 ※ 종목코드 "포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수선물" 참고
|
||||
exch_cd (str): CME
|
||||
start_date_time (str): 공백
|
||||
close_date_time (str): ex) 20230823
|
||||
qry_tp (str): Q : 최초조회시 , P : 다음키(INDEX_KEY) 입력하여 조회시
|
||||
qry_cnt (str): 120 (조회갯수)
|
||||
qry_gap (str): 5 (분간격)
|
||||
index_key (str): 다음조회(QRY_TP를 P로 입력) 시, 이전 호출의 "output1 > index_key" 기입하여 조회
|
||||
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_futurechartprice(
|
||||
... srs_cd="BONU25",
|
||||
... exch_cd="EUREX",
|
||||
... start_date_time="20250101",
|
||||
... close_date_time="20250702",
|
||||
... qry_tp="Q",
|
||||
... qry_cnt="120",
|
||||
... qry_gap="1",
|
||||
... index_key=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. 'BONU25')")
|
||||
raise ValueError("srs_cd is required. (e.g. 'BONU25')")
|
||||
if not exch_cd:
|
||||
logger.error("exch_cd is required. (e.g. 'EUREX')")
|
||||
raise ValueError("exch_cd is required. (e.g. 'EUREX')")
|
||||
if not close_date_time:
|
||||
logger.error("close_date_time is required. (e.g. '20250702')")
|
||||
raise ValueError("close_date_time is required. (e.g. '20250702')")
|
||||
if not qry_cnt:
|
||||
logger.error("qry_cnt is required. (e.g. '120')")
|
||||
raise ValueError("qry_cnt is required. (e.g. '120')")
|
||||
if not qry_gap:
|
||||
logger.error("qry_gap is required. (e.g. '1', '5', '10', '15', '30', '60')")
|
||||
raise ValueError("qry_gap is required. (e.g. '1', '5', '10', '15', '30', '60')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFC55020400"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
"EXCH_CD": exch_cd,
|
||||
"START_DATE_TIME": start_date_time,
|
||||
"CLOSE_DATE_TIME": close_date_time,
|
||||
"QRY_TP": qry_tp,
|
||||
"QRY_CNT": qry_cnt,
|
||||
"QRY_GAP": qry_gap,
|
||||
"INDEX_KEY": index_key,
|
||||
}
|
||||
|
||||
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
|
||||
index_key = res.getBody().output2["index_key"]
|
||||
qry_tp = "P"
|
||||
|
||||
print("index _key", index_key, "//" )
|
||||
|
||||
if tr_cont in ["M", "F"]:
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_time_futurechartprice(
|
||||
srs_cd,
|
||||
exch_cd,
|
||||
start_date_time,
|
||||
close_date_time,
|
||||
qry_tp,
|
||||
qry_cnt,
|
||||
qry_gap,
|
||||
index_key,
|
||||
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,95 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from inquire_time_optchartprice import inquire_time_optchartprice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 분봉조회 [해외선물-040]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'ret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시간',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['자료개수', 'N틱최종개수', '전일대비구분', '전일대비가격', '전일대비율', '시가', '고가', '저가', '체결가격', '체결수량', '누적거래수량']
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외옵션 분봉조회 테스트 함수
|
||||
|
||||
이 함수는 해외옵션 분봉조회 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 DXM24 (ICE 거래소)를 사용합니다.
|
||||
|
||||
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:
|
||||
result1, result2 = inquire_time_optchartprice(srs_cd="DXM24", exch_cd="ICE", qry_cnt="30")
|
||||
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,151 @@
|
||||
"""
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 분봉조회 [해외선물-040]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/inquire-time-optchartprice"
|
||||
|
||||
def inquire_time_optchartprice(
|
||||
srs_cd: str, # 종목코드
|
||||
exch_cd: str, # 거래소코드
|
||||
qry_cnt: str, # 요청개수
|
||||
start_date_time: str = "", # 조회시작일시
|
||||
close_date_time: str = "", # 조회종료일시
|
||||
qry_gap: str = "", # 묶음개수
|
||||
qry_tp: str = "", # 조회구분
|
||||
index_key: str = "", # 이전조회KEY
|
||||
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입니다.
|
||||
한 번의 호출에 120건까지 확인 가능하며, QRY_TP, INDEX_KEY 를 이용하여 다음조회 가능합니다.
|
||||
|
||||
※ 다음조회 방법
|
||||
(처음조회) "QRY_TP":"Q", "QRY_CNT":"120", "INDEX_KEY":""
|
||||
(다음조회) "QRY_TP":"P", "QRY_CNT":"120", "INDEX_KEY":"20240902 5" ◀ 이전 호출의 "output1 > index_key" 기입
|
||||
|
||||
(중요) 해외옵션시세 출력값을 해석하실 때 focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
- focode.mst(해외지수옵션 종목마스터파일), (해외주식옵션 종목마스터파일) 다운로드 방법
|
||||
1) focode.mst(해외지수옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외옵션정보.h)를 참고하여 해석
|
||||
2) fostkcode.mst(해외주식옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외주식옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외주식옵션정보.h)를 참고하여 해석
|
||||
|
||||
- 소수점 계산 시, focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)의 sCalcDesz(계산 소수점) 값 참고
|
||||
EX) focode.mst 파일의 sCalcDesz(계산 소수점) 값
|
||||
품목코드 OES 계산소수점 -2 → 시세 7525 수신 시 75.25 로 해석
|
||||
품목코드 O6E 계산소수점 -4 → 시세 54.0 수신 시 0.0054 로 해석
|
||||
|
||||
Args:
|
||||
srs_cd (str): [필수] 종목코드 (ex. OESU24 C5500)
|
||||
exch_cd (str): [필수] 거래소코드 (ex. CME)
|
||||
qry_cnt (str): [필수] 요청개수 (ex. 20)
|
||||
start_date_time (str): 조회시작일시
|
||||
close_date_time (str): 조회종료일시
|
||||
qry_gap (str): 묶음개수
|
||||
qry_tp (str): 조회구분
|
||||
index_key (str): 이전조회KEY
|
||||
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 DataFrame, output2 DataFrame)
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = inquire_time_optchartprice("OESU24 C5500", "CME", "20")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
if srs_cd == "":
|
||||
raise ValueError("srs_cd is required (e.g. 'OESU24 C5500')")
|
||||
|
||||
if exch_cd == "":
|
||||
raise ValueError("exch_cd is required (e.g. 'CME')")
|
||||
|
||||
if qry_cnt == "":
|
||||
raise ValueError("qry_cnt is required (e.g. '20')")
|
||||
|
||||
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 = "HHDFO55020100" # 해외옵션 분봉조회
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd, # 종목코드
|
||||
"EXCH_CD": exch_cd, # 거래소코드
|
||||
"QRY_CNT": qry_cnt, # 요청개수
|
||||
"START_DATE_TIME": start_date_time, # 조회시작일시
|
||||
"CLOSE_DATE_TIME": close_date_time, # 조회종료일시
|
||||
"QRY_GAP": qry_gap, # 묶음개수
|
||||
"QRY_TP": qry_tp, # 조회구분
|
||||
"INDEX_KEY": index_key # 이전조회KEY
|
||||
}
|
||||
|
||||
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
|
||||
index_key = res.getBody().output1["index_key"]
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return inquire_time_optchartprice(
|
||||
srs_cd, exch_cd, qry_cnt, start_date_time, close_date_time,
|
||||
qry_gap, qry_tp, index_key, "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,111 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from inquire_unpd import inquire_unpd
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 미결제내역조회(잔고) [v1_해외선물-005]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'ovrs_futr_fx_pdno': '해외선물FX상품번호',
|
||||
'prdt_type_cd': '상품유형코드',
|
||||
'crcy_cd': '통화코드',
|
||||
'sll_buy_dvsn_cd': '매도매수구분코드',
|
||||
'fm_ustl_qty': 'FM미결제수량',
|
||||
'fm_ccld_avg_pric': 'FM체결평균가격',
|
||||
'fm_now_pric': 'FM현재가격',
|
||||
'fm_evlu_pfls_amt': 'FM평가손익금액',
|
||||
'fm_opt_evlu_amt': 'FM옵션평가금액',
|
||||
'fm_otp_evlu_pfls_amt': 'FM옵션평가손익금액',
|
||||
'fuop_dvsn': '선물옵션구분',
|
||||
'ecis_rsvn_ord_yn': '행사예약주문여부',
|
||||
'fm_lqd_psbl_qty': 'FM청산가능수량'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['FM미결제수량', 'FM체결평균가격', 'FM현재가격', 'FM평가손익금액', 'FM옵션평가금액', 'FM옵션평가손익금액', 'FM청산가능수량']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 미결제내역조회(잔고)[v1_해외선물-005]
|
||||
|
||||
해외선물옵션 미결제내역조회(잔고) 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- fuop_dvsn (str): 선물옵션구분 (00: 전체 / 01:선물 / 02: 옵션)
|
||||
- ctx_area_fk100 (str): 연속조회검색조건100 ()
|
||||
- ctx_area_nk100 (str): 연속조회키100 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 미결제내역조회(잔고) 결과
|
||||
|
||||
Example:
|
||||
>>> df = inquire_unpd(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, fuop_dvsn="00", ctx_area_fk100="", ctx_area_nk100="")
|
||||
"""
|
||||
try:
|
||||
# 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 호출
|
||||
logger.info("API 호출")
|
||||
result = inquire_unpd(
|
||||
cano=trenv.my_acct,
|
||||
acnt_prdt_cd=trenv.my_prod,
|
||||
fuop_dvsn="00",
|
||||
ctx_area_fk100="",
|
||||
ctx_area_nk100=""
|
||||
)
|
||||
|
||||
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,128 @@
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
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-futureoption/v1/trading/inquire-unpd"
|
||||
|
||||
def inquire_unpd(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
fuop_dvsn: str, # 선물옵션구분
|
||||
ctx_area_fk100: str, # 연속조회검색조건100
|
||||
ctx_area_nk100: str, # 연속조회키100
|
||||
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자리
|
||||
fuop_dvsn (str): 00: 전체 / 01:선물 / 02: 옵션
|
||||
ctx_area_fk100 (str): 연속조회검색조건100
|
||||
ctx_area_nk100 (str): 연속조회키100
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 미결제내역조회(잔고) 데이터
|
||||
|
||||
Example:
|
||||
>>> df = inquire_unpd(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... fuop_dvsn="00",
|
||||
... ctx_area_fk100="",
|
||||
... ctx_area_nk100=""
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '80012345')")
|
||||
raise ValueError("cano is required. (e.g. '80012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if fuop_dvsn not in ['00', '01', '02']:
|
||||
logger.error("fuop_dvsn is required. (e.g. '00')")
|
||||
raise ValueError("fuop_dvsn is required. (e.g. '00')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
if depth >= max_depth:
|
||||
logger.warning("Maximum recursion depth (%d) reached. Stopping further requests.", max_depth)
|
||||
return dataframe if dataframe is not None else pd.DataFrame()
|
||||
|
||||
tr_id = "OTFM1412R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"FUOP_DVSN": fuop_dvsn,
|
||||
"CTX_AREA_FK100": ctx_area_fk100,
|
||||
"CTX_AREA_NK100": ctx_area_nk100,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output'):
|
||||
output_data = res.getBody().output
|
||||
if not isinstance(output_data, list):
|
||||
output_data = [output_data]
|
||||
current_data = pd.DataFrame(output_data)
|
||||
else:
|
||||
current_data = pd.DataFrame()
|
||||
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True)
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont = res.getHeader().tr_cont
|
||||
|
||||
if tr_cont == "M":
|
||||
logger.info("Calling next page...")
|
||||
ka.smart_sleep()
|
||||
return inquire_unpd(
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
fuop_dvsn,
|
||||
ctx_area_fk100,
|
||||
ctx_area_nk100,
|
||||
"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,135 @@
|
||||
# -*- 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 investor_unpd_trend import investor_unpd_trend
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 미결제추이 [v1_해외선물-029]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'row_cnt': '응답레코드카운트',
|
||||
'prod_iscd': '상품',
|
||||
'cftc_iscd': 'CFTC코드',
|
||||
'bsop_date': '일자',
|
||||
'bidp_spec': '매수투기',
|
||||
'askp_spec': '매도투기',
|
||||
'spread_spec': '스프레드투기',
|
||||
'bidp_hedge': '매수헤지',
|
||||
'askp_hedge': '매도헤지',
|
||||
'hts_otst_smtn': '미결제합계',
|
||||
'bidp_missing': '매수누락',
|
||||
'askp_missing': '매도누락',
|
||||
'bidp_spec_cust': '매수투기고객',
|
||||
'askp_spec_cust': '매도투기고객',
|
||||
'spread_spec_cust': '스프레드투기고객',
|
||||
'bidp_hedge_cust': '매수헤지고객',
|
||||
'askp_hedge_cust': '매도헤지고객',
|
||||
'cust_smtn': '고객합계'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['응답레코드카운트', '매수투기', '매도투기', '스프레드투기', '매수헤지', '매도헤지', '미결제합계',
|
||||
'매수누락', '매도누락', '매수투기고객', '매도투기고객', '스프레드투기고객', '매수헤지고객',
|
||||
'매도헤지고객', '고객합계']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 미결제추이[해외선물-029]
|
||||
|
||||
해외선물 미결제추이 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- prod_iscd (str): 상품 (금리 (GE, ZB, ZF,ZN,ZT), 금속(GC, PA, PL,SI, HG), 농산물(CC, CT,KC, OJ, SB, ZC,ZL, ZM, ZO, ZR, ZS, ZW), 에너지(CL, HO, NG, WBS), 지수(ES, NQ, TF, YM, VX), 축산물(GF, HE, LE), 통화(6A, 6B, 6C, 6E, 6J, 6N, 6S, DX))
|
||||
- bsop_date (str): 일자 (기준일(ex)20240513))
|
||||
- upmu_gubun (str): 구분 (0(수량), 1(증감))
|
||||
- cts_key (str): CTS_KEY (공백)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물 미결제추이 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = investor_unpd_trend(prod_iscd="GE", bsop_date="20240513", upmu_gubun="0", cts_key="")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2 = investor_unpd_trend(
|
||||
prod_iscd="CL",
|
||||
bsop_date="20250630",
|
||||
upmu_gubun="0",
|
||||
cts_key=""
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
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,156 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 미결제추이 [해외선물-029]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/investor-unpd-trend"
|
||||
|
||||
def investor_unpd_trend(
|
||||
prod_iscd: str, # 상품
|
||||
bsop_date: str, # 일자
|
||||
upmu_gubun: str, # 구분
|
||||
cts_key: str, # CTS_KEY
|
||||
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]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 미결제추이[해외선물-029]
|
||||
해외선물 미결제추이 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
prod_iscd (str): 금리 (GE, ZB, ZF,ZN,ZT), 금속(GC, PA, PL,SI, HG), 농산물(CC, CT,KC, OJ, SB, ZC,ZL, ZM, ZO, ZR, ZS, ZW), 에너지(CL, HO, NG, WBS), 지수(ES, NQ, TF, YM, VX), 축산물(GF, HE, LE), 통화(6A, 6B, 6C, 6E, 6J, 6N, 6S, DX)
|
||||
bsop_date (str): 기준일(ex)20240513)
|
||||
upmu_gubun (str): 0(수량), 1(증감)
|
||||
cts_key (str): 공백
|
||||
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 = investor_unpd_trend(
|
||||
... prod_iscd="ES",
|
||||
... bsop_date="20240624",
|
||||
... upmu_gubun="0",
|
||||
... cts_key=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not prod_iscd:
|
||||
logger.error("prod_iscd is required. (e.g. 'ES')")
|
||||
raise ValueError("prod_iscd is required. (e.g. 'ES')")
|
||||
if not bsop_date:
|
||||
logger.error("bsop_date is required. (e.g. '20240624')")
|
||||
raise ValueError("bsop_date is required. (e.g. '20240624')")
|
||||
if not upmu_gubun:
|
||||
logger.error("upmu_gubun is required. (e.g. '0')")
|
||||
raise ValueError("upmu_gubun 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 = "HHDDB95030000"
|
||||
|
||||
params = {
|
||||
"PROD_ISCD": prod_iscd,
|
||||
"BSOP_DATE": bsop_date,
|
||||
"UPMU_GUBUN": upmu_gubun,
|
||||
"CTS_KEY": cts_key,
|
||||
}
|
||||
|
||||
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 investor_unpd_trend(
|
||||
prod_iscd,
|
||||
bsop_date,
|
||||
upmu_gubun,
|
||||
cts_key,
|
||||
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,143 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from margin_detail import margin_detail
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 증거금상세 [해외선물-032]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'cano': '종합계좌번호',
|
||||
'acnt_prdt_cd': '계좌상품코드',
|
||||
'crcy_cd': '통화코드',
|
||||
'resp_dt': '응답일자',
|
||||
'acnt_net_risk_mgna_aply_yn': '계좌순위험증거금적용여부',
|
||||
'fm_ord_psbl_amt': 'FM주문가능금액',
|
||||
'fm_add_mgn_amt': 'FM추가증거금액',
|
||||
'fm_brkg_mgn_amt': 'FM위탁증거금액',
|
||||
'fm_excc_brkg_mgn_amt': 'FM정산위탁증거금액',
|
||||
'fm_ustl_mgn_amt': 'FM미결제증거금액',
|
||||
'fm_mntn_mgn_amt': 'FM유지증거금액',
|
||||
'fm_ord_mgn_amt': 'FM주문증거금액',
|
||||
'fm_futr_ord_mgn_amt': 'FM선물주문증거금액',
|
||||
'fm_opt_buy_ord_amt': 'FM옵션매수주문금액',
|
||||
'fm_opt_sll_ord_mgn_amt': 'FM옵션매도주문증거금액',
|
||||
'fm_opt_buy_ord_mgn_amt': 'FM옵션매수주문증거금액',
|
||||
'fm_ecis_rsvn_mgn_amt': 'FM행사예약증거금액',
|
||||
'fm_span_brkg_mgn_amt': 'FMSPAN위탁증거금액',
|
||||
'fm_span_pric_altr_mgn_amt': 'FMSPAN가격변동증거금액',
|
||||
'fm_span_term_sprd_mgn_amt': 'FMSPAN기간스프레드증거금액',
|
||||
'fm_span_buy_opt_min_mgn_amt': 'FMSPAN옵션가격증거금액',
|
||||
'fm_span_opt_min_mgn_amt': 'FMSPAN옵션최소증거금액',
|
||||
'fm_span_tot_risk_mgn_amt': 'FMSPAN총위험증거금액',
|
||||
'fm_span_mntn_mgn_amt': 'FMSPAN유지증거금액',
|
||||
'fm_span_mntn_pric_altr_mgn_amt': 'FMSPAN유지가격변동증거금액',
|
||||
'fm_span_mntn_term_sprd_mgn_amt': 'FMSPAN유지기간스프레드증거금액',
|
||||
'fm_span_mntn_opt_pric_mgn_amt': 'FMSPAN유지옵션가격증거금액',
|
||||
'fm_span_mntn_opt_min_mgn_amt': 'FMSPAN유지옵션최소증거금액',
|
||||
'fm_span_mntn_tot_risk_mgn_amt': 'FMSPAN유지총위험증거금액',
|
||||
'fm_eurx_brkg_mgn_amt': 'FMEUREX위탁증거금액',
|
||||
'fm_eurx_pric_altr_mgn_amt': 'FMEUREX가격변동증거금액',
|
||||
'fm_eurx_term_sprd_mgn_amt': 'FMEUREX기간스프레드증거금액',
|
||||
'fm_eurx_opt_pric_mgn_amt': 'FMEUREX옵션가격증거금액',
|
||||
'fm_eurx_buy_opt_min_mgn_amt': 'FMEUREX매수옵션최소증거금액',
|
||||
'fm_eurx_tot_risk_mgn_amt': 'FMEUREX총위험증거금액',
|
||||
'fm_eurx_mntn_mgn_amt': 'FMEUREX유지증거금액',
|
||||
'fm_eurx_mntn_pric_altr_mgn_amt': 'FMEUREX유지가격변동증거금액',
|
||||
'fm_eurx_mntn_term_sprd_mgn_amt': 'FMEUREX기간스프레드증거금액',
|
||||
'fm_eurx_mntn_opt_pric_mgn_amt': 'FMEUREX유지옵션가격증거금액',
|
||||
'fm_eurx_mntn_tot_risk_mgn_amt': 'FMEUREX유지총위험증거금액',
|
||||
'fm_gnrl_brkg_mgn_amt': 'FM일반위탁증거금액',
|
||||
'fm_futr_ustl_mgn_amt': 'FM선물미결제증거금액',
|
||||
'fm_sll_opt_ustl_mgn_amt': 'FM매도옵션미결제증거금액',
|
||||
'fm_buy_opt_ustl_mgn_amt': 'FM매수옵션미결제증거금액',
|
||||
'fm_sprd_ustl_mgn_amt': 'FM스프레드미결제증거금액',
|
||||
'fm_avg_dsct_mgn_amt': 'FMAVG할인증거금액',
|
||||
'fm_gnrl_mntn_mgn_amt': 'FM일반유지증거금액',
|
||||
'fm_futr_mntn_mgn_amt': 'FM선물유지증거금액',
|
||||
'fm_opt_mntn_mgn_amt': 'FM옵션유지증거금액'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 증거금상세[해외선물-032]
|
||||
|
||||
해외선물옵션 증거금상세 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 ()
|
||||
- acnt_prdt_cd (str): 계좌상품코드 ()
|
||||
- crcy_cd (str): 통화코드 ('TKR(TOT_KRW), TUS(TOT_USD), USD(미국달러), HKD(홍콩달러), CNY(중국위안화), JPY )일본엔화), VND(베트남동)')
|
||||
- inqr_dt (str): 조회일자 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 증거금상세 결과
|
||||
|
||||
Example:
|
||||
>>> df = margin_detail(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, crcy_cd="USD", inqr_dt="20250701")
|
||||
"""
|
||||
try:
|
||||
# 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 호출
|
||||
logger.info("API 호출")
|
||||
result = margin_detail(
|
||||
cano=trenv.my_acct,
|
||||
acnt_prdt_cd=trenv.my_prod,
|
||||
crcy_cd="TKR",
|
||||
inqr_dt="20250625"
|
||||
)
|
||||
|
||||
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,127 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 증거금상세 [해외선물-032]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/margin-detail"
|
||||
|
||||
def margin_detail(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
crcy_cd: str, # 통화코드
|
||||
inqr_dt: str, # 조회일자
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 증거금상세[해외선물-032]
|
||||
해외선물옵션 증거금상세 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 종합계좌번호 (8자리)
|
||||
acnt_prdt_cd (str): 계좌상품코드 (2자리)
|
||||
crcy_cd (str): 통화코드 ('TKR', 'TUS', 'USD', 'HKD', 'CNY', 'JPY', 'VND')
|
||||
inqr_dt (str): 조회일자 (8자리, YYYYMMDD 형식)
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 증거금상세 데이터
|
||||
|
||||
Example:
|
||||
>>> df = margin_detail(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... crcy_cd="USD",
|
||||
... inqr_dt="20230701"
|
||||
... )
|
||||
>>> 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 crcy_cd:
|
||||
logger.error("crcy_cd is required. (e.g. 'USD')")
|
||||
raise ValueError("crcy_cd is required. (e.g. 'USD')")
|
||||
if not inqr_dt:
|
||||
logger.error("inqr_dt is required. (e.g. '20230701')")
|
||||
raise ValueError("inqr_dt is required. (e.g. '20230701')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "OTFM3115R"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"CRCY_CD": crcy_cd,
|
||||
"INQR_DT": inqr_dt,
|
||||
}
|
||||
|
||||
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 margin_detail(
|
||||
cano,
|
||||
acnt_prdt_cd,
|
||||
crcy_cd,
|
||||
inqr_dt,
|
||||
"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,112 @@
|
||||
# -*- 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 market_time import market_time
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물옵션 장운영시간 [해외선물-030]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'fm_pdgr_cd': 'FM상품군코드',
|
||||
'fm_pdgr_name': 'FM상품군명',
|
||||
'fm_excg_cd': 'FM거래소코드',
|
||||
'fm_excg_name': 'FM거래소명',
|
||||
'fuop_dvsn_name': '선물옵션구분명',
|
||||
'fm_clas_cd': 'FM클래스코드',
|
||||
'fm_clas_name': 'FM클래스명',
|
||||
'am_mkmn_strt_tmd': '오전장운영시작시각',
|
||||
'am_mkmn_end_tmd': '오전장운영종료시각',
|
||||
'pm_mkmn_strt_tmd': '오후장운영시작시각',
|
||||
'pm_mkmn_end_tmd': '오후장운영종료시각',
|
||||
'mkmn_nxdy_strt_tmd': '장운영익일시작시각',
|
||||
'mkmn_nxdy_end_tmd': '장운영익일종료시각',
|
||||
'base_mket_strt_tmd': '기본시장시작시각',
|
||||
'base_mket_end_tmd': '기본시장종료시각'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물옵션 장운영시간[해외선물-030]
|
||||
|
||||
해외선물옵션 장운영시간 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- fm_pdgr_cd (str): FM상품군코드 (공백)
|
||||
- fm_clas_cd (str): FM클래스코드 ('공백(전체), 001(통화), 002(금리), 003(지수), 004(농산물),005(축산물),006(금속),007(에너지)')
|
||||
- fm_excg_cd (str): FM거래소코드 ('CME(CME), EUREX(EUREX), HKEx(HKEx), ICE(ICE), SGX(SGX), OSE(OSE), ASX(ASX), CBOE(CBOE), MDEX(MDEX), NYSE(NYSE), BMF(BMF),FTX(FTX), HNX(HNX), ETC(기타)')
|
||||
- opt_yn (str): 옵션여부 (%(전체), N(선물), Y(옵션))
|
||||
- ctx_area_nk200 (str): 연속조회키200 ()
|
||||
- ctx_area_fk200 (str): 연속조회검색조건200 ()
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 장운영시간 결과
|
||||
|
||||
Example:
|
||||
>>> df = market_time(fm_pdgr_cd="", fm_clas_cd="", fm_excg_cd="CME", opt_yn="N", ctx_area_nk200="", ctx_area_fk200="")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result = market_time(
|
||||
fm_pdgr_cd="",
|
||||
fm_clas_cd="",
|
||||
fm_excg_cd="CME",
|
||||
opt_yn="%",
|
||||
ctx_area_nk200="",
|
||||
ctx_area_fk200=""
|
||||
)
|
||||
|
||||
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,134 @@
|
||||
# -*- 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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물옵션 장운영시간 [해외선물-030]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/market-time"
|
||||
|
||||
def market_time(
|
||||
fm_pdgr_cd: str, # FM상품군코드
|
||||
fm_clas_cd: str, # FM클래스코드
|
||||
fm_excg_cd: str, # FM거래소코드
|
||||
opt_yn: str, # 옵션여부
|
||||
ctx_area_nk200: str, # 연속조회키200
|
||||
ctx_area_fk200: str, # 연속조회검색조건200
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물옵션 장운영시간[해외선물-030]
|
||||
해외선물옵션 장운영시간 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
fm_pdgr_cd (str): FM상품군코드
|
||||
fm_clas_cd (str): FM클래스코드
|
||||
fm_excg_cd (str): FM거래소코드
|
||||
opt_yn (str): 옵션여부
|
||||
ctx_area_nk200 (str): 연속조회키200
|
||||
ctx_area_fk200 (str): 연속조회검색조건200
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 장운영시간 데이터
|
||||
|
||||
Example:
|
||||
>>> df = market_time(
|
||||
... fm_pdgr_cd="001",
|
||||
... fm_clas_cd="003",
|
||||
... fm_excg_cd="CME",
|
||||
... opt_yn="N",
|
||||
... ctx_area_nk200="",
|
||||
... ctx_area_fk200=""
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not fm_excg_cd:
|
||||
logger.error("fm_excg_cd is required. (e.g. 'CME')")
|
||||
raise ValueError("fm_excg_cd is required. (e.g. 'CME')")
|
||||
if not opt_yn:
|
||||
logger.error("opt_yn is required. (e.g. 'N')")
|
||||
raise ValueError("opt_yn is required. (e.g. 'N')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "OTFM2229R"
|
||||
|
||||
params = {
|
||||
"FM_PDGR_CD": fm_pdgr_cd,
|
||||
"FM_CLAS_CD": fm_clas_cd,
|
||||
"FM_EXCG_CD": fm_excg_cd,
|
||||
"OPT_YN": opt_yn,
|
||||
"CTX_AREA_NK200": ctx_area_nk200,
|
||||
"CTX_AREA_FK200": ctx_area_fk200,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
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, ctx_area_nk200, ctx_area_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 market_time(
|
||||
fm_pdgr_cd,
|
||||
fm_clas_cd,
|
||||
fm_excg_cd,
|
||||
opt_yn,
|
||||
ctx_area_nk200,
|
||||
ctx_area_fk200,
|
||||
"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,137 @@
|
||||
# -*- 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 monthly_ccnl import monthly_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(월간)[해외선물-020]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'tret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시각',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = ['자료개수', 'N틱최종개수', '전일대비구분', '전일대비가격', '전일대비율', '시가', '고가', '저가', '체결가격', '체결수량', '누적거래수량',]
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 체결추이(월간)[해외선물-020]
|
||||
|
||||
해외선물 체결추이(월간) 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목코드 (예) 6AM24)
|
||||
- exch_cd (str): 거래소코드 (예) CME)
|
||||
- start_date_time (str): 조회시작일시 (공백)
|
||||
- close_date_time (str): 조회종료일시 (예) 20240402)
|
||||
- qry_tp (str): 조회구분 (Q : 최초조회시 , P : 다음키(INDEX_KEY) 입력하여 조회시)
|
||||
- qry_cnt (str): 요청개수 (예) 30 (최대 40))
|
||||
- qry_gap (str): 묶음개수 (공백 (분만 사용))
|
||||
- index_key (str): 이전조회KEY (공백)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물 체결추이(월간) 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = monthly_ccnl(srs_cd="6AM24", exch_cd="CME", start_date_time="", close_date_time="20240402", qry_tp="Q", qry_cnt="30", qry_gap="", index_key="")
|
||||
"""
|
||||
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 = monthly_ccnl(
|
||||
srs_cd="6AM24", # 종목코드
|
||||
exch_cd="CME", # 거래소코드
|
||||
start_date_time="", # 조회시작일시
|
||||
close_date_time="20240402", # 조회종료일시
|
||||
qry_tp="Q", # 조회구분
|
||||
qry_cnt="30", # 요청개수
|
||||
qry_gap="", # 묶음개수
|
||||
index_key="", # 이전조회KEY
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
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,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/monthly-ccnl"
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(월간)[해외선물-020]
|
||||
##############################################################################################
|
||||
|
||||
def monthly_ccnl(
|
||||
srs_cd: str, # 종목코드
|
||||
exch_cd: str, # 거래소코드
|
||||
start_date_time: str, # 조회시작일시
|
||||
close_date_time: str, # 조회종료일시
|
||||
qry_tp: str, # 조회구분
|
||||
qry_cnt: str, # 요청개수
|
||||
qry_gap: str, # 묶음개수
|
||||
index_key: str, # 이전조회KEY
|
||||
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]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 체결추이(월간)[해외선물-020]
|
||||
해외선물 체결추이(월간) API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): 종목코드 (예: '6AM24')
|
||||
exch_cd (str): 거래소코드 (예: 'CME')
|
||||
start_date_time (str): 조회시작일시 (공백 허용)
|
||||
close_date_time (str): 조회종료일시 (예: '20240402')
|
||||
qry_tp (str): 조회구분 ('Q': 최초조회, 'P': 다음키 입력하여 조회)
|
||||
qry_cnt (str): 요청개수 (예: '30', 최대 '40')
|
||||
qry_gap (str): 묶음개수 (공백 허용)
|
||||
index_key (str): 이전조회KEY (공백 허용)
|
||||
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 = monthly_ccnl(
|
||||
... srs_cd="6AM24",
|
||||
... exch_cd="CME",
|
||||
... start_date_time="",
|
||||
... close_date_time="20240402",
|
||||
... qry_tp="Q",
|
||||
... qry_cnt="30",
|
||||
... qry_gap="",
|
||||
... index_key=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. '6AM24')")
|
||||
raise ValueError("srs_cd is required. (e.g. '6AM24')")
|
||||
if not exch_cd:
|
||||
logger.error("exch_cd is required. (e.g. 'CME')")
|
||||
raise ValueError("exch_cd is required. (e.g. 'CME')")
|
||||
if not close_date_time:
|
||||
logger.error("close_date_time is required. (e.g. '20240402')")
|
||||
raise ValueError("close_date_time is required. (e.g. '20240402')")
|
||||
if not qry_tp:
|
||||
logger.error("qry_tp is required. ('Q' or 'P')")
|
||||
raise ValueError("qry_tp is required. ('Q' or 'P')")
|
||||
if not qry_cnt:
|
||||
logger.error("qry_cnt is required. (e.g. '30')")
|
||||
raise ValueError("qry_cnt is required. (e.g. '30')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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()
|
||||
|
||||
url = API_URL
|
||||
tr_id = "HHDFC55020300"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
"EXCH_CD": exch_cd,
|
||||
"START_DATE_TIME": start_date_time,
|
||||
"CLOSE_DATE_TIME": close_date_time,
|
||||
"QRY_TP": qry_tp,
|
||||
"QRY_CNT": qry_cnt,
|
||||
"QRY_GAP": qry_gap,
|
||||
"INDEX_KEY": index_key,
|
||||
}
|
||||
|
||||
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 monthly_ccnl(
|
||||
srs_cd,
|
||||
exch_cd,
|
||||
start_date_time,
|
||||
close_date_time,
|
||||
qry_tp,
|
||||
qry_cnt,
|
||||
qry_gap,
|
||||
index_key,
|
||||
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,124 @@
|
||||
# -*- 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 opt_asking_price import opt_asking_price
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 호가 [해외선물-033]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'lowp_rice': '저가',
|
||||
'last_price': '현재가',
|
||||
'sttl_price': '정산가',
|
||||
'vol': '거래량',
|
||||
'prev_diff_price': '전일대비가',
|
||||
'prev_diff_rate': '전일대비율',
|
||||
'quot_date': '호가수신일자',
|
||||
'quot_time': '호가수신시각',
|
||||
'bid_qntt': '매수수량',
|
||||
'bid_num': '매수번호',
|
||||
'bid_price': '매수호가',
|
||||
'ask_qntt': '매도수량',
|
||||
'ask_num': '매도번호',
|
||||
'ask_price': '매도호가'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['시가', '고가', '저가', '현재가', '정산가', '거래량', '전일대비가', '전일대비율',
|
||||
'매수수량', '매수호가', '매도수량', '매도호가']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외옵션 호가[해외선물-033]
|
||||
|
||||
해외옵션 호가 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목명 (예)OESM24 C5340)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외옵션 호가 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = opt_asking_price(srs_cd="OESM24 C5340")
|
||||
"""
|
||||
try:
|
||||
# pandas 출력 옵션 설정
|
||||
pd.set_option('display.max_columns', None) # 모든 컬럼 표시
|
||||
pd.set_option('display.width', None) # 출력 너비 제한 해제
|
||||
pd.set_option('display.max_rows', None) # 모든 행 표시
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
# API 호출
|
||||
logger.info("API 호출")
|
||||
result1, result2 = opt_asking_price(srs_cd="1ECN25 C5425")
|
||||
|
||||
# 결과 확인
|
||||
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,131 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 호가 [해외선물-033]
|
||||
##############################################################################################
|
||||
|
||||
# API 정보
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/opt-asking-price"
|
||||
|
||||
def opt_asking_price(
|
||||
srs_cd: 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]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외옵션 호가[해외선물-033]
|
||||
해외옵션 호가 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): 종목명 (예: 'OTXM24 C22000')
|
||||
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 = opt_asking_price(srs_cd="OTXM24 C22000")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. 'OTXM24 C22000')")
|
||||
raise ValueError("srs_cd is required. (e.g. 'OTXM24 C22000')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFO86000000"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
}
|
||||
|
||||
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 opt_asking_price(
|
||||
srs_cd,
|
||||
dataframe1, dataframe2, tr_cont, 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,97 @@
|
||||
"""
|
||||
Created on 20250129
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from opt_daily_ccnl import opt_daily_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 체결추이(일간) [해외선물-037]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
COLUMN_MAPPING = {
|
||||
'ret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시간',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
NUMERIC_COLUMNS = ['자료개수', 'N틱최종개수', '시가', '고가', '저가', '체결가격', '체결수량', '누적거래수량', '전일대비가격', '전일대비율']
|
||||
|
||||
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:
|
||||
result1, result2 = opt_daily_ccnl(srs_cd="DXM24", exch_cd="ICE", qry_cnt="30")
|
||||
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,137 @@
|
||||
"""
|
||||
Created on 20250129
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 체결추이(일간) [해외선물-037]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/opt-daily-ccnl"
|
||||
|
||||
def opt_daily_ccnl(
|
||||
srs_cd: str, # [필수] 종목코드 (ex. OESU24 C5500)
|
||||
exch_cd: str, # [필수] 거래소코드 (ex. CME)
|
||||
qry_cnt: str, # [필수] 요청개수 (ex. 20)
|
||||
start_date_time: str = "", # 조회시작일시
|
||||
close_date_time: str = "", # 조회종료일시
|
||||
qry_gap: str = "", # 묶음개수
|
||||
qry_tp: str = "", # 조회구분
|
||||
index_key: str = "", # 이전조회KEY
|
||||
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입니다.
|
||||
최근 120건까지 데이터 확인이 가능합니다. ("QRY_CNT: 119 입력", START_DATE_TIME, CLOSE_DATE_TIME은 공란)
|
||||
|
||||
※ 호출 시 유의사항
|
||||
: START_DATE_TIME, CLOSE_DATE_TIME은 공란 입력, QRY_CNT는 확인 데이터 개수의 -1 개 입력
|
||||
ex) "START_DATE_TIME":"","CLOSE_DATE_TIME":"","QRY_CNT":"119" → 최근 120건 데이터 조회
|
||||
|
||||
(중요) 해외옵션시세 출력값을 해석하실 때 focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): [필수] 종목코드 (ex. OESU24 C5500)
|
||||
exch_cd (str): [필수] 거래소코드 (ex. CME)
|
||||
qry_cnt (str): [필수] 요청개수 (ex. 20)
|
||||
start_date_time (str): 조회시작일시
|
||||
close_date_time (str): 조회종료일시
|
||||
qry_gap (str): 묶음개수
|
||||
qry_tp (str): 조회구분
|
||||
index_key (str): 이전조회KEY
|
||||
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 = opt_daily_ccnl("OESU24 C5500", "CME", "20")
|
||||
>>> print(result1)
|
||||
>>> print(result2)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if srs_cd == "" or srs_cd is None:
|
||||
raise ValueError("srs_cd is required (e.g. 'OESU24 C5500')")
|
||||
|
||||
if exch_cd == "" or exch_cd is None:
|
||||
raise ValueError("exch_cd is required (e.g. 'CME')")
|
||||
|
||||
if qry_cnt == "" or qry_cnt is None:
|
||||
raise ValueError("qry_cnt is required (e.g. '20')")
|
||||
|
||||
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 = "HHDFO55020100"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
"EXCH_CD": exch_cd,
|
||||
"QRY_CNT": qry_cnt,
|
||||
"START_DATE_TIME": start_date_time,
|
||||
"CLOSE_DATE_TIME": close_date_time,
|
||||
"QRY_GAP": qry_gap,
|
||||
"QRY_TP": qry_tp,
|
||||
"INDEX_KEY": index_key
|
||||
}
|
||||
|
||||
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
|
||||
index_key = res.getBody().output1["index_key"]
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return opt_daily_ccnl(
|
||||
srs_cd, exch_cd, qry_cnt, start_date_time, close_date_time,
|
||||
qry_gap, qry_tp, index_key, "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,89 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from opt_detail import opt_detail
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션종목상세 [해외선물-034]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'exch_cd': '거래소코드',
|
||||
'clas_cd': '품목종류',
|
||||
'crc_cd': '거래통화',
|
||||
'sttl_price': '전일종가',
|
||||
'sttl_date': '정산일',
|
||||
'trst_mgn': '증거금',
|
||||
'disp_digit': '가격표시진법',
|
||||
'tick_sz': '틱사이즈',
|
||||
'tick_val': '틱가치',
|
||||
'mrkt_open_date': '장개시일자',
|
||||
'mrkt_open_time': '장개시시각',
|
||||
'mrkt_close_date': '장마감일자',
|
||||
'mrkt_close_time': '장마감시각',
|
||||
'trd_fr_date': '상장일',
|
||||
'expr_date': '만기일',
|
||||
'trd_to_date': '최종거래일',
|
||||
'remn_cnt': '잔존일수',
|
||||
'stat_tp': '매매여부',
|
||||
'ctrt_size': '계약크기',
|
||||
'stl_tp': '최종결제구분',
|
||||
'frst_noti_date': '최초식별일'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
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: SRS_CD: C5500
|
||||
logging.info("=== case1 조회 ===")
|
||||
try:
|
||||
result = opt_detail(srs_cd="C5500")
|
||||
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 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션종목상세 [해외선물-034]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/opt-detail"
|
||||
|
||||
def opt_detail(
|
||||
srs_cd: str # [필수] 종목명
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외옵션종목상세 API입니다.
|
||||
|
||||
(주의) sstl_price 자리에 정산가 X 전일종가 O 가 수신되는 점 유의 부탁드립니다.
|
||||
|
||||
(중요) 해외옵션시세 출력값을 해석하실 때 focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
- focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일) 다운로드 방법
|
||||
1) focode.mst(해외지수옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외옵션정보.h)를 참고하여 해석
|
||||
2) fostkcode.mst(해외주식옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외주식옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외주식옵션정보.h)를 참고하여 해석
|
||||
|
||||
- 소수점 계산 시, focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)의 sCalcDesz(계산 소수점) 값 참고
|
||||
EX) focode.mst 파일의 sCalcDesz(계산 소수점) 값
|
||||
품목코드 OES 계산소수점 -2 → 시세 7525 수신 시 75.25 로 해석
|
||||
품목코드 O6E 계산소수점 -4 → 시세 54.0 수신 시 0.0054 로 해석
|
||||
|
||||
Args:
|
||||
srs_cd (str): [필수] 종목명
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외옵션종목상세 데이터
|
||||
|
||||
Raises:
|
||||
ValueError: 필수 파라미터 누락 시
|
||||
|
||||
Examples:
|
||||
>>> df = opt_detail("C5500")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if srs_cd == "":
|
||||
raise ValueError("srs_cd is required (e.g. 'C5500')")
|
||||
|
||||
tr_id = "HHDFO55010100"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, "", params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output1, index=[0])
|
||||
return current_data
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Created on 20250112
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from opt_monthly_ccnl import opt_monthly_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 체결추이(월간) [해외선물-039]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'ret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시각',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외옵션 체결추이(월간) 조회 테스트 함수
|
||||
|
||||
이 함수는 해외옵션 체결추이(월간) API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 DXM24 ICE 30건을 사용합니다.
|
||||
|
||||
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:
|
||||
result1, result2 = opt_monthly_ccnl(srs_cd="DXM24", exch_cd="ICE", qry_cnt="30")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
# output1 (object) 처리
|
||||
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 (array) 처리
|
||||
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,145 @@
|
||||
"""
|
||||
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)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 체결추이(월간) [해외선물-039]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/opt-monthly-ccnl"
|
||||
|
||||
def opt_monthly_ccnl(
|
||||
srs_cd: str, # 종목코드
|
||||
exch_cd: str, # 거래소코드
|
||||
qry_cnt: str, # 요청개수
|
||||
start_date_time: str = "", # 조회시작일시
|
||||
close_date_time: str = "", # 조회종료일시
|
||||
qry_gap: str = "", # 묶음개수
|
||||
qry_tp: str = "", # 조회구분
|
||||
index_key: str = "", # 이전조회KEY
|
||||
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입니다.
|
||||
최근 120건까지 데이터 확인이 가능합니다. (START_DATE_TIME, CLOSE_DATE_TIME은 공란 입력)
|
||||
|
||||
(중요) 해외옵션시세 출력값을 해석하실 때 focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
- focode.mst(해외지수옵션 종목마스터파일), (해외주식옵션 종목마스터파일) 다운로드 방법
|
||||
1) focode.mst(해외지수옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외옵션정보.h)를 참고하여 해석
|
||||
2) fostkcode.mst(해외주식옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외주식옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외주식옵션정보.h)를 참고하여 해석
|
||||
|
||||
- 소수점 계산 시, focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)의 sCalcDesz(계산 소수점) 값 참고
|
||||
EX) focode.mst 파일의 sCalcDesz(계산 소수점) 값
|
||||
품목코드 OES 계산소수점 -2 → 시세 7525 수신 시 75.25 로 해석
|
||||
품목코드 O6E 계산소수점 -4 → 시세 54.0 수신 시 0.0054 로 해석
|
||||
|
||||
Args:
|
||||
srs_cd (str): [필수] 종목코드 (ex. OESU24 C5500)
|
||||
exch_cd (str): [필수] 거래소코드 (ex. CME)
|
||||
qry_cnt (str): [필수] 요청개수 (ex. 20)
|
||||
start_date_time (str): 조회시작일시 (ex. "")
|
||||
close_date_time (str): 조회종료일시 (ex. "")
|
||||
qry_gap (str): 묶음개수 (ex. "")
|
||||
qry_tp (str): 조회구분 (ex. "")
|
||||
index_key (str): 이전조회KEY (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:
|
||||
>>> result1, result2 = opt_monthly_ccnl("OESU24 C5500", "CME", "20")
|
||||
>>> print(result1)
|
||||
>>> print(result2)
|
||||
"""
|
||||
|
||||
if srs_cd == "":
|
||||
raise ValueError("srs_cd is required (e.g. 'OESU24 C5500')")
|
||||
|
||||
if exch_cd == "":
|
||||
raise ValueError("exch_cd is required (e.g. 'CME')")
|
||||
|
||||
if qry_cnt == "":
|
||||
raise ValueError("qry_cnt is required (e.g. '20')")
|
||||
|
||||
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 = "HHDFO55020300" # 해외옵션 체결추이(월간)
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
"EXCH_CD": exch_cd,
|
||||
"QRY_CNT": qry_cnt,
|
||||
"START_DATE_TIME": start_date_time,
|
||||
"CLOSE_DATE_TIME": close_date_time,
|
||||
"QRY_GAP": qry_gap,
|
||||
"QRY_TP": qry_tp,
|
||||
"INDEX_KEY": index_key
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
current_data1 = pd.DataFrame([res.getBody().output1])
|
||||
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
|
||||
index_key = res.getBody().output1["index_key"]
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return opt_monthly_ccnl(
|
||||
srs_cd, exch_cd, qry_cnt, start_date_time, close_date_time,
|
||||
qry_gap, qry_tp, index_key, "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,99 @@
|
||||
"""
|
||||
Created on 20250101
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from opt_price import opt_price
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션종목현재가 [해외선물-035]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'proc_date': '최종처리일자',
|
||||
'proc_time': '최종처리시각',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '현재가',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율',
|
||||
'bid_qntt': '매수1수량',
|
||||
'bid_price': '매수1호가',
|
||||
'ask_qntt': '매도1수량',
|
||||
'ask_price': '매도1호가',
|
||||
'trst_mgn': '증거금',
|
||||
'exch_cd': '거래소코드',
|
||||
'crc_cd': '거래통화',
|
||||
'trd_fr_date': '상장일',
|
||||
'expr_date': '만기일',
|
||||
'trd_to_date': '최종거래일',
|
||||
'remn_cnt': '잔존일수',
|
||||
'last_qntt': '체결량',
|
||||
'tot_ask_qntt': '총매도잔량',
|
||||
'tot_bid_qntt': '총매수잔량',
|
||||
'tick_size': '틱사이즈',
|
||||
'open_date': '장개시일자',
|
||||
'open_time': '장개시시각',
|
||||
'close_date': '장종료일자',
|
||||
'close_time': '장종료시각',
|
||||
'sbsnsdate': '영업일자',
|
||||
'sttl_price': '정산가'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
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()
|
||||
|
||||
# 해외옵션종목현재가 조회
|
||||
logging.info("=== 해외옵션종목현재가 조회 ===")
|
||||
try:
|
||||
result = opt_price(srs_cd="DXM24")
|
||||
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,72 @@
|
||||
"""
|
||||
Created on 20250101
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션종목현재가 [해외선물-035]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/opt-price"
|
||||
|
||||
def opt_price(
|
||||
srs_cd: str # 종목코드
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외옵션종목현재가 API입니다.
|
||||
|
||||
(중요) 해외옵션시세 출력값을 해석하실 때 focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
- focode.mst(해외지수옵션 종목마스터파일), (해외주식옵션 종목마스터파일) 다운로드 방법
|
||||
1) focode.mst(해외지수옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외옵션정보.h)를 참고하여 해석
|
||||
2) fostkcode.mst(해외주식옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외주식옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외주식옵션정보.h)를 참고하여 해석
|
||||
|
||||
- 소수점 계산 시, focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)의 sCalcDesz(계산 소수점) 값 참고
|
||||
EX) focode.mst 파일의 sCalcDesz(계산 소수점) 값
|
||||
품목코드 OES 계산소수점 -2 → 시세 7525 수신 시 75.25 로 해석
|
||||
품목코드 O6E 계산소수점 -4 → 시세 54.0 수신 시 0.0054 로 해석
|
||||
|
||||
Args:
|
||||
srs_cd (str): [필수] 종목코드
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외옵션종목현재가 데이터
|
||||
|
||||
Example:
|
||||
>>> df = opt_price(srs_cd="DXM24")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
if srs_cd == "":
|
||||
raise ValueError("srs_cd is required")
|
||||
|
||||
tr_id = "HHDFO55010000" # 해외옵션종목현재가
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd # 종목코드
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, "", params)
|
||||
|
||||
if res.isOK():
|
||||
current_data = pd.DataFrame(res.getBody().output1, index=[0])
|
||||
return current_data
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from opt_tick_ccnl import opt_tick_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 체결추이(틱) [해외선물-038]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'ret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시간',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = ['자료개수', 'N틱최종개수', '시가', '고가', '저가', '체결가격', '체결수량', '누적거래수량', '전일대비가격', '전일대비율']
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외옵션 체결추이(틱) 조회 테스트 함수
|
||||
|
||||
이 함수는 해외옵션 체결추이(틱) API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 DXM24(ICE 거래소)를 사용합니다.
|
||||
|
||||
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:
|
||||
result1, result2 = opt_tick_ccnl(srs_cd="DXM24", exch_cd="ICE", qry_cnt="30")
|
||||
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)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시 (메타데이터에 number로 명시된 필드 없음)
|
||||
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)
|
||||
|
||||
# 숫자형 컬럼 소수점 둘째자리까지 표시 (메타데이터에 number로 명시된 필드 없음)
|
||||
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,154 @@
|
||||
"""
|
||||
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-futureoption/v1/quotations/opt-tick-ccnl"
|
||||
|
||||
def opt_tick_ccnl(
|
||||
srs_cd: str, # [필수] 종목코드
|
||||
exch_cd: str, # [필수] 거래소코드
|
||||
qry_cnt: str, # [필수] 요청개수
|
||||
start_date_time: str = "", # 조회시작일시
|
||||
close_date_time: str = "", # 조회종료일시
|
||||
qry_gap: str = "", # 묶음개수
|
||||
qry_tp: str = "", # 조회구분
|
||||
index_key: str = "", # 이전조회KEY
|
||||
tr_cont: str = "", # 연속거래여부
|
||||
dataframe1: Optional[pd.DataFrame] = None, # 누적 데이터프레임1 (output1)
|
||||
dataframe2: Optional[pd.DataFrame] = None, # 누적 데이터프레임2 (output2)
|
||||
depth: int = 0, # 내부 재귀깊이 (자동관리)
|
||||
max_depth: int = 10 # 최대 재귀 횟수 제한
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
해외옵션 체결추이(틱) API입니다.
|
||||
한 번의 호출에 40건까지 확인 가능하며, QRY_TP, INDEX_KEY 를 이용하여 다음조회 가능합니다.
|
||||
|
||||
※ 다음조회 방법
|
||||
(처음조회) "QRY_TP":"Q", "QRY_CNT":"40", "INDEX_KEY":""
|
||||
(다음조회) "QRY_TP":"P", "QRY_CNT":"40", "INDEX_KEY":"20240906 221" ◀ 이전 호출의 "output1 > index_key" 기입
|
||||
|
||||
(중요) 해외옵션시세 출력값을 해석하실 때 focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
- focode.mst(해외지수옵션 종목마스터파일), (해외주식옵션 종목마스터파일) 다운로드 방법
|
||||
1) focode.mst(해외지수옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외옵션정보.h)를 참고하여 해석
|
||||
2) fostkcode.mst(해외주식옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외주식옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외주식옵션정보.h)를 참고하여 해석
|
||||
|
||||
- 소수점 계산 시, focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)의 sCalcDesz(계산 소수점) 값 참고
|
||||
EX) focode.mst 파일의 sCalcDesz(계산 소수점) 값
|
||||
품목코드 OES 계산소수점 -2 → 시세 7525 수신 시 75.25 로 해석
|
||||
품목코드 O6E 계산소수점 -4 → 시세 54.0 수신 시 0.0054 로 해석
|
||||
|
||||
Args:
|
||||
srs_cd (str): [필수] 종목코드 (ex. OESU24 C5500)
|
||||
exch_cd (str): [필수] 거래소코드 (ex. CME)
|
||||
qry_cnt (str): [필수] 요청개수 (ex. 20)
|
||||
start_date_time (str): 조회시작일시
|
||||
close_date_time (str): 조회종료일시
|
||||
qry_gap (str): 묶음개수
|
||||
qry_tp (str): 조회구분
|
||||
index_key (str): 이전조회KEY
|
||||
tr_cont (str): 연속거래여부
|
||||
dataframe1 (Optional[pd.DataFrame]): 누적 데이터프레임1 (output1)
|
||||
dataframe2 (Optional[pd.DataFrame]): 누적 데이터프레임2 (output2)
|
||||
depth (int): 내부 재귀깊이 (자동관리)
|
||||
max_depth (int): 최대 재귀 횟수 제한
|
||||
|
||||
Returns:
|
||||
Tuple[pd.DataFrame, pd.DataFrame]: (output1 데이터, output2 데이터)
|
||||
|
||||
Raises:
|
||||
ValueError: 필수 파라미터가 누락된 경우
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = opt_tick_ccnl("OESU24 C5500", "CME", "20")
|
||||
>>> print(df1) # output1 데이터
|
||||
>>> print(df2) # output2 데이터
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if srs_cd == "":
|
||||
raise ValueError("srs_cd is required (e.g. 'OESU24 C5500')")
|
||||
|
||||
if exch_cd == "":
|
||||
raise ValueError("exch_cd is required (e.g. 'CME')")
|
||||
|
||||
if qry_cnt == "":
|
||||
raise ValueError("qry_cnt is required (e.g. '20')")
|
||||
|
||||
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 = "HHDFO55020200"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
"EXCH_CD": exch_cd,
|
||||
"QRY_CNT": qry_cnt,
|
||||
"START_DATE_TIME": start_date_time,
|
||||
"CLOSE_DATE_TIME": close_date_time,
|
||||
"QRY_GAP": qry_gap,
|
||||
"QRY_TP": qry_tp,
|
||||
"INDEX_KEY": index_key
|
||||
}
|
||||
|
||||
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
|
||||
index_key = res.getBody().output1["index_key"]
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return opt_tick_ccnl(
|
||||
srs_cd, exch_cd, qry_cnt, start_date_time, close_date_time,
|
||||
qry_gap, qry_tp, index_key, "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 opt_weekly_ccnl import opt_weekly_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 체결추이(주간) [해외선물-036]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'ret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시간',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = [
|
||||
'open_price', 'high_price', 'low_price', 'last_price', 'last_qntt', 'vol',
|
||||
'prev_diff_price', 'prev_diff_rate'
|
||||
]
|
||||
|
||||
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:
|
||||
result1, result2 = opt_weekly_ccnl(srs_cd="DXM24", exch_cd="ICE", qry_cnt="30")
|
||||
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,149 @@
|
||||
"""
|
||||
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)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 체결추이(주간) [해외선물-036]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/opt-weekly-ccnl"
|
||||
|
||||
def opt_weekly_ccnl(
|
||||
srs_cd: str, # 종목코드
|
||||
exch_cd: str, # 거래소코드
|
||||
qry_cnt: str, # 요청개수
|
||||
start_date_time: str = "", # 조회시작일시
|
||||
close_date_time: str = "", # 조회종료일시
|
||||
qry_gap: str = "", # 묶음개수
|
||||
qry_tp: str = "", # 조회구분
|
||||
index_key: str = "", # 이전조회KEY
|
||||
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입니다.
|
||||
최근 120건까지 데이터 확인이 가능합니다. (START_DATE_TIME, CLOSE_DATE_TIME은 공란 입력)
|
||||
|
||||
(중요) 해외옵션시세 출력값을 해석하실 때 focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
- focode.mst(해외지수옵션 종목마스터파일), (해외주식옵션 종목마스터파일) 다운로드 방법
|
||||
1) focode.mst(해외지수옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외옵션정보.h)를 참고하여 해석
|
||||
2) fostkcode.mst(해외주식옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외주식옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외주식옵션정보.h)를 참고하여 해석
|
||||
|
||||
- 소수점 계산 시, focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)의 sCalcDesz(계산 소수점) 값 참고
|
||||
EX) focode.mst 파일의 sCalcDesz(계산 소수점) 값
|
||||
품목코드 OES 계산소수점 -2 → 시세 7525 수신 시 75.25 로 해석
|
||||
품목코드 O6E 계산소수점 -4 → 시세 54.0 수신 시 0.0054 로 해석
|
||||
|
||||
Args:
|
||||
srs_cd (str): [필수] 종목코드 (ex. OESU24 C5500)
|
||||
exch_cd (str): [필수] 거래소코드 (ex. CME)
|
||||
qry_cnt (str): [필수] 요청개수 (ex. 20)
|
||||
start_date_time (str): 조회시작일시
|
||||
close_date_time (str): 조회종료일시
|
||||
qry_gap (str): 묶음개수
|
||||
qry_tp (str): 조회구분
|
||||
index_key (str): 이전조회KEY
|
||||
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 = opt_weekly_ccnl(srs_cd="OESU24 C5500", exch_cd="CME", qry_cnt="20")
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if srs_cd == "":
|
||||
raise ValueError("srs_cd is required (e.g. 'OESU24 C5500')")
|
||||
|
||||
if exch_cd == "":
|
||||
raise ValueError("exch_cd is required (e.g. 'CME')")
|
||||
|
||||
if qry_cnt == "":
|
||||
raise ValueError("qry_cnt is required (e.g. '20')")
|
||||
|
||||
# 재귀 깊이 제한 확인
|
||||
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 = "HHDFO55020000" # 해외옵션 체결추이(주간)
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd, # 종목코드
|
||||
"EXCH_CD": exch_cd, # 거래소코드
|
||||
"QRY_CNT": qry_cnt, # 요청개수
|
||||
"START_DATE_TIME": start_date_time, # 조회시작일시
|
||||
"CLOSE_DATE_TIME": close_date_time, # 조회종료일시
|
||||
"QRY_GAP": qry_gap, # 묶음개수
|
||||
"QRY_TP": qry_tp, # 조회구분
|
||||
"INDEX_KEY": index_key # 이전조회KEY
|
||||
}
|
||||
|
||||
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
|
||||
index_key = res.getBody().output1["index_key"]
|
||||
|
||||
if tr_cont in ["M", "F"]: # 다음 페이지 존재
|
||||
logging.info("Call Next page...")
|
||||
ka.smart_sleep() # 시스템 안정적 운영을 위한 지연
|
||||
return opt_weekly_ccnl(
|
||||
srs_cd, exch_cd, qry_cnt, start_date_time, close_date_time,
|
||||
qry_gap, qry_tp, index_key, "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,123 @@
|
||||
# -*- 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 import order
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 주문[v1_해외선물-001]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'ORD_DT': '주문일자',
|
||||
'ODNO': '주문번호'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 주문[v1_해외선물-001]
|
||||
|
||||
해외선물옵션 주문 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- ovrs_futr_fx_pdno (str): 해외선물FX상품번호 ()
|
||||
- sll_buy_dvsn_cd (str): 매도매수구분코드 (01 : 매도 02 : 매수)
|
||||
- fm_lqd_ustl_ccld_dt (str): FM청산미결제체결일자 (빈칸 (hedge청산만 이용))
|
||||
- fm_lqd_ustl_ccno (str): FM청산미결제체결번호 (빈칸 (hedge청산만 이용))
|
||||
- pric_dvsn_cd (str): 가격구분코드 (1.지정, 2. 시장, 3. STOP, 4 S/L)
|
||||
- fm_limit_ord_pric (str): FMLIMIT주문가격 (지정가인 경우 가격 입력 * 시장가, STOP주문인 경우, 빈칸("") 입력)
|
||||
- fm_stop_ord_pric (str): FMSTOP주문가격 (STOP 주문 가격 입력 * 시장가, 지정가인 경우, 빈칸("") 입력)
|
||||
- fm_ord_qty (str): FM주문수량 ()
|
||||
- fm_lqd_lmt_ord_pric (str): FM청산LIMIT주문가격 (빈칸 (hedge청산만 이용))
|
||||
- fm_lqd_stop_ord_pric (str): FM청산STOP주문가격 (빈칸 (hedge청산만 이용))
|
||||
- ccld_cndt_cd (str): 체결조건코드 (일반적으로 6 (EOD, 지정가) GTD인 경우 5, 시장가인 경우만 2)
|
||||
- cplx_ord_dvsn_cd (str): 복합주문구분코드 (0 (hedge청산만 이용))
|
||||
- ecis_rsvn_ord_yn (str): 행사예약주문여부 (N)
|
||||
- fm_hdge_ord_scrn_yn (str): FM_HEDGE주문화면여부 (N)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 주문 결과
|
||||
|
||||
Example:
|
||||
>>> df = order(cano=trenv.my_acct, acnt_prdt_cd=trenv.my_prod, ovrs_futr_fx_pdno="1AALN25 C10.0", sll_buy_dvsn_cd="02", fm_lqd_ustl_ccld_dt="", fm_lqd_ustl_ccno="", pric_dvsn_cd="1", fm_limit_ord_pric="1.17", fm_stop_ord_pric="", fm_ord_qty="1", fm_lqd_lmt_ord_pric="", fm_lqd_stop_ord_pric="", ccld_cndt_cd="6", cplx_ord_dvsn_cd="0", ecis_rsvn_ord_yn="N", fm_hdge_ord_scrn_yn="N")
|
||||
"""
|
||||
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(
|
||||
cano=trenv.my_acct,
|
||||
acnt_prdt_cd=trenv.my_prod,
|
||||
ovrs_futr_fx_pdno="6NU25",
|
||||
sll_buy_dvsn_cd="02",
|
||||
fm_lqd_ustl_ccld_dt="",
|
||||
fm_lqd_ustl_ccno="",
|
||||
pric_dvsn_cd="1",
|
||||
fm_limit_ord_pric="100",
|
||||
fm_stop_ord_pric="",
|
||||
fm_ord_qty="1",
|
||||
fm_lqd_lmt_ord_pric="",
|
||||
fm_lqd_stop_ord_pric="",
|
||||
ccld_cndt_cd="6",
|
||||
cplx_ord_dvsn_cd="0",
|
||||
ecis_rsvn_ord_yn="N",
|
||||
fm_hdge_ord_scrn_yn="N"
|
||||
)
|
||||
|
||||
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()
|
||||
159
한국투자증권(API)/examples_llm/overseas_futureoption/order/order.py
Normal file
159
한국투자증권(API)/examples_llm/overseas_futureoption/order/order.py
Normal file
@@ -0,0 +1,159 @@
|
||||
# -*- 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__)
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/order"
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 주문[v1_해외선물-001]
|
||||
##############################################################################################
|
||||
|
||||
def order(
|
||||
cano: str, # 종합계좌번호
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
ovrs_futr_fx_pdno: str, # 해외선물FX상품번호
|
||||
sll_buy_dvsn_cd: str, # 매도매수구분코드
|
||||
fm_lqd_ustl_ccld_dt: str, # FM청산미결제체결일자
|
||||
fm_lqd_ustl_ccno: str, # FM청산미결제체결번호
|
||||
pric_dvsn_cd: str, # 가격구분코드
|
||||
fm_limit_ord_pric: str, # FMLIMIT주문가격
|
||||
fm_stop_ord_pric: str, # FMSTOP주문가격
|
||||
fm_ord_qty: str, # FM주문수량
|
||||
fm_lqd_lmt_ord_pric: str, # FM청산LIMIT주문가격
|
||||
fm_lqd_stop_ord_pric: str, # FM청산STOP주문가격
|
||||
ccld_cndt_cd: str, # 체결조건코드
|
||||
cplx_ord_dvsn_cd: str, # 복합주문구분코드
|
||||
ecis_rsvn_ord_yn: str, # 행사예약주문여부
|
||||
fm_hdge_ord_scrn_yn: str, # FM_HEDGE주문화면여부
|
||||
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 주문[v1_해외선물-001]
|
||||
해외선물옵션 주문 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
ovrs_futr_fx_pdno (str): 해외선물FX상품번호
|
||||
sll_buy_dvsn_cd (str): 01 : 매도 02 : 매수
|
||||
fm_lqd_ustl_ccld_dt (str): 빈칸 (hedge청산만 이용)
|
||||
fm_lqd_ustl_ccno (str): 빈칸 (hedge청산만 이용)
|
||||
pric_dvsn_cd (str): 1.지정, 2. 시장, 3. STOP, 4 S/L
|
||||
fm_limit_ord_pric (str): 지정가인 경우 가격 입력 * 시장가, STOP주문인 경우, 빈칸("") 입력
|
||||
fm_stop_ord_pric (str): STOP 주문 가격 입력 * 시장가, 지정가인 경우, 빈칸("") 입력
|
||||
fm_ord_qty (str): FM주문수량
|
||||
fm_lqd_lmt_ord_pric (str): 빈칸 (hedge청산만 이용)
|
||||
fm_lqd_stop_ord_pric (str): 빈칸 (hedge청산만 이용)
|
||||
ccld_cndt_cd (str): 일반적으로 6 (EOD, 지정가) GTD인 경우 5, 시장가인 경우만 2
|
||||
cplx_ord_dvsn_cd (str): 0 (hedge청산만 이용)
|
||||
ecis_rsvn_ord_yn (str): N
|
||||
fm_hdge_ord_scrn_yn (str): N
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 주문 데이터
|
||||
|
||||
Example:
|
||||
>>> df = order(
|
||||
... cano=trenv.my_acct,
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... ovrs_futr_fx_pdno="6BZ22",
|
||||
... sll_buy_dvsn_cd="02",
|
||||
... fm_lqd_ustl_ccld_dt="",
|
||||
... fm_lqd_ustl_ccno="",
|
||||
... pric_dvsn_cd="1",
|
||||
... fm_limit_ord_pric="1.17",
|
||||
... fm_stop_ord_pric="",
|
||||
... fm_ord_qty="1",
|
||||
... fm_lqd_lmt_ord_pric="",
|
||||
... fm_lqd_stop_ord_pric="",
|
||||
... ccld_cndt_cd="6",
|
||||
... cplx_ord_dvsn_cd="0",
|
||||
... ecis_rsvn_ord_yn="N",
|
||||
... fm_hdge_ord_scrn_yn="N"
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '81012345')")
|
||||
raise ValueError("cano is required. (e.g. '81012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not ovrs_futr_fx_pdno:
|
||||
logger.error("ovrs_futr_fx_pdno is required. (e.g. '1AALN25 C10.0')")
|
||||
raise ValueError("ovrs_futr_fx_pdno is required. (e.g. '1AALN25 C10.0')")
|
||||
if not sll_buy_dvsn_cd:
|
||||
logger.error("sll_buy_dvsn_cd is required. (e.g. '02')")
|
||||
raise ValueError("sll_buy_dvsn_cd is required. (e.g. '02')")
|
||||
if not pric_dvsn_cd:
|
||||
logger.error("pric_dvsn_cd is required. (e.g. '1')")
|
||||
raise ValueError("pric_dvsn_cd is required. (e.g. '1')")
|
||||
if not fm_ord_qty:
|
||||
logger.error("fm_ord_qty is required. (e.g. '1')")
|
||||
raise ValueError("fm_ord_qty is required. (e.g. '1')")
|
||||
if not ccld_cndt_cd:
|
||||
logger.error("ccld_cndt_cd is required. (e.g. '6')")
|
||||
raise ValueError("ccld_cndt_cd is required. (e.g. '6')")
|
||||
|
||||
url = API_URL
|
||||
tr_id = "OTFM3001U"
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"OVRS_FUTR_FX_PDNO": ovrs_futr_fx_pdno,
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd,
|
||||
"FM_LQD_USTL_CCLD_DT": fm_lqd_ustl_ccld_dt,
|
||||
"FM_LQD_USTL_CCNO": fm_lqd_ustl_ccno,
|
||||
"PRIC_DVSN_CD": pric_dvsn_cd,
|
||||
"FM_LIMIT_ORD_PRIC": fm_limit_ord_pric,
|
||||
"FM_STOP_ORD_PRIC": fm_stop_ord_pric,
|
||||
"FM_ORD_QTY": fm_ord_qty,
|
||||
"FM_LQD_LMT_ORD_PRIC": fm_lqd_lmt_ord_pric,
|
||||
"FM_LQD_STOP_ORD_PRIC": fm_lqd_stop_ord_pric,
|
||||
"CCLD_CNDT_CD": ccld_cndt_cd,
|
||||
"CPLX_ORD_DVSN_CD": cplx_ord_dvsn_cd,
|
||||
"ECIS_RSVN_ORD_YN": ecis_rsvn_ord_yn,
|
||||
"FM_HDGE_ORD_SCRN_YN": fm_hdge_ord_scrn_yn,
|
||||
}
|
||||
|
||||
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,100 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from order_notice import order_notice
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션]실시간시세 > 해외선물옵션 실시간주문내역통보[실시간-019]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
"acct_no": "계좌번호",
|
||||
"ord_dt": "주문일자",
|
||||
"odno": "주문번호",
|
||||
"orgn_ord_dt": "원주문일자",
|
||||
"orgn_odno": "원주문번호",
|
||||
"series": "종목명",
|
||||
"rvse_cncl_dvsn_cd": "정정취소구분코드",
|
||||
"sll_buy_dvsn_cd": "매도매수구분코드",
|
||||
"cplx_ord_dvsn_cd": "복합주문구분코드",
|
||||
"prce_tp": "가격구분코드",
|
||||
"fm_excg_rcit_dvsn_cd": "FM거래소접수구분코드",
|
||||
"ord_qty": "주문수량",
|
||||
"fm_lmt_pric": "FMLIMIT가격",
|
||||
"fm_stop_ord_pric": "FMSTOP주문가격",
|
||||
"tot_ccld_qty": "총체결수량",
|
||||
"tot_ccld_uv": "총체결단가",
|
||||
"ord_remq": "잔량",
|
||||
"fm_ord_grp_dt": "FM주문그룹일자",
|
||||
"ord_grp_stno": "주문그룹번호",
|
||||
"ord_dtl_dtime": "주문상세일시",
|
||||
"oprt_dtl_dtime": "조작상세일시",
|
||||
"work_empl": "주문자",
|
||||
"crcy_cd": "통화코드",
|
||||
"lqd_yn": "청산여부(Y/N)",
|
||||
"lqd_lmt_pric": "청산LIMIT가격",
|
||||
"lqd_stop_pric": "청산STOP가격",
|
||||
"trd_cond": "체결조건코드",
|
||||
"term_ord_vald_dtime": "기간주문유효상세일시",
|
||||
"spec_tp": "계좌청산유형구분코드",
|
||||
"ecis_rsvn_ord_yn": "행사예약주문여부",
|
||||
"fuop_item_dvsn_cd": "선물옵션종목구분코드",
|
||||
"auto_ord_dvsn_cd": "자동주문 전략구분"
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = ["주문수량", "총체결수량", "총체결단가", "잔량", "청산LIMIT가격", "청산STOP가격"]
|
||||
|
||||
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")
|
||||
|
||||
# 조회 case1
|
||||
kws.subscribe(request=order_notice, data=[trenv.my_htsid])
|
||||
|
||||
# 결과 표시
|
||||
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,91 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션]실시간시세 > 해외선물옵션 실시간주문내역통보[실시간-019]
|
||||
##############################################################################################
|
||||
|
||||
def order_notice(
|
||||
tr_type: str,
|
||||
tr_key: str,
|
||||
) -> tuple[dict, list[str]]:
|
||||
"""
|
||||
[참고자료]
|
||||
|
||||
종목코드 마스터파일 파이썬 정제코드는 한국투자증권 Github 참고 부탁드립니다.
|
||||
https://github.com/koreainvestment/open-trading-api/tree/main/stocks_info
|
||||
|
||||
Args:
|
||||
tr_type (str): [필수] 등록/해제
|
||||
tr_key (str): [필수] HTS ID
|
||||
|
||||
Returns:
|
||||
message (dict): 메시지 데이터
|
||||
columns (list[str]): 컬럼 정보
|
||||
|
||||
Example:
|
||||
>>> msg, columns = order_notice("1", trenv.my_htsid)
|
||||
>>> print(msg, columns)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if tr_type == "":
|
||||
raise ValueError("tr_type is required")
|
||||
|
||||
if tr_key == "":
|
||||
raise ValueError("tr_key is required")
|
||||
|
||||
tr_id = "HDFFF1C0"
|
||||
|
||||
params = {
|
||||
"tr_key": tr_key,
|
||||
}
|
||||
|
||||
msg = ka.data_fetch(tr_id, tr_type, params)
|
||||
|
||||
columns = [
|
||||
"acct_no",
|
||||
"ord_dt",
|
||||
"odno",
|
||||
"orgn_ord_dt",
|
||||
"orgn_odno",
|
||||
"series",
|
||||
"rvse_cncl_dvsn_cd",
|
||||
"sll_buy_dvsn_cd",
|
||||
"cplx_ord_dvsn_cd",
|
||||
"prce_tp",
|
||||
"fm_excg_rcit_dvsn_cd",
|
||||
"ord_qty",
|
||||
"fm_lmt_pric",
|
||||
"fm_stop_ord_pric",
|
||||
"tot_ccld_qty",
|
||||
"tot_ccld_uv",
|
||||
"ord_remq",
|
||||
"fm_ord_grp_dt",
|
||||
"ord_grp_stno",
|
||||
"ord_dtl_dtime",
|
||||
"oprt_dtl_dtime",
|
||||
"work_empl",
|
||||
"crcy_cd",
|
||||
"lqd_yn",
|
||||
"lqd_lmt_pric",
|
||||
"lqd_stop_pric",
|
||||
"trd_cond",
|
||||
"term_ord_vald_dtime",
|
||||
"spec_tp",
|
||||
"ecis_rsvn_ord_yn",
|
||||
"fuop_item_dvsn_cd",
|
||||
"auto_ord_dvsn_cd"
|
||||
]
|
||||
|
||||
return msg, columns
|
||||
@@ -0,0 +1,113 @@
|
||||
# -*- 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 order_rvsecncl import order_rvsecncl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 정정취소주문[v1_해외선물-002, 003]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'ORD_DT': '주문일자',
|
||||
'ODNO': '주문번호'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 정정취소주문[v1_해외선물-002, 003]
|
||||
|
||||
해외선물옵션 정정취소주문 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- cano (str): 종합계좌번호 (계좌번호 체계(8-2)의 앞 8자리)
|
||||
- ord_dv (str): 주문구분 (0:정정, 1:취소)
|
||||
- acnt_prdt_cd (str): 계좌상품코드 (계좌번호 체계(8-2)의 뒤 2자리)
|
||||
- orgn_ord_dt (str): 원주문일자 (원 주문 시 출력되는 ORD_DT 값을 입력 (현지거래일))
|
||||
- orgn_odno (str): 원주문번호 (정정/취소시 주문번호(ODNO) 8자리를 문자열처럼 "0"을 포함해서 전송 (원 주문 시 출력된 ODNO 값 활용) (ex. ORGN_ODNO : 00360686))
|
||||
- fm_limit_ord_pric (str): FMLIMIT주문가격 (OTFM3002U(해외선물옵션주문정정)만 사용)
|
||||
- fm_stop_ord_pric (str): FMSTOP주문가격 (OTFM3002U(해외선물옵션주문정정)만 사용)
|
||||
- fm_lqd_lmt_ord_pric (str): FM청산LIMIT주문가격 (OTFM3002U(해외선물옵션주문정정)만 사용)
|
||||
- fm_lqd_stop_ord_pric (str): FM청산STOP주문가격 (OTFM3002U(해외선물옵션주문정정)만 사용)
|
||||
- fm_hdge_ord_scrn_yn (str): FM_HEDGE주문화면여부 (N)
|
||||
- fm_mkpr_cvsn_yn (str): FM시장가전환여부 (OTFM3003U(해외선물옵션주문취소)만 사용 ※ FM_MKPR_CVSN_YN 항목에 'Y'로 설정하여 취소주문을 접수할 경우, 주문 취소확인이 들어오면 원장에서 시장가주문을 하나 또 내줌)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물옵션 정정취소주문 결과
|
||||
|
||||
Example:
|
||||
>>> df = order_rvsecncl(cano=trenv.my_acct, ord_dv="0", acnt_prdt_cd=trenv.my_prod, orgn_ord_dt="20250630", orgn_odno="00123456", fm_limit_ord_pric="10.0", fm_stop_ord_pric="", fm_lqd_lmt_ord_pric="", fm_lqd_stop_ord_pric="", fm_hdge_ord_scrn_yn="N", fm_mkpr_cvsn_yn="") # 정정
|
||||
"""
|
||||
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_rvsecncl(
|
||||
cano=trenv.my_acct,
|
||||
ord_dv="1",
|
||||
acnt_prdt_cd=trenv.my_prod,
|
||||
orgn_ord_dt="20250703",
|
||||
orgn_odno="00000398",
|
||||
fm_limit_ord_pric="",
|
||||
fm_stop_ord_pric="",
|
||||
fm_lqd_lmt_ord_pric="",
|
||||
fm_lqd_stop_ord_pric="",
|
||||
fm_hdge_ord_scrn_yn="N",
|
||||
fm_mkpr_cvsn_yn="N"
|
||||
)
|
||||
|
||||
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,135 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-03
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/trading/order-rvsecncl"
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 정정취소주문[v1_해외선물-002, 003]
|
||||
##############################################################################################
|
||||
|
||||
def order_rvsecncl(
|
||||
cano: str, # 종합계좌번호
|
||||
ord_dv: str, # 주문구분
|
||||
acnt_prdt_cd: str, # 계좌상품코드
|
||||
orgn_ord_dt: str, # 원주문일자
|
||||
orgn_odno: str, # 원주문번호
|
||||
fm_limit_ord_pric: str, # FMLIMIT주문가격
|
||||
fm_stop_ord_pric: str, # FMSTOP주문가격
|
||||
fm_lqd_lmt_ord_pric: str, # FM청산LIMIT주문가격
|
||||
fm_lqd_stop_ord_pric: str, # FM청산STOP주문가격
|
||||
fm_hdge_ord_scrn_yn: str, # FM_HEDGE주문화면여부
|
||||
fm_mkpr_cvsn_yn: str, # FM시장가전환여부
|
||||
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 주문/계좌
|
||||
해외선물옵션 정정취소주문[v1_해외선물-002, 003]
|
||||
해외선물옵션 정정취소주문 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
cano (str): 계좌번호 체계(8-2)의 앞 8자리
|
||||
ord_dv (str): 주문구분 (0:정정, 1:취소)
|
||||
acnt_prdt_cd (str): 계좌번호 체계(8-2)의 뒤 2자리
|
||||
orgn_ord_dt (str): 원 주문 시 출력되는 ORD_DT 값을 입력 (현지거래일)
|
||||
orgn_odno (str): 정정/취소시 주문번호(ODNO) 8자리를 문자열처럼 "0"을 포함해서 전송 (원 주문 시 출력된 ODNO 값 활용) (ex. ORGN_ODNO : 00360686)
|
||||
fm_limit_ord_pric (str): OTFM3002U(해외선물옵션주문정정)만 사용
|
||||
fm_stop_ord_pric (str): OTFM3002U(해외선물옵션주문정정)만 사용
|
||||
fm_lqd_lmt_ord_pric (str): OTFM3002U(해외선물옵션주문정정)만 사용
|
||||
fm_lqd_stop_ord_pric (str): OTFM3002U(해외선물옵션주문정정)만 사용
|
||||
fm_hdge_ord_scrn_yn (str): N
|
||||
fm_mkpr_cvsn_yn (str): OTFM3003U(해외선물옵션주문취소)만 사용 ※ FM_MKPR_CVSN_YN 항목에 'Y'로 설정하여 취소주문을 접수할 경우, 주문 취소확인이 들어오면 원장에서 시장가주문을 하나 또 내줌
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물옵션 정정취소주문 데이터
|
||||
|
||||
Example:
|
||||
>>> df = order_rvsecncl(
|
||||
... cano=trenv.my_acct,
|
||||
... ord_dv="0",
|
||||
... acnt_prdt_cd=trenv.my_prod,
|
||||
... orgn_ord_dt="20250630",
|
||||
... orgn_odno="00360686",
|
||||
... fm_limit_ord_pric="",
|
||||
... fm_stop_ord_pric="",
|
||||
... fm_lqd_lmt_ord_pric="",
|
||||
... fm_lqd_stop_ord_pric="",
|
||||
... fm_hdge_ord_scrn_yn="N",
|
||||
... fm_mkpr_cvsn_yn="N"
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not cano:
|
||||
logger.error("cano is required. (e.g. '81012345')")
|
||||
raise ValueError("cano is required. (e.g. '81012345')")
|
||||
if not acnt_prdt_cd:
|
||||
logger.error("acnt_prdt_cd is required. (e.g. '08')")
|
||||
raise ValueError("acnt_prdt_cd is required. (e.g. '08')")
|
||||
if not orgn_ord_dt:
|
||||
logger.error("orgn_ord_dt is required. (e.g. '20250630')")
|
||||
raise ValueError("orgn_ord_dt is required. (e.g. '20250630')")
|
||||
if not orgn_odno:
|
||||
logger.error("orgn_odno is required. (e.g. '00360686')")
|
||||
raise ValueError("orgn_odno is required. (e.g. '00360686')")
|
||||
|
||||
if ord_dv == "0":
|
||||
tr_id = "OTFM3002U"
|
||||
elif ord_dv == "1":
|
||||
tr_id = "OTFM3003U"
|
||||
else:
|
||||
logger.error("ord_dv is required. (e.g. '0' or '1')")
|
||||
raise ValueError("ord_dv is required. (e.g. '0' or '1')")
|
||||
|
||||
params = {
|
||||
"CANO": cano,
|
||||
"ACNT_PRDT_CD": acnt_prdt_cd,
|
||||
"ORGN_ORD_DT": orgn_ord_dt,
|
||||
"ORGN_ODNO": orgn_odno,
|
||||
"FM_LIMIT_ORD_PRIC": fm_limit_ord_pric,
|
||||
"FM_STOP_ORD_PRIC": fm_stop_ord_pric,
|
||||
"FM_LQD_LMT_ORD_PRIC": fm_lqd_lmt_ord_pric,
|
||||
"FM_LQD_STOP_ORD_PRIC": fm_lqd_stop_ord_pric,
|
||||
"FM_HDGE_ORD_SCRN_YN": fm_hdge_ord_scrn_yn,
|
||||
"FM_MKPR_CVSN_YN": fm_mkpr_cvsn_yn,
|
||||
}
|
||||
|
||||
logger.info("Calling API with parameters: %s", params)
|
||||
|
||||
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,119 @@
|
||||
# -*- 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 search_contract_detail import search_contract_detail
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 상품기본정보[해외선물-023]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'exch_cd': '거래소코드',
|
||||
'clas_cd': '품목종류',
|
||||
'crc_cd': '거래통화',
|
||||
'sttl_price': '정산가',
|
||||
'sttl_date': '정산일',
|
||||
'trst_mgn': '증거금',
|
||||
'disp_digit': '가격표시진법',
|
||||
'tick_sz': '틱사이즈',
|
||||
'tick_val': '틱가치',
|
||||
'mrkt_open_date': '장개시일자',
|
||||
'mrkt_open_time': '장개시시각',
|
||||
'mrkt_close_date': '장마감일자',
|
||||
'mrkt_close_time': '장마감시각',
|
||||
'trd_fr_date': '상장일',
|
||||
'expr_date': '만기일',
|
||||
'trd_to_date': '최종거래일',
|
||||
'remn_cnt': '잔존일수',
|
||||
'stat_tp': '매매여부',
|
||||
'ctrt_size': '계약크기',
|
||||
'stl_tp': '최종결제구분',
|
||||
'frst_noti_date': '최초식별일',
|
||||
'sub_exch_nm': '서브거래소코드'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = ['정산가', '증거금', '틱사이즈', '틱가치', '잔존일수', '계약크기']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 상품기본정보[해외선물-023]
|
||||
|
||||
해외선물 상품기본정보 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- qry_cnt (str): 요청개수 (1~32 사이의 값)
|
||||
- srs_cd_01 (str): 종목코드 1 (예: BONU25)
|
||||
- srs_cd_02 (str): 종목코드 2
|
||||
- ...
|
||||
- srs_cd_32 (str): 종목코드 32 (최대 32개 종목코드까지 조회 가능)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물 상품기본정보 결과
|
||||
|
||||
Example:
|
||||
>>> df = search_contract_detail(qry_cnt="1", srs_cd_01="BONU25", srs_cd_02="BONU25", srs_cd_03="BONU25")
|
||||
"""
|
||||
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 = search_contract_detail(
|
||||
qry_cnt="3",
|
||||
srs_cd_01="BONU25",
|
||||
srs_cd_02="BONU25",
|
||||
srs_cd_03="BONU25"
|
||||
)
|
||||
|
||||
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-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__)
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/search-contract-detail"
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 상품기본정보[해외선물-023]
|
||||
##############################################################################################
|
||||
|
||||
def search_contract_detail(
|
||||
qry_cnt: str, # 요청개수
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10,
|
||||
**kwargs # srs_cd_01, srs_cd_02, ... srs_cd_32 등을 받음
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 상품기본정보[해외선물-023]
|
||||
해외선물 상품기본정보 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
qry_cnt (str): 입력한 코드 개수
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
**kwargs: srs_cd_01, srs_cd_02, ... srs_cd_32 품목종류 코드들
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물 상품기본정보 데이터
|
||||
|
||||
Example:
|
||||
>>> df = search_contract_detail(
|
||||
... qry_cnt="3",
|
||||
... srs_cd_01="SRS001",
|
||||
... srs_cd_02="SRS002",
|
||||
... srs_cd_03="SRS003"
|
||||
... )
|
||||
>>> print(df)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not 1 <= int(qry_cnt) <= 32:
|
||||
logger.error("qry_cnt is required. (e.g. '1' ~ '32')")
|
||||
raise ValueError("qry_cnt is required. (e.g. '1' ~ '32')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFC55200000"
|
||||
|
||||
# 기본 파라미터
|
||||
params = {
|
||||
"QRY_CNT": qry_cnt,
|
||||
}
|
||||
|
||||
# SRS_CD_01 ~ SRS_CD_32 파라미터 동적 생성
|
||||
for i in range(1, 33):
|
||||
srs_key = f"srs_cd_{i:02d}"
|
||||
api_key = f"SRS_CD_{i:02d}"
|
||||
params[api_key] = kwargs.get(srs_key, "")
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output2'):
|
||||
output_data = res.getBody().output2
|
||||
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 search_contract_detail(
|
||||
qry_cnt, "N", dataframe, depth + 1, max_depth, **kwargs
|
||||
)
|
||||
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,90 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
from search_opt_detail import search_opt_detail
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 상품기본정보 [해외선물-041]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'exch_cd': '거래소코드',
|
||||
'clas_cd': '품목종류',
|
||||
'crc_cd': '거래통화',
|
||||
'sttl_price': '정산가',
|
||||
'sttl_date': '정산일',
|
||||
'trst_mgn': '증거금',
|
||||
'disp_digit': '가격표시진법',
|
||||
'tick_sz': '틱사이즈',
|
||||
'tick_val': '틱가치',
|
||||
'mrkt_open_date': '장개시일자',
|
||||
'mrkt_open_time': '장개시시각',
|
||||
'mrkt_close_date': '장마감일자',
|
||||
'mrkt_close_time': '장마감시각',
|
||||
'trd_fr_date': '상장일',
|
||||
'expr_date': '만기일',
|
||||
'trd_to_date': '최종거래일',
|
||||
'remn_cnt': '잔존일수',
|
||||
'stat_tp': '매매여부',
|
||||
'ctrt_size': '계약크기',
|
||||
'stl_tp': '최종결제구분',
|
||||
'frst_noti_date': '최초식별일'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
해외옵션 상품기본정보 조회 테스트 함수
|
||||
|
||||
이 함수는 해외옵션 상품기본정보 API를 호출하여 결과를 출력합니다.
|
||||
테스트 데이터로 6AM24 종목코드를 사용합니다.
|
||||
|
||||
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 = search_opt_detail(qry_cnt="1", srs_cd_01="6AM24")
|
||||
except ValueError as e:
|
||||
logging.error("에러 발생: %s" % str(e))
|
||||
return
|
||||
|
||||
logging.info("사용 가능한 컬럼: %s", result.columns.tolist())
|
||||
|
||||
# 한글 컬럼명으로 변환
|
||||
result = result.rename(columns=COLUMN_MAPPING)
|
||||
|
||||
# 메타데이터에 자료형이 명시적으로 'number'로 선언된 필드 없음
|
||||
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,198 @@
|
||||
"""
|
||||
Created on 20250601
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.'])
|
||||
import kis_auth as ka
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 상품기본정보 [해외선물-041]
|
||||
##############################################################################################
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/search-opt-detail"
|
||||
|
||||
def search_opt_detail(
|
||||
qry_cnt: str, # [필수] 요청개수 (SRS_CD_N 개수)
|
||||
srs_cd_01: str, # [필수] 종목코드1
|
||||
srs_cd_02: Optional[str] = "", # 종목코드2
|
||||
srs_cd_03: Optional[str] = "", # 종목코드3
|
||||
srs_cd_04: Optional[str] = "", # 종목코드4
|
||||
srs_cd_05: Optional[str] = "", # 종목코드5
|
||||
srs_cd_06: Optional[str] = "", # 종목코드6
|
||||
srs_cd_07: Optional[str] = "", # 종목코드7
|
||||
srs_cd_08: Optional[str] = "", # 종목코드8
|
||||
srs_cd_09: Optional[str] = "", # 종목코드9
|
||||
srs_cd_10: Optional[str] = "", # 종목코드10
|
||||
srs_cd_11: Optional[str] = "", # 종목코드11
|
||||
srs_cd_12: Optional[str] = "", # 종목코드12
|
||||
srs_cd_13: Optional[str] = "", # 종목코드13
|
||||
srs_cd_14: Optional[str] = "", # 종목코드14
|
||||
srs_cd_15: Optional[str] = "", # 종목코드15
|
||||
srs_cd_16: Optional[str] = "", # 종목코드16
|
||||
srs_cd_17: Optional[str] = "", # 종목코드17
|
||||
srs_cd_18: Optional[str] = "", # 종목코드18
|
||||
srs_cd_19: Optional[str] = "", # 종목코드19
|
||||
srs_cd_20: Optional[str] = "", # 종목코드20
|
||||
srs_cd_21: Optional[str] = "", # 종목코드21
|
||||
srs_cd_22: Optional[str] = "", # 종목코드22
|
||||
srs_cd_23: Optional[str] = "", # 종목코드23
|
||||
srs_cd_24: Optional[str] = "", # 종목코드24
|
||||
srs_cd_25: Optional[str] = "", # 종목코드25
|
||||
srs_cd_26: Optional[str] = "", # 종목코드26
|
||||
srs_cd_27: Optional[str] = "", # 종목코드27
|
||||
srs_cd_28: Optional[str] = "", # 종목코드28
|
||||
srs_cd_29: Optional[str] = "", # 종목코드29
|
||||
srs_cd_30: Optional[str] = "" # 종목코드30
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
해외옵션 상품기본정보 API입니다.
|
||||
|
||||
(중요) 해외옵션시세 출력값을 해석하실 때 focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)에 있는 sCalcDesz(계산 소수점) 값을 활용하셔야 정확한 값을 받아오실 수 있습니다.
|
||||
|
||||
- focode.mst(해외지수옵션 종목마스터파일), (해외주식옵션 종목마스터파일) 다운로드 방법
|
||||
1) focode.mst(해외지수옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외옵션정보.h)를 참고하여 해석
|
||||
2) fostkcode.mst(해외주식옵션 종목마스터파일)
|
||||
: 포럼 > FAQ > 종목정보 다운로드(해외) - 해외주식옵션 클릭하여 다운로드 후
|
||||
Github의 헤더정보(https://github.com/koreainvestment/open-trading-api/blob/main/stocks_info/해외주식옵션정보.h)를 참고하여 해석
|
||||
|
||||
- 소수점 계산 시, focode.mst(해외지수옵션 종목마스터파일), fostkcode.mst(해외주식옵션 종목마스터파일)의 sCalcDesz(계산 소수점) 값 참고
|
||||
EX) focode.mst 파일의 sCalcDesz(계산 소수점) 값
|
||||
품목코드 OES 계산소수점 -2 → 시세 7525 수신 시 75.25 로 해석
|
||||
품목코드 O6E 계산소수점 -4 → 시세 54.0 수신 시 0.0054 로 해석
|
||||
|
||||
Args:
|
||||
qry_cnt (str): [필수] 요청개수 (ex. SRS_CD_N 개수)
|
||||
srs_cd_01 (str): [필수] 종목코드1
|
||||
srs_cd_02 (Optional[str]): 종목코드2
|
||||
srs_cd_03 (Optional[str]): 종목코드3
|
||||
srs_cd_04 (Optional[str]): 종목코드4
|
||||
srs_cd_05 (Optional[str]): 종목코드5
|
||||
srs_cd_06 (Optional[str]): 종목코드6
|
||||
srs_cd_07 (Optional[str]): 종목코드7
|
||||
srs_cd_08 (Optional[str]): 종목코드8
|
||||
srs_cd_09 (Optional[str]): 종목코드9
|
||||
srs_cd_10 (Optional[str]): 종목코드10
|
||||
srs_cd_11 (Optional[str]): 종목코드11
|
||||
srs_cd_12 (Optional[str]): 종목코드12
|
||||
srs_cd_13 (Optional[str]): 종목코드13
|
||||
srs_cd_14 (Optional[str]): 종목코드14
|
||||
srs_cd_15 (Optional[str]): 종목코드15
|
||||
srs_cd_16 (Optional[str]): 종목코드16
|
||||
srs_cd_17 (Optional[str]): 종목코드17
|
||||
srs_cd_18 (Optional[str]): 종목코드18
|
||||
srs_cd_19 (Optional[str]): 종목코드19
|
||||
srs_cd_20 (Optional[str]): 종목코드20
|
||||
srs_cd_21 (Optional[str]): 종목코드21
|
||||
srs_cd_22 (Optional[str]): 종목코드22
|
||||
srs_cd_23 (Optional[str]): 종목코드23
|
||||
srs_cd_24 (Optional[str]): 종목코드24
|
||||
srs_cd_25 (Optional[str]): 종목코드25
|
||||
srs_cd_26 (Optional[str]): 종목코드26
|
||||
srs_cd_27 (Optional[str]): 종목코드27
|
||||
srs_cd_28 (Optional[str]): 종목코드28
|
||||
srs_cd_29 (Optional[str]): 종목코드29
|
||||
srs_cd_30 (Optional[str]): 종목코드30
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: 해외옵션 상품기본정보 데이터
|
||||
|
||||
Example:
|
||||
>>> df = search_opt_detail(qry_cnt="1", srs_cd_01="6AM24")
|
||||
>>> print(df)
|
||||
"""
|
||||
|
||||
# 필수 파라미터 검증
|
||||
if qry_cnt == "":
|
||||
raise ValueError("qry_cnt is required (e.g. 'SRS_CD_N 개수')")
|
||||
|
||||
if srs_cd_01 == "":
|
||||
raise ValueError("srs_cd_01 is required")
|
||||
|
||||
tr_id = "HHDFO55200000" # 해외옵션 상품기본정보
|
||||
|
||||
params = {
|
||||
"QRY_CNT": qry_cnt,
|
||||
"SRS_CD_01": srs_cd_01
|
||||
}
|
||||
|
||||
# 옵션 파라미터 추가
|
||||
if srs_cd_02:
|
||||
params["SRS_CD_02"] = srs_cd_02
|
||||
if srs_cd_03:
|
||||
params["SRS_CD_03"] = srs_cd_03
|
||||
if srs_cd_04:
|
||||
params["SRS_CD_04"] = srs_cd_04
|
||||
if srs_cd_05:
|
||||
params["SRS_CD_05"] = srs_cd_05
|
||||
if srs_cd_06:
|
||||
params["SRS_CD_06"] = srs_cd_06
|
||||
if srs_cd_07:
|
||||
params["SRS_CD_07"] = srs_cd_07
|
||||
if srs_cd_08:
|
||||
params["SRS_CD_08"] = srs_cd_08
|
||||
if srs_cd_09:
|
||||
params["SRS_CD_09"] = srs_cd_09
|
||||
if srs_cd_10:
|
||||
params["SRS_CD_10"] = srs_cd_10
|
||||
if srs_cd_11:
|
||||
params["SRS_CD_11"] = srs_cd_11
|
||||
if srs_cd_12:
|
||||
params["SRS_CD_12"] = srs_cd_12
|
||||
if srs_cd_13:
|
||||
params["SRS_CD_13"] = srs_cd_13
|
||||
if srs_cd_14:
|
||||
params["SRS_CD_14"] = srs_cd_14
|
||||
if srs_cd_15:
|
||||
params["SRS_CD_15"] = srs_cd_15
|
||||
if srs_cd_16:
|
||||
params["SRS_CD_16"] = srs_cd_16
|
||||
if srs_cd_17:
|
||||
params["SRS_CD_17"] = srs_cd_17
|
||||
if srs_cd_18:
|
||||
params["SRS_CD_18"] = srs_cd_18
|
||||
if srs_cd_19:
|
||||
params["SRS_CD_19"] = srs_cd_19
|
||||
if srs_cd_20:
|
||||
params["SRS_CD_20"] = srs_cd_20
|
||||
if srs_cd_21:
|
||||
params["SRS_CD_21"] = srs_cd_21
|
||||
if srs_cd_22:
|
||||
params["SRS_CD_22"] = srs_cd_22
|
||||
if srs_cd_23:
|
||||
params["SRS_CD_23"] = srs_cd_23
|
||||
if srs_cd_24:
|
||||
params["SRS_CD_24"] = srs_cd_24
|
||||
if srs_cd_25:
|
||||
params["SRS_CD_25"] = srs_cd_25
|
||||
if srs_cd_26:
|
||||
params["SRS_CD_26"] = srs_cd_26
|
||||
if srs_cd_27:
|
||||
params["SRS_CD_27"] = srs_cd_27
|
||||
if srs_cd_28:
|
||||
params["SRS_CD_28"] = srs_cd_28
|
||||
if srs_cd_29:
|
||||
params["SRS_CD_29"] = srs_cd_29
|
||||
if srs_cd_30:
|
||||
params["SRS_CD_30"] = srs_cd_30
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, "", params)
|
||||
|
||||
if res.isOK():
|
||||
# 메타데이터에 따라 output2 (array)를 pd.DataFrame으로 반환
|
||||
return pd.DataFrame(res.getBody().output2)
|
||||
else:
|
||||
res.printError(url=API_URL)
|
||||
return pd.DataFrame()
|
||||
@@ -0,0 +1,111 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
|
||||
sys.path.extend(['../..', '.']) # kis_auth 파일 경로 추가
|
||||
import kis_auth as ka
|
||||
from stock_detail import stock_detail
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물종목상세[v1_해외선물-008]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'exch_cd': '거래소코드',
|
||||
'tick_sz': '틱사이즈',
|
||||
'disp_digit': '가격표시진법',
|
||||
'trst_mgn': '증거금',
|
||||
'sttl_date': '정산일',
|
||||
'prev_price': '전일종가',
|
||||
'crc_cd': '거래통화',
|
||||
'clas_cd': '품목종류',
|
||||
'tick_val': '틱가치',
|
||||
'mrkt_open_date': '장개시일자',
|
||||
'mrkt_open_time': '장개시시각',
|
||||
'mrkt_close_date': '장마감일자',
|
||||
'mrkt_close_time': '장마감시각',
|
||||
'trd_fr_date': '상장일',
|
||||
'expr_date': '만기일',
|
||||
'trd_to_date': '최종거래일',
|
||||
'remn_cnt': '잔존일수',
|
||||
'stat_tp': '매매여부',
|
||||
'ctrt_size': '계약크기',
|
||||
'stl_tp': '최종결제구분',
|
||||
'frst_noti_date': '최초식별일',
|
||||
'sprd_srs_cd1': '스프레드 종목 #1',
|
||||
'sprd_srs_cd2': '스프레드 종목 #2'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = []
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물종목상세[v1_해외선물-008]
|
||||
|
||||
해외선물종목상세 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목코드 (ex) BONU25 ※ 종목코드 "포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수선물" 참고)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물종목상세 결과
|
||||
|
||||
Example:
|
||||
>>> df = stock_detail(srs_cd="BONU25")
|
||||
"""
|
||||
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 = stock_detail(srs_cd="BONU25")
|
||||
|
||||
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,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-02
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/stock-detail"
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물종목상세[v1_해외선물-008]
|
||||
##############################################################################################
|
||||
|
||||
def stock_detail(
|
||||
srs_cd: str, # 종목코드
|
||||
tr_cont: str = "",
|
||||
dataframe: Optional[pd.DataFrame] = None,
|
||||
depth: int = 0,
|
||||
max_depth: int = 10
|
||||
) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물종목상세[v1_해외선물-008]
|
||||
해외선물종목상세 API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): ex) CNHU24 ※ 종목코드 "포럼 > FAQ > 종목정보 다운로드(해외) - 해외지수선물" 참고
|
||||
tr_cont (str): 연속 거래 여부
|
||||
dataframe (Optional[pd.DataFrame]): 누적 데이터프레임
|
||||
depth (int): 현재 재귀 깊이
|
||||
max_depth (int): 최대 재귀 깊이 (기본값: 10)
|
||||
|
||||
Returns:
|
||||
Optional[pd.DataFrame]: 해외선물종목상세 데이터
|
||||
|
||||
Example:
|
||||
>>> df = stock_detail(srs_cd="6AU22")
|
||||
>>> print(df)
|
||||
"""
|
||||
# 필수 파라미터 검증
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. '6AU22')")
|
||||
raise ValueError("srs_cd is required. (e.g. '6AU22')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFC55010100"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
}
|
||||
|
||||
res = ka._url_fetch(API_URL, tr_id, tr_cont, params)
|
||||
|
||||
if res.isOK():
|
||||
if hasattr(res.getBody(), 'output1'):
|
||||
output_data = res.getBody().output1
|
||||
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 stock_detail(
|
||||
srs_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,141 @@
|
||||
# -*- 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 tick_ccnl import tick_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(틱)[해외선물-019]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'tret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시각',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = ['자료개수', 'N틱최종개수', '시가', '고가', '저가', '체결가격', '체결수량', '누적거래수량', '전일대비가격', '전일대비율']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 체결추이(틱)[해외선물-019]
|
||||
|
||||
해외선물 체결추이(틱) 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목코드 (예) 6AM24)
|
||||
- exch_cd (str): 거래소코드 (예) CME)
|
||||
- start_date_time (str): 조회시작일시 (공백)
|
||||
- close_date_time (str): 조회종료일시 (예) 20240402)
|
||||
- qry_tp (str): 조회구분 (Q : 최초조회시 , P : 다음키(INDEX_KEY) 입력하여 조회시)
|
||||
- qry_cnt (str): 요청개수 (예) 30 (최대 40))
|
||||
- qry_gap (str): 묶음개수 (공백 (분만 사용))
|
||||
- index_key (str): 이전조회KEY (공백)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물 체결추이(틱) 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = tick_ccnl(srs_cd="BONU25", exch_cd="EUREX", start_date_time="", close_date_time="20250630", qry_tp="Q", qry_cnt="30", qry_gap="", index_key="")
|
||||
"""
|
||||
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 = tick_ccnl(
|
||||
srs_cd="BONU25",
|
||||
exch_cd="EUREX",
|
||||
start_date_time="",
|
||||
close_date_time="20250630",
|
||||
qry_tp="Q",
|
||||
qry_cnt="30",
|
||||
qry_gap="",
|
||||
index_key=""
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
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,180 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/tick-ccnl"
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(틱)[해외선물-019]
|
||||
##############################################################################################
|
||||
|
||||
def tick_ccnl(
|
||||
srs_cd: str, # 종목코드
|
||||
exch_cd: str, # 거래소코드
|
||||
start_date_time: str, # 조회시작일시
|
||||
close_date_time: str, # 조회종료일시
|
||||
qry_tp: str, # 조회구분
|
||||
qry_cnt: str, # 요청개수
|
||||
qry_gap: str, # 묶음개수
|
||||
index_key: str, # 이전조회KEY
|
||||
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]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 체결추이(틱)[해외선물-019]
|
||||
해외선물 체결추이(틱) API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): 종목코드 (예: '6AM24')
|
||||
exch_cd (str): 거래소코드 (예: 'CME')
|
||||
start_date_time (str): 조회시작일시 (공백 허용)
|
||||
close_date_time (str): 조회종료일시 (예: '20240402')
|
||||
qry_tp (str): 조회구분 ('Q': 최초조회, 'P': 다음키 입력하여 조회)
|
||||
qry_cnt (str): 요청개수 (예: '30', 최대 '40')
|
||||
qry_gap (str): 묶음개수 (공백 허용)
|
||||
index_key (str): 이전조회KEY (공백 허용)
|
||||
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 = tick_ccnl(
|
||||
... srs_cd="BONU25",
|
||||
... exch_cd="EUREX",
|
||||
... start_date_time="",
|
||||
... close_date_time="20250630",
|
||||
... qry_tp="Q",
|
||||
... qry_cnt="30",
|
||||
... qry_gap="",
|
||||
... index_key=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. '6AM24')")
|
||||
raise ValueError("srs_cd is required. (e.g. '6AM24')")
|
||||
if not exch_cd:
|
||||
logger.error("exch_cd is required. (e.g. 'CME')")
|
||||
raise ValueError("exch_cd is required. (e.g. 'CME')")
|
||||
if not close_date_time:
|
||||
logger.error("close_date_time is required. (e.g. '20240402')")
|
||||
raise ValueError("close_date_time is required. (e.g. '20240402')")
|
||||
if not qry_tp:
|
||||
logger.error("qry_tp is required. ('Q' or 'P')")
|
||||
raise ValueError("qry_tp is required. ('Q' or 'P')")
|
||||
if not qry_cnt:
|
||||
logger.error("qry_cnt is required. (e.g. '30')")
|
||||
raise ValueError("qry_cnt is required. (e.g. '30')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFC55020200"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
"EXCH_CD": exch_cd,
|
||||
"START_DATE_TIME": start_date_time,
|
||||
"CLOSE_DATE_TIME": close_date_time,
|
||||
"QRY_TP": qry_tp,
|
||||
"QRY_CNT": qry_cnt,
|
||||
"QRY_GAP": qry_gap,
|
||||
"INDEX_KEY": index_key,
|
||||
}
|
||||
|
||||
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 tick_ccnl(
|
||||
srs_cd,
|
||||
exch_cd,
|
||||
start_date_time,
|
||||
close_date_time,
|
||||
qry_tp,
|
||||
qry_cnt,
|
||||
qry_gap,
|
||||
index_key,
|
||||
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,141 @@
|
||||
# -*- 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 weekly_ccnl import weekly_ccnl
|
||||
|
||||
# 로깅 설정
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(주간)[해외선물-017]
|
||||
##############################################################################################
|
||||
|
||||
# 컬럼명 매핑
|
||||
COLUMN_MAPPING = {
|
||||
'ret_cnt': '자료개수',
|
||||
'last_n_cnt': 'N틱최종개수',
|
||||
'index_key': '이전조회KEY',
|
||||
'data_date': '일자',
|
||||
'data_time': '시각',
|
||||
'open_price': '시가',
|
||||
'high_price': '고가',
|
||||
'low_price': '저가',
|
||||
'last_price': '체결가격',
|
||||
'last_qntt': '체결수량',
|
||||
'vol': '누적거래수량',
|
||||
'prev_diff_flag': '전일대비구분',
|
||||
'prev_diff_price': '전일대비가격',
|
||||
'prev_diff_rate': '전일대비율'
|
||||
}
|
||||
|
||||
# 숫자형 컬럼
|
||||
NUMERIC_COLUMNS = ['자료개수', 'N틱최종개수', '시가', '고가', '저가', '체결가격', '체결수량', '누적거래수량', '전일대비가격', '전일대비율']
|
||||
|
||||
def main():
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 체결추이(주간)[해외선물-017]
|
||||
|
||||
해외선물 체결추이(주간) 테스트 함수
|
||||
|
||||
Parameters:
|
||||
- srs_cd (str): 종목코드 (예) 6AM24)
|
||||
- exch_cd (str): 거래소코드 (예) CME)
|
||||
- start_date_time (str): 조회시작일시 (공백)
|
||||
- close_date_time (str): 조회종료일시 (예) 20240402)
|
||||
- qry_tp (str): 조회구분 (Q : 최초조회시 , P : 다음키(INDEX_KEY) 입력하여 조회시)
|
||||
- qry_cnt (str): 요청개수 (예) 30 (최대 40))
|
||||
- qry_gap (str): 묶음개수 (공백 (분만 사용))
|
||||
- index_key (str): 이전조회KEY (공백)
|
||||
|
||||
Returns:
|
||||
- DataFrame: 해외선물 체결추이(주간) 결과
|
||||
|
||||
Example:
|
||||
>>> df1, df2 = weekly_ccnl(srs_cd="6AM24", exch_cd="CME", start_date_time="", close_date_time="20240402", qry_tp="Q", qry_cnt="30", qry_gap="", index_key="")
|
||||
"""
|
||||
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 = weekly_ccnl(
|
||||
srs_cd="DXM24",
|
||||
exch_cd="ICE",
|
||||
start_date_time="",
|
||||
close_date_time="20250630",
|
||||
qry_tp="",
|
||||
qry_cnt="30",
|
||||
qry_gap="",
|
||||
index_key=""
|
||||
)
|
||||
|
||||
# 결과 확인
|
||||
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,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on 2025-07-01
|
||||
|
||||
"""
|
||||
|
||||
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__)
|
||||
|
||||
# 상수 정의
|
||||
API_URL = "/uapi/overseas-futureoption/v1/quotations/weekly-ccnl"
|
||||
|
||||
##############################################################################################
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(주간)[해외선물-017]
|
||||
##############################################################################################
|
||||
|
||||
def weekly_ccnl(
|
||||
srs_cd: str, # 종목코드
|
||||
exch_cd: str, # 거래소코드
|
||||
start_date_time: str, # 조회시작일시
|
||||
close_date_time: str, # 조회종료일시
|
||||
qry_tp: str, # 조회구분
|
||||
qry_cnt: str, # 요청개수
|
||||
qry_gap: str, # 묶음개수
|
||||
index_key: str, # 이전조회KEY
|
||||
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]:
|
||||
"""
|
||||
[해외선물옵션] 기본시세
|
||||
해외선물 체결추이(주간)[해외선물-017]
|
||||
해외선물 체결추이(주간) API를 호출하여 DataFrame으로 반환합니다.
|
||||
|
||||
Args:
|
||||
srs_cd (str): 종목코드, 예) 6AM24
|
||||
exch_cd (str): 거래소코드, 예) CME
|
||||
start_date_time (str): 조회시작일시, 공백
|
||||
close_date_time (str): 조회종료일시, 예) 20240402
|
||||
qry_tp (str): 조회구분, Q : 최초조회시 , P : 다음키(INDEX_KEY) 입력하여 조회시
|
||||
qry_cnt (str): 요청개수, 예) 30 (최대 40)
|
||||
qry_gap (str): 묶음개수, 공백 (분만 사용)
|
||||
index_key (str): 이전조회KEY, 공백
|
||||
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 = weekly_ccnl(
|
||||
... srs_cd="6AM24",
|
||||
... exch_cd="CME",
|
||||
... start_date_time="",
|
||||
... close_date_time="20240402",
|
||||
... qry_tp="Q",
|
||||
... qry_cnt="30",
|
||||
... qry_gap="",
|
||||
... index_key=""
|
||||
... )
|
||||
>>> print(df1)
|
||||
>>> print(df2)
|
||||
"""
|
||||
# [필수 파라미터 검증]
|
||||
if not srs_cd:
|
||||
logger.error("srs_cd is required. (e.g. '6AM24')")
|
||||
raise ValueError("srs_cd is required. (e.g. '6AM24')")
|
||||
if not exch_cd:
|
||||
logger.error("exch_cd is required. (e.g. 'CME')")
|
||||
raise ValueError("exch_cd is required. (e.g. 'CME')")
|
||||
if not close_date_time:
|
||||
logger.error("close_date_time is required. (e.g. '20240402')")
|
||||
raise ValueError("close_date_time is required. (e.g. '20240402')")
|
||||
if not qry_cnt:
|
||||
logger.error("qry_cnt is required. (e.g. '30')")
|
||||
raise ValueError("qry_cnt is required. (e.g. '30')")
|
||||
|
||||
# 최대 재귀 깊이 체크
|
||||
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 = "HHDFC55020000"
|
||||
|
||||
params = {
|
||||
"SRS_CD": srs_cd,
|
||||
"EXCH_CD": exch_cd,
|
||||
"START_DATE_TIME": start_date_time,
|
||||
"CLOSE_DATE_TIME": close_date_time,
|
||||
"QRY_TP": qry_tp,
|
||||
"QRY_CNT": qry_cnt,
|
||||
"QRY_GAP": qry_gap,
|
||||
"INDEX_KEY": index_key,
|
||||
}
|
||||
|
||||
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 weekly_ccnl(
|
||||
srs_cd,
|
||||
exch_cd,
|
||||
start_date_time,
|
||||
close_date_time,
|
||||
qry_tp,
|
||||
qry_cnt,
|
||||
qry_gap,
|
||||
index_key,
|
||||
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()
|
||||
Reference in New Issue
Block a user