RCI 지표를 활용한 트레이딩 전략(RCI Indicator Trading Strategy)

RCI 지표의 개념과 유래

RCI(Rank Correlation Index)는 주가의 순위 상관관계를 측정하는 지표로, 주가의 상대적인 강도를 평가하는 데 사용된다. RCI는 주가의 순위가 시간에 따라 어떻게 변화하는지를 분석하여 주가 모멘텀의 강도와 방향성을 파악할 수 있다.

RCI는 1970년대에 John Bogle이 개발한 지표로, 그는 Vanguard Group의 설립자로 잘 알려져 있다. Bogle은 주가의 상대적인 순위 변화를 통해 시장 트렌드를 파악할 수 있다는 아이디어에서 출발하여 RCI를 고안하였다.

RCI 지표 계산 방법

RCI는 다음과 같은 단계를 통해 계산된다:

  1. 주가를 일정 기간(예: 30일) 동안의 순위로 변환한다.
  2. 현재 순위와 n일 전(예: 20일) 순위 사이의 스피어만 순위 상관계수를 계산한다.
  3. 스피어만 순위 상관계수에 100을 곱하여 RCI 값을 산출한다.

RCI 값은 -100부터 100 사이의 범위를 가지며, 양수인 경우 주가 순위가 상승하는 추세를, 음수인 경우 주가 순위가 하락하는 추세를 나타낸다. RCI 값의 절대값이 클수록 순위 상관관계가 강하다는 것을 의미한다.

예를 들어, 30일간의 주가 데이터를 사용하여 RCI를 계산한다고 가정해보자. 오늘의 주가 순위가 10위이고 20일 전의 순위가 30위였다면, 주가 순위가 상승한 것으로 볼 수 있다. 이 경우 RCI 값은 양수가 될 것이다.

RCI 지표를 활용한 매매 전략

RCI 지표를 활용한 매매 전략은 다음과 같이 구성할 수 있다:

  1. RCI 값의 상대적 수준을 활용한 전략:
  • RCI 값이 양수이고 일정 기준값(예: 50) 이상일 때 강한 상승 추세로 판단하여 매수한다.
  • RCI 값이 음수이고 일정 기준값(예: -50) 이하일 때 강한 하락 추세로 판단하여 매도한다.
  1. RCI 값의 변화를 포착하는 전략:
  • RCI 값이 음수에서 양수로 전환될 때 상승 추세 전환 신호로 해석하여 매수한다.
  • RCI 값이 양수에서 음수로 전환될 때 하락 추세 전환 신호로 해석하여 매도한다.
  1. RCI 값의 다이버전스를 활용한 전략:
  • 주가는 신고가를 갱신하는데 RCI 값은 이전 고점을 하회할 때 베어리시 다이버전스로 해석하여 매도 신호로 활용한다.
  • 주가는 신저가를 갱신하는데 RCI 값은 이전 저점을 상회할 때 불리시 다이버전스로 해석하여 매수 신호로 활용한다.

RCI 지표는 주가의 상대적인 순위 변화를 통해 추세를 파악하는 데 유용하지만, 다른 기술적 지표나 시장 상황과 함께 종합적으로 분석하는 것이 중요하다. 또한 순위 변화에 기반한 지표이므로 시장 전체의 동향과 개별 종목의 특성을 함께 고려해야 한다.

파이썬을 활용한 RCI 지표 백테스팅

파이썬을 사용하여 RCI 지표 기반의 트레이딩 전략을 백테스팅해보자. 다음 라이브러리를 활용할 것이다:

  • pandas: 데이터 조작 및 분석
  • numpy: 수치 계산
  • matplotlib: 데이터 시각화
  • scipy.stats: 상관계수 계산
  • FinanceDataReader: 주가 데이터 수집

먼저 가상환경을 활성화하고 필요한 라이브러리를 설치한다.

source venv/bin/activate
pip install pandas numpy matplotlib scipy FinanceDataReader

FinanceDataReader를 설치하면 종속 모듈도 함께 설치된다.

다음으로 RCI 지표를 계산하고 매매 신호를 생성하는 코드를 작성해보자.

import FinanceDataReader as fdr
import pandas as pd
import numpy as np
from scipy.stats import spearmanr
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)

# RCI 지표 계산 함수
def calculate_rci(data, period=30, n=20):
    data['rank'] = data['Close'].rolling(window=period).apply(lambda x: pd.Series(x).rank().iloc[-1])
    data['rci'] = data['rank'].rolling(window=n).apply(lambda x: spearmanr(x, range(n))[0]) * 100
    return data

# 매매 신호 생성 함수
def get_signals(data, upper_threshold=50, lower_threshold=-50):
    signals = pd.DataFrame(index=data.index)
    signals['signal'] = 0
    signals['signal'][data['rci'] > upper_threshold] = 1
    signals['signal'][data['rci'] < lower_threshold] = -1
    signals['positions'] = signals['signal'].diff()
    return signals

# RCI 지표 계산
rci_data = calculate_rci(data)

# 매매 신호 생성
signals = get_signals(rci_data)

# 백테스팅
initial_capital = 10000
positions = pd.DataFrame(index=signals.index).fillna(0.0)
positions[symbol] = 100 * signals['signal']

portfolio = positions.multiply(rci_data['Adj Close'], axis=0)
pos_diff = positions.diff()

portfolio['holdings'] = (positions.multiply(rci_data['Adj Close'], axis=0)).sum(axis=1)
portfolio['cash'] = initial_capital - (pos_diff.multiply(rci_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': [3, 1, 1]})

ax1.plot(rci_data['Close'], label='Price')
ax1.set_ylabel('Price')
ax1.set_title('Price Chart')
ax1.legend(loc='upper left')

ax2.plot(rci_data['rci'], label='RCI')
ax2.axhline(y=50, color='r', linestyle='--', label='Overbought')
ax2.axhline(y=-50, color='g', linestyle='--', label='Oversold')
ax2.set_ylabel('RCI')
ax2.set_title('Rank Correlation Index (RCI)')
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_rci 함수는 주어진 데이터를 바탕으로 RCI 지표를 계산한다. 먼저 주가를 일정 기간(기본값: 30일) 동안의 순위로 변환하고, 현재 순위와 n일 전(기본값: 20일) 순위 사이의 스피어만 순위 상관계수를 계산하여 RCI 값을 산출한다.

get_signals 함수는 계산된 RCI 지표를 기준으로 매매 신호를 생성한다. RCI 값이 상단 임계값(기본값: 50)을 초과할 때 매수 신호(1)를, 하단 임계값(기본값: -50)을 하회할 때 매도 신호(-1)를 생성한다.

백테스팅을 위해 초기 자본금을 10,000달러로 설정하고, 생성된 매매 신호를 바탕으로 포트폴리오 가치를 계산한다. 최종적으로 총 수익률과 포트폴리오 가치를 출력한다.

matplotlib을 사용하여 주가, RCI 지표, 포트폴리오 가치를 시각화한다. RCI 지표 차트에는 과매수(50) 및 과매도(-50) 기준선을 표시하여 매매 신호 발생 구간을 나타낸다.

코드 실행 중 발생할 수 있는 오류를 처리하기 위해 try-except 문을 사용하였다. 데이터를 가져오는 과정에서 오류가 발생하면 에러 메시지를 출력하고 프로그램을 종료한다.

Leave a Reply

error: Content is protected !!