대신증권 CYBOS PLUS 프로그램 구현 (16) - 차트 데이터 저장하기 ③
프로그램 구현 목표
- 차트 데이터 조회하고 종목코드로 MySQL에 저장하기
- GUI에서 저장 여부 결정하는 기능 추가하기
- 그럼 매번 체크해야 차트 데이터를 저장할 수 있어요 ?
- 조회 구분에 따라 각기 다른 데이터베이스에 저장하기
차트 데이터 조회하고 종목코드로 MySQL에 저장하기
차트 데이터 조회는 이전에 만들어두었던 CpSysDib.py 파일의 class StockChart
클래스의 def chart_DWM()
함수를 사용하면 된다. 이제 이 함수를 아래의 두 가지 기능을 수정할 것이다.
※ Line: 10, 68
- 함수의 인자로 차트 데이터 저장 여부(
save_gubun
)) 전달받아서 MySQL에 저장할지 말지 결정하기 return
활용하여 조회한 차트 데이터 반환하기
## 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 chart_DWM(self, item_code, request, quantity, save_gubun): _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, "chart_DWM") self.stockchart.BlockRequest() ## 데이터 요청 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 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) while True: if len(_df['date']) <= quantity: ## 아직 다 회신받지 못한 경우 if self.stockchart.Continue == 1: ## 추가 작업 수행 self.handlers.set_instance(self.stockchart, "chart_DWM") self.stockchart.BlockRequest() ## 데이터 요청 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 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) else: ## 데이터 조회 종료 break else: ## 데이터 조회 종료(모두 회신받음) break chart_data = pd.DataFrame(_df, columns=['date', 'open', 'high', 'low', 'close', 'vol', 'tvol']).iloc[:quantity] return chart_data
가장 먼저 차트 데이터를 저장할지 말지가 담겨 있는 변수인 save_gubun
를 통해 그 값이 True라면 차트 데이터를 저장하고 False라면 차트 데이터를 저장하지 않도록 하는 기능을 구현해보자. 그 전에 앞서 True와 False라는 데이터 형식에 대해 간단하게 살펴보고 넘어가도록 하자. 참과 거짓을 의미하는 True와 False의 데이터타입은 bool
형태의 데이터타입에 해당하는데, 이는 'a'라는 문자열은 str
이라는 데이터타입이고 1이라는 숫자는 int
라는 데이터타입에 해당하는 것과 같은 이치이다. 실제로 특정한 변수에 True와 False를 넣어놓고 데이터타입이 bool
인지를 확인하는 예제 코드를 작성해보자.
a = True b = True print(type(a) == bool) print(type(b) == bool)
▶ 데이터타입이 bool인지 확인하는 코드의 실행 결과
True
True
이 내용을 설명했던 이유는 def chart_DWM
함수에서 전달받은 save_gubun
의 데이터타입을 한 번 확인해주기 위함이었다. 함수의 바로 아래 부분에서 이 변수의 데이터타입이 bool
형태인지 아닌지를 판단하고, 만약 bool
데이터타입이 아닌 다른 데이터가 전달되었다면 제대로 된 기능을 수행할 수 없으므로 return False
를 통해 해당 함수를 종료시키도록 하자.
※ Line: 2, 3
def chart_DWM(self, item_code, request, quantity, save_gubun): if not type(save_gubun) == bool: return False
이제 save_gubun
변수가 bool
데이터타입이 맞다면 그 아래에 있는 코드를 실행시키면서 차트 데이터를 불러올 것이고, 마지막에는 모든 차트 데이터를 데이터프레임으로 만들고 return
을 통해 반환해줄 것이다. 이제 우리가 구현하고자 하는 차트 데이터 저장 여부에 대한 정보가 담겨 있는 save_gubun
를 토대로 차트 데이터를 저장하도록 하는 기능을 구현해보자. 우리는 이 변수의 값이 True인 경우에는 차트 데이터를 저장하고, False인 경우에는 별도로 저장하지 않고 데이터프레임으로 만들어둔 차트 데이터를 그대로 반환해주면 된다.
그렇다면 차트 데이터를 저장하기 위해 우리는 어떠한 함수를 사용해야 할까 ? 바로 이전에 제작했던 con_mysql.py 파일의 class manage_db 클래스 내부에 있는 def save_table()
함수이다. 일단 이 클래스를 사용하기 위해서는 여느 라이브러리를 사용할 때와 마찬가지로 차트 데이터를 조회하는 함수가 담겨 있는 CpSysDib.py 파일 내부에서 con_mysql.py 파일을 불러와야(import) 한다. 아래의 코드를 확인해보자.
※ Line: 4
## CpSysDib.py ## import win32com.client import pandas as pd import con_mysql
그 후 다시 차트 데이터 조회 함수(def chart_DWM()
)의 끝자락으로 돌아와서 save_gubun
데이터를 기준으로 조건문을 사용하여 차트 데이터를 저장하는 기능을 추가해주면 된다.
※ Line: 71 ~ 76
## CpSysDib.py ## import win32com.client import pandas as pd import con_mysql class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def chart_DWM(self, item_code, request, quantity, save_gubun): if not type(save_gubun) == bool: return False _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, "chart_DWM") self.stockchart.BlockRequest() ## 데이터 요청 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 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) while True: if len(_df['date']) <= quantity: ## 아직 다 회신받지 못한 경우 if self.stockchart.Continue == 1: ## 추가 작업 수행 self.handlers.set_instance(self.stockchart, "chart_DWM") self.stockchart.BlockRequest() ## 데이터 요청 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 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) else: ## 데이터 조회 종료 break else: ## 데이터 조회 종료(모두 회신받음) break chart_data = pd.DataFrame(_df, columns=['date', 'open', 'high', 'low', 'close', 'vol', 'tvol']).iloc[:quantity] if save_gubun: ## 이는 if save_gubun == True: 구문을 요약한 것이다. con_mysql.manage_db()._save_table(db_name='day_data', table_name=f'{item_code}', table_data=chart_data) return chart_data
GUI에서 저장 여부 결정하는 기능 추가하기
GUI에서 저장 여부를 결정할 수 있는 기능은 QCheckBox
를 통해 간편하게 구현 가능하다. 즉, 이 체크박스가 체크되어 있으면 차트 데이터를 저장하고 체크되어 있지 않으면 저장하지 않도록 하는 기능을 구현하면 된다.
- 체크박스 체크시: 저장 여부 값으로 True를 전달하여 차트 데이터 저장
- 체크박스 해제시: 저장 여부 값으로 False를 전달하여 차트 데이터 미저장
일단 GUI 파일을 열어서 우리가 사용하는 프로그램에 체크박스를 하나 추가해주도록 하자.

그렇다면 QCheckBox
객체가 체크되어 있는지 해제되어 있는지를 확인할 수 있는 방법은 무엇이 있을까 ? 바로 isChecked()
함수를 통해 확인할 수 있다. 물론 다양한 함수들이 존재하긴 하지만 우리가 이 체크박스를 통해 결정하고자 하는 내용은 단순하게 차트 데이터를 저장할 것인가 저장하지 않을 것인가이기 때문에, 체크박스가 체크되어 있는가 체크되어 있지 않은가만 확인하면 된다. 이 기능을 Boss.py 파일 내부의 차트 데이터를 조회하는 함수에서 구현해보도록 하자. 체크박스의 체크 여부를 확인하는 구문은 6번째 줄의 코드 한 줄로만으로 확인이 가능하다. 그 외에 17, 20, 23 부분은 체크박스의 체크 여부를 차트 데이터 조회 함수의 파라미터(인자)로 전달해주는 부분만 변경되었다.
※ Line: 6, 17, 20, 23
def _len_chart(self): """개수를 기준으로 틱, 분, 일, 주, 월봉 차트를 조회하는 함수""" item_code = self.lineEdit_4.text() data_len = int(self.lineEdit_5.text()) cur_idx = self.comboBox_2.currentIndex() is_checked = self.checkBox.isChecked() if cur_idx == 0: request = "T" cycle = int(self.lineEdit_6.text()) CpSysDib.StockChart().chart_MT(item_code, request, data_len, cycle) elif cur_idx == 1: request = "m" cycle = int(self.lineEdit_6.text()) CpSysDib.StockChart().chart_MT(item_code, request, data_len, cycle) elif cur_idx == 2: request = "D" CpSysDib.StockChart().chart_DWM(item_code, request, data_len, save_gubun=is_checked) elif cur_idx == 3: request = "W" CpSysDib.StockChart().chart_DWM(item_code, request, data_len, save_gubun=is_checked) elif cur_idx == 4: request = "M" CpSysDib.StockChart().chart_DWM(item_code, request, data_len, save_gubun=is_checked)
그럼 매번 체크해야 차트 데이터를 저장할 수 있어요 ?
그건 아니다. GUI 파일 내부에서 아예 체크되어 있는 상태로 만들어버리면 되기 때문이다. Qt Designer 프로그램에서 체크박스를 클릭한 후, 우측의 속성 편집기 부분을 아래로 쭉 내려보면 checked라는 부분을 확인할 수 있는데, 그 부분을 활성화시킨 후에 저장하고 프로그램을 실행시켜보면 아예 체크박스가 체크되어 있는 상태로 시작된다는 것을 확인할 수 있다.


조회 구분에 따라 각기 다른 데이터베이스에 저장하기
앞서 코드를 구현할 때 빠뜨린 부분이 있다. 바로 일봉, 주봉, 월봉 차트 데이터를 조회할 때 어떠한 데이터를 조회하든지 간에 모두 동일한 데이터베이스('day_data')에 저장하도록 한 부분이다. 이 부분을 일봉이라면 일봉 데이터베이스로, 주봉이라면 주봉 데이터베이스로, 월봉이라면 월봉 데이터베이스로 저장해주어야 한다. 아래의 코드를 확인해보자. (만약 각각 데이터베이스의 이름을 본인과 다른 이름으로 사용하고 있다면 그 이름으로 맞춰주면 된다.)
※ Line: 73 ~ 79
또한 MySQL은 기본적으로 대문자를 지원하지 않는데, 우리가 차트 데이터를 조회할 때 사용하는 종목코드에서는 A라는 대문자를 사용하게 된다. 따라서 이 대문자를 소문자로 바꿔서 저장해줄 것인데, 이 때 사용하는 함수는 upper()
와 lower()
이다. 단어만 봐도 알 수 있겠지만, 대문자를 소문자로 바꿔주는 함수가 lower()
이다. 이제 차트 데이터를 저장하는 함수의 인자인 table_name로 전달해주는 item_code
부분의 뒤에 lower()
메서드를 추가하여 대문자를 소문자로 바꾸어서 차트 데이터를 저장하도록 해보자.
※ Line: 75, 77, 79
## CpSysDib.py ## import win32com.client import pandas as pd import con_mysql class StockChart: def __init__(self): self.stockchart = win32com.client.Dispatch("CpSysDib.StockChart") ## COM 인스턴스 생성 self.handlers = win32com.client.WithEvents(self.stockchart, event_handler_CpSysDib) def chart_DWM(self, item_code, request, quantity, save_gubun): if not type(save_gubun) == bool: return False _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, "chart_DWM") self.stockchart.BlockRequest() ## 데이터 요청 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 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) while True: if len(_df['date']) <= quantity: ## 아직 다 회신받지 못한 경우 if self.stockchart.Continue == 1: ## 추가 작업 수행 self.handlers.set_instance(self.stockchart, "chart_DWM") self.stockchart.BlockRequest() ## 데이터 요청 len_data = self.stockchart.GetHeaderValue(3) ## 데이터 개수 확인 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) else: ## 데이터 조회 종료 break else: ## 데이터 조회 종료(모두 회신받음) break chart_data = pd.DataFrame(_df, columns=['date', 'open', 'high', 'low', 'close', 'vol', 'tvol']).iloc[:quantity] if save_gubun: ## 이는 if save_gubun == True: 구문을 요약한 것이다. if request == "D": ## 일봉 차트 저장 con_mysql.manage_db()._save_table(db_name='day_data', table_name=f'{item_code}', table_data=chart_data) elif request == "W": ## 주봉 차트 저장 con_mysql.manage_db()._save_table(db_name='week_data', table_name=f'{item_code}', table_data=chart_data) elif request == "M": ## 월봉 차트 저장 con_mysql.manage_db()._save_table(db_name='month_data', table_name=f'{item_code}', table_data=chart_data) return chart_data
아직은 데이터베이스가 없으니 test 데이터베이스에 저장해보자.
앞서 구현한 코드 상에서는 'day_data'라는 데이터베이스가 없으므로 차트 데이터를 저장할 수 없는 오류가 발생하니, 이전에 만들어두었던 'test'라는 데이터베이스에 해당 차트 데이터를 저장하도록 코드를 변경(_save_table 함수의 인자인 db_name에 전달해주었던 'day_data'를 'test'로 바꾸어주면 된다.)한 후 프로그램을 실행시켜서 차트 데이터를 조회해보면, 맨 아래의 이미지와 같이 'test' 데이터베이스 내에 'a005930'이라는 테이블 이름으로 하여 차트 데이터가 저장되었음을 확인할 수 있다.
※ Line: 3
if save_gubun: ## 이는 if save_gubun == True: 구문을 요약한 것이다. if request == "D": ## 일봉 차트 저장 con_mysql.manage_db()._save_table(db_name='test', table_name=f'{item_code.lower()}', table_data=chart_data)
▶ 실행 결과 확인하기

소중한 공감 감사합니다