0

does this RSI code looks correct for you? RSI I'm getting with this code are very often touching oscillator peaks (0 or 100) when comparing to RSIs on different market data apps (TradingView etc.) they hardly ever do that.

Does this formula looks correct? I found it in quite old book Technical Traders Guide to Computer Analysis of the Futures Market. Also it returns almost identical results as RSI=100-(100-RS) where RS=AVG GAIN/AVG LOSS. But still I'm a bit mad that results are different comparing to RSIs available on the web...

# 
# Formula used:
#
# RSI(n) = 100 * (avg_up(n) / (avg_up(n)+avg_down(n)))
#
# Where:
#
# avg_up = average percentage gain from n-periods
# avg_down = average percentage loss from n-periods
# n = number of periods to calculate averages
#

def calculate_percentage_gains_and_losses(prices):
    
    percentage = {"gains":[0.0],
                  "losses":[0.0]}

    for i in range(len(prices)-1):
        
        diff=((float(prices[i + 1]) / float(prices[i]))*100)-100
        
        if diff>=0:
            percentage["gains"].append(diff)
            percentage["losses"].append(0.0)
        else:
            percentage["losses"].append(abs(diff))
            percentage["gains"].append(0.0)

    return percentage

def calculate_avg_percentage_gains_and_losses(prices, gains, losses, periods):

    avg_percentage = {"gains":[ 0.0 for x in range(periods - 1)], 
                      "losses":[ 0.0 for x in range(periods - 1)]}

    for i in range(periods,len(prices)+1):
            avg_percentage["gains"].append(sum(gains[i - periods:i]) / periods)
            avg_percentage["losses"].append(sum(losses[i - periods:i]) / periods)

    return avg_percentage

def calculate_relative_strength_index(prices, periods):

    percentage = calculate_percentage_gains_and_losses(prices)
    avg_percentage = calculate_avg_percentage_gains_and_losses(prices, percentage["gains"], percentage["losses"], periods)
    rsi_list=[0.0 for x in range(periods - 1)]

    for i in range(periods - 1, len(prices)):
        rsi = 100 * round((avg_percentage["gains"][i] / (avg_percentage["gains"][i] + avg_percentage["losses"][i])), 2)
        rsi_list.append(rsi)

    return rsi_list

EDIT Here is the code after adjustment

def calculate_percentage_gains_and_losses(prices):
    
    percentage = {"gains":[0.0],
                  "losses":[0.0]}

    for i in range(len(prices)-1):
        
        diff=((float(prices[i + 1]) / float(prices[i]))*100)-100
        
        if diff>=0:
            percentage["gains"].append(diff)
            percentage["losses"].append(0.0)
        else:
            percentage["losses"].append(abs(diff))
            percentage["gains"].append(0.0)

    return percentage

def calculate_smoothed_avg_percentage_gains_and_losses(prices, gains, losses, periods):

    avg_percentage = {"gains":[ 0.0 if i<(periods-1) else sum(gains[:periods]) / periods for i in range(periods)], 
                      "losses":[ 0.0 if i<(periods-1) else sum(losses[:periods]) / periods for i in range(periods)]}

    for i in range(periods, len(prices)):
        avg_percentage["gains"].append((gains[i] + (avg_percentage["gains"][i-1]* (periods-1))) / periods)
        avg_percentage["losses"].append((losses[i] + (avg_percentage["losses"][i-1]* (periods-1))) / periods)

    return avg_percentage

def calculate_relative_strength_index(prices, periods):

    percentage = calculate_percentage_gains_and_losses(prices)
    avg_percentage = calculate_smoothed_avg_percentage_gains_and_losses(prices, percentage["gains"], percentage["losses"], periods)
    rsi=[ 0.0 if i < (periods-1) else round((100 * (avg_percentage["gains"][i] / (avg_percentage["gains"][i] + avg_percentage["losses"][i]))),2) for i in range(len(prices))]

    return rsi
h0ax
  • 51
  • 6

1 Answers1

1

Firstly, issues with formula: your RSI formula is calculated differently to the usual. As you acknowledge, RS is normally calculated as (Avg gains)/(Avg losses) yet you use (Avg gains)/((Avg gains) + (Avg losses)). While there may be strong correlation between the two, they are two different formulas and will therefore give you different answers. Furthermore you use percentage gains/losses when most charting platforms use raw gains/losses. And finally, you use a simple average of the gains/losses (known as Cutler's RSI). The much more widely used Wells Wilder RSI uses a smoothed average given by: Avg gain = (gain + (prev gain) * (period -1))/period The upshot is you will get very different numbers.

Secondly, your code: While I have not scrutinized your code with a fine tooth comb, there are several issues rendering the code, at best, inefficient. If you intend on continuing to code in Python you MUST learn AND understand list comprehension and how to use it. It is what makes Python so useful and powerful. You have used it merely to initialize some lists. There are many websites and books that cover list comprehension quite extensively, just google it. Additionally, if you intend to use python to calculate series information on chronologically ordered data, I strongly recommend you import the pandas module and use DataFrames rather than lists. DataFrames are like database records that keeps all data neatly in the row they belong two and are easily manipulated. List will get messy as you try to line up the data.

I will not provide code at this point because I think you already have plenty to think a bout and work on but will happily help once you have understood the above.

Galo do Leste
  • 703
  • 5
  • 13
  • Thank you for the response. I will go through it today's evening. – h0ax Dec 14 '22 at 08:07
  • So switching to smoothed average allowed me to flat the RSI. By initial look curve looks much smoother. I will investigate further - RSI still differ. (I'm using Binance API to gather Futures BTCBUSD Pair to compare RSI with TradingView) – h0ax Dec 14 '22 at 18:29
  • I will apply more common RS formula and switch from percentage and lets see what will happen. Hopefully it will match with the reference. – h0ax Dec 14 '22 at 18:31
  • I'm sorry. I did a comparison with wrong time interval. Switching to smoothed average made RSI matching :)) I So that should be it! (I also checked how switching from percentage to just gains/losses is impacting the results but it's almost the same) – h0ax Dec 14 '22 at 18:42
  • I will give list comprehension a look, same with pandas. Could you please give me an example where do you see that list comprehetion could be used to improve the code? – h0ax Dec 14 '22 at 18:59
  • 1
    Glad you got RSI to match. List comprehensions are useful when you would normally need to use some loop to create a list (or any other multi-term container like dict or tuple for that matter). So when you use a for loop to create percentage gains/losses, the whole for loop could be replaced with a single list comprehension statement, eg: percentage["gain"] = [ max(prices[i]/prices[i-1]-1, 0) for i in range(1,len(prices)] – Galo do Leste Dec 18 '22 at 01:26
  • Yeah it's good looking to have it done with list comprehension :) I'm using it more often now, unfortunately it have some limits when more conditions are in play. Here is a nice one I make for SMA generation :) def sma(prices, n): return [sum(prices[:i+1]) / (i+1) if n>i else sum(prices[i+1-n:i+1]) / n for i in range(len(prices))] – h0ax Dec 18 '22 at 14:18
  • Excellent. Definitely there will be cases where limitations will necessitate for loops but you'll be surprised how rare they are. Remember also that list comprehensions are also faster as the loop is executed in C, the language python is written in and is inherently faster - significantly so. As an aside I would also recommend looking at learning about lambda functions on lists. Again google it but basiclly it is a fast and easy method of applying a single function to all the elements in a list at once. Happy Coding – Galo do Leste Dec 19 '22 at 03:03