본문 바로가기
AutoBot

업비트 자동매매 - Upbit Auto Trading No.9 (Trading Code 1)

by zemba 2022. 2. 2.
반응형
SMALL

안녕하세요~ Zemba입니다~ 😁
지난번 포스팅에서는 전체적인 구조와 설정을 Load 하여 사용하는 구조를 작성하였습니다.
이 구조를 구성하지 않으신분은 지난 포스팅을 참고하시길 바랍니다 (참고 : https://zemba.tistory.com/37)

 

Upbit Auto Trading No.8 (설정파일 로드 및 구조 작성)

안녕하세요~ Zemba입니다~ 지난번 포스팅에서는 텔레그램을 세팅하고 코드로 메시지를 보내는 방법에 대해서 알아보았습니다. 그렇기 때문에 이번 포스팅에서는 본격적으로 설정 파일을 읽고 값

zemba.tistory.com

우선 지난 포스팅에 대한 부분이 완료되었다는 가정하에 이후 내용을 진행하도록 하겠습니다. 이번에 다룰 내용은 지난번에 예고하였지만 실제로 이제 거래를 위한 코드를 작성할 예정입니다. 지난번에 작업한 과정을 다시 한번 살펴보겠습니다.

  • 작업 과정
    1. get_member_list 함수를 통해 member_list.json 파일을 읽는다.
    2. member_list.json의 파일 내용을 json data로 변환한다.
    3. 변환된 json data 중에 user의 내용을 가져와서 user 목록을 얻는다.
    4. user 목록을 loop 돌면서 user별 config를 불러와서 텔레그램 객체를 생성하고 메시지를 보낼 수 있는 구조를 작성한다.

네 위와 같이 작업을 완료하였습니다.
4번 작업에서 이제 user별로 loop를 돌면서 user 별 config를 확인하여 (혼자 사용하면 1개만 사용하면 됩니다!) 거래를 하는 코드를 작성해보려고 합니다. 그럼 여기서 추가로 필요한 항목을 정리해 보겠습니다. 우선은 업비트를 이용하여 거래를 해야 하기 때문에 업비트 관련 API를 사용해야 하겠죠? ( 그렇기 때문에 우리가 API Key를 발급받은 것이기도 하고요 ㅎㅎ ) 업비트 API를 사용하여 거래 전략을 적용해볼 것입니다. 다시 정리를 해보면 ( 정리 왕인가.......? ㅎㅎㅎ )

  • 작업 목록
    • 업비트 API
      • 계좌조회 / 현재 보유 티커 정보 조회 / 티커 매수 / 티커 매도 / 티커 현재가 조회 / OHLCV 조회
    • 매매 전략
      • RSI1428 거래 코드
      • RSI 차트 그리기

이 정도로 정리해볼 수 있을 것 같습니다. 우선 업비트의 API에 대한 부분부터 간단히 설명을 하겠습니다. 저도 처음에는 별로 리서치를 하지 않고 단순히 업비트에서 제공해주는 API URL을 사용하여 구축을 하려고 했는데 조금 찾아보다 보니까 pyupbit라는 잘 구현되어있는 Lib가 이미 존재하고 있었습니다. 이 Lib를 사용하지 않는 경우에는 직접 API를 Rest로 Call 해서 결과를 받아 처리해야 하는데 예외처리와 코드 응답에 대한 부분들은 구현하여 처리하기에는 시간이 너무 많이 소요될 것 같아서 이미 구현되어 있는 Lib를 사용할까 합니다. 다음과 같이 설치하여 사용하면 됩니다.

pip install pyupbit

개발 IDE에 따라서 조금 차이가 날 수 있기 때문에 그런 방법들에 대해서는 잘 찾아서 확인해 보면 될 것 같습니다. 그럼 우선 upbit API에 대한 코드부터 설명드리겠습니다. 제가 반복해서 설명드리지만 ( ㅋㅋ 화난 거 아닙니다..ㅎ ) 지인들도 같이 자동매매를 돌려주기 위해서 작성하는 것이다 보니 ㅎㅎ access_key와 secrect_key에 대한 부분이 교체가 필요합니다. 우선 코드를 먼저 보시면......

# -*- coding: utf-8 -*-
import pyupbit


class Upbit:
    __upbit = None

    def __init__(self, access_key, secret_key):
        self.__upbit = pyupbit.Upbit(access=access_key, secret=secret_key)

    def get_tickers(self, type):
        if type is None:
            return pyupbit.get_tickers()
        else:
            return pyupbit.get_tickers(fiat=type)

    def get_current_price(self, ticker):
        return pyupbit.get_current_price(ticker=ticker)

    def get_balances(self):
        return self.__upbit.get_balances()

    def get_balances_krw(self):
        return self.__upbit.get_balance("KRW")

    def get_balances_ticker(self, ticker):
        return self.__upbit.get_balance(ticker)

    def get_orderbook(self, ticker):
        return pyupbit.get_orderbook(ticker=ticker)

    def get_ohlcv(self, ticker, interval):
        return pyupbit.get_ohlcv(ticker=ticker, interval=interval)

    def get_ohlcv_count(self, ticker, interval, count):
        return pyupbit.get_ohlcv(ticker=ticker, interval=interval, count=count)

    def sell_limit_order(self, ticker, price, count):
        return self.__upbit.sell_limit_order(ticker=ticker, price=price, volume=count)

    def sell_market_order(self, ticker, count):
        return self.__upbit.sell_market_order(ticker=ticker, volume=count)

    def buy_limit_order(self, ticker, price, count):
        return self.__upbit.buy_limit_order(ticker=ticker, price=price, volume=count)

    def buy_market_order(self, ticker, price):
        return self.__upbit.buy_market_order(ticker=ticker, price=price)

    def order_cancel(self, uuid):
        return self.__upbit.cancel_order(uuid=uuid)

    def get_order(self, ticker, status):
        return self.__upbit.get_order(ticker_or_uuid=ticker, state=status)

마찬가지로 Upbit라는 Class로 생성하도록 코드를 작성하였고 생성자 부분에서 access_key와 secrect_key를 필수로 받도록 구현하였습니다. user별로 생성하여 처리해야 하기 때문에 이 부분은 파라미터로 전달받도록 구현하였습니다. 나머지 API들은 제가 직접 호출해보고 어떤 식으로 구축을 하면 사용하기 편할까?라고 이것저것 조회해본 다음에 기능을 함수명을 나눠서 좀 정리를 해보았습니다. 사실 여기에 작성한 코드 중에 사용하지 않는 함수도 있지만 우선은 나중에라도 사용할 일이 있을지도 모르는 기능들이 있을까 봐 추가를 해두었습니다.

여기서 잠깐 확인하고 가야 할 것이 있습니다. 제공되는 API를 보면 limit_order와 market_order 가 두 가지로 구분되어있습니다. 대충 눈치를 채신 분들도 계시겠지만 매수/매도를 할 때 지정가로 거래할 것인지 시장가로 거래할 것인지에 대해서 요청하는 API입니다. 그렇기 때문에 지정가로 요청을 할 경우에 채결이 되지 않을 수 있기 때문에 cancel_order라는 함수가 별도로 있다고 보시면 됩니다. 하지만 저희는 처음에 전략을 구성할 때 시장가로 구매하기로 결정하였기 때문에 지정가 거래는 잊으셔도 됩니다. 지정가를 통해서 자동매매를 구현하려면 매우 복잡한 조건식을 구현해야 하기 때문에 시장가로 거래하기로 했습니다. ( 백테스팅 매수매도 기준도 시장가 거래로 진행하였습니다. )


자 그럼 거래를 위한 코드를 우선 작성해보겠습니다.

# Trade Start
ticker_msg = []
ticker_msg.append(f"============================\n")
ticker_msg.append(f"Start ----- {now()} -----\n")
ticker_msg.append(f"============================\n")
upbit = Upbit(access_key, secret_key)
ticker_map = get_my_ticker_map(upbit)
# 구매기준 조회

for ticker in target_ticker:
    ticker_msg.append(f"# {ticker} process start !\n")
    rsi1428_trader(upbit=upbit, ticker=ticker, ticker_map=ticker_map, period='minute60',
                   minimum_buy_price=minimum_buy_price, ticker_msg=ticker_msg)
    ticker_msg.append(f"# {ticker} process End !\n")

time.sleep(1)  # 재갱신 시간 확인하려고 Delay 추가
ticker_map = get_my_ticker_map(upbit)

ticker_msg.append("========== SUMMARY ==========\n")
for ticker in target_ticker:
    info = ticker_map.get(ticker)
    current_price = upbit.get_current_price(ticker)
    rate = round(((float(current_price) / float(info['avg_price'])) * 100) - 100, 3)

    ticker_msg.append(f"#### {ticker} ####\n")
    ticker_msg.append(f"- AVG Price : {info['avg_price']}, Count : {info['count']} \n")
    ticker_msg.append(f"- Count : {info['count']} \n")
    ticker_msg.append(f"- Rate : {rate}\n")

ticker_msg.append(f"============================\n")
ticker_msg.append(f"End ----- {now()}-----\n")
ticker_msg.append(f"============================\n")
telegramBot.send(chat_id, ''.join(ticker_msg))

뭔가.... 코드가 복잡해 보일 수 있지만 그렇게 생각하지 않으셔도 됩니다!!! 대부분의 코드는 텔레그램 메시지를 위한 메세지관련 코드이고 정작 중요한 코드는 몇라인 되지 않습니다. ㅎㅎ 개발자 분들이시라면 이미 하시겠지만 ticker_msg라는 변수에 append되고 있는 메세지들은 결국 텔레그램에 메세지를 발송하기 위한 변수입니다. 결론부터 말씀드리면 거래의 과정을 배열로 저장하여 최종적으로 텔레그램에 전달하여 자동매매가 진행되고 있음을 알리기 위함입니다. ( 봇이 살아있는지도 확인하려면 꾸준히 알람을 받아야 합니다 ㅎㅎ )

이 코드의 위치는 지난번 포스팅했던 반복문 안에 자동매매 구현 부분이란 곳에 해당 코드를 위치하면 됩니다. 그럼 하나씩 살펴보겠습니다.
우선 user의 config를 가져와서 access_key와 secrect_key를 통해서 Upbit의 객체를 생성해주고 upbit api를 사용할 수 있도록 코드를 설정해줍니다. 그리고 get_my_ticker_map()이라는 함수를 통해 Map형태의 데이터를 가지고 옵니다. (Map형태의 데이터란 Key:Value 구조의 데이터로 Key를 알면 그에 해당하는 정보를 가져올 수 있는 컬랙션 종류 중 한 개입니다.)

def get_my_ticker_map(upbit):
    ticker_map = {}
    for ticker in upbit.get_balances():
        ticker_name = ticker['unit_currency'] + "-" + ticker['currency']
        ticker_map.update(
            {ticker_name: {"avg_price": ticker['avg_buy_price'], "count": ticker['balance']}})
    return ticker_map
    

...
...
...
...

upbit = Upbit(access_key, secret_key)
ticker_map = get_my_ticker_map(upbit)

Python에서 Map형태를 구성하는 것은 매우 간편했습니다. 보시는 것과 같이 map의 변수를 지정하고 update함수를 통해 내용을 만들어주면 간단히 사용이 가능했습니다. 아차.... 이것을 사용하는 이유를 설명하지 않았군요.....ㅎㅎㅎ;;;; 🤣
현재 보유하고 있는 나의 티커 정보를 조회해서 티커 별로 평균가와 보유 수량을 확인하여 사용하려고 하기 위함입니다. 별건 아니지만 거래를 위해 사용되는 값이기도 하고 나중에 수익률을 표기하기 위함도 있습니다 ㅎ

for ticker in target_ticker:
    ticker_msg.append(f"# {ticker} process start !\n")
    rsi1428_trader(upbit=upbit, ticker=ticker, ticker_map=ticker_map, period='minute60',
                   minimum_buy_price=minimum_buy_price, ticker_msg=ticker_msg)
    ticker_msg.append(f"# {ticker} process End !\n")

여기가 가장 핵심이 되는 부분입니다. 이제 진짜 거래를 위한 코드라고 보시면 될 것 같습니다. 
target_ticker의 변수를 loop 돌면서 rsi1428_trader라는 함수를 호출하는 코드입니다.
누가 봐도 목표 티 커를 rsi1428이라는 전략으로 거래를 하겠다는 코드로 보이지 않나요? ㅎㅎㅎ 네 맞습니다. 이 함수 안에서 실제로 거래가 발생하는 것이라고 보시면 됩니다. target_ticker는 지난번 포스팅에서 보셨듯이 config에 등록된 user별 거래할 ticker의 목록을 가져온 변수입니다. 그리고 rsi1428_trader에 전달해주는 파라미터로는 upbit 객체, ticker정보, ticker_map, 분봉 기준, 최고 거래금액, 거래 메시지 변수 이렇게 같이 전달해 줍니다. 거래메세지 변수 같은 경우는 내부적으로 거래가 어떤 상황에서 거래가 되었는지를 출력하기 위해 변수를 전달해준 것이고 upbit객체와 ticker 등의 정보를 전달한 것은 rsi를 적용하여 실제 매수/매도를 진행하여야 하기 때문에 upbit api를 사용하기 때문에 객체를 전달하였습니다. ticker_map을 전달한 이유는 저희가 전략 중에 일부는 평균가 이하일 경우에는 추가 매수한다 라는 기준이 있었기 때문에 현재 가지고 있는 계좌의 평균가를 확인하여 현재가와 비교하기 위해서 전달해 줍니다.

우선..... 이번 포스팅은 여기서 마무리하고 다음 포스팅은 rsi1428_trader내부의 내용에 대해서 포스팅을 하겠습니다. 내용을 작성하다 보니 코드가 많이 들어가서 그런가 글이 길어졌네요 ㅎㅎㅎㅎ 그래서 한번 나눠서 작성하는 것이 좋을 것 같습니다. 다음 포스팅에서는 저 Loop내부에서 rsi1428_trader를 통해서 거래를 하게 한 이유도 같이 설명드리도록 하겠습니다.

저는 전문 투자자도 아니고 그냥 일개 개미에 불과하면 그냥 개발자이기 때문에 전문적인 지식이 없어 실제 투자의 결정은 본인에게 있습니다.

반응형
LIST

댓글