Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSI value does NOT match expected values #46

Open
wbrickner opened this issue Jul 22, 2021 · 10 comments
Open

RSI value does NOT match expected values #46

wbrickner opened this issue Jul 22, 2021 · 10 comments

Comments

@wbrickner
Copy link

Hello,

I've been using the RSI indicator provided by ta.
Using the RSI indicator, I compute some higher level indicators, their form is not important here.

I wanted to verify that I had implemented the indicator correctly, and so I wrote extensive test cases on real data lifted from TradingView.

This is when I noticed that the RSI value computed on TradingView does not match the RSI computed from this library.

I figured out the issues here:

  1. The EMAs internal to the ta RSI use a different alpha (k field), as 2 / (period - 1), whereas on TV, the RSI uses an EMA with alpha of 1.0 / period.
  2. The computation of the RSI on TV uses a form that handles division by zero differently, otherwise the expressions should be equivalent.

I am not sure which version of the RSI is the intended "official" RSI,
but in order to make the RSI implementations match I've had to change things inside the ta library.
If you have any information on this please let me know, I am a bit clueless.

My suggested change is that you provide a function on EMA and dependent indicators to set the alpha parameter used.

@greyblake
Copy link
Owner

My thinking process:

ta EMA's k = 2 / (period + 1) (not 2 / (period - 1)).

What makes:
2 / (period + 1) = 1 / (0.5 * period + 0.5), versus TV's 1.0 / period.
So considering that period is usually greater than 1, TA's k is gonna be typically greater than TV's one.

For example. with period = 14,

  • TA's K = 0.133
  • TV's K = 0.071

What means, TA's EMA puts more weight on the most recent input than the old ones.

How to fix

The difference between to formulas is so significant, that it would definitely break working strategies.

Maybe of possible solutions would be:

  • Provide multiple different moving averages
  • Make other indicators be generic over a moving average (with some defaults).

@wbrickner
Copy link
Author

wbrickner commented Jul 23, 2021

Apologies for the mistake in my recollection of your formula for alpha.

Yes, the default should remain the same.

What I had done in my local fork is add a different constructor with_custom_alpha(period: usize, alpha: f64).

It could be better to create a generic struct with a type parameter for this, like

trait EmaConfig {
  fn compute_alpha(period: usize) -> f64;
}

struct DefaultEMAConfig;
impl EmaConfig for DefaultEmaConfig {
  fn compute_alpha(p: usize) -> f64 { 2.0 / (period as f64 + 1.0) }
}

struct TradingViewEMAConfig;
impl EmaConfig for TradingViewEMAConfig {
  fn compute_alpha(p: usize) -> f64 { 1.0 / period as f64 }
}

pub struct GenericRSI<E: EmaConfig> { ... }

pub type RSI = GenericRSI<DefaultEmaConfig>;
pub type TVRSI = GenericRSI<TradingViewEMAConfig>;

The simple way however:

struct Ema { ... }
impl Ema {
  fn set_alpha(&mut self, a: f64) { ... }
}
impl RSI {
  fn set_alpha(&mut self, a: f64) { ... }
}

After compilation the default alpha and the manual override of alpha should simplify away the original assignment & be zero cost.

It could be more elegant to use the generic approach however.

By the way are these two versions of the EMA / RSI named something different? Not cool if the financial community just throws around the name RSI and acknowledges the period as a parameter but not how the alpha parameter is computed! Yields completely different values!

@imokoi
Copy link

imokoi commented Oct 18, 2021

Hello guys, I also met this issue when I was using RSI indicator. Is there any plan or good solution to solve this issue? I just changed the k of EMA temporarily to fix it.

@imokoi
Copy link

imokoi commented Oct 26, 2021

what about this? for EMA, we can pass a value of period or alpha, just select one ( like pandas library )


pub struct ExponentialMovingAverage {
    k: f64,
    current: f64,
    is_new: bool,
}

impl ExponentialMovingAverage {
    pub fn new(period: Option<usize>, alpha: Option<f64>) -> Result<Self> {
        match period {
            None => {
                match alpha {
                    None => Err(TaError::InvalidParameter),
                    Some(a) => Ok(
                        Self {
                            k: a,
                            current: 0.0,
                            is_new: true
                        }
                    )
                }
            },
            Some(p) => Ok(Self {
                k: 2.0 / (p + 1) as f64,
                current: 0.0,
                is_new: true,
            }),
        }
    }
}

then in RSI

impl RelativeStrengthIndex {
    pub fn new(period: usize) -> Result<Self> {
        Ok(Self {
            period,
            up_ema_indicator: Ema::new(None, Some(1.0 / period as f64))?,
            down_ema_indicator: Ema::new(None, Some(1.0 / period as f64))?,
            prev_val: 0.0,
            is_new: true,
        })
    }
}

@pmagaz
Copy link

pmagaz commented Dec 9, 2021

Hi,

I have the same issue not only with RSI but also with SlowStochastic and FastStochastic. Both of them don't match the values that I can see in platforms like TV. Any expectation about when it could be solved?

@OpethiaN73
Copy link

Hi,

I have the same issue not only with RSI but also with SlowStochastic and FastStochastic. Both of them don't match the values that I can see in platforms like TV. Any expectation about when it could be solved?

just something i've encountered in the past, make sure you're using standard candles and not heiken ashi

@pmagaz
Copy link

pmagaz commented Dec 29, 2021

My bad. Both Stochs are correct. I was using the wrong data.

@elbaro
Copy link

elbaro commented Jun 4, 2022

Pinescript v5 doc says alpha = 2 / (length + 1).

https://www.tradingview.com/pine-script-reference/#fun_ta{dot}ema

v4 is the same. Where did 1.0 / period come from? This comes from ta.rma.

@elbaro
Copy link

elbaro commented Jun 4, 2022

Pinescript's ta.rsi uses ta.rma (exponential moving average function that exists only for rsi and atr) not ta.ema.
To simplify things, ta-rs can do the same thing - add new indicator RelativeMovingAverage and change rsi and atr to use this by default.

struct AverageTrueRange<M=RelativeMovingAverage>

@austin-starks
Copy link

austin-starks commented Dec 23, 2023

I am confirming that there is a bug with RSI. I used ChatGPT to help diagnose and fix the bug. Here's an excerpt:

The array is [(t1,10), (t2, 10.5), (t3, 10), (t4,9.5)]. Each tuple represents a time point (t1, t2, etc.) and the corresponding price.

Steps for calculating the 3-day RSI:

Calculate Daily Price Changes: Subtract the previous day's price from the current day's price.
Separate Gains and Losses: If the price change is positive, it's a gain; otherwise, it's a loss. If there's no change, both gain and loss are zero.
Average Gains and Losses: Sum the gains (and losses) over the past 3 days and divide by 3.
Calculate the Relative Strength (RS): This is the average gain divided by the average loss.
Calculate the RSI: RSI = 100 - (100 / (1 + RS)).
Now, let's perform these calculations step-by-step.

Daily Price Changes:

From t1 to t2:
10.5 − 10 = 0.5

From t2 to t3:
10−10.5=−0.5

From t3 to t4:
9.5 − 10 = −0.5

Gains and Losses:
Day 1
Gain:
0.5
0.5
Loss:
0
0
__
Day 2
Gain:
0
0
Loss:
0.5
0.5
__
Day 3
Gain:
0
0
Loss:
0.5
0.5

__
Average Gains and Losses:

Average Gain:
(0.5+0+0)/3=0.1667
Average Loss:
(0+0.5+0.5)/3=0.3333

Calculate RS:

RS = Average Gain / Average Loss =
0.1667/0.3333=0.5

Calculate RSI:
100−(100/(1+0.5))=100−(100/1.5)=100−66.67=33.33
So, the 3-day RSI for the given data is approximately 33.33.

However, the unit tests here have the following (wrong) unit test:

#[test]
fn test_next() {
    let mut rsi = RelativeStrengthIndex::new(3).unwrap();
    assert_eq!(rsi.next(10.0), 50.0);
    assert_eq!(rsi.next(10.5).round(), 86.0);
    assert_eq!(rsi.next(10.0).round(), 35.0);
    assert_eq!(rsi.next(9.5).round(), 16.0);
}

I forked the repo to fix these issues and also made it easier to have a dynamic window size.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants