Relative Strength Index in python pandas

Viewed 103798

I am new to pandas. What is the best way to calculate the relative strength part in the RSI indicator in pandas? So far I got the following:

from pylab import *
import pandas as pd
import numpy as np



def Datapull(Stock):
    try:
        df = (pd.io.data.DataReader(Stock,'yahoo',start='01/01/2010'))
        return df
        print 'Retrieved', Stock
        time.sleep(5)
    except Exception, e:
        print 'Main Loop', str(e)


def RSIfun(price, n=14):
    delta = price['Close'].diff()
    #-----------
    dUp=
    dDown=

    RolUp=pd.rolling_mean(dUp, n)
    RolDown=pd.rolling_mean(dDown, n).abs()

    RS = RolUp / RolDown
    rsi= 100.0 - (100.0 / (1.0 + RS))
    return rsi

Stock='AAPL'
df=Datapull(Stock)
RSIfun(df)

Am I doing it correctly so far? I am having trouble with the difference part of the equation where you separate out upward and downward calculations

11 Answers

You can get a massive speed up of Bill's answer by using numba. 100 loops of 20k row series( regular = 113 seconds, numba = 0.28 seconds ). Numba excels with loops and arithmetic.

import numpy as np
import numba as nb

@nb.jit(fastmath=True, nopython=True)   
def calc_rsi( array, deltas, avg_gain, avg_loss, n ):

    # Use Wilder smoothing method
    up   = lambda x:  x if x > 0 else 0
    down = lambda x: -x if x < 0 else 0
    i = n+1
    for d in deltas[n+1:]:
        avg_gain = ((avg_gain * (n-1)) + up(d)) / n
        avg_loss = ((avg_loss * (n-1)) + down(d)) / n
        if avg_loss != 0:
            rs = avg_gain / avg_loss
            array[i] = 100 - (100 / (1 + rs))
        else:
            array[i] = 100
        i += 1

    return array

def get_rsi( array, n = 14 ):   

    deltas = np.append([0],np.diff(array))

    avg_gain =  np.sum(deltas[1:n+1].clip(min=0)) / n
    avg_loss = -np.sum(deltas[1:n+1].clip(max=0)) / n

    array = np.empty(deltas.shape[0])
    array.fill(np.nan)

    array = calc_rsi( array, deltas, avg_gain, avg_loss, n )
    return array

rsi = get_rsi( array or series, 14 )
rsi_Indictor(close,n_days):
    rsi_series = pd.DataFrame(close)


    # Change = close[i]-Change[i-1]
    rsi_series["Change"] = (rsi_series["Close"] - rsi_series["Close"].shift(1)).fillna(0)

    # Upword Movement
    rsi_series["Upword Movement"] = (rsi_series["Change"][rsi_series["Change"] >0])
    rsi_series["Upword Movement"] = rsi_series["Upword Movement"].fillna(0)

    # Downword Movement
    rsi_series["Downword Movement"] = (abs(rsi_series["Change"])[rsi_series["Change"] <0]).fillna(0)
    rsi_series["Downword Movement"] = rsi_series["Downword Movement"].fillna(0)

    #Average Upword Movement
    # For first Upword Movement Mean of first n elements.
    rsi_series["Average Upword Movement"] = 0.00
    rsi_series["Average Upword Movement"][n] = rsi_series["Upword Movement"][1:n+1].mean()

    # For Second onwords
    for i in range(n+1,len(rsi_series),1):
        #print(rsi_series["Average Upword Movement"][i-1],rsi_series["Upword Movement"][i])
        rsi_series["Average Upword Movement"][i] = (rsi_series["Average Upword Movement"][i-1]*(n-1)+rsi_series["Upword Movement"][i])/n

    #Average Downword Movement
    # For first Downword Movement Mean of first n elements.
    rsi_series["Average Downword Movement"] = 0.00
    rsi_series["Average Downword Movement"][n] = rsi_series["Downword Movement"][1:n+1].mean()

    # For Second onwords
    for i in range(n+1,len(rsi_series),1):
        #print(rsi_series["Average Downword Movement"][i-1],rsi_series["Downword Movement"][i])
        rsi_series["Average Downword Movement"][i] = (rsi_series["Average Downword Movement"][i-1]*(n-1)+rsi_series["Downword Movement"][i])/n

    #Relative Index
    rsi_series["Relative Strength"] = (rsi_series["Average Upword Movement"]/rsi_series["Average Downword Movement"]).fillna(0)

    #RSI
    rsi_series["RSI"] = 100 - 100/(rsi_series["Relative Strength"]+1)
    return rsi_series.round(2)     

For More Information

You do this using finta package as well just to add above

ref: https://github.com/peerchemist/finta/tree/master/examples

import pandas as pd
from finta import TA
import matplotlib.pyplot as plt

ohlc = pd.read_csv("C:\\WorkSpace\\Python\\ta-lib\\intraday_5min_IBM.csv", index_col="timestamp", parse_dates=True)
ohlc['RSI']= TA.RSI(ohlc)

It is not really necessary to calculate the mean, because after they are divided, you only need to calculate the sum, so we can use Series.cumsum ...

def rsi(serie, n):

    diff_serie = close.diff()
    cumsum_incr = diff_serie.where(lambda x: x.gt(0), 0).cumsum()
    cumsum_decr = diff_serie.where(lambda x: x.lt(0), 0).abs().cumsum()
    rs_serie = cumsum_incr.div(cumsum_decr)
    rsi = rs_serie.mul(100).div(rs_serie.add(1)).fillna(0)

    return rsi

Less code here but seems to work for me:

df['Change'] = (df['Close'].shift(-1)-df['Close']).shift(1)
df['ChangeAverage'] = df['Change'].rolling(window=2).mean()
df['ChangeAverage+'] = df.apply(lambda x: x['ChangeAverage'] if x['ChangeAverage'] > 0 else 0,axis=1).rolling(window=14).mean()
df['ChangeAverage-'] = df.apply(lambda x: x['ChangeAverage'] if x['ChangeAverage'] < 0 else 0,axis=1).rolling(window=14).mean()*-1
df['RSI'] = 100-(100/(1+(df['ChangeAverage+']/df['ChangeAverage-'])))
Related