Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Why the RSI values are so different between those from buil-in factor RSI and those talib.RSI() ?

Hi,

Since monthes I use normally only the built-in factor in my algos, especially the RSI. But I have such a feeling that the RSI values from the built-in factor RSI are quite different from the google finance or stockcharts.com. So I came to the idea that I will compare these between built-in factor RSI and talib.RSI(). I have they are indeed quite different. See my attached algo.

The logs look as follow:

2016-09-02 14:45 before_trading_start:55 INFO Original DF:  
                         rsi      sma  
Equity(24 [AAPL])  24.517906  106.517  
2016-09-02 14:45 before_trading_start:63 INFO SMA_History of Equity(24 [AAPL]):  
[     nan      nan      nan      nan  108.116  108.254  108.48   108.594
  108.834  109.064  109.304  109.104  108.992  108.758  108.458  107.974  
  107.638  107.072  106.688  106.517]  
2016-09-02 14:45 before_trading_start:67 INFO RSI_History of Equity(24 [AAPL]):  
[         nan          nan          nan          nan          nan
          nan          nan          nan          nan          nan  
          nan          nan          nan          nan  44.20908039  
  45.84502937  48.98400094  50.2131313   52.03214827  49.58981461  
  57.15269859  57.05617064  60.84495889  60.94957286  61.17300745  
  58.13984522  53.85984608  47.30223289  44.41672822  65.77895053  
  68.69926505  68.05731591  71.72294723  64.8843848   67.66132754  
  68.97924375  71.92860175  73.47560324  74.22350642  70.2940245  
  69.9493768   70.46183096  73.31679819  72.63969834  71.57195214  
  70.8245212   71.57055425  65.98921827  67.01982495  62.0230344  
  59.31041463  55.58684017  54.89098942  50.20922893  50.86178131  
  54.17640506]  
2016-09-02 16:30 my_rebalance:82 INFO SMA_History of Equity(24 [AAPL]):  
[     nan      nan      nan      nan  108.254  108.48   108.594  108.834
  109.064  109.304  109.104  108.992  108.758  108.458  107.974  107.638  
  107.072  106.688  106.517  106.624]  
2016-09-02 16:30 my_rebalance:86 INFO RSI_History of Equity(24 [AAPL]):  
[         nan          nan          nan          nan          nan
          nan          nan          nan          nan          nan  
          nan          nan          nan          nan  44.05133038  
  47.1697219   48.39481278  50.21202161  47.93656298  55.51687751  
  55.42587607  59.25253817  59.35848099  59.58480485  56.705531  
  52.62965692  46.35504735  43.58259685  65.00247732  67.95589498  
  67.32787713  71.04066051  64.32695203  67.12502459  68.45433739  
  71.43239699  72.99622272  73.75269687  69.87216886  69.53168746  
  70.04813652  72.92733585  72.257464    71.20097911  70.46133152  
  71.21278146  65.68559189  66.72063318  61.76725737  59.07678772  
  55.38199149  54.69130987  50.0426168   50.69513134  54.01047096  
  57.74224603]  
End of logs.  
12 responses

One can tell, the values of SMA, no matter from built-in factor SimpleMovingAverage or from talib.SMA, they are identical. B ut those of RSI are quite different.

Can someone tell?

Cheers

Thomas

It appears that the built-in RSI factor is computed using Cutler's method rather than using the more standard Welles Wilder version.

Hi mhp,

Thanks for the reply. I also realized the cause of the difference. I created an own custom factor which use the Wlider's version. But it seems not the same as that from talib.RSI. I notice, the talib.RSI values are (alomost) identical with those from stockchart.com and yahoo.com/finance.

Maybe you know where to get out the source code of talib.RSI?

Cheers

Here's what I came up with for wrapping a CustomFactor around the TALIB RSI function:

class WRSI(CustomFactor):  
    # Relative Strength Index using Wilder's smoothing  
    # (default RSI factor is Cutler's RSI which uses SMA smoothing)  
    inputs = (USEquityPricing.close,)  
    params = {'rsi_len' : 14,}  
    window_length = 80 # allow at least 4 times RSI len for proper smoothing  
    def compute(self, today, assets, out, close, rsi_len):  
        # TODO: figure out how this can be coded without a loop  
        rsi = []  
        for col in close.T:  
            try:  
                rsi.append(talib.RSI(col, timeperiod=rsi_len)[-1])  
            except:  
                rsi.append(np.nan)  
        out[:] = rsi  

Hi mhp,

Many thanks to your WRSI. It's wondeful! :-)

Cheers

Hi mhp,

Sorry, a question, if I want to calculate the RSI of 10 or other length, how should I call the factor? I try followings but it failt:

... my_wrsi_10 = WRSI(params= {'rsi_len' : 10})
...

Cheers

This is how to do it:

...my_wrsi_10 = WRSI(rsi_len=10)

For a very tiny speed boost you could also shorten the window length in this case, thus:

...my_wrsi_10 = WRSI(rsi_len=10, window_length=40)

Keep in mind that the "length" in Wilder's smoothing is equivalent to 2*length-1 of typical EMA smoothing, which is why I recommend 4X as minimum.

If you want to go nuts with this, download this sample worksheet and experiment with how many extra bars are actually needed to make today's value stable.

Hi,

Best thanks!

After much trial and error, I've isolated the difficulty I was having running longer backtests without getting memory errors to the WRSI function as coded above. I have no idea why wrapping a talib function in this way would cause a memory leak, but apparently it does.

In any case, I wanted to find a better way to code it, based on the RSI factor in the zipline source code, which looks like this:

class RSI(CustomFactor, SingleInputMixin):  
    """  
    Relative Strength Index  

    **Default Inputs**: [USEquityPricing.close]  

    **Default Window Length**: 15  
    """  
    window_length = 15  
    inputs = (USEquityPricing.close,)  

    def compute(self, today, assets, out, closes):  
        diffs = diff(closes, axis=0)  
        ups = nanmean(clip(diffs, 0, inf), axis=0)  
        downs = abs(nanmean(clip(diffs, -inf, 0), axis=0))  
        return evaluate(  
            "100 - (100 / (1 + (ups / downs)))",  
            local_dict={'ups': ups, 'downs': downs},  
            global_dict={},  
            out=out,  
        )

To convert this to Wilder's smoothing, I had to do 4 things: (a) add a 'rsi_len' parameter to allow the window length to be longer than the calculation length; (b) use np.average to compute the smoothing rather than nanmean; (c) get the decay_length right for Wilder's smoothing; (d) replace the numexpr evaluate call with something less efficient since Q algos are not allowed to import numexpr.

Here is the improved function:

class WRSI(CustomFactor):  
    # Relative Strength Index using Wilder's smoothing  
    # (default RSI factor is Cutler's RSI which uses SMA smoothing)  
    inputs = (USEquityPricing.close,)  
    params = {'rsi_len' : 14,}  
    window_length = 60 # allow at least 4 times RSI len for proper smoothing  
    def compute(self, today, assets, out, close, rsi_len):  
        diffs = np.diff(close, axis=0)  
        count = len(diffs)  
        decay_rate = (1.0 - (1.0 / rsi_len)) # Wilder's smoothing (different from standard EWMA)  
        weights = np.full(count, decay_rate, float) ** np.arange(count + 1, 1, -1)  
        ups = np.average(np.clip(diffs, 0, np.inf), axis=0, weights=weights)  
        downs = abs(np.average(np.clip(diffs, -np.inf, 0), axis=0, weights=weights))  
        out[:] = [100 - (100 / (1 + (up / down))) for up, down in zip(ups, downs)]  

Well, I'm still getting some memory errors after this code change. I guess they're just random, based on current server load. In any case, the new WRSI code is still better.

Hi mhp,

I wonder how and where have found out you get memory leak or memory erros?

Cheers

I mean do you get any error message by back testing?