__init__.py ====================
from ui.ui import *
class Main():
def __init__(self):
print("__init__ 실행할 메인 클래스")
Ui_class()
if __name__ == "__main__":
Main()
ui>ui.py ====================
from kiwoom.kiwoom import *
from PyQt5.QtWidgets import *
import sys
class Ui_class():
def __init__(self):
print("Ui_class 입니다.")
self.app = QApplication(sys.argv) # ui 초기화 ['파이썬파일경로','추가할옵션','추가할옵션']
self.kiwoom = Kiwoom()
self.app.exec_() # 이벤트루프 실행된 상태에서는 다음 코드 실행 안됨
kiwoom>kiwoom.py ====================
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
########################
####### 변수 모음
self.account_num = None
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw0001", "0", "2000")
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
kiwoom>kiwoom.py ====================42강 TR요청 4편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
########################
####### 변수 모음
self.account_num = None
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", "2000")
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
kiwoom>kiwoom.py ====================43강 TR요청 5편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
########################
####### 변수 모음
self.account_num = None
#######################
####### 이벤트루프 모음
self.detail_acount_info_event_loop = None
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", "2000")
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
kiwoom>kiwoom.py ====================44강 TR요청 6편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = None
self.detail_acount_info_event_loop2 = None
########################
####### 변수 모음
self.account_num = None
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", "2000")
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print("계좌평가 잔고내역 요청")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, "2000")
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop2 = QEventLoop()
self.detail_acount_info_event_loop2.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
if sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
self.detail_acount_info_event_loop2.exit()
kiwoom>kiwoom.py ====================45강 TR요청 7편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = None
self.detail_acount_info_event_loop2 = None
########################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 변수 모음
self.account_stock_dict = {}
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", "2000")
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print("계좌평가 잔고내역 요청")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, "2000")
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop2 = QEventLoop()
self.detail_acount_info_event_loop2.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
if sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, cnt, "종목번호")
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, "매매가능수량")
code_nm = code_nm.strip()
stock_quantity = int(stock_quantity.strip())
buy_price = int(buy_price.strip())
learn_rate = float(learn_rate.strip)
currrent_price = int(current_price.strip())
total_chegual_price = int(total_chegual_price.strip())
self.detail_acount_info_event_loop2.exit()
kiwoom>kiwoom.py ====================46강 TR요청 8편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = None
self.detail_acount_info_event_loop_2 = QEventLoop()
########################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 변수 모음
self.account_stock_dict = {}
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", "2000")
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, "2000")
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop_2.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
if sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop_2.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
kiwoom>kiwoom.py ====================47강 TR요청 9편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = QEventLoop()
# self.detail_acount_info_event_loop_2 = QEventLoop()
########################
####### 스크린번호 모음
self.screen_my_info = "2000"
#######################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 딕션너리 모음
self.account_stock_dict = {}
self.not_account_stock_dict = {}
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
self.not_concluded_account() # 미체결 요청
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", self.screen_my_info)
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, self.screen_my_info)
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"): # 미체결
print("미체결 요청")
self.dynamicCall("SetInputValue(QString, QString)", "계좌번호", self.account_num)
# self.dynamicCall("SetInputValue(QString, QString)", "전체종목구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "체결구분", "1") # 1 미체결
self.dynamicCall("CommRqData(String, String, int, String)", "실시간미체결요청", "opt10075", sPrevNext, self.screen_my_info)
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
elif sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
elif sRQName == "실시간미체결요청":
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
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_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 = code.strip()
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.strip().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})
self.not_account_stock_dict[order_no].update({"체결량": ok_quantity})
print(f"미체결 종목 : {self.not_account_stock_dict[order_no]}")
self.detail_acount_info_event_loop.exit()
kiwoom>kiwoom.py ====================49강 TR요청 11편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = QEventLoop()
# self.detail_acount_info_event_loop_2 = QEventLoop()
########################
####### 스크린번호 모음
self.screen_my_info = "2000"
self.sereen_calculation_stock = "4000"
#######################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 딕션너리 모음
self.account_stock_dict = {}
self.not_account_stock_dict = {}
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
self.not_concluded_account() # 미체결 요청
# 실시간
self.calculator_fnc() # 종목 분석용, 임시용으로 실행
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", self.screen_my_info)
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, self.screen_my_info)
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"): # 미체결
print("미체결 요청")
self.dynamicCall("SetInputValue(QString, QString)", "계좌번호", self.account_num)
# self.dynamicCall("SetInputValue(QString, QString)", "전체종목구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "체결구분", "1") # 1 미체결
self.dynamicCall("CommRqData(String, String, int, String)", "실시간미체결요청", "opt10075", sPrevNext, self.screen_my_info)
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
elif sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
elif sRQName == "실시간미체결요청":
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
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_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 = code.strip()
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.strip().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})
self.not_account_stock_dict[order_no].update({"체결량": ok_quantity})
print(f"미체결 종목 : {self.not_account_stock_dict[order_no]}")
self.detail_acount_info_event_loop.exit()
elif "주식일봉차트조회" == sRQName:
print("일봉데이터 요청")
# KOA studio - 개발가이드 - 기타함수 - 종목정보관련함수 - GetCodeListByMarket
def get_code_list_by_market(self, market_code):
'''
종목 코드들 반환
:param market_code:
:return:
'''
code_list = self.dynamicCall("GetCodeListByMarket(QString)", market_code)
# "109012;2394823;293481;092834;" → ["11912", "@349023", ""]
code_list = code_list.split(";")[:-1]
return code_list
def calculator_fnc(self):
'''
종목 분석 실행용 함수
:return:
'''
code_list = self.get_code_list_by_market("10")
print(f"코스닥 갯수 {len(code_list)}")
for idx, code in enumerate(code_list):
self.dynamicCall("DisconnectRealData(QString)", self.sereen_calculation_stock)
print(f"{idx+1} / {len(code_list)} : KOSDAQ Stock Code: {code} is updating...")
self.day_kiwoom_db(code=code)
def day_kiwoom_db(self, code=None, date=None, sPrevNext="0"):
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
if date != None:
self.dynamicCall("SetInpuValue(QString, QString)", "기준일자", date)
self.dynamicCall("CommRqData(QString, QString, int, QString)", "주식일봉차트조회", "opt10081", sPrevNext, self.sereen_calculation_stock)
kiwoom>kiwoom.py ====================52강 TR요청 14편 ~ 53강 TR요청 15편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
from PyQt5.QtTest import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = QEventLoop()
# self.detail_acount_info_event_loop_2 = QEventLoop()
self.calculator_event_loop = QEventLoop()
########################
####### 스크린번호 모음
self.screen_my_info = "2000"
self.sereen_calculation_stock = "4000"
#######################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 딕션너리 모음
self.account_stock_dict = {}
self.not_account_stock_dict = {}
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
self.not_concluded_account() # 미체결 요청
# 실시간
self.calculator_fnc() # 종목 분석용, 임시용으로 실행
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", self.screen_my_info)
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, self.screen_my_info)
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"): # 미체결
print("미체결 요청")
self.dynamicCall("SetInputValue(QString, QString)", "계좌번호", self.account_num)
# self.dynamicCall("SetInputValue(QString, QString)", "전체종목구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "체결구분", "1") # 1 미체결
self.dynamicCall("CommRqData(String, String, int, String)", "실시간미체결요청", "opt10075", sPrevNext, self.screen_my_info)
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
elif sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
elif sRQName == "실시간미체결요청":
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
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_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 = code.strip()
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.strip().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})
self.not_account_stock_dict[order_no].update({"체결량": ok_quantity})
print(f"미체결 종목 : {self.not_account_stock_dict[order_no]}")
self.detail_acount_info_event_loop.exit()
if sRQName == "주식일봉차트조회":
code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "종목코드")
code = code.strip()
print(f"일봉데이터 요청 {code}")
rows = self.dynamicCall("GetRepeatCnt(QString, QString")
print(rows)
if sPrevNext == "2":
self.day_kiwoom_db(code=code, sPrevNext=sPrevNext)
else:
self.calculator_event_loop.exit()
# KOA studio - 개발가이드 - 기타함수 - 종목정보관련함수 - GetCodeListByMarket
def get_code_list_by_market(self, market_code):
'''
종목 코드들 반환
:param market_code:
:return:
'''
code_list = self.dynamicCall("GetCodeListByMarket(QString)", market_code)
# "109012;2394823;293481;092834;" → ["11912", "@349023", ""]
code_list = code_list.split(";")[:-1]
return code_list
def calculator_fnc(self):
'''
종목 분석 실행용 함수
:return:
'''
code_list = self.get_code_list_by_market("10")
print(f"코스닥 갯수 {len(code_list)}")
for idx, code in enumerate(code_list):
self.dynamicCall("DisconnectRealData(QString)", self.sereen_calculation_stock)
print(f"{idx+1} / {len(code_list)} : KOSDAQ Stock Code: {code} is updating...")
self.day_kiwoom_db(code=code)
def day_kiwoom_db(self, code=None, date=None, sPrevNext="0"):
# 시간 텀을 주자
QTest.qWait(3600) # 3초 딜레이
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
if date != None:
self.dynamicCall("SetInpuValue(QString, QString)", "기준일자", date)
self.dynamicCall("CommRqData(QString, QString, int, QString)", "주식일봉차트조회", "opt10081", sPrevNext, self.sereen_calculation_stock)
self.calculator_event_loop.exec_()
kiwoom>kiwoom.py ====================54강 TR요청 16편 ~ 55강 TR요청 17편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
from PyQt5.QtTest import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = QEventLoop()
# self.detail_acount_info_event_loop_2 = QEventLoop()
self.calculator_event_loop = QEventLoop()
########################
####### 스크린번호 모음
self.screen_my_info = "2000"
self.sereen_calculation_stock = "4000"
#######################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 딕션너리 모음
self.account_stock_dict = {}
self.not_account_stock_dict = {}
#######################
####### 종목 분석용
self.calcul_data = []
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
self.not_concluded_account() # 미체결 요청
# 실시간
self.calculator_fnc() # 종목 분석용, 임시용으로 실행
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", self.screen_my_info)
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, self.screen_my_info)
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"): # 미체결
print("미체결 요청")
self.dynamicCall("SetInputValue(QString, QString)", "계좌번호", self.account_num)
# self.dynamicCall("SetInputValue(QString, QString)", "전체종목구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "체결구분", "1") # 1 미체결
self.dynamicCall("CommRqData(String, String, int, String)", "실시간미체결요청", "opt10075", sPrevNext, self.screen_my_info)
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
elif sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
elif sRQName == "실시간미체결요청":
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
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_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 = code.strip()
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.strip().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})
self.not_account_stock_dict[order_no].update({"체결량": ok_quantity})
print(f"미체결 종목 : {self.not_account_stock_dict[order_no]}")
self.detail_acount_info_event_loop.exit()
if sRQName == "주식일봉차트조회":
code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "종목코드")
code = code.strip()
print(f"일봉데이터 요청 {code}")
cnt = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
print(f"데이터 일수 {cnt}")
# data = self.dynamicCall("GetCommDataEx(QString, QString)", sTrCode, sRQName)
# [['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],
# 한 번 조회하면 600일치까지 일봉데이터를 받을 수 있다.
# KOA studio - TR 목록 - opt10081 주식일봉차트조회요청
for i in range(cnt): # [0,...,599]
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())
data.append("")
self.calcul_data.append(data.copy())
# print(self.calcul_data)
# print(len(self.calcul_data))
if sPrevNext == "2":
self.day_kiwoom_db(code=code, sPrevNext=sPrevNext)
else:
# 조건에 부합하는 종목 추출
print(f"총 일수{len(self.calcul_data)}")
pass_success = False
# 120일 이평선을 그릴 만큼의 데이터가 있는지 체크
if self.calcul_data == None or len(self.calcul_data) < 120:
pass_success = False
else:
# 120일 이상이 되면
total_price = 0
for value in self.calcul_data[:120]: # [오늘, 하루전,...,119일전]
total_price += int(value[1]) # 현재가
moving_average_price = total_price / 120 # 120일 이평선 오늘 평균가격
# 오늘자의 저가 7가 120일 이평선 오늘 평균가격보다 낮아야 된다 # 오늘자의 고가 6
if int(self.calcul_data[0][7]) <= moving_average_price and moving_average_price <= int(self.calcul_data[0][6]):
print("오늘 주가 120이평선에 걸쳐있는 것 확인")
self.calculator_event_loop.exit()
# KOA studio - 개발가이드 - 기타함수 - 종목정보관련함수 - GetCodeListByMarket
def get_code_list_by_market(self, market_code):
'''
종목 코드들 반환
:param market_code:
:return:
'''
code_list = self.dynamicCall("GetCodeListByMarket(QString)", market_code)
# "109012;2394823;293481;092834;" → ["11912", "@349023", ""]
code_list = code_list.split(";")[:-1]
return code_list
def calculator_fnc(self):
'''
종목 분석 실행용 함수
:return:
'''
code_list = self.get_code_list_by_market("10")
print(f"코스닥 갯수 {len(code_list)}")
for idx, code in enumerate(code_list):
self.dynamicCall("DisconnectRealData(QString)", self.sereen_calculation_stock)
print(f"{idx+1} / {len(code_list)} : KOSDAQ Stock Code: {code} is updating...")
self.day_kiwoom_db(code=code)
def day_kiwoom_db(self, code=None, date=None, sPrevNext="0"):
# 시간 텀을 주자
QTest.qWait(3600) # 3초 딜레이
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
if date != None:
self.dynamicCall("SetInpuValue(QString, QString)", "기준일자", date)
self.dynamicCall("CommRqData(QString, QString, int, QString)", "주식일봉차트조회", "opt10081", sPrevNext, self.sereen_calculation_stock)
self.calculator_event_loop.exec_()
kiwoom>kiwoom.py ====================56강 TR요청 18편
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
from PyQt5.QtTest import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = QEventLoop()
# self.detail_acount_info_event_loop_2 = QEventLoop()
self.calculator_event_loop = QEventLoop()
########################
####### 스크린번호 모음
self.screen_my_info = "2000"
self.sereen_calculation_stock = "4000"
#######################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 딕션너리 모음
self.account_stock_dict = {}
self.not_account_stock_dict = {}
#######################
####### 종목 분석 용
self.calcul_data = []
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
self.not_concluded_account() # 미체결 요청
# 실시간
self.calculator_fnc() # 종목 분석용, 임시용으로 실행
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", self.screen_my_info)
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, self.screen_my_info)
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"): # 미체결
print("미체결 요청")
self.dynamicCall("SetInputValue(QString, QString)", "계좌번호", self.account_num)
# self.dynamicCall("SetInputValue(QString, QString)", "전체종목구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "체결구분", "1") # 1 미체결
self.dynamicCall("CommRqData(String, String, int, String)", "실시간미체결요청", "opt10075", sPrevNext, self.screen_my_info)
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
elif sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
elif sRQName == "실시간미체결요청":
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
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_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 = code.strip()
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.strip().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})
self.not_account_stock_dict[order_no].update({"체결량": ok_quantity})
print(f"미체결 종목 : {self.not_account_stock_dict[order_no]}")
self.detail_acount_info_event_loop.exit()
if sRQName == "주식일봉차트조회":
code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "종목코드")
code = code.strip()
print(f"{code} 일봉데이터 요청")
cnt = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
print(f"데이터 일수 {cnt}")
# data = self.dynamicCall("GetCommDataEx(QString, QString)", sTrCode, sRQName)
# [['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],
# 한 번 조회하면 600일치까지 일봉데이터를 받을 수 있다.
# KOA studio - TR 목록 - opt10081 주식일봉차트조회요청
for i in range(cnt): # [0,...,599]
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())
data.append("")
self.calcul_data.append(data.copy())
# print(self.calcul_data)
# print(len(self.calcul_data))
if sPrevNext == "2": # 과거 데이터가 더 있다면
self.day_kiwoom_db(code=code, sPrevNext=sPrevNext)
else:
# 조건에 부합하는 종목 추출
print(f"총 일수 {len(self.calcul_data)}")
pass_success = False
# 120일 이평선을 그릴 만큼의 데이터가 있는지 체크
if self.calcul_data == None or len(self.calcul_data) < 120:
pass_success = False
else:
# 120일 이상이 되면
total_price = 0
for value in self.calcul_data[:120]: # [오늘, 하루전,...,119일전]
total_price += int(value[1]) # 현재가(종가)
moving_average_price = total_price / 120 # 120일 이평선 오늘 평균가격
# moving_average_price = int(total_price / 120) # 120일 이평선 오늘 평균가격
# 오늘자의 저가 7가 120일 이평선 오늘 평균가격보다 낮아야 된다 # 오늘자의 고가 6
# 오늘자 주가가 120일 이평선에 걸쳐있는지 확인
bottom_stock_price = False
check_price = None
if int(self.calcul_data[0][7]) <= moving_average_price and moving_average_price <= int(self.calcul_data[0][6]):
print("오늘 주가 120이평선에 걸쳐있는 것 확인")
bottom_stock_price = True
check_price = int(self.calcul_data[0][6]) # 오늘의 고가
# 과거 일봉들이 120일 이평선보다 밑에 있는지 확인
# 확인하다가 일봉이 120일 이평선보다 위에 있으면 계산 진행
prev_price = None # 과거의 일봉 저가
if bottom_stock_price == True:
moving_average_price_prev = 0 # 과거
price_top_moving = False
idx = 1
while True:
if len(self.calcul_data[idx:]) < 120: # 120일치가 있는지 계속 확인
print("120일치가 없음!")
break
total_price = 0
for value in self.calcul_data[idx:120+idx]:
total_price += int(value[1])
moving_average_price_prev = total_price / 120
# 고가가 이평선 위에 있을 경우
if moving_average_price_prev <= int(self.calcul_data[idx][6]) and idx <=20: # 이평선아래 있는 구간이 20일은 되게
print("20일 동안 주가가 120일 이평선과 같거나 위에 있으면 조건 통과 못함")
price_top_moving = False
break
elif int(self.calcul_data[idx][7]) > moving_average_price_prev and idx > 20: # 7 저가가 이평선보다 위에 있고 20일보다 많으면
print("120일 이평선 위에 있는 일봉 확인됨")
price_top_moving = True
prev_price = int(self.calcul_data[idx][7]) # 이 때의 저가 가격 확인
break
idx += 1
# 해당 부분 이평선이 가장 최근 일자의 이평선 가격보다 낮은지 확인
if price_top_moving == True:
if moving_average_price > moving_average_price_prev and check_price > prev_price:
print("포착된 이평선의 가격이 오늘자(최근일자) 이평선 가격보다 낮은 것 확인됨")
print("포착된 부분의 일봉 저가가 오늘자 일봉의 고가보다 낮은지 확인됨")
pass_success = True
if pass_success == True:
print("조건부 통과됨")
code_nm = self.dynamicCall("GetMasterCodeName(QString)", code)
f = open("files/condition_stock.txt", "a", encoding="utf8")
f.write(f"{code}\t{code_nm}\t{str(self.calcul_data[0][1])}\n") # 1 현재가
f.close()
elif pass_success == False:
print("조건부 통과 못함")
self.calcul_data.clear()
self.calculator_event_loop.exit()
# KOA studio - 개발가이드 - 기타함수 - 종목정보관련함수 - GetCodeListByMarket
def get_code_list_by_market(self, market_code):
'''
종목 코드들 반환
:param market_code:
:return:
'''
code_list = self.dynamicCall("GetCodeListByMarket(QString)", market_code)
# "109012;2394823;293481;092834;" → ["11912", "@349023", ""]
code_list = code_list.split(";")[:-1]
return code_list
def calculator_fnc(self):
'''
종목 분석 실행용 함수
:return:
'''
code_list = self.get_code_list_by_market("10")
print(f"코스닥 갯수 {len(code_list)}")
for idx, code in enumerate(code_list):
self.dynamicCall("DisconnectRealData(QString)", self.sereen_calculation_stock)
print(f"{idx+1} / {len(code_list)} : KOSDAQ Stock Code: {code} is updating...")
self.day_kiwoom_db(code=code)
def day_kiwoom_db(self, code=None, date=None, sPrevNext="0"):
# 시간 텀을 주자
QTest.qWait(3600) # 3초 딜레이
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
if date != None:
self.dynamicCall("SetInpuValue(QString, QString)", "기준일자", date)
self.dynamicCall("CommRqData(QString, QString, int, QString)", "주식일봉차트조회", "opt10081", sPrevNext, self.sereen_calculation_stock)
self.calculator_event_loop.exec_()
코스닥 갯수 1604
1 / 1604 : KOSDAQ Stock Code: 900110 is updating...
일봉데이터 요청 900110
데이터 일수 600
QEventLoop::exec: instance 0x10e6c00 has already called exec()
일봉데이터 요청 900110
데이터 일수 600
QEventLoop::exec: instance 0x10e6c00 has already called exec()
일봉데이터 요청 900110
데이터 일수 600
QEventLoop::exec: instance 0x10e6c00 has already called exec()
일봉데이터 요청 900110
데이터 일수 600
QEventLoop::exec: instance 0x10e6c00 has already called exec()
일봉데이터 요청 900110
데이터 일수 600
QEventLoop::exec: instance 0x10e6c00 has already called exec()
일봉데이터 요청 900110
데이터 일수 98
총 일수 3098
조건부 통과 못함
kiwoom>kiwoom.py ====================58강 파일 종목 읽기, 종목별 스크린번호 할당하기
import os
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
from PyQt5.QtTest import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = QEventLoop()
# self.detail_acount_info_event_loop_2 = QEventLoop()
self.calculator_event_loop = QEventLoop()
########################
####### 스크린번호 모음
self.screen_my_info = "2000"
self.sereen_calculation_stock = "4000"
self.screen_real_stock = "5000" # 종목별로 할당할 스크린 번호
self.screen_meme_stock = "6000" # 종목별 할당할 주문용 스크린 번호
#######################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 딕션너리 모음
self.account_stock_dict = {}
self.not_account_stock_dict = {}
self.portfolio_stock_dict = {}
#######################
####### 종목 분석 용
self.calcul_data = []
#######################
self.get_ocx_instance()
self.event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
self.not_concluded_account() # 미체결 요청
# 실시간
# self.calculator_fnc() # 종목 분석용, 임시용으로 실행
self.read_code() # 저장된 종목들 불러오기
self.screen_number_setting() # 스크린 번호 할당
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", self.screen_my_info)
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, self.screen_my_info)
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"): # 미체결
print("미체결 요청")
self.dynamicCall("SetInputValue(QString, QString)", "계좌번호", self.account_num)
# self.dynamicCall("SetInputValue(QString, QString)", "전체종목구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "체결구분", "1") # 1 미체결
self.dynamicCall("CommRqData(String, String, int, String)", "실시간미체결요청", "opt10075", sPrevNext, self.screen_my_info)
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
elif sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
elif sRQName == "실시간미체결요청":
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
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_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 = code.strip()
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.strip().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})
self.not_account_stock_dict[order_no].update({"체결량": ok_quantity})
print(f"미체결 종목 : {self.not_account_stock_dict[order_no]}")
self.detail_acount_info_event_loop.exit()
if sRQName == "주식일봉차트조회":
code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "종목코드")
code = code.strip()
print(f"{code} 일봉데이터 요청")
cnt = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
print(f"데이터 일수 {cnt}")
# data = self.dynamicCall("GetCommDataEx(QString, QString)", sTrCode, sRQName)
# [['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],
# 한 번 조회하면 600일치까지 일봉데이터를 받을 수 있다.
# KOA studio - TR 목록 - opt10081 주식일봉차트조회요청
for i in range(cnt): # [0,...,599]
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())
data.append("")
self.calcul_data.append(data.copy())
# print(self.calcul_data)
# print(len(self.calcul_data))
if sPrevNext == "2": # 과거 데이터가 더 있다면
self.day_kiwoom_db(code=code, sPrevNext=sPrevNext)
else:
# 조건에 부합하는 종목 추출
print(f"총 일수 {len(self.calcul_data)}")
pass_success = False
# 120일 이평선을 그릴 만큼의 데이터가 있는지 체크
if self.calcul_data == None or len(self.calcul_data) < 120:
pass_success = False
else:
# 120일 이상이 되면
total_price = 0
for value in self.calcul_data[:120]: # [오늘, 하루전,...,119일전]
total_price += int(value[1]) # 현재가(종가)
moving_average_price = total_price / 120 # 120일 이평선 오늘 평균가격
# moving_average_price = int(total_price / 120) # 120일 이평선 오늘 평균가격
# 오늘자의 저가 7가 120일 이평선 오늘 평균가격보다 낮아야 된다 # 오늘자의 고가 6
# 오늘자 주가가 120일 이평선에 걸쳐있는지 확인
bottom_stock_price = False
check_price = None
if int(self.calcul_data[0][7]) <= moving_average_price and moving_average_price <= int(self.calcul_data[0][6]):
print("오늘 주가 120이평선에 걸쳐있는 것 확인")
bottom_stock_price = True
check_price = int(self.calcul_data[0][6]) # 오늘의 고가
# 과거 일봉들이 120일 이평선보다 밑에 있는지 확인
# 확인하다가 일봉이 120일 이평선보다 위에 있으면 계산 진행
prev_price = None # 과거의 일봉 저가
if bottom_stock_price == True:
moving_average_price_prev = 0 # 과거
price_top_moving = False
idx = 1
while True:
if len(self.calcul_data[idx:]) < 120: # 120일치가 있는지 계속 확인
print("120일치가 없음!")
break
total_price = 0
for value in self.calcul_data[idx:120+idx]:
total_price += int(value[1])
moving_average_price_prev = total_price / 120
# 고가가 이평선 위에 있을 경우
if moving_average_price_prev <= int(self.calcul_data[idx][6]) and idx <=20: # 이평선아래 있는 구간이 20일은 되게
print("20일 동안 주가가 120일 이평선과 같거나 위에 있으면 조건 통과 못함")
price_top_moving = False
break
elif int(self.calcul_data[idx][7]) > moving_average_price_prev and idx > 20: # 7 저가가 이평선보다 위에 있고 20일보다 많으면
print("120일 이평선 위에 있는 일봉 확인됨")
price_top_moving = True
prev_price = int(self.calcul_data[idx][7]) # 이 때의 저가 가격 확인
break
idx += 1
# 해당 부분 이평선이 가장 최근 일자의 이평선 가격보다 낮은지 확인
if price_top_moving == True:
if moving_average_price > moving_average_price_prev and check_price > prev_price:
print("포착된 이평선의 가격이 오늘자(최근일자) 이평선 가격보다 낮은 것 확인됨")
print("포착된 부분의 일봉 저가가 오늘자 일봉의 고가보다 낮은지 확인됨")
pass_success = True
if pass_success == True:
print("조건부 통과됨")
code_nm = self.dynamicCall("GetMasterCodeName(QString)", code)
f = open("files/condition_stock.txt", "a", encoding="utf8")
f.write(f"{code}\t{code_nm}\t{str(self.calcul_data[0][1])}\n") # 1 현재가
f.close()
elif pass_success == False:
print("조건부 통과 못함")
self.calcul_data.clear()
self.calculator_event_loop.exit()
# KOA studio - 개발가이드 - 기타함수 - 종목정보관련함수 - GetCodeListByMarket
def get_code_list_by_market(self, market_code):
'''
종목 코드들 반환
:param market_code:
:return:
'''
code_list = self.dynamicCall("GetCodeListByMarket(QString)", market_code)
# "109012;2394823;293481;092834;" → ["11912", "@349023", ""]
code_list = code_list.split(";")[:-1]
return code_list
def calculator_fnc(self):
'''
종목 분석 실행용 함수
:return:
'''
code_list = self.get_code_list_by_market("10")
print(f"코스닥 갯수 {len(code_list)}")
for idx, code in enumerate(code_list):
self.dynamicCall("DisconnectRealData(QString)", self.sereen_calculation_stock)
print(f"{idx+1} / {len(code_list)} : KOSDAQ Stock Code: {code} is updating...")
self.day_kiwoom_db(code=code)
def day_kiwoom_db(self, code=None, date=None, sPrevNext="0"):
# 시간 텀을 주자
QTest.qWait(3600) # 3초 딜레이
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
if date != None:
self.dynamicCall("SetInpuValue(QString, QString)", "기준일자", date)
self.dynamicCall("CommRqData(QString, QString, int, QString)", "주식일봉차트조회", "opt10081", sPrevNext, self.sereen_calculation_stock)
self.calculator_event_loop.exec_()
# 매수법칙 계산 들어가면 됨
def read_code(self):
if os.path.exists("files/condition_stock.txt"): # 존재하냐
f = open("files/condition_stock.txt", "r", encoding="utf8")
lines = f.readlines()
for line in lines:
if line != "":
ls = line.split("\t") # ["230923", "종목명", "현재가\n"]
stock_code = ls[0]
stock_name = ls[1]
stock_price = int(ls[2].split("\n")[0])
stock_price = abs(stock_price)
self.portfolio_stock_dict.update({stock_code:{"종목명":stock_name, "현재가":stock_price}}) # 딕셔너리 업데이트
# {"2090923":{"종목명":"삼성","현재가":"2000"}, "091092":{"종목명":"LG"...}}
f.close()
print(self.portfolio_stock_dict)
def screen_number_setting(self):
# 겹치는 종목 있는지 확인
screen_overwrite = []
# 계좌평가잔고내역에 있는 종목들
for code in self.account_stock_dict.keys():
if code not in screen_overwrite:
screen_overwrite.append(code)
# 미체결에 있는 종목들
for order_number in self.not_account_stock_dict.keys():
code = self.not_account_stock_dict[order_number]['종목코드']
if code not in screen_overwrite:
screen_overwrite.append(code)
# 포트폴리오에 담겨있는 종목들
for code in self.portfolio_stock_dict.keys():
if code not in screen_overwrite:
screen_overwrite.append(code)
# 스크린번호 할당
# 스크린번호 하나에 요청 갯수는 100개까지
# 스크린번호는 200개까지 생성가능
cnt = 0
for code in screen_overwrite:
temp_screen = int(self.screen_real_stock)
meme_screen = int(self.screen_real_stock)
if (cnt % 50) == 0:
temp_screen += 1 # 스크린번호 하나당 50개까지만 할당하겠다 5000 → 5001
self.screen_real_stock = str(temp_screen)
if (cnt % 50) == 0:
meme_screen += 1
self.screen_meme_stock = str(meme_screen)
if code in self.portfolio_stock_dict.keys():
self.portfolio_stock_dict[code].update({"스크린번호": str(self.screen_real_stock)})
self.portfolio_stock_dict[code].update({"주문용스크린번호": str(self.screen_meme_stock)})
elif code not in self.portfolio_stock_dict.keys():
self.portfolio_stock_dict.update({code: {"스크린번호":str(self.screen_real_stock), "주문용스크린번호":str(self.screen_meme_stock)}})
cnt += 1
print(self.portfolio_stock_dict)
kiwoom>kiwoom.py ====================59강 주식 종목 수천개를 빠르게 데이터 받기
import os
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
from PyQt5.QtTest import *
from config.kiwoomType import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
self.realType = RealType()
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = QEventLoop()
# self.detail_acount_info_event_loop_2 = QEventLoop()
self.calculator_event_loop = QEventLoop()
########################
####### 스크린번호 모음
self.screen_my_info = "2000"
self.sereen_calculation_stock = "4000"
self.screen_real_stock = "5000" # 종목별로 할당할 스크린 번호
self.screen_meme_stock = "6000" # 종목별 할당할 주문용 스크린 번호
self.screen_start_stop_real = "1000" #
#######################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 딕션너리 모음
self.account_stock_dict = {}
self.not_account_stock_dict = {}
self.portfolio_stock_dict = {}
#######################
####### 종목 분석 용
self.calcul_data = []
#######################
self.get_ocx_instance()
self.event_slots()
self.real_event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
self.not_concluded_account() # 미체결 요청
# 실시간
# self.calculator_fnc() # 종목 분석용, 임시용으로 실행
self.read_code() # 저장된 종목들 불러오기
self.screen_number_setting() # 스크린 번호 할당
# 장 시작 시간이냐 장 끝 시간이냐
# KOA studio - 실시간 목록 - Real Type: 장시작시간 # FID 번호
self.dynamicCall("SetRealReg(QString, QString, QString, QString)", self.screen_start_stop_real, "", self.realType.REALTYPE['장시작시간']['장운영구분'], "0")
for code in self.portfolio_stock_dict.keys():
screen_num = self.portfolio_stock_dict[code]['스크린번호']
fids = self.realType.REALTYPE['주식체결']['체결시간'] # 틱발생시 - 체결시간 하나만 꺼내오라고 하면 전체 다 가져옴
self.dynamicCall("SetRealReg(QString, QString, QString, QString)", screen_num, code, fids, "1") # 최초등록 0, 추가등록 1
print(f"실시간 등록 코드 : {code}, 스크린번호 : {screen_num}, 번호: {fids}")
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def real_event_slots(self):
# KOA studio - 개발가이드 - 조회와 실시간데이터처리 - 관련함수
self.OnReceiveRealData.connect(self.realdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", self.screen_my_info)
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, self.screen_my_info)
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"): # 미체결
print("미체결 요청")
self.dynamicCall("SetInputValue(QString, QString)", "계좌번호", self.account_num)
# self.dynamicCall("SetInputValue(QString, QString)", "전체종목구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "체결구분", "1") # 1 미체결
self.dynamicCall("CommRqData(String, String, int, String)", "실시간미체결요청", "opt10075", sPrevNext, self.screen_my_info)
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
elif sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
elif sRQName == "실시간미체결요청":
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
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_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 = code.strip()
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.strip().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})
self.not_account_stock_dict[order_no].update({"체결량": ok_quantity})
print(f"미체결 종목 : {self.not_account_stock_dict[order_no]}")
self.detail_acount_info_event_loop.exit()
if sRQName == "주식일봉차트조회":
code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "종목코드")
code = code.strip()
print(f"{code} 일봉데이터 요청")
cnt = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
print(f"데이터 일수 {cnt}")
# data = self.dynamicCall("GetCommDataEx(QString, QString)", sTrCode, sRQName)
# [['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],
# 한 번 조회하면 600일치까지 일봉데이터를 받을 수 있다.
# KOA studio - TR 목록 - opt10081 주식일봉차트조회요청
for i in range(cnt): # [0,...,599]
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())
data.append("")
self.calcul_data.append(data.copy())
# print(self.calcul_data)
# print(len(self.calcul_data))
if sPrevNext == "2": # 과거 데이터가 더 있다면
self.day_kiwoom_db(code=code, sPrevNext=sPrevNext)
else:
# 조건에 부합하는 종목 추출
print(f"총 일수 {len(self.calcul_data)}")
pass_success = False
# 120일 이평선을 그릴 만큼의 데이터가 있는지 체크
if self.calcul_data == None or len(self.calcul_data) < 120:
pass_success = False
else:
# 120일 이상이 되면
total_price = 0
for value in self.calcul_data[:120]: # [오늘, 하루전,...,119일전]
total_price += int(value[1]) # 현재가(종가)
moving_average_price = total_price / 120 # 120일 이평선 오늘 평균가격
# moving_average_price = int(total_price / 120) # 120일 이평선 오늘 평균가격
# 오늘자의 저가 7가 120일 이평선 오늘 평균가격보다 낮아야 된다 # 오늘자의 고가 6
# 오늘자 주가가 120일 이평선에 걸쳐있는지 확인
bottom_stock_price = False
check_price = None
if int(self.calcul_data[0][7]) <= moving_average_price and moving_average_price <= int(self.calcul_data[0][6]):
print("오늘 주가 120이평선에 걸쳐있는 것 확인")
bottom_stock_price = True
check_price = int(self.calcul_data[0][6]) # 오늘의 고가
# 과거 일봉들이 120일 이평선보다 밑에 있는지 확인
# 확인하다가 일봉이 120일 이평선보다 위에 있으면 계산 진행
prev_price = None # 과거의 일봉 저가
if bottom_stock_price == True:
moving_average_price_prev = 0 # 과거
price_top_moving = False
idx = 1
while True:
if len(self.calcul_data[idx:]) < 120: # 120일치가 있는지 계속 확인
print("120일치가 없음!")
break
total_price = 0
for value in self.calcul_data[idx:120+idx]:
total_price += int(value[1])
moving_average_price_prev = total_price / 120
# 고가가 이평선 위에 있을 경우
if moving_average_price_prev <= int(self.calcul_data[idx][6]) and idx <=20: # 이평선아래 있는 구간이 20일은 되게
print("20일 동안 주가가 120일 이평선과 같거나 위에 있으면 조건 통과 못함")
price_top_moving = False
break
elif int(self.calcul_data[idx][7]) > moving_average_price_prev and idx > 20: # 7 저가가 이평선보다 위에 있고 20일보다 많으면
print("120일 이평선 위에 있는 일봉 확인됨")
price_top_moving = True
prev_price = int(self.calcul_data[idx][7]) # 이 때의 저가 가격 확인
break
idx += 1
# 해당 부분 이평선이 가장 최근 일자의 이평선 가격보다 낮은지 확인
if price_top_moving == True:
if moving_average_price > moving_average_price_prev and check_price > prev_price:
print("포착된 이평선의 가격이 오늘자(최근일자) 이평선 가격보다 낮은 것 확인됨")
print("포착된 부분의 일봉 저가가 오늘자 일봉의 고가보다 낮은지 확인됨")
pass_success = True
if pass_success == True:
print("조건부 통과됨")
code_nm = self.dynamicCall("GetMasterCodeName(QString)", code)
f = open("files/condition_stock.txt", "a", encoding="utf8")
f.write(f"{code}\t{code_nm}\t{str(self.calcul_data[0][1])}\n") # 1 현재가
f.close()
elif pass_success == False:
print("조건부 통과 못함")
self.calcul_data.clear()
self.calculator_event_loop.exit()
# KOA studio - 개발가이드 - 기타함수 - 종목정보관련함수 - GetCodeListByMarket
def get_code_list_by_market(self, market_code):
'''
종목 코드들 반환
:param market_code:
:return:
'''
code_list = self.dynamicCall("GetCodeListByMarket(QString)", market_code)
# "109012;2394823;293481;092834;" → ["11912", "@349023", ""]
code_list = code_list.split(";")[:-1]
return code_list
def calculator_fnc(self):
'''
종목 분석 실행용 함수
:return:
'''
code_list = self.get_code_list_by_market("10")
print(f"코스닥 갯수 {len(code_list)}")
for idx, code in enumerate(code_list):
self.dynamicCall("DisconnectRealData(QString)", self.sereen_calculation_stock)
print(f"{idx+1} / {len(code_list)} : KOSDAQ Stock Code: {code} is updating...")
self.day_kiwoom_db(code=code)
def day_kiwoom_db(self, code=None, date=None, sPrevNext="0"):
# 시간 텀을 주자
QTest.qWait(3600) # 3초 딜레이
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
if date != None:
self.dynamicCall("SetInpuValue(QString, QString)", "기준일자", date)
self.dynamicCall("CommRqData(QString, QString, int, QString)", "주식일봉차트조회", "opt10081", sPrevNext, self.sereen_calculation_stock)
self.calculator_event_loop.exec_()
# 매수법칙 계산 들어가면 됨
def read_code(self):
if os.path.exists("files/condition_stock.txt"): # 존재하냐
f = open("files/condition_stock.txt", "r", encoding="utf8")
lines = f.readlines()
for line in lines:
if line != "":
ls = line.split("\t") # ["230923", "종목명", "현재가\n"]
stock_code = ls[0]
stock_name = ls[1]
stock_price = int(ls[2].split("\n")[0])
stock_price = abs(stock_price)
self.portfolio_stock_dict.update({stock_code:{"종목명":stock_name, "현재가":stock_price}}) # 딕셔너리 업데이트
# {"2090923":{"종목명":"삼성","현재가":"2000"}, "091092":{"종목명":"LG"...}}
f.close()
print(self.portfolio_stock_dict)
def screen_number_setting(self):
# 겹치는 종목 있는지 확인
screen_overwrite = []
# 계좌평가잔고내역에 있는 종목들
for code in self.account_stock_dict.keys():
if code not in screen_overwrite:
screen_overwrite.append(code)
# 미체결에 있는 종목들
for order_number in self.not_account_stock_dict.keys():
code = self.not_account_stock_dict[order_number]['종목코드']
if code not in screen_overwrite:
screen_overwrite.append(code)
# 포트폴리오에 담겨있는 종목들
for code in self.portfolio_stock_dict.keys():
if code not in screen_overwrite:
screen_overwrite.append(code)
# 스크린번호 할당
# 스크린번호 하나에 요청 갯수는 100개까지
# 스크린번호는 200개까지 생성가능
cnt = 0
for code in screen_overwrite:
temp_screen = int(self.screen_real_stock)
meme_screen = int(self.screen_real_stock)
if (cnt % 50) == 0:
temp_screen += 1 # 스크린번호 하나당 50개까지만 할당하겠다 5000 → 5001
self.screen_real_stock = str(temp_screen)
if (cnt % 50) == 0:
meme_screen += 1
self.screen_meme_stock = str(meme_screen)
if code in self.portfolio_stock_dict.keys():
self.portfolio_stock_dict[code].update({"스크린번호": str(self.screen_real_stock)})
self.portfolio_stock_dict[code].update({"주문용스크린번호": str(self.screen_meme_stock)})
elif code not in self.portfolio_stock_dict.keys():
self.portfolio_stock_dict.update({code: {"스크린번호":str(self.screen_real_stock), "주문용스크린번호":str(self.screen_meme_stock)}})
cnt += 1
print(self.portfolio_stock_dict)
def realdata_slot(self, sCode, sRealType, sRealData):
# print(sCode)
if sRealType == "장시작시간": # KOA studio - 실시간목록 - Real Type:장시작시간
fid = self.realType.REALTYPE[sRealType]['장운영구분']
# KOA studio - 개발가이드 - 조회와 실시간데이터처리 - 관련함수
value = self.dynamicCall("GetCommRealData(QString, int)", sCode, fid)
if value == '0':
print("장 시작 전")
elif value == '3':
print("장 시작")
elif value == '2':
print("장 종료, 동시호가로 넘어감")
elif value == '4':
print("3시30분 장 종료")
elif sRealType == "주식체결":
print(sCode)
kiwoom>kiwoom.py ====================60강 틱봉마다 데이터 업데이트
import os
from PyQt5.QAxContainer import *
from PyQt5.QtCore import *
from config.errorCode import *
from PyQt5.QtTest import *
from config.kiwoomType import *
class Kiwoom(QAxWidget):
def __init__(self):
super().__init__() # 부모의 초기값을 그대로 쓰겠다
print("Kiwoom 클래스 입니다.")
self.realType = RealType()
####### eventloop 모음
self.login_event_loop = None
self.detail_acount_info_event_loop = QEventLoop()
# self.detail_acount_info_event_loop_2 = QEventLoop()
self.calculator_event_loop = QEventLoop()
########################
####### 스크린번호 모음
self.screen_my_info = "2000"
self.sereen_calculation_stock = "4000"
self.screen_real_stock = "5000" # 종목별로 할당할 스크린 번호
self.screen_meme_stock = "6000" # 종목별 할당할 주문용 스크린 번호
self.screen_start_stop_real = "1000" #
#######################
####### 변수 모음
self.account_num = None
#######################
####### 계좌 관련 변수
self.use_money = 0
self.use_money_percent = 0.5
#######################
####### 딕션너리 모음
self.account_stock_dict = {}
self.not_account_stock_dict = {}
self.portfolio_stock_dict = {}
#######################
####### 종목 분석 용
self.calcul_data = []
#######################
self.get_ocx_instance()
self.event_slots()
self.real_event_slots()
self.signal_login_commConnect()
self.get_account_info() # 계좌번호 받아오기
self.detail_account_info() # 예수금 가져오는 부분
self.detail_account_mystock() # 계좌평가 잔고내역 요청
self.not_concluded_account() # 미체결 요청
# 실시간
# self.calculator_fnc() # 종목 분석용, 임시용으로 실행
self.read_code() # 저장된 종목들 불러오기
self.screen_number_setting() # 스크린 번호 할당
# 장 시작 시간이냐 장 끝 시간이냐
# KOA studio - 실시간 목록 - Real Type: 장시작시간 # FID 번호
self.dynamicCall("SetRealReg(QString, QString, QString, QString)", self.screen_start_stop_real, "", self.realType.REALTYPE['장시작시간']['장운영구분'], "0")
for code in self.portfolio_stock_dict.keys():
screen_num = self.portfolio_stock_dict[code]['스크린번호']
fids = self.realType.REALTYPE['주식체결']['체결시간'] # 틱발생시 - 체결시간 하나만 꺼내오라고 하면 전체 다 가져옴
self.dynamicCall("SetRealReg(QString, QString, QString, QString)", screen_num, code, fids, "1") # 최초등록 0, 추가등록 1
print(f"실시간 등록 코드 : {code}, 스크린번호 : {screen_num}, 번호: {fids}")
def get_ocx_instance(self):
self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
def event_slots(self):
self.OnEventConnect.connect(self.login_slot) # 로그인용 이벤트
# TR용 이벤트 - KOA 스튜디오 - 개발가이드 - 조회와실시간데이터처리 - 관련함수
self.OnReceiveTrData.connect(self.trdata_slot)
def real_event_slots(self):
# KOA studio - 개발가이드 - 조회와 실시간데이터처리 - 관련함수
self.OnReceiveRealData.connect(self.realdata_slot)
def signal_login_commConnect(self):
self.dynamicCall("CommConnect()")
self.login_event_loop = QEventLoop()
self.login_event_loop.exec_() # 로그인이 완료될 때까지 다음 코드 실행 안되게 막음
def login_slot(self, errCode):
print(errors(errCode))
self.login_event_loop.exit()
def get_account_info(self): # 계좌번호 받아오기
account_list = self.dynamicCall("GetLoginInfo(String)", "ACCNO") # 계좌번호 확인해보자
# account_list = self.dynamicCall("GetLoginInfo(String)", "USER_ID") # 계좌번호 확인해보자
self.account_num = account_list.split(';')[0]
print(f"나의 보유 계좌번호 {self.account_num}")
def detail_account_info(self): # 예수금 가져오는 부분 ######## signal로 요청하고
print("예수금 요청하는 부분")
# KOA 스튜디오 - TR목록 - 예수금상세 - 다음 버튼
# SetInputValue 인적사항 작성해서 signal로 정보 요청 CommRqData
# - 이벤트 OnReceiveTrData에 의해 slot 결과값 GetCommData 가져옴
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
self.dynamicCall("SetInputValue(String, String)", "비밀번호", "0000")
self.dynamicCall("SetInputValue(String, String)", "비밀번호입력매체구분", "00")
self.dynamicCall("SetInputValue(String, String)", "조회구분", "2") # 일반조회
# 예수금 상세 조회 요청 CommRqData
# ("내가 지은 요청이름", "TR번호", "preNext", "화면번호") - 스크린번호 - 그룹 - 항목100개로 200개그룹까지 가능
self.dynamicCall("CommRqData(String, String, int, String)", "예수금상세현황요청", "opw00001", "0", self.screen_my_info)
# 참고 설명
# ("DisconnectRealData(QString)","2000") 스크린번호 취소 가능 - 안에 있는 내용물들 다 날라감
# ("SetRealRemove(QString, QString)","2001","종목101") 스크린번호 안에 있는 종목 하나 취소가능 - 지정한 종목만 날라감
# 데이터 처리하는 동안 다른 작업을 할 수 있게 하는 것 - 이벤트루프
# 예수금 요청을 하는 동안에 결과 값을 다 받고
# 이벤트루프 실행하는 것을 직접 종료해 주지 않으면
# 이 다음 코드가 실행이 안된다.
# 이 사이에서 이벤트루프를 실행해 주어야 된다
self.detail_acount_info_event_loop = QEventLoop()
self.detail_acount_info_event_loop.exec_()
def detail_account_mystock(self, sPrevNext="0"):
print(f"계좌평가 잔고내역 요청하기 연속조회 {sPrevNext}")
self.dynamicCall("SetInputValue(String, String)", "계좌번호", self.account_num)
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)", "계좌평가잔고내역요청", "opw00018", sPrevNext, self.screen_my_info)
# KOA studio - TR 목록 - 계좌평가 잔고내역 - 영웅문0391
self.detail_acount_info_event_loop.exec_()
def not_concluded_account(self, sPrevNext="0"): # 미체결
print("미체결 요청")
self.dynamicCall("SetInputValue(QString, QString)", "계좌번호", self.account_num)
# self.dynamicCall("SetInputValue(QString, QString)", "전체종목구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "매매구분", "0")
self.dynamicCall("SetInputValue(QString, QString)", "체결구분", "1") # 1 미체결
self.dynamicCall("CommRqData(String, String, int, String)", "실시간미체결요청", "opt10075", sPrevNext, self.screen_my_info)
self.detail_acount_info_event_loop.exec_()
def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext): # KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - OnReceiveTrData
'''
TR 요청을 받는 구역이다! 슬롯이다!
:param sScrNo: 스크린번호
:param sRQName: 내가 요청했을 때 지은 이름
:param sTrCode: 요청id, TR코드
:param sRecordName: 사용 안함
:param sPrevNext: 다음 페이지가 있는지
:return:
'''
# 다음 페이지 없으면 - sPrevNext : "0", "" 으로 나온다
######## TR로 받아서
if sRQName == "예수금상세현황요청":
# KOA 스튜디오 - 개발가이드 - 조회와 실시간데이터처리 - GetCommData ######## 해당 함수로 꺼내왔다GetCommData
deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "예수금")
print(f"예수금 {int(deposit)}")
ok_deposit = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "출금가능금액")
print(f"출금가능금액 {int(ok_deposit)}")
# 데이터 다 받았으니까
self.detail_acount_info_event_loop.exit()
elif sRQName == "계좌평가잔고내역요청":
# 총매입금액
total_buy_money = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총매입금액")
# string형태로 나온다
total_buy_money_result = int(total_buy_money)
print(f"총매입금액 {total_buy_money_result}")
total_profit_loss_rate = self.dynamicCall("GetCommData(String, String, int, String", sTrCode, sRQName, 0, "총수익률(%)")
total_profit_loss_rate_result = float(total_profit_loss_rate)
print(f"총수익률(%) {total_profit_loss_rate_result}")
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
cnt = 0
for i in range(rows):
# KOA studio - TR목록 - 계좌평가잔고내역
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()
stock_quantity = int(stock_quantity.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.strip())
possible_quantity = int(possible_quantity.strip())
self.account_stock_dict[code].update({"종목명": code_nm})
self.account_stock_dict[code].update({"보유수량": stock_quantity})
self.account_stock_dict[code].update({"매입가": buy_price})
self.account_stock_dict[code].update({"수익률(%)": learn_rate})
self.account_stock_dict[code].update({"현재가": current_price})
self.account_stock_dict[code].update({"매입금액": total_chegual_price})
self.account_stock_dict[code].update({"매매가능수량": possible_quantity})
# {"02039":{"종목명":"어디", "보유수량":1}}
cnt += 1
print(f"계좌에 가지고 있는 종목 {self.account_stock_dict} ")
print(f"계좌에 보유 종목 카운트 {cnt} ")
# 다음 페이지 있으면 - 계좌평가잔고내역 시그널을 또 보내줘야 된다
if sPrevNext == "2":
self.detail_account_mystock(sPrevNext="2")
else:
self.detail_acount_info_event_loop.exit() # 더 이상 조회할게 없으니까 연결을 끊어준거다
elif sRQName == "실시간미체결요청":
# KOA studio - 개발가이드 - 조회와실시간데이터처리 - GETRepeatCnt
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_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 = code.strip()
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.strip().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})
self.not_account_stock_dict[order_no].update({"체결량": ok_quantity})
print(f"미체결 종목 : {self.not_account_stock_dict[order_no]}")
self.detail_acount_info_event_loop.exit()
if sRQName == "주식일봉차트조회":
code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, "종목코드")
code = code.strip()
print(f"{code} 일봉데이터 요청")
cnt = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
print(f"데이터 일수 {cnt}")
# data = self.dynamicCall("GetCommDataEx(QString, QString)", sTrCode, sRQName)
# [['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],['', '현재가', '거래량', '거래대금', '날짜', '시가', '고가', '저가', ''],
# 한 번 조회하면 600일치까지 일봉데이터를 받을 수 있다.
# KOA studio - TR 목록 - opt10081 주식일봉차트조회요청
for i in range(cnt): # [0,...,599]
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())
data.append("")
self.calcul_data.append(data.copy())
# print(self.calcul_data)
# print(len(self.calcul_data))
if sPrevNext == "2": # 과거 데이터가 더 있다면
self.day_kiwoom_db(code=code, sPrevNext=sPrevNext)
else:
# 조건에 부합하는 종목 추출
print(f"총 일수 {len(self.calcul_data)}")
pass_success = False
# 120일 이평선을 그릴 만큼의 데이터가 있는지 체크
if self.calcul_data == None or len(self.calcul_data) < 120:
pass_success = False
else:
# 120일 이상이 되면
total_price = 0
for value in self.calcul_data[:120]: # [오늘, 하루전,...,119일전]
total_price += int(value[1]) # 현재가(종가)
moving_average_price = total_price / 120 # 120일 이평선 오늘 평균가격
# moving_average_price = int(total_price / 120) # 120일 이평선 오늘 평균가격
# 오늘자의 저가 7가 120일 이평선 오늘 평균가격보다 낮아야 된다 # 오늘자의 고가 6
# 오늘자 주가가 120일 이평선에 걸쳐있는지 확인
bottom_stock_price = False
check_price = None
if int(self.calcul_data[0][7]) <= moving_average_price and moving_average_price <= int(self.calcul_data[0][6]):
print("오늘 주가 120이평선에 걸쳐있는 것 확인")
bottom_stock_price = True
check_price = int(self.calcul_data[0][6]) # 오늘의 고가
# 과거 일봉들이 120일 이평선보다 밑에 있는지 확인
# 확인하다가 일봉이 120일 이평선보다 위에 있으면 계산 진행
prev_price = None # 과거의 일봉 저가
if bottom_stock_price == True:
moving_average_price_prev = 0 # 과거
price_top_moving = False
idx = 1
while True:
if len(self.calcul_data[idx:]) < 120: # 120일치가 있는지 계속 확인
print("120일치가 없음!")
break
total_price = 0
for value in self.calcul_data[idx:120+idx]:
total_price += int(value[1])
moving_average_price_prev = total_price / 120
# 고가가 이평선 위에 있을 경우
if moving_average_price_prev <= int(self.calcul_data[idx][6]) and idx <=20: # 이평선아래 있는 구간이 20일은 되게
print("20일 동안 주가가 120일 이평선과 같거나 위에 있으면 조건 통과 못함")
price_top_moving = False
break
elif int(self.calcul_data[idx][7]) > moving_average_price_prev and idx > 20: # 7 저가가 이평선보다 위에 있고 20일보다 많으면
print("120일 이평선 위에 있는 일봉 확인됨")
price_top_moving = True
prev_price = int(self.calcul_data[idx][7]) # 이 때의 저가 가격 확인
break
idx += 1
# 해당 부분 이평선이 가장 최근 일자의 이평선 가격보다 낮은지 확인
if price_top_moving == True:
if moving_average_price > moving_average_price_prev and check_price > prev_price:
print("포착된 이평선의 가격이 오늘자(최근일자) 이평선 가격보다 낮은 것 확인됨")
print("포착된 부분의 일봉 저가가 오늘자 일봉의 고가보다 낮은지 확인됨")
pass_success = True
if pass_success == True:
print("조건부 통과됨")
code_nm = self.dynamicCall("GetMasterCodeName(QString)", code)
f = open("files/condition_stock.txt", "a", encoding="utf8")
f.write(f"{code}\t{code_nm}\t{str(self.calcul_data[0][1])}\n") # 1 현재가
f.close()
elif pass_success == False:
print("조건부 통과 못함")
self.calcul_data.clear()
self.calculator_event_loop.exit()
# KOA studio - 개발가이드 - 기타함수 - 종목정보관련함수 - GetCodeListByMarket
def get_code_list_by_market(self, market_code):
'''
종목 코드들 반환
:param market_code:
:return:
'''
code_list = self.dynamicCall("GetCodeListByMarket(QString)", market_code)
# "109012;2394823;293481;092834;" → ["11912", "@349023", ""]
code_list = code_list.split(";")[:-1]
return code_list
def calculator_fnc(self):
'''
종목 분석 실행용 함수
:return:
'''
code_list = self.get_code_list_by_market("10")
print(f"코스닥 갯수 {len(code_list)}")
for idx, code in enumerate(code_list):
self.dynamicCall("DisconnectRealData(QString)", self.sereen_calculation_stock)
print(f"{idx+1} / {len(code_list)} : KOSDAQ Stock Code: {code} is updating...")
self.day_kiwoom_db(code=code)
def day_kiwoom_db(self, code=None, date=None, sPrevNext="0"):
# 시간 텀을 주자
QTest.qWait(3600) # 3초 딜레이
self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
if date != None:
self.dynamicCall("SetInpuValue(QString, QString)", "기준일자", date)
self.dynamicCall("CommRqData(QString, QString, int, QString)", "주식일봉차트조회", "opt10081", sPrevNext, self.sereen_calculation_stock)
self.calculator_event_loop.exec_()
# 매수법칙 계산 들어가면 됨
def read_code(self):
if os.path.exists("files/condition_stock.txt"): # 존재하냐
f = open("files/condition_stock.txt", "r", encoding="utf8")
lines = f.readlines()
for line in lines:
if line != "":
ls = line.split("\t") # ["230923", "종목명", "현재가\n"]
stock_code = ls[0]
stock_name = ls[1]
stock_price = int(ls[2].split("\n")[0])
stock_price = abs(stock_price)
self.portfolio_stock_dict.update({stock_code:{"종목명":stock_name, "현재가":stock_price}}) # 딕셔너리 업데이트
# {"2090923":{"종목명":"삼성","현재가":"2000"}, "091092":{"종목명":"LG"...}}
f.close()
print(self.portfolio_stock_dict)
def screen_number_setting(self):
# 겹치는 종목 있는지 확인
screen_overwrite = []
# 계좌평가잔고내역에 있는 종목들
for code in self.account_stock_dict.keys():
if code not in screen_overwrite:
screen_overwrite.append(code)
# 미체결에 있는 종목들
for order_number in self.not_account_stock_dict.keys():
code = self.not_account_stock_dict[order_number]['종목코드']
if code not in screen_overwrite:
screen_overwrite.append(code)
# 포트폴리오에 담겨있는 종목들
for code in self.portfolio_stock_dict.keys():
if code not in screen_overwrite:
screen_overwrite.append(code)
# 스크린번호 할당
# 스크린번호 하나에 요청 갯수는 100개까지
# 스크린번호는 200개까지 생성가능
cnt = 0
for code in screen_overwrite:
temp_screen = int(self.screen_real_stock)
meme_screen = int(self.screen_real_stock)
if (cnt % 50) == 0:
temp_screen += 1 # 스크린번호 하나당 50개까지만 할당하겠다 5000 → 5001
self.screen_real_stock = str(temp_screen)
if (cnt % 50) == 0:
meme_screen += 1
self.screen_meme_stock = str(meme_screen)
if code in self.portfolio_stock_dict.keys():
self.portfolio_stock_dict[code].update({"스크린번호": str(self.screen_real_stock)})
self.portfolio_stock_dict[code].update({"주문용스크린번호": str(self.screen_meme_stock)})
elif code not in self.portfolio_stock_dict.keys():
self.portfolio_stock_dict.update({code: {"스크린번호":str(self.screen_real_stock), "주문용스크린번호":str(self.screen_meme_stock)}})
cnt += 1
print(self.portfolio_stock_dict)
def realdata_slot(self, sCode, sRealType, sRealData):
# print(sCode)
if sRealType == "장시작시간": # KOA studio - 실시간목록 - Real Type:장시작시간
fid = self.realType.REALTYPE[sRealType]['장운영구분']
# KOA studio - 개발가이드 - 조회와 실시간데이터처리 - 관련함수
value = self.dynamicCall("GetCommRealData(QString, int)", sCode, fid)
if value == '0':
print("장 시작 전")
elif value == '3':
print("장 시작")
elif value == '2':
print("장 종료, 동시호가로 넘어감")
elif value == '4':
print("3시30분 장 종료")
elif sRealType == "주식체결":
a = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["체결시간"]) # HHMMSS
b = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["현재가"]) # +(-) 2500
b = abs(int(b))
c = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["전일대비"]) # +(-) 50
c = abs(int(c))
d = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["등락율"]) # +(-) 12.98
d = float(d)
e = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["(최우선)매도호가"]) # +(-) 50
e = abs(int(e))
f = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["(최우선)매수호가"]) # +(-) 50
f = abs(int(f))
g = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["거래량"]) # +(-) 50
g = abs(int(g))
h = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["누적거래량"]) # +(-) 50
h = abs(int(h))
i = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["고가"]) # +(-) 50
i = abs(int(i))
j = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["시가"]) # +(-) 50
j = abs(int(j))
k = self.dynamicCall("GetCommRealData(QString, int)", sCode, self.realType.REALTYPE[sRealType]["저가"]) # +(-) 50
k = abs(int(k))
if sCode not in self.portfolio_stock_dict:
self.portfolio_stock_dict.update({sCode:{}})
self.portfolio_stock_dict[sCode].update({"체결시간": a})
self.portfolio_stock_dict[sCode].update({"현재가": b})
self.portfolio_stock_dict[sCode].update({"전일대비": c})
self.portfolio_stock_dict[sCode].update({"등락율": d})
self.portfolio_stock_dict[sCode].update({"(최우선)매도호가": e})
self.portfolio_stock_dict[sCode].update({"(최우선)매수호가": f})
self.portfolio_stock_dict[sCode].update({"거래량": g})
self.portfolio_stock_dict[sCode].update({"누적거래량": h})
self.portfolio_stock_dict[sCode].update({"고가": i})
self.portfolio_stock_dict[sCode].update({"시가": j})
self.portfolio_stock_dict[sCode].update({"저가": k})
print(self.portfolio_stock_dict[sCode])
kiwoom>kiwoom.py ====================
kiwoom>kiwoom.py ====================
kiwoom>kiwoom.py ====================
kiwoom>kiwoom.py ====================
'컴퓨터 > Python' 카테고리의 다른 글
opencv 마우스 클릭 4번 이미지 가져오기 - 마지막 코드 (0) | 2022.11.18 |
---|---|
jupyter notebook 배치파일 만들기 (0) | 2022.11.13 |
python class 이해 (0) | 2022.10.27 |
python 기다리기 time.sleep 등 (0) | 2022.10.25 |
python excel 셀에 삽입된 이미지 파일 전체 삭제하기 (0) | 2022.10.24 |