[TradingBot] Upbit Trend Following Bot


개요

백테스트한 추세 추종 전략을 실행하는 트레이딩 봇을 만듭니다.

트레이딩 전략을 만들고 실제 거래하는 것은 좋습니다. Backtest에서 드러나지 않았던 문제들을 찾아낼 수 있기 때문입니다. 그러나, 현생이 있기 때문에 실시간으로 사람이 직접 하기는 제약이 많습니다. Bot을 만들어 자동으로 매매하도록 하여 사람이 관여할 일을 최대한 줄여봅니다.

필요한 파이썬 라이브러리를 가져옵니다.

import pyupbit
import pandas as pd
from datetime import datetime
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import math
import time
import calendar
import telegram
import json
plt.rcParams["figure.figsize"] = (10, 6) # (w, h)

Upbit 거래소 API 접속 관련 정보와 Telegram Bot의 token 및 ID를 가져옵니다. AWS에서 Trading Bot이 동작하면서 Upbit 거래소 API로 가격 데이터를 받아오고, 거래에 필요한 기준선들을 계산한 후 매수, 매도, 손절 조건이 만족하면 API로 주문을 내고 Telegram으로 관련 로그를 보내주는 구조입니다.

with open('UpbitTrendFollow_Bot.txt', 'r') as f:
    lines = f.readlines()
    access = lines[0].strip()
    secret = lines[1].strip()
    token = lines[2].strip()
    chat_id = lines[3].strip()

Upbit API와 Telegram Bot을 활성화합니다.

upbit = pyupbit.Upbit(access, secret)
bot = telegram.Bot(token=token)

Telegram 메시지로 날짜와 거래 로그를 보내줄 것이고, Bot이 작동하다가 오류가 발생하면 오류 발생 메시지도 보낼 수 있습니다.

def telegramlog(message):
    # 함수로 받은 문자열을 파이썬에서 출력 + 텔레그램 메시지로 전송
    print(datetime.now().strftime('[%y/%m/%d %H:%M:%S]'), message)
    strbuf = datetime.now().strftime('[%y/%m/%d %H:%M:%S] ') + message
    
    # Use your telegram chat_id
    bot.sendMessage(chat_id = chat_id, text = strbuf)

def printlog(message, *args):
    # 함수로 받은 문자열을 파이썬에서 출력
    print(datetime.now().strftime('[%y/%m/%d %H:%M:%S]'), message, *args)

4시간봉 20개를 기준으로 거래 기준이 될 값들을 계산합니다. 암호화폐 티커 (KRW-BTC, KRW-ETH 등)를 받아서 그 암호화폐에 대한 계산을 합니다.

# 원본
# 4시간봉, 최근 num_candle봉
def get_candle_high_low_range(crypto):
    df = pyupbit.get_ohlcv(crypto, interval = 'minute240', to=datetime.now()).tail(20)
    candle_high = max(df['high'])
    candle_low = min(df['low'])
    candle_high80 = candle_low + 0.8 * (candle_high - candle_low)
    candle_low20 = candle_low + 0.2 * (candle_high - candle_low)
    
    return candle_high, candle_low, candle_high80, candle_low20

4시간봉 20개 동안의 고가와 저가 범위 상단 80%를 현재가가 돌파할 때 매수합니다. 이미 보유 중이면 추가 매수는 하지 않습니다. 암호화폐 티커를 받아서 현재가와 계좌 내 보유 수량을 가져옵니다. 원화 마켓에서만 할 것이므로 계좌의 원화 예수금을 가져오고, 거래 수량을 계산하기 위해 매도 호가 중 가장 낮은 호가를 가져옵니다.

매수 금액은 5개 암호화폐를 대상으로 하여 예수금의 1/5에 가깝게 배정하되, 거래 수수료 및 슬리피지 때문에 매수가 안 되는 것을 방지하기 위해 매수 때마다 현재 예수금의 1%씩은 남기도록 설정했습니다. 첫 매수 시 19%, 두 번째 매수 시 19.44%, 세 번째 매수 시 19.9%, 네 번째 매수 시 20.41%, 마지막 매수 시 21% 정도 매수 주문이 들어갈 것입니다. 주문은 시장가로 주문합니다. 비교적 긴 4시간봉이기 때문에 약간 비싸게 사는 것이 큰 문제가 되지 않고, 지정가 주문 시 가끔 매수 체결이 안 되는 것도 보완됩니다.

Telegram 메시지로 이전에 계산해 둔 거래 수량을 가지고 로그를 보냅니다. 저는 주문이 나갈 때 메시지로 받고 싶어서 미리 매도 호가를 가져와서 시장가 주문 시 수량을 가상으로 계산하고 그것을 전송하도록 했습니다. 그 이유는 매수 주문이 나간 후에 잔고 조회를 했을 때 아직 체결이 안 된 상태면 잔고로 잡히지 않아서 메시지로 보낼 수 없기 때문입니다. 매도 호가를 가져오는 시점과 실제 매수 주문이 나가는 시점 사이에 호가창이 변하면 계산된 수량과 실제 매수된 수량에 약간의 차이가 생길 수 있으나, 거래 금액이나 Bot 작동에는 전혀 문제가 되지 않습니다.

def buy_crypto(crypto):
    current_price = pyupbit.get_current_price(crypto)
    unit = upbit.get_balance(ticker=crypto)
    # 매수 신호: 4시간봉 20개 동안 고가-저가 범위의 상단 80%보다 현재가가 높음. 이미 보유 중이면 추가 매수 안 함
    # (보유 개수 0일 때만 매수)
    if current_price > get_candle_high_low_range(crypto)[2] and str(unit)=='0':
        # 본인 계좌 예수금
        krw = upbit.get_balance(ticker="KRW")
        # crypto currency의 매도 호가 중 가장 낮은 호가
        orderbook = pyupbit.get_orderbook(tickers=crypto)[0]['orderbook_units'][0]['ask_price']
        # BTC, ETH, BCH, EOS, XRP
        amount = krw / (6 - len(upbit.get_balances())) - 0.01 * krw
        unit = amount / orderbook
        # 시장가 매수: 매수는 돈 얼마 넣는지로 나옴
        upbit.buy_market_order(crypto, amount)
        telegramlog("BUY ORDER SUBMITTED: "+str(unit)+" "+str(crypto))

매도 주문은 4시간봉 20개 동안의 고가와 저가 범위 하단 20%를 현재가가 돌파해서 내려갈 때 들어갑니다. 지금 가지고 있는 암호화폐에 대해서만 작동을 할 것이고, 시장가로 거래합니다.

def sell_crypto(crypto):
    current_price = pyupbit.get_current_price(crypto)
    unit = upbit.get_balance(ticker=crypto)
    # 매도 신호: 4시간봉 20개 동안 고가-저가 범위의 하단 20%보다 현재가가 낮음
    if current_price < get_candle_high_low_range(crypto)[3] and str(unit) != '0':
        # 시장가 매도: 매도는 몇 개 파는지로 나옴
        upbit.sell_market_order(crypto, unit)
        telegramlog("SELL ORDER SUBMITTED "+str(unit)+" "+str(crypto))

손절 주문입니다. 트레이딩에서는 한 번의 거래에서 총 투자금의 2% 이상을 잃지 마라는 자금 관리 방식이 있습니다. 암호화폐 5개를 가지고 하고, 대략 1/5 비중으로 들어가기 때문에 10%까지 허용됩니다. 계산한 가격과의 슬리피지를 고려하여 매수가 대비 -9%에 손절 주문을 설정합니다.

def stoploss_crypto(crypto):
    current_price = pyupbit.get_current_price(crypto)
    unit = upbit.get_balance(ticker=crypto)
    # 손절 신호: 매수가보다 10% 하락 시 손절 (매매 1회 당 총 투자금의 2% 손실까지 허용). 슬리피지 감안하여 9%에 손절선 설정
    if current_price < 0.91 * upbit.get_avg_buy_price(ticker=crypto) and str(unit) != '0':
        # 시장가 매도
        upbit.sell_market_order(crypto, unit)
        telegramlog("STOP LOSS ORDER SUBMITTED "+str(unit)+" "+str(crypto))

작동 부분입니다. 원화 마켓의 BTC, ETH, BCH, EOS, XRP 5개를 다룹니다. 멈추지 않고 계속 작동하면서 오류가 발생하면 Telegram으로 오류 알림을 보냅니다. Upbit API 호출 횟수 제한을 피하기 위해 1초의 sleep을 추가했습니다.

while True:
    try:        
        buy_crypto("KRW-BTC")
        buy_crypto("KRW-ETH")
        buy_crypto("KRW-BCH")
        buy_crypto("KRW-EOS")
        buy_crypto("KRW-XRP")
        
        sell_crypto("KRW-BTC")
        sell_crypto("KRW-ETH")
        sell_crypto("KRW-BCH")
        sell_crypto("KRW-EOS")
        sell_crypto("KRW-XRP")
        
        stoploss_crypto("KRW-BTC")
        stoploss_crypto("KRW-ETH")
        stoploss_crypto("KRW-BCH")
        stoploss_crypto("KRW-EOS")
        stoploss_crypto("KRW-XRP")
        
    except:
        print("Error! ")
        telegramlog("Bot Error!")
    
    time.sleep(1)

본 글은 매수 및 매도, 종목 추천 등과는 무관하며, 거래에 따른 책임은 모두 거래 실행 당사자에게 있습니다.






© 2021.03. by JacobJinwonLee

Powered by theorydb