본문 바로가기
Python

파이썬을 이용한 가상화폐 자동거래 - RSI Divergence (update)

by 아짱이아빠 2022. 12. 19. 21:42
반응형

파이썬을 이용하여 업비트에서 가상화폐 자동거래를 하는 방법에 대해 알아보겠다.

이전 포스트에서도 가상화폐 자동거래 코드에 대해 얘기한 적이 있는데 이번에는 주기적으로 조건에 맞는 종목을 찾다가 조건이 일치하면 거래에 들어가는 기능이 포함되어 있으며 또한, 자동거래 전략에 있어서 RSI Divergence를 이용한다.

 

RSI Divergence에 대한 기본 내용과 파이썬 프로그램에 대해서는 아래의 포스트에 나와있으니 참조하면 되겠다.

 

 

파이썬을 이용한 RSI Divergence 구현

이번에는 파이썬을 이용하여 RSI Divergence 구간을 찾아 매수를 하고 RSI가 과매도 구간에서 매도를 하는 것에 대해 알아보겠다. [ RSI Divergence ] RSI Divergence에 대해 간단히 알아보면 다음과 같다. 먼

superhky.tistory.com

 

상화폐 자동거래에 대한 파이썬 코드는 예전 포스트에서 얘기했으며 여기서의 프로그램 구조가 거의 유사하므로 자세한 내용은 아래의 링크를 참조하면 되고 여기서는 자동거래에서 사용될 수 있는 RSI Divergence 코드에 대해 집중으로 얘기해보고자 한다.

 

 

SMA와 MACD 이용 Python 가상화폐 자동매매(1/2)

이번에는 Python을 이용한 가상화폐의 자동매매에 대해 알아본다. 최근 2개월간 자동매매 프로그램을 짜고 실제로 투자를 해보았으며 이를 통해 알게 된 것들을 포함하여 정리해보고자 한다. 처

superhky.tistory.com

 

바로 본론으로 들어가겠다.

다음의 코드는 업비트 API를 이용하여 자동거래를 하기 위해 필요한 모듈을 불러오고 개인별 접근 코드를 입력한다.

import time
import pyupbit
import datetime
import pandas as pd
import numpy as np

access = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

 

이후 두 개의 정의를 한다.

하나는 매수 전 특정 조건을 만족하는 가상화폐 즉, ticker를 찾을 수 있는 조건을 만들어주는 정의이다.

다른 하나는 매수 이후에 매도를 위한 조건을 만드는 정의이다.

 

[특정 조건을 만족하는 가상화폐 찾기]

다음의 정의는 크게 세 부분으로 나눌 수 있겠다.

첫 부분은 가상화폐의 ohlcv 데이터를 불러오고 RSI를 계산하는 부분이고 두 번째는 RSI Divergence를 찾는 부분이다.

세 번째는 if문을 통해 조건을 만들어 준다.

 

(1) ohlcv 데이터와 RSI 계산

def Rising_Market_RSI_Divergence(ticker) :
    df = pyupbit.get_ohlcv(ticker, interval='minutes5', count=100)
    new_df = df['close']

	df_2 = pyupbit.get_ohlcv(ticker, interval='minutes60', count=24)
    df_2_sum = df['value'].sum() 

    #RSI
    delta = df['close'].diff(1)
    delta = delta.dropna()
    up = delta.copy()
    down = delta.copy()
    up[ up < 0 ] = 0
    down[ down > 0 ] = 0
    time_period = 9
    AVG_Gain = up.ewm(com=time_period-1,min_periods=time_period).mean()
    AVG_Loss = abs(down.ewm(com=time_period-1, min_periods=time_period).mean())
    RS = AVG_Gain / AVG_Loss
    RSI = 100.0 - (100.0/(1.0 + RS))

 

코드의 첫 부분은 전체 정의의 이름을 정하며 ‘ticker’를 입력으로 받는다.

이후 5분봉 데이터 100개를 불러오고 종가 정보는 ‘new_df’란 이름으로 저장한다.

몇 분봉 데이터를 기준으로 자동매매를 할 것인지는 각자에 맞게 변경하면 되는데 이에 따라 자동매매의 결과는 달라질 것이다.

 

그 다음 코드는 60분봉 데이터 24개를 가져와서 value 값의 총합을 ‘df_2_sum’의 이름으로 저장한다.

이는 거래량이 너무 없는 가상화폐는 제외하고 어느 정도 거래량이 있는 가상화폐만을 매매의 대상으로 하기 위함이다.

마지막은 RSI를 계산하는 내용으로 이는 많이 얘기했던 부분이므로 설명은 하지 않고 넘어가도록 하겠다.

 

(2) RSI Divergence 계산

 

RSI Divergence는 위의 링크에 상세한 내용이 있다.

다만, 자동거래시에는 데이터를 읽어서 계산하는 방향에 있어서 차이점이 있다.

단순히 과거의 데이터를 불러와서 RSI Divergence를 계산할 때에는 과거부터 데이터를 읽어가면서 조건을 분석하지만 실시간 자동거래에서는 이와는 반대로 가장 최신의 데이터부터 과거의 데이터로 읽어가면서 조건을 계산해야 한다.

과거의 데이터로 backtesting을 해볼 때에는 과거의 데이터부터 현재의 데이터로 읽으면서 계산해도 되지만 실시간 자동매매에서는 거꾸로 최근의 데이터에서 시작하여 계산해야만 RSI Divergence가 발생한 시점에 즉시 매수가 이뤄질 수 있다. 

    low_barrier = 30
    high_barrier = 70
    temp01 = []
    temp01_id = []
    temp02 = []
    temp02_id = [] 
    temp01_min_price = []
    temp02_min_price = []
    temp01_min_rsi = []
    temp02_min_rsi = []
    mark = 0
    i=0
    k=0
    m=0
    n=0

	for i in range (len(df)):
        if RSI[len(df)-2-i] < low_barrier:
            for k in range(i,len(df)):
                if RSI[len(df)-2-k] > low_barrier and RSI[len(df)-1-k] < low_barrier:
                    temp01 = RSI.iloc[len(df)-2-k:len(df)-1-i]
                    temp01_id = len(df)-2-k+temp01.argmin()
                    temp01_min_rsi = RSI[temp01_id]
                    temp01_min_price = df['close'][temp01_id]

					for m in range(k, len(df)):
                        if RSI[len(df)-2-m] < low_barrier and RSI[len(df)-1-m] > low_barrier:

							for n in range(m, len(df)):
                                if RSI[len(df)-2-n] > low_barrier and RSI[len(df)-1-n] < low_barrier:
                                    temp02 = RSI.iloc[len(df)-2-n:len(df)-1-m]
                                    temp02_id = len(df)-2-n+temp02.argmin()
                                    temp02_min_rsi = RSI[temp02_id]
                                    temp02_min_price = df['close'][temp02_id]
                                    if temp01_min_rsi > temp02_min_rsi and temp01_min_price < temp02_min_price and RSI.iloc[len(df)-1-m:len(df)-2-k].max() < high_barrier :
                                        mark = RSI[-1]
                                    break
                            break
                    break
            break

 

전체적인 코드는 이전 포스트에 있는 구조와 동일하지만 최신의 데이터부터 계산하기 위한 부분과 RSI 최저점을 찾아내는 조건 부분이 수정되었다.

그리고 마지막 부분에 ‘mark = RSI[-1]’은 위의 모든 조건에 만족하는 가상화폐가 확인될 경우 즉, RSI Divergence가 막 발생한 가상화폐를 찾았을 때 초기값이 0이었던 변수 'mark'에 RSI 최신 값을 넣어주어 다음의 조건 문을 True가 되어 조건에 부합하는 가상화폐가 있음을 알리기 위함이다.

 

(3) 조건 생성

    if mark !=0 and (df_2_sum > 5000000000) :
        return True and (new_df, temp01_min_rsi,temp02_min_rsi, temp01_min_price, temp02_min_price, mark)

 

앞에서의 ‘mark’ 값이 0이 아니고 또한 위에서 계산한 거래량이 특정 값 이상이면 True와  이때의 종가, 최소 RSI, 최소 가격 등을 반환한다.

 

 

[매도 조건 설정]

다음의 정의는 매도 시에 적용할 RSI를 계산하여 최신 종가와 RSI 값을 반환한다.

앞에서도 얘기했듯이 이 프로그램에서 매도 전략은 RSI 값이 70 이상이 되었고 이후 RSI가 이전 값 대비 낮아지는 순간에 매도를 하는 것이다.

def Buying_Selling_RSI(ticker) :
    df_1 = pyupbit.get_ohlcv(ticker, interval='minutes5', count=100)
    new_df_1 = df_1['close']
 
    #RSI
    delta_1 = df_1['close'].diff(1)
    delta_1 = delta_1.dropna()
    up_1 = delta_1.copy()
    down_1 = delta_1.copy()
    up_1[ up_1 < 0 ] = 0
    down_1[ down_1 > 0 ] = 0
    time_period = 9
    AVG_Gain_1 = up_1.ewm(com=time_period-1, min_periods=time_period).mean()
    AVG_Loss_1 = abs(down_1.ewm(com=time_period-1, min_periods=time_period).mean())
    RS_1 = AVG_Gain_1 / AVG_Loss_1
    RSI_1 = 100.0 - (100.0/(1.0 + RS_1))

	return(new_df_1, RSI_1)

 

여기서는 특별히 설명할 부분이 없으므로 넘어가도록 하겠다.

 

[잔액/현재액 조회 및 프로그램 실행 부분]

#잔액 조회
def get_balance(ticker):
    """잔고 조회"""
    balances = upbit.get_balances()
    for b in balances:
        if b['currency'] == ticker:
            if b['balance'] is not None:
                return float(b['balance'])
            else:
                return 0
    return 0

#현재가 조회
def get_current_price(ticker):
    """현재가 조회"""
    return pyupbit.get_orderbook(tickers=ticker)[0]["orderbook_units"][0]["ask_price"]

# 로그인
upbit = pyupbit.Upbit(access, secret)
print("autotrade start")

 

위의 코드는 업비트 API를 이용하여 내 계좌의 잔액을 조회하고 또한 매수한 ticker의 현재가를 조회하는 부분이다.

그리고 앞에서의 접근코드를 통해 로그인을 한다.

로그인이 되면 이를 알리기위해 “autotrade start”를 출력하고 계좌의 잔액을 보여주도록 하는 코드가 마지막 부분에 있다.

 

[자동매매 전략의 실행 부분]

가상화폐 자동매매 프로그램의 마지막 부분은 전략이 실행되는 부분이다.

먼저 실행에 필요한 변수의 초기값을 정하고 ‘while’ 문을 통해 수행된다.

low_barrier = 30
high_barrier = 70
buy_price = 0
sell_price = 0
bal = 0
flag = 1
target = 0

 

‘While’ 문은 전체 가상화폐 즉, ticker를 대상으로 앞에서 정의한 ‘def Rising_Market_RSI_Divergence(ticker) :’을 실행하여 조건에 부합하는 ticker 유무를 확인하는 부분과 조건에 부합하는 ticker 확인 시, 매수 조건을 확인하여 매수를 하는 부분, 매수 이후 매도 조건을 확인하고 매도를 하는 부분으로 구성된다.

 

반응형

 

(1) 전체 ticker를 대상으로 조건 확인

while True:
    try:
        tickers = pyupbit.get_tickers(fiat="KRW")

        for ticker in tickers:
            is_bull = Rising_Market_RSI_Divergence(ticker)
            print(is_bull)
            print('1')
            if upbit.get_balance("KRW") >= 10:
                if is_bull:
                    coin = ticker
                    print(coin)
                    coin_= ticker
                    current_price = get_current_price(coin)

                    new_df = Rising_Market_RSI_Divergence(coin)[0]
                    temp01_min_rsi = Rising_Market_RSI_Divergence(coin)[1]
                    temp02_min_rsi = Rising_Market_RSI_Divergence(coin)[2]
                    temp01_min_price = Rising_Market_RSI_Divergence(coin)[3]
                    temp02_min_price = Rising_Market_RSI_Divergence(coin)[4]
                    mark = Rising_Market_RSI_Divergence(coin)[5]                   

                    print('현재가 = ', new_df[-1])
                    print('두번째 RSI =', temp01_min_rsi)
                    print('첫번째 RSI =', temp02_min_rsi)
                    print('두번째 가격 =', temp01_min_price)
                    print('첫번째 가격 =', temp02_min_price)
                    print('현재 RSI = ', mark)

 

RSI divergence와 거래량 조건이 부합하는 ticker가 있고 현재의 계좌 잔액이 10원 이상이면 즉, 다른 가상화페에 대한 매수가 이뤄지지 않은 상태가 되면 조건에 부합했던 가상화폐 이름을 출력하고 또한, 현재가, 최소 RSI, 이때의 최소 가격 등을 출력한다.

 

 

(2) 매수 전략

 

앞에서의 코드까지 수행되면 다음의 코드에서 최종 조건을 확인하여 만족하면 매수가 이뤄진다.

            if RSI_1[-2] < low_barrier and RSI_1[-1] > low_barrier and flag == 1:
    			krw = upbit.get_balance("KRW")
    			upbit.buy_market_order(coin, krw*0.9995)
    			buy_price = new_df[-1]
    			print('Buy:',buy_price)
    			flag=0
    			time.sleep(10)

RSI Divergence가 발생했고, 30 밑에 있던 RSI 값이 30을 넘는 순간에 매수를 하는 것이다.

매수는 수수료를 제외하고 계좌의 잔액 전체로 매수를 한다.

매수가 이뤄지면 이때의 매수 가격을 출력하고 flag를 0으로 바꾸며 10초가 기다린다.

 

 

(3) 매도 전략

 

매수가 이뤄지면 계좌의 잔액은 10원 미만이 될 것이다.

다음의 코드는 매수가 이뤄진 상태임을 확인하고 이후 매도가 일어날때까지 ‘while’ 문이 수행된다.

            if upbit.get_balance("KRW") <= 10:

                while True:
                    new_df_1 = Buying_Selling_RSI(coin)[0]
                    RSI_1 = Buying_Selling_RSI(coin)[1]
                    print('현재가 = ' , new_df_1[-1])
                    print('현재 RSI:', RSI_1[-1])

                    if RSI_1[-2] > high_barrier and RSI_1[-2] > RSI_1[-1] and flag == 0:
                        bal = upbit.get_balance(coin_)
                        upbit.sell_market_order(coin, bal*0.9995)
                        sell_price = new_df_1[-1]
                        print('Sell:',sell_price)
                        flag=1

                    if upbit.get_balance("KRW") >= 10:
                        break

                    time.sleep(60)

        time.sleep(140)
    except Exception as e:
        print(e)
        time.sleep(1)

 

‘While’문 내에서 60초 간격으로 앞에서 정의한 매도 관련 ‘Buying_Selling_RSI’가 수행되고 이때 계산된 RSI 값을 바탕으로 RSI가 70을 넘었었고 가장 최신 RSI가 이전 RSI 보다 작아지면 그때의 호가로 전량 매도를 한다.

매도가 일어난 후는 계좌에 잔고가 10원 이상이 되고 이 while 문을 빠져나오게 된다.

 

여기까지 RSI Divergence를 파악하여 가상화폐 실시간 매매 프로그램에 대해 알아봤다.

이 프로그램은 특정 거래량 이상이면서 RSI Divergence가 발생한 가상화폐를 계속 탐색하다가 조건에 부합하는 가상화폐가 찾아지면 RSI가 30 미만에서 30이상이 될 때 매수를 하고 이후 RSI가 70을 넘고 이전 RSI 보다 최근 RSI가 작아질 때 매도를 한다.

 

다음에는 이 프로그램을 통해 며칠간 어떤 실적이 있었는지 정리해보겠다.   

 

 

 

[업데이트 - 2023.01.19]

위에서 설명한 RSI Divergence로 실시간 매매 성능을 확인하던 중 코드에 오류가 있음을 확이하였다.

오류가 있는 부분은 RSI의 최저점을 찾는 코드 부분이며 실제 매매 상황에서 최저점이 아닌 지점을 설정하는 오류이다.

그래서 실제로는 RSI Divergence가 아님에도 매수까지 이어지는 문제가 있었다.

 

다음의 코드는 이를 수정한 것이며 이를 통해 RSI의 최저점을 정상적으로 찾을 수 있다.

for i in range (len(df)-1):
        if RSI[len(df)-2-i] < low_barrier:
            for k in range(i,len(df)-1):
                if RSI[len(df)-2-k] < low_barrier and RSI[len(df)-3-k] > low_barrier:
                    temp01 = RSI.iloc[len(df)-2-k:len(df)-2-i+1]
                    temp01_id = len(df)-3-k + np.argmin(temp01)+1
                    temp01_min_rsi = RSI[temp01_id]
                    temp01_min_price = df['close'][temp01_id+1]
                    temp01_index = df.index[temp01_id+1]
        
                    for m in range(k, len(df)-1):
                        if RSI[len(df)-3-m] > low_barrier and RSI[len(df)-4-m] < low_barrier:
                            for n in range(m, len(df)-1):
                                if RSI[len(df)-4-n] < low_barrier and RSI[len(df)-5-n] > low_barrier:
                                    temp02 = RSI.iloc[len(df)-4-n:len(df)-4-m+1]
                                    temp02_id = len(df)-5-n + np.argmin(temp02)+1
                                    temp02_min_rsi = RSI[temp02_id]
                                    temp02_min_price = df['close'][temp02_id+1]
                                    temp02_index = df.index[temp02_id+1]
                                    if temp01_min_rsi > temp02_min_rsi and temp01_min_price < temp02_min_price and len(RSI.iloc[len(df)-2-k:len(df)-2-i+1]) > 1 and len(RSI.iloc[len(df)-4-n:len(df)-4-m+1]) > 2:
                                        mark = RSI[-1]
                                    break
                            break
                    break
            break

 

반응형

댓글