키움증권 Open API 차트 데이터 불러오기 (2)
지난 포스팅에서 `OnReceiveTrData` 이벤트 발생 시 `OnReceiveTrData()` 함수로 연결되도록 하고, 이벤트 루프까지 제작해두었다. 또한 `CommRqData()`를 통해 입력한 값들이 `OnReceiveTrData()` 함수를 통해 반환된다는 것도 확인하였다.
이번 포스팅에서는 `prenext`가 2인 경우 즉, 추가로 조회할 데이터가 있는 경우에는 어떻게 불러올 수 있는지 그리고 차트 데이터를 어떻게 불러올 수 있는지 등에 대해 작성할 예정이다.
추가 데이터 조회 구축 - OnReceiveTrData 함수 수정하기
지난 게시글에서 `prenext`가 어떤 함수에서 사용할 수 있는 변수인지 기억이 나는가? 바로 `OnReceiveTrData` 함수에서 사용 가능했다. 하지만 우리가 차트 데이터 조회를 요청하는 함수는 `def rq_chart_data():` 함수였다. 즉, 이 함수에서는 `prenext` 값을 얻어올 수 없다는 것이다. 지난 포스팅에서 제작했던 차트 데이터 조회 요청의 구조를 보면 다음과 같다.
- `def rq_chart_data()` 함수 → `OnReceiveTrData` 이벤트 → `OnReceiveTrData()` 함수
여기서 문제는 추가 데이터를 조회할 수 있는지의 여부를 `OnReceiveTrData()` 함수에 도달해서야 확인할 수 있다는 것이다. 그렇다면 어떻게 해야 `rq_chart_data()` 함수 내에서 추가로 조회가 가능한 데이터가 있는지의 여부를 확인할 수 있을까? 그는 바로 `OnReceiveTrData()` 함수 내에서 self를 이용한 변수를 설정해주면 된다. self는 단순하게 말하자면, 특정 클래스(Class) 내에서의 전역 변수로 사용할 수 있도록 하는 방법이라고 생각하면 된다. 다시 말해, `OnReceiveTrData()`에서 self를 이용한 변수에 추가로 조회가 가능한 데이터의 존재 유무를 확인할 수 있도록 한다면 이후 rq_chart_data 함수에서도 그 값을 이용할 수 있다는 것이다. 보다 편리한 이해를 위해 지난 게시글에서 작성했던 코드를 실행해보고 그를 바탕으로 `OnReceiveTrData`의 코드 구조를 수정하도록 하자. 현재 코드의 결과물은 다음과 같다.
연결되었습니다..
로그인에 성공하였습니다.
scrno: 0101
rqname: hello
trcordname: opt10081
recordname:
prenext: 2
`prenext: 2`라는 부분을 바탕으로 우리는 추가로 조회할 수 있는 데이터가 있다는 것을 확인할 수 있기 때문에, 해당 함수 내에 다음과 같은 내용을 추가해주도록 하자.
if prenext == 2:
self.remained_data = True
elif prenext != 2:
self.remained_data = False
즉, `prenext`가 2라면 남아 있는 데이터가 Ture이며 2가 아니라면 False라는 값을 입력하도록 하는 것이다. 여기서 사용한 self.remained_data 변수는 `def rq_chart_data()` 함수 내에서도 이용할 수 있다. 현재의 `OnReceiveTrData()` 함수의 코드는 아래의 더보기를 클릭하면 볼 수 있다.
def OnReceiveTrData(self, scrno, rqname, trcode, recordname, prenext, unused1, unused2, unused3, unused4):
if prenext == '2':
self.remained_data = True
elif prenext != '2':
self.remained_data = False
print(self.remained_data)
print("scrno:", scrno)
print("rqname:", rqname)
print("trcordname:", trcode)
print("recordname:", recordname)
print("prenext:", prenext)
추가 데이터 조회 구축 - rq_chart_data 함수 수정하기
이제 `OnReceiveTrData()` 함수 내에서 추가로 조회 가능한 데이터가 있는지의 여부를 self.remained_data 변수에 입력하였으니 `rq_chart_data()` 함수 내에서는 self.remained_data라는 변수를 통해 추가 조회를 요청할 것인지를 제작할 수 있다.`
while self.remained_data = True:
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "종목코드", itemcode)
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "기준일자", date)
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", justify)
self.kiwoom.dynamicCall("CommRqData(QString, QString, int, QString)", "hello", "opt10081", 2, "0101")
self.tr_event_loop = QEventLoop()
self.tr_event_loop.exec_()
즉, 위와 같은 코드를 `rq_chart_data()`의 하단 부분에 추가해주면 된다. 그렇다면 아래와 같은 코드의 모습을 갖추게 될텐데, 의아한 점이 있다. while 문을 기준으로 위와 아래의 코드에 어떤 차이점이 있는지 잘 모를 수 있는데, 바로 `CommRqData()`를 사용하는 부분에서 세 번째 변수로 '2'라는 값을 입력해주었다. 마찬가지로, 현재의 `rq_chart_data()` 함수의 코드 구조는 더보기를 클릭하면 확인할 수 있다.
def rq_chart_data(self, itemcode, date, justify):
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "종목코드", itemcode)
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "기준일자", date)
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", justify)
self.kiwoom.dynamicCall("CommRqData(QString, QString, int, QString)", "hello", "opt10081", 0, "0101")
self.tr_event_loop = QEventLoop()
self.tr_event_loop.exec_()
while self.remained_data == True:
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "종목코드", itemcode)
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "기준일자", date)
self.kiwoom.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", justify)
self.kiwoom.dynamicCall("CommRqData(QString, QString, int, QString)", "hello", "opt10081", 2, "0101")
self.tr_event_loop = QEventLoop()
self.tr_event_loop.exec_()
삼성전자의 경우 상장일이 1900년대이기 때문에 엄청나게 많은 데이터들이 있기 때문에 이제 여기까지의 코드를 바탕으로 실행해보면, self.remained_data라는 변수의 값이 True가 입력되어 있다는 것을 확인할 수 있다.
차트 데이터 가져오기 - GetCommData() 함수
일단 앞서 제작한 코드 내에서, `CommRqData()` 내에 입력한 값들이 `OnReceiveTrData()`라는 함수 내에서 각각 scrno, rqname, trcord, recordname 등의 변수로 출력된다는 것을 확인하였다. 다만 여기서 이야기하고자 하는 것은 바로 KOA Studio 내에서도 확인할 수 있듯이, 차트 조회를 요청할 경우 일봉 차트를 조회할 때에는 opt10081이라는 trcode만을 사용해야 한다는 것이다. 하지만 우리는 일봉 차트에서는 괜찮을진 몰라도 분봉 차트를 조회할 경우에는 3분봉이든 5분봉이든 15분봉이든 간에 opt10080을 사용해야만 한다. 그렇다면 3분봉을 사용하든 5분봉을 사용하든 여러 개의 함수를 제작하게 될 수도 있고 opt10080을 사용하는 함수를 여러 번 제작하게 될 수도 있기 때문에 사용자가 임의적으로 사용할 수 있는 rqname이라는 변수를 바탕으로 데이터를 불러오도록 할 것이다. 다시 말해, 지금의 경우에는 rqname이 hello로 설정되어 있으며 이 hello라는 값은 우리가 입력한 값이다. 그렇기 때문에 `OnReceiveTrData()` 내에서 rqname이 hello라면 다음과 같은 함수를 실행하라는 코드를 구축할 수 있다. 그렇다면 이제 `OnReceiveTrData()` 내에 다음과 같은 코드를 추가해주도록 하자.
if rqname == 'hello':
self.opt10081()
즉, rqname이 hello라면 opt10081이라는 함수를 실행하도록 하는 코드이다. 이에 따라서 우리는 opt10081이라는 함수를 제작해주면 되는데, 그 안에서 사용할 함수가 바로 `GetCommData()`함수이다. 이 함수도 마찬가지로 개발 가이드 내에 서술되어 있는 설명을 한 번 보고 넘어가도록 하자. 앞으로 사용할 모든 정보 요청은 위와 같이 OnReceiveTrData 내에서 수신된 rqname을 바탕으로 함수를 연결해주면 된다. opt10081의 경우에는 rqname을 hello로 입력하고 정보를 요청했기 때문에 if rqname == 'hello':라는 조건문을 내걸어 함수를 연결하는 것 뿐이다. 만약 다른 정보를 요청할 때 `CommRqData()` 내에서 rqname 자리에 hi를 입력했다면 마찬가지로 `OnReceiveTrData()` 내에서는 rqname == 'hi':를 통해 연결하고자 하는 함수와 연동하면 된다.
원형 | GetCommData(Trcode, RecordName, index, itemName) |
설명 | 수신 데이터를 반환한다 |
입력값 | Trcode : Tran 코드 RecordName : 레코드 명 Index : 복수데이터 인덱스 ItemName : 아이템 명 |
반환값 | 수신 데이터 |
즉, `GetCommData()`를 사용하기 위해서는 TrCode, RecordName, index, itemName이라는 네 개의 변수를 사용해야 한다. 이와 관련하여 우리는 앞에서 제작했던 코드를 실행하고 `OnReceiveTrData()`에 반환된 그 결과값을 확인했었는데, 그 결과값 안에는 scrno, rqname, trcord, prenext만 있었고 recordname와 index, itemname이라는 세 개의 변수는 찾아볼 수 없었다. 즉, 'TrCode가 opt10081이다'라는 정보만 우리가 확인할 수 있다는 것이다. 그렇다면 나머지 세 개의 변수는 무엇을 입력해야하는지 어디서 확인할 수 있는가? 사실 이는 앞의 포스팅에서 이미 한 번 다루었었다. 물론 여기서도 재차 설명할 것이지만, 앞서 작성했던 포스팅을 읽으면서 봤던 기억이 있다면 그 부분을 상기시키면 보다 이해가 잘 될 수 있으니 봤었나? 싶다면 보고 올 수 있도록 링크를 걸어두도록 하겠다.
자 이제 우리는 KOA Studio 프로그램을 켜서 Recordname과 index, itemname을 확인해보자. 지금 현재 사용하고자 하는 opt10081을 더블클릭하면 다음과 같은 화면이 나온다는 것을 확인할 수 있다.
위의 사진에서 파란색 배경으로 칠해져 있는 구간을 보면 opt10081 : 주식일봉차트조회요청이라고 작성되어 있는데, opt10081은 여러 번 봤듯이 TrCode이며 그 뒤에 있는 주식일봉차트조회요청이 RecordName이다. 마지막으로 ItemName은 그 아래에 있는 종목코드, 현재가, 거래량, 거래대금, 일자, 시가, 고가, 저가, 전일종가 등등 다양한 데이터들을 의미한다. 그렇다면 Index는 어디서 확인할 수 있는지 궁금할 수도 있는데, 이 index라는 값은 지금 몇 번째 조회하고 있는지에 관한 정보를 담고 있는 변수이다. 그렇다면 이제 opt10081이라는 새로운 함수를 만들고 `GetCommData()`를 제작해보도록 하자.
def opt10081(self):
item_code = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", "opt10081", "주식일봉차트조회요청", 0, "종목코드")
print(item_code)
앞서 살펴봤듯이 trcode(첫 번째 자리)에는 opt10081을, recordname(두 번째 자리)에는 주식일봉차트조회요청을, index(세 번째 자리)에는 0을, itemname(네 번째 자리)에는 종목코드를 입력했다. 이제 이를 실행해보면 005930이라는 값을 얻을 수 있게 된다.
아니 근데 이걸 다 일일이 써야 되나? 귀찮은데..
일단 바로 위에 있는 코드를 보면 알 수 있듯이, 코드가 길다보니 지 혼자 줄을 바꾸는 일이 생기게 되는데, 이는 `GetCommData()`라는 함수를 별도로 제작함으로써 보다 깔끔하게 이용할 수 있다. 이제 함수를 만들어보도록 하자.
def GetCommData(self, trcode, recordname, index, itemname):
result = self.kiwoom.dynamicCall("GetCommData(QString, QString, int, QString)", trcode, recordname, index, itemname)
return result
즉 `GetCommData()` 함수는 trcode, recordname, index, itemname이라는 네 가지 변수를 받아오도록 제작했다. 그리고 그 값을 result라는 변수에 입력한 후, 그 변수를 반환하도록 제작하였다. 이제 `self.GetCommData()`라는 코드를 입력함으로써 길다란 self.kiwoom. 뭐시기 뭐시기 코드를 일일이 입력하지 않아도 사용할 수 있게 되었다. 물론 복사 붙여넣기 신공으로 제작할 수도 있지만, 코드가 좌우로 길어지게 되면 알아서 줄이 넘어간다거나 하는 문제점들이 있기 때문에 별도의 함수를 제작함으로써 이용하는 것이 깔끔하고 편리할 수도 있다. 그렇다면 이제 opt10081이라는 함수 내에 지저분하게 제작되어 있던 코드를 다음과 같이 수정해보자.
def opt10081(self):
item_code = self.GetCommData("opt10081", "주식일봉차트조회요청", 0, "종목코드")
print(item_code)
그 후에 다시 실행해보면 역시 마찬가지로 005930이라는 결과값을 얻어오는 모습을 확인할 수 있다. 이제부터 우리는 self.GetCommData(trcode, recordname, index, itemname)이라는 구조를 통해 길다란 코드를 대체할 수 있게 됐다. 앞전의 코드와 비교하여 백배 천배는 깔끔해졌다는 걸 확인할 수 있다. 이를 응용하여, Open API를 사용할 때 자주 사용하게 되는 SetInputValue 또는 CommRqData 역시 하나의 함수로 제작하여 사용할 수도 있으니, 사용하고 싶다면 미리 함수를 제작해놓고 본인만의 방법대로 이 글을 따라와도 된다.
시가와 고가, 저가, 현재가(종가) 전일 종가 불러오기
다시 본론으로 돌아와, 아까 제작하다 말았던 opt10081이라는 함수 내에 종목 코드 외에도 여러 가지 아이템명을 입력하여 데이터를 불러오도록 하자. 아이템 명은 위의 사진에 있는 여러 가지 정보들이 모두 아이템 명이기 때문에, 아래의 코드에 포함되지 않은 정보가 궁금하다면 그에 관한 정보도 따로 구축해도 된다. 본인이 사용할 정보라면 가리지 말고 모두 사용하도록 하자.
def opt10081(self):
item_code = self.GetCommData("opt10081", "주식일봉차트조회요청", 0, "종목코드")
date = self..GetCommData("opt10081", "주식일봉차트조회요청", 0, "일자")
open = self.GetCommData("opt10081", "주식일봉차트조회요청", 0, "시가")
high = self..GetCommData("opt10081", "주식일봉차트조회요청", 0, "고가")
low = self..GetCommData("opt10081", "주식일봉차트조회요청", 0, "저가")
close = self..GetCommData("opt10081", "주식일봉차트조회요청", 0, "현재가")
yester_close = self..GetCommData("opt10081", "주식일봉차트조회요청", 0, "전일종가")
volume = self..GetCommData("opt10081", "주식일봉차트조회요청", 0, "거래량")
trade_volume = self..GetCommData("opt10081", "주식일봉차트조회요청", 0, "거래대금")
print("종목코드:", item_code)
print("조회일자:", date)
print("전일종가:", yester_close)
print("시가:", open)
print("고가:", high)
print("저가:", low)
print("종가:", close)
print("거래량:", volume)
print("거래대금:", trade_volume)
이제 위의 코드를 실행하면, 아래와 같은 결과물을 얻을 수 있다.
종목코드: 005930
조회일자: 20210528
전일종가:
시가: 79800
고가: 80400
저가: 79400
종가: 80100
거래량: 12360199
거래대금: 988666
결과값을 보니 서버로부터 받아온 결과값 안에 의미 없는 띄어쓰기가 많이 포함되어 있는 것을 확인할 수 있다. 이 띄어쓰기는 strip()을 사용함으로써 제거할 수 있는데, 사용례는 아래와 같다. 코드 하나하나 맨 뒤 부분에 .strip()을 붙여넣어주면 띄어쓰기가 제거된 후 깔끔한 결과물을 출력되는 것을 확인할 수 있다.
## strip() 사용하기 ##
item_code = self.GetCommData("opt10081", "주식일봉차트조회요청", 0, "종목코드").strip()
종목코드: 005930
조회일자: 20210528
전일종가:
시가: 79800
고가: 80400
저가: 79400
종가: 80100
거래량: 12360199
거래대금: 988666
이번 포스팅에서 차트 데이터 조회에 대한 내용을 마무리하려 했으나 작성하다보니 내용도 길어지고 눈도 뻑뻑해져서 현재 데이터 뿐만 아니라 이전의 데이터를 불러오도록 하는 내용은 다음 포스팅에서 마무리하도록 하겠습니다.
'AUTO TRADE > [키움증권] Kiwoom Open API' 카테고리의 다른 글
키움증권 Open API 전체 종목 리스트 불러오기 (0) | 2021.06.01 |
---|---|
키움증권 Open API 차트 데이터 불러오기 (3) (3) | 2021.05.31 |
키움증권 Open API 차트 데이터 불러오기 (1) (0) | 2021.05.31 |
키움증권 OpenAPI 개발 가이드 사용 설명서 (2) (0) | 2021.05.22 |
키움증권 Open API 개발 가이드 사용 설명서 (1) (3) | 2021.05.22 |
소중한 공감 감사합니다