initial commit
This commit is contained in:
12
한국투자증권(API)/legacy/Sample01/README.md
Normal file
12
한국투자증권(API)/legacy/Sample01/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
#### **[당사에서 제공하는 샘플코드에 대한 유의사항]** ####
|
||||
- 샘플 코드는 한국투자증권 오픈API(KIS Develpers)를 연동하는 예시입니다. 고객님의 개발부담을 줄이고자 참고용으로 제공되고 있습니다.
|
||||
- 샘플 코드는 별도의 공지 없이 지속적으로 업데이트될 수 있습니다.
|
||||
- 샘플 코드를 활용하여 제작한 고객님의 프로그램으로 인한 손해에 대해서는 당사에서 책임지지 않습니다.
|
||||
|
||||

|
||||
|
||||
[작성중]
|
||||
## 1. 샘플코드 이용 안내 (API 호출 이제부터 한줄이면 됩니다.)
|
||||
|
||||
한국투자증권 오픈API 활용하는 매매프로그램 개발을 좀더 쉽게 가능하도록 API호출에 필요한 Header, Body 정의를 Import 만으로 한줄로만 호출이 가능합니다.
|
||||
|
||||
170
한국투자증권(API)/legacy/Sample01/kis_api01.py
Normal file
170
한국투자증권(API)/legacy/Sample01/kis_api01.py
Normal file
@@ -0,0 +1,170 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Feb 15 07:56:54 2022
|
||||
"""
|
||||
#kis_api module 을 찾을 수 없다는 에러가 나는 경우 sys.path에 kis_api.py 가 있는 폴더를 추가해준다.
|
||||
import kis_auth as ka
|
||||
import kis_domstk as kb
|
||||
|
||||
import pandas as pd
|
||||
|
||||
import sys
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
|
||||
#====| 국내주식(kis_domstk) import 파일을 하신후 프로그램에서 필요한 API 호출 샘플 아래 참고하시기 바랍니다. |=====================
|
||||
#====| 국내주식(kis_domstk) import 파일을 하신후 프로그램에서 필요한 API 호출 샘플 아래 참고하시기 바랍니다. |=====================
|
||||
|
||||
#====| 국내선물옵션, 해외주식, 해외선물옵션, 채권 등 지속적으로 추가하도록 하겠습니다. 2024.05.16 KIS Developers Team |======================
|
||||
|
||||
#====| [국내주식] 주문/계좌 |============================================================================================================================
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식현금주문 (매수매도구분 buy,sell + 종목번호 6자리 + 주문수량 + 주문단가)
|
||||
# 지정가 기준이며 시장가 옵션(주문구분코드)을 사용하는 경우 kis_domstk.py get_order_cash 수정요망!
|
||||
rt_data = kb.get_order_cash(ord_dv="buy",itm_no="071050", qty=10, unpr=65000)
|
||||
print(rt_data.KRX_FWDG_ORD_ORGNO + "+" + rt_data.ODNO + "+" + rt_data.ORD_TMD) # 주문접수조직번호+주문접수번호+주문시각
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식주문(정정취소) (한국거래소전송주문조직번호 5자리+원주문번호 10자리('0'을 채우지 않아도됨)+정정취소구분코드+정정및취소주문수량+잔량전부주문여부)
|
||||
# 지정가 기준이며 시장가 옵션(주문구분코드)을 사용하는 경우 kis_domstk.py get_order_rvsecncl 수정요망!
|
||||
rt_data = kb.get_order_rvsecncl(ord_orgno="06010", orgn_odno="0000224003", ord_dvsn="00", rvse_cncl_dvsn_cd="01", ord_qty=0, ord_unpr=64900, qty_all_ord_yn="Y")
|
||||
print(rt_data.KRX_FWDG_ORD_ORGNO + "+" + rt_data.ODNO + "+" + rt_data.ORD_TMD) # 주문접수조직번호+주문접수번호+주문시각
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식정정취소가능주문내역조회
|
||||
rt_data = kb.get_inquire_psbl_rvsecncl_lst()
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식일별주문체결(현황)조회
|
||||
# dv="01" 01:3개월 이내 국내주식체결내역 (월단위 ex: 2024.04.25 이면 2024.01월~04월조회)
|
||||
# dv="02" 02:3개월 이전 국내주식체결내역 (월단위 ex: 2024.04.25 이면 2024.01월이전)
|
||||
rt_data = kb.get_inquire_daily_ccld_obj(dv="01")
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식일별주문체결(내역)조회
|
||||
# dv="01" 01:3개월 이내 국내주식체결내역 (월단위 ex: 2024.04.25 이면 2024.01월~04월조회)
|
||||
# dv="02" 02:3개월 이전 국내주식체결내역 (월단위 ex: 2024.04.25 이면 2024.01월이전)
|
||||
rt_data = kb.get_inquire_daily_ccld_lst(dv="01")
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식잔고조회 (잔고현황)
|
||||
rt_data = kb.get_inquire_balance_obj()
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식잔고조회 (보유종목리스트)
|
||||
rt_data = kb.get_inquire_balance_lst()
|
||||
print(rt_data)
|
||||
|
||||
|
||||
# [국내주식] 주문/계좌 > 매수가능조회 (종목번호 5자리 + 종목단가)
|
||||
rt_data = kb.get_inquire_psbl_order(pdno="", ord_unpr=0)
|
||||
ord_psbl_cash_value = rt_data.loc[0, 'ord_psbl_cash'] # ord_psbl_cash 주문가능현금
|
||||
ord_psbl_cash_value = rt_data.loc[0, 'nrcvb_buy_amt'] # nrcvb_buy_amt 미수없는매수가능금액
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식예약주문 (매수매도구분 buy,sell + 종목번호 6자리 + 주문수량 + 주문단가 + 주문구분코드 )
|
||||
# 주문구분코드 : 00 : 지정가 01 : 시장가 02 : 조건부지정가 05 : 장전 시간외
|
||||
rt_data = kb.get_order_resv(ord_dv="buy", itm_no="052400", qty=100, unpr=12650, ord_dvsn_cd="01")
|
||||
print(rt_data.RSVN_ORD_SEQ) # 예약주문순번
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식예약주문(취소) (예약주문순번)
|
||||
rt_data = kb.get_order_resv_cncl(rsvn_ord_seq="93601")
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식예약주문(정정) (종목번호 6자리 + 주문수량 + 주문단가 + 매도매수구분코드 + 주문구분코드 + 주문대상잔고구분코드 + 예약주문순번)
|
||||
# 매도매수구분코드 01 : 매도 02 : 매수
|
||||
# 주문구분코드 00 : 지정가 01 : 시장가 02 : 조건부지정가 05 : 장전 시간외
|
||||
# 주문대상잔고구분코드 10 : 현금 12 : 주식담보대출 14 : 대여상환 21 : 자기융자신규 .........
|
||||
rt_data = kb.get_order_resv_rvse(pdno="052400", ord_qty=1, ord_unpr=12700, sll_buy_dvsn_cd="02", ord_dvsn="00", ord_objt_cblc_dvsn_cd="", rsvn_ord_seq=93605)
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식예약주문조회[v1_국내주식-020] (조회시작일자 + 조회종료일자)
|
||||
rt_data = kb.get_order_resv_ccnl(inqr_strt_dt="20240429", inqr_end_dt="20240430")
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 주식잔고조회_실현손익[v1_국내주식-041]
|
||||
rt_data = kb.get_inquire_balance_rlz_pl_obj()
|
||||
print(rt_data)
|
||||
rt_data = kb.get_inquire_balance_rlz_pl_lst()
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 신용매수가능조회
|
||||
rt_data = kb.get_inquire_credit_psamount()
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 기간별매매손익현황조회
|
||||
rt_data = kb.get_inquire_period_trade_profit_obj()
|
||||
print(rt_data)
|
||||
rt_data = kb.get_inquire_period_trade_profit_lst()
|
||||
print(rt_data)
|
||||
|
||||
# [국내주식] 주문/계좌 > 기간별손익일별합산조회
|
||||
rt_data = kb.get_inquire_period_profit_obj()
|
||||
print(rt_data)
|
||||
rt_data = kb.get_inquire_period_profit_lst()
|
||||
print(rt_data)
|
||||
|
||||
|
||||
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 시세 (종목번호 6자리)
|
||||
rt_data = kb.get_inquire_price(itm_no="071050")
|
||||
print(rt_data.stck_prpr+ " " + rt_data.prdy_vrss) # 현재가, 전일대비
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 체결 (종목번호 6자리)
|
||||
rt_data = kb.get_inquire_ccnl(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 일자별 (종목번호 6자리 + 기간분류코드)
|
||||
# 기간분류코드 D : (일)최근 30거래일 W : (주)최근 30주 M : (월)최근 30개월
|
||||
# 수정주가기준이며 수정주가미반영 기준을 원하시면 인자값 adj_prc_code="2" 추가
|
||||
rt_data = kb.get_inquire_daily_price(itm_no="071050", period_code="M")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 호가 (종목번호 6자리)
|
||||
rt_data = kb.get_inquire_asking_price_exp_ccn(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 예상체결 (출력구분="2" + 종목번호 6자리)
|
||||
rt_data = kb.get_inquire_asking_price_exp_ccn(output_dv="2", itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 투자자 (종목번호 6자리)
|
||||
rt_data = kb.get_inquire_investor(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 회원사 (종목번호 6자리)
|
||||
rt_data = kb.get_inquire_member(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 국내주식기간별시세(일/주/월/년) (현재) (종목번호 6자리)
|
||||
rt_data = kb.get_inquire_daily_itemchartprice(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 국내주식기간별시세(일/주/월/년) (기간별 데이터 Default는 일별이며 조회기간은 100일전(영업일수 아님)부터 금일까지)
|
||||
rt_data = kb.get_inquire_daily_itemchartprice(output_dv="2", itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 당일시간대별체결 (현재가 : 주식현재가, 전일대비, 전일대비율, 누적거래량,전일거래량, 대표시장한글명))
|
||||
rt_data = kb.get_inquire_time_itemconclusion(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 당일시간대별체결 (시간대별체결내역)
|
||||
rt_data = kb.get_inquire_time_itemconclusion(output_dv='2', itm_no="071050") # 기준시각 미지정시 현재시각 이전 체결 내역이 30건 조회됨
|
||||
rt_data = kb.get_inquire_time_itemconclusion(output_dv='2', itm_no="071050", inqr_hour='100000') # 지정 기준시각 이전 체결 내역이 30건 조회됨
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 시간외현재주가 (시간외 현재가 : 주식현재가, 전일대비, 전일대비율, 누적거래량, 거래량, 거래대금, 단일가상한가 .... 등)
|
||||
rt_data = kb.get_inquire_daily_overtimeprice(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 시간외일자별주가 (최근 30일) (출력구분 + 종목번호 6자리)
|
||||
rt_data = kb.get_inquire_daily_overtimeprice(output_dv='2', itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식당일분봉조회 (종목번호 6자리) (출력구분 + 종목번호 6자리)
|
||||
rt_data = kb.get_inquire_time_itemchartprice(itm_no="071050") #
|
||||
rt_data = kb.get_inquire_time_itemchartprice(output_dv="2", itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > 주식현재가 시세2
|
||||
rt_data = kb.get_inquire_daily_price_2(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > ETF/ETN 현재가
|
||||
rt_data = kb.get_quotations_inquire_price(itm_no="071050")
|
||||
|
||||
# [국내주식] 기본시세 > NAV 비교추이(종목)
|
||||
rt_data = kb.get_quotations_nav_comparison_trend(itm_no="071050") # ETF 종목 정보
|
||||
rt_data = kb.get_quotations_nav_comparison_trend(output_dv="2", itm_no="071050") # ETF NAV 정보
|
||||
|
||||
# [국내주식] 업종/기타 > 국내휴장일조회
|
||||
rt_data = kb.get_quotations_ch_holiday(dt="20240302")
|
||||
|
||||
print(rt_data)
|
||||
53
한국투자증권(API)/legacy/Sample01/kis_api02.py
Normal file
53
한국투자증권(API)/legacy/Sample01/kis_api02.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Feb 15 07:56:54 2022
|
||||
"""
|
||||
#kis_api module 을 찾을 수 없다는 에러가 나는 경우 sys.path에 kis_api.py 가 있는 폴더를 추가해준다.
|
||||
import kis_auth as ka
|
||||
import kis_domfuopt as kb
|
||||
|
||||
import pandas as pd
|
||||
|
||||
import sys
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
#====| 국내선물옵션(야간포함)(kis_domfuopt) import 파일을 하신후 프로그램에서 필요한 API 호출 샘플 아래 참고하시기 바랍니다. |=======================
|
||||
#====| 국내선물옵션(야간포함)(kis_domfuopt) import 파일을 하신후 프로그램에서 필요한 API 호출 샘플 아래 참고하시기 바랍니다. |=======================
|
||||
#====| 국내선물옵션(야간포함)(kis_domfuopt) import 파일을 하신후 프로그램에서 필요한 API 호출 샘플 아래 참고하시기 바랍니다. |=======================
|
||||
|
||||
#====| 국내선물옵션, 해외주식, 해외선물옵션, 채권 등 지속적으로 추가하도록 하겠습니다. 2024.05.16 KIS Developers Team |======================
|
||||
|
||||
|
||||
#====| [국내선물옵션] 주문/계좌 |============================================================================================================================
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > 선물옵션 주문[v1_국내선물-001] (주간:01/야간(Eurex):02구분 + 매수매도구분 buy,sell + 종목번호 + 주문수량 + 주문단가)
|
||||
# 지정가 기준이며 시장가 옵션(주문구분코드)을 사용하는 경우 kis_domstk.py get_order_cash 수정요망!
|
||||
rt_data = kb.get_domfuopt_order(dv_cd="01", sll_buy_dvsn_cd="02", dvsn_cd="02", itm_no="101V06", qty=100, unpr=0)
|
||||
print(rt_data)
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > 선물옵션 정정취소주문[v1_국내선물-002] (주간:01/야간(Eurex):02구분 + 정정취소구분코드(정정:01. 취소:02) + 원주문번호 + 주문구분 + 주문수량 + 주문단가 + 잔량전부여부)
|
||||
# 지정가 기준이며 시장가 옵션(주문구분코드)을 사용하는 경우 kis_domfuopt.py get_domfuopt_order_rvsecncl 수정요망!
|
||||
rt_data = kb.get_domfuopt_order_rvsecncl(orgn_odno="0000362027", ord_dvsn="01", rvse_cncl_dvsn_cd="01", ord_qty=1, ord_unpr=366.45, rmn_qty_yn="")
|
||||
print(rt_data)
|
||||
|
||||
# [[국내선물옵션] 주문/계좌 > (야간)선물옵션 주문체결 현황조회 [국내선물-009] (조회시작일 + 조회종료일)
|
||||
rt_data = kb.get_domfuopt_inquire_ngt_ccnl_obj(inqr_strt_dt="20230701", inqr_end_dt="20230731")
|
||||
print(rt_data)
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 주문체결 내역조회 [국내선물-009] (조회시작일 + 조회종료일)
|
||||
rt_data = kb.get_domfuopt_inquire_ngt_ccnl_lst(inqr_strt_dt="20230701", inqr_end_dt="20230731")
|
||||
print(rt_data)
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 잔고현황 [국내선물-010]
|
||||
rt_data = kb.get_domfuopt_inquire_ngt_balance_obj()
|
||||
print(rt_data)
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 잔고현황
|
||||
rt_data = kb.get_domfuopt_inquire_ngt_balance_lst()
|
||||
print(rt_data)
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 주문가능 조회 [국내선물-011]
|
||||
rt_data = kb.get_domfuopt_inquire_psbl_ngt_order(pdno="101V06", sll_buy_dvsn_cd="02", ord_unpr=0)
|
||||
print(rt_data)
|
||||
151
한국투자증권(API)/legacy/Sample01/kis_api03.py
Normal file
151
한국투자증권(API)/legacy/Sample01/kis_api03.py
Normal file
@@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Feb 15 07:56:54 2022
|
||||
"""
|
||||
#kis_api module 을 찾을 수 없다는 에러가 나는 경우 sys.path에 kis_api.py 가 있는 폴더를 추가해준다.
|
||||
import kis_auth as ka
|
||||
import kis_ovrseastk as kb
|
||||
|
||||
import pandas as pd
|
||||
|
||||
import sys
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
|
||||
#====| [해외주식] 주문/계좌 |============================================================================================================================
|
||||
|
||||
# [해외주식] 주문/계좌 > 주문 (매수매도구분 buy,sell + 종목코드6자리 + 주문수량 + 주문단가)
|
||||
# 지정가 기준이며 시장가 옵션(주문구분코드)을 사용하는 경우 kis_ovrseastk.py get_overseas_order 수정요망!
|
||||
#rt_data = kb.get_overseas_order(ord_dv="buy", excg_cd="NASD", itm_no="TSLA", qty=1, unpr=170)
|
||||
#rt_data = kb.get_overseas_order(ord_dv="buy", excg_cd="NASD", itm_no="AAPL", qty=1, unpr=216.75)
|
||||
rt_data = kb.get_overseas_order(ord_dv="buy", excg_cd="NASD", itm_no="NVDA", qty=1, unpr=123.3)
|
||||
print(rt_data.KRX_FWDG_ORD_ORGNO + "+" + rt_data.ODNO + "+" + rt_data.ORD_TMD) # 주문접수조직번호+주문접수번호+주문시각
|
||||
|
||||
# [해외주식] 주문/계좌 > 정정취소주문 (해외거래소코드excg_cd + 종목코드itm_no + 주문번호orgn_odno + 정정취소구분rvse_cncl_dvsn_cd + 수량qty + 주문단가unpr)
|
||||
# 지정가 기준이며 시장가 옵션(주문구분코드)을 사용하는 경우 kis_ovrseastk.py get_overseas_order 수정요망!
|
||||
rt_data = kb.get_overseas_order_rvsecncl(excg_cd="NASD", itm_no="TSLA", orgn_odno="0030089601", rvse_cncl_dvsn_cd="02", qty=1, unpr=0)
|
||||
print(rt_data) # 주문접수조직번호+주문접수번호+주문시각
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외주식 미체결내역 (해외거래소코드)
|
||||
# 해외거래소코드 NASD:나스닥,NYSE:뉴욕,AMEX:아멕스,SEHK:홍콩,SHAA:중국상해,SZAA:중국심천,TKSE:일본,HASE:베트남하노이,VNSE:호치민
|
||||
rt_data = kb.get_overseas_inquire_nccs(excg_cd="NASD")
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외주식 미체결전량취소주문 (해외거래소코드excg_cd + 종목코드itm_no)
|
||||
# 해외거래소코드 NASD:나스닥,NYSE:뉴욕,AMEX:아멕스,SEHK:홍콩,SHAA:중국상해,SZAA:중국심천,TKSE:일본,HASE:베트남하노이,VNSE:호치민
|
||||
rt_data = kb.get_overseas_order_allcncl(excg_cd="NASD", itm_no="")
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외주식 잔고 현황
|
||||
# 해외거래소코드 NASD:나스닥,NYSE:뉴욕,AMEX:아멕스,SEHK:홍콩,SHAA:중국상해,SZAA:중국심천,TKSE:일본,HASE:베트남하노이,VNSE:호치민
|
||||
# 거래통화코드 - USD : 미국달러,HKD : 홍콩달러,CNY : 중국위안화,JPY : 일본엔화,VND : 베트남동
|
||||
rt_data = kb.get_overseas_inquire_balance(excg_cd="NASD", crcy_cd="")
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외주식 잔고 내역
|
||||
# 해외거래소코드 NASD:나스닥,NYSE:뉴욕,AMEX:아멕스,SEHK:홍콩,SHAA:중국상해,SZAA:중국심천,TKSE:일본,HASE:베트남하노이,VNSE:호치민
|
||||
# 거래통화코드 - USD : 미국달러,HKD : 홍콩달러,CNY : 중국위안화,JPY : 일본엔화,VND : 베트남동
|
||||
rt_data = kb.get_overseas_inquire_balance(excg_cd="NASD", crcy_cd="")
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외주식 주문체결내역
|
||||
# 해외거래소코드 NASD:미국시장 전체(나스닥,뉴욕,아멕스),NYSE:뉴욕,AMEX:아멕스,SEHK:홍콩,SHAA:중국상해,SZAA:중국심천,TKSE:일본,HASE:베트남하노이,VNSE:호치민
|
||||
rt_data = kb.get_overseas_inquire_ccnl(st_dt="", ed_dt="")
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외주식 체결기준현재잔고
|
||||
# dv : 01 보유종목, 02 외화잔고, 03 체결기준현재잔고
|
||||
# dvsn : 01 원화, 02 외화
|
||||
# natn 국가코드 : 000 전체,840 미국,344 홍콩,156 중국,392 일본,704 베트남
|
||||
# mkt 거래시장코드 [Request body NATN_CD 000 설정]
|
||||
# 00 : 전체 , (NATN_CD 840 인경우) 00:전체,01:나스닥(NASD),02:뉴욕거래소(NYSE),03:미국(PINK SHEETS),04:미국(OTCBB),05:아멕스(AMEX) (다른시장 API문서 참조)
|
||||
rt_data = kb.get_overseas_inquire_present_balance(dv="02", dvsn="01", natn="000", mkt="00", inqr_dvsn="00")
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 미국주간주문 (매수매도구분 buy,sell + 종목번호 + 주문수량 + 주문단가)
|
||||
# 지정가 기준이며 시장가 옵션(주문구분코드)을 사용하는 경우 kis_ovrseastk.py get_overseas_order 수정요망!
|
||||
#rt_data = kb.get_overseas_daytime_order(ord_dv="buy", excg_cd="NASD", itm_no="TSLA", qty=1, unpr=251)
|
||||
#rt_data = kb.get_overseas_daytime_order(ord_dv="buy", excg_cd="NASD", itm_no="AAPL", qty=1, unpr=216.75)
|
||||
rt_data = kb.get_overseas_daytime_order(ord_dv="buy", excg_cd="NASD", itm_no="NVDA", qty=1, unpr=123.3)
|
||||
print(rt_data.KRX_FWDG_ORD_ORGNO + "+" + rt_data.ODNO + "+" + rt_data.ORD_TMD) # 주문접수조직번호+주문접수번호+주문시각
|
||||
|
||||
# [해외주식] 주문/계좌 > 미국주간정정취소 (해외거래소코드excg_cd + 종목코드itm_no + 주문번호orgn_odno + 정정취소구분rvse_cncl_dvsn_cd + 수량qty + 주문단가unpr)
|
||||
# 지정가 기준이며 시장가 옵션(주문구분코드)을 사용하는 경우 kis_ovrseastk.py get_overseas_order 수정요망!
|
||||
rt_data = kb.get_overseas_daytime_order_rvsecncl(excg_cd="NASD", itm_no="TSLA", orgn_odno="0030089601", rvse_cncl_dvsn_cd="02", qty=1, unpr=0)
|
||||
print(rt_data) # 주문접수조직번호+주문접수번호+주문시각
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외주식 기간손익[v1_해외주식-032] (해외거래소코드 + 통화코드 + 종목번호 6자리 + 조회시작일 + 조회종료일)
|
||||
# 해외거래소코드 NASD:미국,SEHK:홍콩,SHAA:중국,TKSE:일본,HASE:베트남
|
||||
rt_data = kb.get_overseas_inquire_period_profit(excg_cd="", crcy="", itm_no="", st_dt="20240601", ed_dt="20240709")
|
||||
print(rt_data)
|
||||
# [해외주식] 주문/계좌 > 해외주식 기간손익(매매일자종목별 기간손익) (해외거래소코드 + 통화코드 + 종목번호 6자리 + 조회시작일 + 조회종료일)
|
||||
rt_data = kb.get_overseas_inquire_period_profit_output1(excg_cd="NASD", crcy="", itm_no="", st_dt="20240501", ed_dt="20240709")
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외증거금 통화별조회
|
||||
rt_data = kb.get_overseas_inquire_foreign_margin()
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외증거금 일별거래내역 (해외거래소코드 + 매도매수구분코드 + 종목번호 6자리 + 조회시작일 + 조회종료일)
|
||||
rt_data = kb.get_overseas_inquire_period_trans(excg_cd="", dvsn="", itm_no="", st_dt="20240601", ed_dt="20240709")
|
||||
# [해외주식] 주문/계좌 > 해외증거금 일별거래내역[합계]
|
||||
rt_data = kb.get_overseas_inquire_period_trans_output2(excg_cd="", dvsn="", itm_no="", st_dt="20240601", ed_dt="20240709")
|
||||
print(rt_data)
|
||||
|
||||
# [해외주식] 주문/계좌 > 해외주식 결제기준현재잔고
|
||||
# dv : 01 보유종목, 02 외화잔고, 03 결제기준현재잔고
|
||||
# dt : 기준일자(YYYYMMDD)
|
||||
# dvsn : 01 원화, 02 외화
|
||||
# inqr_dvsn : 00(전체), 01(일반), 02(미니스탁)
|
||||
rt_data = kb.get_overseas_inquire_paymt_stdr_balance(dv="03", dt="", dvsn="01", inqr_dvsn="00")
|
||||
print(rt_data)
|
||||
|
||||
#====| [해외주식] 기본시세 |============================================================================================================================
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식 현재체결가 (해외거래소코드, 종목번호)
|
||||
rt_data = kb.get_overseas_price_quot_search_info(excd="NAS", itm_no="AAPL")
|
||||
print(rt_data) # 해외주식 현재체결가
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식 기간별시세
|
||||
# ※ 기준일(bymd) 지정일자 이후 100일치 조회, 미입력시 당일자 기본 셋팅
|
||||
rt_data = kb.get_overseas_price_quot_dailyprice(excd="NAS", itm_no="AAPL", gubn="0", bymd="")
|
||||
print(rt_data) # 해외주식 기간별시세
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식 종목/지수/환율기간별시세(일/주/월/년)
|
||||
# ※ 기준일(bymd) 지정일자 이후 100일치 조회, 미입력시 당일자 기본 셋팅
|
||||
rt_data = kb.get_overseas_price_quot_inquire_daily_price(div="N", itm_no="AAPL", inqr_strt_dt="", inqr_end_dt="", period="D")
|
||||
rt_data = kb.get_overseas_price_quot_inquire_daily_chartprice(div="N", itm_no="AAPL", inqr_strt_dt="20240605", inqr_end_dt="20240610", period="D")
|
||||
print(rt_data) # 해외주식 종목/지수/환율기간별시세(일/주/월/년)
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식조건검색 div 01 : 검색결과종목수, 02:검색결과종목리스트
|
||||
rt_data = kb.get_overseas_price_quot_inquire_search(div="02", excd="NAS", pr_st="160", pr_en="170")
|
||||
print(rt_data) # 해외주식조건검색
|
||||
|
||||
# [해외주식] 기본시세 > 해외결재일자조회 (기준일자)
|
||||
rt_data = kb.get_overseas_price_quot_countries_holiday(dt="")
|
||||
print(rt_data) # 해외결재일자조회
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식 현재가상세 (해외거래소시장코드, 종목코드)
|
||||
rt_data = kb.get_overseas_price_quot_price_detail(excd="NAS", itm_no="AAPL")
|
||||
print(rt_data) # 해외주식 현재가상세
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식 해외주식분봉조회 (조회구분 div-02:분봉데이터,01:시장별장운영시간, 해외거래소시장코드, 종목코드, 분갭, 전일포함여부)
|
||||
rt_data = kb.get_overseas_price_quot_inquire_time_itemchartprice(div="02", excd="NAS", itm_no="AAPL", nmin="", pinc="0")
|
||||
print(rt_data) # 해외주식 해외주식분봉조회
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식 해외지수분봉조회 (조회구분 div-02:분봉데이터,01:지수정보, 조건시장분류코드, 입력종목코드, 시간구분코드, 과거데이터포함여부)
|
||||
rt_data = kb.get_overseas_price_quot_inquire_time_indexchartprice(div="02", code="N", iscd="SPX", tm_dv="0", inc="Y")
|
||||
print(rt_data) # 해외주식 해외지수분봉조회
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식 상품기본정보 (종목번호, 종목유형)
|
||||
# 종목유형 : 512 미국 나스닥/513 미국 뉴욕/529 미국 아멕스/515 일본/501 홍콩/543 홍콩CNY/558 홍콩USD/507 베트남 하노이/508 베트남 호치민/551 중국 상해A/552 중국 심천A
|
||||
rt_data = kb.get_overseas_price_search_info(itm_no="AAPL", prdt_type_cd="512")
|
||||
print("종목코드("+rt_data.std_pdno+") 종목명(" +rt_data.prdt_eng_name+") 거래시장(" +rt_data.ovrs_excg_cd+":" +rt_data.tr_mket_name+")") # 해외주식 상품기본정보
|
||||
print(rt_data) # 해외주식 상품기본정보
|
||||
|
||||
# [해외주식] 기본시세 > 해외주식 현재가 10호가 (조회구분 01:기본시세 02:10호가 , 해외거래소코드, 종목번호)
|
||||
rt_data = kb.get_overseas_price_inquire_asking_price(div="02", excd="NAS", itm_no="AAPL")
|
||||
print(rt_data) # 해외주식 상품기본정보
|
||||
|
||||
132
한국투자증권(API)/legacy/Sample01/kis_api04.py
Normal file
132
한국투자증권(API)/legacy/Sample01/kis_api04.py
Normal file
@@ -0,0 +1,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Feb 15 07:56:54 2022
|
||||
"""
|
||||
#kis_api module 을 찾을 수 없다는 에러가 나는 경우 sys.path에 kis_api.py 가 있는 폴더를 추가해준다.
|
||||
import kis_auth as ka
|
||||
import kis_ovrseafuopt as kb
|
||||
|
||||
import pandas as pd
|
||||
|
||||
import sys
|
||||
|
||||
# 토큰 발급
|
||||
ka.auth()
|
||||
|
||||
#====| [해외선물옵션] 주문/계좌 |============================================================================================================================
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션주문 (종목번호<6자리 5자리> + 매수매도구분ord_dv + 가격구분dvsn + 주문수량qty + 주문가격limt_pric + 주문가격stop_pric)
|
||||
# 매수매도구분ord_dv 01 : 매도, 02 : 매수 # 가격구분dvsn : 1.지정, 2. 시장, 3. STOP, 4 S/L
|
||||
# 주문가격limt_pric : 지정가인 경우 가격 입력 * 시장가, STOP주문인 경우, 빈칸("") 입력
|
||||
# 주문가격stop_pric : STOP 주문 가격 입력 * 시장가, 지정가인 경우, 빈칸("") 입력
|
||||
rt_data = kb.get_overseasfuopt_order(itm_no="OESU24 C6000", ord_dv="02", dvsn="1", qty=1, limt_pric=13.75, stop_pric=0)
|
||||
print(rt_data.ORD_DT + "+" + rt_data.ODNO) # 주문일자+주문접수번호
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 정정취소주문 (정정취소구분dvsn + 원주문일자ord_dt + 원주문번호orgn_odno + 주문가격limt_pric + 주문가격stop_pric + 청산주문가격lqd_limt_pric + 청산주문가격lqd_stop_pric)
|
||||
rt_data = kb.get_overseasfuopt_order_rvsecncl(dvsn="01", ord_dt="", orgn_odno="", limt_pric=0, stop_pric=0, lqd_limt_pric=0, lqd_stop_pric=0)
|
||||
print(rt_data.ORD_DT + "+" + rt_data.ODNO) # 주문일자+주문접수번호
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 정정취소주문 (정정취소구분dvsn + 원주문일자ord_dt + 원주문번호orgn_odno + 주문가격limt_pric + 주문가격stop_pric + 청산주문가격lqd_limt_pric + 청산주문가격lqd_stop_pric)
|
||||
rt_data = kb.get_overseasfuopt_order_rvsecncl(dvsn="01", ord_dt="", orgn_odno="", limt_pric=0, stop_pric=0, lqd_limt_pric=0, lqd_stop_pric=0)
|
||||
print(rt_data.ORD_DT + "+" + rt_data.ODNO) # 주문일자+주문접수번호
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 정정취소주문 (체결미체결구분ccld_dv + 매도매수구분코드ord_dv + 선물옵션구분fuop_dvsn)
|
||||
# 체결미체결구분 01:전체,02:체결,03:미체결 # 매도매수구분코드 %%:전체,01:매도,02:매수 # 선물옵션구분 00:전체 / 01:선물 / 02:옵션
|
||||
rt_data = kb.get_overseasfuopt_inquire_ccld(ccld_dv="01", ord_dv="%%", fuop_dvsn="00")
|
||||
print(rt_data)
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 미결제내역조회(잔고) (선물옵션구분fuop_dvsn)
|
||||
# 선물옵션구분 00:전체 / 01:선물 / 02:옵션
|
||||
rt_data = kb.get_overseasfuopt_inquire_unpd(fuop_dvsn="00")
|
||||
print(rt_data)
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 해외선물옵션 주문가능조회 (선물옵션구분fuop_dvsn)
|
||||
# 선물옵션구분 00:전체 / 01:선물 / 02:옵션
|
||||
rt_data = kb.get_overseasfuopt_inquire_psamount(itm_no="OESU24 C6000", dvsn="02", pric=0, ordyn="")
|
||||
print(rt_data)
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 기간계좌손익 일별 (조회구분inqr_dvsn + 조회시작일자fr_dt + 조회종료일자to_dt + 통화코드crcy + 선물옵션구분fuop_dvsn")
|
||||
# 조회구분코드 : 01 통화별, 02 종목별
|
||||
rt_data = kb.get_overseasfuopt_inquire_period_ccld(inqr_dvsn="01", fr_dt="", to_dt="", crcy="%%%", fuop_dvsn="00")
|
||||
print(rt_data)
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 일별 체결내역 (조회시작일자fr_dt + 조회종료일자to_dt + 선물옵션구분fuop_dvsn + 통화코드crcy + 매도매수구분코드dvsn)
|
||||
rt_data = kb.get_overseasfuopt_inquire_daily_ccld(fr_dt="", to_dt="", fuop_dvsn="00", crcy="%%%", dvsn="%%")
|
||||
print(rt_data)
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 예수금현황 (통화코드crcy + 조회일자inqr_dt)
|
||||
rt_data = kb.get_overseasfuopt_inquire_deposit(crcy="%%%", inqr_dt="")
|
||||
print(rt_data)
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 일별 주문내역 (조회시작일자fr_dt + 조회종료일자to_dt + 체결미체결구분ccld_dvsn + 매수매도구분dvsn + 선물옵션구분fuop_dvsn)
|
||||
# ccld_dvsn 01:전체 / 02:체결 / 03:미체결 dvsn %% : 전체 / 01 : 매도 / 02 : 매수 fuop_dvsn 00:전체 / 01:선물 / 02:옵션
|
||||
rt_data = kb.get_overseasfuopt_inquire_daily_order(fr_dt="20240401", to_dt="", ccld_dvsn="01", dvsn="%%", fuop_dvsn="00")
|
||||
print(rt_data)
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 기간계좌거래내역 (조회시작일자fr_dt, 조회종료일자to_dt)
|
||||
rt_data = kb.get_overseasfuopt_inquire_period_trans(fr_dt="20240101", to_dt="20240717")
|
||||
print(rt_data)
|
||||
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 증거금상세 (통화구분crcy + 조회일자inqr_dt)
|
||||
rt_data = kb.get_overseasfuopt_inquire_margin_detail(crcy="TUS", inqr_dt="20240717")
|
||||
print(rt_data)
|
||||
|
||||
|
||||
#====| [해외선물옵션] 기본시세 |============================================================================================================================
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물종목상세 (종목코드)
|
||||
rt_data = kb.get_overseas_fuopt_stock_detail(itm_no="6EU24")
|
||||
print(rt_data) # 해외선물종목상세
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물종목현재가 (종목코드)
|
||||
rt_data = kb.get_overseas_fuopt_inquire_price(itm_no="6EU24")
|
||||
print(rt_data) # 해외선물종목현재가
|
||||
|
||||
# [해외선물옵션] 기본시세 > 분봉조회 (종목코드 + 거래소시장코드 + 조회시작일 + 조회종료일 + 조회건수(120건) + 갭(5분))
|
||||
# 조회일자 1주일 이내 (조회시작일~조회종료일)
|
||||
rt_data = kb.get_overseas_fuopt_inquire_time_futurechartprice(itm_no="6EU24", exch="CME", st_dt="20240709", ed_dt="20240710", cnt="120", gap="5", idx="")
|
||||
print(rt_data) # 해외선물분봉조회
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(주간)[해외선물-017] (종목코드 + 거래소시장코드 + 조회시작일 + 조회종료일)
|
||||
# 조회일자 1주일 이내 (조회시작일~조회종료일)
|
||||
rt_data = kb.get_overseas_fuopt_weekly_ccnl(itm_no="6EU24", exch="CME", st_dt="20240709", ed_dt="20240710")
|
||||
print(rt_data) # 체결추이(주간)
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(일간)[해외선물-018] (종목코드 + 거래소시장코드 + 조회시작일 + 조회종료일)
|
||||
# 조회일자 1주일 이내 (조회시작일~조회종료일)
|
||||
rt_data = kb.get_overseas_fuopt_daily_ccnl(itm_no="6EU24", exch="CME", st_dt="20240707", ed_dt="20240710")
|
||||
print(rt_data) # 체결추이(일간)
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(틱)[해외선물-019] (종목코드 + 거래소시장코드)
|
||||
# 조회일자는 당일만 조회하도록 샘플코드 구성
|
||||
rt_data = kb.get_overseas_fuopt_tick_ccnl(itm_no="6EU24", exch="CME")
|
||||
print(rt_data) # 체결추이(틱)
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물 체결추이(월간)[해외선물-020] (종목코드 + 거래소시장코드 + 조회시작일 + 조회종료일)
|
||||
# 조회일자 1주일 이내 (조회시작일~조회종료일)
|
||||
rt_data = kb.get_overseas_fuopt_monthly_ccnl(itm_no="6EU24", exch="CME", st_dt="20240707", ed_dt="20240710")
|
||||
print(rt_data) # 체결추이(일간)
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물 호가 [해외선물-031] (종목코드)
|
||||
rt_data = kb.get_overseas_fuopt_inquire_asking_price(itm_no="6EU24")
|
||||
print(rt_data) # 해외선물 호가
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물 상품기본정보 [해외선물-023] (종목코드)
|
||||
rt_data = kb.get_overseas_fuopt_search_contract_detail(itm_no01="6EU24")
|
||||
print(rt_data) # 해외선물 상품기본정보
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물 장운영시간 [해외선물-030] (클래스코드 + 거래소코드 + 옵션여부)
|
||||
rt_data = kb.get_overseas_fuopt_market_time(clas="", excg="CME", opt="%")
|
||||
print(rt_data) # 해외선물 장운영시간
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외선물 미결제추이 [해외선물-029] (상품 + 거래소코드 + 옵션여부)
|
||||
# 상품(PROD_ISCD) : 상품 (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)
|
||||
# 100건씩 조회되면 다음 페이지 조회는 응답메시지 (bsop_date 일자 순으로) 마지막 응답 데이터 일자 +1일 일자 값셋팅(bsop_date)하여 조회
|
||||
rt_data = kb.get_overseas_fuopt_investor_unpd_trend(iscd="CL", dt="20240612", kbn="0", ctskey="1")
|
||||
print(rt_data) # 해외선물 장운영시간
|
||||
|
||||
# [해외선물옵션] 기본시세 > 해외옵션 호가 [해외선물-033] (조회구분 + 옵션종목코드)
|
||||
# dv(조회구분) : 01 현재가, 02 호가
|
||||
rt_data = kb.get_overseas_fuopt_opt_asking_price(dv="02", itm_no="OESU24 C6000")
|
||||
print(rt_data) # 해외옵션 호가
|
||||
355
한국투자증권(API)/legacy/Sample01/kis_auth.py
Normal file
355
한국투자증권(API)/legacy/Sample01/kis_auth.py
Normal file
@@ -0,0 +1,355 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Wed Feb 15 16:57:19 2023
|
||||
|
||||
@author: Administrator
|
||||
"""
|
||||
#====| 토큰 발급에 필요한 API 호출 샘플 아래 참고하시기 바랍니다. |=====================
|
||||
#====| 토큰 발급에 필요한 API 호출 샘플 아래 참고하시기 바랍니다. |=====================
|
||||
#====| 토큰 발급에 필요한 API 호출 샘플 아래 참고하시기 바랍니다. |=====================
|
||||
#====| API 호출 공통 함수 포함 |=====================
|
||||
|
||||
|
||||
import time, copy
|
||||
import yaml
|
||||
import requests
|
||||
import json
|
||||
|
||||
# 웹 소켓 모듈을 선언한다.
|
||||
import asyncio
|
||||
|
||||
import os
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from collections import namedtuple
|
||||
from datetime import datetime
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import unpad
|
||||
from base64 import b64decode
|
||||
|
||||
clearConsole = lambda: os.system('cls' if os.name in ('nt', 'dos') else 'clear')
|
||||
|
||||
key_bytes = 32
|
||||
|
||||
config_root = 'd:\\KIS\\config\\' # 토큰 파일이 저장될 폴더, 제3자가 찾지 어렵도록 경로 설정하시기 바랍니다.
|
||||
#token_tmp = config_root + 'KIS000000' # 토큰 로컬저장시 파일 이름 지정, 파일이름을 토큰값이 유추가능한 파일명은 삼가바랍니다.
|
||||
#token_tmp = config_root + 'KIS' + datetime.today().strftime("%Y%m%d%H%M%S") # 토큰 로컬저장시 파일명 년월일시분초
|
||||
token_tmp = config_root + 'KIS' + datetime.today().strftime("%Y%m%d") # 토큰 로컬저장시 파일명 년월일
|
||||
|
||||
# 접근토큰 관리하는 파일 존재여부 체크, 없으면 생성
|
||||
if os.path.exists(token_tmp) == False:
|
||||
f = open(token_tmp, "w+")
|
||||
|
||||
# 앱키, 앱시크리트, 토큰, 계좌번호 등 저장관리, 자신만의 경로와 파일명으로 설정하시기 바랍니다.
|
||||
# pip install PyYAML (패키지설치)
|
||||
with open(config_root + 'kis_devlp.yaml', encoding='UTF-8') as f:
|
||||
_cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
_TRENV = tuple()
|
||||
_last_auth_time = datetime.now()
|
||||
_autoReAuth = False
|
||||
_DEBUG = False
|
||||
_isPaper = False
|
||||
|
||||
# 기본 헤더값 정의
|
||||
_base_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "text/plain",
|
||||
"charset": "UTF-8",
|
||||
'User-Agent': _cfg['my_agent']
|
||||
}
|
||||
|
||||
|
||||
# 토큰 발급 받아 저장 (토큰값, 토큰 유효시간,1일, 6시간 이내 발급신청시는 기존 토큰값과 동일, 발급시 알림톡 발송)
|
||||
def save_token(my_token, my_expired):
|
||||
valid_date = datetime.strptime(my_expired, '%Y-%m-%d %H:%M:%S')
|
||||
# print('Save token date: ', valid_date)
|
||||
with open(token_tmp, 'w', encoding='utf-8') as f:
|
||||
f.write(f'token: {my_token}\n')
|
||||
f.write(f'valid-date: {valid_date}\n')
|
||||
|
||||
|
||||
# 토큰 확인 (토큰값, 토큰 유효시간_1일, 6시간 이내 발급신청시는 기존 토큰값과 동일, 발급시 알림톡 발송)
|
||||
def read_token():
|
||||
try:
|
||||
# 토큰이 저장된 파일 읽기
|
||||
with open(token_tmp, encoding='UTF-8') as f:
|
||||
tkg_tmp = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
# 토큰 만료 일,시간
|
||||
exp_dt = datetime.strftime(tkg_tmp['valid-date'], '%Y-%m-%d %H:%M:%S')
|
||||
# 현재일자,시간
|
||||
now_dt = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# print('expire dt: ', exp_dt, ' vs now dt:', now_dt)
|
||||
# 저장된 토큰 만료일자 체크 (만료일시 > 현재일시 인경우 보관 토큰 리턴)
|
||||
if exp_dt > now_dt:
|
||||
return tkg_tmp['token']
|
||||
else:
|
||||
# print('Need new token: ', tkg_tmp['valid-date'])
|
||||
return None
|
||||
except Exception as e:
|
||||
# print('read token error: ', e)
|
||||
return None
|
||||
|
||||
# 토큰 유효시간 체크해서 만료된 토큰이면 재발급처리
|
||||
def _getBaseHeader():
|
||||
if _autoReAuth: reAuth()
|
||||
return copy.deepcopy(_base_headers)
|
||||
|
||||
|
||||
# 가져오기 : 앱키, 앱시크리트, 종합계좌번호(계좌번호 중 숫자8자리), 계좌상품코드(계좌번호 중 숫자2자리), 토큰, 도메인
|
||||
def _setTRENV(cfg):
|
||||
nt1 = namedtuple('KISEnv', ['my_app', 'my_sec', 'my_acct', 'my_prod', 'my_token', 'my_url'])
|
||||
d = {
|
||||
'my_app': cfg['my_app'], # 앱키
|
||||
'my_sec': cfg['my_sec'], # 앱시크리트
|
||||
'my_acct': cfg['my_acct'], # 종합계좌번호(8자리)
|
||||
'my_prod': cfg['my_prod'], # 계좌상품코드(2자리)
|
||||
'my_token': cfg['my_token'], # 토큰
|
||||
'my_url': cfg['my_url'] # 실전 도메인 (https://openapi.koreainvestment.com:9443)
|
||||
} # 모의 도메인 (https://openapivts.koreainvestment.com:29443)
|
||||
|
||||
# print(cfg['my_app'])
|
||||
global _TRENV
|
||||
_TRENV = nt1(**d)
|
||||
|
||||
|
||||
def isPaperTrading(): # 모의투자 매매
|
||||
return _isPaper
|
||||
|
||||
|
||||
# 실전투자면 'prod', 모의투자면 'vps'를 셋팅 하시기 바랍니다.
|
||||
def changeTREnv(token_key, svr='prod', product=_cfg['my_prod']):
|
||||
cfg = dict()
|
||||
|
||||
global _isPaper
|
||||
if svr == 'prod': # 실전투자
|
||||
ak1 = 'my_app' # 실전투자용 앱키
|
||||
ak2 = 'my_sec' # 실전투자용 앱시크리트
|
||||
_isPaper = False
|
||||
elif svr == 'vps': # 모의투자
|
||||
ak1 = 'paper_app' # 모의투자용 앱키
|
||||
ak2 = 'paper_sec' # 모의투자용 앱시크리트
|
||||
_isPaper = True
|
||||
|
||||
cfg['my_app'] = _cfg[ak1]
|
||||
cfg['my_sec'] = _cfg[ak2]
|
||||
|
||||
if svr == 'prod' and product == '01': # 실전투자 주식투자, 위탁계좌, 투자계좌
|
||||
cfg['my_acct'] = _cfg['my_acct_stock']
|
||||
elif svr == 'prod' and product == '30': # 실전투자 증권저축계좌
|
||||
cfg['my_acct'] = _cfg['my_acct_stock']
|
||||
elif svr == 'prod' and product == '03': # 실전투자 선물옵션(파생)
|
||||
cfg['my_acct'] = _cfg['my_acct_future']
|
||||
elif svr == 'prod' and product == '08': # 실전투자 해외선물옵션(파생)
|
||||
cfg['my_acct'] = _cfg['my_acct_future']
|
||||
elif svr == 'vps' and product == '01': # 모의투자 주식투자, 위탁계좌, 투자계좌
|
||||
cfg['my_acct'] = _cfg['my_paper_stock']
|
||||
elif svr == 'vps' and product == '03': # 모의투자 선물옵션(파생)
|
||||
cfg['my_acct'] = _cfg['my_paper_future']
|
||||
|
||||
cfg['my_prod'] = product
|
||||
cfg['my_token'] = token_key
|
||||
cfg['my_url'] = _cfg[svr]
|
||||
|
||||
# print(cfg)
|
||||
_setTRENV(cfg)
|
||||
|
||||
|
||||
def _getResultObject(json_data):
|
||||
_tc_ = namedtuple('res', json_data.keys())
|
||||
|
||||
return _tc_(**json_data)
|
||||
|
||||
|
||||
# Token 발급, 유효기간 1일, 6시간 이내 발급시 기존 token값 유지, 발급시 알림톡 무조건 발송
|
||||
# 모의투자인 경우 svr='vps', 투자계좌(01)이 아닌경우 product='XX' 변경하세요 (계좌번호 뒤 2자리)
|
||||
def auth(svr='prod', product=_cfg['my_prod'], url=None):
|
||||
p = {
|
||||
"grant_type": "client_credentials",
|
||||
}
|
||||
# 개인 환경파일 "kis_devlp.yaml" 파일을 참조하여 앱키, 앱시크리트 정보 가져오기
|
||||
# 개인 환경파일명과 위치는 고객님만 아는 위치로 설정 바랍니다.
|
||||
if svr == 'prod': # 실전투자
|
||||
ak1 = 'my_app' # 앱키 (실전투자용)
|
||||
ak2 = 'my_sec' # 앱시크리트 (실전투자용)
|
||||
elif svr == 'vps': # 모의투자
|
||||
ak1 = 'paper_app' # 앱키 (모의투자용)
|
||||
ak2 = 'paper_sec' # 앱시크리트 (모의투자용)
|
||||
|
||||
# 앱키, 앱시크리트 가져오기
|
||||
p["appkey"] = _cfg[ak1]
|
||||
p["appsecret"] = _cfg[ak2]
|
||||
|
||||
# 기존 발급된 토큰이 있는지 확인
|
||||
saved_token = read_token() # 기존 발급 토큰 확인
|
||||
# print("saved_token: ", saved_token)
|
||||
if saved_token is None: # 기존 발급 토큰 확인이 안되면 발급처리
|
||||
url = f'{_cfg[svr]}/oauth2/tokenP'
|
||||
res = requests.post(url, data=json.dumps(p), headers=_getBaseHeader()) # 토큰 발급
|
||||
rescode = res.status_code
|
||||
if rescode == 200: # 토큰 정상 발급
|
||||
my_token = _getResultObject(res.json()).access_token # 토큰값 가져오기
|
||||
my_expired= _getResultObject(res.json()).access_token_token_expired # 토큰값 만료일시 가져오기
|
||||
save_token(my_token, my_expired) # 새로 발급 받은 토큰 저장
|
||||
else:
|
||||
print('Get Authentification token fail!\nYou have to restart your app!!!')
|
||||
return
|
||||
else:
|
||||
my_token = saved_token # 기존 발급 토큰 확인되어 기존 토큰 사용
|
||||
|
||||
# 발급토큰 정보 포함해서 헤더값 저장 관리, API 호출시 필요
|
||||
changeTREnv(f"Bearer {my_token}", svr, product)
|
||||
|
||||
_base_headers["authorization"] = _TRENV.my_token
|
||||
_base_headers["appkey"] = _TRENV.my_app
|
||||
_base_headers["appsecret"] = _TRENV.my_sec
|
||||
|
||||
global _last_auth_time
|
||||
_last_auth_time = datetime.now()
|
||||
|
||||
if (_DEBUG):
|
||||
print(f'[{_last_auth_time}] => get AUTH Key completed!')
|
||||
|
||||
|
||||
# end of initialize, 토큰 재발급, 토큰 발급시 유효시간 1일
|
||||
# 프로그램 실행시 _last_auth_time에 저장하여 유효시간 체크, 유효시간 만료시 토큰 발급 처리
|
||||
def reAuth(svr='prod', product=_cfg['my_prod']):
|
||||
n2 = datetime.now()
|
||||
if (n2 - _last_auth_time).seconds >= 86400: # 유효시간 1일
|
||||
auth(svr, product)
|
||||
|
||||
|
||||
def getEnv():
|
||||
return _cfg
|
||||
|
||||
|
||||
def getTREnv():
|
||||
return _TRENV
|
||||
|
||||
# 주문 API에서 사용할 hash key값을 받아 header에 설정해 주는 함수
|
||||
# 현재는 hash key 필수 사항아님, 생략가능, API 호출과정에서 변조 우려를 하는 경우 사용
|
||||
# Input: HTTP Header, HTTP post param
|
||||
# Output: None
|
||||
def set_order_hash_key(h, p):
|
||||
url = f"{getTREnv().my_url}/uapi/hashkey" # hashkey 발급 API URL
|
||||
|
||||
res = requests.post(url, data=json.dumps(p), headers=h)
|
||||
rescode = res.status_code
|
||||
if rescode == 200:
|
||||
h['hashkey'] = _getResultObject(res.json()).HASH
|
||||
else:
|
||||
print("Error:", rescode)
|
||||
|
||||
|
||||
# API 호출 응답에 필요한 처리 공통 함수
|
||||
class APIResp:
|
||||
def __init__(self, resp):
|
||||
self._rescode = resp.status_code
|
||||
self._resp = resp
|
||||
self._header = self._setHeader()
|
||||
self._body = self._setBody()
|
||||
self._err_code = self._body.msg_cd
|
||||
self._err_message = self._body.msg1
|
||||
|
||||
def getResCode(self):
|
||||
return self._rescode
|
||||
|
||||
def _setHeader(self):
|
||||
fld = dict()
|
||||
for x in self._resp.headers.keys():
|
||||
if x.islower():
|
||||
fld[x] = self._resp.headers.get(x)
|
||||
_th_ = namedtuple('header', fld.keys())
|
||||
|
||||
return _th_(**fld)
|
||||
|
||||
def _setBody(self):
|
||||
_tb_ = namedtuple('body', self._resp.json().keys())
|
||||
|
||||
return _tb_(**self._resp.json())
|
||||
|
||||
def getHeader(self):
|
||||
return self._header
|
||||
|
||||
def getBody(self):
|
||||
return self._body
|
||||
|
||||
def getResponse(self):
|
||||
return self._resp
|
||||
|
||||
def isOK(self):
|
||||
try:
|
||||
if (self.getBody().rt_cd == '0'):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def getErrorCode(self):
|
||||
return self._err_code
|
||||
|
||||
def getErrorMessage(self):
|
||||
return self._err_message
|
||||
|
||||
def printAll(self):
|
||||
print("<Header>")
|
||||
for x in self.getHeader()._fields:
|
||||
print(f'\t-{x}: {getattr(self.getHeader(), x)}')
|
||||
print("<Body>")
|
||||
for x in self.getBody()._fields:
|
||||
print(f'\t-{x}: {getattr(self.getBody(), x)}')
|
||||
|
||||
def printError(self, url):
|
||||
print('-------------------------------\nError in response: ', self.getResCode(), ' url=', url)
|
||||
print('rt_cd : ', self.getBody().rt_cd, '/ msg_cd : ',self.getErrorCode(), '/ msg1 : ',self.getErrorMessage())
|
||||
print('-------------------------------')
|
||||
|
||||
# end of class APIResp
|
||||
|
||||
########### API call wrapping : API 호출 공통
|
||||
|
||||
def _url_fetch(api_url, ptr_id, tr_cont, params, appendHeaders=None, postFlag=False, hashFlag=True):
|
||||
url = f"{getTREnv().my_url}{api_url}"
|
||||
|
||||
headers = _getBaseHeader() # 기본 header 값 정리
|
||||
|
||||
# 추가 Header 설정
|
||||
tr_id = ptr_id
|
||||
if ptr_id[0] in ('T', 'J', 'C'): # 실전투자용 TR id 체크
|
||||
if isPaperTrading(): # 모의투자용 TR id 식별
|
||||
tr_id = 'V' + ptr_id[1:]
|
||||
|
||||
headers["tr_id"] = tr_id # 트랜젝션 TR id
|
||||
headers["custtype"] = "P" # 일반(개인고객,법인고객) "P", 제휴사 "B"
|
||||
headers["tr_cont"] = tr_cont # 트랜젝션 TR id
|
||||
|
||||
|
||||
if appendHeaders is not None:
|
||||
if len(appendHeaders) > 0:
|
||||
for x in appendHeaders.keys():
|
||||
headers[x] = appendHeaders.get(x)
|
||||
|
||||
if (_DEBUG):
|
||||
print("< Sending Info >")
|
||||
print(f"URL: {url}, TR: {tr_id}")
|
||||
print(f"<header>\n{headers}")
|
||||
print(f"<body>\n{params}")
|
||||
|
||||
if (postFlag):
|
||||
#if (hashFlag): set_order_hash_key(headers, params)
|
||||
res = requests.post(url, headers=headers, data=json.dumps(params))
|
||||
else:
|
||||
res = requests.get(url, headers=headers, params=params)
|
||||
|
||||
if res.status_code == 200:
|
||||
ar = APIResp(res)
|
||||
if (_DEBUG): ar.printAll()
|
||||
return ar
|
||||
else:
|
||||
print("Error Code : " + str(res.status_code) + " | " + res.text)
|
||||
return None
|
||||
67
한국투자증권(API)/legacy/Sample01/kis_devlp.yaml
Normal file
67
한국투자증권(API)/legacy/Sample01/kis_devlp.yaml
Normal file
@@ -0,0 +1,67 @@
|
||||
#====| 사용자 환경 샘플 아래 참고하시기 바랍니다. |======================
|
||||
#====| 본 샘플은 토큰 발급 후 파일 저장 방식이므로 보안강화를 위해 메모리 방식 등 사용자 원하시는 방식으로 구현하시기 바랍니다. |=====
|
||||
|
||||
#====| kis_auth.py에서 환경파일 위치를 사용자가 정하시기 바랍니다. . 2024.05.16 KIS Developers Team |======================
|
||||
#====| kis_auth.py에서 환경파일 위치를 사용자가 정하시기 바랍니다. . 2024.05.16 KIS Developers Team |======================
|
||||
#====| kis_auth.py에서 환경파일 위치를 사용자가 정하시기 바랍니다. . 2024.05.16 KIS Developers Team |======================
|
||||
#
|
||||
############################### kis_auth.py ##################################################
|
||||
# clearConsole = lambda: os.system('cls' if os.name in ('nt', 'dos') else 'clear')
|
||||
#
|
||||
# key_bytes = 32
|
||||
#
|
||||
# config_root = 'd:\\KIS\\config\\' # 토큰 파일이 저장될 폴더, 제3자가 찾지 어렵도록 경로 설정하시기 바랍니다. <<<==== 파일 위치 지정
|
||||
# #token_tmp = config_root + 'KIS000000' # 토큰 로컬저장시 파일 이름 지정, 파일이름을 토큰값이 유추가능한 파일명은 삼가바랍니다. <<<==== 토큰 저장 파일 방식
|
||||
# #token_tmp = config_root + 'KIS' + datetime.today().strftime("%Y%m%d%H%M%S") # 토큰 로컬저장시 파일명 년월일시분초
|
||||
# token_tmp = config_root + 'KIS' + datetime.today().strftime("%Y%m%d") # 토큰 로컬저장시 파일명 년월일
|
||||
#
|
||||
# # 접근토큰 관리하는 파일 존재여부 체크, 없으면 생성
|
||||
# if os.path.exists(token_tmp) == False:
|
||||
# f = open(token_tmp, "w+")
|
||||
#
|
||||
# # 앱키, 앱시크리트, 토큰, 계좌번호 등 저장관리, 자신만의 경로와 파일명으로 설정하시기 바랍니다.
|
||||
# # pip install PyYAML (패키지설치)
|
||||
# with open(config_root + 'kis_devlp.yaml', encoding='UTF-8') as f:
|
||||
# _cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
#
|
||||
# _TRENV = tuple()
|
||||
# _last_auth_time = datetime.now()
|
||||
# _autoReAuth = False
|
||||
# _DEBUG = False
|
||||
# _isPaper = False
|
||||
#
|
||||
# 기본 헤더값 정의
|
||||
# _base_headers = {
|
||||
# "Content-Type": "application/json",
|
||||
# "Accept": "text/plain",
|
||||
# "charset": "UTF-8",
|
||||
# 'User-Agent': _cfg['my_agent']
|
||||
# }
|
||||
######################################################################################################
|
||||
|
||||
#홈페이지에서 API서비스 신청시 받은 Appkey, Appsecret 값 설정
|
||||
#모의투자
|
||||
#my_app: "앱키"
|
||||
#my_sec: "앱시크리트"
|
||||
#실전투자
|
||||
my_app: "앱키"
|
||||
my_sec: "앱시크리트"
|
||||
|
||||
|
||||
#계좌번호 앞 8자리
|
||||
my_acct: "계좌번호 8자리"
|
||||
my_acct_stock: "증권계좌 8자리"
|
||||
my_acct_future: "선물옵션계좌 8자리"
|
||||
#계좌번호 뒤 2자리
|
||||
my_prod: "01"
|
||||
#my_prod: "03"
|
||||
|
||||
#실전투자
|
||||
prod: "https://openapi.koreainvestment.com:9443"
|
||||
#모의투자
|
||||
vps: "https://openapivts.koreainvestment.com:29443"
|
||||
|
||||
#디스코드 웹훅 URL
|
||||
DISCORD_WEBHOOK_URL: ""
|
||||
|
||||
my_agent : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
|
||||
366
한국투자증권(API)/legacy/Sample01/kis_domfuopt.py
Normal file
366
한국투자증권(API)/legacy/Sample01/kis_domfuopt.py
Normal file
@@ -0,0 +1,366 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Wed Feb 15 16:57:19 2023
|
||||
|
||||
@author: Administrator
|
||||
"""
|
||||
import kis_auth as kis
|
||||
|
||||
import time, copy
|
||||
import requests
|
||||
import json
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from collections import namedtuple
|
||||
from datetime import datetime
|
||||
from pandas import DataFrame
|
||||
|
||||
#====| [국내선물옵션] 주문/계좌 |===========================================================================================================================
|
||||
|
||||
##############################################################################################
|
||||
# [국내선물옵션] 주문/계좌 > 선물옵션 주문[v1_국내선물-001]
|
||||
##############################################################################################
|
||||
# Input: None (Option) 상세 Input값 변경이 필요한 경우 API문서 참조
|
||||
# Output: DataFrame (Option) output API 문서 참조 등
|
||||
def get_domfuopt_order(dv_cd="02", sll_buy_dvsn_cd="", dvsn_cd="01", itm_no="", qty=0, unpr=0, tr_cont="", FK100="", NK100="", dataframe=None): # [국내선물옵션] 주문/계좌 > 선물옵션 주문
|
||||
url = '/uapi/domestic-futureoption/v1/trading/order'
|
||||
|
||||
if dv_cd == "01":
|
||||
tr_id = "TTTO1101U" # 선물 옵션 매수 매도 주문 주간 [모의투자] VTTO1101U : 선물 옵션 매수 매도 주문 주간
|
||||
elif dv_cd == "02":
|
||||
tr_id = "JTCE1001U" # 선물 옵션 매수 매도 주문 야간 [모의투자] VTCE1001U : 선물 옵션 매수 매도 주문 야간
|
||||
else:
|
||||
print("선물옵션매수매도주문주간/선물옵션매수매도주문야간 구분 확인요망!!!")
|
||||
return None
|
||||
|
||||
if sll_buy_dvsn_cd == "":
|
||||
print("매도매수구분코드 확인요망!!!")
|
||||
return None
|
||||
|
||||
if itm_no == "":
|
||||
print("주문종목번호 확인요망!!!")
|
||||
return None
|
||||
|
||||
if qty == 0:
|
||||
print("주문수량 확인요망!!!")
|
||||
return None
|
||||
|
||||
#if unpr == 0:
|
||||
# print("주문단가 확인요망!!!")
|
||||
# return None
|
||||
|
||||
params = {
|
||||
"ORD_PRCS_DVSN_CD": "02", # 주문처리구분코드 02 : 주문전송
|
||||
"CANO": kis.getTREnv().my_acct, # 종합계좌번호 8자리
|
||||
"ACNT_PRDT_CD": kis.getTREnv().my_prod, # 계좌상품코드 2자리
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd, # 매수매도구분코드
|
||||
"SHTN_PDNO": itm_no, # 종목코드(단축상품번호 6자리) 선물 6자리 (예: 101S03) 옵션 9자리 (예: 201S03370)
|
||||
"ORD_QTY": str(int(qty)), # 주문수량
|
||||
"UNIT_PRICE": str(int(unpr)), # 주문가격
|
||||
"NMPR_TYPE_CD": "", # 호가유형코드 ※ ORD_DVSN_CD(주문구분코드)를 입력한 경우 ""(공란)으로 입력해도 됨
|
||||
"KRX_NMPR_CNDT_CD": "", # 한국거래소호가조건코드 ※ ORD_DVSN_CD(주문구분코드)를 입력한 경우 ""(공란)으로 입력해도 됨
|
||||
"CTAC_TLNO": "", # 연락전화번호
|
||||
"FUOP_ITEM_DVSN_CD": "", # 선물옵션종목구분코드
|
||||
"ORD_DVSN_CD": dvsn_cd # 주문구분코드 01 : 지정가 02 : 시장가 03 : 조건부 04 : 최유리 10 : 지정가(IOC) .....
|
||||
}
|
||||
|
||||
res = kis._url_fetch(url, tr_id, tr_cont, params, postFlag=True)
|
||||
if str(res.getBody().rt_cd) == "0":
|
||||
current_data = pd.DataFrame(res.getBody().output, index=[0])
|
||||
dataframe = current_data
|
||||
else:
|
||||
print(res.getBody().msg_cd + "," + res.getBody().msg1)
|
||||
dataframe = None
|
||||
|
||||
return dataframe
|
||||
|
||||
|
||||
##############################################################################################
|
||||
# [국내선물옵션] 주문/계좌 > 선물옵션 정정취소주문[v1_국내선물-002]
|
||||
##############################################################################################
|
||||
# Input: None (Option) 상세 Input값 변경이 필요한 경우 API문서 참조
|
||||
# Output: DataFrame (Option) output API 문서 참조 등
|
||||
def get_domfuopt_order_rvsecncl(dv_cd="01", rvse_cncl_dvsn_cd="", orgn_odno="", ord_dvsn="01", ord_qty=0,
|
||||
ord_unpr=0, rmn_qty_yn="", tr_cont="", dataframe=None): # [국내선물옵션] 선물옵션 정정취소주문[v1_국내선물-002]
|
||||
url = '/uapi/domestic-futureoption/v1/trading/order-rvsecncl'
|
||||
|
||||
if dv_cd == "01":
|
||||
tr_id = "TTTO1103U" # 선물 옵션 정정 취소 주문 주간 [모의투자] VTTO1103U : 선물 옵션 정정 취소 주문 주간
|
||||
elif dv_cd == "02":
|
||||
tr_id = "JTCE1002U" # 선물 옵션 정정 취소 주문 야간 [모의투자] VTCE1002U : 선물 옵션 정정 취소 주문 야간
|
||||
else:
|
||||
print("선물옵션정정취소주문주간/선물옵션정정취소주문야간 구분 확인요망!!!")
|
||||
return None
|
||||
|
||||
if not rvse_cncl_dvsn_cd in ["01","02"]:
|
||||
print("정정취소구분코드 확인요망!!!") # 정정:01. 취소:02
|
||||
return None
|
||||
|
||||
if orgn_odno == "":
|
||||
print("원주문번호 확인요망!!!")
|
||||
return None
|
||||
|
||||
if ord_dvsn == "":
|
||||
print("주문구분 확인요망!!!")
|
||||
return None
|
||||
|
||||
if rmn_qty_yn == "Y" and ord_qty > 0:
|
||||
print("잔량전부 취소/정정주문인 경우 주문수량 0 처리!!!")
|
||||
ord_qty = 0
|
||||
|
||||
if rmn_qty_yn == "N" and ord_qty == 0:
|
||||
print("취소/정정 수량 확인요망!!!")
|
||||
return None
|
||||
|
||||
if rvse_cncl_dvsn_cd == "01" and ord_unpr == 0:
|
||||
print("주문단가 확인요망!!!")
|
||||
return None
|
||||
|
||||
params = {
|
||||
"ORD_PRCS_DVSN_CD": "02", # 주문처리구분코드 02 : 주문전송
|
||||
"CANO": kis.getTREnv().my_acct, # 종합계좌번호 8자리
|
||||
"ACNT_PRDT_CD": kis.getTREnv().my_prod, # 계좌상품코드 2자리
|
||||
"RVSE_CNCL_DVSN_CD": rvse_cncl_dvsn_cd, # 정정취소구분코드 01 : 정정 02 : 취소
|
||||
"ORGN_ODNO": orgn_odno, # 원주문번호 정정 혹은 취소할 주문의 번호
|
||||
"ORD_QTY": str(int(ord_qty)), # 주문수량 전량일 경우 0으로 입력, 일부수량 정정 및 취소 불가, 주문수량 반드시 입력 (공백 불가) 일부 미체결 시 잔량 전체에 대해서 취소 가능
|
||||
"UNIT_PRICE": str(int(ord_unpr)), # 주문가격 시장가나 최유리의 경우 0으로 입력 (취소 시에도 0 입력)
|
||||
"NMPR_TYPE_CD": "", # 호가유형코드 ※ ORD_DVSN_CD(주문구분코드)를 입력한 경우 ""(공란)으로 입력해도 됨
|
||||
"KRX_NMPR_CNDT_CD": "", # 한국거래소호가조건코드 ※ ORD_DVSN_CD(주문구분코드)를 입력한 경우 ""(공란)으로 입력해도 됨
|
||||
"RMN_QTY_YN": rmn_qty_yn, # 잔여수량여부 Y : 전량 N : 일부
|
||||
"FUOP_ITEM_DVSN_CD": "", # 선물옵션종목구분코드 (주간) 공란 (야간) 01:선물 02:콜옵션 03:풋옵션 04:스프레드
|
||||
"ORD_DVSN_CD": ord_dvsn # 주문구분코드 (취소) 01, (정정) 01 : 지정가 02 : 시장가 03 : 조건부 04 : 최유리 10 : 지정가(IOC) .....
|
||||
}
|
||||
|
||||
res = kis._url_fetch(url, tr_id, tr_cont, params, postFlag=True)
|
||||
|
||||
if str(res.getBody().rt_cd) == "0":
|
||||
current_data = pd.DataFrame(res.getBody().output, index=[0])
|
||||
dataframe = current_data
|
||||
else:
|
||||
print(res.getBody().msg_cd + "," + res.getBody().msg1)
|
||||
#print(res.getErrorCode() + "," + res.getErrorMessage())
|
||||
dataframe = None
|
||||
|
||||
return dataframe
|
||||
|
||||
##############################################################################################
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 주문체결 내역조회 [국내선물-009]
|
||||
##############################################################################################
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 주문체결 내역조회 [국내선물-009] Object를 DataFrame 으로 반환
|
||||
# Input: None (Option) 상세 Input값 변경이 필요한 경우 API문서 참조
|
||||
# (JTCE5005R)
|
||||
# Output: DataFrame (Option) output2 API 문서 참조 등
|
||||
def get_domfuopt_inquire_ngt_ccnl_obj(inqr_strt_dt=None, inqr_end_dt=None, sll_buy_dvsn_cd="00", tr_cont="", FK100="", NK100="", dataframe=None):
|
||||
url = '/uapi/domestic-futureoption/v1/trading/inquire-ngt-ccnl'
|
||||
tr_id = "JTCE5005R"
|
||||
|
||||
if inqr_strt_dt is None:
|
||||
inqr_strt_dt = datetime.today().strftime("%Y%m%d") # 시작일자 값이 없으면 현재일자
|
||||
if inqr_end_dt is None:
|
||||
inqr_end_dt = datetime.today().strftime("%Y%m%d") # 종료일자 값이 없으면 현재일자
|
||||
|
||||
params = {
|
||||
"CANO": kis.getTREnv().my_acct, # 종합계좌번호 8자리
|
||||
"ACNT_PRDT_CD": kis.getTREnv().my_prod, # 계좌상품코드 2자리
|
||||
"STRT_ORD_DT": inqr_strt_dt, # 시작주문일자
|
||||
"END_ORD_DT": inqr_end_dt, # 종료주문일자
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd, # 매도매수구분코드 00:전체 01:매도, 02:매수
|
||||
"CCLD_NCCS_DVSN": "01", # 체결미체결구분
|
||||
"SORT_SQN": "", # 정렬순서 DS : 정순, 그외 : 역순
|
||||
"STRT_ODNO": "", # 시작주문번호 00:전체, 01:체결, 02:미체결
|
||||
"PDNO": "", # 상품번호 (종목번호)
|
||||
"MKET_ID_CD": "", # 시장ID코드
|
||||
"FUOP_DVSN_CD": "", # 선물옵션구분코드 공란 : 전체, 01 : 선물, 02 : 옵션
|
||||
"SCRN_DVSN": "02", # 화면구분 02 default
|
||||
"CTX_AREA_FK200": FK100, # 공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK100 값 : 다음페이지 조회시(2번째부터)
|
||||
"CTX_AREA_NK200": NK100 # 공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK100 값 : 다음페이지 조회시(2번째부터)
|
||||
}
|
||||
|
||||
res = kis._url_fetch(url, tr_id, tr_cont, params)
|
||||
|
||||
# print(res.getBody())
|
||||
if res.isOK():
|
||||
# API 응답의 output 속성이 스칼라 값인지 확인
|
||||
output_data = res.getBody().output2
|
||||
if not isinstance(output_data, list):
|
||||
# 스칼라 값이면 리스트로 감싸서 반환
|
||||
output_data = [output_data]
|
||||
|
||||
# DataFrame 생성 시 index 매개변수를 추가하여 스칼라 값일 경우 처리
|
||||
current_data = pd.DataFrame(output_data, index=[0])
|
||||
|
||||
return current_data
|
||||
|
||||
else:
|
||||
res.printError()
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 주문체결 내역조회 [국내선물-009] List를 DataFrame 으로 반환
|
||||
# Input: None (Option) 상세 Input값 변경이 필요한 경우 API문서 참조
|
||||
# (JTCE5005R)
|
||||
# Output: DataFrame (Option) output1 API 문서 참조 등
|
||||
def get_domfuopt_inquire_ngt_ccnl_lst(inqr_strt_dt=None, inqr_end_dt=None, sll_buy_dvsn_cd="00", tr_cont="", FK100="", NK100="", dataframe=None):
|
||||
url = '/uapi/domestic-futureoption/v1/trading/inquire-ngt-ccnl'
|
||||
tr_id = "JTCE5005R"
|
||||
|
||||
if inqr_strt_dt == "":
|
||||
inqr_strt_dt = datetime.today().strftime("%Y%m%d") # 시작일자 값이 없으면 현재일자
|
||||
if inqr_end_dt == "":
|
||||
inqr_end_dt = datetime.today().strftime("%Y%m%d") # 종료일자 값이 없으면 현재일자
|
||||
|
||||
params = {
|
||||
"CANO": kis.getTREnv().my_acct, # 종합계좌번호 8자리
|
||||
"ACNT_PRDT_CD": kis.getTREnv().my_prod, # 계좌상품코드 2자리
|
||||
"STRT_ORD_DT": inqr_strt_dt, # 시작주문일자
|
||||
"END_ORD_DT": inqr_end_dt, # 종료주문일자
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd, # 매도매수구분코드 00:전체 01:매도, 02:매수
|
||||
"CCLD_NCCS_DVSN": "00", # 체결미체결구분
|
||||
"SORT_SQN": "DS", # 정렬순서 DS : 정순, 그외 : 역순
|
||||
"STRT_ODNO": "", # 시작주문번호 00:전체, 01:체결, 02:미체결
|
||||
"PDNO": "", # 상품번호 (종목번호)
|
||||
"MKET_ID_CD": "", # 시장ID코드
|
||||
"FUOP_DVSN_CD": "", # 선물옵션구분코드 공란 : 전체, 01 : 선물, 02 : 옵션
|
||||
"SCRN_DVSN": "02", # 화면구분 02 default
|
||||
"CTX_AREA_FK200": FK100, # 공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK100 값 : 다음페이지 조회시(2번째부터)
|
||||
"CTX_AREA_NK200": NK100 # 공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK100 값 : 다음페이지 조회시(2번째부터)
|
||||
}
|
||||
res = kis._url_fetch(url, tr_id, tr_cont, params) # API 호출, kis_auth.py에 존재
|
||||
|
||||
# Assuming 'output1' is a dictionary that you want to convert to a DataFrame
|
||||
current_data = pd.DataFrame(res.getBody().output1)
|
||||
|
||||
# Append to the existing DataFrame if it exists
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True) #
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont, FK100, NK100 = res.getHeader().tr_cont, res.getBody().ctx_area_fk200, res.getBody().ctx_area_nk200 # 페이징 처리 getHeader(), getBody() kis_auth.py 존재
|
||||
# print(dv, tr_cont, FK100, NK100)
|
||||
|
||||
if tr_cont == "D" or tr_cont == "E": # 마지막 페이지
|
||||
print("The End")
|
||||
return dataframe
|
||||
elif tr_cont == "F" or tr_cont == "M": # 다음 페이지 존재하는 경우 자기 호출 처리
|
||||
print('Call Next')
|
||||
time.sleep(0.1) # 시스템 안정적 운영을 위하여 반드시 지연 time 필요
|
||||
tr_cont = "N" # 다음조회
|
||||
return get_domfuopt_inquire_ngt_ccnl_lst(inqr_strt_dt, inqr_end_dt, sll_buy_dvsn_cd, "N", FK100, NK100, dataframe)
|
||||
|
||||
|
||||
##############################################################################################
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 잔고현황 [국내선물-010]
|
||||
##############################################################################################
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 잔고현황 Object를 DataFrame 으로 반환
|
||||
# Input: None (Option) 상세 Input값 변경이 필요한 경우 API문서 참조
|
||||
# Output: DataFrame (Option) output2 -
|
||||
def get_domfuopt_inquire_ngt_balance_obj(tr_cont="", FK100="", NK100="", dataframe=None):
|
||||
url = '/uapi/domestic-futureoption/v1/trading/inquire-ngt-balance'
|
||||
tr_id = "JTCE6001R"
|
||||
|
||||
params = {
|
||||
"CANO": kis.getTREnv().my_acct, # 종합계좌번호 8자리
|
||||
"ACNT_PRDT_CD": kis.getTREnv().my_prod, # 계좌상품코드 2자리
|
||||
"MGNA_DVSN": "01", # 증거금구분 01 : 개시, 02 : 유지
|
||||
"EXCC_STAT_CD": "1", # 정산상태코드 1 : 정산 (정산가격으로 잔고 조회) 2 : 본정산 (매입가격으로 잔고 조회)
|
||||
"CTX_AREA_FK200": FK100, # 공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK100 값 : 다음페이지 조회시(2번째부터)
|
||||
"CTX_AREA_NK200": NK100 # 공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK100 값 : 다음페이지 조회시(2번째부터)
|
||||
}
|
||||
res = kis._url_fetch(url, tr_id, tr_cont, params)
|
||||
|
||||
# print(res.getBody())
|
||||
if res.isOK():
|
||||
# API 응답의 output 속성이 스칼라 값인지 확인
|
||||
output_data = res.getBody().output2
|
||||
if not isinstance(output_data, list):
|
||||
# 스칼라 값이면 리스트로 감싸서 반환
|
||||
output_data = [output_data]
|
||||
|
||||
# DataFrame 생성 시 index 매개변수를 추가하여 스칼라 값일 경우 처리
|
||||
current_data = pd.DataFrame(output_data, index=[0])
|
||||
|
||||
return current_data
|
||||
|
||||
else:
|
||||
res.printError()
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 잔고현황 종목별 List를 DataFrame 으로 반환
|
||||
# Input: None (Option) 상세 Input값 변경이 필요한 경우 API문서 참조
|
||||
# Output: DataFrame (Option) output2 - 종목번호, 상품명(종목명), 매매구분명(매수매도구분), 전일매수수량 ... 등
|
||||
def get_domfuopt_inquire_ngt_balance_lst(tr_cont="", FK100="", NK100="", dataframe=None): # 국내주식주문 > 주식잔고조회(현재종목별 잔고)
|
||||
url = '/uapi/domestic-futureoption/v1/trading/inquire-ngt-balance'
|
||||
tr_id = "JTCE6001R"
|
||||
|
||||
params = {
|
||||
"CANO": kis.getTREnv().my_acct, # 종합계좌번호 8자리
|
||||
"ACNT_PRDT_CD": kis.getTREnv().my_prod, # 계좌상품코드 2자리
|
||||
"MGNA_DVSN": "01", # 증거금구분 01 : 개시, 02 : 유지
|
||||
"EXCC_STAT_CD": "1", # 정산상태코드 1 : 정산 (정산가격으로 잔고 조회) 2 : 본정산 (매입가격으로 잔고 조회)
|
||||
"CTX_AREA_FK200": FK100, # 공란 : 최초 조회시 이전 조회 Output CTX_AREA_FK100 값 : 다음페이지 조회시(2번째부터)
|
||||
"CTX_AREA_NK200": NK100 # 공란 : 최초 조회시 이전 조회 Output CTX_AREA_NK100 값 : 다음페이지 조회시(2번째부터)
|
||||
}
|
||||
res = kis._url_fetch(url, tr_id, tr_cont, params) # API 호출, kis_auth.py에 존재
|
||||
|
||||
print(res.getBody())
|
||||
# Assuming 'output1' is a dictionary that you want to convert to a DataFrame
|
||||
current_data = pd.DataFrame(res.getBody().output1)
|
||||
|
||||
# Append to the existing DataFrame if it exists
|
||||
if dataframe is not None:
|
||||
dataframe = pd.concat([dataframe, current_data], ignore_index=True) #
|
||||
else:
|
||||
dataframe = current_data
|
||||
|
||||
tr_cont, FK100, NK100 = res.getHeader().tr_cont, res.getBody().ctx_area_fk200, res.getBody().ctx_area_nk200 # 페이징 처리 getHeader(), getBody() kis_auth.py 존재
|
||||
|
||||
if tr_cont == "D" or tr_cont == "E": # 마지막 페이지
|
||||
print("The End")
|
||||
return dataframe
|
||||
elif tr_cont == "F" or tr_cont == "M": # 다음 페이지 존재하는 경우 자기 호출 처리
|
||||
print('Call Next')
|
||||
time.sleep(0.1) # 시스템 안정적 운영을 위하여 반드시 지연 time 필요
|
||||
return get_domfuopt_inquire_ngt_balance_lst("N", FK100, NK100, dataframe)
|
||||
|
||||
|
||||
##############################################################################################
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 주문가능 조회 [국내선물-011]
|
||||
##############################################################################################
|
||||
# [국내선물옵션] 주문/계좌 > (야간)선물옵션 주문가능 조회 [국내선물-011] List를 DataFrame 으로 반환
|
||||
# Input: None (Option) 상세 Input값 변경이 필요한 경우 API문서 참조
|
||||
# Output: DataFrame (Option) output2 -
|
||||
def get_domfuopt_inquire_psbl_ngt_order(pdno="", sll_buy_dvsn_cd="", ord_unpr=0, ord_dvsn="01", tr_cont="", FK100="", NK100="", dataframe=None): # 국내주식주문 > 매수가능조회
|
||||
url = '/uapi/domestic-futureoption/v1/trading/inquire-psbl-ngt-order'
|
||||
tr_id = "JTCE1004R"
|
||||
|
||||
params = {
|
||||
"CANO": kis.getTREnv().my_acct, # 종합계좌번호 8자리
|
||||
"ACNT_PRDT_CD": kis.getTREnv().my_prod, # 계좌상품코드 2자리
|
||||
"PDNO": pdno, # 상품번호
|
||||
"PRDT_TYPE_CD": "301", # 상품유형코드 301 : 선물옵션
|
||||
"SLL_BUY_DVSN_CD": sll_buy_dvsn_cd, # 매도매수구분코드 01 : 매도 , 02 : 매수
|
||||
"UNIT_PRICE": ord_unpr, # 주문가격
|
||||
"ORD_DVSN_CD": ord_dvsn # 주문구분코드 01 : 지정가 02 : 시장가 03 : 조건부 04 : 최유리 10 : 지정가(IOC) .....
|
||||
}
|
||||
res = kis._url_fetch(url, tr_id, tr_cont, params) # API 호출, kis_auth.py에 존재
|
||||
|
||||
print(res.getBody())
|
||||
if res.isOK():
|
||||
# API 응답의 output 속성이 스칼라 값인지 확인
|
||||
output_data = res.getBody().output
|
||||
if not isinstance(output_data, list):
|
||||
# 스칼라 값이면 리스트로 감싸서 반환
|
||||
output_data = [output_data]
|
||||
|
||||
# DataFrame 생성 시 index 매개변수를 추가하여 스칼라 값일 경우 처리
|
||||
current_data = pd.DataFrame(output_data, index=[0])
|
||||
|
||||
return current_data
|
||||
|
||||
else:
|
||||
res.printError()
|
||||
return pd.DataFrame()
|
||||
1357
한국투자증권(API)/legacy/Sample01/kis_domstk.py
Normal file
1357
한국투자증권(API)/legacy/Sample01/kis_domstk.py
Normal file
File diff suppressed because it is too large
Load Diff
487
한국투자증권(API)/legacy/Sample01/kis_domstk_ws.py
Normal file
487
한국투자증권(API)/legacy/Sample01/kis_domstk_ws.py
Normal file
@@ -0,0 +1,487 @@
|
||||
# 국내주식 실시간 websocket sample
|
||||
import websocket
|
||||
|
||||
import kis_auth as ka
|
||||
import kis_domstk as kb
|
||||
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from io import StringIO
|
||||
from threading import Thread
|
||||
|
||||
from collections import namedtuple, deque
|
||||
|
||||
try:
|
||||
import websockets
|
||||
|
||||
except ImportError:
|
||||
print("websocket-client 설치중입니다.")
|
||||
os.system('python3 -m pip3 install websocket-client')
|
||||
|
||||
from enum import StrEnum
|
||||
|
||||
|
||||
class KIS_WSReq(StrEnum):
|
||||
BID_ASK = 'H0STASP0' # 실시간 국내주식 호가
|
||||
CONTRACT = 'H0STCNT0' # 실시간 국내주식 체결
|
||||
NOTICE = 'H0STCNI0' # 실시간 계좌체결발생통보
|
||||
|
||||
|
||||
import talib as ta
|
||||
|
||||
|
||||
class BasicPlan:
|
||||
|
||||
def __init__(self, stock_code, window=20):
|
||||
self._stock_code = stock_code
|
||||
self._queue = deque(maxlen=window)
|
||||
self._prev_ma = None
|
||||
|
||||
def push(self, value):
|
||||
self._queue.append(value)
|
||||
ma = sum(self._queue) / len(self._queue)
|
||||
diff = ma - self._prev_ma if self._prev_ma is not None else None
|
||||
self._prev_ma = ma
|
||||
|
||||
print(f"{self._stock_code}****** value: {value}, MA: {ma}, diff: {diff}...")
|
||||
|
||||
class RSI_ST: # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산
|
||||
def __init__(self, stock_code, window=21):
|
||||
self._stock_code = stock_code
|
||||
self._queue = deque(maxlen=window)
|
||||
self.rsi_period = window
|
||||
|
||||
def eval(self):
|
||||
# dftt = getStreamdDF(self._stock_code)
|
||||
# print(self)
|
||||
dftt = contract_sub_df.get(self._stock_code).copy()
|
||||
dftt = dftt.set_index(['TICK_HOUR'])
|
||||
dftt['STCK_PRPR'] = pd.to_numeric(dftt['STCK_PRPR'], errors='coerce').convert_dtypes()
|
||||
np_closes = np.array(dftt['STCK_PRPR'], dtype=np.float64)
|
||||
rsi = ta.RSI(np_closes, self.rsi_period)
|
||||
|
||||
last_rsi = rsi[-1]
|
||||
|
||||
if last_rsi < 30:
|
||||
print(f"({self._stock_code})[BUY] ***RSI: {last_rsi}") # 통상적으로 RSI가 30 이하면 과매도 상태인 것으로 판단하고 시장이 과도하게 하락했음을 나타냄
|
||||
elif last_rsi < 70 and last_rsi >= 30:
|
||||
print(f"({self._stock_code})[N/A] ***RSI: {last_rsi}")
|
||||
elif last_rsi >= 70:
|
||||
print(f"({self._stock_code})[SELL] ***RSI: {last_rsi}") # 통상적으로 RSI가 70 이상이면 과매수 상태로 간주하고 시장이 과열되었을 가능성이 있음을 나타냄
|
||||
else:
|
||||
print(self._stock_code)
|
||||
|
||||
|
||||
_today__ = datetime.date.today().strftime("%Y%m%d")
|
||||
|
||||
ka.auth()
|
||||
|
||||
__DEBUG__ = False # True
|
||||
|
||||
# 실시간 국내주식 계좌체결통보 복호화를 위한 부분-start
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import unpad
|
||||
from base64 import b64decode
|
||||
|
||||
|
||||
# AES256 DECODE: Copied from KIS Developers Github sample code
|
||||
def aes_cbc_base64_dec(key, iv, cipher_text):
|
||||
"""
|
||||
:param key: str type AES256 secret key value
|
||||
:param iv: str type AES256 Initialize Vector
|
||||
:param cipher_text: Base64 encoded AES256 str
|
||||
:return: Base64-AES256 decodec str
|
||||
"""
|
||||
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
|
||||
return bytes.decode(unpad(cipher.decrypt(b64decode(cipher_text)), AES.block_size))
|
||||
|
||||
|
||||
# 실시간 국내주식 계좌체결통보 복호화를 위한 부분 - end
|
||||
|
||||
|
||||
contract_sub_df = dict() # 실시간 국내주식 체결 결과를 종목별로 저장하기 위한 container
|
||||
tr_plans = dict() # 실시간 국내주식 체결 값에 따라 무언가를 수행할 Class 를 저장하기 위한 container
|
||||
|
||||
reserved_cols = ['TICK_HOUR', 'STCK_PRPR', 'ACML_VOL'] # 실시간 국내주식 체결 중 사용할 column 만 추출하기 위한 column 정의
|
||||
|
||||
# 실시간 국내주식체결 column header
|
||||
contract_cols = ['MKSC_SHRN_ISCD',
|
||||
'TICK_HOUR', # pandas time conversion 편의를 위해 이 필드만 이름을 통일한다
|
||||
'STCK_PRPR', # 현재가
|
||||
'PRDY_VRSS_SIGN', # 전일 대비 부호
|
||||
'PRDY_VRSS', # 전일 대비
|
||||
'PRDY_CTRT', # 전일 대비율
|
||||
'WGHN_AVRG_STCK_PRC', # 가중 평균 주식 가격
|
||||
'STCK_OPRC', # 시가
|
||||
'STCK_HGPR', # 고가
|
||||
'STCK_LWPR', # 저가
|
||||
'ASKP1', # 매도호가1
|
||||
'BIDP1', # 매수호가1
|
||||
'CNTG_VOL', # 체결 거래량
|
||||
'ACML_VOL', # 누적 거래량
|
||||
'ACML_TR_PBMN', # 누적 거래 대금
|
||||
'SELN_CNTG_CSNU', # 매도 체결 건수
|
||||
'SHNU_CNTG_CSNU', # 매수 체결 건수
|
||||
'NTBY_CNTG_CSNU', # 순매수 체결 건수
|
||||
'CTTR', # 체결강도
|
||||
'SELN_CNTG_SMTN', # 총 매도 수량
|
||||
'SHNU_CNTG_SMTN', # 총 매수 수량
|
||||
'CCLD_DVSN', # 체결구분 (1:매수(+), 3:장전, 5:매도(-))
|
||||
'SHNU_RATE', # 매수비율
|
||||
'PRDY_VOL_VRSS_ACML_VOL_RATE', # 전일 거래량 대비 등락율
|
||||
'OPRC_HOUR', # 시가 시간
|
||||
'OPRC_VRSS_PRPR_SIGN', # 시가대비구분
|
||||
'OPRC_VRSS_PRPR', # 시가대비
|
||||
'HGPR_HOUR',
|
||||
'HGPR_VRSS_PRPR_SIGN',
|
||||
'HGPR_VRSS_PRPR',
|
||||
'LWPR_HOUR',
|
||||
'LWPR_VRSS_PRPR_SIGN',
|
||||
'LWPR_VRSS_PRPR',
|
||||
'BSOP_DATE', # 영업 일자
|
||||
'NEW_MKOP_CLS_CODE', # 신 장운영 구분 코드
|
||||
'TRHT_YN',
|
||||
'ASKP_RSQN1',
|
||||
'BIDP_RSQN1',
|
||||
'TOTAL_ASKP_RSQN',
|
||||
'TOTAL_BIDP_RSQN',
|
||||
'VOL_TNRT', # 거래량 회전율
|
||||
'PRDY_SMNS_HOUR_ACML_VOL', # 전일 동시간 누적 거래량
|
||||
'PRDY_SMNS_HOUR_ACML_VOL_RATE', # 전일 동시간 누적 거래량 비율
|
||||
'HOUR_CLS_CODE', # 시간 구분 코드(0 : 장중 )
|
||||
'MRKT_TRTM_CLS_CODE',
|
||||
'VI_STND_PRC']
|
||||
|
||||
# 실시간 국내주식호가 column eader
|
||||
bid_ask_cols = ['MKSC_SHRN_ISCD',
|
||||
'TICK_HOUR', # pandas time conversion 편의를 위해 이 필드만 이름을 통일한다
|
||||
'HOUR_CLS_CODE', # 시간 구분 코드(0 : 장중 )
|
||||
'ASKP1', # 매도호가1
|
||||
'ASKP2',
|
||||
'ASKP3',
|
||||
'ASKP4',
|
||||
'ASKP5',
|
||||
'ASKP6',
|
||||
'ASKP7',
|
||||
'ASKP8',
|
||||
'ASKP9',
|
||||
'ASKP10',
|
||||
'BIDP1', # 매수호가1
|
||||
'BIDP2',
|
||||
'BIDP3',
|
||||
'BIDP4',
|
||||
'BIDP5',
|
||||
'BIDP6',
|
||||
'BIDP7',
|
||||
'BIDP8',
|
||||
'BIDP9',
|
||||
'BIDP10',
|
||||
'ASKP_RSQN1', # 매도호가 잔량1
|
||||
'ASKP_RSQN2',
|
||||
'ASKP_RSQN3',
|
||||
'ASKP_RSQN4',
|
||||
'ASKP_RSQN5',
|
||||
'ASKP_RSQN6',
|
||||
'ASKP_RSQN7',
|
||||
'ASKP_RSQN8',
|
||||
'ASKP_RSQN9',
|
||||
'ASKP_RSQN10',
|
||||
'BIDP_RSQN1', # 매수호가 잔량1
|
||||
'BIDP_RSQN2',
|
||||
'BIDP_RSQN3',
|
||||
'BIDP_RSQN4',
|
||||
'BIDP_RSQN5',
|
||||
'BIDP_RSQN6',
|
||||
'BIDP_RSQN7',
|
||||
'BIDP_RSQN8',
|
||||
'BIDP_RSQN9',
|
||||
'BIDP_RSQN10',
|
||||
'TOTAL_ASKP_RSQN', # 총 매도호가 잔량
|
||||
'TOTAL_BIDP_RSQN', # 총 매수호가 잔량
|
||||
'OVTM_TOTAL_ASKP_RSQN',
|
||||
'OVTM_TOTAL_BIDP_RSQN',
|
||||
'ANTC_CNPR',
|
||||
'ANTC_CNQN',
|
||||
'ANTC_VOL',
|
||||
'ANTC_CNTG_VRSS',
|
||||
'ANTC_CNTG_VRSS_SIGN',
|
||||
'ANTC_CNTG_PRDY_CTRT',
|
||||
'ACML_VOL', # 누적 거래량
|
||||
'TOTAL_ASKP_RSQN_ICDC',
|
||||
'TOTAL_BIDP_RSQN_ICDC',
|
||||
'OVTM_TOTAL_ASKP_ICDC',
|
||||
'OVTM_TOTAL_BIDP_ICDC',
|
||||
'STCK_DEAL_CLS_CODE']
|
||||
|
||||
# 실시간 계좌체결발생통보 column header
|
||||
notice_cols = ['CUST_ID', # HTS ID
|
||||
'ACNT_NO',
|
||||
'ODER_NO', # 주문번호
|
||||
'OODER_NO', # 원주문번호
|
||||
'SELN_BYOV_CLS', # 매도매수구분
|
||||
'RCTF_CLS', # 정정구분
|
||||
'ODER_KIND', # 주문종류(00 : 지정가,01 : 시장가,02 : 조건부지정가)
|
||||
'ODER_COND', # 주문조건
|
||||
'STCK_SHRN_ISCD', # 주식 단축 종목코드
|
||||
'CNTG_QTY', # 체결 수량(체결통보(CNTG_YN=2): 체결 수량, 주문·정정·취소·거부 접수 통보(CNTG_YN=1): 주문수량의미)
|
||||
'CNTG_UNPR', # 체결단가
|
||||
'STCK_CNTG_HOUR', # 주식 체결 시간
|
||||
'RFUS_YN', # 거부여부(0 : 승인, 1 : 거부)
|
||||
'CNTG_YN', # 체결여부(1 : 주문,정정,취소,거부,, 2 : 체결 (★ 체결만 볼 경우 2번만 ))
|
||||
'ACPT_YN', # 접수여부(1 : 주문접수, 2 : 확인 )
|
||||
'BRNC_NO', # 지점
|
||||
'ODER_QTY', # 주문수량
|
||||
'ACNT_NAME', # 계좌명
|
||||
'ORD_COND_PRC', # 호가조건가격 (스톱지정가 시 표시)
|
||||
'ORD_EXG_GB', # 주문거래소 구분 (1:KRX, 2:NXT, 3:SOR-KRX, 4:SOR-NXT)
|
||||
'POPUP_YN', # 실시간체결창 표시여부 (Y/N)
|
||||
'FILLER', # 필러
|
||||
'CRDT_CLS', # 신용구분
|
||||
'CRDT_LOAN_DATE', # 신용대출일자
|
||||
'CNTG_ISNM40', # 체결종목명40
|
||||
'ODER_PRC' # 주문가격
|
||||
]
|
||||
|
||||
|
||||
# 웹소켓 접속키 발급
|
||||
def get_approval():
|
||||
url = ka.getTREnv().my_url
|
||||
headers = {"content-type": "application/json"}
|
||||
body = {"grant_type": "client_credentials",
|
||||
"appkey": ka.getTREnv().my_app,
|
||||
"secretkey": ka.getTREnv().my_sec}
|
||||
PATH = "oauth2/Approval"
|
||||
URL = f"{url}/{PATH}"
|
||||
res = requests.post(URL, headers=headers, data=json.dumps(body))
|
||||
approval_key = res.json()["approval_key"]
|
||||
return approval_key
|
||||
|
||||
|
||||
_connect_key = get_approval() # websocker 연결Key
|
||||
_iv = None # for 복호화
|
||||
_ekey = None # for 복호화
|
||||
executed_df = pd.DataFrame(data=None, columns=contract_cols) # 체결통보 저장용 DF
|
||||
|
||||
|
||||
# added_data 는 종목코드(실시간체결, 실시간호가) 또는 HTS_ID(체결통보)
|
||||
def _build_message(app_key, tr_id, added_data, tr_type='1'):
|
||||
_h = {
|
||||
"approval_key": app_key,
|
||||
"custtype": 'P',
|
||||
"tr_type": tr_type,
|
||||
"content-type": "utf-8"
|
||||
}
|
||||
_inp = {
|
||||
"tr_id": tr_id,
|
||||
"tr_key": added_data
|
||||
}
|
||||
_b = {
|
||||
"input": _inp
|
||||
}
|
||||
_data = {
|
||||
"header": _h,
|
||||
"body": _b
|
||||
}
|
||||
|
||||
d1 = json.dumps(_data)
|
||||
|
||||
return d1
|
||||
|
||||
|
||||
# sub_data 는 종목코드(실시간체결, 실시간호가) 또는 HTS_ID(실시간 계좌체결발생통보)
|
||||
def subscribe(ws, sub_type, app_key, sub_data): # 세션 종목코드(실시간체결, 실시간호가) 등록
|
||||
ws.send(_build_message(app_key, sub_type, sub_data), websocket.ABNF.OPCODE_TEXT)
|
||||
|
||||
time.sleep(.1)
|
||||
|
||||
|
||||
def unsubscribe(ws, sub_type, app_key, sub_data): # 세션 종목코드(실시간체결, 실시간호가) 등록해제
|
||||
ws.send(_build_message(app_key, sub_type, sub_data, '2'), websocket.ABNF.OPCODE_TEXT)
|
||||
|
||||
time.sleep(.1)
|
||||
|
||||
|
||||
# streaming data 를 이용해 주어진 bar 크기(예: 1분, 5분 등)의 OHLC(x분봉) 데이터프레임을 반환한다.
|
||||
# 이때 streamign data 는 websocket client 가 시작한 다음부터 지금까지의 해당 종목의 가격 정보를 의미한다.
|
||||
# ** 동시호가 시간은 OHLC data 가 모두 NA 가 된다.
|
||||
def getStreamdDF(stock_code, bar_sz='1Min'):
|
||||
df3 = contract_sub_df.get(stock_code).copy()
|
||||
df3 = df3.set_index(['TICK_HOUR'])
|
||||
df3['STCK_PRPR'] = pd.to_numeric(df3['STCK_PRPR'], errors='coerce').convert_dtypes()
|
||||
df3 = df3['STCK_PRPR'].resample(bar_sz).ohlc()
|
||||
|
||||
return df3
|
||||
|
||||
# 수신데이터 파싱
|
||||
def _dparse(data):
|
||||
global executed_df
|
||||
d1 = data.split("|")
|
||||
dp_ = None
|
||||
|
||||
hcols = []
|
||||
|
||||
if len(d1) >= 4:
|
||||
tr_id = d1[1]
|
||||
if tr_id == KIS_WSReq.CONTRACT: # 실시간체결
|
||||
hcols = contract_cols
|
||||
elif tr_id == KIS_WSReq.BID_ASK: # 실시간호가
|
||||
hcols = bid_ask_cols
|
||||
elif tr_id == KIS_WSReq.NOTICE: # 계좌체결통보
|
||||
hcols = notice_cols
|
||||
else:
|
||||
pass
|
||||
|
||||
if tr_id in (KIS_WSReq.CONTRACT, KIS_WSReq.BID_ASK): # 실시간체결, 실시간호가
|
||||
dp_ = pd.read_csv(StringIO(d1[3]), header=None, sep='^', names=hcols, dtype=object) # 수신데이터 parsing
|
||||
|
||||
print(dp_) # 실시간체결, 실시간호가 수신 데이터 파싱 결과 확인
|
||||
|
||||
dp_['TICK_HOUR'] = _today__ + dp_['TICK_HOUR'] # 수신시간
|
||||
dp_['TICK_HOUR'] = pd.to_datetime(dp_['TICK_HOUR'], format='%Y%m%d%H%M%S', errors='coerce')
|
||||
else: # 실시간 계좌체결발생통보는 암호화되어서 수신되므로 복호화 과정이 필요
|
||||
dp_ = pd.read_csv(StringIO(aes_cbc_base64_dec(_ekey, _iv, d1[3])), header=None, sep='^', names=hcols, # 수신데이터 parsing 및 복호화
|
||||
dtype=object)
|
||||
|
||||
print(dp_) # 실시간 계좌체결발생통보 수신 파싱 결과 확인
|
||||
|
||||
if __DEBUG__: print(f'***EXECUTED NOTICE [{dp_.to_string(header=False, index=False)}]')
|
||||
|
||||
if tr_id == KIS_WSReq.CONTRACT: # 실시간 체결
|
||||
if __DEBUG__: print(dp_.to_string(header=False, index=False))
|
||||
stock_code = dp_[dp_.columns[0]].values.tolist()[0]
|
||||
df2_ = dp_[reserved_cols]
|
||||
# dft_ = pd.concat([contract_sub_df.get(stock_code), df2_], axis=0, ignore_index=True)
|
||||
# 선택된 열이 비어 있거나 모든 값이 NA인지 확인
|
||||
selected_df = contract_sub_df.get(stock_code)
|
||||
if selected_df is not None and not selected_df.dropna().empty:
|
||||
dft_ = pd.concat([selected_df, df2_], axis=0, ignore_index=True)
|
||||
else:
|
||||
dft_ = df2_
|
||||
contract_sub_df[stock_code] = dft_
|
||||
|
||||
######### 이 부분에서 로직을 적용한 후 매수/매도를 수행하면 될 듯!!
|
||||
|
||||
val1 = dp_['STCK_PRPR'].tolist()[0]
|
||||
tr_plans[stock_code].push(int(val1)) # 이동평균값 활용
|
||||
# tr_plans[stock_code].eval() # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산 활용
|
||||
|
||||
# [국내주식] 주문/계좌 > 매수가능조회 (종목번호 5자리 + 종목단가) REST API
|
||||
rt_data = kb.get_inquire_psbl_order(pdno=stock_code, ord_unpr=val1)
|
||||
ord_qty = rt_data.loc[0, 'nrcvb_buy_qty'] # nrcvb_buy_qty 미수없는매수수량
|
||||
print("[미수없는매수주문가능수량!] : " + ord_qty)
|
||||
|
||||
# 국내주식 현금 주문
|
||||
# rt_data = kb.get_order_cash(ord_dv="buy",itm_no=stock_code, qty=ord_qty, unpr=val1)
|
||||
# print(rt_data.KRX_FWDG_ORD_ORGNO + "+" + rt_data.ODNO + "+" + rt_data.ORD_TMD) # 주문접수조직번호+주문접수번호+주문시각
|
||||
print("매수/매도 조건 주문 : " + val1)
|
||||
|
||||
#########################################################
|
||||
|
||||
elif tr_id == KIS_WSReq.NOTICE: # 체결통보의 경우, 일단 executed_df 에만 저장해 둠
|
||||
if __DEBUG__: print(dp_.to_string(header=False, index=False))
|
||||
executed_df = pd.concat([executed_df, dp_], axis=0, ignore_index=True)
|
||||
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
print("Data length error...{data}")
|
||||
|
||||
|
||||
def _get_sys_resp(data):
|
||||
global _iv
|
||||
global _ekey
|
||||
|
||||
isPingPong = False
|
||||
isUnSub = False
|
||||
isOk = False
|
||||
tr_msg = None
|
||||
tr_key = None
|
||||
|
||||
rdic = json.loads(data)
|
||||
|
||||
tr_id = rdic['header']['tr_id']
|
||||
if tr_id != "PINGPONG": tr_key = rdic['header']['tr_key']
|
||||
if rdic.get("body", None) is not None:
|
||||
isOk = True if rdic["body"]["rt_cd"] == "0" else False
|
||||
tr_msg = rdic["body"]["msg1"]
|
||||
# 복호화를 위한 key 를 추출
|
||||
if 'output' in rdic["body"]:
|
||||
_iv = rdic["body"]["output"]["iv"]
|
||||
_ekey = rdic["body"]["output"]["key"]
|
||||
isUnSub = True if tr_msg[:5] == "UNSUB" else False
|
||||
else:
|
||||
isPingPong = True if tr_id == "PINGPONG" else False
|
||||
|
||||
nt2 = namedtuple('SysMsg', ['isOk', 'tr_id', 'tr_key', 'isUnSub', 'isPingPong'])
|
||||
d = {
|
||||
'isOk': isOk,
|
||||
'tr_id': tr_id,
|
||||
'tr_key': tr_key,
|
||||
'isUnSub': isUnSub,
|
||||
'isPingPong': isPingPong
|
||||
}
|
||||
|
||||
return nt2(**d)
|
||||
|
||||
|
||||
def on_data(ws, data, resp_type, data_continu):
|
||||
# print(f"On data => {resp_type}, {data_continu}, {data}") #return only 1, True
|
||||
pass
|
||||
|
||||
|
||||
def on_message(ws, data):
|
||||
if data[0] in ('0', '1'): # 실시간체결 or 실시간호가
|
||||
_dparse(data)
|
||||
else: # system message or PINGPONG
|
||||
rsp = _get_sys_resp(data)
|
||||
if rsp.isPingPong:
|
||||
ws.send(data, websocket.ABNF.OPCODE_PING)
|
||||
else:
|
||||
if (not rsp.isUnSub and rsp.tr_id == KIS_WSReq.CONTRACT):
|
||||
contract_sub_df[rsp.tr_key] = pd.DataFrame(columns=reserved_cols)
|
||||
|
||||
########################################################################
|
||||
#### 이 부분에서 전략을 수행할 class 를 등록한다.
|
||||
#### 실제 주문 실행은 _dparse 함수에서 처리
|
||||
tr_plans[rsp.tr_key] = BasicPlan(rsp.tr_key) # 이동 평균선 계산 (웹소켓 프로그램 실행시 수집된 데이터만 반영)
|
||||
# tr_plans[rsp.tr_key] = RSI_ST(rsp.tr_key) # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산
|
||||
########################################################################
|
||||
elif (rsp.isUnSub):
|
||||
del (contract_sub_df[rsp.tr_key])
|
||||
else:
|
||||
print(rsp)
|
||||
|
||||
|
||||
def on_error(ws, error):
|
||||
print('error=', error)
|
||||
|
||||
|
||||
def on_close(ws, status_code, close_msg):
|
||||
print('on_close close_status_code=', status_code, " close_msg=", close_msg)
|
||||
|
||||
|
||||
def on_open(ws):
|
||||
# stocks 에는 40개까지만 가능
|
||||
stocks = ('009540', '012630', '052300', '089860', '218410', '330590', '357550', '419080', '348370')
|
||||
for scode in stocks:
|
||||
subscribe(ws, KIS_WSReq.BID_ASK, _connect_key, scode) # 실시간 호가
|
||||
subscribe(ws, KIS_WSReq.CONTRACT, _connect_key, scode) # 실시간 체결
|
||||
|
||||
# unsubscribe(ws, KIS_WSReq.CONTRACT, _connect_key, "005930") #실시간 체결 해제
|
||||
# subscribe(ws, KIS_WSReq.BID_ASK, _connect_key, "005930") #실시간 호가
|
||||
# 실시간 계좌체결발생통보를 등록한다. 계좌체결발생통보 결과는 executed_df 에 저장된다.
|
||||
subscribe(ws, KIS_WSReq.NOTICE, _connect_key, "HTS ID 입력") # HTS ID 입력
|
||||
|
||||
|
||||
ws = websocket.WebSocketApp("ws://ops.koreainvestment.com:21000/tryitout",
|
||||
on_open=on_open, on_message=on_message, on_error=on_error, on_data=on_data)
|
||||
|
||||
ws.run_forever() # 실시간 웹소켓 연결 작동
|
||||
1173
한국투자증권(API)/legacy/Sample01/kis_ovrseafuopt.py
Normal file
1173
한국투자증권(API)/legacy/Sample01/kis_ovrseafuopt.py
Normal file
File diff suppressed because it is too large
Load Diff
508
한국투자증권(API)/legacy/Sample01/kis_ovrseafuopt_ws.py
Normal file
508
한국투자증권(API)/legacy/Sample01/kis_ovrseafuopt_ws.py
Normal file
@@ -0,0 +1,508 @@
|
||||
# 국내주식 실시간 websocket sample
|
||||
import websocket
|
||||
|
||||
import kis_auth as ka
|
||||
import kis_ovrseafuopt as kb
|
||||
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from io import StringIO
|
||||
from threading import Thread
|
||||
|
||||
from collections import namedtuple, deque
|
||||
|
||||
try:
|
||||
import websockets
|
||||
|
||||
except ImportError:
|
||||
print("websocket-client 설치중입니다.")
|
||||
os.system('python3 -m pip3 install websocket-client')
|
||||
|
||||
from enum import StrEnum
|
||||
|
||||
|
||||
class KIS_WSReq(StrEnum):
|
||||
CONTRACT = 'HDFFF020' # 해외선물옵션 실시간체결가
|
||||
BID_ASK = 'HDFFF010' # 해외선물옵션 실시간호가
|
||||
ORDERNOTICE = 'HDFFF1C0' # 실시간 해외선물옵션 주문내역발생통보
|
||||
CCLDNOTICE = 'HDFFF2C0' # 실시간 해외선물옵션 체결내역발생통보
|
||||
|
||||
import talib as ta
|
||||
|
||||
|
||||
class BasicPlan:
|
||||
|
||||
def __init__(self, stock_code, window=20):
|
||||
self._stock_code = stock_code
|
||||
self._queue = deque(maxlen=window)
|
||||
self._prev_ma = None
|
||||
|
||||
def push(self, value):
|
||||
self._queue.append(value)
|
||||
ma = sum(self._queue) / len(self._queue)
|
||||
diff = ma - self._prev_ma if self._prev_ma is not None else None
|
||||
self._prev_ma = ma
|
||||
|
||||
print(f"{self._stock_code}****** value: {value}, MA: {ma}, diff: {diff}...")
|
||||
|
||||
class RSI_ST: # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산
|
||||
def __init__(self, stock_code, window=21):
|
||||
self._stock_code = stock_code
|
||||
self._queue = deque(maxlen=window)
|
||||
self.rsi_period = window
|
||||
|
||||
def eval(self):
|
||||
# dftt = getStreamdDF(self._stock_code)
|
||||
# print(self)
|
||||
dftt = contract_sub_df.get(self._stock_code).copy()
|
||||
dftt = dftt.set_index(['TICK_HOUR'])
|
||||
dftt['LAST_PRICE'] = pd.to_numeric(dftt['LAST_PRICE'], errors='coerce').convert_dtypes()
|
||||
np_closes = np.array(dftt['LAST_PRICE'], dtype=np.float64)
|
||||
rsi = ta.RSI(np_closes, self.rsi_period)
|
||||
|
||||
last_rsi = rsi[-1]
|
||||
|
||||
if last_rsi < 30:
|
||||
print(f"({self._stock_code})[BUY] ***RSI: {last_rsi}") # 통상적으로 RSI가 30 이하면 과매도 상태인 것으로 판단하고 시장이 과도하게 하락했음을 나타냄
|
||||
elif last_rsi < 70 and last_rsi >= 30:
|
||||
print(f"({self._stock_code})[N/A] ***RSI: {last_rsi}")
|
||||
elif last_rsi >= 70:
|
||||
print(f"({self._stock_code})[SELL] ***RSI: {last_rsi}") # 통상적으로 RSI가 70 이상이면 과매수 상태로 간주하고 시장이 과열되었을 가능성이 있음을 나타냄
|
||||
else:
|
||||
print(self._stock_code)
|
||||
|
||||
|
||||
_today__ = datetime.date.today().strftime("%Y%m%d")
|
||||
|
||||
ka.auth()
|
||||
|
||||
__DEBUG__ = False # True
|
||||
|
||||
# 실시간 해외주식 계좌체결통보 복호화를 위한 부분 - start
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import unpad
|
||||
from base64 import b64decode
|
||||
|
||||
|
||||
# AES256 DECODE: Copied from KIS Developers Github sample code
|
||||
def aes_cbc_base64_dec(key, iv, cipher_text):
|
||||
"""
|
||||
:param key: str type AES256 secret key value
|
||||
:param iv: str type AES256 Initialize Vector
|
||||
:param cipher_text: Base64 encoded AES256 str
|
||||
:return: Base64-AES256 decodec str
|
||||
"""
|
||||
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
|
||||
return bytes.decode(unpad(cipher.decrypt(b64decode(cipher_text)), AES.block_size))
|
||||
|
||||
|
||||
# 실시간 해외주식 계좌체결통보 복호화를 위한 부분 - end
|
||||
|
||||
|
||||
contract_sub_df = dict() # 실시간 해외주식 체결 결과를 종목별로 저장하기 위한 container
|
||||
tr_plans = dict() # 실시간 해외주식 체결 값에 따라 무언가를 수행할 Class 를 저장하기 위한 container
|
||||
excg_dict = {
|
||||
'NYS' : 'NYSE', #미국뉴욕
|
||||
'NAS' : 'NASD', #미국나스닥
|
||||
'AMS' : 'AMEX', #미국아멕스
|
||||
'TSE' : 'TKSE', #일본도쿄
|
||||
'HKS' : 'SEHK', #홍콩
|
||||
'SHS' : 'SHAA', #중국상해
|
||||
'SZS' : 'SZAA', #중국심천
|
||||
'HSX' : 'VNSE', #베트남호치민,
|
||||
'HNX' : 'HASE', #베트남하노이
|
||||
'BAY' : 'NYSE', #미국뉴욕(주간)
|
||||
'BAQ' : 'NASD', #미국나스닥(주간),
|
||||
'BAA' : 'AMEX' #미국아멕스(주간)
|
||||
}
|
||||
|
||||
|
||||
#reserved_cols = ['TICK_HOUR', 'STCK_PRPR', 'ACML_VOL'] # 실시간 해외주식 체결 중 사용할 수신시간, 현재가, 누적거래량 만 추출하기 위한 column 정의
|
||||
reserved_cols = ['TICK_HOUR', 'LAST_PRICE', 'VOL'] # 실시간 해외선물옵션 체결 중 사용할 column 만 추출하기 위한 column 정의
|
||||
|
||||
# 해외선물옵션 실시간체결가 column header
|
||||
contract_cols = ['SERIES_CD', # 종목코드 * 각 항목사이에는 구분자로 ^ 사용, 모든 데이터타입은 STRING으로 변환되어 PUSH 처리됨'
|
||||
'BSNS_DATE', # 영업일자
|
||||
'MRKT_OPEN_DATE', # 장개시일자
|
||||
'MRKT_OPEN_TIME', # 장개시시각
|
||||
'MRKT_CLOSE_DATE', # 장종료일자
|
||||
'MRKT_CLOSE_TIME', # 장종료시각
|
||||
'PREV_PRICE', # 전일종가 ※ 전일종가, 체결가격, 전일대비가, 시가, 고가, 저가 ※ FFCODE.MST(해외선물종목마스터 파일)의 SCALCDESZ(계산 소수점) 값 참고
|
||||
'RECV_DATE', # 수신일자
|
||||
'TICK_HOUR', # 수신시각 ※ 수신시각(RECV_TIME) = 실제 체결시각 ★ pandas time conversion 편의를 위해 이 필드만 이름을 통일한다 'KHMS' 한국시간
|
||||
'ACTIVE_FLAG', # 본장_전산장구분
|
||||
'LAST_PRICE', # 체결가격
|
||||
'LAST_QNTT', # 체결수량
|
||||
'PREV_DIFF_PRICE', # 전일대비가
|
||||
'PREV_DIFF_RATE', # 등락률
|
||||
'OPEN_PRICE', # 시가
|
||||
'HIGH_PRICE', # 고가
|
||||
'LOW_PRICE', # 저가
|
||||
'VOL', # 누적거래량
|
||||
'PREV_SIGN', # 전일대비부호
|
||||
'QUOTSIGN', # 체결구분 ※ 2:매수체결 5:매도체결
|
||||
'RECV_TIME2', # 수신시각2 만분의일초
|
||||
'PSTTL_PRICE', # 전일정산가
|
||||
'PSTTL_SIGN', # 전일정산가대비
|
||||
'PSTTL_DIFF_PRICE', # 전일정산가대비가격
|
||||
'PSTTL_DIFF_RATE'] # 전일정산가대비율
|
||||
|
||||
# 실시간 해외선물옵션호가 column eader
|
||||
bid_ask_cols = ['SERIES_CD', # 종목코드 '각 항목사이에는 구분자로 ^ 사용,모든 데이터타입은 STRING으로 변환되어 PUSH 처리됨'
|
||||
'RECV_DATE', # 수신일자
|
||||
'TICK_HOUR', # 수신시각 ※ 수신시각(RECV_TIME) = 실제 체결시각 ★ pandas time conversion 편의를 위해 이 필드만 이름을 통일한다 'KHMS' 한국시간
|
||||
'PREV_PRICE', # 전일종가 ※ 전일종가, 매수1호가~매도5호가 ※ FFCODE.MST(해외선물종목마스터 파일)의 SCALCDESZ(계산 소수점) 값 참고
|
||||
'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'] # 전일정산가
|
||||
|
||||
# 실시간 계좌주문내역발생통보 column header
|
||||
ordernotice_cols = ['USER_ID', # 유저ID 각 항목사이에는 구분자로 ^ 사용, 모든 데이터타입은 STRING으로 변환되어 PUSH 처리됨'
|
||||
'ACCT_NO', # 계좌번호
|
||||
'ORD_DT', # 주문일자
|
||||
'ODNO', # 주문번호
|
||||
'ORGN_ORD_DT', # 원주문일자
|
||||
'ORGN_ODNO', # 원주문번호
|
||||
'SERIES', # 종목명
|
||||
'RVSE_CNCL_DVSN_CD',# 정정취소구분코드 해당없음 : 00 , 정정 : 01 , 취소 : 02
|
||||
'SLL_BUY_DVSN_CD', # 매도매수구분코드 01 : 매도, 02 : 매수
|
||||
'CPLX_ORD_DVSN_CD', # 복합주문구분코드 0 (HEDGE청산만 이용)
|
||||
'PRCE_TP', # 가격구분코드', # 1:LIMIT, 2:MARKET, 3:STOP(STOP가격시 시장가)
|
||||
'FM_EXCG_RCIT_DVSN_CD', # FM거래소접수구분코드 01:접수전, 02:응답, 03:거부
|
||||
'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_DT)와 동일
|
||||
'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'] # 자동주문 전략구분
|
||||
|
||||
# 실시간 계좌체결내역발생통보 column header
|
||||
ccldnotice_cols = ['USER_ID', # 유저ID '각 항목사이에는 구분자로 ^ 사용, 모든 데이터타입은 STRING으로 변환되어 PUSH 처리됨'
|
||||
'ACCT_NO', # 계좌번호
|
||||
'ORD_DT', # 주문일자
|
||||
'ODNO', # 주문번호
|
||||
'ORGN_ORD_DT', # 원주문일자
|
||||
'ORGN_ODNO', # 원주문번호
|
||||
'SERIES', # 종목명
|
||||
'RVSE_CNCL_DVSN_CD',# 정정취소구분코드', # 해당없음 : 00 , 정정 : 01 , 취소 : 02
|
||||
'SLL_BUY_DVSN_CD', # 매도매수구분코드', # 01 : 매도, 02 : 매수
|
||||
'CPLX_ORD_DVSN_CD', # 복합주문구분코드', # 0 (HEDGE청산만 이용)
|
||||
'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']# 선물옵션종목구분코드
|
||||
|
||||
|
||||
# 웹소켓 접속키 발급
|
||||
def get_approval():
|
||||
url = ka.getTREnv().my_url
|
||||
headers = {"content-type": "application/json"}
|
||||
body = {"grant_type": "client_credentials",
|
||||
"appkey": ka.getTREnv().my_app,
|
||||
"secretkey": ka.getTREnv().my_sec}
|
||||
PATH = "oauth2/Approval"
|
||||
URL = f"{url}/{PATH}"
|
||||
res = requests.post(URL, headers=headers, data=json.dumps(body))
|
||||
approval_key = res.json()["approval_key"]
|
||||
return approval_key
|
||||
|
||||
|
||||
_connect_key = get_approval() # websocker 연결Key
|
||||
_iv = None # for 복호화
|
||||
_ekey = None # for 복호화
|
||||
executed_df = pd.DataFrame(data=None, columns=contract_cols) # 체결통보 저장용 DF
|
||||
|
||||
|
||||
# added_data 는 종목코드(실시간체결, 실시간호가) 또는 HTS_ID(체결통보)
|
||||
def _build_message(app_key, tr_id, added_data, tr_type='1'):
|
||||
_h = {
|
||||
"approval_key": app_key,
|
||||
"custtype": 'P',
|
||||
"tr_type": tr_type,
|
||||
"content-type": "utf-8"
|
||||
}
|
||||
_inp = {
|
||||
"tr_id": tr_id,
|
||||
"tr_key": added_data
|
||||
}
|
||||
_b = {
|
||||
"input": _inp
|
||||
}
|
||||
_data = {
|
||||
"header": _h,
|
||||
"body": _b
|
||||
}
|
||||
|
||||
d1 = json.dumps(_data)
|
||||
|
||||
return d1
|
||||
|
||||
|
||||
# sub_data 는 종목코드(실시간체결, 실시간호가) 또는 HTS_ID(실시간 계좌체결발생통보)
|
||||
def subscribe(ws, sub_type, app_key, sub_data): # 세션 종목코드(실시간체결, 실시간호가) 등록
|
||||
ws.send(_build_message(app_key, sub_type, sub_data), websocket.ABNF.OPCODE_TEXT)
|
||||
|
||||
time.sleep(.1)
|
||||
|
||||
|
||||
def unsubscribe(ws, sub_type, app_key, sub_data): # 세션 종목코드(실시간체결, 실시간호가) 등록해제
|
||||
ws.send(_build_message(app_key, sub_type, sub_data, '2'), websocket.ABNF.OPCODE_TEXT)
|
||||
|
||||
time.sleep(.1)
|
||||
|
||||
|
||||
# streaming data 를 이용해 주어진 bar 크기(예: 1분, 5분 등)의 OHLC(x분봉) 데이터프레임을 반환한다.
|
||||
# 이때 streamign data 는 websocket client 가 시작한 다음부터 지금까지의 해당 종목의 가격 정보를 의미한다.
|
||||
# ** 동시호가 시간은 OHLC data 가 모두 NA 가 된다.
|
||||
def getStreamdDF(stock_code, bar_sz='1Min'):
|
||||
df3 = contract_sub_df.get(stock_code).copy()
|
||||
df3 = df3.set_index(['TICK_HOUR'])
|
||||
df3['LAST_PRICE'] = pd.to_numeric(df3['LAST_PRICE'], errors='coerce').convert_dtypes()
|
||||
df3 = df3['LAST_PRICE'].resample(bar_sz).ohlc()
|
||||
|
||||
return df3
|
||||
|
||||
# 수신데이터 파싱
|
||||
def _dparse(data):
|
||||
global executed_df
|
||||
d1 = data.split("|")
|
||||
dp_ = None
|
||||
|
||||
hcols = []
|
||||
|
||||
if len(d1) >= 4:
|
||||
tr_id = d1[1]
|
||||
if tr_id == KIS_WSReq.CONTRACT: # 실시간체결
|
||||
hcols = contract_cols
|
||||
elif tr_id == KIS_WSReq.BID_ASK: # 해외선물옵션 실시간호가
|
||||
hcols = bid_ask_cols
|
||||
elif tr_id == KIS_WSReq.ORDERNOTICE: # 주문내역발생통보
|
||||
hcols = ordernotice_cols
|
||||
elif tr_id == KIS_WSReq.CCLDNOTICE: # 체결내역발생통보
|
||||
hcols = ccldnotice_cols
|
||||
else:
|
||||
pass
|
||||
|
||||
if tr_id in (KIS_WSReq.CONTRACT, KIS_WSReq.BID_ASK): # 실시간체결, 실시간호가
|
||||
dp_ = pd.read_csv(StringIO(d1[3]), header=None, sep='^', names=hcols, dtype=object) # 수신데이터 parsing
|
||||
|
||||
print(dp_) # 실시간체결, 실시간호가 수신 데이터 파싱 결과 확인
|
||||
|
||||
dp_['TICK_HOUR'] = _today__ + dp_['TICK_HOUR'] # 수신시간
|
||||
dp_['TICK_HOUR'] = pd.to_datetime(dp_['TICK_HOUR'], format='%Y%m%d%H%M%S', errors='coerce')
|
||||
else: # 실시간 계좌체결발생통보는 암호화되어서 수신되므로 복호화 과정이 필요
|
||||
dp_ = pd.read_csv(StringIO(aes_cbc_base64_dec(_ekey, _iv, d1[3])), header=None, sep='^', names=hcols, # 수신데이터 parsing 및 복호화
|
||||
dtype=object)
|
||||
|
||||
print(dp_) # 실시간 계좌체결발생통보 수신 파싱 결과 확인
|
||||
|
||||
if __DEBUG__: print(f'***EXECUTED CCLDNOTICE [{dp_.to_string(header=False, index=False)}]')
|
||||
|
||||
if tr_id == KIS_WSReq.CONTRACT: # 실시간 체결
|
||||
if __DEBUG__: print(dp_.to_string(header=False, index=False))
|
||||
stock_code = dp_[dp_.columns[0]].values.tolist()[0]
|
||||
df2_ = dp_[reserved_cols]
|
||||
# dft_ = pd.concat([contract_sub_df.get(stock_code), df2_], axis=0, ignore_index=True)
|
||||
# 선택된 열이 비어 있거나 모든 값이 NA인지 확인
|
||||
selected_df = contract_sub_df.get(stock_code)
|
||||
if selected_df is not None and not selected_df.dropna().empty:
|
||||
dft_ = pd.concat([selected_df, df2_], axis=0, ignore_index=True)
|
||||
else:
|
||||
dft_ = df2_
|
||||
contract_sub_df[stock_code] = dft_
|
||||
######### 이 부분에서 로직을 적용한 후 매수/매도를 수행하면 될 듯!!
|
||||
|
||||
val1 = dp_['LAST_PRICE'].tolist()[0]
|
||||
tr_plans[stock_code].push(float(val1)) # 이동평균값 활용
|
||||
# tr_plans[stock_code].eval() # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산 활용
|
||||
stock_df = dp_['SERIES_CD'].tolist()[0] # 종목코드
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션 주문가능조회 (선물옵션구분fuop_dvsn)
|
||||
# 선물옵션구분 00:전체 / 01:선물 / 02:옵션
|
||||
rt_data = kb.get_overseasfuopt_inquire_psamount(itm_no=stock_df, dvsn="00", pric=0, ordyn="")
|
||||
ord_qty = rt_data.loc[0, 'fm_new_ord_psbl_qty'] # 신규주문가능수량 총주문가능수량(fm_tot_ord_psbl_qty), 시장가총주문가능수량(fm_mkpr_tot_ord_psbl_qty)
|
||||
print("[주문가능수량!] : " + ord_qty)
|
||||
|
||||
###########################################################
|
||||
# [해외선물옵션] 주문/계좌 > 해외선물옵션주문 (종목번호<6자리 5자리> + 매수매도구분ord_dv + 가격구분dvsn + 주문수량qty + 주문가격limt_pric + 주문가격stop_pric)
|
||||
# 매수매도구분ord_dv 01 : 매도, 02 : 매수 # 가격구분dvsn : 1.지정, 2. 시장, 3. STOP, 4 S/L
|
||||
# 주문가격limt_pric : 지정가인 경우 가격 입력 * 시장가, STOP주문인 경우, 빈칸("") 입력
|
||||
# 주문가격stop_pric : STOP 주문 가격 입력 * 시장가, 지정가인 경우, 빈칸("") 입력
|
||||
# rt_data = kb.get_overseasfuopt_order(itm_no=stock_df, ord_dv="02", dvsn="1", qty=ord_qty, limt_pric=val1, stop_pric=0)
|
||||
# print(rt_data.ORD_DT + "+" + rt_data.ODNO) # 주문일자+주문접수번호
|
||||
|
||||
print("매수/매도 조건 주문 : " + val1)
|
||||
###########################################################
|
||||
|
||||
elif tr_id == KIS_WSReq.CCLDNOTICE: # 체결통보의 경우, 일단 executed_df 에만 저장해 둠
|
||||
if __DEBUG__: print(dp_.to_string(header=False, index=False))
|
||||
executed_df = pd.concat([executed_df, dp_], axis=0, ignore_index=True)
|
||||
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
print("Data length error...{data}")
|
||||
|
||||
|
||||
def _get_sys_resp(data):
|
||||
global _iv
|
||||
global _ekey
|
||||
|
||||
isPingPong = False
|
||||
isUnSub = False
|
||||
isOk = False
|
||||
tr_msg = None
|
||||
tr_key = None
|
||||
|
||||
rdic = json.loads(data)
|
||||
|
||||
tr_id = rdic['header']['tr_id']
|
||||
if tr_id != "PINGPONG": tr_key = rdic['header']['tr_key']
|
||||
if rdic.get("body", None) is not None:
|
||||
isOk = True if rdic["body"]["rt_cd"] == "0" else False
|
||||
tr_msg = rdic["body"]["msg1"]
|
||||
# 복호화를 위한 key 를 추출
|
||||
if 'output' in rdic["body"]:
|
||||
_iv = rdic["body"]["output"]["iv"]
|
||||
_ekey = rdic["body"]["output"]["key"]
|
||||
isUnSub = True if tr_msg[:5] == "UNSUB" else False
|
||||
else:
|
||||
isPingPong = True if tr_id == "PINGPONG" else False
|
||||
|
||||
nt2 = namedtuple('SysMsg', ['isOk', 'tr_id', 'tr_key', 'isUnSub', 'isPingPong'])
|
||||
d = {
|
||||
'isOk': isOk,
|
||||
'tr_id': tr_id,
|
||||
'tr_key': tr_key,
|
||||
'isUnSub': isUnSub,
|
||||
'isPingPong': isPingPong
|
||||
}
|
||||
|
||||
return nt2(**d)
|
||||
|
||||
|
||||
def on_data(ws, data, resp_type, data_continu):
|
||||
# print(f"On data => {resp_type}, {data_continu}, {data}") #return only 1, True
|
||||
pass
|
||||
|
||||
|
||||
def on_message(ws, data):
|
||||
if data[0] in ('0', '1'): # 실시간체결 or 실시간호가
|
||||
_dparse(data)
|
||||
else: # system message or PINGPONG
|
||||
rsp = _get_sys_resp(data)
|
||||
if rsp.isPingPong:
|
||||
ws.send(data, websocket.ABNF.OPCODE_PING)
|
||||
else:
|
||||
if (not rsp.isUnSub and rsp.tr_id == KIS_WSReq.CONTRACT):
|
||||
contract_sub_df[rsp.tr_key] = pd.DataFrame(columns=reserved_cols)
|
||||
|
||||
########################################################################
|
||||
#### 이 부분에서 전략을 수행할 class 를 등록한다.
|
||||
#### 실제 주문 실행은 _dparse 함수에서 처리
|
||||
tr_plans[rsp.tr_key] = BasicPlan(rsp.tr_key) # 이동 평균선 계산 (웹소켓 프로그램 실행시 수집된 데이터만 반영)
|
||||
# tr_plans[rsp.tr_key] = RSI_ST(rsp.tr_key) # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산
|
||||
########################################################################
|
||||
|
||||
elif (rsp.isUnSub):
|
||||
del (contract_sub_df[rsp.tr_key])
|
||||
else:
|
||||
print(rsp)
|
||||
|
||||
|
||||
def on_error(ws, error):
|
||||
print('error=', error)
|
||||
|
||||
def on_close(ws, status_code, close_msg):
|
||||
print('on_close close_status_code=', status_code, " close_msg=", close_msg)
|
||||
|
||||
|
||||
def on_open(ws):
|
||||
# stocks 에는 40개까지만 가능
|
||||
stocks = ('6EV24', '6EU24', 'ESU24', 'OESU24 C5450', 'ONQU24 C18900', 'OESU24 C6000') # 해외선물옵션
|
||||
for scode in stocks:
|
||||
subscribe(ws, KIS_WSReq.BID_ASK, _connect_key, scode) # 실시간 호가(미국)
|
||||
subscribe(ws, KIS_WSReq.CONTRACT, _connect_key, scode) # 실시간 체결
|
||||
|
||||
# unsubscribe(ws, KIS_WSReq.CONTRACT, _connect_key, "RBAQAAPL") #실시간 체결 연결해제
|
||||
# subscribe(ws, KIS_WSReq.CONTRACT, _connect_key, "RBAQAAPL") #실시간 체결 연결등록
|
||||
# unsubscribe(ws, KIS_WSReq.BID_USA, _connect_key, "RBAQAAPL") #실시간 호가(미국) 연결해제
|
||||
# subscribe(ws, KIS_WSReq.BID_USA, _connect_key, "RBAQAAPL") #실시간 호가(미국) 연결등록
|
||||
# 실시간 계좌체결발생통보를 등록한다. 계좌체결발생통보 결과는 executed_df 에 저장된다.
|
||||
#subscribe(ws, KIS_WSReq.ORDERNOTICE, _connect_key, "HTS ID 입력") # "HTS ID 입력 하세요" 계좌주문내역발생통보
|
||||
subscribe(ws, KIS_WSReq.CCLDNOTICE, _connect_key, "HTS ID 입력") # "HTS ID 입력 하세요" 계좌체결내역발생통보
|
||||
|
||||
|
||||
ws = websocket.WebSocketApp("ws://ops.koreainvestment.com:21000/tryitout",
|
||||
on_open=on_open, on_message=on_message, on_error=on_error, on_data=on_data)
|
||||
|
||||
ws.run_forever() # 실시간 웹소켓 연결 작동
|
||||
1581
한국투자증권(API)/legacy/Sample01/kis_ovrseastk.py
Normal file
1581
한국투자증권(API)/legacy/Sample01/kis_ovrseastk.py
Normal file
File diff suppressed because it is too large
Load Diff
468
한국투자증권(API)/legacy/Sample01/kis_ovrseastk_ws.py
Normal file
468
한국투자증권(API)/legacy/Sample01/kis_ovrseastk_ws.py
Normal file
@@ -0,0 +1,468 @@
|
||||
# 해외주식 실시간 websocket sample
|
||||
import websocket
|
||||
|
||||
import kis_auth as ka
|
||||
import kis_ovrseastk as kb
|
||||
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
import time
|
||||
import datetime
|
||||
|
||||
from io import StringIO
|
||||
from threading import Thread
|
||||
|
||||
from collections import namedtuple, deque
|
||||
|
||||
try:
|
||||
import websockets
|
||||
|
||||
except ImportError:
|
||||
print("websocket-client 설치중입니다.")
|
||||
os.system('python3 -m pip3 install websocket-client')
|
||||
|
||||
from enum import StrEnum
|
||||
|
||||
|
||||
class KIS_WSReq(StrEnum):
|
||||
BID_USA = 'HDFSASP0' # 해외주식 실시간지연호가(미국)
|
||||
BID_ASA = 'HDFSASP1' # 해외주식 실시간지연호가(아시아)
|
||||
CONTRACT = 'HDFSCNT0' # 해외주식 실시간지연 체결가
|
||||
NOTICE = 'H0GSCNI0' # 실시간 해외주식 체결통보
|
||||
|
||||
import talib as ta
|
||||
|
||||
|
||||
class BasicPlan:
|
||||
|
||||
def __init__(self, stock_code, window=20):
|
||||
self._stock_code = stock_code
|
||||
self._queue = deque(maxlen=window)
|
||||
self._prev_ma = None
|
||||
|
||||
def push(self, value):
|
||||
self._queue.append(value)
|
||||
ma = sum(self._queue) / len(self._queue)
|
||||
diff = ma - self._prev_ma if self._prev_ma is not None else None
|
||||
self._prev_ma = ma
|
||||
|
||||
print(f"{self._stock_code}****** value: {value}, MA: {ma}, diff: {diff}...")
|
||||
|
||||
class RSI_ST: # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산
|
||||
def __init__(self, stock_code, window=21):
|
||||
self._stock_code = stock_code
|
||||
self._queue = deque(maxlen=window)
|
||||
self.rsi_period = window
|
||||
|
||||
def eval(self):
|
||||
# dftt = getStreamdDF(self._stock_code)
|
||||
# print(self)
|
||||
dftt = contract_sub_df.get(self._stock_code).copy()
|
||||
dftt = dftt.set_index(['TICK_HOUR'])
|
||||
dftt['LAST'] = pd.to_numeric(dftt['LAST'], errors='coerce').convert_dtypes()
|
||||
np_closes = np.array(dftt['LAST'], dtype=np.float64)
|
||||
rsi = ta.RSI(np_closes, self.rsi_period)
|
||||
|
||||
last_rsi = rsi[-1]
|
||||
|
||||
if last_rsi < 30:
|
||||
print(f"({self._stock_code})[BUY] ***RSI: {last_rsi}") # 통상적으로 RSI가 30 이하면 과매도 상태인 것으로 판단하고 시장이 과도하게 하락했음을 나타냄
|
||||
elif last_rsi < 70 and last_rsi >= 30:
|
||||
print(f"({self._stock_code})[N/A] ***RSI: {last_rsi}")
|
||||
elif last_rsi >= 70:
|
||||
print(f"({self._stock_code})[SELL] ***RSI: {last_rsi}") # 통상적으로 RSI가 70 이상이면 과매수 상태로 간주하고 시장이 과열되었을 가능성이 있음을 나타냄
|
||||
else:
|
||||
print(self._stock_code)
|
||||
|
||||
|
||||
_today__ = datetime.date.today().strftime("%Y%m%d")
|
||||
|
||||
ka.auth()
|
||||
|
||||
__DEBUG__ = False # True
|
||||
|
||||
# 실시간 해외주식 계좌체결통보 복호화를 위한 부분 - start
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import unpad
|
||||
from base64 import b64decode
|
||||
|
||||
|
||||
# AES256 DECODE: Copied from KIS Developers Github sample code
|
||||
def aes_cbc_base64_dec(key, iv, cipher_text):
|
||||
"""
|
||||
:param key: str type AES256 secret key value
|
||||
:param iv: str type AES256 Initialize Vector
|
||||
:param cipher_text: Base64 encoded AES256 str
|
||||
:return: Base64-AES256 decodec str
|
||||
"""
|
||||
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
|
||||
return bytes.decode(unpad(cipher.decrypt(b64decode(cipher_text)), AES.block_size))
|
||||
|
||||
|
||||
# 실시간 해외주식 계좌체결통보 복호화를 위한 부분 - end
|
||||
|
||||
|
||||
contract_sub_df = dict() # 실시간 해외주식 체결 결과를 종목별로 저장하기 위한 container
|
||||
tr_plans = dict() # 실시간 해외주식 체결 값에 따라 무언가를 수행할 Class 를 저장하기 위한 container
|
||||
excg_dict = {
|
||||
'NYS' : 'NYSE', #미국뉴욕
|
||||
'NAS' : 'NASD', #미국나스닥
|
||||
'AMS' : 'AMEX', #미국아멕스
|
||||
'TSE' : 'TKSE', #일본도쿄
|
||||
'HKS' : 'SEHK', #홍콩
|
||||
'SHS' : 'SHAA', #중국상해
|
||||
'SZS' : 'SZAA', #중국심천
|
||||
'HSX' : 'VNSE', #베트남호치민,
|
||||
'HNX' : 'HASE', #베트남하노이
|
||||
'BAY' : 'NYSE', #미국뉴욕(주간)
|
||||
'BAQ' : 'NASD', #미국나스닥(주간),
|
||||
'BAA' : 'AMEX' #미국아멕스(주간)
|
||||
}
|
||||
|
||||
|
||||
#reserved_cols = ['TICK_HOUR', 'STCK_PRPR', 'ACML_VOL'] # 실시간 해외주식 체결 중 사용할 수신시간, 현재가, 누적거래량 만 추출하기 위한 column 정의
|
||||
reserved_cols = ['TICK_HOUR', 'LAST'] # 실시간 해외주식 체결 중 사용할 column 만 추출하기 위한 column 정의
|
||||
|
||||
# 해외주식 실시간지연체결가 column header
|
||||
contract_cols = ['RSYM', # 실시간종목코드
|
||||
'SYMB', # 종목코드
|
||||
'ZDIV', # 수수점자리수
|
||||
'TYMD', # 현지영업일자
|
||||
'XYMD', # 현지일자
|
||||
'XHMS', # 현지시간
|
||||
'KYMD', # 한국일자
|
||||
'TICK_HOUR', # pandas time conversion 편의를 위해 이 필드만 이름을 통일한다 'KHMS' 한국시간
|
||||
'OPEN', # 시가
|
||||
'HIGH', # 고가
|
||||
'LOW', # 저가
|
||||
'LAST', # 현재가
|
||||
'SIGN', # 대비구분
|
||||
'DIFF', # 전일대비
|
||||
'RATE', # 등락율
|
||||
'PBID', # 매수호가
|
||||
'PASK', # 매도호가
|
||||
'VBID', # 매수잔량
|
||||
'VASK', # 매도잔량
|
||||
'EVOL', # 체결량
|
||||
'TVOL', # 거래량
|
||||
'TAMT', # 거래대금
|
||||
'BIVL', # 매도체결량
|
||||
'ASVL', # 매수체결량
|
||||
'STRN', # 체결강도
|
||||
'MTYP'] # 시장구분 1:장중,2:장전,3:장후
|
||||
|
||||
# 실시간 해외주식호가(미국) column eader
|
||||
bid_usa_cols = ['RSYM', #실시간종목코드
|
||||
'SYMB', # 종목코드
|
||||
'ZDIV', # 소숫점자리수
|
||||
'XYMD', # 현지일자
|
||||
'XHMS', # 현지시간
|
||||
'KYMD', # 한국일자
|
||||
'TICK_HOUR', # pandas time conversion 편의를 위해 이 필드만 이름을 통일한다 'KHMS' 한국시간
|
||||
'BVOL', # 매수총잔량
|
||||
'AVOL', # 매도총잔량
|
||||
'BDVL', # 매수총잔량대비
|
||||
'ADVL', # 매도총잔량대비
|
||||
'PBID1', # 매수호가1
|
||||
'PASK1', # 매도호가1
|
||||
'VBID1', # 매수잔량1
|
||||
'VASK1', # 매도잔량1
|
||||
'DBID1', # 매수잔량대비1
|
||||
'DASK1' ] # 매도잔량대비1
|
||||
|
||||
# 실시간 해외주식호가(아시아) column eader
|
||||
bid_asa_cols = ['RSYM', #실시간종목코드
|
||||
'SYMB', #종목코드
|
||||
'ZDIV', #소수점자리수
|
||||
'XYMD', #현지일자
|
||||
'XHMS', #현지시간
|
||||
'KYMD', #한국일자
|
||||
'TICK_HOUR', # pandas time conversion 편의를 위해 이 필드만 이름을 통일한다 'KHMS' 한국시간
|
||||
'BVOL', #매수총잔량
|
||||
'AVOL', #매도총잔량
|
||||
'BDVL', #매수총잔량대비
|
||||
'ADVL', #매도총잔량대비
|
||||
'PBID1', #매수호가1
|
||||
'PASK1', #매도호가1
|
||||
'VBID1', #매수잔량1
|
||||
'VASK1', #매도잔량1
|
||||
'DBID1', #매수잔량대비1
|
||||
'DASK1'] #매도잔량대비1
|
||||
|
||||
|
||||
# 실시간 계좌체결발생통보 column header
|
||||
notice_cols = ['CUST_ID', # HTS ID
|
||||
'ACNT_NO',
|
||||
'ODER_NO', # 주문번호
|
||||
'OODER_NO', # 원주문번호
|
||||
'SELN_BYOV_CLS', # 매도매수구분
|
||||
'RCTF_CLS', # 정정구분
|
||||
'ODER_KIND2', # 주문종류2(1:시장가 2:지정자 6:단주시장가 7:단주지정가 A:MOO B:LOO C:MOC D:LOC)
|
||||
'STCK_SHRN_ISCD', # 주식 단축 종목코드
|
||||
'CNTG_QTY', # 체결 수량 - 주문통보의 경우 해당 위치에 주문수량이 출력, - 체결통보인 경우 해당 위치에 체결수량이 출력
|
||||
'CNTG_UNPR', # 체결단가 ※ 주문통보 시에는 주문단가가, 체결통보 시에는 체결단가가 수신 됩니다. ※ 체결단가의 경우, 국가에 따라 소수점 생략 위치가 상이합니다.
|
||||
# 미국 4 일본 1 중국 3 홍콩 3 베트남 0 EX) 미국 AAPL(현재가 : 148.0100)의 경우 001480100으로 체결단가가 오는데, 4번째 자리에 소수점을 찍어 148.01로 해석하시면 됩니다.
|
||||
'STCK_CNTG_HOUR', # 주식 체결 시간
|
||||
'RFUS_YN', # 거부여부(0 : 승인, 1 : 거부)
|
||||
'CNTG_YN', # 체결여부(1 : 주문,정정,취소,거부,, 2 : 체결 (★ 체결만 볼 경우 2번만 ))
|
||||
'ACPT_YN', # 접수여부(1:주문접수 2:확인 3:취소(FOK/IOC))
|
||||
'BRNC_NO', # 지점
|
||||
'ODER_QTY', # 주문수량 - 주문통보인 경우 해당 위치 미출력 (주문통보의 주문수량은 CNTG_QTY 위치에 출력) - 체결통보인 경우 해당 위치에 주문수량이 출력
|
||||
'ACNT_NAME', # 계좌명
|
||||
'CNTG_ISNM', # 체결종목명
|
||||
'ODER_COND', # 해외종목구분
|
||||
'DEBT_GB', # 담보유형코드 10:현금 15:해외주식담보대출
|
||||
'DEBT_DATE'] # 담보대출일자 대출일(YYYYMMDD)
|
||||
|
||||
|
||||
# 웹소켓 접속키 발급
|
||||
def get_approval():
|
||||
url = ka.getTREnv().my_url
|
||||
headers = {"content-type": "application/json"}
|
||||
body = {"grant_type": "client_credentials",
|
||||
"appkey": ka.getTREnv().my_app,
|
||||
"secretkey": ka.getTREnv().my_sec}
|
||||
PATH = "oauth2/Approval"
|
||||
URL = f"{url}/{PATH}"
|
||||
res = requests.post(URL, headers=headers, data=json.dumps(body))
|
||||
approval_key = res.json()["approval_key"]
|
||||
return approval_key
|
||||
|
||||
|
||||
_connect_key = get_approval() # websocker 연결Key
|
||||
_iv = None # for 복호화
|
||||
_ekey = None # for 복호화
|
||||
executed_df = pd.DataFrame(data=None, columns=contract_cols) # 체결통보 저장용 DF
|
||||
|
||||
|
||||
# added_data 는 종목코드(실시간체결, 실시간호가) 또는 HTS_ID(체결통보)
|
||||
def _build_message(app_key, tr_id, added_data, tr_type='1'):
|
||||
_h = {
|
||||
"approval_key": app_key,
|
||||
"custtype": 'P',
|
||||
"tr_type": tr_type,
|
||||
"content-type": "utf-8"
|
||||
}
|
||||
_inp = {
|
||||
"tr_id": tr_id,
|
||||
"tr_key": added_data
|
||||
}
|
||||
_b = {
|
||||
"input": _inp
|
||||
}
|
||||
_data = {
|
||||
"header": _h,
|
||||
"body": _b
|
||||
}
|
||||
|
||||
d1 = json.dumps(_data)
|
||||
|
||||
return d1
|
||||
|
||||
|
||||
# sub_data 는 종목코드(실시간체결, 실시간호가) 또는 HTS_ID(실시간 계좌체결발생통보)
|
||||
def subscribe(ws, sub_type, app_key, sub_data): # 세션 종목코드(실시간체결, 실시간호가) 등록
|
||||
ws.send(_build_message(app_key, sub_type, sub_data), websocket.ABNF.OPCODE_TEXT)
|
||||
|
||||
time.sleep(.1)
|
||||
|
||||
|
||||
def unsubscribe(ws, sub_type, app_key, sub_data): # 세션 종목코드(실시간체결, 실시간호가) 등록해제
|
||||
ws.send(_build_message(app_key, sub_type, sub_data, '2'), websocket.ABNF.OPCODE_TEXT)
|
||||
|
||||
time.sleep(.1)
|
||||
|
||||
|
||||
# streaming data 를 이용해 주어진 bar 크기(예: 1분, 5분 등)의 OHLC(x분봉) 데이터프레임을 반환한다.
|
||||
# 이때 streamign data 는 websocket client 가 시작한 다음부터 지금까지의 해당 종목의 가격 정보를 의미한다.
|
||||
# ** 동시호가 시간은 OHLC data 가 모두 NA 가 된다.
|
||||
def getStreamdDF(stock_code, bar_sz='1Min'):
|
||||
df3 = contract_sub_df.get(stock_code).copy()
|
||||
df3 = df3.set_index(['TICK_HOUR'])
|
||||
df3['LAST'] = pd.to_numeric(df3['LAST'], errors='coerce').convert_dtypes()
|
||||
df3 = df3['LAST'].resample(bar_sz).ohlc()
|
||||
|
||||
return df3
|
||||
|
||||
# 수신데이터 파싱
|
||||
def _dparse(data):
|
||||
global executed_df
|
||||
d1 = data.split("|")
|
||||
dp_ = None
|
||||
|
||||
hcols = []
|
||||
|
||||
if len(d1) >= 4:
|
||||
tr_id = d1[1]
|
||||
if tr_id == KIS_WSReq.CONTRACT: # 실시간체결
|
||||
hcols = contract_cols
|
||||
elif tr_id == KIS_WSReq.BID_USA: # 해외주식 실시간지연호가(미국)
|
||||
hcols = bid_usa_cols
|
||||
elif tr_id == KIS_WSReq.BID_ASA: # 해외주식 실시간지연호가(아시아)
|
||||
hcols = bid_asa_cols
|
||||
elif tr_id == KIS_WSReq.NOTICE: # 계좌체결통보
|
||||
hcols = notice_cols
|
||||
else:
|
||||
pass
|
||||
|
||||
if tr_id in (KIS_WSReq.CONTRACT, KIS_WSReq.BID_USA, KIS_WSReq.BID_ASA): # 실시간체결, 실시간지연호가(미국), 실시간지연호가(아시아)
|
||||
dp_ = pd.read_csv(StringIO(d1[3]), header=None, sep='^', names=hcols, dtype=object) # 수신데이터 parsing
|
||||
|
||||
print(dp_) # 실시간체결, 실시간호가 수신 데이터 파싱 결과 확인
|
||||
|
||||
dp_['TICK_HOUR'] = _today__ + dp_['TICK_HOUR'] # 수신시간
|
||||
dp_['TICK_HOUR'] = pd.to_datetime(dp_['TICK_HOUR'], format='%Y%m%d%H%M%S', errors='coerce')
|
||||
else: # 실시간 계좌체결발생통보는 암호화되어서 수신되므로 복호화 과정이 필요
|
||||
dp_ = pd.read_csv(StringIO(aes_cbc_base64_dec(_ekey, _iv, d1[3])), header=None, sep='^', names=hcols, # 수신데이터 parsing 및 복호화
|
||||
dtype=object)
|
||||
|
||||
print(dp_) # 실시간 계좌체결발생통보 수신 파싱 결과 확인
|
||||
|
||||
if __DEBUG__: print(f'***EXECUTED NOTICE [{dp_.to_string(header=False, index=False)}]')
|
||||
|
||||
if tr_id == KIS_WSReq.CONTRACT: # 실시간 체결
|
||||
if __DEBUG__: print(dp_.to_string(header=False, index=False))
|
||||
stock_code = dp_[dp_.columns[0]].values.tolist()[0]
|
||||
df2_ = dp_[reserved_cols]
|
||||
# dft_ = pd.concat([contract_sub_df.get(stock_code), df2_], axis=0, ignore_index=True)
|
||||
# 선택된 열이 비어 있거나 모든 값이 NA인지 확인
|
||||
selected_df = contract_sub_df.get(stock_code)
|
||||
if selected_df is not None and not selected_df.dropna().empty:
|
||||
dft_ = pd.concat([selected_df, df2_], axis=0, ignore_index=True)
|
||||
else:
|
||||
dft_ = df2_
|
||||
contract_sub_df[stock_code] = dft_
|
||||
|
||||
######### 이 부분에서 로직을 적용한 후 매수/매도를 수행하면 될 듯!!
|
||||
|
||||
val1 = dp_['LAST'].tolist()[0]
|
||||
tr_plans[stock_code].push(float(val1)) # 이동평균값 활용
|
||||
# tr_plans[stock_code].eval() # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산 활용
|
||||
|
||||
excg_df = excg_dict[stock_code[1:4]] # 해외거래소코드(3자리) 주문API 사용가능 해외거래소코드(4자리) 변환
|
||||
stock_df = dp_['SYMB'].tolist()[0] # 종목코드
|
||||
# [국내주식] 주문/계좌 > 매수가능조회 (종목번호 5자리 + 종목단가) REST API
|
||||
#rt_data = kb.get_inquire_psbl_order(pdno=stock_code, ord_unpr=val1, itm_no="TSLA")
|
||||
rt_data = kb.get_overseas_inquire_psamount(excg=excg_df, itm_no=stock_df)
|
||||
ord_qty = rt_data.loc[0, 'ord_psbl_qty'] # ord_psbl_qty 주문가능수량 또는 외화인 경우 max_ord_psbl_qty 최대주문가능수량
|
||||
print("[주문가능수량!] : " + ord_qty)
|
||||
|
||||
###########################################################
|
||||
# 해외주식(미국) 현금 주문
|
||||
# rt_data = kb.get_overseas_order(ord_dv="buy", excg_cd=excg_df, itm_no=stock_df, qty=1, unpr=123.3)
|
||||
# print(rt_data.KRX_FWDG_ORD_ORGNO + "+" + rt_data.ODNO + "+" + rt_data.ORD_TMD) # 주문접수조직번호+주문접수번호+주문시각
|
||||
# 해외주식(미국) 현금 주문(주간)
|
||||
# rt_data = kb.get_overseas_daytime_order(ord_dv="buy", excg_cd=excg_df, itm_no=stock_df, qty=1, unpr=123.3)
|
||||
# print(rt_data.KRX_FWDG_ORD_ORGNO + "+" + rt_data.ODNO + "+" + rt_data.ORD_TMD) # 주문접수조직번호+주문접수번호+주문시각
|
||||
print("매수/매도 조건 주문 : " + val1)
|
||||
###########################################################
|
||||
|
||||
elif tr_id == KIS_WSReq.NOTICE: # 체결통보의 경우, 일단 executed_df 에만 저장해 둠
|
||||
if __DEBUG__: print(dp_.to_string(header=False, index=False))
|
||||
executed_df = pd.concat([executed_df, dp_], axis=0, ignore_index=True)
|
||||
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
print("Data length error...{data}")
|
||||
|
||||
|
||||
def _get_sys_resp(data):
|
||||
global _iv
|
||||
global _ekey
|
||||
|
||||
isPingPong = False
|
||||
isUnSub = False
|
||||
isOk = False
|
||||
tr_msg = None
|
||||
tr_key = None
|
||||
|
||||
rdic = json.loads(data)
|
||||
|
||||
tr_id = rdic['header']['tr_id']
|
||||
if tr_id != "PINGPONG": tr_key = rdic['header']['tr_key']
|
||||
if rdic.get("body", None) is not None:
|
||||
isOk = True if rdic["body"]["rt_cd"] == "0" else False
|
||||
tr_msg = rdic["body"]["msg1"]
|
||||
# 복호화를 위한 key 를 추출
|
||||
if 'output' in rdic["body"]:
|
||||
_iv = rdic["body"]["output"]["iv"]
|
||||
_ekey = rdic["body"]["output"]["key"]
|
||||
isUnSub = True if tr_msg[:5] == "UNSUB" else False
|
||||
else:
|
||||
isPingPong = True if tr_id == "PINGPONG" else False
|
||||
|
||||
nt2 = namedtuple('SysMsg', ['isOk', 'tr_id', 'tr_key', 'isUnSub', 'isPingPong'])
|
||||
d = {
|
||||
'isOk': isOk,
|
||||
'tr_id': tr_id,
|
||||
'tr_key': tr_key,
|
||||
'isUnSub': isUnSub,
|
||||
'isPingPong': isPingPong
|
||||
}
|
||||
|
||||
return nt2(**d)
|
||||
|
||||
|
||||
def on_data(ws, data, resp_type, data_continu):
|
||||
# print(f"On data => {resp_type}, {data_continu}, {data}") #return only 1, True
|
||||
pass
|
||||
|
||||
|
||||
def on_message(ws, data):
|
||||
if data[0] in ('0', '1'): # 실시간체결 or 실시간호가
|
||||
_dparse(data)
|
||||
else: # system message or PINGPONG
|
||||
rsp = _get_sys_resp(data)
|
||||
if rsp.isPingPong:
|
||||
ws.send(data, websocket.ABNF.OPCODE_PING)
|
||||
else:
|
||||
if (not rsp.isUnSub and rsp.tr_id == KIS_WSReq.CONTRACT):
|
||||
contract_sub_df[rsp.tr_key] = pd.DataFrame(columns=reserved_cols)
|
||||
|
||||
########################################################################
|
||||
#### 이 부분에서 전략을 수행할 class 를 등록한다.
|
||||
#### 실제 주문 실행은 _dparse 함수에서 처리
|
||||
tr_plans[rsp.tr_key] = BasicPlan(rsp.tr_key) # 이동 평균선 계산 (웹소켓 프로그램 실행시 수집된 데이터만 반영)
|
||||
# tr_plans[rsp.tr_key] = RSI_ST(rsp.tr_key) # RSI(Relative Strength Index, 상대강도지수)라는 주가 지표 계산
|
||||
########################################################################
|
||||
|
||||
elif (rsp.isUnSub):
|
||||
del (contract_sub_df[rsp.tr_key])
|
||||
else:
|
||||
print(rsp)
|
||||
|
||||
|
||||
def on_error(ws, error):
|
||||
print('error=', error)
|
||||
|
||||
def on_close(ws, status_code, close_msg):
|
||||
print('on_close close_status_code=', status_code, " close_msg=", close_msg)
|
||||
|
||||
|
||||
def on_open(ws):
|
||||
# stocks 에는 40개까지만 가능
|
||||
stocks = ('RBAQAAPL', 'RBAQTSLA', 'RBAQAMZN', 'RBAQNVDA', 'RBAQINTC', 'RBAQMSFT') # 미국주식 주간거래
|
||||
#stocks = ('DNASAAPL', 'DNASTSLA', 'DNASAMZN', 'DNASNVDA', 'DNASINTC', 'DNASMSFT') # 미국주식 야간거래(정규시장)
|
||||
for scode in stocks:
|
||||
subscribe(ws, KIS_WSReq.BID_USA, _connect_key, scode) # 실시간 호가(미국)
|
||||
#subscribe(ws, KIS_WSReq.BID_USA, _connect_key, scode) # 실시간 호가(아시아)
|
||||
subscribe(ws, KIS_WSReq.CONTRACT, _connect_key, scode) # 실시간 체결
|
||||
|
||||
# unsubscribe(ws, KIS_WSReq.CONTRACT, _connect_key, "RBAQAAPL") #실시간 체결 연결해제
|
||||
# subscribe(ws, KIS_WSReq.CONTRACT, _connect_key, "RBAQAAPL") #실시간 체결 연결등록
|
||||
# unsubscribe(ws, KIS_WSReq.BID_USA, _connect_key, "RBAQAAPL") #실시간 호가(미국) 연결해제
|
||||
# subscribe(ws, KIS_WSReq.BID_USA, _connect_key, "RBAQAAPL") #실시간 호가(미국) 연결등록
|
||||
# 실시간 계좌체결발생통보를 등록한다. 계좌체결발생통보 결과는 executed_df 에 저장된다.
|
||||
subscribe(ws, KIS_WSReq.NOTICE, _connect_key, "HTS ID 입력") # HTS ID 입력 계좌체결발생통보
|
||||
|
||||
|
||||
ws = websocket.WebSocketApp("ws://ops.koreainvestment.com:21000/tryitout",
|
||||
on_open=on_open, on_message=on_message, on_error=on_error, on_data=on_data)
|
||||
|
||||
ws.run_forever() # 실시간 웹소켓 연결 작동
|
||||
Reference in New Issue
Block a user