이하영

open api를 통한 정보 조회 기능 추가

No preview for this file type
def errors(err_code):
err_dict={0:('OP_ERR_NONE','정상처리'),
-10:('OP_ERR_FAIL','실패'),
-100:('OP_ERR_LOGIN','사용자정보교환실패'),
-101:('OP_ERR_CONNECT','서버접속실패'),
-102:('OP_ERR_VERSION','버전처리실패'),
-103:('OP_ERR_FAIRWALL','개인방화벽실패'),
-104:('OP_ERR_MEMORY','메모리보호실패'),
-105:('OP_ERR_INPUT','함수입력값오류'),
-106:('OP_ERR_SOCKET_CLOSED','통신연결종료'),
-200:('OP_ERR_SISE_OVERFLOW','시세조회과부화'),
-201:('OP_ERR_RQ_STRUCT_FAIL','전문작성초기화실패'),
-202:('OP_ERR_RQ_STRING_FAIL','전문작성입력값오류'),
-203:('OP_ERR_NO_DATA','데이터없음'),
-204:('OP_ERR_OVER_MAX_DATA','조회가능한종목수초과'),
-205:('OP_ERR_DATA_RCV_FAIL','데이터수신실패'),
-206:('OP_ERR_OVER_MAX_FID','조회가능한FID수초과'),
-207:('OP_ERR_REAL_CANCEL','실시간해제오류'),
-300:('OP_ERR_ORD_WRONG_INPUT','입력값오류'),
-301:('OP_ERR_ORD_WRONG_ACCNO','계좌비밀번호없음'),
-302:('OP_ERR_OTHER_ACC_USE','타인계좌사용오류'),
-303:('OP_ERR_MIS_2BILL_EXC','주문가격이20억원을초과'),
-304:('OP_ERR_MIS_5BILL_EXC','주문가격이50억원을초과'),
-305:('OP_ERR_MIS_1PER_EXC','주문수량이총발행주수의1 % 초과오류'),
-306:('OP_ERR_MIS_3PER_EXC','주문수량이총발행주수의3 % 초과오류'),
-307:('OP_ERR_SEND_FAIL','주문전송실패'),
-308:('OP_ERR_ORD_OVERFLOW','주문전송과부화'),
-309:('OP_ERR_MIS_300CNT_EXC','주문수량300계약초과'),
-310:('OP_ERR_MIS_500CNT_EXC','주문수량500계약초과'),
-340:('OP_ERR_ORD_WRONG_ACCINFO','계좌정보없음'),
-500:('OP_ERR_ORD_SYMCODE_EMPTY','종목코드없음')
}
result=err_dict[err_code]
return result
\ No newline at end of file
from PyQt5.QtWidgets import *
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from PyQt5.QtTest import *
from pandas import DataFrame
import time
import sys
import logging.handlers
import errCode
# set formatter object
formatter = logging.Formatter('[%(levelname)s|%(filename)s:%(lineno)s] %(asctime)s > %(message)s')
logger = logging.getLogger("crumbs")
logger.setLevel(logging.DEBUG)
# set handler object
streamHandler = logging.StreamHandler()
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)
TR_REQ_TIME_INTERVAL = 0.2
TR_REQ_TIME_INTERVAL = 3600
class OpenApi(QAxWidget):
def __init__(self):
......@@ -23,36 +14,63 @@ class OpenApi(QAxWidget):
# event_loop list
self.login_event_loop=None
self.detail_account_info_event_loop=QEventLoop()
self.calculator_event_loop=QEventLoop()
# variable list
self.account_no=None # 계좌번호
self.account_stock_dict={} # 보유종목
self.not_account_stock_dict={} # 미체결종목
self.calc_data=[] # 종목분석용
# account variable list
self.user_money=0
self.user_money_percent=0.5
# screen number list
self.screen_my_info="2000"
self.screen_calculation_stock="4000"
self._get_instance()
self._event_slot()
self.comm_connect()
self.account_info()
self.account_info() # 계좌정보 가져오기
self.detail_account_info() # 예수금 가져오기
self.detail_account_mystock() # 계좌평가잔고내역 가져오기
self.not_concluded_account() # 미체결 종목 조회
# get ocx controller for kioom open api
self.calculator_fnc() # 종목분석용용
#get ocx controller to use kiwoom open api
def _get_instance(self):
try:
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
except Exception as e:
logger.critical(e)
print(e)
sys.exit()
# deal events for using open api
# event slot to use open api
def _event_slot(self):
try:
self.OnEventConnect.connect(self.login_slot)
self.OnEventConnect.connect(self.login_slot) # for login
self.OnReceiveTrData.connect(self.trdata_slot) # for tr data
except Exception as e:
logger.critical(e)
print(e)
sys.exit()
# if login event success, return errCode=0
def login_slot(self,errCode):
# login event slot
# param code : if login success, code=0
def login_slot(self,code):
try:
if errCode==0:
logger.debug("connected")
result=errCode.errors(code)
if code==0:
print("Connected - ",result[0],result[1])
else:
logger.debug("failed to connect. err_code : "+errCode)
print("Failed to connect",result[0],result[1])
self.login_event_loop.exit()
except Exception as e:
logger.critical(e)
print(e)
sys.exit()
# try login
def comm_connect(self):
......@@ -61,24 +79,248 @@ class OpenApi(QAxWidget):
self.login_event_loop = QEventLoop() # wait for login complete
self.login_event_loop.exec_()
except Exception as e:
logger.critical(e)
print(e)
sys.exit()
# get user account information
# get user information
# param tag: ACCNO - 보유계좌리스트
# ACCOUNT_CNT - 보유계좌 수
# USER_ID - 사용자 ID
# USER_NAME - 사용자 이름
# KEY_BSECGB - 키보드 보안 해제 여부 (0 : 정상, 1: 해지)
# FIREW_SECGB - 방화벽 설정여부 (0 : 미설정, 1: 설정, 2 : 해지)
# GetServerGubun - 접속서버 구분 (1 : 모의투자, 나머지 : 실서버)
# return: tag를 통해 요청한 정보(ret) 반환
def get_login_info(self,tag):
try:
ret=self.dynamicCall("GetLoginInfo(QString)",tag)
return ret
except Exception as e:
logger.critical(e)
print(e)
sys.exit()
# print user account information
def account_info(self):
try:
account_number=self.get_login_info("ACCNO")
self.account_no=account_number.split(";")[0]
logger.debug("계좌번호 : "+self.account_no)
print("계좌번호 : ",self.account_no)
except AttributeError as e:
logger.critical(e)
print(e)
sys.exit()
# detail information about deposit
def detail_account_info(self):
self.dynamicCall("SetInputValue(String,String)","계좌번호",self.account_no)
self.dynamicCall("SetInputValue(String,String)","비밀번호",'0000')
self.dynamicCall("SetInputValue(String,String)","비밀번호입력매체구분",'00')
self.dynamicCall("SetInputValue(String,String)","조회구분",'2')
self.dynamicCall("CommRqData(String,String,int,String)","예수금상세현황요청","opw00001","0",self.screen_my_info)
self.detail_account_info_event_loop.exec_()
# detail information about account
def detail_account_mystock(self,sPreNext="0"):
self.dynamicCall("SetInputValue(QString,QString)","계좌번호",self.account_no)
self.dynamicCall("SetInputValue(QString,QString)","비밀번호",'0000')
self.dynamicCall("SetInputValue(QString,QString)","비밀번호입력매체구분",'00')
self.dynamicCall("SetInputValue(QString,QString)","조회구분",'2')
self.dynamicCall("CommRqData(QString,QString,int,QString)","계좌평가잔고내역요청","opw00018",sPreNext,"2000")
self.detail_account_info_event_loop.exec_()
# detail information about outstanding order
def not_concluded_account(self,sPrevNext="0"):
self.dynamicCall("SetInputValue(QString,QString)","계좌번호",self.account_no)
self.dynamicCall("SetInputValue(QString,QString)","체결구분",'1')
self.dynamicCall("SetInputValue(QString,QString)","매매구분",'0')
self.dynamicCall("CommRqData(QString,QString,int,QString)","실시간미체결요청",'opt10075',sPrevNext,self.screen_my_info)
self.detail_account_info_event_loop.exec_()
# slot receiving tr request
# param sScrNo: 스크린번호
# param sRQName: 요청했을 때 지은 이름
# param sTrCode: 요청 id, tr코드
# param sRecordName: 레코드 이름
# param sPrevNext: 다음 페이지가 있는지 여부. "2" : 다음페이지 존재, "0" or "" : 다음페이지 없음
def trdata_slot(self,sScrNo,sRQName,sTrCode,sRecordName,sPrevNext):
if sRQName=="예수금상세현황요청":
deposit=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,0,"예수금")
possible=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,0,"출금가능금액")
order_possible=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,0,"주문가능금액")
self.user_money=int(deposit)*self.user_money_percent
self.user_money=self.user_money/4
print("예수금 : ",int(deposit))
print("출금가능금액 : ",int(possible))
print("주문가능금액 : ",int(order_possible))
self.detail_account_info_event_loop.exit()
elif sRQName=="계좌평가잔고내역요청":
total_buy_money = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, 0, "총매입금액")
total_profit_rate=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,0,"총수익률(%)")
print("총매입금액 : ",int(total_buy_money))
print("총수익률(%) : ",float(total_profit_rate))
rows=self.dynamicCall("GetRepeatCnt(QString,QString)",sTrCode,sRQName) # 보유종목수 반환
cnt=0
for i in range(rows):
code=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"종목번호")
code=code.strip()[1:]
code_nm = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "종목명")
stock_quantity=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"보유수량")
buy_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"매입가")
learn_rate=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"수익률(%)")
current_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"현재가")
total_chegual_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"매입금액")
possible_quantity=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"매매가능수량")
if code in self.account_stock_dict:
pass
else:
self.account_stock_dict.update({code:{}})
code_nm=code_nm.strip()
buy_price=int(buy_price.strip())
learn_rate=float(learn_rate.strip())
current_price=int(current_price.strip())
total_chegual_price=int(total_chegual_price)
possible_quantity=int(possible_quantity)
acd=self.account_stock_dict[code]
acd.update({"종목명":code_nm})
acd.update({"보유수량":stock_quantity})
acd.update({"매입가":buy_price})
acd.update({"수익률(%)":learn_rate})
acd.update({"현재가":current_price})
acd.update({"매입금액":total_chegual_price})
acd.update({"매매가능수량":possible_quantity})
cnt+=1
print("계좌 종목 : ",self.account_stock_dict)
print("계좌 종목 수 : ",cnt)
if sPrevNext=="2":
self.detail_account_mystock(sPreNext="2") # If the next page exists, repeat the process
else:
self.detail_account_info_event_loop.exit()
elif sRQName=="실시간미체결요청":
rows=self.dynamicCall("GetRepeatCnt(QString,QString)",sTrCode,sRQName)
for i in range(rows):
code=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"종목번호")
code=code.strip()
code_nm = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "종목명")
order_no = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문번호")
order_status = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문상태") # 접수, 확인, 체결
order_quantity = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문수량")
order_price = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문가격")
order_gubun = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문구분") # 매도, 매수,
not_quantity= self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "미체결수량")
ok_quantity = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "체결량")
code_nm=code_nm.strip()
order_no=int(order_no.strip())
order_status=order_status.strip()
order_quantity=int(order_quantity.strip())
order_price=int(order_price.strip())
order_gubun=order_gubun.lstrip("+").lstrip('-')
not_quantity=int(not_quantity.strip())
ok_quantity=int(ok_quantity.strip())
if order_no in self.not_account_stock_dict:
pass
else:
self.not_account_stock_dict[order_no]={}
nasd=self.not_account_stock_dict[order_no]
nasd.update({"종목코드":code})
nasd.update({"주문명":code_nm})
nasd.update({"주분번호":order_no})
nasd.update({"주문상태":order_status})
nasd.update({"주문수량":order_quantity})
nasd.update({"주문가격":order_price})
nasd.update({"주문구분":order_gubun})
nasd.update({"미체결수량":not_quantity})
nasd.update({"체결량":ok_quantity})
print("미체결 종목",self.not_account_stock_dict[order_no])
self.detail_account_info_event_loop.exit()
elif sRQName=="주식일봉차트조회":
code=self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, "0", "종목코드")
code=code.strip()
print(code," 일봉데이터 요청")
cnt=self.dynamicCall("GetRepeatCnt(QString,QString)",sTrCode,sRQName)
print("데이터 일수", cnt)
# 한 번 조회시 600일치까지 조회 가능
for i in range(cnt):
data=[]
current_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"현재가") # 종가
value=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"거래량")
trading_value=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"거래대금")
date=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"일자")
start_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"시가")
high_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"고가")
low_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"저가")
data.append("")
data.append(current_price.strip())
data.append(value.strip())
data.append(trading_value.strip())
data.append(date.strip())
data.append(start_price.strip())
data.append(high_price.strip())
data.append(low_price.strip())
self.calc_data.append(data.copy())
print(len(self.calc_data))
if sPrevNext=="2":
self.day_kiwoom_db(code=code,sPrevNext=sPrevNext)
else:
self.calculator_event_loop.exit()
# 종목 코드를 반환
def get_code_list_by_market(self,market_code):
code_list=self.dynamicCall("GetCodeListByMarket(QString)",market_code)
code_list=code_list.split(";")[:-1]
return code_list
# 종목 분석 실행용 함수
def calculator_fnc(self):
code_list=self.get_code_list_by_market("10")
print("코스닥 갯수",len(code_list))
for idx,code in enumerate(code_list):
self.dynamicCall("DisconnectRealData(QString)",self.screen_calculation_stock)
print("%s / %s : KOSDAQ Stock Code : %s is updating..."%(idx+1,len(code_list),code))
self.day_kiwoom_db(code=code)
def day_kiwoom_db(self,code=None, date=None, sPrevNext="0"):
QTest.qWait(TR_REQ_TIME_INTERVAL)
self.dynamicCall("SetInputValue(QString,QString)","종목코드",code)
self.dynamicCall("SetInputValue(QString,QString)","수정주가구분","1")
if date!=None:
self.dynamicCall("SetInputValue(QString,QString)","기준일자",date)
self.dynamicCall("CommRqData(QString,QString,int,QString)","주식일봉차트조회","opt10081",sPrevNext,self.screen_calculation_stock)
self.calculator_event_loop.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
......