스토캐스틱 오실레이터는 RSI(상대 강도 지수, Relative Strangth Index)와 궁합이 좋다고 알려져 있는데 이 두 가지를 합친 게 바로 스토캐스틱 RSI(Stochastic RSI)이다. 스토캐스틱 RSI는 투샤르 샹드(Tushar S. Chande)와 스탠리 크롤(Stanley Kroll)에 의해 만들어졌으며, 투샤르 샹드는 CMO(Chande Momentum Oscillator) AROON 등 다양한 지표를 만든 사람이며 투샤르 샹드와 스탠리 크롤은 샹드 크롤 스톱(Chande Kroll Stop)으로도 유명하다.
스토캐스틱 RSI는 고가, 저가, 종가 대신에 RSI를 이용하여 스토캐스틱 오실레이터를 만든다. RSI는 값이 하난데, 스토캐스틱 RSI는 입력해야 하는 값이 고가, 저가, 종가 총 3개다. 스토캐스틱 RSI는 이 3가지 값에 RSI를 동일하게 넣어 만든다. 스토캐스틱 슬로우의 형태로 RSI를 이동평균하여 사용하며, RSI가 민감하게 움직여 노이즈를 줄이기 위해 통상 14일 이동평균을 씌워 사용하던 것에 반해 스토캐스틱 RSI는 스토캐스틱 슬로우처럼 3일 이동평균으로 %K를 만들다 보니 RSI에 비해 민감하게 움직인다. 이는 스토캐스틱 RSI를 만든 목적이 RSI가 거래 신호를 내는 빈도가 너무 뜸하기에 민감도를 올리기 위해 만들어졌기 때문에 투샤르 샹드와 스탠리 크롤이 의도한 바이나, 이 말은 RSI에 비해 허위 신호를 낼 확률이 높다는 말과 같다. 단순히 민감도를 올리기 위해서면 RSI에 이동평균 3을 적용했으면 될 일 이긴 하나 계산 방식을 스토캐스틱 오실레이터와 동일하게 하며 일부 보완이 이뤄지고 추가로 %D를 산출하여 오실레이터로 활용한다.
매매 방법은 스토캐스틱과 유사하다. %K나 %D가 20 이하일 경우, 과매도, 80 이상일 경우, 과매수로 보고 20을 상향 돌파할 경우, 매수, 80을 하향 돌파할 경우 매도하는 전략이다. 단 %K를 사용할 경우, 신호 포착은 빠르나 거래가 잦아지거나 허위 시그널이 자주 발생 할 수 있다.
추세추종 전략으로 스토캐스틱 RSI가 기준선인 50를 상향 돌파하면 매수, 50을 하향 돌파하면 매도하는 전략을 취하기도 한다. 이 역시, %K를 사용할 경우, 시그널이 너무 자주 발생해 거래가 잦아져 수수료만 엄청 내는 경우가 발생할 수 있어 %D를 사용하곤 하는데, 추세추종 전략이다 보니 저점 매수, 고점 매도를 노리는 과매수, 과매도 전략보다 진입 시점이 느리고 기간을 적절히 조정해야 장기 추세에 대응할 수 있는데, 추세가 확실하지 않은 구간에서는 수익을 내기 힘들다.
%K가 %D를 상향 돌파하면 매수, %K가 %D를 하향 돌파하면 매도 전략을 취할 수도 있다. 이는 신호 포착이 굉장히 빠르고 단기적으로 가격 변호가 크거나, 추세를 이뤄 상승하거나 하락할 경우 굉장히 주효한 전략이다. 다만, 박스권 장세나 매일 가격이 오락가락 횡보하면 허위시그널이 엄청나게 발생하며 거래에 적용하기 어렵다. 이 경우, 크로스가 발생하는 구간을 지정하여 거래 진입을 하도록 하면(30 이하에서 골든 크로스는 매수 등) 이 문제는 일부 해결할 수 있다.
이 외에 %D 다이버전스라 하여 %D가 저점을 갱신하지 못하고 저점을 계속 올려간다면 상승 전환 시그널, %D가 고점을 갱신하지 못하고 고점을 계속 내려가면 하락 전환 시그널로 보기도 하나, 이는 계량화 하여 적용하기 어려워 시스템 트레이딩이나 시그널 스크리닝에는 적합하지 않다.
마지막으로 트레이딩 뷰 파인 스크립트 소스와 pandas-ta 소스를 공유하며 마친다.
- 스토캐스틱 RSI(Stochastic RSI) 트레이딩 뷰 파인 스크립트 지표 소스
//@version=5
indicator(title="Stochastic RSI", shorttitle="Stoch RSI", format=format.price, precision=2, timeframe="", timeframe_gaps=true)
import TradingView/ta/7 as ta7
import blackcat1402/pandas_ta/7 as pta
ma(source, length, _type) =>
switch _type
"SMA" => ta.sma(source, length)
"EMA" => ta.ema(source, length)
"DEMA" => ta7.dema(source,length)
"TEMA" => ta7.tema(source,length)
"FRAMA" => ta7.frama(source,length)
"T3" => ta7.t3(source,length)
"TRIMA" => ta7.trima(source,length)
"RMA" => ta.rma(source, length)
"WMA" => ta.wma(source, length)
"HMA" => ta.hma(source, length)
"VWMA" => ta.vwma(source * volume, length)
"ALMA" => pta.alma(source, length)
"JMA" => pta.jma(source, length)
"SINWMA" => pta.sinwma(source, length)
"FWMA" => pta.fwma(source, length)
"LINREG" => pta.linreg(source, length)
"SWMA" => pta.swma(source)
"YIDYA" => pta.vidya(source, length)
"VWAP" => pta.vwap(source)
"ZLMA" => pta.zlma(source, length)
src = input(close, title="RSI Source", group="RSI Settings")
lengthRSI = input.int(14, "RSI Length", group="RSI Settings", minval=1)
periodK = input.int(14, title="%K Length", group="%K Settings", minval=1)
smoothK = input.int(3, title="%K Smoothing", group="%K Settings", minval=1)
kmaTypeInput = input.string("SMA", title="%K MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="%K Settings", display = display.data_window)
periodD = input.int(3, title="%D Smoothing", group="%D Settings", minval=1)
dmaTypeInput = input.string("SMA", title="%K MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="%D Settings", display = display.data_window)
lower = input.int(80, title="Lower", group="Band Settings")
upper = input.int(20, title="Upper", group="Band Settings")
rsi1 = ta.rsi(src, lengthRSI)
k = ma(ta.stoch(rsi1, rsi1, rsi1, periodK), smoothK, kmaTypeInput)
d = ma(k, periodD, dmaTypeInput)
plot(k, title="%K", color=color.blue)
plot(d, title="%D", color=color.red)
upperBand = hline(upper, "Upper Band", color=color.gray)
hline(50, "Middle Band", linestyle=hline.style_dotted, color=color.gray)
lowerBand = hline(lower, "Lower Band", color=color.gray)
fill(upperBand, lowerBand, title="Background", color=color.new(color.gray, 80))
- 스토캐스틱 RSI(Stochastic RSI) 트레이딩 뷰 파인 스크립트 전략 소스
//@version=5
strategy(title="Stochastic RSI", shorttitle="Stoch RSI", margin_long=100, margin_short=100, default_qty_type=strategy.percent_of_equity, default_qty_value=50, commission_type=strategy.commission.percent, commission_value=0.2, pyramiding=0)
import TradingView/ta/7 as ta7
import blackcat1402/pandas_ta/7 as pta
ma(source, length, _type) =>
switch _type
"SMA" => ta.sma(source, length)
"EMA" => ta.ema(source, length)
"DEMA" => ta7.dema(source,length)
"TEMA" => ta7.tema(source,length)
"FRAMA" => ta7.frama(source,length)
"T3" => ta7.t3(source,length)
"TRIMA" => ta7.trima(source,length)
"RMA" => ta.rma(source, length)
"WMA" => ta.wma(source, length)
"HMA" => ta.hma(source, length)
"VWMA" => ta.vwma(source * volume, length)
"ALMA" => pta.alma(source, length)
"JMA" => pta.jma(source, length)
"SINWMA" => pta.sinwma(source, length)
"FWMA" => pta.fwma(source, length)
"LINREG" => pta.linreg(source, length)
"SWMA" => pta.swma(source)
"YIDYA" => pta.vidya(source, length)
"VWAP" => pta.vwap(source)
"ZLMA" => pta.zlma(source, length)
src = input(close, title="RSI Source", group="RSI Settings")
lengthRSI = input.int(14, "RSI Length", group="RSI Settings", minval=1)
periodK = input.int(14, title="%K Length", group="%K Settings", minval=1)
smoothK = input.int(3, title="%K Smoothing", group="%K Settings", minval=1)
kmaTypeInput = input.string("SMA", title="%K MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="%K Settings", display = display.data_window)
periodD = input.int(3, title="%D Smoothing", group="%D Settings", minval=1)
dmaTypeInput = input.string("SMA", title="%K MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="%D Settings", display = display.data_window)
lower = input.int(80, title="Lower", group="Band Settings")
upper = input.int(20, title="Upper", group="Band Settings")
indi = input.string("%K-Band", title="Strategy Indicator", options = ["%K-Band", "%D-Band", "%K-Mid", "%D-Mid", "%K-%D"])
rsi1 = ta.rsi(src, lengthRSI)
k = ma(ta.stoch(rsi1, rsi1, rsi1, periodK), smoothK, kmaTypeInput)
d = ma(k, periodD, dmaTypeInput)
plot(k, title="%K", color=color.blue)
plot(d, title="%D", color=color.red)
upperBand = hline(upper, "Upper Band", color=color.gray)
hline(50, "Middle Band", linestyle=hline.style_dotted, color=color.gray)
lowerBand = hline(lower, "Lower Band", color=color.gray)
fill(upperBand, lowerBand, title="Background", color=color.new(color.gray, 80))
startDate = input.time(defval=timestamp("01 Jan 1970 00:00 +0000"), group = "Test Range")
finishDate = input.time(defval=timestamp("31 Dec 2025 24:00 +0000"), group = "Test Range")
time_condition = time >= startDate and time <= finishDate
if(time_condition)
if (indi == "%K-Band")
if ta.crossover(k, lower)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(k, upper)
strategy.close_all('매도')
if (indi == "%D-Band")
if ta.crossover(d, lower)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(d, upper)
strategy.close_all('매도')
if (indi == "%K-Mid")
if ta.crossover(k, 50)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(k, 50)
strategy.close_all('매도')
if (indi == "%D-Mid")
if ta.crossover(d, 50)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(d, 50)
strategy.close_all('매도')
if (indi == "%K-%D")
if ta.crossover(k, d)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(k, d)
strategy.close_all('매도')
bgcolor(strategy.position_size > 0 ? color.new(color.yellow,90) : na)
- 스토캐스틱 RSI(Stochastic RSI) pandas-ta 소스
import pandas as pd
import pandas_ta as ta
import FinanceDataReader as fdr
data = fdr.DataReader('005930')
stochrsi = ta.stochrsi(close = data['Close'], length=14, rsi_length=14, k=3, d=3, mamode='SMA')
data = pd.concat([data, stochrsi], axis=1)
data.dropna(inplace=True)