AUTO TRADE/[대신증권] CYBOS PLUS

대신증권 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

 


반응형
728x90

 

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)

▶ 실행 결과 확인하기

일봉 차트 데이터가 정상적으로 저장됨

 

 


728x90
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.