PYTHON/AUTO TRADE SYSTEM

[자동 매매 시스템 구축하기] 매수 매도 함수 구현하기 (3) - 매수 매도 함수 발생 시 디테일한 데이터 처리 구현하기 ②

  • -

지난 게시글에서는 SendOrder 메서드를 통해 주문을 접수하는 함수를 제작했고, 이번 게시글에서는 해당 함수를 활용해서 매수 매도 함수가 정상적으로 동작하도록 하기 위한 함수를 구현할 예정이다.

 

 

def algo1_buy_1():을 제작해보자.

지난 게시글에서 작성했던 부분에 이어,   def algo1_buy_1()  함수 내에   self._send_order()  함수를 추가해주도록 하자.

"""알고리즘 매수함수1"""
def algo1_buy_1():
    print("알고리즘1의 첫 번째 매수함수를 실행합니다.")
    self._send_order():

 

우리는 지난 게시글에서   def _send_order()  함수를 제작하면서   SendOrder  메서드는 상당히 많은 파라미터를 요구한다는 점을 확인할 수 있었는데, 매수 함수에서 그 데이터들을 하나하나씩 확인하고 입력해서 전달해주어야 한다. 많이 번거로워보이지만, 의외로 간단하다. 왜냐하면 우리는 큰 틀에서는   def receive_realdata()  라는 실시간 데이터를 기반으로 작업하고 있기 때문이다. 다시 말해,   SendOrder  메서드는 서버를 통해 해당 함수로 전달되는 데이터 내에서 모두 감당이 가능한 수의 파라미터를 요구하고 있다.

아래의 도표를 확인해보자. 우리가   SendOrder  로 전달해야 하는 데이터와 서버로부터 전달받는 실시간 데이터 간에는 다음과 같은 관계가 성립하고 있다. 

앗, 생각보다 아무런 관계가 없어 보이는가? 제대로 보셨다.

우리가 실시간 데이터로부터 수신받은 데이터 중에   SendOrder  함수로 전달해줄 수 있는 데이터는   jongmokcode  딱 하나만 있다. 왜일까? 사실 실시간 데이터는 해당 종목의 주가 데이터만을 실시간으로 전달해주고 있을 뿐, 계좌번호나 실시간 데이터가 발생한 종목을 얼마에 몇 주를 매수하라는 등의 데이터를 제공해주지는 않는다. 하지만 걱정할 필요는 없다. 우리는 모든 데이터를 제공해줄 수 있다. (그래도 하나는 전달받았으니, 그거로 족하자. Line 11에서 code 대신에 jongmokcode를 입력해주면 된다.)

"""알고리즘 매수함수1"""
def algo1_buy_1():
    print("알고리즘1의 첫 번째 매수함수를 실행합니다.")
    rqname = ""
    accno = ""
    ordertype = ""
    qty = ""
    price = ""
    hogagb = ""
    orgorderno = ""
    self._send_order(rqname, accno, ordertype, jongmokcode, qty, price, hogagb, orgorderno):

 

위의 코드를 보면  Line 4부터 10까지에 해당 함수에서 전달해줘야 하는 변수들을 모두 입력해두었다. 그렇다면  변수들을 하나씩 살펴보고 어떤 방식으로 구현해야되는지 살펴보도록 하자.

첫째로   rqname  변수는 사실 아무거나 입력해도 되긴 하는데, 본인의 경우에는 해당 거래구분(hogagb)과 매치되는 데이터를 전달해준다. 예를 들어, hogagb이 03인 경우에는 시장가매수를 의미하는데, 이 때에는 rqname으로 "시장가매수"를 입력한다는 것이다.

다음으로   accno  변수는 주문을 접수할 계좌번호를 입력하는 것인데, 사실 이 변수는 사전에 미리 입력해두는 것도 좋은 방법이다. 예를 들어 본인이 여러 개의 알고리즘을 사용하고 있고 각 알고리즘 별로 거래 성과를 확실히 파악하기 위해 각기 다른 계좌를 활용해서 거래를 진행하고 싶다고 한다면 알고리즘 1은 A계좌의 계좌번호로만 주문이 접수되도록 하고 알고리즘 2는 B계좌의 계좌번호로만 주문이 접수되도록 하면 된다. 일단 프로그램 구축 초반기이고 하니, 계좌번호를 별도로 입력해두는 것은 추후로 미루고 여기서는 로그인 시점에 얻어오는 계좌번호를 활용하도록 하자.

  ordertype  은 말그대로 주문유형인데, 매수함수의 경우에는 "1"이라는 값만을 사용하게 된다. ordertype이 "1"인 경우에는 신규매수를 의미하며 "2"인 경우에는 신규매도를 의미한다. 매수함수에서는 "1"만을 사용하는 것처럼, 매도함수 내에서는 동일한 자리에 "2"를 전달해주면 된다.

그리고 qty와 price에 대해 살펴보기 전에 앞서, hogagb을 먼저 알아보자.   hogagb  은 거래구분에 해당하는 것인데, 이전 게시글에서도 살펴봤듯이(def _send_order 함수 내에 메모도 해뒀다.) 시장가("03"), 최유리지정가("06") 등등과 같이 price 값을 공란으로 전달해야 하는 경우들이 있었다. 그렇기 때문에 price 변수는 hogagb이 어떤 값인가에 따라 공란으로 둘지 아니면 가격을 전달할 것인지를 결정해서 입력하면 된다.

 마지막으로   qty  의 경우에는 주문을 접수하고자 하는 수량으로, 본인의 경우에는 알고리즘마다 한 종목당 매수를 진행할 한도 금액을 설정해둔 후에 해당 금액 내에서 def receive_realdata를 통해 전달받은 현재가 데이터(  now  )를 기반으로 주문 수량을 구한 후 해당 수량만큼 주문을 진행하도록 하고 있다. 예를 들어, 알고리즘 1의 경우에는 한 종목달 최대 100만원을 매수하도록 설정해두었다고 할 때 주가가 1,000원인 종목은 최대 1,000주(1,000,000 나누기 1,000)를, 주가가 10,000원인 종목은 최대 100주(1,000,000 나누기 10,000)를 주문하도록 하는 것이다.

 

 


728x90

 

 

시장가매수 주문 함수를 구현해보자.

기본적으로 매수 조건이 충족되었을 때, 본인의 경우에는 시장가로 매수 주문을 접수하는 편이다.(물론 시장가매수를 진행한다면 반드시 호가의 두꼐를 고려하긴 해야겠지만, 여기서는 논외로 하자. 호가의 두께를 고려하지 않은 시장가매수는 오히려 독이 될 수 있다.) 이제   rqname  의 경우에는 앞서 이야기했듯 "시장가매수"를 전달하고, 매수 주문을 접수하는 것이므로 주문유형(  ordertype  )은 1(신규매수)을 전달하며 시장가로 접수하는 것이니 주문가격(  price  )은 공란("")으로, 거래구분(  hogagb  )은 시장가를 뜻하는 "03"을 전달하면 된다. 

여기서,   self._send_order  함수의 맨 마지막 인자인 원주문번호(  orgorderno  )는  주문 취소와 정정 주문을 접수할 때 필요한 것이기 때문에 신규매수 또는 신규매도를 진행할 때에는 공란("")을 전달해주면 된다.
※ Line 11

"""알고리즘 매수함수1"""
def algo1_buy_1():
    print("알고리즘1의 첫 번째 매수함수를 실행합니다.")
    # rqname = "" # 미사용
    accno = ""
    # ordertype = "" # 미사용
    qty = ""
    price = ""  # 공란으로 전달
    # hogagb = "" # 미사용
    # orgorderno = "" # 미사용
    self._send_order("시장가매수", accno, "1", jongmokcode, qty, price, "03", ""):

 

그럼 결국 위와 같이   accno  와   qty  만 남게 되는데, 먼저   qty  는 앞서 이야기했던 것처럼 종목별 매수 금액을 기반으로 하여 수량을 계산하면 된다. 예를 들어 종목별 매수 금액을 1000000(백만원, 1,000,000)으로 설정했다고 가정했을 때 현재가 데이터(  now  )를 기반으로 자동적으로 수량을 계산하도록 하는 코드는 아래와 같을 것이다.

volume_per_algo = 1000000	      ## 종목당 백만원 (사전에 설정했다고 가정)
qty = int(volume_per_algo / now)  ## 주문 수량

여기서   now  의 데이터형은 숫자형(  int  )이고 종목당 매수 금액인   volume_per_algo  의 데이터형도 숫자형(  int  )이나,   qty  를 계산하는 과정에서 또 한 번 숫자형으로 데이터를 변환시켜주어야 한다. 왜냐하면 종목당 백만원을 매수하기로 했으나 현재 주가가 3,000원인 경우에는 3,333.3333주가 나오기 때문이다. 이를   int()  를 씌워서   qty  값을 3,333주로 만들어야 정상적인 주문 접수가 이루어진다. 이러한 내용을 반영한 현재까지의   def algo1_buy_1():  함수는 다음과 같다.
※ Line 4, 8

"""알고리즘 매수함수1"""
def algo1_buy_1():
    print("알고리즘1의 첫 번째 매수함수를 실행합니다.")
    volume_per_algo = 1000000
    # rqname = "" # 미사용
    accno = ""
    # ordertype = "" # 미사용
    qty = int(volume_per_algo / now)
    price = ""  # 공란으로 전달
    # hogagb = "" # 미사용
    # orgorderno = "" # 미사용
    self._send_order("시장가매수", accno, "1", jongmokcode, qty, price, "03", ""):

 

 

계좌번호(accno)를 입력하는 두 가지 방법

먼저 본인의 경우에는 기본적으로 계좌번호를 사전에 별도의 변수로 입력해두었다가 필요할 때마다 호출해서 사용하는 방법을 사용하고 있다. 예를 들면 변동성이 있을 수 있는 변수들만을 입력하기 위한 py 파일(편의상 파일 이름을   valuable.py  로 했다고 하자.)을 새롭게 생성해서, 알고리즘 1의 계좌번호는   algorithm1_acc_no = "0000000000"  , 알고리즘 4의 계좌번호는   algorithm4_acc_no = "0000000000"  과 같은 형태로 미리 입력해둔 후에 아래와 같이 작성하는 것이다.
※ Line 6

"""알고리즘 매수함수1"""
def algo1_buy_1():
    print("알고리즘1의 첫 번째 매수함수를 실행합니다.")
    volume_per_algo = 1000000
    # rqname = "" # 미사용
    accno = valuable.algorithm1_acc_no
    # ordertype = "" # 미사용
    qty = int(volume_per_algo / now)
    price = ""  # 공란으로 전달
    # hogagb = "" # 미사용
    # orgorderno = "" # 미사용
    self._send_order("시장가매수", accno, "1", jongmokcode, qty, price, "03", ""):

 

다음으로는 로그인 함수가 진행되었을 때,   self.accno  와 같은 임의의 변수를 활용해서 계좌번호를 입력하도록 한 후에, 주문 과정에서 그 변수를 그대로 활용하는 방법이다. 예를 들어, 계좌번호를 저장하기 위한 변수의 이름이   self.accno라고 했을 경우 아래와 같은 방식으로 데이터를 처리할 수 있을 것이다.
※ Line 1~4, 16

def login_info(self):
    accno = self.kiwoom.dynamicCall("GetLogininfo(QString)", ["ACCNO"])
    all_acc = str(accno.rstrip(';')).split(";")
    self.accno = all_acc
    
    
    
def receive_realdata(생략):
    """생략"""
    
    """알고리즘 매수함수1"""
    def algo1_buy_1():
        print("알고리즘1의 첫 번째 매수함수를 실행합니다.")
        volume_per_algo = 1000000
        # rqname = "" # 미사용
        accno = self.accno
        # ordertype = "" # 미사용
        qty = int(volume_per_algo / now)
        price = ""  # 공란으로 전달
        # hogagb = "" # 미사용
        # orgorderno = "" # 미사용
        self._send_order("시장가매수", accno, "1", jongmokcode, qty, price, "03", ""):

 

키움증권 Open API는   GetLogininfo  메서드를 통해 계좌 리스트를 전달해주고 있는데, 여기서 모든 계좌번호를 ;라는 문자열을 기준으로 분류해서 전달해준다. 예를 들어 "0000000000"과 "0000000001", "0000000002"이라는 세 개의 계좌번호가 있다고 했을 때 Line 2에 위치한   accno  에는 "0000000000;0000000001;00000000002"와 같이 하나의 문자열로 전달된다. 따라서 이 데이터를   split(";")  을 통해 계좌번호를 세 개로 나누어서   all_acc  와   self.accno  라는 변수 안에 그대로 입력하는 것이다.

그렇게 되면   self.accno  라는 변수 안에는 ['0000000000', '0000000001', '0000000002']와 같은 형태로 데이터가 입력되며 첫 번째 계좌번호를 알고리즘 1이라는 알고리즘의 계좌번호로 활용하고 싶을 경우, 매수함수1 내에서의   accno  는 다음과 같이 리스트 형태의 계좌번호에서 인덱스 번호를 제공함으로써 계좌번호를 호출하여 사용해야 한다.

accno = self.accno[0]

다만 이 방법의 경우에는 어떤 계좌번호가 어떤 알고리즘을 담당할 계좌번호인지 알기 어렵다는 점, 직접 지정해주는 것이 아니기 때문에 중간에 계좌번호의 변동 또는 계좌 추가 운용 시 서버로부터 전달받는 계좌번호의 순서 변동 등과 같은 상황이 발생했을 때 오류가 발생할 수 있다는 점 때문에 추천하지는 않는다.

이제 다음 게시글에서는 매도 함수를 구현해보도록 하자.

 

 


728x90
반응형
Contents

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

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