상세 컨텐츠

본문 제목

QEventLoop를 이용해 키움증권 API 동기 처리하기

develop

by @polrais 2020. 4. 18. 22:06

본문

키움 증권에서 제공하는 API는 pyQt 라이브러리를 이용해서 python으로 호출할 수 있다. Qt에서는 키움 API 호출이 시그널 - 슬롯 형태로 처리가 된다.

시그널이란 이벤트를 말하고 슬롯은 이벤트가 발생하면 동작할 메소드를 의미한다. 예를 들어 브라우저 닫기를 클릭하면 클릭이라는 시그널이 발생하고 브라우저를 종료하는 슬롯이 실행되는 것이다.

API로 삼성전자 종목 정보를 조회하고 싶다면 다음과 같이 호출하면 된다.

def getStockBasicInfo(code): # opt10001
    rqname = "getStockBasicInfo"
    kiwoom.dynamicCall("SetInputValue(QString, QString)", "종목코드", code) 
    kiwoom.dynamicCall("CommRqData(QString, QString, int, QString)", "getStockBasicInfo", "opt10001", 0, "0101")

getStockBasicInfo("005930") # 005930 삼성전자

하지만 dynamicCall 메소드의 호출 결과가 바로 리턴되는 것이 아니라 API 호출 결과가 준비되면 OnReceiveTrData라는 시그널(이벤트)가 발생한다. 결과를 받기 위해서는 OnReceiveTrData 시그널과 연결된 슬롯에서 처리를 해줘야 한다.

시그널에 대한 슬롯은 다음과 같이 등록해 줄 수 있다.

class Kiwoom(QAxWidget):    
    def __init__(self):
        super().__init__()
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
        self.OnReceiveTrData.connect(self._receiveDataHandler) # OnReceiveTrData 시그널 발생시 _receiveDataHandler 메소드 호출 
        self.received_data = None

        ...

        def _receiveDataHandler(self, scrno, rqname, trcode, recordname, prenext, **kwargs):
            data = None

            if rqname == "getStockBasicInfo":
                items = ["종목명", "시가총액", "EPS", "PER", "BPS", "PBR", "ROE", "250최고", "250최저", "시가", "고가", "저가", "현재가", "거래량", "등락율"]
                data = {item:self.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10001", "주식기본정보", 0, item).strip() for item in items}

            self.received_data = data

이렇게 _receiveDataHandler 메소드를 슬롯으로 등록해 주고 필요한 데이터를 얻어오는 로직을 구현하면 된다.

하지만 이 과정이 비동기로 동작하기 때문에 이벤트가 발생한 시점에는 이미 getStockBasicInfo 매소드가 종료된 이후 일 것이다. 만약 getStockBasicInfo 메소드의 리턴값으로 정보를 받아오는 메소드를 구현하고 싶다면 동기 처리가 필요하다. 이때 사용할 수 있는 것이 QEventLoop 객체이다.

QEventLoop 객체는 다음과 같이 동작한다.

event_loop = QEventLoop()
event_loop.exec_() # event_loop.exit()이 호출 될때까지 메소드를 블록함

QEventLoop를 이용해 위의 toy example을 동기로 수정하면 아래와 같다.

class Kiwoom(QAxWidget):    
    def __init__(self):
        super().__init__()
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
        self.OnReceiveTrData.connect(self._receiveDataHandler) # OnReceiveTrData 시그널 발생시 _receiveDataHandler 메소드 호출 
        self.event_loop = QEventLoop()
        self.received_data = None

        ...

        def _receiveDataHandler(self, scrno, rqname, trcode, recordname, prenext, **kwargs):
            data = None

            if rqname == "getStockBasicInfo":
                items = ["종목명", "시가총액", "EPS", "PER", "BPS", "PBR", "ROE", "250최고", "250최저", "시가", "고가", "저가", "현재가", "거래량", "등락율"]
                data = {item:self.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10001", "주식기본정보", 0, item).strip() for item in items}

            self.received_data = data
            self.event_loop.exit()

def getStockBasicInfo(code): # opt10001
    rqname = "getStockBasicInfo"
    kiwoom.dynamicCall("SetInputValue(QString, QString)", "종목코드", code) 
    kiwoom.dynamicCall("CommRqData(QString, QString, int, QString)", "getStockBasicInfo", "opt10001", 0, "0101")
    kiwoom.event_loop.exec_() # event_loop.exit() 이 호출될 때 까지 기다림

    return kiwoom.received_data

getStockBasicInfo("005930") # 005930 삼성전자

이렇게 수정해주면 getStockBasicInfo의 호출결과로 _receiveDataHandler 슬롯이 처리한 결과를 리턴해 줄 수 있다.

'develop' 카테고리의 다른 글

공공데이터 포털 SERVICE KEY IS NOT REGISTERED ERROR  (1) 2020.01.05

관련글 더보기

댓글 영역