3

I want to exactly mirror the RSI values on cryptowatch.de (In my case LTC-EUR), I used the site stockcharts.com, which explains how to calculate the RSI, to write the calculator in Javascript (node).

My code so far:

// data is an array of open-prices in descending date order (the current price is the last element)
function calculateRSI(data) {
  data = data.reverse(); // Reverse to handle it better
  let avgGain = 0;
  let aveLoss = 0;

  // Calculate first 14 periods
  for (let i = 0; i < 14; i++) {
    const ch = data[i] - data[i + 1];

    if (ch >= 0) {
      avgGain += ch;
    } else {
      aveLoss -= ch;
    }
  }

  avgGain /= 14;
  aveLoss /= 14;

  // Smooth values 250 times
  for (let i = 14; i < 264; i++) {
    const ch = data[i] - data[i + 1];

    if (ch >= 0) {
      avgGain = (avgGain * 13 + ch) / 14;
      aveLoss = (aveLoss * 13) / 14;
    } else {
      avgGain = (avgGain * 13) / 14;
      aveLoss = (aveLoss * 13 - ch) / 14;
    }
  }

  // Calculate relative strength index
  const rs = avgGain / aveLoss;
  return 100 - 100 / (1 + rs);
}

But the result is always far from the values which are displayed on cryptowatch.de, what's wrong? How to calculate it correctly? (Post in other programming languages are also okay)

Thanks @jingx but the results are still wrong

Community
  • 1
  • 1
Simon
  • 2,686
  • 2
  • 31
  • 43

4 Answers4

2

I know it's been a log time but i just had this problem and got the right technique. This took me waaay to long to figure out so enjoy in C#.

Step 1. You are receiving the values from the API from past[0] to now[x]. For 'Close/14' you have to calculate the diffrence at the 'close' values (the profit/loss) something like this:

            var profitAndLoss = new List<double>();
            for (int i = 0; i < values.Count - 1; i++)
                profitAndLoss.Add(values[i + 1] - values[i]); //newer - older value will give you negative values for losses and positiv values for profits

Step 2. Calculate your initial rsi value (often reffered to as RSI StepOne), notice i didn't reverse the recieved values. This initial calculation is done with the 'oldest' values. _samples is the amount of values you initally take to calculate the RSI in my case the default 'Close/14' _samples = 14:

            var avgGain = 0.0;
            var avgLoss = 0.0;

            //initial
            for (int i = 0; i < _samples; i++)
            {
                var value = profitAndLoss[i];
                if (value >= 0)
                    avgGain += value;
                else
                    avgLoss += value * -1; //here i multiply -1 so i only have positive values
            }

            avgGain /= _samples;
            avgLoss /= _samples;

Step 3. Smoothen the average values with the remaining values you got from the API:

            //smooth with given values
            for (int i = _samples; i < profitAndLoss.Count; i++)
            {
                var value = profitAndLoss[i];
                if (value >= 0)
                {
                    avgGain = (avgGain * (_samples - 1) + value) / _samples;
                    avgLoss = (avgLoss * (_samples - 1)) / _samples;
                }
                else
                {
                    value *= -1;
                    avgLoss = (avgLoss * (_samples - 1) + value) / _samples;
                    avgGain = (avgGain * (_samples - 1)) / _samples;
                }
            }

Step 4. Time to calculate the RSI:

            var rs = avgGain / avgLoss;
            var rsi = 100 - (100 / (1 + rs));

This will give you the same values as Kraken in their RSI chart (+/- 0.05, depending on your updatefrequency)

resultimage1

Crypto
  • 21
  • 3
1

How to calculate it correctly? ( Post in other programming languages are also okay )

Well, let me add one such, from QuantFX module

One may meet many formulations, some with examples, some with validation data-set(s), so let me pick one such, using a numba.jit decorated python code, with a few numpy vectorised tricks:

def RSI_func( priceVEC, RSI_period = 14 ):
    """
    __doc__ 
    USE:
             RSI_func( priceVEC, RSI_period = 14 )

             Developed by J. Welles Wilder and introduced in his 1978 book,
             New Concepts in Technical Trading Systems, the Relative Strength Index
             (RSI) is an extremely useful and popular momentum oscillator.

             The RSI compares the magnitude of a stock's recent gains
             to the magnitude of its recent losses and turns that information
             into a number that ranges from 0 to 100.

             It takes a single parameter, the number of time periods to use
             in the calculation. In his book, Wilder recommends using 14 periods.

             The RSI's full name is actually rather unfortunate as it is easily
             confused with other forms of Relative Strength analysis such as
             John Murphy's "Relative Strength" charts and IBD's "Relative Strength"
             rankings.

             Most other kinds of "Relative Strength" stuff involve using
             more than one stock in the calculation. Like most true indicators,
             the RSI only needs one stock to be computed.

             In order to avoid confusion,
             many people avoid using the RSI's full name and just call it "the RSI."

             ( Written by Nicholas Fisher)

    PARAMS:  
             pricesVEC  - a vector of price-DOMAIN-alike data in natural order
             RSI_period - a positive integer for an RSI averaging period

    RETURNS:
             a vector of RSI values

    EXAMPLE:
             >>> RSI_func( np.asarray( [ 46.1250, 47.1250, 46.4375, 46.9375, 44.9375,
                                         44.2500, 44.6250, 45.7500, 47.8125, 47.5625,
                                         47.0000, 44.5625, 46.3125, 47.6875, 46.6875,
                                         45.6875, 43.0625, 43.5625, 44.8750, 43.6875
                                         ]
                                       ),
                           RSI_period = 14  
                           )

             array( [ 51.77865613,  51.77865613,  51.77865613,  51.77865613,  51.77865613,  51.77865613,  51.77865613,
                      51.77865613,  51.77865613,  51.77865613,  51.77865613,  51.77865613,  51.77865613,
                      51.77865613,  48.47708511,  41.07344947,  42.86342911,  47.38184958,  43.99211059
                      ]
                    )
             OUGHT BE:
                      51.779,       48.477,       41.073,       42.863,       47.382,       43.992
             [PASSED]
    Ref.s:
             >>> http://cns.bu.edu/~gsc/CN710/fincast/Technical%20_indicators/Relative%20Strength%20Index%20(RSI).htm
    """
    deltas           =  np.diff( priceVEC )
    seed             =  deltas[:RSI_period]
    up               =  seed[seed >= 0].sum() / RSI_period
    down             = -seed[seed <  0].sum() / RSI_period
    rs               =  up / down
    rsi              =   50. * np.ones_like( priceVEC )                 # NEUTRAL VALUE PRE-FILL
    rsi[:RSI_period] =  100. - ( 100. / ( 1. + rs ) )

    for i in np.arange( RSI_period, len( priceVEC )-1 ):
        delta = deltas[i]

        if  delta   >  0:
            upval   =  delta
            downval =  0.
        else:
            upval   =  0.
            downval = -delta

        up   = ( up   * ( RSI_period - 1 ) + upval   ) / RSI_period
        down = ( down * ( RSI_period - 1 ) + downval ) / RSI_period

        rs      = up / down

        rsi[i]  = 100. - ( 100. / ( 1. + rs ) )

    return rsi[:-1]

Given your wish was to " exactly mirror " someone other's graph, there is a safest mode to cross-check what formulation did they actually used for RSI-calculation. It is common to see differences, so " exact " match requires sort of investigation what they did actually use for generating their data ( also notice a respective administrative UTC-offset difference(s), if working with D1-time-frame ).

user229044
  • 232,980
  • 40
  • 330
  • 338
user3666197
  • 1
  • 6
  • 50
  • 92
0

You probably missed smoothing avgLoss when it's a gain, and avgGain when it's a loss, i.e. in the smooth loop:

if (ch >= 0) {
  avgGain = (avgGain * 13 + ch) / 14;
  aveLoss = (aveLoss * 13) / 14;
} else {
  avgGain = (avgGain * 13) / 14;
  aveLoss = (aveLoss * 13 - ch) / 14;
}
jingx
  • 3,698
  • 3
  • 24
  • 40
  • @SimonW. stockcharts' formula uses a 250 smooth period. cryptowatch might not. Did you try stockcharts data and see if you get the same RSI as **stockcharts** result? – jingx May 07 '18 at 14:00
  • Case in point, if you look at the answer by @user3666197, it seems to use a rolling smooth period of 14. – jingx May 07 '18 at 14:04
0

You need buffers to store previous values, in other words, you need global variables, not just functions (unless you're creating a function for a simple indicator like SMA).

For detailed step-by-step instructions, I wrote a lengthy article, you can check it here:: https://turmanauli.medium.com/a-step-by-step-guide-for-calculating-reliable-rsi-values-programmatically-a6a604a06b77

below you an see a final class (C#), it is tested and verified, generating RSI values with 100% accuracy:

using System;
using System.Data;
using System.Globalization;

namespace YourNameSpace
  {
   class PriceEngine
      {
        public static DataTable data;
        public static double[] positiveChanges;
        public static double[] negativeChanges;
        public static double[] averageGain;
        public static double[] averageLoss;
        public static double[] rsi;
        
        public static double CalculateDifference(double current_price, double previous_price)
          {
              return current_price - previous_price;
          }

        public static double CalculatePositiveChange(double difference)
          {
              return difference > 0 ? difference : 0;
          }

        public static double CalculateNegativeChange(double difference)
          {
              return difference < 0 ? difference * -1 : 0;
          }

        public static void CalculateRSI(int rsi_period, int price_index = 5)
          {
              for(int i = 0; i < PriceEngine.data.Rows.Count; i++)
              {
                  double current_difference = 0.0;
                  if (i > 0)
                  {
                      double previous_close = Convert.ToDouble(PriceEngine.data.Rows[i-1].Field<string>(price_index));
                      double current_close = Convert.ToDouble(PriceEngine.data.Rows[i].Field<string>(price_index));
                      current_difference = CalculateDifference(current_close, previous_close);
                  }
                  PriceEngine.positiveChanges[i] = CalculatePositiveChange(current_difference);
                  PriceEngine.negativeChanges[i] = CalculateNegativeChange(current_difference);

                  if(i == Math.Max(1,rsi_period))
                  {
                      double gain_sum = 0.0;
                      double loss_sum = 0.0;
                      for(int x = Math.Max(1,rsi_period); x > 0; x--)
                      {
                          gain_sum += PriceEngine.positiveChanges[x];
                          loss_sum += PriceEngine.negativeChanges[x];
                      }

                      PriceEngine.averageGain[i] = gain_sum / Math.Max(1,rsi_period);
                      PriceEngine.averageLoss[i] = loss_sum / Math.Max(1,rsi_period);

                  }else if (i > Math.Max(1,rsi_period))
                  {
                      PriceEngine.averageGain[i] = ( PriceEngine.averageGain[i-1]*(rsi_period-1) + PriceEngine.positiveChanges[i]) / Math.Max(1, rsi_period);
                      PriceEngine.averageLoss[i] = ( PriceEngine.averageLoss[i-1]*(rsi_period-1) + PriceEngine.negativeChanges[i]) / Math.Max(1, rsi_period);
                      PriceEngine.rsi[i] = PriceEngine.averageLoss[i] == 0 ? 100 : PriceEngine.averageGain[i] == 0 ? 0 : Math.Round(100 - (100 / (1 + PriceEngine.averageGain[i] / PriceEngine.averageLoss[i])), 5);
                  }
              }
          }
          
        public static void Launch()
          {
            PriceEngine.data = new DataTable();            
            //load {date, time, open, high, low, close} values in PriceEngine.data (6th column (index #5) = close price) here
            
            positiveChanges = new double[PriceEngine.data.Rows.Count];
            negativeChanges = new double[PriceEngine.data.Rows.Count];
            averageGain = new double[PriceEngine.data.Rows.Count];
            averageLoss = new double[PriceEngine.data.Rows.Count];
            rsi = new double[PriceEngine.data.Rows.Count];
            
            CalculateRSI(14);
          }
          
      }
  }