PYTHON/AUTO TRADE SYSTEM

[자동 매매 시스템 구축하기] 실시간 데이터 얻기 (6) - tableWidget 사용하기 ②

  • -

지난 게시글에서 tableWidget의 행 이름에 해당하는 행의 번호를 dummy_variable.py라는 파일 내에 하나하나 수작업으로 생성해주었다. 따라서 이번 게시글에서는 실시간 데이터를 수신하는 시점에서 발생하는 실시간 데이터들을 모두 tableWidget에 입력하도록 하는 코드를 구축할 계획이다. 코드를 작성할 함수는 당연히 실시간 데이터가 발생하는 함수인   def receive_realdata  함수 하단이다.

 

 

잠깐!

지난 게시글에서 tableWidget을 사용하기 위해서는   self.tableWidget.setItem(row_num, column_num, DATA)  과 같은 형태로 사용해야 된다고 했는데,   row_num  데이터를 얻어오는 과정이 먼저 우선시되어야 한다. 여기서 이야기하는   row_num  이란 다시 이야기하지만 데이터프레임(DataFrame)의 인덱스 번호와 같은 역할이다. 다시 말해,   row_num  을 얻어오기 위해서는 tableWidget 내에 별도의 열을 추가해줘야 한다는 것이다.

여기서 열을 추가한다는 것은 곧 데이터를 입력할 열을 새롭게 만들어준다는 의미가 되는데, 그렇다면 열을 추가해줘야 하는 지점은 어디가 되어야 할까? 바로 실시간 등록이 이루어지는 시점이다. 우리는 예전 구축하는 게시글에서 실시간 등록이 이루어진 후에   self.setrealreg  변수를 출력하도록 한 지점이 있는데, 해당 지점에서 tableWidget 내에 종목코드를 추가해주어야 한다.

그렇다면 tableWidget의 열을 추가하는 방법은 무엇이 있을까? 바로   setRowCount()  메서드이다.   setRowCount()  는 tableWidget 내에 있는 열의 개수를 지정해주는 번호이다. 다시 말해 실시간으로 등록되어 있는 종목의 개수만큼 tableWidget의 열 개수를 늘려주어야 한다는 것이다. 다만, 우리는 실시간으로 등록된 종목의 개수를 어렵지 않게 얻어올 수 있다. 바로 이전에 제작했던   self.setrealreg  변수의 개수를 활용하는 것이다.

다만 tableWidget의 열 개념은 다소 혼동할 수 있는 여지가 많은 개념이므로, 아래의 도표를 바탕으로 조금이나마 이해해보도록 하자. 만약 아래의 행 1, 열 1을 지칭하는 (0, 0)자리에 데이터를 입력하고 싶다면   self.tableWIdget.setItem(0, 0, ITEM)  과 같이 0을 입력해야겠지만, 열 1을 생성하기 위해서는   setRowCount()  안에는 1을 입력해야 된다는 것이다. 이 개념이 다소 어려울 수도 있겠지만, 이전 게시글들에서 언급했던 DataFrame 내에 1개의 데이터가 있더라도 해당 데이터의 인덱스 번호는 0인 것과 같다고 생각하면 이해하기가 한결 수월해질 것 같다.

  행 1(num:0) 행 2(num:1) 행 3(num:2) 행 4(num:3) 행 5(num:4) 행 6(num:5) 행 7(num:6)
열 1(num:0) (0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6)
열 2(num:1) (1, 0) (1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (1, 6)
열 3(num:2) (2, 0) (2, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6)

 

 

그럼 어떻게 만들라는 거야?

앞서 설명했듯이,   def __setrealreg(self, item_code)  함수 내에서 실시간 등록이 이루어지기 전에 다음과 같은 두 가지 작업을 수행한다는 것이다. 1번 작업은 기존에 제작했던 작업이고, 2번 작업이 새롭게 추가해줘야 하는 작업이다.

  1. def add_to_setrealreg()를 통해 self.setrealreg 변수에 종목코드를 등록한다
  2. self.tableWidget.setRowCount()를 통해 tableWidget의 열 개수를 설정해준다.

그렇다면 우리는   setRowCount()  안에 어떤 값을 입력해줘야 현재 실시간으로 등록되어 있는 종목의 개수에 알맞게 tableWidget을 만들어낼 수 있을까? 바로 실시간으로 등록되어 있는 종목들의 정보가 담긴   self.setrealreg  변수를 활용하면 된다. 만약 5개의 데이터가 입력되어 있다면   len(self.setrealreg)  은 5일 것이고, 100개의 데이터가 있더라도   len(self.setrealreg)  는 100의 값을 갖기 때문이다. 이를 이용해서 우리는   setRowCount()  안에 5 또는 100이라는 값을 전달해줌으로써 필요한 만큼의 열을 tableWidget에 생성할 수 있다.
※ Line : 4, 8

    def __setrealreg(self, item_code):
        if len(self.setrealreg['item_code']) == 0:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 0])
        else:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 1])

 

실시간 등록을 누른 횟수에 맞게 생겨난 tableWidget 내 열의 개수.

 

 

뭔가 허전하다?

그렇다.   self.setrealreg  변수 내에는 '005930', '066570', '000020'이라는 세 개의 종목이 모두 입력됐지만 막상 tableWidget 내에는 아무런 값도 입력되지 않았다. 왜냐하면 값을 입력하지 않았기 때문이다. 이제 우리는   setItem()   메서드를 활용해서 데이터를 입력해줄 것이다. 위치는 방금   setRowCount()  의 바로 하단부가 된다.

데이터를 입력하는 코드를 작성하기 전에 앞서, 위의 도표에서 살펴봤듯이 첫 번째 열의 번호는 0이었다. 약간 데이터프레임의 인덱스(index)와 같은 개념으로 이해하라고 설명했었다. 다시 말해, 현재 세 개의 열이 추가됐으나 첫 번째 열의 열 번호는 0이고 두 번째 열의 열 번호는 1이며 세 번째 열의 열 번호는 2이다. 뭔가 생각나지 않는가? 바로 DataFrame 형태의 자료형인   self.setrealreg  변수 내에 있는 종목코드 별 인덱스(index) 번호와 동일한 것이다.

다시 말해, 데이터프레임(DataFrame) 자료형에 있어서, 새롭게 데이터를 입력하고자 할 때에는 현재 데이터의 개수(  len()  메서드)를 얻어서 그 자리에 그대로 데이터를 입력했다. tableWidget 역시 그와 마찬가지로, 원래의 데이터프레임 형태인   self.setrealreg  변수의 데이터 개수(  len()  메서드)를 얻어서 그 값을 기반으로 열의 개수를 지정(  setRowCount()  메서드)해주는 것이다.

따라서 우리는 해당 종목이   self.setrealreg   변수 내에서 갖고 있는 인덱스 번호를 가져다가 전달해주면 된다. 데이터프레임 내에서 원하는 값을 얻고자 할 경우에 사용하는 방법은 아래와 같은 형태이다.

  • value = DataFrame.column[conditions] 

우리가 얻고자 하는 값은 index이고, 조건은   self.setrealreg  ['item_code']칼럼에 입력돼있는 값이 우리가 실시간으로 등록하는 종목과 같은지이다. 몇 가지 예제를 통해 학습해보자.

## self.setrealreg(원 데이터)
  item_code
0    005930
1    066570
2    000020

## 종목코드가 '005930'인 데이터의 index 번호
result = self.setrealreg.index[self.setrealreg['item_code'] == '005930']
>>> 0


## 종목코드가 '066570'인 데이터의 index 번호
result = self.setrealreg.index[self.setrealreg['item_code'] == '066570']
>>> 1

 

그렇다면 우리가 열의 번호로 전달해줘야 하는 값이 데이터프레임 내 해당 종목의 인덱스(index) 값이라는 것을 확인했으니,   row_num  이라는 이름아래 해당 종목의 index 번호를 입력할 수 있을 것이다.
※ Line : 5, 10

    def __setrealreg(self, item_code):
        if len(self.setrealreg['item_code']) == 0:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code]
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 0])
        else:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code]
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 1])

 

 


728x90

 

 

행 번호도 필요하다.

여기까지 열 번호를 얻는 절차였다면, 이제는 우리가 입력하고자 하는 행 번호를 얻어와야 한다. 잠깐. 행 번호는 우리가 만들어두지 않았었나? 개꿀이다. 해당 파일을 사용하기 위해   import  를 통해 야무지게 사용해보도록 하자. 

import valuable.dummy_variable as dv

          """중략"""

    def __setrealreg(self, item_code):
        if len(self.setrealreg['item_code']) == 0:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code]
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 0])
        else:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code]
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 1])

 

이제 dummy_variable을 as dv로 불러왔으니, 우리가 사전에 입력했던 변수들은 모두 dv.~~~과 같은 형태로 사용하게 된다. 그렇다면 현재 우리가 입력하고자 하는 데이터의 행 번호는 0이긴 하지만, 사전에 입력해뒀으니   dv.tableWidget_itemcode  를 불러다가 행 번호로 전달해주면 되는 것이다. (만약 추후 행의 위치가 변경되는 경우에는, dummy_variable.py 파일 내에서   tableWidget_itemcode  에 입력돼있는 값을 0이 아닌 해당하는 값으로 변경해주면 된다.)

특정 위치에 데이터를 추가하는 방법은 아래와 같은 구조를 갖고 있다.

  • tableWidget.setItem(row_num, column_num, QTableWidget(DATA))

위에서 변경할 수 있는 데이터는 row_num, column_num, DATA의 세 개이다. 어떤 경우든지 간에 QTableWidget()은 반드시 사용해줘야 한다. (핑크색으로 표시해둔 세 개만 변경 가능하다.)

그렇다면 우리의 tableWidget에 데이터를 입력할 때에는 어떻게 작성하면 될까?
※ Line : 10, 16

import valuable.dummy_variable as dv

          """중략"""

    def __setrealreg(self, item_code):
        if len(self.setrealreg['item_code']) == 0:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code]
            self.tableWidget.setItem(row_num, dv.tableWidget_itemcode, QTableWidgetItem(item_code))
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 0])
        else:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code]
            self.tableWidget.setItem(row_num, dv.tableWidget_itemcode, QTableWidgetItem(item_code))
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 1])

 

 

이제 코드를 실행해서 실시간 등록을 눌러보면, 아래와 같은   TypeError:  오류가 발생한다.

Traceback (most recent call last):
  File "/main.py", line 208, in _pushbtn_3
    self.__setrealreg(item_code)
  File "/main.py", line 217, in __setrealreg
    self.tableWidget.setItem(row_num, dv.tableWidget_itemcode, QTableWidgetItem(str(item_code)))
TypeError: setItem(self, int, int, QTableWidgetItem): argument 1 has unexpected type 'Int64Index'

 

그 내용을 보면 Int64Index 형태의 자료형이 전달되었으며, 해당 자료형은 올바르지 않아 tableWidget.setItem()을 실행시킬 수 없다는 오류이다. 즉, TypeError: 뒤의 내용을 보면   setItem(self, int, int, QTableWidgetItem):  과 같이 나와 있는데, int형으로 전달해줘야 하는 데이터가 Int64Index 형태로 전달되었다는 것이다. 이는 .item() 메서드를 통해 해결할 수 있다.

위의 코드 중   row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code]  부분에서 발생하는 오류로, index 값으로 전달받은 0이 int가 아닌 Int64Index 형태로 전달되었기 떄문이다. 이에 대해 아예   item()  , 즉 지정한 데이터의 데이터 형태를 제외한 데이터 그 자체의 값만 가져오라는 메서드를 추가해주면 된다.
※ Line : 10, 15

import valuable.dummy_variable as dv

          """중략"""

    def __setrealreg(self, item_code):
        if len(self.setrealreg['item_code']) == 0:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code].item()
            self.tableWidget.setItem(row_num, dv.tableWidget_itemcode, QTableWidgetItem(item_code))
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 0])
        else:
            self.add_to_setrealreg(item_code)
            self.tableWidget.setRowCount(len(self.setrealreg['item_code']))
            row_num = self.setrealreg.index[self.setrealreg['item_code'] == item_code].item()
            self.tableWidget.setItem(row_num, dv.tableWidget_itemcode, QTableWidgetItem(item_code))
            return self.kiwoom.dynamicCall("SetRealReg(QString, QString, QString, QString)", ["0101", item_code, '10', 1])

 

이제 코드를 실행시켜서 실시간 등록을 실행시켜보면 아래의 사진과 같이 정상적으로 동작한다는 것을 확인할 수 있다.

 

 

다음 게시글에서는 실시간 데이터가 발생했을 때 해당 데이터가 tableWidget 내에 입력되도록 하는 코드를 구축해보도록 하자.

 

 


728x90
반응형
Contents

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

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