대신증권 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
'AUTO TRADE > [대신증권] CYBOS PLUS' 카테고리의 다른 글
대신증권 CYBOS PLUS 프로그램 구현 (11) - 일봉 차트 조회 : 연속 조회 사용하기 ② (1) | 2024.09.06 |
---|---|
대신증권 CYBOS PLUS 프로그램 구현 (10) - 일봉 차트 조회 : 연속 조회 사용하기 ① (0) | 2024.09.02 |
대신증권 CYBOS PLUS 프로그램 구현 (8) - 일봉 차트 조회 : 데이터 개수 (0) | 2024.09.01 |
대신증권 CYBOS PLUS 프로그램 구현 (7) - 일봉 차트 조회 : 일자 범위 (0) | 2024.08.27 |
대신증권 CYBOS PLUS 프로그램 구현 (6) - 전종목 종목코드 조회하기 ② (0) | 2024.08.26 |
소중한 공감 감사합니다