대신증권 CYBOS PLUS 프로그램 구현 (9) - 일봉 차트 조회 : 오류 개선
프로그램 구현 목표
- 오류 내용 확인하기
- 오류 개선하기
- 일봉 차트 데이터프레임화하기
오류 내용 확인하기
지난 두 개의 게시글에서 일봉 차트를 조회할 때 일자 범위를 대상으로 조회하는 함수와 조회하고자 하는 데이터 개수를 기준으로 조회하는 함수를 구현하고 두 함수 모두를 GUI와 연결하여 차트 데이터를 조회할 수 있도록 하는 기능을 모두 구현했다. 하지만 차트 데이터를 조회하는 순서에 있어서, def _day_range()
함수를 먼저 사용하게 되면 이전에 데이터를 조회할 때 입력해두었던 일자 범위 데이터가 남아서 def _len_chart()
함수에도 영향을 미친다는 오류가 확인됐다. 아래의 데이터를 살펴보자.
▶ 개수로 조회한 후에 일자 범위로 조회한 경우의 결과 데이터
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_len_chart
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:5
[20240830] 시:74400, 고:75000, 저:74100, 종:74300, 거래량:16358520, 거래대금:1217838000000
[20240829] 시:73600, 고:74700, 저:73500, 종:74000, 거래량:16884479, 거래대금:1250517000000
[20240828] 시:75800, 고:76400, 저:75400, 종:76400, 거래량:9794514, 거래대금:743267000000
[20240827] 시:75700, 고:76500, 저:75600, 종:75800, 거래량:11130145, 거래대금:845521000000
[20240826] 시:78100, 고:78200, 저:76000, 종:76100, 거래량:15655938, 거래대금:1200212000000
self.object:_day_range
Raised OnReceived Event(_day_range)
필드 개수:9(('날짜', '시가', '고가', '저가', '종가', '전일대비', '거래량', '거래대금', '대비부호')), 데이터 개수:62
[20230331] 시:64000, 고:64000, 저:63700, 종:64000, 전일대비:50800, 거래량:14094479, 거래대금:900711000000
[20230330] 시:63700, 고:63700, 저:63100, 종:63200, 전일대비:50500, 거래량:15684377, 거래대금:993903000000
( 중략 )
[20230103] 시:55400, 고:56000, 저:54500, 종:55400, 전일대비:53-100, 거래량:13547030, 거래대금:747898000000
[20230102] 시:55500, 고:56100, 저:55200, 종:55500, 전일대비:50200, 거래량:10031448, 거래대금:558433000000
▶ 일자 범위로 조회한 후에 개수로 조회한 경우의 결과 데이터
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_day_range
Raised OnReceived Event(_day_range)
필드 개수:9(('날짜', '시가', '고가', '저가', '종가', '전일대비', '거래량', '거래대금', '대비부호')), 데이터 개수:62
[20230331] 시:64000, 고:64000, 저:63700, 종:64000, 전일대비:50800, 거래량:14094479, 거래대금:900711000000
[20230330] 시:63700, 고:63700, 저:63100, 종:63200, 전일대비:50500, 거래량:15684377, 거래대금:993903000000
( 중략 )
[20230103] 시:55400, 고:56000, 저:54500, 종:55400, 전일대비:53-100, 거래량:13547030, 거래대금:747898000000
[20230102] 시:55500, 고:56100, 저:55200, 종:55500, 전일대비:50200, 거래량:10031448, 거래대금:558433000000
self.object:_len_chart
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:5
[20230331] 시:64000, 고:64000, 저:63700, 종:64000, 거래량:14094479, 거래대금:900711000000
[20230330] 시:63700, 고:63700, 저:63100, 종:63200, 거래량:15684377, 거래대금:993903000000
[20230329] 시:62500, 고:62700, 저:62200, 종:62700, 거래량:11216008, 거래대금:700455000000
[20230328] 시:62400, 고:62900, 저:62100, 종:62900, 거래량:11614118, 거래대금:726295000000
[20230327] 시:62600, 고:62800, 저:62000, 종:62100, 거래량:11039331, 거래대금:687170000000
파악한 오류의 원인은 바로 Boss.py 파일 내부에서 self.stockchart = CpSysDib.StockChart
와 같이 하나의 인스턴스로 생성하는 과정이 잘못되었기 때문이다. 우리가 여태까지 구현한 코드의 구조를 보면 기본적으로 COM의 이름을 따서 만든 CpSysDib.py 파일 내에서 모듈의 이름을 따서 만든 class StockChart
내부의 초기화 함수에서 self.stockchart = CpSysDib.StockChart
라는 인스턴스를 생성하여 데이터를 조회하고 있는데, 문제는 Boss.py 파일 내부에서 동일한 코드를 또 제작하여 사용하고 있다는 것이다.
따라서 Boss.py 파일 내부에서 다른 파일에서 class
내부의 초기화 함수를 통해 인스턴스로 생성해두었던 COM을 사용할 때에는 별도의 인스턴스(self.stockchart = CpSysDib.StockChart
)를 생성할 게 아니라 Boss.py 파일 내에서 차트 데이터를 조회하는 함수(def _day_range()
또는 def _len_chart()
) 내부에서 CpSysDib.StockChart._day_range()
와 같이 해당 파일의 클래스를 직접적으로 호출하여 사용해야 한다는 것이다.
오류 개선하기
일단 크게 개선해야 할 부분은 없고, Boss.py 파일 내부의 def _len_chart(self):
함수를 먼저 살펴보도록 하자.
※ Line: 19
self.stockchart._len_chart(item_code, request, data_len) # 수정 전 CpSysDib.StockChart()._len_chart(item_code, request, data_len) # 수정 후 def _len_chart(self): item_code = self.lineEdit_4.text() data_len = 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()._len_chart(item_code, request, data_len)
다음으로 수정할 함수는 def _day_range(self):
함수이다. 아래의 코드를 확인해보자.
※ Line: 8
self.stockchart._day_range(item_code, start_date, end_date) # 수정 전 CpSysDib.StockChart()._day_range(item_code, start_date, end_date) # 수정 후 def _day_range(self): item_code = self.lineEdit.text() start_date = self.lineEdit_2.text() end_date = self.lineEdit_3.text() CpSysDib.StockChart()._day_range(item_code, start_date, end_date)
이제 프로그램을 다시 실행시켜서 동일하게 조회한 후, 결과 데이터를 확인해보도록 하자. 결론부터 말하자면, 이제부터는 차트 데이터가 정상적으로 조회될 것이다.

▶ 실행 결과 확인하기 ① 일자 범위로 먼저 조회한 후 개수로 조회
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_day_range
Raised OnReceived Event(_day_range)
필드 개수:9(('날짜', '시가', '고가', '저가', '종가', '전일대비', '거래량', '거래대금', '대비부호')), 데이터 개수:62
[20230331] 시:64000, 고:64000, 저:63700, 종:64000, 전일대비:50800, 거래량:14094479, 거래대금:900711000000
[20230330] 시:63700, 고:63700, 저:63100, 종:63200, 전일대비:50500, 거래량:15684377, 거래대금:993903000000
( 중략 )
[20230103] 시:55400, 고:56000, 저:54500, 종:55400, 전일대비:53-100, 거래량:13547030, 거래대금:747898000000
[20230102] 시:55500, 고:56100, 저:55200, 종:55500, 전일대비:50200, 거래량:10031448, 거래대금:558433000000
self.object:_len_chart
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:5
[20240830] 시:74400, 고:75000, 저:74100, 종:74300, 거래량:16358520, 거래대금:1217838000000
[20240829] 시:73600, 고:74700, 저:73500, 종:74000, 거래량:16884479, 거래대금:1250517000000
[20240828] 시:75800, 고:76400, 저:75400, 종:76400, 거래량:9794514, 거래대금:743267000000
[20240827] 시:75700, 고:76500, 저:75600, 종:75800, 거래량:11130145, 거래대금:845521000000
[20240826] 시:78100, 고:78200, 저:76000, 종:76100, 거래량:15655938, 거래대금:1200212000000
▶ 실행 결과 확인하기 ② 개수로 먼저 조회한 후 일자 범위로 조회
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_len_chart
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:5
[20240830] 시:74400, 고:75000, 저:74100, 종:74300, 거래량:16358520, 거래대금:1217838000000
[20240829] 시:73600, 고:74700, 저:73500, 종:74000, 거래량:16884479, 거래대금:1250517000000
[20240828] 시:75800, 고:76400, 저:75400, 종:76400, 거래량:9794514, 거래대금:743267000000
[20240827] 시:75700, 고:76500, 저:75600, 종:75800, 거래량:11130145, 거래대금:845521000000
[20240826] 시:78100, 고:78200, 저:76000, 종:76100, 거래량:15655938, 거래대금:1200212000000
self.object:_day_range
Raised OnReceived Event(_day_range)
필드 개수:9(('날짜', '시가', '고가', '저가', '종가', '전일대비', '거래량', '거래대금', '대비부호')), 데이터 개수:62
[20230331] 시:64000, 고:64000, 저:63700, 종:64000, 전일대비:50800, 거래량:14094479, 거래대금:900711000000
[20230330] 시:63700, 고:63700, 저:63100, 종:63200, 전일대비:50500, 거래량:15684377, 거래대금:993903000000
( 중략 )
[20230103] 시:55400, 고:56000, 저:54500, 종:55400, 전일대비:53100, 거래량:13547030, 거래대금:747898000000
[20230102] 시:55500, 고:56100, 저:55200, 종:55500, 전일대비:50200, 거래량:10031448, 거래대금:558433000000
일봉 차트 데이터프레임화하기
데이터프레임(DataFrame
)이란 pandas
모듈의 함수 중 하나로, 우리가 추후에 데이터를 저장할 수 있는 MySQL에 저장하거나 불러올 때 정말 유용하게 사용할 수 있는 함수이다. 단순히 보기 좋은 데이터 형태로 만들어준다는 기능에 국한되지 않고, 다양한 가짓수로 활용할 수 있기 때문에 이 데이터프레임은 정확하게 알고 있으면 좋다.
※ Line: 3
## CpSysDib.py ## import win32com.client import pandas as pd
데이터프레임(DataFrame
)은 기본적으로 dictionary
형태의 데이터를 모아서 하나의 표처럼 보여주는 데이터타입인데, 우리는 차트 데이터를 조회한 후에 서버로부터 회신받은 데이터들을 dictionary
에 입력하도록 한 후 그 변수를 DataFrame
으로 만들어주면 된다. 그렇다면 서버로부터 회신받은 데이터는 어떻게 입력해주어야 할까 ? 아래의 예제를 살펴보도록 하자. 데이터프레임으로 만들기 위한 데이터는 { }
로 감싸져 있고, 그 안에서 칼럼명에 입력될 데이터들은 리스트 형태를 의미하는 [ ]
로 감싸져 있다는 것 정도만 이해하면 된다.
import pandas as pd temporary = {'date':['20200101', '20210101'], 'item_code':['A005930', 'A000020']} df = pd.DataFrame(temporary, columns=['date', 'item_code']) print(temporary) ## temporary 변수 확인 {'date': ['20200101', '20210101'], 'item_code': ['A005930', 'A000020']} print(df) ## df 변수 확인 date item_code 0 20200101 A005930 1 20210101 A000020
그렇다면 이제 다시 본론으로 돌아와서, def _len_chart
함수를 사용하여 데이터를 조회할 경우 def OnReceived(self):
함수로 결과값이 전달되므로 그 함수 내에서 서버로부터 회신받은 데이터들을 모으고 모아서 데이터프레임으로 만들어보자. 가장 먼저 앞서 살펴봤듯이 temporary
변수처럼 데이터가 입력되어 있는 변수를 만들어주어야 하는데, 그러기 위해서는 일단 비어 있는 변수를 생성해주어야만 한다. 비어 있는 변수를 토대로 그 외의 데이터들을 입력해주면 되기 때문이다.
※ Line: 3
## CpSysDib.py ## elif self.object == "_len_chart": temporary = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} print("Raised OnReceived Event(_len_chart)") len_fild = self.disp.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.disp.GetHeaderValue(2) ## 팔드 배열 len = self.disp.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len}") for index in range(len): date = self.disp.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.disp.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.disp.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.disp.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.disp.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.disp.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.disp.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 print(f"[{date}] 시:{open}, 고:{high}, 저:{low}, 종:{close}, 거래량:{vol}, 거래대금:{tvol}")
그 다음으로는 이제 for
문 내부에서 데이터를 조회한 후의 데이터들을 모두 temporary
변수에 입력해주면 되는데, 이 때 사용하는 메서드는 append
이다. 즉, temporary
변수에 있는 date
(KEY)에 open
(데이터, VALUE)를 넣어주기 위해서는 아래와 같은 코드를 작성하면 된다.
## 단순 예제 temporary['date'].append(open)
이제 어떠한 방식으로 데이터를 입력해야 하는지 확인했다면, elif self.object == "_len_chart":
부분을 아래와 같이 수정하자. 그 후에 프로그램을 실행시켜서 일봉 차트 데이터를 5개만 조회해보도록 하자. 그 후에 print(temporary)
부분의 코드가 출력해준 결과값을 확인해보면, 앞전에 살펴봤던 temporary
변수와 같은 형태의 모습이 나온다는 걸 볼 수 있다.
※ Line: 19 ~ 27
## CpSysDib.py elif self.object == "_len_chart": temporary = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} print("Raised OnReceived Event(_len_chart)") len_fild = self.disp.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.disp.GetHeaderValue(2) ## 팔드 배열 len = self.disp.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len}") for index in range(len): date = self.disp.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.disp.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.disp.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.disp.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.disp.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.disp.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.disp.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 print(f"[{date}] 시:{open}, 고:{high}, 저:{low}, 종:{close}, 거래량:{vol}, 거래대금:{tvol}") temporary['date'].append(date) temporary['open'].append(open) temporary['high'].append(high) temporary['low'].append(low) temporary['close'].append(close) temporary['vol'].append(vol) temporary['tvol'].append(tvol) print(temporary)
▶ 실행 결과 확인하기
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_len_chart
Raised OnReceived Event(_len_chart)
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:5
{'date': [20240904, 20240903, 20240902, 20240830, 20240829], 'open': [69800, 74100, 74500, 74400, 73600], 'high': [71100, 74300, 74700, 75000, 74700], 'low': [69800, 72500, 73500, 74100, 73500], 'close': [70000, 72500, 74400, 74300, 74000], 'vol': [27366563, 16314599, 12641376, 16358520, 16884479], 'tvol': [1923751000000, 1195017000000, 938130000000, 1217838000000, 1250517000000]}
이제 마지막으로, pandas
모듈의 DataFrame
을 활용하여 위의 데이터를 데이터프레임으로 만들어보자.
※ Line: 29, 30
## CpSysDib.py elif self.object == "_len_chart": temporary = {'date':[], 'open':[], 'high':[], 'low':[], 'close':[], 'vol':[], 'tvol':[]} print("Raised OnReceived Event(_len_chart)") len_fild = self.disp.GetHeaderValue(1) ## 요청한 필드 개수 array_fild = self.disp.GetHeaderValue(2) ## 팔드 배열 len = self.disp.GetHeaderValue(3) ## 데이터 개수 확인 print(f"필드 개수:{len_fild}({array_fild}), 데이터 개수:{len}") for index in range(len): date = self.disp.GetDataValue(0, index) ## 0: 날짜, index: 인덱스 번호 open = self.disp.GetDataValue(1, index) ## 2: 시가, index: 인덱스 번호 high = self.disp.GetDataValue(2, index) ## 3: 고가, index: 인덱스 번호 low = self.disp.GetDataValue(3, index) ## 4: 저가, index: 인덱스 번호 close = self.disp.GetDataValue(4, index) ## 5: 종가, index: 인덱스 번호 vol = self.disp.GetDataValue(5, index) ## 8: 거래량, index: 인덱스 번호 tvol = self.disp.GetDataValue(6, index) ## 9: 거래대금, index: 인덱스 번호 print(f"[{date}] 시:{open}, 고:{high}, 저:{low}, 종:{close}, 거래량:{vol}, 거래대금:{tvol}") temporary['date'].append(date) temporary['open'].append(open) temporary['high'].append(high) temporary['low'].append(low) temporary['close'].append(close) temporary['vol'].append(vol) temporary['tvol'].append(tvol) print(temporary) df = pd.DataFrame(temporary, columns=['date', 'open', 'high', 'low', 'close', 'vol', 'tvol']) print(df)
▶ 실행 결과 확인하기
self.object:CpCybos
[통신결과:1] 서버와의 연결에 성공했습니다.
로그인되어 있습니다.
self.object:_len_chart
Raised OnReceived Event(_len_chart)
필드 개수:7(('날짜', '시가', '고가', '저가', '종가', '거래량', '거래대금')), 데이터 개수:5
{'date': [20240904, 20240903, 20240902, 20240830, 20240829], 'open': [69800, 74100, 74500, 74400, 73600], 'high': [71100, 74300, 74700, 75000, 74700], 'low': [69800, 72500, 73500, 74100, 73500], 'close': [70000, 72500, 74400, 74300, 74000], 'vol': [27366563, 16314599, 12641376, 16358520, 16884479], 'tvol': [1923751000000, 1195017000000, 938130000000, 1217838000000, 1250517000000]}
date open high low close vol tvol
0 20240904 69800 71100 69800 70000 27366563 1923751000000
1 20240903 74100 74300 72500 72500 16314599 1195017000000
2 20240902 74500 74700 73500 74400 12641376 938130000000
3 20240830 74400 75000 74100 74300 16358520 1217838000000
4 20240829 73600 74700 73500 74000 16884479 1250517000000
소중한 공감 감사합니다