대신증권 CYBOS PLUS 프로그램 구현 (10) - 일봉 차트 조회 : 연속 조회 사용하기 ①
프로그램 구현 목표
- 조회 횟수당 최대로 조회할 수 있는 데이터 확인해보기
- Continue 속성 활용하여 이해하기
- 개수 비교 로직 추가하기
- 연속 조회 구현하기
조회 횟수당 최대로 조회할 수 있는 데이터 확인해보기
이전에 만들어둔 프로그램을 실행시켜서 조회 개수 란에 3,000을 입력한 후 데이터를 조회해보면, 서버로부터 회신되는 데이터의 개수는 2,856개밖에 되지 않는다는 사실을 확인할 수 있다. 그렇다면 요청하고자 하는 차트 데이터의 유형에 따라 각각의 유형에 대하여 각기 다른 제한된 데이터 개수가 지정되어 있을까 ? 결론부터 이야기하자면, 일봉이든 주봉이든 분봉이든 틱봉이든 간에 2,856개의 데이터를 한도로 하여 데이터를 회신해준다. 그렇다면 결국에는 2,856개의 데이터가 회신된 후에 우리가 요청하고도 돌려받지 못한 144개의 데이터를 추가로 요청해야 되는데, 이 때 활용해야 하는 정보가 바로 COM의 Continue
속성이다.

이전까지는 우리가 일봉 차트 데이터를 조회할 때, 데이터 회신 제한 개수인 2,856개보다 적은 5개의 데이터만 조회했기 때문에 아무런 문제가 발생하지 않았지만 이보다 큰 5,000개나 10,000개의 데이터를 조회할 때에는 서버로부터 올바른 수량의 데이터를 회신받지 못하는 문제가 발생하게 된다. 따라서 여기서 우리가 서버로 요청한 데이터의 개수(위의 경우 3,000개)와 서버로부터 회신 받은 데이터의 개수(위의 경우에는 2,856개)를 서로 비교하는 로직을 추가해주어야 하는데, 조금 더 구체적으로 살펴보자면 '서버로부터 회신 받은 데이터의 개수를 누적시켜서 우리가 서버로 요청한 데이터의 개수와 같아졌는지'를 확인해주어야 한다.
Continue 속성 활용하여 추가 데이터 조회하기
앞서 언급했던 Continue
속성이란, 단순하게 '아직 추가적으로 조회할 수 있는 데이터가 있는지'를 회신해주는 속성이다. 이 속성은 추가적으로 조회할 수 있는 데이터가 있다면 "1"을 반환해주고 없다면 데이터를 반환해주지 않는다. 여기서 오해하지 않아야 하는 부분이 하나 있는데 여기서 "추가적으로 조회할 수 있는 데이터가 있는지"라는 개념의 기준이 우리가 서버에 요청한 '데이터의 개수'에 있지 않고 그 종목의 전체 '차트 데이터의 개수'에 있다는 것이다.
예를 들어, 서버에 3,000개의 데이터를 요청했을 때에는 서버에서는 당연히 2,856개의 데이터만 회신해줄 것이고 우리는 144개의 데이터를 추가로 요청해야 하므로 Continue
속성으로 반환된 "1"이라는 데이터를 가지고 서버에 재요청을 할 수 있다. 하지만 반대로 서버에 1,000개의 데이터를 요청했을 때에는 서버에서는 2,856개보다 적은 데이터를 요청했으므로 1,000개의 데이터만 전달해주지만, 그와 동시에 그 종목의 일봉 차트 데이터로 조회할 수 있는 데이터가 있으므로 Continue
속성으로 "1"이라는 데이터를 전달해주면서 '추가적으로 조회할 수 있는 데이터가 있음'이라는 신호를 전달해준다.
그렇다면 우리는 어떤 정보를 기준으로 서버에 데이터를 다시 요청할 수 있을까 ? Continue
속성은 반드시 활용해야 하는 것은 맞다. 왜냐하면 서버로부터 데이터를 회신해줄 근거가 없다면 데이터 조회를 멈춰야 하기 때문이다. 하지만 서버로부터 데이터를 회신받을 수 있는 잔여분 데이터가 있다 하더라도 우리가 필요하지 않은(우리가 요청하지 않은) 데이터는 굳이 회신받을 필요는 없는데, 이 때 사용할 수 있는 것이 바로 사전에 만들어둔 데이터프레임에 있는 데이터 개수와 서버에 요청한 데이터 개수를 비교하는 것이다. 여기서 Continue
속성을 활용하여 서버로부터 회신받을 데이터가 있는지 없는지 여부를 먼저 확인하여 이 속성이 어떻게 동작하는지 살펴보고, 이후에 데이터프레임의 개수와 서버에 요청한 개수를 비교하는 로직을 추가하여 '개수로 조회하는 함수'를 완성시켜보자.
일단 기존에 작성했던 def _len_chart(self):
함수가 아닌 def _cont_len_chart(self):
라는 새로운 이름의 함수를 생성해주도록 하자. 이 함수는 별도로 이벤트 처리기와 연결하지 않을 예정이다.(class event_handler_CpSysDib: 내부의 함수인 def OnReceived(self): 함수에 if self.object == "_cont_len_chart":를 사용하지 않을 것이다.) 이 함수 역시 서버에 데이터를 요청할 때에는 종목코드와 차트구분(request
), 그리고 데이터 개수(quantity
)는 필요하기 때문에 def _len_chart(self):
함수와 동일한 인자를 전달받도록 하자.
## CpSysDib.py ## import win32com.client import pandas as pd class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def _cont_len_chart(self, item_code, request, quantity): pass
다음으로, 서버에 데이터를 요청하기 전에 앞서 데이터프레임으로 만들기 위한 dictionary
형태의 변수를 새롭게 생성해주자. 이 변수는 데이터프레임(DataFrame
)으로 만들 때 그 데이터프레임의 근간이 되는 데이터이다. 이 내용은 지난 게시글에서 설명했으니, 여기서는 이쯤 하고 넘어가도록 하자.
※ Line: 11
## CpSysDib.py ## import win32com.client import pandas as pd class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def _cont_len_chart(self, item_code, request, quantity): _df = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]}
이제 이 아래에 들어올 내용은 이전에 작성했던 def _len_chart(self):
함수에서 작성했던 부분과 완벽하게 동일하다. 차이점이 있다면 Line 21 부분에서, set_instance의 두 번째 인자로 "_cont_len_chart"를 전달해준다는 것이다. 하지만 앞서 설명했듯이 이벤트 처리기 내부에서 "_cont_len_chart"를 처리하도록 하는 코드를 구현하지 않을 예정이므로 이 코드는 아무런 기능을 수행하지 않는다. 그럼에도 인자로 전달해주는 이유는, 서버로부터 이벤트가 발생했을 때 그 이벤트를 처리할 담당자가 없으면 오류가 발생하기 때문이다. 즉, 우리는 담당자는 지정해두었지만 그 담당자의 업무를 공란으로 두는 것이다.
※ Line: 12 ~ 22
## CpSysDib.py ## import win32com.client import pandas as pd class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def _cont_len_chart(self, item_code, request, quantity): _df = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} self.stockchart.SetInputValue(0, item_code) self.stockchart.SetInputValue(1, ord("2")) self.stockchart.SetInputValue(4, quantity) self.stockchart.SetInputValue(5, [0, 2, 3, 4, 5, 8, 9]) self.stockchart.SetInputValue(6, ord(f"{request}")) ## 차트 구분("D", "W", "M:) self.stockchart.SetInputValue(8, ord("0")) ## 갭 보정 여부(0: 보정 X, 1: 보정 O) self.stockchart.SetInputValue(9, ord("0")) ## 수정주가 여부(0: 수정 X, 1: 수정 O) self.stockchart.SetInputValue(10, ord("1")) ## 거래량 구분 self.stockchart.SetInputValue(11, ord("N")) ## 조기 적용 여부 self.handlers.set_instance(self.stockchart, "_cont_len_chart") self.stockchart.BlockRequest() ## 데이터 요청
이제 데이터를 요청했으면 서버로부터 데이터를 가져오는 코드가 필요한데, 이 부분 역시 이벤트 처리기에 있는 코드와 동일하다. 다만 다른 코드와의 차이점이 있다면 서버로부터 데이터를 회신받은 후에 각각의 데이터들을 _df
라는 변수에 입력해주는 것이다.(Line: 37~43)
※ Line: 24 ~ 43
## CpSysDib.py ## import win32com.client import pandas as pd class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def _cont_len_chart(self, item_code, request, quantity): _df = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} self.stockchart.SetInputValue(0, item_code) self.stockchart.SetInputValue(1, ord("2")) self.stockchart.SetInputValue(4, quantity) self.stockchart.SetInputValue(5, [0, 2, 3, 4, 5, 8, 9]) self.stockchart.SetInputValue(6, ord(f"{request}")) ## 차트 구분("D", "W", "M:) self.stockchart.SetInputValue(8, ord("0")) ## 갭 보정 여부(0: 보정 X, 1: 보정 O) self.stockchart.SetInputValue(9, ord("0")) ## 수정주가 여부(0: 수정 X, 1: 수정 O) self.stockchart.SetInputValue(10, ord("1")) ## 거래량 구분 self.stockchart.SetInputValue(11, ord("N")) ## 조기 적용 여부 self.handlers.set_instance(self.stockchart, "_cont_len_chart") self.stockchart.BlockRequest() ## 데이터 요청 len_fild = self.stockchart.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.stockchart.GetHeaderValue(2) ## 팔드 배열 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len_data}") for index in range(len_data): date = self.stockchart.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.stockchart.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.stockchart.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.stockchart.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.stockchart.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.stockchart.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.stockchart.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 _df['date'].append(str(date)) _df['open'].append(open) _df['high'].append(high) _df['low'].append(low) _df['close'].append(close) _df['vol'].append(vol) _df['tvol'].append(tvol)
이제부터 Continue
속성을 활용하여 데이터를 추가적으로 조회할 예정인데, 서버로부터 회신되는 데이터가 어떠한지를 살펴보아야 하니 일단 Boss.py 파일의 코드를 아래와 같이 수정한 후에, 다시 CpSysDib.py 파일로 돌아와서 for
문이 끝난 후의 지점에서 아래와 같은 코드를 추가하여 이 속성이 전달해주는 값을 확인해보도록 하자.
※ Boss.py : Line: 32, 45
※ CpSysDib.py : Line: 45
## Boss.py ## import win32com.client from pywinauto import application from COM import CpSysDib from COM import CpUtil from COM import CpTrade from COM import DsCbo1 import time ## GUI ## import sys from PyQt5 import uic from PyQt5.QtWidgets import * main_ui = uic.loadUiType("main.ui")[0] class cybos(QMainWindow, main_ui): def __init__(self): super().__init__() self.setupUi(self) self.stockcode = CpUtil.CpStockCode() self.trade = CpTrade.CpTdUtil() self.stockmst = DsCbo1.StockMst() self._open_cybosplus() self.pushButton.clicked.connect(self._GetStockListByMarket) self.pushButton_2.clicked.connect(self._day_range) self.pushButton_3.clicked.connect(self._len_chart) def _len_chart(self): item_code = self.lineEdit_4.text() data_len = int(self.lineEdit_5.text()) request = "" cur_idx = self.comboBox_2.currentIndex() if cur_idx == 0: request = "T" elif cur_idx == 1: request = "m" elif cur_idx == 2: request = "D" elif cur_idx == 3: request = "W" elif cur_idx == 4: request = "M" CpSysDib.StockChart()._cont_len_chart(item_code, request, data_len)
## CpSysDib.py ## import win32com.client import pandas as pd class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def _cont_len_chart(self, item_code, request, quantity): _df = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} self.stockchart.SetInputValue(0, item_code) self.stockchart.SetInputValue(1, ord("2")) self.stockchart.SetInputValue(4, quantity) self.stockchart.SetInputValue(5, [0, 2, 3, 4, 5, 8, 9]) self.stockchart.SetInputValue(6, ord(f"{request}")) ## 차트 구분("D", "W", "M:) self.stockchart.SetInputValue(8, ord("0")) ## 갭 보정 여부(0: 보정 X, 1: 보정 O) self.stockchart.SetInputValue(9, ord("0")) ## 수정주가 여부(0: 수정 X, 1: 수정 O) self.stockchart.SetInputValue(10, ord("1")) ## 거래량 구분 self.stockchart.SetInputValue(11, ord("N")) ## 조기 적용 여부 self.handlers.set_instance(self.stockchart, "_cont_len_chart") self.stockchart.BlockRequest() ## 데이터 요청 len_fild = self.stockchart.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.stockchart.GetHeaderValue(2) ## 팔드 배열 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len_data}") for index in range(len_data): date = self.stockchart.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.stockchart.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.stockchart.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.stockchart.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.stockchart.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.stockchart.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.stockchart.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 _df['date'].append(str(date)) _df['open'].append(open) _df['high'].append(high) _df['low'].append(low) _df['close'].append(close) _df['vol'].append(vol) _df['tvol'].append(tvol) print(self.stockchart.Continue)
▶ 실행 결과 확인하기
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_cont_len_chart
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:2856
1
개수 비교 로직 추가하기
위에서 구현한 부분은 Continue
속성을 확인하는 부분이었다. 그렇다면 이제는 조회된 데이터의 개수와 우리가 서버에 요청한 데이터의 개수를 확인해주도록 하자. 현재까지 조회된 데이터의 개수는 _df['date']
변수의 개수로 확인할 수 있고, 서버에 요청한 데이터의 개수는 quantity
변수로 확인할 수 있다. 이제 아래와 같이 코드를 수정한 후, 프로그램을 실행시켜서 서버로 5,000개의 일봉 차트 데이터를 요청해보자.
※ Line: 46
## CpSysDib.py ## import win32com.client import pandas as pd class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def _cont_len_chart(self, item_code, request, quantity): _df = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} self.stockchart.SetInputValue(0, item_code) self.stockchart.SetInputValue(1, ord("2")) self.stockchart.SetInputValue(4, quantity) self.stockchart.SetInputValue(5, [0, 2, 3, 4, 5, 8, 9]) self.stockchart.SetInputValue(6, ord(f"{request}")) ## 차트 구분("D", "W", "M:) self.stockchart.SetInputValue(8, ord("0")) ## 갭 보정 여부(0: 보정 X, 1: 보정 O) self.stockchart.SetInputValue(9, ord("0")) ## 수정주가 여부(0: 수정 X, 1: 수정 O) self.stockchart.SetInputValue(10, ord("1")) ## 거래량 구분 self.stockchart.SetInputValue(11, ord("N")) ## 조기 적용 여부 self.handlers.set_instance(self.stockchart, "_cont_len_chart") self.stockchart.BlockRequest() ## 데이터 요청 len_fild = self.stockchart.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.stockchart.GetHeaderValue(2) ## 팔드 배열 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len_data}") for index in range(len_data): date = self.stockchart.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.stockchart.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.stockchart.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.stockchart.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.stockchart.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.stockchart.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.stockchart.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 _df['date'].append(str(date)) _df['open'].append(open) _df['high'].append(high) _df['low'].append(low) _df['close'].append(close) _df['vol'].append(vol) _df['tvol'].append(tvol) print(self.stockchart.Continue) print(f"회신받은 개수:{len(_df['date'])}, 요청한 개수:{quantity}")
▶ 실행 결과 확인하기
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_cont_len_chart
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:2856
1
회신받은 개수:2856, 요청한 개수:5000
이제 프로그램을 어떻게 구현해야할지 대충 감이 올 것이다. 가장 먼저, 우리가 요청한 개수(quantity
)보다 서버로부터 회신받은 데이터의 개수(len(_df['date']
)가 작거나 같은 경우에는 데이터를 추가적으로 조회해야 하는데, 그 때 사용하는 조건문은 아래와 같이 구현할 수 있다. 즉, 이 조건문을 통해 "현재 서버에 요청한 데이터 개수보다 적은 데이터가 회신된 경우"라는 조건을 상정한 후 그 이후에 수행할 작업들을 입력해주면 된다.
## 개수 비교 로직 if len(_df['date']) <= quantity: pass else: pass
□ 만약 Traceback (most recent call last): 오류가 발생한다면
Traceback (most recent call last):
if len(_df['date']) <= quantity:
TypeError: '<=' not supported between instances of 'int' and 'str'
와 같은 오류가 발생하시는 경우에는 Boss.py 파일의 Line 32를 참고하시기 바랍니다. 기본적으로 QLineEdit 클래스가 갖는 text() 메서드의 경우에는 그 데이터값을 문자열 형태로 가져오기 때문에, 애초에 quantity 값이 문자열 형태로 전달되었을 수 있으므로 self.lineEdit_5.text()가 아닌 int(self.lineEdit_5.text())와 같은 형태로 변경해주셔야 합니다. 이 오류는 데이터의 형식이 맞지 않아서 발생하는 오류이며, 문자열의 "5000"과 숫자형의 2,856은 비교할 수 없다는 오류로 문자열인 "5000"을 숫자형인 5,000으로 변경해주시면 되는 간단한 오류입니다.
이제 서버로부터 회신받은 데이터의 개수가 회신받아야 하는 데이터의 개수보다 적다는 것이 확인되었다면, 아래의 두 내용을 구분해야 하는데 이 때 사용하는 것이 Continue
속성이다.
- 정말 그 종목의 일봉 차트로 회신해줄 데이터가 없는지(상장된지 2,856 거래일이 안 됐는지)
- 일봉 차트로 회신해줄 데이터가 있다면 추가 작업 수행
## CpSysDib.py ## if len(_df['date']) <= quantity: ## 아직 다 회신받지 못한 경우 if self.stockchart.Continue == 1: ## 추가 작업 수행 pass else: ## 데이터 조회 종료 pass else: ## 데이터 조회 종료(모두 회신받음) pass
연속 조회 기능 구현하기
이제 서버로 데이터를 요청했으나 ①서버에서 회신받을 추가적인 데이터가 존재하고 ②요청한 데이터를 다 전달받지 못한 경우에만 수행할 작업을 코드로 구현해주면 된다. 이 부분도 크게 어려울 게 없는게 현재 제작 중인 def _cont_len_chart(self):
함수에서 사용했던 코드 중에서 SetInputValue()
를 통해 데이터를 입력해주었던 부분을 제외하고 그 아래에 데이터를 요청하는 코드(set_instance
와 BlockRequest()
)부터 나머지 부분만 그대로 복사해서 넣어주면 된다. (Line 12부터 34까지의 코드가 Line 41에서 63에 그대로 들어감) 아래는 현재까지 작성한 함수의 코드 전문이다.
def _cont_len_chart(self, item_code, request, quantity): _df = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} self.stockchart.SetInputValue(0, item_code) self.stockchart.SetInputValue(1, ord("2")) self.stockchart.SetInputValue(4, quantity) self.stockchart.SetInputValue(5, [0, 2, 3, 4, 5, 8, 9]) self.stockchart.SetInputValue(6, ord(f"{request}")) ## 차트 구분("D", "W", "M:) self.stockchart.SetInputValue(8, ord("0")) ## 갭 보정 여부(0: 보정 X, 1: 보정 O) self.stockchart.SetInputValue(9, ord("0")) ## 수정주가 여부(0: 수정 X, 1: 수정 O) self.stockchart.SetInputValue(10, ord("1")) ## 거래량 구분 self.stockchart.SetInputValue(11, ord("N")) ## 조기 적용 여부 self.handlers.set_instance(self.stockchart, "_cont_len_chart") self.stockchart.BlockRequest() ## 데이터 요청 len_fild = self.stockchart.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.stockchart.GetHeaderValue(2) ## 팔드 배열 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len_data}") for index in range(len_data): date = self.stockchart.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.stockchart.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.stockchart.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.stockchart.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.stockchart.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.stockchart.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.stockchart.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 _df['date'].append(str(date)) _df['open'].append(open) _df['high'].append(high) _df['low'].append(low) _df['close'].append(close) _df['vol'].append(vol) _df['tvol'].append(tvol) print(self.stockchart.Continue) print(f"회신받은 개수:{len(_df['date'])}, 요청한 개수:{quantity}") if len(_df['date']) <= quantity: ## 아직 다 회신받지 못한 경우 if self.stockchart.Continue == 1: ## 추가 작업 수행 self.handlers.set_instance(self.stockchart, "_cont_len_chart") self.stockchart.BlockRequest() ## 데이터 요청 len_fild = self.stockchart.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.stockchart.GetHeaderValue(2) ## 팔드 배열 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len_data}") for index in range(len_data): date = self.stockchart.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.stockchart.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.stockchart.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.stockchart.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.stockchart.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.stockchart.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.stockchart.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 _df['date'].append(str(date)) _df['open'].append(open) _df['high'].append(high) _df['low'].append(low) _df['close'].append(close) _df['vol'].append(vol) _df['tvol'].append(tvol) pass else: ## 데이터 조회 종료 pass else: ## 데이터 조회 종료(모두 회신받음) pass
이제 데이터가 모두 조회된 후에는 차트 데이터를 데이터프레임으로 만들어서 출력해보자. 조회 개수는 5,000개이다.
※ Line: 79, 80
## CpSysDib.py ## import win32com.client import pandas as pd class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def _cont_len_chart(self, item_code, request, quantity): _df = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} self.stockchart.SetInputValue(0, item_code) self.stockchart.SetInputValue(1, ord("2")) self.stockchart.SetInputValue(4, quantity) self.stockchart.SetInputValue(5, [0, 2, 3, 4, 5, 8, 9]) self.stockchart.SetInputValue(6, ord(f"{request}")) ## 차트 구분("D", "W", "M:) self.stockchart.SetInputValue(8, ord("0")) ## 갭 보정 여부(0: 보정 X, 1: 보정 O) self.stockchart.SetInputValue(9, ord("0")) ## 수정주가 여부(0: 수정 X, 1: 수정 O) self.stockchart.SetInputValue(10, ord("1")) ## 거래량 구분 self.stockchart.SetInputValue(11, ord("N")) ## 조기 적용 여부 self.handlers.set_instance(self.stockchart, "_cont_len_chart") self.stockchart.BlockRequest() ## 데이터 요청 len_fild = self.stockchart.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.stockchart.GetHeaderValue(2) ## 팔드 배열 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len_data}") for index in range(len_data): date = self.stockchart.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.stockchart.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.stockchart.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.stockchart.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.stockchart.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.stockchart.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.stockchart.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 _df['date'].append(str(date)) _df['open'].append(open) _df['high'].append(high) _df['low'].append(low) _df['close'].append(close) _df['vol'].append(vol) _df['tvol'].append(tvol) print(self.stockchart.Continue) print(f"회신받은 개수:{len(_df['date'])}, 요청한 개수:{quantity}") if len(_df['date']) <= quantity: ## 아직 다 회신받지 못한 경우 if self.stockchart.Continue == 1: ## 추가 작업 수행 self.handlers.set_instance(self.stockchart, "_cont_len_chart") self.stockchart.BlockRequest() ## 데이터 요청 len_fild = self.stockchart.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.stockchart.GetHeaderValue(2) ## 팔드 배열 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len_data}") for index in range(len_data): date = self.stockchart.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.stockchart.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.stockchart.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.stockchart.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.stockchart.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.stockchart.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.stockchart.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 _df['date'].append(str(date)) _df['open'].append(open) _df['high'].append(high) _df['low'].append(low) _df['close'].append(close) _df['vol'].append(vol) _df['tvol'].append(tvol) pass else: ## 데이터 조회 종료 pass else: ## 데이터 조회 종료(모두 회신받음) pass chart_data = pd.DataFrame(_df, columns=['date', 'open', 'high', 'low', 'close', 'vol', 'tvol']) print(chart_data)
▶ 실행 결과 확인하기
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_cont_len_chart
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:2856
1
회신받은 개수:2856, 요청한 개수:5000
self.object:_cont_len_chart
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:2856
date open high low close vol tvol
0 20240906 69100 69700 68000 69000 12647589 870495000000
1 20240905 70100 71200 69000 69000 25686769 1795890000000
2 20240904 69800 71100 69800 70000 27366563 1923751000000
3 20240903 74100 74300 72500 72500 16314599 1195017000000
4 20240902 74500 74700 73500 74400 12641376 938130000000
... ... ... ... ... ... ... ...
5707 20010802 201000 202000 194000 195500 635840 125604000000
5708 20010801 191500 199500 190500 198000 1157583 227329000000
5709 20010731 187000 190500 186000 189500 444664 84178000000
5710 20010730 188000 189000 184500 185500 401109 74832000000
5711 20010727 186500 189500 184500 185500 576045 107796000000
2,856개의 데이터가 두 번 회신되면서 총 5,712개의 데이터가 수신되었다. 물론 우리가 요청한 데이터의 개수는 5,000개가 맞긴 하지만 5,000개에서 딱 끊어내는 코드는 따로 구현하지 않았다. 다음 게시글에서는 ①두 번보다도 더 많은 횟수의 데이터를 조회할 수 있는 기능과 ②우리가 요청한 데이터의 개수만큼만 출력하도록 하는 기능을 추가한 후 일봉 차트 데이터를 연속으로 조회하는 방법을 마무리하도록 할 예정이다.
소중한 공감 감사합니다