데이터 생존 로그

Python으로 이해하는 주가지표① - RSI(Relative Strength Index) (feat. 엔비디아) 본문

생존 도구🏹

Python으로 이해하는 주가지표① - RSI(Relative Strength Index) (feat. 엔비디아)

분석가 베어그릴스 2024. 3. 3. 14:17
RSI(Relative Strength Index, 상대강도지수)라는 주가 지표를
파이썬을 통해 계산해보고 직관적으로 이해해봅시다.

 

RSI란 무엇일까?

RSI는 가격의 상승압력과 하락압력 간의 상대적인 강도를 나타낸다고 한다. (출처)

(무슨 말인지 직관적으로 이해가 잘 안되지만.. 일단 pass)

 

RSI는 그 종목이 과매수 상태인지 과매도 상태인지를 판단할 때 사용한다.

일반적으로 RSI가 70% 이상이면 과매수, 30% 이하면 과매도로 판단한다.

 

RSI는 다음과 같은 복잡한 식으로 표현된다.

RSI = 100 - 100 / (1+RS)
RS = (평균 이득) / (평균 손실)

 

위 식을 파이썬으로 나타내보고 지표를 직관적으로 이해해보고,

엔비디아 주가에서 RSI를 뽑아보자!


RSI 뽑아보기

RSI를 뽑기 위한 랜덤 데이터를 만들어보자. 

다음은 2024년 1월 1일부터 20일 간 랜덤으로 주가 데이터를 만드는 코드이다.

import pandas as pd
import numpy as np

# 예시 데이터 생성
np.random.seed(0)  # 결과 일관성을 위해 시드 설정
dates = pd.date_range('2024-01-01', periods=20)  # 20일간의 날짜
prices = np.random.normal(100, 10, size=len(dates))  # 10에서 100사이의 값만 가지도록

# 데이터프레임 생성
df = pd.DataFrame({'Date': dates, 'Price': prices})

 

만들어진 데이터로 이전 날짜 대비 증감을 계산해보도록 하자.

# 가격 변동 계산
df['Price Change'] = df['Price'].diff()

 

Price Change라는 값은 날짜 증감을 담고 있다.

 

다음은 이득과 손실을 계산해보자.

증감이 0보다 큰지 여부에 따라 값을 그대로 가져오면 된다.

# 이득과 손실 계산
df['Gain'] = df['Price Change'].apply(lambda x: x if x > 0 else 0)
df['Loss'] = df['Price Change'].apply(lambda x: -x if x < 0 else 0)
df

 

다음으로, 평균 이득과 평균 손실을 계산해야하는데,

일반적으로 14일을 기준으로 평균을 계산한다고 한다.

# 평균 이득과 평균 손실 계산 (14일 기준)
window = 14
df['Avg Gain'] = df['Gain'].rolling(window=window).mean()
df['Avg Loss'] = df['Loss'].rolling(window=window).mean()

14일 평균으로 계산했기 때문에 13행(index = 12)까지는 값이 없다.

 

이번엔 RS를 뽑아보자!

# RS 계산
df['RS'] = df['Avg Gain'] / df['Avg Loss']
df

 

RS는 1을 기점으로 분포되어있다.

  • 평균 이득이 더 크다면 1보다 큰 수를 가지고
  • 평균 손실이 더 크다면 1보다 작은 수를 가진다.

 

따라서,

  • RS가 큰 값을 가질수록 최근 14일간 손실보다 이득이 더 크다는 것을 의미하고
  • RS가 작은 값을 가질수록 최근 14일간 이득보다 손실이 더 크다는 것을 의미한다!

 

최종적으로 우리의 목표인 RSI를 구해보자!

# RSI 계산
df['RSI'] = 100 - (100 / (1 + df['RS']))
df

 

RSI가 30 ~ 70 사이에 존재하므로, 과매도 혹은 과매수 구간은 아닌 것으로 보인다.

 

그럼 RSI의 의미를 살펴볼까?

  • 최근 14일의 평균 이득과 손실이 같다면, RS = 1이다.
  • RS = 1이라면 RSI는 50이 된다.
  • 즉, RSI가 50이라면 평균 이득과 평균 손실이 동일한 상태임을 의미한다!

 

자 이번엔 RSI의 기준이었던 30과 70의 의미를 역산을 통해 알아보면

  • RSI = 30이라면, RS = 3 / 7이어야한다.
    • 따라서, 평균 이득 : 평균 손실 = 3 : 7이어야 하며,
    • 이는 평균 이득보다 평균 손실이 2.3배 이상일 때를 의미한다!
  • RSI = 70이라면, RS = 7 / 3이어야 한다.
    • 따라서, 평균 이득 : 평균 손실 = 7 : 3이어야 하며,
    • 이는 평균 손실보다 평균 이득이 2.3배 이상일 때를 의미한다!

엔비디아 주가에서 RSI 뽑아보기

요즘 엔비디아 주가는 연일 최고치를 경신하고 있다.

(곧 조정이 오겠지... 라며 구경만 하다가 못들어간 사람이 바로 나다🥲)

 

yfinance를 통해 엔비디아 주가를 불러와서 RSI를 확인해보자!

(yfinance가 무엇인지 모르겠다면? → 링크 클릭해보기!)

import yfinance as yfinance
import matplotlib.pyplot as plt

# 2023년 12월 ~ 24년 2월
nvidia_data = yfinance.download('NVDA', start='2023-12-01', end='2024-02-29')

# RSI 집계하는 함수
def calculate_rsi(data, window=14):
    delta = data['Adj Close'].diff() # 수정 종가 기준으로 집계
    gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()

    rs = gain / loss
    rsi = 100 - (100 / (1 + rs))
    return rsi
    
nvidia_data['RSI'] = calculate_rsi(nvidia_data)

fig, ax = plt.subplots(2, 1, figsize=(14, 10))

# 주가 그래프
ax[0].plot(nvidia_data.index, nvidia_data['Adj Close'], label='NVDA Adjusted Close', color='blue')
ax[0].set_title('NVIDIA Adjusted Close Price')
ax[0].set_ylabel('Price ($)')
ax[0].legend()

# RSI 그래프
ax[1].plot(nvidia_data.index, nvidia_data['RSI'], label='RSI', color='purple')
ax[1].axhline(70, linestyle='--', color='red', label='Overbought (70)')
ax[1].axhline(30, linestyle='--', color='green', label='Oversold (30)')
ax[1].set_title('NVIDIA RSI')
ax[1].set_ylabel('RSI')
ax[1].set_ylim(0, 100)
ax[1].legend()

plt.tight_layout()
plt.show()

 

 

RSI에 따르면,

  • 1월 11일에 80을 기록하며 과매수 구간에 진입
  • 2월 20일에 65를 기록하며 과매수 구간 탈출
  • 그 이후 70 선을 횡보

함을 알 수 있다!


요약

  • RSI를 통해 현재 시점이 과매수 혹은 과매도 구간인지 판단할 수 있다.
  • RSI는 최근 n일 간 평균 이득과 평균 손실의 비율을 나타내며
  • 2.3배를 기준으로 과매도, 과매수를 판단한다.
  • 엔비디아는 광기다
반응형
Comments