upbit에서 주는 실시간 데이터를 받는 방법은 아래 글에 기술해 놓았습니다. 여기에 나오는 함수를 사용할 예정입니다.

https://money-expert.tistory.com/32?category=757693 

 

실시간 거래를 위하여 기존 시뮬레이터를 다시 refactoring해봅니다. 프로그램 내용을 최대한 간단하게 만들기 위하여 로그관련된 부분은 과감하게 삭제하고, 매매 로직에서도 클래스 하나로 구현해보았습니다.

 

최대한 간단한 구조로 만들었으며, 로직도 래리윌리암스가 아닌 아래와 같이 아주 간단한 로직으로 개발하였습니다.

 

매매로직 :

매수 :

   기준가 대비 x% 오르면 매수

매도 :

    1) 익절 : 매수가 대비 y% 오르면 매도

    2) 손절 : 매수가 대비 z% 내리면 매도 

 

 

우선 Trader()라는 class를 하나 추가했습니다.

Trader 클래스에서는 실제 매매를 담당합니다.

Trader의 간단한 동작 방식은 아래와 같습니다.

 

 

실시간 거래 정보가 들어온다.

if 매수 대기 중이면 :

   if 현재 가격이 매수 조건이면 :

        매수주문

else 매도 대기 중이면 :

   if 현재 가격이 매도 조건이면 :

        매도주문

        매수 대기중으로 변경

  

코딩할 내용은 단순하지만 이런 저런 조건문이 많이 붙기 때문에 실제 코드는 약간 복잡합니다. 예를들어 매수할 수량을 정하는 부분에서도 암호화폐의 특성상 소숫점 이하까지 주문을 할 수 있습니다. 그렇다고 소숫점 10자리까지 주문을 할 수 없으니 매수할 수량에 따라서 적절한 수량을 계산해야합니다. 아래와 같은 방식으로요. 이런식으로 매매 흐름과는 관계없이 세세하게 코딩하여야 하는 부분이 나오면 생각외로 시간이 많이 걸리게 됩니다.

 

                # 살 수량 결정
                buying_vol = amount / buy_price    # 매수 주수
                if buying_vol > 10 :    #10개 보다 크면 소숫점 0
                    buying_vol = int(buying_vol)
                elif buying_vol > 0 :   #0개 보다 크면 소숫점 2
                    buying_vol = float(format(buying_vol,'.2f'))
                else :                  #0개 적으면 크면 소숫점 4
                    buying_vol = float(format(buying_vol,'.4f'))

 다음은 프로그램을 시작하는 부분입니다.

우선 거래소를 생성합니다. 여기에서는 upbit 거래소를 사용합니다. 이때 본인의 키로 변경하셔야 합니다.

사용할 로직도 생성합니다. 일반적으로 candle 정보를 사용하기 때문에 인자로 넘겨주지만 본 예제에서는 candle은 사용하지 않습니다. 대신에 로직에서 사용할 매수기준, 매도기준, 손절기준을 %를 logic에 설정을 합니다.

 

다음으로는 로직에서 사용할 시작가격을 설정합니다. 일반적으로는 candle 정보를 읽어서 적절한 값을 설정하거나, 아니면 일정 기간 돌리면서 candle 정보를 쌓아나가야 합니다. 이 부분도 일단은 간단하게 입력하도록 하였습니다.

 

이렇게 필요한 class를 만들고 변수 값을 설정한 후 Trader를 생성하면 됩니다. 

    access = 'my acess'  # 본인의 access 키값
    secret = 'my secret' # 본인의 secret 키값
    
    upbit = MyUpbit(access, secret)
    # 상승 따라가기
    tr_logic = TR_FOLLOW_TREND('min', 1) # candle정보는 무시

    ticker = 'KRW-LBC'
    seed = 1000
    buy_perc = 0.03  # 시작가 대비 3% 오르면 매수
    sell_perc = 0.01 # 매수가 대비 10% 오르면 매도(익절)
    losscut = 0.03   # 매수가 대비 losscut 내리면 매도(손절)
    tr_logic.init_set(buy_perc, sell_perc, losscut)

    start_price = 102 # 시작가, 로직에 따라 시초가일 수도 있고, 이전 30분 candle의 open 가격일 수 있음.
    tr_logic.set_start_price(start_price)

    trader = Trader(upbit, ticker, tr_logic, seed)

 

다음에는 websocket를 설정하는 부분입니다. myconnect() 함수에 앞에서 만든 trader와 관련 정보를 설정하면 됩니다. 만약 받은 실시간 정보를 출력하고 싶으면 display_web_socket_recv 값을 1로 하면 됩니다. 체결 정보가 너무 많으면 시스템이 느려질 수 있으므로 정상적으로 동작하는지 확인할 때는 1로 설정하고, 그 후에는 0으로 하는 것을 권합니다. 막상 돌려보니 연결이 끊어지는 경우가 빈번하더군요. 따라서 접속이 끝어지면 잠시 쉬었다가 다시 연결하는 방식으로 코딩을 했습니다.

 

    display_web_socket_recv = 0
    while(1) :
        try :
            asyncio.get_event_loop().run_until_complete(my_connect(trader, ticker, display_web_socket_recv))
        except Exception as x: 
            print('websocket error : ', x)

        time.sleep(10)

 


이제 체결 정보를 실시간으로 받을 수 있습니다. upbit에서 넘겨주는 체결정보에는 당양한 정보가 있는데요. 이걸 제가 필요한 내용만 추려서 dict 형태 정보로 변경을 합니다. 이렇게 하는 이유는 향후 다른 거래소에서 거래를 할 때 거래소 의존적인 부분을 최소화하기 위함입니다. 실거래 데이터가 왔으니 이제는 거래를 한는 trader에게 이 정보를 넘겨줍니다.

 

            data_rev = await websocket.recv()
            my_json = data_rev.decode('utf8').replace("'", '"')
            data = json.loads(my_json)
            if show :
                print(data['code'], data['trade_time'], data['ask_bid'], data['trade_price'], data['trade_volume'])
            if 'type' in  data :
                if data['type'] == 'trade' :
                    info = make_info_from_upbit_real(data)
                    real.do_trading(info)

 

거래를 하는 방식은 앞에서 설명한대로입니다. 매수 조건이 되면 매수, 매수되었다면 매도 조건이면 매도. 간단하죠.

그런데 실제로는 좀더 작업을 해야합니다. 예를들어 매수 주문을 했다고 매수가 된 것이 아닙니다. 시장가 주문을 하면 되겠지만 이런 경우에는 수익률이 떨어질 수 있습니다. 만약 지정가 매수를 하게되면 매수가 되었는지 확인하는 코드를 꼭 추가하여야 합니다. 이번 소스에서는 빠졌지만 실제 매매를 하기 위해서는 꼭 추가하여야 합니다. 

 

    def do_trading(self, info) :
        if self.buying_order_info == {} : # 매수 대기 중
            buy_price = self.tr_logic.is_enter_condition(info) 
            if buy_price > 0 : # 매수조건임
                amount = min(self.balance, self.init_seed)
                # 살 수량 결정
                buying_vol = amount / buy_price    # 매수 주수
                self.do_new_order('buy', buy_price, buying_vol)

        else : # 이미 매수함
            sell_price, losscut = self.tr_logic.is_exit_condition(info)  #
            if sell_price > 0 : # 매도 조건
                # 매도 주문 
                # 매도 가격 : tr_logic에서 결정한 값, 
                # 매도 수량 : 매수 수량
                self.do_new_order('sell', sell_price, self.buying_order_info['org_qty'])

 

로직은 앞에서 설명한 것과 같이 일정 % 오르면 매수하고, 매수한 후 일정 % 이상 오르면 매도, 일정 %이하 내리면 손절입니다. 필요한 초기값들 설정하는 부분도 추가를 해야합니다. 주 부분은 매수조건, 매도저건입니다. 예로 만든 로직은 아주 간단합니다. 매수는 현재가가 기준가 대비하여 올랐으면 현재가로 매수 가격으로 돌려줍니다. 매도는 익절/손절 조건을 만족하면 현재가를 매도 가격으로 돌려줍니다.   

 

class TR_FOLLOW_TREND() 

    def is_enter_condition(self, info) :
        # buy_price는 시작가 * 상승률
        buy_price = self.open * (1+self.buy_percent) 
        if info['close'] >= buy_price :
            self.bought_price = info['close']  # 매수 가격 저장
            return info['close']
        return 0

    # 조건 조사
    # cut : 익절(1) 혹은 손절 (2)
    def is_exit_condition(self, info) :
        sell_price = self.bought_price * (1+self.sell_percent)  # 매수 가격 대비 올랐으면
        if info['close'] >= sell_price :
            self.bought_price = 0
            return info['close'], 1    # 1: 익절

        sell_price = self.bought_price * (1-self.losscut)  # 매수 가격 대비 내렸으면
        if info['close'] <= sell_price :
            self.bought_price = 0
            return info['close'], 2    # 2: 손절

        return 0, 0

 

upbit websocket으로 실시간 체결 데이터를 받아서 정의한 로직에 맞게 매매를 하는 프로그램이 완성되었습니다.

물론 매수/매도 주문 후 실제 체결이 되었는지 확인하는 부분을 추가하여야 완벽한 실전 매매가 됩니다. 

 

이 부분은 좀 복잡하기 때문에 일전에 소개해드린 전략매매 흐름도를 바탕으로 trader class를 변경하면 향후 기능 변경시에 적응하기 좋습니다.

 

money-expert.tistory.com/12

 

다음에는 업비트에서 전종목 시세를 받아서 (open 가격 자동 설정) 돌아가는 방식을 설명하도록 하겠습니다. 

현재 개인적으로 upbit에서 실시간 시세를 받아서 래리윌리엄스 로직을 돌리고 있는데요. 생각만큼 수익이 나오지는 않습니다. 시뮬레이션에서는 수익이 좋았는데, 실제 적용해보니 생각만큼 수익이 안나는군요. 세상에 쉬운 일이 없군요. 

 

관련 코드는 아래 깃허브에 있습니다. pyupbit 수정한 버전도 함께 올려 놓았습니다.

 

github.com/multizone-quant/system-trading-crypto

반응형

설정

트랙백

댓글

전략을 기반으로 자동 매매를 하기 위해서는 특정 코인의 실시간 체결내역을 받아야 하는데요. 거래소에서는 서버의 부하를 줄이기 위하여 REST 방식으로는 단위시간 당 접속 제한이 걸어 두어 원하는 코인의 실시간 시세를 받기 어렵습니다. 이를 위하여 websocket 방식으로 현재가, 실시간 체결가, 호가 정보를 제공합니다. websocket 방식은 REST 방식과는 달리 접속이 한번 이루어지면 체결 데이터를 계속 받을 수 있습니다. test한 바로는 동시에 받을 수 있는 코인의 수도 제한이 없는 것 같습니다.

 

websocket을 이용하여 실시간 체결가를 얻는 방법에 대하여 기술합니다.

 

우선 아래 두 패키지를 import합니다. 


import websockets
import asyncio

 

만약 처음 사용하는 경우라면 pip 명령어를 사용하여 설치하시기 바랍니다.

pip install websockets

pip install asyncio

 

socket을 이용한 네트워크 프로그램 경험이 없더라도 간단하게 구현이 가능합니다.

 

우선 connect() 함수를 이용하여 upbit의 websocket 서버에 접속합니다.

 

websockets.connect('wss://api.upbit.com/websocket/v1')

 

접속이 성공하였다면 시세를 받을 코인명을 전달합니다. 이때 ticket은 임의의 문자열 사용하시면 되고, code에 시세를 받고 싶은 코인명을 기술하면 됩니다.

 

ss = '[{"ticket":"test1243563456"},{"type":"trade","codes":["KRW-BTC", "KRW-ETH"]}]'

websocket.send(ss)

 

send가 성공적으로 이루지면 등록한 코인에 대하여 체결이 이루어지면 체결 정보가 전달됩니다. 

data_rev = websocket.recv()

 

recv() 함수로 받은 데이터를 json 형태로 변환하면 아래와 같은 정보 형태를 가지게 됩니다.

 

 

프로그램 동작 중에 coin을 추가하거나 삭제하면 바로 반영이 됩니다. 

 

이것을 모두 모아서 만든 sample 프로그램입니다. 우선 KRW-BTC만 시세만 받다가 5개를 받았으면 KRW-ETH를 추가하여 BTC, ETH를 받다가 받은 시세 수량이 20개를 넘으면 마지막 코인(ETH)을 지우고 TRX를 추가하여 받는 방식으로 동작합니다.

 

 

소스코드는 아래 github에 있습니다. 

github.com/multizone-quant/upbit/blob/main/upbit-websocket-ex

 

multizone-quant/upbit

python codes for upbit. Contribute to multizone-quant/upbit development by creating an account on GitHub.

github.com

 

반응형

설정

트랙백

댓글