Source code for pyqqq.brokerage.kis.domestic_futureoption

import datetime as dtm
from decimal import Decimal

from pyqqq.brokerage.kis.oauth import KISAuth
from pyqqq.brokerage.kis.tr_client import KISTRClient

BALANCE_STR_KEYS = ["cano", "acnt_prdt_cd", "pdno", "prdt_type_cd", "shtn_pdno", "prdt_name", "sll_buy_dvsn_name"]
BALANCE_DECIMAL_KEYS = ["excc_unpr", "ccld_avg_unpr1", "idx_clpr"]
SETTLEMENT_STR_KEYS = ["pdno", "prdt_name", "trad_dvsn_name"]
DAILY_AMOUNT_FEE_STR_KEYS = ["pdno", "item_name"]
DAILY_AMOUNT_FEE_DATE_KEYS = ["ord_dt"]
CCNL_STR_KEYS = [
    "ord_gno_brno",
    "cano",
    "csac_name",
    "acnt_prdt_cd",
    "odno",
    "orgn_odno",
    "sll_buy_dvsn_cd",
    "trad_dvsn_name",
    "nmpr_type_cd",
    "nmpr_type_name",
    "pdno",
    "prdt_name",
    "prdt_type_cd",
    "ingr_trad_rjct_rson_cd",
    "ingr_trad_rjct_rson_name",
    "ord_stfno",
    "sprd_item_yn",
    "ord_ip_addr",
    "ctac_tlno",
]
CCNL_DECIMAL_KEYS = ["ord_idx", "avg_idx"]
NGT_CCNL_DECIMAL_KEYS = ["ord_idx4", "avg_idx"]
CCNL_DATE_KEYS = ["ord_dt"]
CCNL_TIME_KEYS = ["ord_tmd"]
DEPOSIT_DECIMAL_KEYS = ["mtnc_rt"]

MARGIN_DIVISION_VALUES = ["01", "02"]
SETTLEMENT_STATUS_VALUES = ["1", "2"]
SELL_BUY_DIVISION_VALUES = ["00", "01", "02"]
FILLED_UNFILLED_DIVISION_VALUES = ["00", "01", "02"]
SORT_SEQUENCE_VALUES = ["AS", "DS"]
NGT_SORT_SEQUENCE_VALUES = ["", "AS", "DS"]
FUOP_DIVISION_VALUES = ["", "01", "02"]
NGT_SCREEN_DIVISION_DEFAULT = "02"


def _convert_fields(record: dict, str_keys: list, decimal_keys: list = (), date_keys: list = (), time_keys: list = ()) -> dict:
    """KIS 응답 레코드의 문자열 값을 타입에 맞게 변환한다. 변환 불가한 값은 문자열로 남긴다."""
    for key, value in record.items():
        if key in str_keys or len(value) == 0:
            continue
        if key in decimal_keys:
            record[key] = Decimal(value)
            continue
        if key in date_keys:
            record[key] = dtm.datetime.strptime(value, "%Y%m%d").date()
            continue
        if key in time_keys:
            record[key] = dtm.datetime.strptime(value, "%H%M%S").time()
            continue
        try:
            record[key] = int(value)
        except ValueError:
            pass

    return record


[docs] class KISDomesticFutureOption: """ 한국투자증권 국내선물옵션 API (시세 + 주문/계좌) """
[docs] def __init__(self, auth: KISAuth, corp_data: dict = None): self.auth = auth self.corp_data = corp_data self.tr_client = KISTRClient(auth, corp_data)
def _tr_request(self, *args, **kwargs): return self.tr_client.request(*args, **kwargs)
[docs] def inquire_time_fuopchartprice( self, fid_input_iscd: str, fid_input_date_1: dtm.date, fid_input_hour_1: dtm.time | str, fid_cond_mrkt_div_code: str = "F", fid_hour_cls_code: str = "60", fid_pw_data_incu_yn: str = "Y", fid_fake_tick_incu_yn: str = "N", ): """ (국내선물옵션시세) 선물옵션 분봉조회[v1_국내선물-012] 선물옵션 분봉조회 API입니다. 실전계좌의 경우, 한 번의 호출에 최대 102건까지 확인 가능하며, FID_INPUT_DATE_1(입력날짜), FID_INPUT_HOUR_1(입력시간)을 이용하여 다음조회 가능합니다. tr_cont를 이용한 다음조회는 불가능한 API입니다. CM(야간선물), EU(야간옵션)의 경우 자정 이후 시간은 +24시간으로 입력/표시됩니다. (ex. "253000" = 새벽 1시 30분) 자정 이후 봉은 stck_bsop_date(영업일자)와 실제 달력상 일자가 다릅니다. Args: fid_input_iscd (str): 종목번호 (지수선물 6자리, 지수옵션 9자리. ex. A01609 - KOSPI200 선물 2026년 9월물) fid_input_date_1 (dtm.date): 조회 기준일자 - 입력 날짜 기준으로 이전 기간 역순 조회 fid_input_hour_1 (dtm.time | str): 조회 기준시간 - 입력 시간 기준으로 역순 조회. 야간시장(CM/EU)의 자정 이후 시간은 +24시간 HHMMSS 문자열로 입력 (ex. "253000") fid_cond_mrkt_div_code (str): FID 조건시장분류코드 - F:지수선물 O:지수옵션 JF:주식선물 JO:주식옵션 CF:상품선물(금),금리선물(국채),통화선물(달러) CM:야간선물 EU:야간옵션 fid_hour_cls_code (str): FID 시간구분코드 - 30:30초 60:1분 3600:1시간 fid_pw_data_incu_yn (str): 과거 데이터 포함 여부 - Y:과거 N:당일 fid_fake_tick_incu_yn (str): 허봉 포함 여부 - N으로 입력 Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - output1 (dict): 응답상세1 - futs_prdy_vrss: 선물 전일 대비 - prdy_vrss_sign: 전일 대비 부호 - 1:상한 2:상승 3:보합 4:하한 5:하락 - futs_prdy_ctrt: 선물 전일 대비율 - futs_prdy_clpr: 선물 전일 종가 - prdy_nmix: 전일 지수 - acml_vol: 누적 거래량 - acml_tr_pbmn: 누적 거래 대금 - hts_kor_isnm: HTS 한글 종목명 - futs_prpr: 선물 현재가 - futs_shrn_iscd: 선물 단축 종목코드 - prdy_vol: 전일 거래량 - futs_mxpr: 선물 상한가 - futs_llam: 선물 하한가 - futs_oprc: 선물 시가2 - futs_hgpr: 선물 최고가 - futs_lwpr: 선물 최저가 - futs_prdy_oprc: 선물 전일 시가 - futs_prdy_hgpr: 선물 전일 최고가 - futs_prdy_lwpr: 선물 전일 최저가 - futs_askp: 선물 매도호가 - futs_bidp: 선물 매수호가 - basis: 베이시스 - kospi200_nmix: KOSPI200 지수 - kospi200_prdy_vrss: KOSPI200 전일 대비 - kospi200_prdy_ctrt: KOSPI200 전일 대비율 - kospi200_prdy_vrss_sign: KOSPI200 전일 대비 부호 - hts_otst_stpl_qty: HTS 미결제 약정 수량 - otst_stpl_qty_icdc: 미결제 약정 수량 증감 - tday_rltv: 당일 체결강도 - hts_thpr: HTS 이론가 - dprt: 괴리율 - output2 (list): 응답상세2 - stck_bsop_date: 주식 영업 일자 - stck_cntg_hour: 주식 체결 시간 - 야간시장(CM/EU)의 자정 이후 시간은 +24시간에서 24시간을 뺀 시간으로 변환됨 - futs_prpr: 선물 현재가 - futs_oprc: 선물 시가2 - futs_hgpr: 선물 최고가 - futs_lwpr: 선물 최저가 - cntg_vol: 체결 거래량 - acml_tr_pbmn: 누적 거래 대금 Raise: ValueError: API 에러 발생시 """ assert not self.auth.paper_trading, "실전계좌에서만 사용 가능한 API입니다." assert fid_cond_mrkt_div_code in ["F", "O", "JF", "JO", "CF", "CM", "EU"], 'fid_cond_mrkt_div_code must be one of "F", "O", "JF", "JO", "CF", "CM", or "EU"' assert fid_hour_cls_code in ["30", "60", "3600"], 'fid_hour_cls_code must be one of "30", "60", or "3600"' assert fid_pw_data_incu_yn in ["Y", "N"], 'fid_pw_data_incu_yn must be "Y" or "N"' assert fid_fake_tick_incu_yn in ["Y", "N"], 'fid_fake_tick_incu_yn must be "Y" or "N"' url_path = "/uapi/domestic-futureoption/v1/quotations/inquire-time-fuopchartprice" tr_id = "FHKIF03020200" params = { "FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code, "FID_INPUT_ISCD": fid_input_iscd, "FID_HOUR_CLS_CODE": fid_hour_cls_code, "FID_PW_DATA_INCU_YN": fid_pw_data_incu_yn, "FID_FAKE_TICK_INCU_YN": fid_fake_tick_incu_yn, "FID_INPUT_DATE_1": fid_input_date_1.strftime("%Y%m%d"), "FID_INPUT_HOUR_1": fid_input_hour_1.strftime("%H%M%S") if isinstance(fid_input_hour_1, dtm.time) else fid_input_hour_1, } res_body, res_headers = self._tr_request(url_path, tr_id, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") output1 = res_body["output1"] for k in output1.keys(): if k in ["hts_kor_isnm", "futs_shrn_iscd"]: pass elif k in ["prdy_vrss_sign", "kospi200_prdy_vrss_sign", "acml_vol", "prdy_vol", "acml_tr_pbmn", "hts_otst_stpl_qty", "otst_stpl_qty_icdc"]: output1[k] = int(output1[k]) else: output1[k] = Decimal(output1[k]) output2 = [] for el in res_body["output2"]: for k in el.keys(): if k == "stck_bsop_date": el[k] = dtm.datetime.strptime(el[k], "%Y%m%d").date() elif k == "stck_cntg_hour": hour = int(el[k][:2]) if hour >= 24: el[k] = dtm.time(hour - 24, int(el[k][2:4]), int(el[k][4:6])) else: el[k] = dtm.datetime.strptime(el[k], "%H%M%S").time() elif k in ["cntg_vol", "acml_tr_pbmn"]: el[k] = int(el[k]) else: el[k] = Decimal(el[k]) output2.append(el) result = { "rt_cd": res_body["rt_cd"], "msg_cd": res_body["msg_cd"], "msg1": res_body["msg1"], "tr_cont": res_headers["tr_cont"], "output1": output1, "output2": output2, } return result
[docs] def inquire_daily_fuopchartprice( self, fid_input_iscd: str, fid_input_date_1: dtm.date, fid_input_date_2: dtm.date, fid_period_div_code: str = "D", fid_cond_mrkt_div_code: str = "F", ): """ (국내선물옵션시세) 선물옵션 기간별시세(일/주/월/년)[v1_국내선물-008] (지수)선물옵션 기간별시세 데이터(일/주/월/년) 조회 API입니다. 한 번의 호출에 최대 100건까지 확인 가능하며, 그 이상의 기간은 조회 기간을 나누어 요청해야 합니다. Args: fid_input_iscd (str): 종목번호 (지수선물 6자리, 지수옵션 9자리. ex. A01609 - KOSPI200 선물 2026년 9월물) fid_input_date_1 (dtm.date): 조회 시작일자 fid_input_date_2 (dtm.date): 조회 종료일자 fid_period_div_code (str): FID 기간분류코드 - D:일봉 W:주봉 M:월봉 Y:년봉 fid_cond_mrkt_div_code (str): FID 조건시장분류코드 - F:지수선물 O:지수옵션 CM:야간선물 EU:야간옵션 (CM/EU는 공식 문서에 없으나 실측 확인됨. 야간세션 봉의 stck_bsop_date는 거래일 기준) Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - output1 (dict): 응답상세1 - futs_prdy_vrss: 선물 전일 대비 - prdy_vrss_sign: 전일 대비 부호 - 1:상한 2:상승 3:보합 4:하한 5:하락 - futs_prdy_ctrt: 선물 전일 대비율 - futs_prdy_clpr: 선물 전일 종가 - acml_vol: 누적 거래량 - acml_tr_pbmn: 누적 거래 대금 - hts_kor_isnm: HTS 한글 종목명 - futs_prpr: 선물 현재가 - futs_shrn_iscd: 선물 단축 종목코드 - prdy_vol: 전일 거래량 - futs_mxpr: 선물 상한가 - futs_llam: 선물 하한가 - futs_oprc: 선물 시가2 - futs_hgpr: 선물 최고가 - futs_lwpr: 선물 최저가 - futs_prdy_oprc: 선물 전일 시가 - futs_prdy_hgpr: 선물 전일 최고가 - futs_prdy_lwpr: 선물 전일 최저가 - futs_askp: 선물 매도호가 - futs_bidp: 선물 매수호가 - basis: 베이시스 - kospi200_nmix: KOSPI200 지수 - kospi200_prdy_vrss: KOSPI200 전일 대비 - kospi200_prdy_ctrt: KOSPI200 전일 대비율 - kospi200_prdy_vrss_sign: KOSPI200 전일 대비 부호 - hts_otst_stpl_qty: HTS 미결제 약정 수량 - otst_stpl_qty_icdc: 미결제 약정 수량 증감 - tday_rltv: 당일 체결강도 - hts_thpr: HTS 이론가 - dprt: 괴리율 - output2 (list): 응답상세2 - stck_bsop_date: 주식 영업 일자 - futs_prpr: 선물 현재가 (해당 기간 종가) - futs_oprc: 선물 시가2 - futs_hgpr: 선물 최고가 - futs_lwpr: 선물 최저가 - acml_vol: 누적 거래량 - acml_tr_pbmn: 누적 거래 대금 - mod_yn: 변경 여부 Raise: ValueError: API 에러 발생시 """ assert fid_period_div_code in ["D", "W", "M", "Y"], 'fid_period_div_code must be one of "D", "W", "M", or "Y"' assert fid_cond_mrkt_div_code in ["F", "O", "CM", "EU"], 'fid_cond_mrkt_div_code must be one of "F", "O", "CM", or "EU"' url_path = "/uapi/domestic-futureoption/v1/quotations/inquire-daily-fuopchartprice" tr_id = "FHKIF03020100" params = { "FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code, "FID_INPUT_ISCD": fid_input_iscd, "FID_INPUT_DATE_1": fid_input_date_1.strftime("%Y%m%d"), "FID_INPUT_DATE_2": fid_input_date_2.strftime("%Y%m%d"), "FID_PERIOD_DIV_CODE": fid_period_div_code, } res_body, res_headers = self._tr_request(url_path, tr_id, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") str_keys = ["hts_kor_isnm", "futs_shrn_iscd"] int_keys = ["prdy_vrss_sign", "kospi200_prdy_vrss_sign", "acml_vol", "prdy_vol", "acml_tr_pbmn", "hts_otst_stpl_qty", "otst_stpl_qty_icdc"] output1 = res_body["output1"] for k in output1.keys(): if k in str_keys or len(output1[k]) == 0: pass elif k in int_keys: output1[k] = int(output1[k]) else: output1[k] = Decimal(output1[k]) output2 = [] for el in res_body["output2"]: if not el: continue for k in el.keys(): if k == "stck_bsop_date": el[k] = dtm.datetime.strptime(el[k], "%Y%m%d").date() elif k == "mod_yn" or len(el[k]) == 0: pass elif k in ["acml_vol", "acml_tr_pbmn"]: el[k] = int(el[k]) else: el[k] = Decimal(el[k]) output2.append(el) result = { "rt_cd": res_body["rt_cd"], "msg_cd": res_body["msg_cd"], "msg1": res_body["msg1"], "tr_cont": res_headers["tr_cont"], "output1": output1, "output2": output2, } return result
def inquire_balance( self, cano: str, acnt_prdt_cd: str, mgna_dvsn: str = "01", excc_stat_cd: str = "1", ctx_area_fk200: str = "", ctx_area_nk200: str = "", tr_cont: str = "", ): """ (국내선물옵션주문/계좌) 선물옵션 잔고현황[v1_국내선물-004] Args: cano (str): 종합계좌번호 (8자리) acnt_prdt_cd (str): 계좌상품코드 (선물옵션 계좌는 "03") mgna_dvsn (str): 증거금 구분 - 01:개시 02:유지 excc_stat_cd (str): 정산상태코드 - 1:정산 2:본정산 ctx_area_fk200 (str): 연속조회검색조건200 ctx_area_nk200 (str): 연속조회키200 tr_cont (str): 연속거래여부 - 공백:최초조회 N:다음조회 Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - F/M:다음 데이터 있음 D/E:마지막 - ctx_area_fk200 (str): 연속조회검색조건200 - ctx_area_nk200 (str): 연속조회키200 - output1 (list): 종목별 잔고 - cano: 종합계좌번호 - acnt_prdt_cd: 계좌상품코드 - pdno: 상품번호 - prdt_type_cd: 상품유형코드 - shtn_pdno: 단축상품번호 - prdt_name: 상품명 - sll_buy_dvsn_name: 매도매수구분명 - cblc_qty: 잔고수량 - excc_unpr: 정산단가 (Decimal) - ccld_avg_unpr1: 체결평균단가1 (Decimal) - idx_clpr: 지수종가 (Decimal) - pchs_amt: 매입금액 - evlu_amt: 평가금액 - evlu_pfls_amt: 평가손익금액 - trad_pfls_amt: 매매손익금액 - lqd_psbl_qty: 청산가능수량 - output2 (dict): 계좌 합계 - dnca_cash: 예수금현금 - frcr_dncl_amt: 외화예수금액 - dnca_sbst: 예수금대용 - tot_dncl_amt: 총예수금액 - tot_ccld_amt: 총체결금액 - cash_mgna: 현금증거금 - sbst_mgna: 대용증거금 - mgna_tota: 증거금총액 - opt_dfpa: 옵션차금 - thdt_dfpa: 당일차금 - rnwl_dfpa: 갱신차금 - fee: 수수료 - nxdy_dnca: 익일예수금 - nxdy_dncl_amt: 익일예수금액 - prsm_dpast: 추정예탁자산 - prsm_dpast_amt: 추정예탁자산금액 - pprt_ord_psbl_cash: 적정주문가능현금 - add_mgna_cash: 추가증거금현금 - add_mgna_tota: 추가증거금총액 - futr_trad_pfls_amt: 선물매매손익금액 - opt_trad_pfls_amt: 옵션매매손익금액 - futr_evlu_pfls_amt: 선물평가손익금액 - opt_evlu_pfls_amt: 옵션평가손익금액 - trad_pfls_amt_smtl: 매매손익금액합계 - evlu_pfls_amt_smtl: 평가손익금액합계 - wdrw_psbl_tot_amt: 인출가능총금액 - ord_psbl_cash: 주문가능현금 - ord_psbl_sbst: 주문가능대용 - ord_psbl_tota: 주문가능총액 - pchs_amt_smtl: 매입금액합계 - evlu_amt_smtl: 평가금액합계 Raise: ValueError: API 에러 발생시 """ assert mgna_dvsn in MARGIN_DIVISION_VALUES, f"mgna_dvsn must be one of {MARGIN_DIVISION_VALUES}" assert excc_stat_cd in SETTLEMENT_STATUS_VALUES, f"excc_stat_cd must be one of {SETTLEMENT_STATUS_VALUES}" url_path = "/uapi/domestic-futureoption/v1/trading/inquire-balance" tr_id = "VTFO6118R" if self.auth.paper_trading else "CTFO6118R" params = { "CANO": cano, "ACNT_PRDT_CD": acnt_prdt_cd, "MGNA_DVSN": mgna_dvsn, "EXCC_STAT_CD": excc_stat_cd, "CTX_AREA_FK200": ctx_area_fk200, "CTX_AREA_NK200": ctx_area_nk200, } res_body, res_headers = self._tr_request(url_path, tr_id, tr_cont, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") output1 = [_convert_fields(el, BALANCE_STR_KEYS, BALANCE_DECIMAL_KEYS) for el in res_body["output1"]] output2 = _convert_fields(res_body["output2"], BALANCE_STR_KEYS) return self._build_result(res_body, res_headers, output1, output2) def inquire_balance_valuation_pl( self, cano: str, acnt_prdt_cd: str, mgna_dvsn: str = "01", excc_stat_cd: str = "1", ctx_area_fk200: str = "", ctx_area_nk200: str = "", tr_cont: str = "", ): """ (국내선물옵션주문/계좌) 선물옵션 잔고평가손익내역[v1_국내선물-015] 보유 종목별 평가금액/평가손익/매매손익과 계좌 합계를 조회한다. 실전계좌 전용. Args: cano (str): 종합계좌번호 (8자리) acnt_prdt_cd (str): 계좌상품코드 (선물옵션 계좌는 "03") mgna_dvsn (str): 증거금 구분 - 01:개시 02:유지 excc_stat_cd (str): 정산상태코드 - 1:정산 2:본정산 ctx_area_fk200 (str): 연속조회검색조건200 ctx_area_nk200 (str): 연속조회키200 tr_cont (str): 연속거래여부 - 공백:최초조회 N:다음조회 Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - F/M:다음 데이터 있음 D/E:마지막 - ctx_area_fk200 (str): 연속조회검색조건200 - ctx_area_nk200 (str): 연속조회키200 - output1 (list): 종목별 잔고 평가손익 - cano: 종합계좌번호 - acnt_prdt_cd: 계좌상품코드 - pdno: 상품번호 - prdt_type_cd: 상품유형코드 - shtn_pdno: 단축상품번호 - prdt_name: 상품명 - sll_buy_dvsn_name: 매도매수구분명 - cblc_qty1: 잔고수량1 - excc_unpr: 정산단가 (Decimal) - ccld_avg_unpr1: 체결평균단가1 (Decimal) - idx_clpr: 지수종가 (Decimal) - pchs_amt: 매입금액 - evlu_amt: 평가금액 - evlu_pfls_amt: 평가손익금액 - trad_pfls_amt: 매매손익금액 - lqd_psbl_qty: 청산가능수량 - output2 (dict): 계좌 합계 - dnca_cash: 예수금현금 - frcr_dncl_amt: 외화예수금액 - dnca_sbst: 예수금대용 - tot_dncl_amt: 총예수금액 - tot_ccld_amt: 총체결금액 - cash_mgna: 현금증거금 - sbst_mgna: 대용증거금 - mgna_tota: 증거금총액 - opt_dfpa: 옵션차금 - thdt_dfpa: 당일차금 - rnwl_dfpa: 갱신차금 - fee: 수수료 - nxdy_dnca: 익일예수금 - nxdy_dncl_amt: 익일예수금액 - prsm_dpast: 추정예탁자산 - prsm_dpast_amt: 추정예탁자산금액 - pprt_ord_psbl_cash: 적정주문가능현금 - add_mgna_cash: 추가증거금현금 - add_mgna_tota: 추가증거금총액 - futr_trad_pfls_amt: 선물매매손익금액 - opt_trad_pfls_amt: 옵션매매손익금액 - futr_evlu_pfls_amt: 선물평가손익금액 - opt_evlu_pfls_amt: 옵션평가손익금액 - trad_pfls_amt_smtl: 매매손익금액합계 - evlu_pfls_amt_smtl: 평가손익금액합계 - wdrw_psbl_tot_amt: 인출가능총금액 - ord_psbl_cash: 주문가능현금 - ord_psbl_sbst: 주문가능대용 - ord_psbl_tota: 주문가능총액 Raise: ValueError: API 에러 발생시 """ assert not self.auth.paper_trading, "실전계좌에서만 사용 가능한 API입니다." assert mgna_dvsn in MARGIN_DIVISION_VALUES, f"mgna_dvsn must be one of {MARGIN_DIVISION_VALUES}" assert excc_stat_cd in SETTLEMENT_STATUS_VALUES, f"excc_stat_cd must be one of {SETTLEMENT_STATUS_VALUES}" url_path = "/uapi/domestic-futureoption/v1/trading/inquire-balance-valuation-pl" tr_id = "CTFO6159R" params = { "CANO": cano, "ACNT_PRDT_CD": acnt_prdt_cd, "MGNA_DVSN": mgna_dvsn, "EXCC_STAT_CD": excc_stat_cd, "CTX_AREA_FK200": ctx_area_fk200, "CTX_AREA_NK200": ctx_area_nk200, } res_body, res_headers = self._tr_request(url_path, tr_id, tr_cont, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") output1 = [_convert_fields(el, BALANCE_STR_KEYS, BALANCE_DECIMAL_KEYS) for el in res_body["output1"]] output2 = _convert_fields(res_body["output2"], BALANCE_STR_KEYS) return self._build_result(res_body, res_headers, output1, output2) def inquire_balance_settlement_pl( self, cano: str, acnt_prdt_cd: str, inqr_dt: dtm.date, ctx_area_fk200: str = "", ctx_area_nk200: str = "", tr_cont: str = "", ): """ (국내선물옵션주문/계좌) 선물옵션 잔고정산손익내역[v1_국내선물-013] 조회일 기준 종목별 매매손익/평가손익과 계좌 정산 요약을 조회한다. Args: cano (str): 종합계좌번호 (8자리) acnt_prdt_cd (str): 계좌상품코드 (선물옵션 계좌는 "03") inqr_dt (dtm.date): 조회일자 ctx_area_fk200 (str): 연속조회검색조건200 ctx_area_nk200 (str): 연속조회키200 tr_cont (str): 연속거래여부 - 공백:최초조회 N:다음조회 Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - F/M:다음 데이터 있음 D/E:마지막 - ctx_area_fk200 (str): 연속조회검색조건200 - ctx_area_nk200 (str): 연속조회키200 - output1 (list): 종목별 정산손익 - pdno: 상품번호 - prdt_name: 상품명 - trad_dvsn_name: 매매구분명 - bfdy_cblc_qty: 전일잔고수량 - new_qty: 신규수량 - mnpl_rpch_qty: 전매환매수량 - cblc_qty: 잔고수량 - cblc_amt: 잔고금액 - trad_pfls_amt: 매매손익금액 - evlu_amt: 평가금액 - evlu_pfls_amt: 평가손익금액 - output2 (dict): 계좌 정산 요약 - nxdy_dnca: 익일예수금 - mmga_cash: 유지증거금현금 - brkg_mgna_cash: 위탁증거금현금 - opt_buy_chgs: 옵션매수대금 - opt_lqd_evlu_amt: 옵션청산평가금액 - dnca_sbst: 예수금대용 - mmga_tota: 유지증거금총액 - brkg_mgna_tota: 위탁증거금총액 - opt_sll_chgs: 옵션매도대금 - fee: 수수료 - thdt_dfpa: 당일차금 - rnwl_dfpa: 갱신차금 - dnca_cash: 예수금현금 Raise: ValueError: API 에러 발생시 """ url_path = "/uapi/domestic-futureoption/v1/trading/inquire-balance-settlement-pl" tr_id = "CTFO6117R" params = { "CANO": cano, "ACNT_PRDT_CD": acnt_prdt_cd, "INQR_DT": inqr_dt.strftime("%Y%m%d"), "CTX_AREA_FK200": ctx_area_fk200, "CTX_AREA_NK200": ctx_area_nk200, } res_body, res_headers = self._tr_request(url_path, tr_id, tr_cont, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") output1 = [_convert_fields(el, SETTLEMENT_STR_KEYS) for el in res_body["output1"]] output2 = _convert_fields(res_body["output2"], SETTLEMENT_STR_KEYS) return self._build_result(res_body, res_headers, output1, output2) def inquire_daily_amount_fee( self, cano: str, acnt_prdt_cd: str, inqr_strt_day: dtm.date, inqr_end_day: dtm.date, ctx_area_fk200: str = "", ctx_area_nk200: str = "", tr_cont: str = "", ): """ (국내선물옵션주문/계좌) 선물옵션 기간약정수수료일별[v1_국내선물-017] 조회 기간의 일자·종목별 매도/매수 약정금액, 수수료, 매매손익과 기간 합계를 조회한다. 같은 날짜에 종목 수만큼 행이 내려온다. 조회 기간이 3개월을 넘으면 에러가 발생하므로, 더 긴 기간은 3개월 이내로 나누어 조회한다. Args: cano (str): 종합계좌번호 (8자리) acnt_prdt_cd (str): 계좌상품코드 (선물옵션 계좌는 "03") inqr_strt_day (dtm.date): 조회시작일 inqr_end_day (dtm.date): 조회종료일 ctx_area_fk200 (str): 연속조회검색조건200 ctx_area_nk200 (str): 연속조회키200 tr_cont (str): 연속거래여부 - 공백:최초조회 N:다음조회 Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - F/M:다음 데이터 있음 D/E:마지막 - ctx_area_fk200 (str): 연속조회검색조건200 - ctx_area_nk200 (str): 연속조회키200 - output1 (list): 일자별 약정/수수료/손익 - ord_dt: 주문일자 (date) - pdno: 상품번호 - item_name: 종목명 - sll_agrm_amt: 매도약정금액 - sll_fee: 매도수수료 - buy_agrm_amt: 매수약정금액 - buy_fee: 매수수수료 - tot_fee_smtl: 총수수료합계 - trad_pfls: 매매손익 - output2 (dict): 기간 합계 - futr_agrm: 선물약정 - futr_agrm_amt: 선물약정금액 - futr_agrm_amt_smtl: 선물약정금액합계 - futr_sll_fee_smtl: 선물매도수수료합계 - futr_buy_fee_smtl: 선물매수수수료합계 - futr_fee_smtl: 선물수수료합계 - opt_agrm: 옵션약정 - opt_agrm_amt: 옵션약정금액 - opt_agrm_amt_smtl: 옵션약정금액합계 - opt_sll_fee_smtl: 옵션매도수수료합계 - opt_buy_fee_smtl: 옵션매수수수료합계 - opt_fee_smtl: 옵션수수료합계 - prdt_futr_agrm: 상품선물약정 - prdt_fuop: 상품선물옵션 - prdt_futr_evlu_amt: 상품선물평가금액 - futr_fee: 선물수수료 - opt_fee: 옵션수수료 - fee: 수수료 - sll_agrm_amt: 매도약정금액 - buy_agrm_amt: 매수약정금액 - agrm_amt_smtl: 약정금액합계 - sll_fee: 매도수수료 - buy_fee: 매수수수료 - fee_smtl: 수수료합계 - trad_pfls_smtl: 매매손익합계 Raise: ValueError: API 에러 발생시 """ url_path = "/uapi/domestic-futureoption/v1/trading/inquire-daily-amount-fee" tr_id = "CTFO6119R" params = { "CANO": cano, "ACNT_PRDT_CD": acnt_prdt_cd, "INQR_STRT_DAY": inqr_strt_day.strftime("%Y%m%d"), "INQR_END_DAY": inqr_end_day.strftime("%Y%m%d"), "CTX_AREA_FK200": ctx_area_fk200, "CTX_AREA_NK200": ctx_area_nk200, } res_body, res_headers = self._tr_request(url_path, tr_id, tr_cont, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") output1 = [_convert_fields(el, DAILY_AMOUNT_FEE_STR_KEYS, date_keys=DAILY_AMOUNT_FEE_DATE_KEYS) for el in res_body["output1"]] output2 = _convert_fields(res_body["output2"], DAILY_AMOUNT_FEE_STR_KEYS) return self._build_result(res_body, res_headers, output1, output2) def inquire_ccnl( self, cano: str, acnt_prdt_cd: str, strt_ord_dt: dtm.date, end_ord_dt: dtm.date, sll_buy_dvsn_cd: str = "00", ccld_nccs_dvsn: str = "00", sort_sqn: str = "DS", pdno: str = "", strt_odno: str = "", mket_id_cd: str = "", ctx_area_fk200: str = "", ctx_area_nk200: str = "", tr_cont: str = "", ): """ (국내선물옵션주문/계좌) 선물옵션 주문체결내역조회[v1_국내선물-005] Args: cano (str): 종합계좌번호 (8자리) acnt_prdt_cd (str): 계좌상품코드 (선물옵션 계좌는 "03") strt_ord_dt (dtm.date): 주문 조회 시작일자 end_ord_dt (dtm.date): 주문 조회 종료일자 sll_buy_dvsn_cd (str): 매도매수구분코드 - 00:전체 01:매도 02:매수 ccld_nccs_dvsn (str): 체결미체결구분 - 00:전체 01:체결 02:미체결 sort_sqn (str): 정렬순서 - AS:정순 DS:역순 pdno (str): 상품번호 (공백:전체) strt_odno (str): 시작주문번호 (공백:전체) mket_id_cd (str): 시장ID코드 (공백:전체) ctx_area_fk200 (str): 연속조회검색조건200 ctx_area_nk200 (str): 연속조회키200 tr_cont (str): 연속거래여부 - 공백:최초조회 N:다음조회 Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - F/M:다음 데이터 있음 D/E:마지막 - ctx_area_fk200 (str): 연속조회검색조건200 - ctx_area_nk200 (str): 연속조회키200 - output1 (list): 주문체결내역 - ord_gno_brno: 주문채번지점번호 - cano: 종합계좌번호 - csac_name: 종합계좌명 - acnt_prdt_cd: 계좌상품코드 - ord_dt: 주문일자 (date) - odno: 주문번호 - orgn_odno: 원주문번호 - sll_buy_dvsn_cd: 매도매수구분코드 - 01:매도 02:매수 - trad_dvsn_name: 매매구분명 - nmpr_type_cd: 호가유형코드 - nmpr_type_name: 호가유형명 - pdno: 상품번호 - prdt_name: 상품명 - prdt_type_cd: 상품유형코드 - ord_qty: 주문수량 - ord_idx: 주문지수 (Decimal) - qty: 잔량 - ord_tmd: 주문시각 (time) - tot_ccld_qty: 총체결수량 - avg_idx: 평균지수 (Decimal) - tot_ccld_amt: 총체결금액 - rjct_qty: 거부수량 - ingr_trad_rjct_rson_cd: 장내매매거부사유코드 - ingr_trad_rjct_rson_name: 장내매매거부사유명 - ord_stfno: 주문직원번호 - sprd_item_yn: 스프레드종목여부 - ord_ip_addr: 주문IP주소 - output2 (dict): 합계 - tot_ord_qty: 총주문수량 - tot_ccld_amt_smtl: 총체결금액합계 - tot_ccld_qty_smtl: 총체결수량합계 - fee_smtl: 수수료합계 - ctac_tlno: 연락전화번호 Raise: ValueError: API 에러 발생시 """ assert sll_buy_dvsn_cd in SELL_BUY_DIVISION_VALUES, f"sll_buy_dvsn_cd must be one of {SELL_BUY_DIVISION_VALUES}" assert ccld_nccs_dvsn in FILLED_UNFILLED_DIVISION_VALUES, f"ccld_nccs_dvsn must be one of {FILLED_UNFILLED_DIVISION_VALUES}" assert sort_sqn in SORT_SEQUENCE_VALUES, f"sort_sqn must be one of {SORT_SEQUENCE_VALUES}" url_path = "/uapi/domestic-futureoption/v1/trading/inquire-ccnl" tr_id = "VTTO5201R" if self.auth.paper_trading else "TTTO5201R" params = { "CANO": cano, "ACNT_PRDT_CD": acnt_prdt_cd, "STRT_ORD_DT": strt_ord_dt.strftime("%Y%m%d"), "END_ORD_DT": end_ord_dt.strftime("%Y%m%d"), "SLL_BUY_DVSN_CD": sll_buy_dvsn_cd, "CCLD_NCCS_DVSN": ccld_nccs_dvsn, "SORT_SQN": sort_sqn, "PDNO": pdno, "STRT_ODNO": strt_odno, "MKET_ID_CD": mket_id_cd, "CTX_AREA_FK200": ctx_area_fk200, "CTX_AREA_NK200": ctx_area_nk200, } res_body, res_headers = self._tr_request(url_path, tr_id, tr_cont, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") output1 = [_convert_fields(el, CCNL_STR_KEYS, CCNL_DECIMAL_KEYS, CCNL_DATE_KEYS, CCNL_TIME_KEYS) for el in res_body["output1"]] output2 = _convert_fields(res_body["output2"], CCNL_STR_KEYS) return self._build_result(res_body, res_headers, output1, output2) def inquire_ngt_ccnl( self, cano: str, acnt_prdt_cd: str, strt_ord_dt: dtm.date, end_ord_dt: dtm.date, sll_buy_dvsn_cd: str = "00", ccld_nccs_dvsn: str = "00", sort_sqn: str = "", strt_odno: str = "", pdno: str = "", mket_id_cd: str = "", fuop_dvsn_cd: str = "", ctx_area_fk200: str = "", ctx_area_nk200: str = "", tr_cont: str = "", ): """ (국내선물옵션주문/계좌) (야간)선물옵션 주문체결 내역조회[국내선물-010] 야간시장(18:00~익일 06:00) 주문체결 내역을 조회한다. 실전계좌 전용. Args: cano (str): 종합계좌번호 (8자리) acnt_prdt_cd (str): 계좌상품코드 (선물옵션 계좌는 "03") strt_ord_dt (dtm.date): 주문 조회 시작일자 end_ord_dt (dtm.date): 주문 조회 종료일자 (해당일 포함 조회는 다음날로 지정) sll_buy_dvsn_cd (str): 매도매수구분코드 - 00:전체 01:매도 02:매수 ccld_nccs_dvsn (str): 체결미체결구분 - 00:전체 01:체결 02:미체결 sort_sqn (str): 정렬순서 - 공백:기본 AS:정순 DS:역순 strt_odno (str): 시작주문번호 (공백:전체) pdno (str): 상품번호 (공백:전체) mket_id_cd (str): 시장ID코드 (공백:전체) fuop_dvsn_cd (str): 선물옵션구분코드 - 공백:전체 01:선물 02:옵션 ctx_area_fk200 (str): 연속조회검색조건200 ctx_area_nk200 (str): 연속조회키200 tr_cont (str): 연속거래여부 - 공백:최초조회 N:다음조회 Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - F/M:다음 데이터 있음 D/E:마지막 - ctx_area_fk200 (str): 연속조회검색조건200 - ctx_area_nk200 (str): 연속조회키200 - output1 (list): 주문체결내역 - ord_gno_brno: 주문채번지점번호 - cano: 종합계좌번호 - csac_name: 종합계좌명 - acnt_prdt_cd: 계좌상품코드 - ord_dt: 주문일자 (date) - odno: 주문번호 - orgn_odno: 원주문번호 - sll_buy_dvsn_cd: 매도매수구분코드 - 01:매도 02:매수 - trad_dvsn_name: 매매구분명 - nmpr_type_name: 호가유형명 - pdno: 상품번호 - prdt_name: 상품명 - prdt_type_cd: 상품유형코드 - ord_qty: 주문수량 - ord_idx4: 주문지수 (Decimal) - qty: 잔량 - ord_tmd: 주문시각 (time) - tot_ccld_qty: 총체결수량 - avg_idx: 평균지수 (Decimal) - tot_ccld_amt: 총체결금액 - rjct_qty: 거부수량 - ingr_trad_rjct_rson_cd: 장내매매거부사유코드 - ingr_trad_rjct_rson_name: 장내매매거부사유명 - ord_stfno: 주문직원번호 - sprd_item_yn: 스프레드종목여부 - ord_ip_addr: 주문IP주소 - output2 (dict): 합계 - tot_ord_qty: 총주문수량 - fee: 수수료 - ctac_tlno: 연락전화번호 Raise: ValueError: API 에러 발생시 """ assert not self.auth.paper_trading, "실전계좌에서만 사용 가능한 API입니다." assert sll_buy_dvsn_cd in SELL_BUY_DIVISION_VALUES, f"sll_buy_dvsn_cd must be one of {SELL_BUY_DIVISION_VALUES}" assert ccld_nccs_dvsn in FILLED_UNFILLED_DIVISION_VALUES, f"ccld_nccs_dvsn must be one of {FILLED_UNFILLED_DIVISION_VALUES}" assert sort_sqn in NGT_SORT_SEQUENCE_VALUES, f"sort_sqn must be one of {NGT_SORT_SEQUENCE_VALUES}" assert fuop_dvsn_cd in FUOP_DIVISION_VALUES, f"fuop_dvsn_cd must be one of {FUOP_DIVISION_VALUES}" url_path = "/uapi/domestic-futureoption/v1/trading/inquire-ngt-ccnl" tr_id = "STTN5201R" params = { "CANO": cano, "ACNT_PRDT_CD": acnt_prdt_cd, "STRT_ORD_DT": strt_ord_dt.strftime("%Y%m%d"), "END_ORD_DT": end_ord_dt.strftime("%Y%m%d"), "SLL_BUY_DVSN_CD": sll_buy_dvsn_cd, "CCLD_NCCS_DVSN": ccld_nccs_dvsn, "SORT_SQN": sort_sqn, "STRT_ODNO": strt_odno, "PDNO": pdno, "MKET_ID_CD": mket_id_cd, "FUOP_DVSN_CD": fuop_dvsn_cd, "SCRN_DVSN": NGT_SCREEN_DIVISION_DEFAULT, "CTX_AREA_FK200": ctx_area_fk200, "CTX_AREA_NK200": ctx_area_nk200, } res_body, res_headers = self._tr_request(url_path, tr_id, tr_cont, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") output1 = [_convert_fields(el, CCNL_STR_KEYS, NGT_CCNL_DECIMAL_KEYS, CCNL_DATE_KEYS, CCNL_TIME_KEYS) for el in res_body["output1"]] output2 = _convert_fields(res_body["output2"], CCNL_STR_KEYS) return self._build_result(res_body, res_headers, output1, output2) def inquire_deposit(self, cano: str, acnt_prdt_cd: str): """ (국내선물옵션주문/계좌) 선물옵션 총자산현황[v1_국내선물-014] 예수금, 증거금, 매매/평가손익, 추정예탁자산 등 계좌 단위 자산 현황을 조회한다. 실전계좌 전용. Args: cano (str): 종합계좌번호 (8자리) acnt_prdt_cd (str): 계좌상품코드 (선물옵션 계좌는 "03") Returns: dict: - rt_cd (str): 응답코드 - msg_cd (str): 메시지코드 - msg1 (str): 메시지1 - tr_cont (str): 연속거래여부 - output (dict): 총자산현황 - dnca_tota: 예수금총액 - bfdy_chck_amt: 전일수표금액 - thdt_chck_amt: 당일수표금액 - rlth_uwdl_dpos_amt: 실물인수도예치금액 - brkg_mgna_cash: 위탁증거금현금 - wdrw_psbl_tot_amt: 인출가능총금액 - ord_psbl_cash: 주문가능현금 - ord_psbl_tota: 주문가능총액 - dnca_sbst: 예수금대용 - scts_sbst_amt: 유가증권대용금액 - frcr_evlu_amt: 외화평가금액 - brkg_mgna_sbst: 위탁증거금대용 - sbst_rlse_psbl_amt: 대용해제가능금액 - mtnc_rt: 유지비율 (Decimal) - add_mgna_tota: 추가증거금총액 - add_mgna_cash: 추가증거금현금 - rcva: 미수금 - futr_trad_pfls: 선물매매손익 - opt_trad_pfls_amt: 옵션매매손익금액 - trad_pfls_smtl: 매매손익합계 - futr_evlu_pfls_amt: 선물평가손익금액 - opt_evlu_pfls_amt: 옵션평가손익금액 - evlu_pfls_smtl: 평가손익합계 - excc_dfpa: 정산차금 - opt_dfpa: 옵션차금 - brkg_fee: 위탁수수료 - nxdy_dnca: 익일예수금 - prsm_dpast_amt: 추정예탁자산금액 - cash_mntn_amt: 현금유지금액 - hack_acdt_acnt_move_amt: 해킹사고계좌이전금액 Raise: ValueError: API 에러 발생시 """ assert not self.auth.paper_trading, "실전계좌에서만 사용 가능한 API입니다." url_path = "/uapi/domestic-futureoption/v1/trading/inquire-deposit" tr_id = "CTRP6550R" params = { "CANO": cano, "ACNT_PRDT_CD": acnt_prdt_cd, } res_body, res_headers = self._tr_request(url_path, tr_id, params=params) if res_body["rt_cd"] != "0": raise ValueError(f"Error: ({res_body['msg_cd']}) {res_body['msg1']}") return { "rt_cd": res_body["rt_cd"], "msg_cd": res_body["msg_cd"], "msg1": res_body["msg1"].strip(), "tr_cont": res_headers.get("tr_cont", "").strip(), "output": _convert_fields(res_body["output"], [], DEPOSIT_DECIMAL_KEYS), } def _build_result(self, res_body: dict, res_headers: dict, output1: list, output2: dict) -> dict: return { "rt_cd": res_body["rt_cd"], "msg_cd": res_body["msg_cd"], "msg1": res_body["msg1"].strip(), "tr_cont": res_headers.get("tr_cont", "").strip(), "ctx_area_fk200": res_body.get("ctx_area_fk200", "").strip(), "ctx_area_nk200": res_body.get("ctx_area_nk200", "").strip(), "output1": output1, "output2": output2, }