Relative Strength Index in python pandas Relative Strength Index in python pandas python python

Relative Strength Index in python pandas


It is important to note that there are various ways of defining the RSI. It is commonly defined in at least two ways: using a simple moving average (SMA) as above, or using an exponential moving average (EMA). Here's a code snippet that calculates both definitions of RSI and plots them for comparison. I'm discarding the first row after taking the difference, since it is always NaN by definition.

Note that when using EMA one has to be careful: since it includes a memory going back to the beginning of the data, the result depends on where you start! For this reason, typically people will add some data at the beginning, say 100 time steps, and then cut off the first 100 RSI values.

In the plot below, one can see the difference between the RSI calculated using SMA and EMA: the SMA one tends to be more sensitive. Note that the RSI based on EMA has its first finite value at the first time step (which is the second time step of the original period, due to discarding the first row), whereas the RSI based on SMA has its first finite value at the 14th time step. This is because by default rolling_mean() only returns a finite value once there are enough values to fill the window.

A comparison of the RSI calculated using exponential or simple moving average

import pandasimport pandas_datareader.data as webimport datetimeimport matplotlib.pyplot as plt# Window length for moving averagewindow_length = 14# Datesstart = '2010-01-01'end = '2013-01-27'# Get datadata = web.DataReader('AAPL', 'yahoo', start, end)# Get just the adjusted closeclose = data['Adj Close']# Get the difference in price from previous stepdelta = close.diff()# Get rid of the first row, which is NaN since it did not have a previous # row to calculate the differencesdelta = delta[1:] # Make the positive gains (up) and negative gains (down) Seriesup, down = delta.clip(lower=0), delta.clip(upper=0)# Calculate the EWMAroll_up1 = up.ewm(span=window_length).mean()roll_down1 = down.abs().ewm(span=window_length).mean()# Calculate the RSI based on EWMARS1 = roll_up1 / roll_down1RSI1 = 100.0 - (100.0 / (1.0 + RS1))# Calculate the SMAroll_up2 = up.rolling(window_length).mean()roll_down2 = down.abs().rolling(window_length).mean()# Calculate the RSI based on SMARS2 = roll_up2 / roll_down2RSI2 = 100.0 - (100.0 / (1.0 + RS2))# Compare graphicallyplt.figure(figsize=(8, 6))RSI1.plot()RSI2.plot()plt.legend(['RSI via EWMA', 'RSI via SMA'])plt.show()


dUp= delta[delta > 0]dDown= delta[delta < 0]

also you need something like:

RolUp = RolUp.reindex_like(delta, method='ffill')RolDown = RolDown.reindex_like(delta, method='ffill')

otherwise RS = RolUp / RolDown will not do what you desire

Edit: seems this is a more accurate way of RS calculation:

# dUp= delta[delta > 0]# dDown= delta[delta < 0]# dUp = dUp.reindex_like(delta, fill_value=0)# dDown = dDown.reindex_like(delta, fill_value=0)dUp, dDown = delta.copy(), delta.copy()dUp[dUp < 0] = 0dDown[dDown > 0] = 0RolUp = pd.rolling_mean(dUp, n)RolDown = pd.rolling_mean(dDown, n).abs()RS = RolUp / RolDown


My answer is tested on StockCharts sample data.

StockChart RSI info

def RSI(series, period):    delta = series.diff().dropna()    u = delta * 0    d = u.copy()    u[delta > 0] = delta[delta > 0]    d[delta < 0] = -delta[delta < 0]    u[u.index[period-1]] = np.mean( u[:period] ) #first value is sum of avg gains    u = u.drop(u.index[:(period-1)])    d[d.index[period-1]] = np.mean( d[:period] ) #first value is sum of avg losses    d = d.drop(d.index[:(period-1)])    rs = pd.DataFrame.ewm(u, com=period-1, adjust=False).mean() / \         pd.DataFrame.ewm(d, com=period-1, adjust=False).mean()    return 100 - 100 / (1 + rs)#sample data from StockChartsdata = pd.Series( [ 44.34, 44.09, 44.15, 43.61,                    44.33, 44.83, 45.10, 45.42,                    45.84, 46.08, 45.89, 46.03,                    45.61, 46.28, 46.28, 46.00,                    46.03, 46.41, 46.22, 45.64 ] )print RSI( data, 14 )#output14    70.46413515    66.24961916    66.48094217    69.34685318    66.29471319    57.915021