스토캐스틱RSI(Stochastic RSI)는 RSI(Relative Strength Index) 지표를 활용하여 개발된 모멘텀 오실레이터로, 주가의 상대적 강도를 측정하는 데 사용된다. 이 지표는 RSI 값의 스토캐스틱 계산을 통해 도출되며, 주가의 과매수 및 과매도 상태를 파악하는 데 유용하다.
스토캐스틱RSI의 계산 방법은 다음과 같다:
- 일정 기간(일반적으로 14일) 동안의 RSI 값을 구한다.
- 스토캐스틱 계산을 위해 일정 기간(일반적으로 14일) 동안의 RSI 값 중 최저값과 최고값을 구한다.
- 스토캐스틱RSI = (현재 RSI 값 – 최저 RSI 값) / (최고 RSI 값 – 최저 RSI 값) × 100
예를 들어, 14일 동안의 RSI 값이 다음과 같다고 가정해보자:
50, 60, 70, 80, 90, 80, 70, 60, 50, 40, 30, 20, 30, 40
이 경우 최저 RSI 값은 20, 최고 RSI 값은 90이다. 현재 RSI 값이 40이라면,
스토캐스틱RSI = (40 – 20) / (90 – 20) × 100 = 28.57이 된다.
스토캐스틱RSI는 0에서 100 사이의 값을 가지며, 일반적으로 80 이상은 과매수 구간, 20 이하는 과매도 구간으로 해석된다.
스토캐스틱RSI를 활용한 매매 전략은 다음과 같이 구성할 수 있다:
- 과매수/과매도 구간 활용: 스토캐스틱RSI가 80 이상일 때 매도하고, 20 이하일 때 매수한다.
- 시그널 라인 crossover 전략: 스토캐스틱RSI와 시그널 라인(스토캐스틱RSI의 이동평균)의 crossover를 활용하여, golden cross 발생 시 매수, dead cross 발생 시 매도한다.
- 다이버전스 전략: 주가는 신고가를 경신하는데 스토캐스틱RSI는 신고가를 경신하지 못할 때(bearish divergence) 매도하고, 주가는 신저가를 경신하는데 스토캐스틱RSI는 신저가를 경신하지 못할 때(bullish divergence) 매수한다.
파이썬을 활용하여 스토캐스틱RSI 기반의 트레이딩 전략을 백테스팅해보자. 다음 라이브러리를 활용할 것이다:
pandas
: 데이터 조작 및 분석numpy
: 수치 계산matplotlib
: 데이터 시각화FinanceDataReader
: 주가 데이터 수집
먼저 가상환경을 활성화하고 필요한 라이브러리를 설치한다.
source venv/bin/activate
pip install pandas numpy matplotlib FinanceDataReader
FinanceDataReader를 설치하면 종속 모듈도 함께 설치된다.
다음으로 스토캐스틱RSI를 계산하고 매매 신호를 생성하는 코드를 작성해보자.
import FinanceDataReader as fdr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 데이터 가져오기
symbol = "VOO"
start_date = "1900-01-01"
end_date = "2023-05-01"
try:
data = fdr.DataReader(symbol, start_date, end_date)
except Exception as e:
print(f"Error occurred while fetching data: {e}")
exit(1)
# RSI 계산 함수
def calculate_rsi(data, period=14):
delta = data['Close'].diff()
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
avg_gain = gain.rolling(window=period).mean()
avg_loss = loss.rolling(window=period).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
# 스토캐스틱RSI 계산 함수
def calculate_stochastic_rsi(rsi, period=14):
stoch_rsi = (rsi - rsi.rolling(window=period).min()) / (rsi.rolling(window=period).max() - rsi.rolling(window=period).min()) * 100
return stoch_rsi
# 매매 신호 생성 함수
def get_signals(stoch_rsi, threshold_buy=20, threshold_sell=80):
signals = pd.DataFrame(index=stoch_rsi.index)
signals['signal'] = 0
signals['signal'][stoch_rsi > threshold_sell] = -1
signals['signal'][stoch_rsi < threshold_buy] = 1
signals['positions'] = signals['signal'].diff()
return signals
# RSI 계산
rsi = calculate_rsi(data)
# 스토캐스틱RSI 계산
stoch_rsi = calculate_stochastic_rsi(rsi)
# 매매 신호 생성
signals = get_signals(stoch_rsi)
# 백테스팅
initial_capital = 10000
positions = pd.DataFrame(index=signals.index).fillna(0.0)
positions[symbol] = 100 * signals['signal']
portfolio = positions.multiply(data['Adj Close'], axis=0)
pos_diff = positions.diff()
portfolio['holdings'] = (positions.multiply(data['Adj Close'], axis=0)).sum(axis=1)
portfolio['cash'] = initial_capital - (pos_diff.multiply(data['Adj Close'], axis=0)).sum(axis=1).cumsum()
portfolio['total'] = portfolio['cash'] + portfolio['holdings']
portfolio['returns'] = portfolio['total'].pct_change()
# 결과 출력
print(f"Final portfolio value: ${portfolio['total'][-1]:,.2f}")
print(f"Total returns: {100 * (portfolio['total'][-1] / initial_capital - 1):.2f}%")
# 시각화
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(15, 15), gridspec_kw={'height_ratios': [2, 1, 1]})
ax1.plot(data['Close'], label='Price')
ax1.set_ylabel('Price')
ax1.set_title('Price Chart')
ax1.legend(loc='upper left')
ax2.plot(stoch_rsi, label='Stochastic RSI')
ax2.axhline(y=80, color='r', linestyle='--', label='Overbought')
ax2.axhline(y=20, color='g', linestyle='--', label='Oversold')
ax2.set_ylabel('Stochastic RSI')
ax2.set_title('Stochastic RSI')
ax2.legend(loc='upper left')
ax3.plot(portfolio['total'], label='Portfolio Value')
ax3.set_ylabel('Portfolio Value')
ax3.set_title('Portfolio Performance')
ax3.legend(loc='upper left')
plt.tight_layout()
plt.show()
위 코드에서는 FinanceDataReader를 사용하여 VOO ETF의 전체 기간 주가 데이터를 가져온다. start_date
를 가능한 한 오래전 날짜로 설정하여 모든 데이터를 가져올 수 있도록 한다.
calculate_rsi
함수는 주어진 데이터를 바탕으로 RSI를 계산하고, calculate_stochastic_rsi
함수는 RSI 값을 활용하여 스토캐스틱RSI를 계산한다.
get_signals
함수는 계산된 스토캐스틱RSI를 기준으로 매매 신호를 생성한다. 스토캐스틱RSI가 과매수 임계값(기본값: 80)을 초과할 때 매도 신호(-1)를, 과매도 임계값(기본값: 20)을 하회할 때 매수 신호(1)를 생성한다.
백테스팅을 위해 초기 자본금을 10,000달러로 설정하고, 생성된 매매 신호를 바탕으로 포트폴리오 가치를 계산한다. 최종적으로 총 수익률과 포트폴리오 가치를 출력한다.
matplotlib
을 사용하여 주가, 스토캐스틱RSI, 포트폴리오 가치를 시각화한다. 스토캐스틱RSI 차트에는 과매수(80)와 과매도(20) 기준선을 표시하여 매매 신호 발생 구간을 나타낸다.
코드 실행 중 발생할 수 있는 오류를 처리하기 위해 try-except
문을 사용하였다. 데이터를 가져오는 과정에서 오류가 발생하면 에러 메시지를 출력하고 프로그램을 종료한다.