본문 바로가기

주식/[Upbit] 자동매매

[Upbit] 자동매매 프로그램 (Python) #2

728x90
반응형

이전에 자동매매프로그램을 만들어보았습니다.

 

https://bab-dev-study.tistory.com/8

 

[Upbit] 자동매매 프로그램 (Python) #1

○ 준비작업 Upbit Open API 를 활용하여 자동매매 프로그램을 만들어 보겠습니다. 먼저 API를 사용하기 위해서는 Upbit 에 사용신청을 해야합니다. 참조 : wikidocs.net/31063 위키독스 온라인 책을 제작

bab-dev-study.tistory.com

현재 프로그램 하나에서 매수와 매도를 동시에 하는걸로 만들었는데 

1분에 한번씩 조회를 하다보니 매도 타이밍을 놓치는 경우가 많았습니다.

 

그래서 이번엔 매도만 하는 프로그램으로 따로 분리하여 만들어보겠습니다.

 


  • class Myupbit(pyupbit.Upbit) 생성

pyupbit 를 import 해서 사용했었는데 사용하다보니 불편해서 상속받아 필요한 함수를 추가하였습니다.

빠르게 조회를 해서 매도할지 확인을 해야하는데 하나하나 조회를 하다보니 속도가 느려져서 

한꺼번에 받아오는 함수를 만들었습니다.

 

myupbit.py 파일을 만들고 Myupbit class 에서 pyupbit를 상속받았습니다.

나중에 main.py 에서 import myupbit 를 사용할 예정입니다.

main.py에서는 pyupbit를 안쓰고 필요한 내용은 myupbit에서 재정의 해서 사용할것입니다.

 

import pyupbit

class MyUpbit(pyupbit.Upbit):
    def getPurchaseCoins(self, ticker="KRW"):
        try:
            balances, req = self.get_balances(contain_req=True)
            return balances
        except Exception as x:
            print(x.__class__.__name__)
            return None
            
    def order_sell(session, ticker, price, unit):
        result = session.sell_limit_order(ticker, price, unit)
        return result
    

def inquiry_price(tickers):
    try:
        return pyupbit.get_current_price(tickers)
    except:
        return None

- getPurchaseCoins() : 보유하고 있는 모든 ticker 정보를 가져옵니다.

- order_sell() : 해당 ticker를 매도 등록 합니다.

      - inquiry_price() : 해당 ticker의 현재 가격을 가져옵니다



  • Utils 함수 생성

사용할 함수를 정의 합니다.

 

def getTime():
    return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')


def getSession():
    with open("key.txt") as f:
        lines = f.readlines()
        key = lines[0].strip()
        secret = lines[1].strip()
        session = myupbit.MyUpbit(key, secret)
    return session


def sellCheck(df, price, avg_buy_price, close_ma_5, close_ma_15):
    now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
	
    '''
    if 매도 조건 1:  
        return True    

    if 매도 조건 2:  
        return True  
        
    if 매도 조건 3:  
        return True    
    '''

    return False


def sell(session, ticker, price, unit, before_price):
    print(getTime(), '[SELL]', ticker, 'price:', price, 'unit:', unit,\
          ' => ', before_price, ' > ', price, ', ', round(((price-before_price)/before_price)*100, 2), '%')
    return myupbit.order_sell(session, ticker, price, unit)


def trade_wait_append(array, result):
    try:
        array.append(result['uuid'])
    except KeyError:
        print("[trade_wait_append] Fail")

- getTime() : 현재시간을 불러옵니다.

- getSession() : Upbit API를 사용하기 위한 Session을 얻어옵니다.

- sellCheck() : 매도 조건을 만듭니다.

- sell() : ticker 매도를 등록합니다.

- trade_wait_append() : 매도 등록된 정보를 저장해 둡니다.

                              매도가 오랫동안 대기중일때 취소시키기 위해 사용합니다.


 

 


  • def main( ) 함수 정의

반복실행하여 매도를 실행하는 main 함수 입니다.

보유중인 ticker를 조회 후 1초마다 각 ticker의 매도조건을 체크합니다.

 

1. 저는 평가금액이 -3% 인 경우에는 매도조건를 체크하지 않습니다.

2. 매도 등록 후 일정 시간이 지나면 등록된 정보를 제거하는 로직이 뒤에 들어가 있습니다.

 

def main():
    session = getSession()
    trade_wait = []
    cancel_flag = 0
    #properties = configparser.ConfigParser()

    print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'start')
    while True:
        #properties.read('config.ini')
        try:
            # 현재 보유코인
            purchaseCoinsInfo = session.getPurchaseCoins()

            for purchaseCoin in purchaseCoinsInfo:
                time.sleep(1)
                
                # 현금 보유 내용, 단가가 없는 ticker 제외
                if purchaseCoin['currency'] == 'KRW' or purchaseCoin['avg_buy_price'] == '0':
                    continue

                ticker = purchaseCoin['unit_currency'] + '-' + purchaseCoin['currency']
                price = myupbit.inquiry_price(ticker)  # 현재 가격
                balance = float(purchaseCoin['balance'])  # 보유 수량
                avg_buy_price = float(purchaseCoin['avg_buy_price'])  # 매수 평균 단가

                if ((price - avg_buy_price) / avg_buy_price) < -0.03:
                    continue

                df = myupbit.get_df(ticker=ticker, interval='minute1', window=40)
                if df is None or price is None:
                    print(getTime(), '[df_error] ', ticker)
                    continue

                close_ma_5 = df['close'].rolling(window=5).mean()
                close_ma_15 = df['close'].rolling(window=15).mean()

                if sellCheck(df, price, avg_buy_price, close_ma_5, close_ma_15):
                    result = sell(session, ticker, price, balance, avg_buy_price)
                    trade_wait_append(trade_wait, result)
                    cancel_flag = 0

                #print(purchaseCoin)
            ## for-end

        except Exception as e:
            print(getTime(), '[exception] : ', e)
            print(traceback.format_exc())

        if cancel_flag > 5:
            for cancel_uuid in trade_wait:
                print(getTime(), '[Cancel] ')
                session.cancel_order(cancel_uuid)
            trade_wait.clear()
            cancel_flag = 0

        if len(trade_wait) > 5:
            cancel_flag += 1
        ## while-end

if __name__ == '__main__':
    main()


  • 후기

이전보다 조금 더 깔끔하게 소스가 나왔습니다.

1초마다 보유 중인 ticker 하나씩 조건 검사를 하니까 이전보다 빠르게 매도가 이루어 집니다.

그리고 보유중인 정보를 한번에 불러오기 때문에 timeout 발생도 없습니다.

 

이거보다 더 빠르게 조회를 할 수 있겠지만 이정도로도 충분한거 같고

매수 프로그램에 영향을 줄꺼같아서 더 과하게 반복은 안돌릴 예정입니다.

 

다음에는 매수 프로그램을 올려보겠습니다. 

 

( 쓰다보니 매도 등록한거 취소 로직이 필요가 없는거같아서 빼버렸습니다. 하지만 매수할땐 반드시 필요!)

728x90
반응형