머신러닝

암호화폐 상대강도지수 파이선 알고리듬:Python Cryptocurency RSI(Relative Strength Index)

coding art 2021. 7. 18. 14:40
728x90

바이낸스(Binance)의 오픈(public) API를 사용하여 그래픽 지원 라이브러리인 Plotly를 사용하여 인터액티브 한 가격 캔들 커브를 작성하였다. 오픈 API를 통해 입수되는 암호화폐의 시가, 저가, 고가 및 종가 데이터 중에서 종가 정보를 사용하여 앞서의 코드 이후에 셀을 추가하여 RSI(상대강도지수)를 계산해보자.

 

J. Welles Wilder 가 창시한 상대강도지수(Relative Strength Index) RSI는 증권이나 코인의 가격 움직임을 측정하기 위한 대표적인 모멘텀 지표(indicator)이다. 0~100 사이의 값을 가지는 RSI는 그 값이 70 이상이면 과매수(overbought) 상태이며 30이하이면 과매도(oversold) 상태로 정의한다.

1978년에 출판한 Wilder의 저서 “New Concepts in Technical Trading Systems”에서 Parabolic SAR, Average True Range, DAX(Directional MOvement Concepts) 지표에 관한 내용을 포함하고 있다. 본격적인 컴퓨터 시대 이전에 개발되었음에도 불구하고 시간에 따른 테스트에도 살아남아 지금은 대중적으로 잘 알려지게 되었다.

 

Wilder RSI 공식을 잘 이해하기 위해서는 시계열 데이터의 SMA(Simple Moving Average:단순이동평균)EMA(Exponential Moving Average) 에 관해서 사전 이해가 필요하다. SMA는 말 그대로 순차적인 N개의 데이터의 산술 평균을 뜻한다. 현재 시점의 시계열 데이터는 그에 앞선 시계열 데이터들로부터의 균일한 가중치 영향을 받게끔 산술 평균 처리한다. 반면에 EMA는 최근 시계열 데이터에 더 큰 가중치가 부여된다.

특히 EMA는 증권 가격 트렌드 분석에서 단기 50일선과 장기 200일선과의 교차 Golden Cross 또는 Death 출현에 따른 트렌드의 역전(reversal) 여부를 판단을 위한 기술적 분석에 흔히 사용되는 좀 지연이 심한 지표(lagging indicator)로 사용된다.

 

EMA 계산 공식을 살펴보자.

일반적으로 Smoothing2.0 으로 취한다. Days 값은 이동평균 계산에서 주로 5, 10, 50, 200 이 주로 사용 된다. 공식의 뒷 부분 괄호친 부분의 값은 대체로 1.0 보다 아주 작은 값이 되기 쉽다. 한편 앞 뒷부분 박스 친 부분들을 합하면 1.0 이 된다. 이 공식은 엔지니어링에서 흔히 사용되는 저주파 필터링 공식과 동일한 모양을 보여준다.

 

다음 그래프는 비트코인 1분봉 챠트 사례이다. 이동평균들이 서서히 변동되는 부드러운 곡선임에 반해 매도 매수를 결정하기 위한 RSI 곡선은 캔들의 급격한 변동을 잘 반영하고 있음을 알 수 있다. 특히 4시간봉 또는 일본의 RSI 곡선에서 70% 이상은 과매수 상태임을 뜻하고 30% 이하는 과매도 상태를 의미한다고 하므로 매도 및 매수의 판단 조건으로 사용된다.

캔들의 기민한 움직임을 잘 추적할 수 있는 RSI 지표를 계산해 보도록 하자. RSI 지표를 계산하기 위해서는 2종류의 평균값 즉 SMMA(U,n)SMMA(D,n)이 필요하다. n은 지금 현재를 기준으로 그 앞의 연속된 한 주기의 캔들 개수를 뜻한다. U(Up)n개의 캔들 중 연이은 2개의 캔들을 대상으로 상승이면 그 차이 값을, 하강이면 0을 취하도록 하고 모두 합산한 후에 n으로 나누어 평균값을 구한다. D(Down)는 그 반대이다. n개의 캔들 신호를 대상으로 (n-1)개의 SMMA(U,n)SMMA(D,n) 값을 계산할 수 있다. SMMASimplified or Modified Moving Average의 약어이다. 2종류의 평균값을 사용하여 RS(Relative Strength)를 정의하자.

 

RS를 사용하여 상대강도지표 RSI는 다음과 같이 표현된다.

처음 RSI 계산을 시작함에 있어서 자신을 포함하는 n 개의 캔들 신호에 대한 U(Up)값과 D(Down)값이 준비되어 있지 않다. 즉 초기 데이터들이 아예 없음을 뜻한다. 초기치 문제를 풀어야 할 경우 임의 값을 줄 수도 있으나 RSI 계산에 있어서는 첫 번째 단계만큼은 SMA(Simple Moving Average)를 사용하는 것 하나의 방법이다. 즉 초기치 문제에서는 첫 출발 단계에서 만큼은 어느 정도 오차 값이 있을 수밖에 없으나 캔들 데이터수가 늘어남에 따라 제대로 RSI 값들을 계산할 수 있을 것이다. 이런 방법으로 첫 단계 계산이 이루어지게 되면 그 다음 단계에서부터는 즉 i (i-1) 상태에 대해서 다음과 같이 SMMA(U,n)SMMA(D,n) 계산이 가능해진다. 이 계산 공식은 파이선에서 ewm.avg() 명령어를 사용하여 계산이 가능하다. 하지만 예를 들어 n=14를 적용할 경우 13개까지는 n=14를 충족하지 못하므로 계산 자체가 불가능하기에 파이선 ewm.avg()를 사용할 경우 NaN 이 출력 될 수밖에 없음에 유의하자.

한편 이 공식을 잘 살펴보면 계수들의 합이 1.0 이 되는 저주파 필터 공식임을 알 수 있다.

 

RSI 계산을 위한 computeRSI(data, time_window) 함수의 코드를 살펴보자.

파이선 명령 또는 pandas 명령이기도 한 diff()dropna()를 알아보자.

 

- diff(axis=n, periods=m): 지정 축(axis)을 기준으로 m번째 간격을 띤 요소와 차이 계산

axis=0 row, axis=1 column을 뜻한다.

- dropna(): 매트릭스의 row에서 Nat, None, NaN 이 들어 있는 항을 제거한다.

- diff = data.diff(1).dropna():

 

pandas DataFramedata에 대해서 periods=1 로 차이 값을 계산하되 그 결과가 Nat, None, NaN 이면 제외한다. data 의 요소 수가 500개라면 diff는 차이 값 계산 이므로 499개 가 된다. 본 블로그 끝 부분에 첨부된 computeRSI 코드는 다음 블로그 사이트에서 일부 참조하였다.

Predicting XRP (Ripple) Cryptocurrency Price with Python and Machine Learning

https://medium.com/analytics-vidhya/predicting-xrp-ripple-cryptocurrency-price-with-python-and-machine-learning-45a6b258f688

 

바이낸스 API를 사용하여 엔드포인트에서 반환 받는 데이터 수는 pandas DataFrame 기준500개로 고정 되어 있다. 이 중에서 종가(Close)만 뽑아내어 RSI를 계산해보자. RSI 계산에 필요한 차이값은 499개가 된다.

 

다음의 그림은 RSI 계산 과정 이해를 위해 변수 값들을 중간에 출력해 보았다.

499개의 차이 값을 대상으로 위와 같이 명령어 ewm.avg()을 사용하여 up_chg_avg down_chg_avg를 계산한다. 파라메터 값 time_window = 14가 사용된다.

아울러 다음의 파라메터 설명을 참조하자. Defaultadjust=False 이면 앞서 기술된 SMMA 계산 공식이 적용될 수 있으며 adjust=Trueα=(1/(1+com))의 고차 항들까지 고려가 된다.

이 명령에 따라 14번째 데이터를 사용하여 15번째 SMMA(U,14)를 계산해 보자. 14번째 데이터는 위 수치계산 사례에서 두 번째 컬럼에서의 diff 출력 값 중 +에 해당하는 값들을 14개 합하여 평균한 SMA 값이다. 15번째 SMMA 는 다음 계산을 통해 확인이 가능하다.

동일한 방식으로 UpDown SMMA 값 계산이 이루어지면 RSI 계산이 이루어질 수 있다. 계산이 완료된 RSI 컬럼 데이터를 따로 뜯어내어 별도로 pandas DataFrame r_df를 준비한 후 matplotlibiplot 기능을 사용하여 그래프를 출력하자. 그래프 속성이 캔들이 아닌 라인 그래프인 만큼 matplotlib 사용이 편리하다전체 500개 데이터 위치 중 그래프 전반 부 13NaN 만큼 비어 있음을 확인하자.

#compute RSI after exeucuting cryptocurrency_etc.ipynb

def computeRSI (datatime_window):

    diff = data.diff(1).dropna() # diff in one field(one day)

    print(diff[:15]) #499개 출력

    #this preservers dimensions off diff values

    up_chg = 0 * diff

    down_chg = 0 * diff

 

    # up change is equal to the positive difference, otherwise equal to zero

    up_chg[diff > 0] = diff[ diff>0 ]   

    # down change is equal to negative deifference, otherwise equal to zero

    down_chg[diff < 0] = diff[ diff < 0 ]

    

    # we set com=time_window-1 so we get decay alpha=1/time_window

    up_chg_avg = up_chg.ewm(com=time_window-1, min_periods=time_window, adjust=False).mean()

    down_chg_avg = down_chg.ewm(com=time_window-1, min_periods=time_window, adjust=False).mean()

    print(up_chg_avg[:15]) # NaN 포함 15개 출력

    rs = abs(up_chg_avg/down_chg_avg)

    rsi = 100 - 100/(1+rs)

    return rsi

 

#print(df[['Close']]) #500개 출력

c_df = pd.DataFrame(df[['Close']]) 

#c_df.plot()

c_df['rsi'] = computeRSI(c_df, 14)

r_df =pd.DataFrame(c_df[['rsi']])

print(r_df)

r_df.plot()