-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathKiwoom.py
More file actions
331 lines (275 loc) · 13.8 KB
/
Kiwoom.py
File metadata and controls
331 lines (275 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
import sys
from PyQt5.QtWidgets import *
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from Saveditem import *
#from ui_main import *
import time
import sqlite3
TR_REQ_TIME_INTERVAL = 0.2
class Sig(QObject):
sig_ = pyqtSignal()
def __init__(self):
super().__init__()
def signal_(self):
self.sig_.emit()
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__()
self._create_kiwoom_instance()
self._set_signal_slots()
self.sig_cl = Sig()
self.saveditem = Saveditem()
self.orderNum = ""
def signal_kiwoom(self):
self.sig_cl.signal_()
# openAPI 사용을 위해선 COM 오브젝트 생성이 필요
def _create_kiwoom_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
# 이벤트(signal)와 이를 처리할 메서드(slot) 연결
def _set_signal_slots(self):
self.OnEventConnect.connect(self._event_connect)
self.OnReceiveTrData.connect(self._receive_tr_data)
# self.OnReceiveRealData.connect(self._receive_real_data)
self.OnReceiveChejanData.connect(self._receive_chejan_data)
# 로그인 함수
def comm_connect(self):
# CommConnect 함수를 dynamicCall 함수를 통해 호출
self.dynamicCall("CommConnect()")
# 이벤트 루프(이벤트가 발생할 때까지 프로그램 종료되지 않음) / GUI형태로 만들지 않을 시 필요
self.login_event_loop = QEventLoop()
# exec_메서드를 호출하여 생성
self.login_event_loop.exec_()
# OnEventConnect 이벤트 발생 하면 _event_connect 호출
# comm_connect 호출 시 생성된 이벤트 루프 종료
def _event_connect(self, err_code):
if err_code == 0:
print('connected')
else:
print('disconnected')
self.login_event_loop.exit()
# 각 시장에 속하는 종목의 종목 코드 리스트 불러옴
# 종목 코드는 ;로 구분
def get_code_list_by_market(self, market):
code_list = self.dynamicCall("GetCodeListByMarket(QString)", market)
code_list = code_list.split(';')
return code_list[:-1]
# 종목 코드로부터 한글 종목명 얻어오기
def get_master_code_name(self, code):
code_name = self.dynamicCall("GetMasterCodeName(QString)", code)
return code_name
# 현재 접속 상태 반환
def get_connect_state(self):
ret = self.dynamicCall("GetConnectState()")
return ret
# 계좌 정보 및 로그인 사용자 정보 얻어오기
def get_login_info(self, tag):
ret = self.dynamicCall("GetLoginInfo(QString)", tag)
return ret
# TR 입력 값을 서버통신 전에 입력
def set_input_value(self, id, value):
self.dynamicCall("setInputValue(QString, QString)", id, value)
# TR을 서버로 송신
# 사용자 구분명, TR명, 조회:0/연속:2, 4개 화면번호
# TR 처리 후 키움증권이 이벤트 줄 때까지 대기해야 하기 때문에 이벤트 루프 만들어줌
def comm_rq_data(self, rqname, trcode, next, screen_no):
self.dynamicCall("CommRqData(QString, QString, int, QString)", rqname, trcode, next, screen_no)
self.tr_event_loop = QEventLoop()
self.tr_event_loop.exec_()
# TR 처리에 대한 이벤트가 발생했을 때 실제로 데이터 가져오기
def _comm_get_data(self, code, real_type, field_name, index, item_name):
ret = self.dynamicCall("CommGetData(QString, QString, QString, int, QString)", code,
real_type, field_name, index, item_name)
return ret.strip()
# 총 몇 개의 데이터가 왔는지 확인하기 위함
def _get_repeat_cnt(self, trcode, rqname):
ret = self.dynamicCall("GetRepeatCnt(QString, QString)", trcode, rqname)
return ret
# 주식 주문에 대한 정보를 서버로 전송
def send_order(self, rqname, screen_no, acc_no, order_type, code, quantity, price, hoga, order_no):
self.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
[rqname, screen_no, acc_no, order_type, code, quantity, price, hoga, order_no])
# 체결잔고 데이터 가져오기
def get_chejan_data(self, fid):
ret = self.dynamicCall("GetChejanData(int)", fid)
return ret
# 접속서버 구분
def get_server_gubun(self):
ret = self.dynamicCall("KOA_Functions(QString, QString)", "GetServerGubun", "")
return ret
# gubun: '0' 접수,체결 / '1' 잔고전달
def _receive_chejan_data(self, gubun, item_cnt, fid_list):
print(gubun)
cj_jumooncode = self.get_chejan_data(9203) # 주문번호
cj_code = self.get_chejan_data(9001) # 종목코드
cj_name = self.get_chejan_data(302) # 종목명
cj_vol = self.get_chejan_data(900) # 주문수량
cj_wprice = self.get_chejan_data(901) # 주문가격
cj_time = self.get_chejan_data(908) # 주문/체결 시간
cj_actual_vol = self.get_chejan_data(911) # 체결량
cj_price = self.get_chejan_data(10) # 현재가, 체결가, 실시간 종가
cj_data = [cj_jumooncode, cj_code, cj_name, cj_vol, cj_wprice, cj_time, cj_actual_vol, cj_price]
print(cj_data)
return cj_data
def _get_comm_real_data(self, code, fid):
ret = self.dynamicCall("GetCommRealData(QString, int)", code, fid)
return ret
def _receive_real_data(self, scode, real_type, real_data):
if real_type == "주식체결":
self.scode = scode
# 체결시간
self.time = int(self.GetCommRealData(scode, 20))
# 현재가
self.price = int(self.GetCommRealData(scode, 10))
if self.price < 0:
self.price = self.price*(-1)
# 등략률
self.price_up_down = self.GetCommRealData(scode, 12)
self.chcode = self.get_master_code_name(scode)
self.saveditem.item_view[scode] = [self.chcode, str(self.price), self.price_up_down]
self.sig_cl.signal_()
# 실시간 등록
def _set_real_reg(self, screen_no, code, fid_list, real_type):
self.dynamicCall("SetRealReg(QString, QString, QString, QString)",
screen_no, code, fid_list, real_type)
# SetRealReg() 해제
def _set_real_remove(self, screen_no, del_code):
print("SetRealRemove")
self.dynamicCall("SetRealRemove(QString, QString)", screen_no, del_code)
def _on_receive_msg(self, screen_no, rqname, trcode, msg):
msg = self.dynnamicCall("OnResceiveMsg(")
# def _receive_real_condition(self, scode, event_type, condi_name, cond_index):
# 이벤트 발생 시 처리
def _receive_tr_data(self, screen_no, rqname, trcode, record_name, next, unused1, unused2, unused3, unused4):
self.orderNum = self._comm_get_data(trcode, "", rqname, 0, "주문번호")
# next라는 인자 값을 통해 연속조회 필요여부
if next == '2':
self.remained_data = True
else:
self.remained_data = False
# TR 구분을 위한 적당한 문자열
# 올바른 데이터를 받기 위함
if rqname == "opt10081_req":
self._opt10081(rqname, trcode)
# 예수금 정보
elif rqname == "opw00001_req":
self._opw00001(rqname, trcode)
# 계좌 잔고 데이터
elif rqname == "opw00018_req":
self._opw00018(rqname, trcode)
# 실시간 미체결 요청
elif rqname == "opt10075_req":
self._opt10075(rqname, trcode)
# comm_rq_data 호출 시 생성된 이벤트 루프 종료
try:
self.tr_event_loop.exit()
except AttributeError:
pass
# 숫자 정리. 콤마 표시
@staticmethod
def change_format(data):
strip_data = data.lstrip('-0')
if strip_data == '' or strip_data == '.00':
strip_data = '0'
format_data = format(int(strip_data), ',d')
if data.startswith('-'):
format_data = '-' + format_data
return format_data
@staticmethod
def change_format2(data):
strip_data = data.lstrip('-0')
if strip_data == '':
strip_data = '0'
if strip_data.startswith('.'):
strip_data = '0' + strip_data
if data.startswith('-'):
strip_data = '-' + strip_data
return strip_data
def reset_opt10075_output(self):
self.opt10075_output = {'no_che': [], 'che': []}
def _opt10075(self, rqname, trcode):
data_cnt = self._get_repeat_cnt(trcode, rqname)
for i in range(data_cnt):
status = self._comm_get_data(trcode, "", rqname, i, "주문상태")
gubun = self._comm_get_data(trcode, "", rqname, i, "주문구분")
order_num = self._comm_get_data(trcode, "", rqname, i, "주문번호")
code = self._comm_get_data(trcode, "", rqname, i, "종목명")
price = self._comm_get_data(trcode, "", rqname, i, "체결가")
vol = self._comm_get_data(trcode, "", rqname, i, "주문수량")
order_price = self._comm_get_data(trcode, "", rqname, i, "주문가격")
yet_vol = self._comm_get_data(trcode, "", rqname, i, "미체결수량")
time = self._comm_get_data(trcode, "", rqname, i, "시간")
# self.sig.signal()
print(status, gubun, order_num, code, vol, yet_vol, time, order_price)
self.opt10075_output['no_che'].append([status, gubun, order_num, code, vol, yet_vol, time, order_price, price])
# 예수금 정보 얻기 위한 TR
def _opw00001(self, rqname, trcode):
d2_deposit = self._comm_get_data(trcode, "", rqname, 0, "d+2추정예수금")
# 포맷 변경
self.d2_deposit = Kiwoom.change_format(d2_deposit)
def _opt10081(self, rqname, trcode):
# 데이터를 얻어오기 전 데이터 개수 받아오기
data_cnt = self._get_repeat_cnt(trcode, rqname)
# 반복문을 통해 데이터 하나씩 얻어오기
for i in range(data_cnt):
date = self._comm_get_data(trcode, "", rqname, i, "일자")
open = self._comm_get_data(trcode, "", rqname, i, "시가")
high = self._comm_get_data(trcode, "", rqname, i, "고가")
low = self._comm_get_data(trcode, "", rqname, i, "저가")
close = self._comm_get_data(trcode, "", rqname, i, "현재가")
volume = self._comm_get_data(trcode, "", rqname, i, "거래량")
# opw00018 데이터 변수에 저장
def reset_opw00018_output(self):
self.opw00018_output = {'single': [], 'multi': [], 'compare': []}
# 잔고 및 보유종목 현황
def _opw00018(self, rqname, trcode):
total_purchase_price = self._comm_get_data(trcode, "", rqname, 0, "총매입금액")
total_eval_price = self._comm_get_data(trcode, "", rqname, 0, "총평가금액")
total_eval_profit_loss_price = self._comm_get_data(trcode, "", rqname, 0, "총평가손익금액")
total_earning_rate = self._comm_get_data(trcode, "", rqname, 0, "총수익률(%)")
estimated_deposit = self._comm_get_data(trcode, "", rqname, 0, "추정예탁자산")
print("1")
self.opw00018_output['single'].append(Kiwoom.change_format(total_purchase_price))
self.opw00018_output['single'].append(Kiwoom.change_format(total_eval_price))
self.opw00018_output['single'].append(Kiwoom.change_format(total_eval_profit_loss_price))
print("2")
total_earning_rate = Kiwoom.change_format2(total_earning_rate)
print("3")
if self.get_server_gubun():
total_earning_rate = float(total_earning_rate) / 100
total_earning_rate = str(total_earning_rate)
print("4")
self.opw00018_output['single'].append(total_earning_rate)
self.opw00018_output['single'].append(Kiwoom.change_format(estimated_deposit))
print("5")
# multi data
rows = self._get_repeat_cnt(trcode, rqname)
for i in range(rows):
name = self._comm_get_data(trcode, "", rqname, i, "종목명")
quantity = self._comm_get_data(trcode, "", rqname, i, "보유수량")
purchase_price = self._comm_get_data(trcode, "", rqname, i, "매입가")
current_price = self._comm_get_data(trcode, "", rqname, i, "현재가")
eval_profit_loss_price = self._comm_get_data(trcode, "", rqname, i, "평가손익")
earning_rate = self._comm_get_data(trcode, "", rqname, i, "수익률(%)")
print("6")
quantity = Kiwoom.change_format(quantity)
purchase_price = Kiwoom.change_format(purchase_price)
current_price = Kiwoom.change_format(current_price)
eval_profit_loss_price = Kiwoom.change_format(eval_profit_loss_price)
earning_rate = Kiwoom.change_format2(earning_rate)
print("7")
self.opw00018_output['multi'].append([name, quantity, purchase_price, current_price,
current_price, current_price,
eval_profit_loss_price, earning_rate])
print("8")
self.opw00018_output['compare'].append([name, quantity, current_price, purchase_price, earning_rate])
if __name__ == "__main__":
app = QApplication(sys.argv)
kiwoom = Kiwoom()
kiwoom.comm_connect()
# kiwoom.ohlcv = {'date' : [], 'open': [], 'high': [], 'low': [], 'close': [], 'volume': []}
kiwoom.reset_opw00018_output()
account_number = kiwoom.get_login_info("ACCNO")
account_number = account_number.split(';')[0]
kiwoom.set_input_value("계좌번호", account_number)
# kiwoom.comm_rq_data("opt10075_req", "opt10075", 0, "2000")