본문 바로가기

Python/Investment

Python으로 RSI 계산 및 차트 생성

반응형

Python으로 RSI 계산 및 차트 생성

이번 포스트에서는 주식 투자에서 보조지표 중 하나인 RSI를 Python으로 계산하고 차트를 구현해보고자 한다.
RSI는 주가의 과매수 또는 과매도 상태를 판단하는데 도움을 주는 중요한 보조지표이다.
그럼 RSI가 무엇인지, 계산법 등을 알아보고 Python으로 구현해보자.

목차

RSI(Relative Strength Index)/Signal 이란?

✔ RSI

RSI는 J. Welles Wilder가 개발한 주식 보조 지표로서, 가격의 상승세와 하락세를 비교하여 주가의 과매수(overbought) 또는 과매도(oversold) 상태를 판단하는 도구이다.

RSI는 0에서 100까지의 범위로 표시되며, 주로 14일 기간을 기준으로 계산한다.
일반적으로 RSI 값이 70 이상이면 과매수로, 30 이하면 과매도로 판단된다.


✔ RSI Signal

RSI Signal은 RSI 지표에 적용되는 이동평균선으로, RSI 지표의 변동을 부드럽게 하여 보다 명확하게 추세를 파악하는데 도움을 준다.
일반적으로 RSI와 함께 사용되는 이동평균선은 9일 이동평균선이지만, 사용자의 선호나 전략에 따라 다른 기간을 설정할 수 있다.


RSI


RSI에 대한 자세한 내용 및 매매 방법은 아래 포스트에서 확인 부탁드린다.


RSI/Signal 계산 방법

✔ RSI 계산

1. 평균 이익(Average Gain)평균 손실(Average Loss)을 계산해야한다.
이 값들은 주어진 기간동안의 이익과 손실의 평균값이다.
예를 들어, 14일 동안의 RSI를 계산할 때, 14일 동안의 상승분(종가가 전일보다 높을 때의 변동값)과 하락분(종가가 전일보다 낮을 때의 변동값)의 평균을 계산한다.

\(\text{Average Gain}=(\text{최근 13일간의 이익 합}+\text{오늘의 이익})/14\)
\(\text{Average Loss }=(\text{최근 13일간의 손실 합}+\text{오늘의 손실})/14\)

이익은 주가가 상승한 날의 상승폭을, 손실은 주가가 하락한 날의 하락폭을 말하며, 이익 또는 손실이 없는 날은 0으로 계산한다.


2. 다음으로, 상대강도(RS, Relative Strength)를 계산한다.
는 평균 이익을 평균 손실로 나눈 값이다.

\(\text{RS} = \text{Average Gain} / \text{Average Loss}\)


3. 마지막으로 RSI를 계산한다.
이는 상대 강도를 기반으로 0에서 100 사이의 값으로 변환한 것이다.

\(\text{RSI} = 100 - (100 / (1 + \text{RS}))\)


✔ RSI Signal 계산

RSI Signal 라인은 일반적으로 RSI의 이동 평균을 사용하여 계산된다.
주로 사용하는 이동 평균 기간은 9일이다.

\(\text{RSI Signal} = \text{최근 9일간의 RSI 값의 평균}\)

Python으로 RSI/Signal 구현

pandas 라이브러리를 사용할 것이며, 샘플 데이터는 아래와 같다.
여기서 close 컬럼이 종가이며, date는 날짜 타입으로 변환해주었다.


샘플 데이터


import pandas as pd
df = pd.read_csv('../TEST.csv')
df['date'] = pd.to_datetime(df['date'])

✔ Python으로 RSI 계산

우선 일별 주가의 변동을 측정해야한다.
즉, 각 거래일의 종가와 이전 거래일의 종가 사이의 차이를 알아야 한다.
이 차이를 계산하기 위해 pandas의 diff 메서드를 사용하면 된다.


🔎 Pandas diff method

diff() 메서드는 DataFrame이나 Series에서 이전 행 또는 열과의 차이를 계산하는 데 사용된다.
이 메서드는 주로 시계열 데이터에서 변동률을 계산하거나, 연속적인 두 값 사이의 차이를 찾는 데 활용된다.

DataFrame.diff(periods=1, axis=0)
  • periods : 얼마나 떨어진 두 데이터 포인트 사이의 차이를 계산할지를 지정. 기본값은 1로, 이는 이전 행 또는 열과의 차이를 계산함을 의미한다.
  • axis : 어느 축을 따라 차이를 계산할지를 지정. axis=0 (기본값)은 행을 따라 계산하고, axis=1은 열을 따라 계산한다.

diff 메서드를 사용해서 각 거래일의 종가와 이전 거래일의 종가의 차이를 구하면 아래와 같다.
여기서 이 차이가 계산된 데이터프레임을 delta인 변수에 저장하겠다.

# 각 거래일 종가와 이전 거래일 종가 차이
delta = df['close'].diff()

이제 평균 이익과 평균 손실을 계산해야하는데, RSI의 경우 이익 또는 손실이 없는 날은 0으로 계산한다.
즉, diff로 계산된 결과가 0보다 크면 이익이 있는 것이고, 0보다 작으면 손실이 있는 것이다.
그리고 이익 또는 손실이 없으면 0으로 두면 된다.
이를 적용하기 위해 이번에는 pandas의 where 메서드를 사용하면 된다.



🔎 Pandas where method

where() 메서드는 DataFrame이나 Series의 값을 조건에 따라 선택적으로 변경하는 데 사용된다.
이 메서드는 주어진 조건이 참(True)인 경우 해당 데이터를 그대로 유지하고, 거짓(False)인 경우 지정한 다른 값으로 변경한다.
이런 방식으로 특정 조건을 만족하지 않는 데이터를 변경하거나 필터링하는 데 사용된다.

DataFrame.where(cond, other=nan)
  • cond : 각 요소에 적용할 조건을 지정하는 데 사용. 이 조건은 각 요소에 대해 참 또는 거짓을 반환하는 불리언 표현식이어야 한다.
  • other : 조건이 거짓(False)일 때 사용할 값을 지정하는 데 사용. 지정하지 않으면 기본적으로 NaN(결측치)가 사용된다.

where 메서드를 사용해서 이익/손실이 없는 데이터는 0으로 변환하고,
해당 데이터의 14일 이동평균을 구하면 평균이익, 평균손실을 구할 수 있다.
여기서는 pandas의 rolling 메서드를 사용하는데, rolling에 대한 내용은 이동평균선 관련 포스트에서 확인 부탁드린다.

# 평균이익/평균손실
avg_gain = gains.rolling(window=14).mean()
avg_loss = losses.rolling(window=14).mean()

평균 이익과 평균 손실을 구했으니 이제 상대강도(RS)를 계산하면 된다.
상대강도는 평균 이익을 평균 손실로 나눈 값이다.

# 상대강도
rs = avg_gain / avg_loss

마지막으로 RSI를 계산하면 되는데, 상대 강도를 기반으로 0에서 100 사이의 값으로 변환하면 된다.

# RSI
df['RSI'] = 100 - (100 / (1 + rs))

이렇게 계산된 RSI는 아래와 같다.


RSI


✔ Python으로 RSI Signal 구현

RSI를 구했다면 Signal을 간단하게 구현할 수 있다.
pandas의 rolling을 사용하면 된다.

# RSI Signal
df['RSI_Signal'] = df['RSI'].rolling(window=9).mean()

RSI Signal은 아래와 같다.


RSI Signal

RSI/Signal 차트 생성

이제 계산된 RSI와 Signal을 사용해서 차트를 생성해보자.
차트는 matplotlib를 사용할 것이다.

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.dates import DateFormatter

matplotlib의 dates 모듈은 차트에서 날짜와 관련된 라벨을 조정하고 포맷팅할 수 있게 해주는 모듈이다.
관련 내용은 이전 포스트인 볼린저 밴드 관련 포스트에 작성해두었으니 참고 부탁드린다.


차트 생성할 때 아래와 같은 내용을 반영할 것이다.
1. 종가선 차트 생성
2. RSI/Signal 차트 생성
3. 종가선은 초록색, RSI는 주황색, Signal은 보라색으로 설정
4. 일반적으로 RSI가 70 이상이면 과매수, 30 이하이면 과매도로 판단한다. 이를 위해 30과 70을 점선으로 그려줄 것이다.
5. 과매수와 과매도 구간의 배경색을 지정.
6. matplotlib.dates 모듈로 x 축의 라벨은 월단위로 변경할 것이다.


위 내용을 모두 구현한 코드는 아래와 같다.

# 차트 생성
fig, ax = plt.subplots(2, 1, figsize=(14, 10), sharex=True)

# 종가선 차트
ax[0].plot(df['date'], df['close'], label='Close', color='lightgreen')
ax[0].set_title('Close Price')
ax[0].legend()

# RSI/Signal 차트
ax[1].plot(df['date'], df['RSI'], label='RSI', color='sandybrown')
ax[1].plot(df['date'], df['RSI_Signal'], label='RSI Signal', color='plum')

# 과매도/과매수 구간 배경색 설정
ax[1].fill_between(df['date'], 70, df['RSI'], where=(df['RSI'] >= 70), color='lightcoral', alpha=0.4)
ax[1].fill_between(df['date'], 30, df['RSI'], where=(df['RSI'] <= 30), color='lightblue', alpha=0.4)

# 각 차트별 라인 컬러 설정
ax[1].axhline(0, linestyle='--', alpha=0.5, color='gray')
ax[1].axhline(20, linestyle='--', alpha=0.5, color='orange')
ax[1].axhline(30, linestyle='--', alpha=0.5, color='green')
ax[1].axhline(70, linestyle='--', alpha=0.5, color='green')
ax[1].axhline(80, linestyle='--', alpha=0.5, color='orange')
ax[1].axhline(100, linestyle='--', alpha=0.5, color='gray')
ax[1].set_title('RSI Value')
ax[1].legend()

# x-axis를 월단위로 변경
months = mdates.MonthLocator()  # every month
date_fmt = DateFormatter("%Y-%m")
ax[1].xaxis.set_major_locator(months)
ax[1].xaxis.set_major_formatter(date_fmt)

plt.tight_layout()
plt.show()



코드를 실행하면 아래와 같은 차트를 생성할 수 있다.


RSI/Signal 차트 구현


차트를 보면 주황색인 RSI가 70 이상인 구간에선 빨간색 배경을, 30 이하인 구간은 짧아서 잘 안 보이지만 파란색 배경이 되는 것을 확인해볼 수 있다.

해당 차트만 확인했을 때 과매도 구간에서 매수를 하고 그 다음 과매수 구간에서 매도를 했다면 수익을 낼 수 있었음을 볼 수 있다.
그러나 언제나 그렇듯, 이것만으로 매매를 결정하는 것은 위험하며, 다른 기술 지표들과 함께 사용하는 것이 좋다.


과매도에서 매수/과매수에서 매도

이렇게 해서 RSI와 RSI Signal을 Python 코드로 계산하고 차트를 생성하는 방법에 대해서 알아 보았다.
이동평균선, MACD, 볼린저 밴드를 계산하는 것에 비하면 RSI는 조금 복잡한 편이었던 것 같다.

커피 한 잔으로
저를 응원해주세요!
반응형

loading