[파이썬] 코인 자동매매 프로그램 만들기 #5 - 슬랙 알림 봇
개발/Python

[파이썬] 코인 자동매매 프로그램 만들기 #5 - 슬랙 알림 봇

반응형


이제 매매를 위한 프로그램은 작성을 했으니 몇 가지 상황에 간단한 알림을 받아볼 수 있도록 슬랙이라는 협업용 도구를 이용하여 알림 봇을 추가해주겠습니다

워크스페이스 생성

먼저 슬랙 사이트에 접속(https://slack.com/)하여 슬랙 워크스페이스를 생성해줍니다

 

워크스페이스 이름 설정

 

채널 이름 설정

 

이 단계 건너뛰기 선택


앱 생성

다음으로는 생성한 채널에 알림을 보내줄 앱을 생성해 주어야 합니다

슬랙 api 사이트에 접속(https://api.slack.com/)하여 Create an app 버튼을 클릭하고 From scratch를 선택

 

앱의 이름과 사용할 워크스페이스를 입력 → Create App

 

앱의 기능을 Bots로 설정

 

Review Scopes to Add 클릭

 

아래쪽으로 스크롤하여 Bot Token Scopes에서 Add an OAuth Scope를 클릭 → chat:wirte를 선택하여 봇에게 채팅창에 메시지를 입력할 수 있는 권한을 설정

 

Install to Workspace 클릭하여 앱을 설치할 워크스페이스 선택

 

만들어진 앱의 토큰 확인

 

워크스페이스 화면에서 채널 우클릭 → 채널 세부정보 열기 → 통합 → 앱추가에서 생성한 앱 선택


코드 작성

테스트 코드

이제 만들어 놓은 봇을 이용하여 실제로 메시지를 보내는 코드를 작성해보도록 하겠습니다

requests 라이브러리를 설치해줍니다

pip install requests

 

requests를 import해주고 다음과 같이 메시지를 전송하는 함수를 작성합니다

함수의 첫 번째 인자로 생성한 앱의 OAuth 토큰, 두 번째로 #채널명, 마지막으로 보낼 메시지를 작성합니다

import requests
 
def post_message(token, channel, text):
    response = requests.post("https://slack.com/api/chat.postMessage",
        headers={"Authorization": "Bearer "+token},
        data={"channel": channel,"text": text}
    )
    print(response)
 
appToken = "앱 토큰 입력"
 
post_message(appToken,"#coinautotade","test message!")

 

테스트 코드를 실행시켜보면 아래와 같이 알림 봇이 입력한 메시지를 잘 전송해 줍니다

Slack 어플리케이션을 설치한다면 휴대폰에서도 알림을 받을 수가 있습니다

 

코드가 정상적으로 동작하는 것을 확인했으니 기존에 만들어두었던 프로그램과 연결을 해주도록 하겠습니다

원래 코드에서 슬랙 봇을 호출하기 쉽도록 클래스를 만들어주고 토큰과 채널명은 프로그램 API키와 마찬가지로 파일에 저장해두고 읽어서 사용하겠습니다

class slack :
    def __init__(self, token, channel) :
        self.token = token
        self.channel = channel

    def message(self, message):
        response = requests.post("https://slack.com/api/chat.postMessage",
        headers={"Authorization": "Bearer " + self.token},
        data={"channel": self.channel,"text": message}
    )
    
with open("key_info.txt") as f :
    lines = f.readlines()
    
    app_token = lines[2].strip()
    channel = lines[3].strip()
    
slackBot = slack(appToken, channel)

 

 

저는 4가지 상황에서 알림을 보내보도록 하겠습니다

  1. 프로그램 시작
  2. 매수 시도
  3. 매도 시도
  4. 오류 발생

 

프로그램 시작

프로그램이 실행했을 때 시작 정보를 확인하기 위해 start 함수 실행시 시작 시간, 매매 대상 화폐, 시작 자산 정보를 알림으로 보내도록 하겠습니다

def start(self) :
    now = datetime.datetime.now() # 현재 시간
    slackBot.message("자동 매매 프로그램이 시작되었습니다\n시작 시간 : " + str(now) + "\n매매 대상 : " + self.ticker + "\n시작 자산 : " + str(self.start_cash))

 

매수 시도

매수시에는 매수 가격을 알림으로 보냅니다

매수시 주문 가격을 확인 하기 위해 최우선 매도 호가를 조회합니다

그리고 이 가격을 메시지에 담아 알림으로 전송합니다

buy_price = pyupbit.get_orderbook("KRW-BTC")['orderbook_units'][0]['ask_price'] # 최우선 매도 호가
slackBot.message("#매수 주문\n매수 주문 가격 : " + buy_price + "원)

 

매도 시도

매도시에도 마찬가지로 매도 가격을 알림으로 보냅니다

sell_price = pyupbit.get_orderbook("KRW-BTC")['orderbook_units'][0]['bid_price'] # 최우선 매수 호가
slackBot.message("#매도 주문\n매도 주문 가격 : " + sell_price + "원")

 

오류 발생

프로그램 실행중 오류 발생시 except 처리를 하고 있기는 하지만 지속적인 오류 발생시에는 코드 수정이 필요하기 때문에 오류 발생시에도 알림을 보내주도록 하겠습니다

except :
    slackBot.message("!!! 프로그램 오류 발생 !!!")

 

이제 실제로 프로그램을 돌려보면서 발생하는 오류들을 수정하고 결과를 지켜보도록 하겠습니다


최종코드

import pyupbit
import time
import datetime
import math
import requests

class autoTrade :
    def __init__(self, start_cash, ticker) :
        self.fee = 0.05 # 수수료
        self.target_price = 0 # 목표 매수가
        self.bull = False # 상승장 여부
        self.ticker = ticker # 티커
        self.buy_yn = False # 매수 여부

        self.start_cash = start_cash # 시작 자산

        self.get_today_data()

    def start(self) :
        now = datetime.datetime.now() # 현재 시간
        slackBot.message("자동 매매 프로그램이 시작되었습니다\n시작 시간 : " + str(now) + "\n매매 대상 : " + self.ticker + "\n시작 자산 : " + str(self.start_cash))
        openTime = datetime.datetime(now.year, now.month, now.day) + datetime.timedelta(1) # 자정

        while True :
            try :
                now = datetime.datetime.now()

                if openTime < now < openTime + datetime.timedelta(seconds=5) :
                    openTime = datetime.datetime(now.year, now.month, now.day) + datetime.timedelta(1)
                    self.sell_coin() # 매도 시도
                    self.get_today_data() # 데이터 갱신

                current_price = pyupbit.get_current_price(self.ticker)
                if((current_price >= self.target_price) and self.bull and not self.buy_yn) : # 매수 시도
                    self.buy_coin()
            except :
                slackBot.message("!!! 프로그램 오류 발생 !!!")

            time.sleep(1)

    def get_today_data(self) :
        daily_data = pyupbit.get_ohlcv(self.ticker, count=41)
        # 노이즈 계산 ( 1- 절대값(시가 - 종가) / (고가 - 저가) )
        daily_data['noise'] = 1 - abs(daily_data['open'] - daily_data['close']) / (daily_data['high'] - daily_data['low'])
        # 노이즈 20일 평균
        daily_data['noise_ma20'] = daily_data['noise'].rolling(window=20).mean().shift(1)
       
        # 변동폭 ( 고가 - 저가 )
        daily_data['range'] = daily_data['high'] - daily_data['low']
        # 목표매수가 ( 시가 + 변동폭 * K )
        daily_data['targetPrice'] = daily_data['open'] + daily_data['range'].shift(1) * daily_data['noise_ma20']

        # 5일 이동평균선
        daily_data['ma5'] = daily_data['close'].rolling(window=5, min_periods=1).mean().shift(1)
        # 상승장 여부
        daily_data['bull'] = daily_data['open'] > daily_data['ma5']

        today = daily_data.iloc[-1]

        self.target_price = today.targetPrice
        self.bull = today.bull

    def buy_coin(self) :
        balance = upbit.get_balance() # 잔고 조회
        
        if balance > 5000 : # 잔고 5000원 이상일 때
            upbit.buy_market_order(self.ticker, balance * 0.9995)
            self.buy_yn = True

            buy_price = pyupbit.get_orderbook("KRW-BTC")['orderbook_units'][0]['ask_price'] # 최우선 매도 호가
            slackBot.message("#매수 주문\n매수 주문 가격 : " + buy_price + "원")

    def sell_coin(self) :
        balance = upbit.get_balance("KRW_BTC") # 잔고 조회

        upbit.sell_market_order(ticker, balance)
        self.buy_yn = False

        sell_price = pyupbit.get_orderbook("KRW-BTC")['orderbook_units'][0]['bid_price'] # 최우선 매수 호가
        slackBot.message("#매도 주문\n매도 주문 가격 : " + sell_price + "원")

class slack :
    def __init__(self, token, channel) :
        self.token = token
        self.channel = channel

    def message(self, message):
        response = requests.post("https://slack.com/api/chat.postMessage",
        headers={"Authorization": "Bearer " + self.token},
        data={"channel": self.channel,"text": message}
    )

with open("key_info.txt") as f :
    lines = f.readlines()
    acc_key = lines[0].strip()    # Access Key
    sec_key = lines[1].strip()    # Secret Key
    app_token = lines[2].strip()  # App Token
    channel = lines[3].strip()    # Slack Channel Name

upbit = pyupbit.Upbit(acc_key, sec_key)
slackBot = slack(app_token, channel)

start_cash = upbit.get_balance()
ticker = "KRW-BTC"
tradingBot = autoTrade(start_cash, ticker)
tradingBot.start()