본문 바로가기
Python

파이썬을 이용한 가상 화폐 매매 전략 - Stochastic RSI & EMA

by 아짱이아빠 2021. 12. 5. 11:31
반응형

파이썬을 이용하여 시세 분석 보조 지표인 Stochastic RSI와 EMA를 계산하고 이를 통해 가상 화폐 거래에 적용했을 때에 얼마큼의 수익이 있을지 backtesting을 통해 알아보겠다.

 

먼저 Stochastic RSI에 대해 간단히 알아보고 EMA를 결합한 거래 전략에 대해 얘기해 보겠다.

 

[Stochastic RSI]

Stochastic RSI는 시장이 과매수 혹은 과매도 되었는지를 판단하는 기술적 분석 지표이다.

이름에서 알 수 있듯이 RSI(Relative Strength Index)에서 파생되었다.

Stochastic RSI의 공식은 아래와 같으며 가장 일반적으로 설정하는 시간은 RSI와 마찬가지로 14 주기로 설정한다.

기간은 상향 또는 하향 조정할 수 있으며 이를 통해 장기 또는 단기 추세를 식별하기도 한다.

 

Stochastic RSI = (현 시점 RSI - 최저점 RSI) / (최고점 RSI - 최저점 RSI)

 

Stochastic RSI에서 과매수 신호는 80 이상이 해당하며 과매도 신호는 20 이하에 해당한다.

 

[거래 전략]

하나의 분석 지표로만으로 거래의 기준을 삼는 것은 매우 위험하다. 

그러므로 Stochastic RSI에 지수이동평균인 EMA(Exponential Moving Average)를 같이 적용한다. 

EMA는 50일 기준과 150일 기준 두 개의 EMA를 적용하였다.

그래서 거래의 전략은 아래와 같다.

 

   - 매수 : 현재의 가격이 150일 기준 EMA 보다 위에 있고 50일 기준 EMA의 기준보다도 위에 있어야 하며

              Stochastic RSI가 20을 넘을 때

   - 매도 : 현재의 가격이 매수 가격에서 3%이상 일 때

 

전략은 매수에 비해 매도의 전략이 무척 단순하다.

주식이나 가상 화폐나 모두 어느 시점에 매도를 할 것인지가 언제 매수를 할 것인지보다 중요한 것 같다. 

기존에 여러 방식의 매도의 조건을 설정해봤는데 계속 손해를 보면서 최근 드는 생각은 오히려 단순하게 특정 퍼센트의 이익이 발생하면 과감히 나오고 다시 기회를 보는 것이 수익에 있어서는 더 좋지 않을까 하는 생각이다.

그래서 여기서는 매도의 조건을 매우 단순하게 설정하였다.

 

[파이썬 코드]

아래는 프로그램에 필요한 모듈을 불러오는 코드이다.

!pip install pyupbit
import pyupbit
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')

프로그램은 Google Colab에서 구성하였으며 맨 처음 코드는 업비트 모듈을 설치하는 내용이고 마지막 코드는 matplotlib의 그래프 스타일을 설정하는 것이며 여기서는 ‘fivethirtyeight’이라는 스타일을 적용하였다.

 

coin = input("코인 :")
df = pyupbit.get_ohlcv(coin,interval="minutes60", count=200)

위는 어떤 가상 화폐를 거래할 것인지 입력을 받도록 한 것이며 원화 기준이면 ‘KRW-코인’의 형태로 입력한다.

입력받은 가상 화폐에 대해 60분 기준의 데이터 200개를 현재 시간 기준으로 불러와서 ‘df’에 저장한다.

 

Stochastic RSI를 계산하기 위해서는 먼저 표준 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 = 14
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))
df['RSI'] = RSI

앞에서의 ‘df’에 저장된 데이터 중 종가인 ‘close’ column의 데이터를 이용하여 RSI를 구하고 이를 다시 ‘df’의 ‘RSI’ column에 저장한다.

 

여기서 구한 RSI를 이용하여 Stochastic RSI를 구하며 아래와 같이 함수로 정의한다.

def stochastic(data, k_window, d_window, window):
    min_val  = data.rolling(window=window, center=False ).min()
    max_val = data.rolling(window=window, center=False).max()
    stoch = ( (data - min_val) / (max_val - min_val) ) * 100
    K = stoch.rolling(window=k_window, center=False).mean()  
    D = K.rolling(window=d_window, center=False).mean() 
    return K, D

위의 함수는 다음의 코드로 실행한다. 

df['K'], df['D'] = stochastic(df['RSI'], 5, 5, 5)

 

또 다른 보조 지표인 50일 기준 EMA와 150일 기준 EMA는 아래와 같이 쉽게 구할 수 있다.

df['EMA50'] = df['close'].ewm(50).mean()
df['EMA150'] = df['close'].ewm(150).mean()

 

이더리움(ETH)을 대상으로 여기까지 계산된 결과를 도식화 해보면 다음과 같다.

50 EMA와 150 EMA &#44; Stochastic RSI 결과 - 이더리움(ETH)
(50 EMA와 150 EMA , Stochastic RSI 결과 - 이더리움(ETH))

 

이제 거래 전략을 파이썬 함수로 정의하면 다음과 같다.

def buy_sell(signal, case1, case2, case3):
  sigPriceBuy = []
  sigPriceSell = []
  buy_price=0
  flag = 1

  for i in range(0, len(signal)):
    if (signal[case1][i] < signal['close'][i]) and (signal[case2][i] < signal['close'][i]) and ((signal[case3][i-1] < 30) and (signal[case3][i] >= 30)) and flag == 1:      
      sigPriceBuy.append(signal['close'][i])
      buy_price = signal['close'][i]
      sigPriceSell.append(np.nan)
      flag = 0

    elif (signal['close'][i] > buy_price * 1.03) and flag == 0:
      sigPriceSell.append(signal['close'][i])
      sigPriceBuy.append(np.nan)
      flag = 1

    else:
      sigPriceSell.append(np.nan)
      sigPriceBuy.append(np.nan)

  return(sigPriceBuy, sigPriceSell)

코드 자체는 다른 포스트에서 설명을 하였으므로 자세한 내용은 넘어가도록 하겠다.

거래 전략은 앞에서 설명한 것과 같다.

다만 stochastic RSI의 값 중 %D의 값이 생각보다 20 밑으로 내려가는 경우가 많지 않아서 기준을 30으로 변경하였다.

 

반응형

 

위의 함수는 다음의 코드로 실행되며 결과는 역시 ‘df’의 새로운 column에 저장된다.

x = buy_sell(df, 'EMA50', 'EMA150', 'D')
df['Buy_Signal_Price'] = x[0]
df['Sell_Signal_Price'] = x[1]

 

거래의 결과를 그림으로 도식화하면 다음과 같다.

Stochastic RSI와 50 EMA&#44; 150 EMA 전략에 따른 결과 - 이더리움(ETH)
(Stochastic RSI와 50 EMA, 150 EMA 전략에 따른 결과 - 이더리움(ETH))

결과는 약 11월 27일부터 12월 5일까지의 기간 중 두 번의 거래가 있고 모두 매수 가격의 3% 이상이 되었다면 매도를 하였다.

그리고 12월 2일경에 다시 조건이 되어 매수를 했으나 이후 이더리움 가격은 계속 하락을 하여 매도를 하지 못하고 물려있는 상태가 되었다.

이렇게 물리게 되는 상황을 막기 위해서는 조건을 추가하여 특정 퍼센트 이상 손해가 발생하거나 가격이 내려가면 매도를 하게 할 수 있을 것이다.

두 번의 거래로 약 6%의 누적 수익을 낼 수 있었을 것이다. 

그러나 가격 추세를 보면 첫 매수를 하고 가격이 피크가 되었을 때에 매도를 했더라면 약 11%의 더 높은 수익을 올릴 수 있었을 것이다.

하지만 문제는 그때가 피크이고 가격이 곧 내려갈 것이라고 아무도 모른다는 것이다.

 

[Backtesting]

Backtesting은 최근 거래량이 많아진 가상 화폐 밀크(MLK)와 베이직어텐션토큰(BAT)에 대해서 수행해 봤다.

Backtesting 코드는 아래의 링크를 참고하길 바라며 여기서의 설명은 넘어가도록 하겠다.

 

 

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

지난번에 알아본 자동매매 프로그램을 backtesting 하는 방법과 그 결과에 대한 분석을 해보겠다. 먼저 backtesting 코드를 알아본 후 dp이다(ADA)와 이더리움클래식(ETC)에 대해 자동매매 프로그램의 누

superhky.tistory.com

 

테스트의 기간은 11월 27일부터 12월 5일까지이며 결과는 아래와 같다.

Backtesting 결과 - 밀크(MLK)
(Backtesting 결과 - 밀크(MLK))

 

위에서 보듯이 총 4번의 거래가 이루어졌고 누적 수익률은 21%이다.

밀크가 오름세에 있기도 했지만 그래도 꽤 안정적이고 높은 수익률이 나왔다.

 

다음은 베이지어텐션토큰(BAT)에 대한 backtesting 결과이다.

Backtesting 결과 - 베이직어텐션토큰(BAT)
(Backtesting 결과 - 베이직어텐션토큰(BAT))

이번에는 한 번의 거래가 있었고 수익률은 약 4.7%이다.

위의 그래프처럼 11월 28일 오후부터는 계속 하락세가 이어지고 있어서 수익을 내기 힘든 상황이었으나 이 거래 전략은 그래도 수익을 냈으며 EMA의 역할로 이후 거래를 하지 않아 물리지도 않았다.

 

 

[결 론]

Stochastic RSI와 50일 기준 EMA 그리고 150일 기준 EMA를 통한 가상 화폐 거래 전략을 파이썬으로 구현해보고 backtesting으로 누적 수익률을 알아봤다.

이 글을 쓰고 있는 현재 모든 가상 화폐가 큰 하락을 하고 있어서 다른 가상 화폐에 대해 backtesting을 해보지는 못했지만 이제까지 다른 전략에 비해 꽤 안정적일 것 같다는 생각이 든다.

위의 내용에서는 60분 기준으로 실행했지만 사실 이 전략은 주로 5분 기준이나 10분 기준으로 하는 단타에 좀 더 적합할 것이다.

그리고 이 전략이 안정적이란 느낌은 아마도 매도 기준을 단순하게 매수 가격의 몇 퍼센트 이상이라고 설정해서 일 수 있다.

보조 지표는 실제 가격의 변화보다는 느리기 때문에 보조 지표를 기준으로 매도의 조건 설정하면 예상했던 수익이 나오지 않거나 오히려 손해를 보는 거래를 할 수 있다.

 

이 전략은 매도의 조건인 수익률을 바꿔보면서 좀 더 테스트해보고 또한 갑작스러운 하락에 대처하기 위한 조건을 추가하면 실제 자동매매에 적용해 봐도 좋을 것 같다.

 

반응형

댓글