PYTHON/AUTO TRADE SYSTEM

[자동 매매 시스템 구축하기] 주문 정보 관리하기 (5) - self.order_info 변수 tableWidget과 연동하기

  • -

지난 게시글에서   self.order_info  변수가 동작하는 로직과 실제로 어떻게 동작하는지(어느 지점에서 데이터가 입력되고 어느 지점에서 데이터가 삭제되는지 등)에 대해 살펴보았다. 이제부터는 이   self.order_info  데이터를 기반으로 하여 동작시킬 수 있는 여러 가지 기능들을 구현해볼 예정이다.

 

 

tableWidget 구현하기

앞서 실시간으로 등록한 종목들을 대상으로 tableWidget에 데이터를 추가하는 방법을 살펴봤었는데, 여기서 구현할 기능 역시 그 때 당시에 사용했던 방법과 동일한 로직으로 진행할 예정이다. GUI 파일 내에 tableWidget을 생성하고, 그를   self.order_info  변수와 연동시켜 동작하도록 할 것이다. 일단, GUI에 tableWidget부터 생성해보자. tableWidget의 행이 되는 칼럼으로는 종목코드, 종목명, 원주문번호, 주문가격, 주문량, 체결량, 미체결량의 7개이다. 

 

이제 tableWidget의 동작 방식에 대해 고민해볼 필요가 있다. 다시 말해, ①주문 정보가 발생할 때마다 해당 tableWidget에 데이터를 입력하고 삭제하도록 할 것인지, 아니면   self.order_info  변수를 기반으로 하여 Thread를 통해 tableWidget이 일정 간격을 두어 지속적으로 데이터를 갱신하도록 할 것인지가 그 내용이다.

첫 번째 방법의 경우에는 파이썬이 동작하는 코드의 구조에 대해 세부적으로 살펴볼 수 있다는 장점이 있지만 구현하는 과정이 다소 복잡해서 구현하기가 복잡하다는 단점이 있다. 반면 두 번째 방법의 경우에는 구현하기에는 쉽지만 추후 오류가 발생하게 되면 그 오류가 발생한 지점이 어느 지점인지 등에 대해 파악하기가 어렵다는 단점이 있다. 쓰레드(Thread)라는 개념 자체가 오류가 발생하면 그 오류를 찾아내기 힘들고 프로그램이 전체적으로 멈춰버린다는 단점을 갖고 있다.(물론 해결 방법도 있지만 그 개념까지 살펴보기에는 너무 복잡하다.)

하지만 조금만 생각해보면, tableWidget이 어떤 방식으로 동작하도록 할지를 결정하는 것은 어렵지 않다. 결론부터 이야기하자면 전자의 방법이 조금 더 효율적이며, 그 이유에 대해 생각해보자면 주문이라는 것 자체가 엄청나게 많은 빈도로 발생하는 것이 아님에도 불구하고 초 단위로 계속해서 데이터를 갱신해줄 필요가 없기 때문이다. 따라서 우리는 주문이 발생할 때 열을 생성해주고, 해당 열에 데이터를 입력해주는 방식으로 동작하도록 기능을 구현해주면 된다. 

 

 


728x90

 

 

tableWidget에 데이터 입력하기

이전에도 실시간으로 등록되어 있는 종목들의 데이터를 tableWidget에 실시간으로 입력하는 기능을 구현했었는데, 주문 정보 역시 그 때 당시에 구현했던 내용과 동일한 방식으로 구현하면 된다. 짤막하게 설명해보자면, "접수" 데이터가 발생하면 데이터를 입력하고, "체결" 데이터에서 미체결 잔량이 0주가 되면 데이터를 삭제하면 된다. 이 절차는   self.order_info  변수에 데이터를 입력하고 삭제하는 과정과 완전히 동일하다. 

  • setRowCount(인덱스 번호) : 열 추가
  • setItem(row, column, QTableWidgetiItem(데이터)) : 열과 행의 위치에 데이터 입력
  • removeRow(인덱스 번호) : 열 삭제

tableWidget에서 사용되는 여러 메소드들에는 인덱스 번호가 필요한데, 가장 대표적인   setRowCount()  와   removeRow()  같은 경우에는 이전에도 살펴봤듯이 데이터프레임의 인덱스 번호와 완벽하게 일치하기 때문에, 별도의 인덱스 번호 데이터를 얻어오지 않아도 된다는 장점이 있다. 하지만   setItem()  에서 사용하는 행(column)의 번호는 별도로 지정해줘야 한다는 단점이 있다. 이 데이터에 대해서는 이전에 실시간 데이터와 관련된 tableWIdget 객체를 구현할 때   dummy_variable.py  파일 내에서 변수명에 상응하는 번호를 적어서 저장했는데, 이 역시 마찬가지로 사전에 변수를 생성해주고 그 변수를 사용하면 된다.
※ Line : 18~25

"""dummy_variable.py"""

## 실시간 데이터
tableWidget_itemcode = 0
tableWidget_itemname = 1
tableWidget_now = 2
tableWidget_gubun = 3
tableWidget_strength = 4
tableWidget_buy_one = 5
tableWidget_buy_two = 6
tableWidget_sell_one = 7
tableWidget_sell_two = 8
tableWidget_open = 9
tableWidget_high = 10
tableWidget_low = 11
tableWidget_turnover = 12

## 주문 데이터
tableWidget_2_item_code = 0
tableWidget_2_item_name = 1
tableWidget_2_origin_code = 2
tableWidget_2_order_price = 3
tableWidget_2_order_quan = 4
tableWidget_2_trade_quan = 5
tableWidget_2_remained_quan = 6

 

이제 다시   main.py  파일로 돌아와서, 주문 접수 데이터가 발생했을 때   self.order_info  변수 내에 데이터를 입력하는 곳 하단에서 열을 추가하고 tableWidget에도 데이터를 입력하는 코드를 구현해보자. 코드의 전체적인 구조는 실시간 데이터를 입력할 때 사용한 tableWidget과 동일하며, 현재 주문 정보 데이터를 입력할 tableWidget 객체의 이름은   tableWidget_2  이다.
※ Line : 42~49

    def receive_chejandata(self, Gubun, ItemCnt, FidList):
        """
        Gubun(string) - 0:주문체결통보, 1:국내주식잔고통보, 4:파생상품잔고통보
        FidList(string) - 데이터 구분은 ;이다.
        """
        if Gubun == "0":
            accno = self.__getchejandata("9201").strip()        ## 계좌번호
            orderno = self.__getchejandata("9203").strip()      ## 주문번호
            item_code = self.__getchejandata("9001").strip()    ## 종목코드
            order_gubun = self.__getchejandata("912").strip()   ## 주문 구분(JJ:주식주문, FJ:선옵, JG:주식잔고, FG:선옵잔고)
            order_status = self.__getchejandata("913").strip()  ## 주문 상태
            item_name = self.__getchejandata("302").strip()     ## 종목명
            order_quan = self.__getchejandata("900").strip()    ## 주문 수량
            order_pri = self.__getchejandata("901").strip()     ## 주문 가격
            remained_quan = self.__getchejandata("902").strip() ## 미체결량
            signed_vol = self.__getchejandata("903").strip()    ## 체결대금
            origin_ordno = self.__getchejandata("904").strip()  ## 원주문번호
            order_bns = self.__getchejandata("905").strip()     ## 주문 구분(+:매수, -:매도)
            trade_gubun = self.__getchejandata("906").strip()   ## 매수 구분(시장가, 지정가, 보통 등)
            signed_num = self.__getchejandata("909").strip()    ## 체결 번호
            signed_pri = self.__getchejandata("910").strip()    ## 체결가
            signed_quan = self.__getchejandata("911").strip()   ## 체결량
            today_com = self.__getchejandata("938").strip()     ## 당일매매 수수료
            today_tax = self.__getchejandata("939").strip()     ## 당일 매매 세금

            if order_status == "접수":
                idx = len(self.order_info['item_code'].to_list())

                self.order_info.loc[idx, 'item_code'] = item_code
                self.order_info.loc[idx, 'item_name'] = item_name
                self.order_info.loc[idx, 'bns'] = order_bns
                self.order_info.loc[idx, 'origin_code'] = origin_ordno
                self.order_info.loc[idx, 'order_gubun'] = order_gubun
                self.order_info.loc[idx, 'trade_gubun'] = trade_gubun
                self.order_info.loc[idx, 'order_quan'] = order_quan
                self.order_info.loc[idx, 'signed_quan'] = signed_quan
                self.order_info.loc[idx, 'remained_quan'] = remained_quan
                self.order_info.loc[idx, 'order_info'] = ""
                print("self.order_info")
                print(self.order_info)

                self.tableWidget_2.setRowCount(idx)
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_item_code, QTableWidgetItem(str(item_code)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_item_name, QTableWidgetItem(str(item_name)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_origin_code, QTableWidgetItem(str(origin_ordno)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_order_price, QTableWidgetItem(str(order_pri)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_order_quan, QTableWidgetItem(str(order_quan)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_trade_quan, QTableWidgetItem(str(signed_quan)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_remained_quan, QTableWidgetItem(str(remained_quan)))

 

이제   tableWidget_2  객체에 대한 데이터를 모두 입력했으니, 미체결 잔량이 0주가 되면 해당   tableWIdget  객체에서 데이터를 삭제하는 기능을 구현해주면 된다.
※ Line : 58

    def receive_chejandata(self, Gubun, ItemCnt, FidList):
        """
        Gubun(string) - 0:주문체결통보, 1:국내주식잔고통보, 4:파생상품잔고통보
        FidList(string) - 데이터 구분은 ;이다.
        """
        if Gubun == "0":
            accno = self.__getchejandata("9201").strip()        ## 계좌번호
            orderno = self.__getchejandata("9203").strip()      ## 주문번호
            item_code = self.__getchejandata("9001").strip()    ## 종목코드
            order_gubun = self.__getchejandata("912").strip()   ## 주문 구분(JJ:주식주문, FJ:선옵, JG:주식잔고, FG:선옵잔고)
            order_status = self.__getchejandata("913").strip()  ## 주문 상태
            item_name = self.__getchejandata("302").strip()     ## 종목명
            order_quan = self.__getchejandata("900").strip()    ## 주문 수량
            order_pri = self.__getchejandata("901").strip()     ## 주문 가격
            remained_quan = self.__getchejandata("902").strip() ## 미체결량
            signed_vol = self.__getchejandata("903").strip()    ## 체결대금
            origin_ordno = self.__getchejandata("904").strip()  ## 원주문번호
            order_bns = self.__getchejandata("905").strip()     ## 주문 구분(+:매수, -:매도)
            trade_gubun = self.__getchejandata("906").strip()   ## 매수 구분(시장가, 지정가, 보통 등)
            signed_num = self.__getchejandata("909").strip()    ## 체결 번호
            signed_pri = self.__getchejandata("910").strip()    ## 체결가
            signed_quan = self.__getchejandata("911").strip()   ## 체결량
            today_com = self.__getchejandata("938").strip()     ## 당일매매 수수료
            today_tax = self.__getchejandata("939").strip()     ## 당일 매매 세금

            if order_status == "접수":
                idx = len(self.order_info['item_code'].to_list())

                self.order_info.loc[idx, 'item_code'] = item_code
                self.order_info.loc[idx, 'item_name'] = item_name
                self.order_info.loc[idx, 'bns'] = order_bns
                self.order_info.loc[idx, 'origin_code'] = origin_ordno
                self.order_info.loc[idx, 'order_gubun'] = order_gubun
                self.order_info.loc[idx, 'trade_gubun'] = trade_gubun
                self.order_info.loc[idx, 'order_quan'] = order_quan
                self.order_info.loc[idx, 'signed_quan'] = signed_quan
                self.order_info.loc[idx, 'remained_quan'] = remained_quan
                self.order_info.loc[idx, 'order_info'] = ""
                print("self.order_info")
                print(self.order_info)

                self.tableWidget_2.setRowCount(idx)
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_item_code, QTableWidgetItem(str(item_code)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_item_name, QTableWidgetItem(str(item_name)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_origin_code, QTableWidgetItem(str(origin_ordno)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_order_price, QTableWidgetItem(str(order_pri)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_order_quan, QTableWidgetItem(str(order_quan)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_trade_quan, QTableWidgetItem(str(signed_quan)))
                self.tableWidget_2.setItem(idx, dv.tableWidget_2_remained_quan, QTableWidgetItem(str(remained_quan)))


            elif order_status == "체결":

                if remained_quan == "0":
                    idx = self.order_info.index[self.order_info['item_code'] == item_code]
                    self.order_info = self.order_info.drop(index=idx)

                    self.tableWidget_2.removeRow(idx)

 

다음 게시글에서는 정정 주문과 취소 주문이 이루어졌을 때 데이터가 발생하는 로직에 대해 살펴보고, 해당 로직을 기반으로 추후에 구현해야 할 기능으로는 어떠한 것들이 있는지에 대해 살펴볼 예정이다.

 

 


728x90
반응형
Contents

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

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