이하영

open_api.py 변경- 중복기능을 함수로 분할

......@@ -13,314 +13,239 @@ class OpenApi(QAxWidget):
super().__init__()
# event_loop list
self.login_event_loop=None
self.detail_account_info_event_loop=QEventLoop()
self.calculator_event_loop=QEventLoop()
self.tr_event_loop=QEventLoop()
self.login_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
self.ohlcv = None # 거래정보(시가,종가,고가,저가,거래량)
self.remained_data=None # 다음페이지 존재여부
# screen number list
self.screen_my_info="2000"
self.screen_calculation_stock="4000"
self.screen_data_req="0101" # 거래 데이터
self.create_instance()
self._signal_slots()
self.comm_connect() # 로그인
self.get_account_info() # 계좌정보 가져오기
# 주식일봉차트조회요청
def _opt10081(self,sRQName,sTrCode):
QTest.qWait(TR_REQ_TIME_INTERVAL)
cnt=self._get_repeat_cnt(sTrCode,sRQName)
for i in range(cnt):
date=self._get_comm_data(sTrCode,sRQName,i,"일자")
open=self._get_comm_data(sTrCode,sRQName,i,"시가")
high=self._get_comm_data(sTrCode,sRQName,i,"고가")
low=self._get_comm_data(sTrCode,sRQName,i,"저가")
close=self._get_comm_data(sTrCode,sRQName,i,"현재가")
volume=self._get_comm_data(sTrCode,sRQName,i,"거래량")
self._get_instance()
self._event_slot()
self.comm_connect()
self.account_info() # 계좌정보 가져오기
self.detail_account_info() # 예수금 가져오기
self.detail_account_mystock() # 계좌평가잔고내역 가져오기
self.not_concluded_account() # 미체결 종목 조회
self.ohlcv['date'].append(date)
self.ohlcv['open'].append(int(open))
self.ohlcv['high'].append(int(high))
self.ohlcv['low'].append(int(low))
self.ohlcv['close'].append(int(close))
self.ohlcv['volumn'].append(int(volume))
self.calculator_fnc() # 종목분석용용
# TR 요청을 처리하는 slot
# param sScrNo: 스크린번호
# sRQName: 요청했을 때 지은 이름
# sTrCode: 요청 id, tr코드
# sRecordName: 레코드 이름
# sPrevNext: 다음 페이지가 있는지 여부. "2" : 다음페이지 존재, "0" or "" : 다음페이지 없음
def _receive_tr_data(self,sScrNo,sRQName,sTrCode,sRecordName,sPrevNext):
if sPrevNext=='2':
self.remained_data=True
else:
self.remained_data=False
if sRQName=="opt10081":
print("==============주식일봉차트조회요청================")
self._opt10081(sRQName,sTrCode)
elif sRQName=="opw0001_req":
return
elif sRQName=="opw00018_req":
return
elif sRQName=="opt10074_req":
return
elif sRQName=="opw00015_req":
return
elif sRQName=="opt10076_req":
return
elif sRQName=="opt10073_req":
return
#get ocx controller to use kiwoom open api
def _get_instance(self):
try:
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
self.tr_event_loop.exit()
except AttributeError:
pass
# 특정 종목의 일자별 거래 데이터 조회 함수
# param : code - 종목코드
# start - 기준일자
# return : df - 특정종목의 일자별 거래 데이터 목록
def get_total_data(self,code,start):
self.ohlcv = {'date': [], 'open': [], 'high': [], 'low': [], 'close': [], 'volume': []}
self._set_input_value("종목코드",code)
self._set_input_value("기준일자",start)
self._set_input_value("수정주가구분",1)
self._comm_rq_data("opt10081_req","opt10081",0,self.screen_data_req)
while self.remained_data:
self._set_input_value("종목코드",code)
self._set_input_value("기준일자",start)
self._set_input_value("수정주가구분",1)
self._comm_rq_data("opt10081_req","opt10081",2,self.screen_data_req)
# 데이터가 없거나, date=''일 경우 empty list를 반환
if len(self.ohlcv)==0:
return []
if self.ohlcv['date']=='':
return []
# 데이터를 DataFrame형태로 저장 및 반환
df=DataFrame(self.ohlcv,columns=['open','high','low','close','volume'],index=self.ohlcv['date'])
return df
# 특정 종목의 특정 날짜의 시가,종가,고가,저가,거래량 중 특정한 데이터를 반환하는 함수
# param : code - 종목코드
# : date - 조회날짜
# : option - open(시가)/close(종가)/high(고가)/low(저가)/volume(거래량)
# return : 조회한 데이터에 해당하는 값
# 값이 없거나, option이 오류일 경우 return False
def get_oneday_option_data(self,code,date,option):
self.ohlcv = {'date': [], 'open': [], 'high': [], 'low': [], 'close': [], 'volume': []}
self._set_input_value("종목코드",code)
self._set_input_value("기준일자",date)
self._set_input_value("수정주가구분",1)
self._comm_rq_data("opt10081_req","opt10081",0,self.screen_data_req)
if self.ohlcv['date']=="":
return False
df = DataFrame(self.ohlcv, columns=['open', 'high', 'low', 'close', 'volume'], index=self.ohlcv['date'])
if option == 'open':
return df.iloc[0, 0]
elif option == 'high':
return df.iloc[0, 1]
elif option == 'low':
return df.iloc[0, 2]
elif option == 'close':
return df.iloc[0, 3]
elif option == 'volume':
return df.iloc[0, 4]
else:
return False
# 사용자의 계좌정보 저장 및 출력
def get_account_info(self):
account=self.get_login_info("ACCNO")
self.account_no=account.split(";")[0]
print("======== 계좌번호 : ",self.account_no,"========")
# 원하는 사용자 정보 반환
# 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:
print(e)
sys.exit()
# event slot to use open api
def _event_slot(self):
# 키움 api를 사용하기 위한 ocx controller 저장
def create_instance(self):
try:
self.OnEventConnect.connect(self.login_slot) # for login
self.OnReceiveTrData.connect(self.trdata_slot) # for tr data
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
except Exception as e:
print(e)
sys.exit()
# login event slot
# param code : if login success, code=0
def login_slot(self,code):
# event처리를 위한 slot
def _signal_slots(self):
try:
result=errCode.errors(code)
if code==0:
print("Connected - ",result[0],result[1])
else:
print("Failed to connect",result[0],result[1])
self.login_event_loop.exit()
self.OnEventConnect.connect(self._login_slot)
self.OnReceiveTrData.connect(self._receive_tr_data)
except Exception as e:
print(e)
sys.exit()
# try login
# 수동 로그인설정인 경우 로그인창을 출력해서 로그인을 시도
# 자동로그인 설정인 경우 로그인창 출력없이 로그인을 시도
def comm_connect(self):
try:
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop() # wait for login complete
self.login_event_loop.exec_()
except Exception as e:
print(e)
sys.exit()
# 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):
# 로그인 이벤트 처리 slot
# param : code - 로그인 성공 시 0
# 실패 시 에러코드 출력
def _login_slot(self,code):
try:
ret=self.dynamicCall("GetLoginInfo(QString)",tag)
return ret
result=errCode.errors(code)
if code==0:
print("Connected",result[1])
else:
print("Failed to connect",result[1])
self.login_event_loop.exit()
except Exception as e:
print(e)
sys.exit()
# print user account information
def account_info(self):
# 조회요청시 TR의 Input값을 지정하는 함수
# param : sId - TR에 명시된 Input이름
# svalue - Input이름으로 지정한 값
def _set_input_value(self,sId,sValue):
try:
account_number=self.get_login_info("ACCNO")
self.account_no=account_number.split(";")[0]
print("계좌번호 : ",self.account_no)
except AttributeError as e:
self.dynamicCall("SetInputValue(QString, QString)", sId, sValue)
except Exception as 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_()
# 조회요청함수
# param : sRQName - 사용자 구분명
# sTrCode - 조회하려는 TR이름
# nPrevNext - 연속조회여부
# sScreenNo - 화면번호
def _comm_rq_data(self,sRQName,sTrData,nPrevNext,sScrNo):
self.dynamicCall("CommRqData(QString, QString, int, QString", sRQName, sTrData, nPrevNext, sScrNo)
self.tr_event_loop.exec_()
# OnReceiveTRData()이벤트가 호출될때 조회데이터를 얻어오는 함수
# param : sTrCode - TR 이름
# sRecordName - 레코드이름
# nIndex - TR반복부
# sItemName - TR에서 얻어오려는 출력항목이름
def _get_comm_data(self,sTrCode,sRecordName,nIndex,sItemName):
ret = self.dynamicCall("GetCommData(QString, QString, int, QString", sTrCode, sRecordName, nIndex, sItemName)
return ret.strip()
# 조회수신한 멀티데이터의 갯수(반복)을 얻는다
# param : sTrCode - tr이름
# sRecordName - 레코드 이름
def _get_repeat_cnt(self,sTrCode,sRecordName):
try:
ret=self.dynamicCall("GetRepeatCnt(QString, QString)",sTrCode,sRecordName)
return ret
except Exception as e:
print(e)
sys.exit()
if __name__ == "__main__":
app = QApplication(sys.argv)
......