Calculate/replicate RSI from Tradingview's pine script
Asked Answered
F

1

2

Im trying to recreate Tradingviews pine script RSI code into Javascript code. But having a hard time figuring out how it works. I made the basic RSI using a normal moving average calculation. But the pine script uses exponential weighted moving average. And there documentation is really hard to follow to me. This is the pine script.

//@version=4
study(title="Relative Strength Index", shorttitle="RSI", format=format.price, precision=2, resolution="")
len = input(14, minval=1, title="Length")
src = input(close, "Source", type = input.source)
up = rma(max(change(src), 0), len)
down = rma(-min(change(src), 0), len)
rsi = down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down))
plot(rsi, "RSI", color=#7E57C2)
band1 = hline(70, "Upper Band", color=#787B86)
bandm = hline(50, "Middle Band", color=color.new(#787B86, 50))
band0 = hline(30, "Lower Band", color=#787B86)
fill(band1, band0, color=color.rgb(126, 87, 194, 90), title="Background")

This is what I could make of it in Javascript:

// Period = 200
// Close variable is 200 closed values. Where [0] in array = oldest, [199] in array = newest value.

/**
 * Relative strength index. Based on closed periods.
 * 
 * @param {Array} close 
 * @param {Integer} period 
 * @returns 
 */
function calculateRSI(close, period) {

    // Only calculate if it is worth it. First {period - 1} amount of calculations aren't correct anyway.
    if (close.length < period) {
        return 50;
    }

    let averageGain = 0;
    let averageLoss = 0;
    const alpha = 1 / period;

    // Exponential weighted moving average.
    for (let i = 1; i < period; i++)
    {
        let change = close[i] - close[i - 1];

        if (change >= 0) {
            averageGain = alpha * change + (1 - alpha) * averageGain;
        } else {
            averageLoss = alpha * -change + (1 - alpha) * averageLoss;
        }
    }

    // Tried this too, but seems to not really matter.
    // To get an actual average.
    // averageGain /= period;
    // averageLoss /= period;

    // Calculate relative strength index. Where it can only be between 0 and 100.
    var rsi = 100 - (100 / (1 + (averageGain / averageLoss)));

    return rsi;
}

The results this function gives on my chart is not too bad, but it just isn't the same as I have it in Tradingview. I belive im missing something that the pine script does and I don't.

Things I dont understand of the pine script:

  • When does it do a for loop? I don't see it in there functions. If they don't, how do they calculate the average for a period of longer than 2? You have to loop for that right?
  • How does the rma function work? This is their docs.

I might have too many questions on this, but I think if you show a somewhat working example in Javascript of the RSI calculation like they do. Then I can probably make sense of it.

Is my calculation in Javascript correct to the one in the pine script?

Fulk answered 5/1, 2022 at 13:53 Comment(3)
It will probably be easier for you to go back to the original description of the RSI calcs by Wilder in his "New Concepts in Technical Trading Systems" book, which includes the average calcs. The "RMA" acronym is not by Wilder, btw; but it's used by some platforms to name the EMA variation Wilder created.Cliffcliffes
There is no loop in the Pine version of RMA because none is needed; the Pine runtime does the work for us. Wilder was always looking for simple ways to calculate his stuff because he did the calcs by hand. RMA uses a recursive calc where it starts from its previous value and only requires adding the new value to it.Cliffcliffes
@Cliffcliffes Its been some time and I went for the easier route. I would've liked to get it the way Tradingview does but currently made it in a way thats profitable too so im happy :)Fulk
E
0

in your change calculation you forgot to divide by close[i-1] ... and then you are not storing average gain and loss as an array

i tried to make my own function exactly how tradingview does and it still wont come out correctly

import numpy as np
candles = mufex_main.get_candles(symbol="BTCUSDT", timeframe='5m', candles_to_dl=200)

rsi_period = 14
closes = candles[:, 4]
close_shift = np.roll(candles[:, 4], 1)
close_shift[0] = np.nan
pchg = (closes-close_shift)/close_shift

alpha = 1/rsi_period
gain = np.where(pchg > 0, pchg, 0)
rma_up = np.zeros_like(gain)
avg_gain = np.full_like(gain, np.nan)

loss = np.where(pchg < 0, abs(pchg), 0)
rma_down = np.zeros_like(loss)
avg_loss = np.full_like(loss, np.nan)

for i in range(1, gain.size):
    rma_up[i] = alpha * gain[i] + (1 - alpha) * rma_up[i-1]
    rma_down[i] = alpha * loss[i] + (1 - alpha) * rma_down[i-1]

rma_up[:13] = 0
rma_down[:13] = 0

min_one = rsi_period - 1
for i in range(rsi_period-1, gain.size):
    avg_gain[i] = rma_up[i-rsi_period:i+1].mean()
    avg_loss[i] = rma_down[i-rsi_period:i+1].mean()

rs = avg_gain / avg_loss

print(100-(100/(1+rs)))

This even gives wrong answers which makes no sense because it is the same exact formula they are using

Enduring answered 28/10, 2023 at 14:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.