이하영

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

No preview for this file type
1 +def errors(err_code):
2 + err_dict={0:('OP_ERR_NONE','정상처리'),
3 + -10:('OP_ERR_FAIL','실패'),
4 + -100:('OP_ERR_LOGIN','사용자정보교환실패'),
5 + -101:('OP_ERR_CONNECT','서버접속실패'),
6 + -102:('OP_ERR_VERSION','버전처리실패'),
7 + -103:('OP_ERR_FAIRWALL','개인방화벽실패'),
8 + -104:('OP_ERR_MEMORY','메모리보호실패'),
9 + -105:('OP_ERR_INPUT','함수입력값오류'),
10 + -106:('OP_ERR_SOCKET_CLOSED','통신연결종료'),
11 + -200:('OP_ERR_SISE_OVERFLOW','시세조회과부화'),
12 + -201:('OP_ERR_RQ_STRUCT_FAIL','전문작성초기화실패'),
13 + -202:('OP_ERR_RQ_STRING_FAIL','전문작성입력값오류'),
14 + -203:('OP_ERR_NO_DATA','데이터없음'),
15 + -204:('OP_ERR_OVER_MAX_DATA','조회가능한종목수초과'),
16 + -205:('OP_ERR_DATA_RCV_FAIL','데이터수신실패'),
17 + -206:('OP_ERR_OVER_MAX_FID','조회가능한FID수초과'),
18 + -207:('OP_ERR_REAL_CANCEL','실시간해제오류'),
19 + -300:('OP_ERR_ORD_WRONG_INPUT','입력값오류'),
20 + -301:('OP_ERR_ORD_WRONG_ACCNO','계좌비밀번호없음'),
21 + -302:('OP_ERR_OTHER_ACC_USE','타인계좌사용오류'),
22 + -303:('OP_ERR_MIS_2BILL_EXC','주문가격이20억원을초과'),
23 + -304:('OP_ERR_MIS_5BILL_EXC','주문가격이50억원을초과'),
24 + -305:('OP_ERR_MIS_1PER_EXC','주문수량이총발행주수의1 % 초과오류'),
25 + -306:('OP_ERR_MIS_3PER_EXC','주문수량이총발행주수의3 % 초과오류'),
26 + -307:('OP_ERR_SEND_FAIL','주문전송실패'),
27 + -308:('OP_ERR_ORD_OVERFLOW','주문전송과부화'),
28 + -309:('OP_ERR_MIS_300CNT_EXC','주문수량300계약초과'),
29 + -310:('OP_ERR_MIS_500CNT_EXC','주문수량500계약초과'),
30 + -340:('OP_ERR_ORD_WRONG_ACCINFO','계좌정보없음'),
31 + -500:('OP_ERR_ORD_SYMCODE_EMPTY','종목코드없음')
32 + }
33 +
34 + result=err_dict[err_code]
35 + return result
...\ No newline at end of file ...\ No newline at end of file
1 from PyQt5.QtWidgets import * 1 from PyQt5.QtWidgets import *
2 from PyQt5.QAxContainer import * 2 from PyQt5.QAxContainer import *
3 from PyQt5.QtCore import * 3 from PyQt5.QtCore import *
4 +from PyQt5.QtTest import *
4 from pandas import DataFrame 5 from pandas import DataFrame
5 -import time
6 import sys 6 import sys
7 -import logging.handlers 7 +import errCode
8 8
9 -# set formatter object 9 +TR_REQ_TIME_INTERVAL = 3600
10 -formatter = logging.Formatter('[%(levelname)s|%(filename)s:%(lineno)s] %(asctime)s > %(message)s')
11 -logger = logging.getLogger("crumbs")
12 -logger.setLevel(logging.DEBUG)
13 -# set handler object
14 -streamHandler = logging.StreamHandler()
15 -streamHandler.setFormatter(formatter)
16 -logger.addHandler(streamHandler)
17 -
18 -TR_REQ_TIME_INTERVAL = 0.2
19 10
20 class OpenApi(QAxWidget): 11 class OpenApi(QAxWidget):
21 def __init__(self): 12 def __init__(self):
...@@ -23,36 +14,63 @@ class OpenApi(QAxWidget): ...@@ -23,36 +14,63 @@ class OpenApi(QAxWidget):
23 14
24 # event_loop list 15 # event_loop list
25 self.login_event_loop=None 16 self.login_event_loop=None
17 + self.detail_account_info_event_loop=QEventLoop()
18 + self.calculator_event_loop=QEventLoop()
19 +
20 + # variable list
21 + self.account_no=None # 계좌번호
22 + self.account_stock_dict={} # 보유종목
23 + self.not_account_stock_dict={} # 미체결종목
24 + self.calc_data=[] # 종목분석용
25 +
26 + # account variable list
27 + self.user_money=0
28 + self.user_money_percent=0.5
29 +
30 + # screen number list
31 + self.screen_my_info="2000"
32 + self.screen_calculation_stock="4000"
26 33
27 self._get_instance() 34 self._get_instance()
28 self._event_slot() 35 self._event_slot()
29 self.comm_connect() 36 self.comm_connect()
30 - self.account_info() 37 + self.account_info() # 계좌정보 가져오기
38 + self.detail_account_info() # 예수금 가져오기
39 + self.detail_account_mystock() # 계좌평가잔고내역 가져오기
40 + self.not_concluded_account() # 미체결 종목 조회
31 41
32 - # get ocx controller for kioom open api 42 + self.calculator_fnc() # 종목분석용용
43 +
44 + #get ocx controller to use kiwoom open api
33 def _get_instance(self): 45 def _get_instance(self):
34 try: 46 try:
35 self.setControl("KHOPENAPI.KHOpenAPICtrl.1") 47 self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
36 except Exception as e: 48 except Exception as e:
37 - logger.critical(e) 49 + print(e)
50 + sys.exit()
38 51
39 - # deal events for using open api 52 + # event slot to use open api
40 def _event_slot(self): 53 def _event_slot(self):
41 try: 54 try:
42 - self.OnEventConnect.connect(self.login_slot) 55 + self.OnEventConnect.connect(self.login_slot) # for login
56 + self.OnReceiveTrData.connect(self.trdata_slot) # for tr data
43 except Exception as e: 57 except Exception as e:
44 - logger.critical(e) 58 + print(e)
59 + sys.exit()
45 60
46 - # if login event success, return errCode=0 61 + # login event slot
47 - def login_slot(self,errCode): 62 + # param code : if login success, code=0
63 + def login_slot(self,code):
48 try: 64 try:
49 - if errCode==0: 65 + result=errCode.errors(code)
50 - logger.debug("connected") 66 + if code==0:
67 + print("Connected - ",result[0],result[1])
51 else: 68 else:
52 - logger.debug("failed to connect. err_code : "+errCode) 69 + print("Failed to connect",result[0],result[1])
53 self.login_event_loop.exit() 70 self.login_event_loop.exit()
54 except Exception as e: 71 except Exception as e:
55 - logger.critical(e) 72 + print(e)
73 + sys.exit()
56 74
57 # try login 75 # try login
58 def comm_connect(self): 76 def comm_connect(self):
...@@ -61,24 +79,248 @@ class OpenApi(QAxWidget): ...@@ -61,24 +79,248 @@ class OpenApi(QAxWidget):
61 self.login_event_loop = QEventLoop() # wait for login complete 79 self.login_event_loop = QEventLoop() # wait for login complete
62 self.login_event_loop.exec_() 80 self.login_event_loop.exec_()
63 except Exception as e: 81 except Exception as e:
64 - logger.critical(e) 82 + print(e)
83 + sys.exit()
65 84
66 - # get user account information 85 + # get user information
86 + # param tag: ACCNO - 보유계좌리스트
87 + # ACCOUNT_CNT - 보유계좌 수
88 + # USER_ID - 사용자 ID
89 + # USER_NAME - 사용자 이름
90 + # KEY_BSECGB - 키보드 보안 해제 여부 (0 : 정상, 1: 해지)
91 + # FIREW_SECGB - 방화벽 설정여부 (0 : 미설정, 1: 설정, 2 : 해지)
92 + # GetServerGubun - 접속서버 구분 (1 : 모의투자, 나머지 : 실서버)
93 + # return: tag를 통해 요청한 정보(ret) 반환
67 def get_login_info(self,tag): 94 def get_login_info(self,tag):
68 try: 95 try:
69 ret=self.dynamicCall("GetLoginInfo(QString)",tag) 96 ret=self.dynamicCall("GetLoginInfo(QString)",tag)
70 return ret 97 return ret
71 except Exception as e: 98 except Exception as e:
72 - logger.critical(e) 99 + print(e)
100 + sys.exit()
73 101
74 # print user account information 102 # print user account information
75 def account_info(self): 103 def account_info(self):
76 try: 104 try:
77 account_number=self.get_login_info("ACCNO") 105 account_number=self.get_login_info("ACCNO")
78 self.account_no=account_number.split(";")[0] 106 self.account_no=account_number.split(";")[0]
79 - logger.debug("계좌번호 : "+self.account_no) 107 + print("계좌번호 : ",self.account_no)
80 except AttributeError as e: 108 except AttributeError as e:
81 - logger.critical(e) 109 + print(e)
110 + sys.exit()
111 +
112 + # detail information about deposit
113 + def detail_account_info(self):
114 + self.dynamicCall("SetInputValue(String,String)","계좌번호",self.account_no)
115 + self.dynamicCall("SetInputValue(String,String)","비밀번호",'0000')
116 + self.dynamicCall("SetInputValue(String,String)","비밀번호입력매체구분",'00')
117 + self.dynamicCall("SetInputValue(String,String)","조회구분",'2')
118 + self.dynamicCall("CommRqData(String,String,int,String)","예수금상세현황요청","opw00001","0",self.screen_my_info)
119 +
120 + self.detail_account_info_event_loop.exec_()
121 +
122 + # detail information about account
123 + def detail_account_mystock(self,sPreNext="0"):
124 + self.dynamicCall("SetInputValue(QString,QString)","계좌번호",self.account_no)
125 + self.dynamicCall("SetInputValue(QString,QString)","비밀번호",'0000')
126 + self.dynamicCall("SetInputValue(QString,QString)","비밀번호입력매체구분",'00')
127 + self.dynamicCall("SetInputValue(QString,QString)","조회구분",'2')
128 + self.dynamicCall("CommRqData(QString,QString,int,QString)","계좌평가잔고내역요청","opw00018",sPreNext,"2000")
129 +
130 + self.detail_account_info_event_loop.exec_()
131 +
132 + # detail information about outstanding order
133 + def not_concluded_account(self,sPrevNext="0"):
134 + self.dynamicCall("SetInputValue(QString,QString)","계좌번호",self.account_no)
135 + self.dynamicCall("SetInputValue(QString,QString)","체결구분",'1')
136 + self.dynamicCall("SetInputValue(QString,QString)","매매구분",'0')
137 + self.dynamicCall("CommRqData(QString,QString,int,QString)","실시간미체결요청",'opt10075',sPrevNext,self.screen_my_info)
138 +
139 + self.detail_account_info_event_loop.exec_()
140 +
141 + # slot receiving tr request
142 + # param sScrNo: 스크린번호
143 + # param sRQName: 요청했을 때 지은 이름
144 + # param sTrCode: 요청 id, tr코드
145 + # param sRecordName: 레코드 이름
146 + # param sPrevNext: 다음 페이지가 있는지 여부. "2" : 다음페이지 존재, "0" or "" : 다음페이지 없음
147 + def trdata_slot(self,sScrNo,sRQName,sTrCode,sRecordName,sPrevNext):
148 + if sRQName=="예수금상세현황요청":
149 + deposit=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,0,"예수금")
150 + possible=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,0,"출금가능금액")
151 + order_possible=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,0,"주문가능금액")
152 +
153 + self.user_money=int(deposit)*self.user_money_percent
154 + self.user_money=self.user_money/4
155 +
156 + print("예수금 : ",int(deposit))
157 + print("출금가능금액 : ",int(possible))
158 + print("주문가능금액 : ",int(order_possible))
159 +
160 + self.detail_account_info_event_loop.exit()
161 +
162 + elif sRQName=="계좌평가잔고내역요청":
163 + total_buy_money = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, 0, "총매입금액")
164 + total_profit_rate=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,0,"총수익률(%)")
165 + print("총매입금액 : ",int(total_buy_money))
166 + print("총수익률(%) : ",float(total_profit_rate))
167 +
168 + rows=self.dynamicCall("GetRepeatCnt(QString,QString)",sTrCode,sRQName) # 보유종목수 반환
169 + cnt=0
170 + for i in range(rows):
171 + code=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"종목번호")
172 + code=code.strip()[1:]
173 +
174 + code_nm = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "종목명")
175 + stock_quantity=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"보유수량")
176 + buy_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"매입가")
177 + learn_rate=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"수익률(%)")
178 + current_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"현재가")
179 + total_chegual_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"매입금액")
180 + possible_quantity=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"매매가능수량")
181 +
182 + if code in self.account_stock_dict:
183 + pass
184 + else:
185 + self.account_stock_dict.update({code:{}})
186 +
187 + code_nm=code_nm.strip()
188 + buy_price=int(buy_price.strip())
189 + learn_rate=float(learn_rate.strip())
190 + current_price=int(current_price.strip())
191 + total_chegual_price=int(total_chegual_price)
192 + possible_quantity=int(possible_quantity)
193 +
194 + acd=self.account_stock_dict[code]
195 +
196 + acd.update({"종목명":code_nm})
197 + acd.update({"보유수량":stock_quantity})
198 + acd.update({"매입가":buy_price})
199 + acd.update({"수익률(%)":learn_rate})
200 + acd.update({"현재가":current_price})
201 + acd.update({"매입금액":total_chegual_price})
202 + acd.update({"매매가능수량":possible_quantity})
203 +
204 + cnt+=1
205 +
206 + print("계좌 종목 : ",self.account_stock_dict)
207 + print("계좌 종목 수 : ",cnt)
208 +
209 + if sPrevNext=="2":
210 + self.detail_account_mystock(sPreNext="2") # If the next page exists, repeat the process
211 + else:
212 + self.detail_account_info_event_loop.exit()
213 +
214 + elif sRQName=="실시간미체결요청":
215 + rows=self.dynamicCall("GetRepeatCnt(QString,QString)",sTrCode,sRQName)
216 + for i in range(rows):
217 + code=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"종목번호")
218 + code=code.strip()
219 + code_nm = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "종목명")
220 + order_no = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문번호")
221 + order_status = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문상태") # 접수, 확인, 체결
222 + order_quantity = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문수량")
223 + order_price = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문가격")
224 + order_gubun = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "주문구분") # 매도, 매수,
225 + not_quantity= self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "미체결수량")
226 + ok_quantity = self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, i, "체결량")
227 +
228 + code_nm=code_nm.strip()
229 + order_no=int(order_no.strip())
230 + order_status=order_status.strip()
231 + order_quantity=int(order_quantity.strip())
232 + order_price=int(order_price.strip())
233 + order_gubun=order_gubun.lstrip("+").lstrip('-')
234 + not_quantity=int(not_quantity.strip())
235 + ok_quantity=int(ok_quantity.strip())
236 +
237 + if order_no in self.not_account_stock_dict:
238 + pass
239 + else:
240 + self.not_account_stock_dict[order_no]={}
241 +
242 + nasd=self.not_account_stock_dict[order_no]
243 +
244 + nasd.update({"종목코드":code})
245 + nasd.update({"주문명":code_nm})
246 + nasd.update({"주분번호":order_no})
247 + nasd.update({"주문상태":order_status})
248 + nasd.update({"주문수량":order_quantity})
249 + nasd.update({"주문가격":order_price})
250 + nasd.update({"주문구분":order_gubun})
251 + nasd.update({"미체결수량":not_quantity})
252 + nasd.update({"체결량":ok_quantity})
253 +
254 + print("미체결 종목",self.not_account_stock_dict[order_no])
255 +
256 + self.detail_account_info_event_loop.exit()
257 +
258 + elif sRQName=="주식일봉차트조회":
259 + code=self.dynamicCall("GetCommData(QString,QString,int,QString)", sTrCode, sRQName, "0", "종목코드")
260 + code=code.strip()
261 + print(code," 일봉데이터 요청")
262 +
263 + cnt=self.dynamicCall("GetRepeatCnt(QString,QString)",sTrCode,sRQName)
264 + print("데이터 일수", cnt)
265 +
266 + # 한 번 조회시 600일치까지 조회 가능
267 + for i in range(cnt):
268 + data=[]
269 +
270 + current_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"현재가") # 종가
271 + value=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"거래량")
272 + trading_value=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"거래대금")
273 + date=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"일자")
274 + start_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"시가")
275 + high_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"고가")
276 + low_price=self.dynamicCall("GetCommData(QString,QString,int,QString)",sTrCode,sRQName,i,"저가")
277 +
278 + data.append("")
279 + data.append(current_price.strip())
280 + data.append(value.strip())
281 + data.append(trading_value.strip())
282 + data.append(date.strip())
283 + data.append(start_price.strip())
284 + data.append(high_price.strip())
285 + data.append(low_price.strip())
286 +
287 + self.calc_data.append(data.copy())
288 +
289 + print(len(self.calc_data))
290 +
291 + if sPrevNext=="2":
292 + self.day_kiwoom_db(code=code,sPrevNext=sPrevNext)
293 + else:
294 + self.calculator_event_loop.exit()
295 +
296 + # 종목 코드를 반환
297 + def get_code_list_by_market(self,market_code):
298 + code_list=self.dynamicCall("GetCodeListByMarket(QString)",market_code)
299 + code_list=code_list.split(";")[:-1]
300 + return code_list
301 +
302 + # 종목 분석 실행용 함수
303 + def calculator_fnc(self):
304 + code_list=self.get_code_list_by_market("10")
305 + print("코스닥 갯수",len(code_list))
306 +
307 + for idx,code in enumerate(code_list):
308 + self.dynamicCall("DisconnectRealData(QString)",self.screen_calculation_stock)
309 + print("%s / %s : KOSDAQ Stock Code : %s is updating..."%(idx+1,len(code_list),code))
310 + self.day_kiwoom_db(code=code)
311 +
312 + def day_kiwoom_db(self,code=None, date=None, sPrevNext="0"):
313 +
314 + QTest.qWait(TR_REQ_TIME_INTERVAL)
315 +
316 + self.dynamicCall("SetInputValue(QString,QString)","종목코드",code)
317 + self.dynamicCall("SetInputValue(QString,QString)","수정주가구분","1")
318 + if date!=None:
319 + self.dynamicCall("SetInputValue(QString,QString)","기준일자",date)
320 + self.dynamicCall("CommRqData(QString,QString,int,QString)","주식일봉차트조회","opt10081",sPrevNext,self.screen_calculation_stock)
321 +
322 + self.calculator_event_loop.exec_()
323 +
82 324
83 if __name__ == "__main__": 325 if __name__ == "__main__":
84 app = QApplication(sys.argv) 326 app = QApplication(sys.argv)
......