Search code examples
c#financialstocks

Relative Strength Index


I'm trying to calculate the relative strength index, RSI, for a financial instrument. When I compare my calculation with one done by a commercial software, they don't look the same. I can't figure out what I'm doing wrong. Can anyone help?
The RSI formula:

{100} - (100/(1+RS)). Where RS is AvgGain(N periods)/AvgLoss( N periods)

public DataTable RSI(string instrument, int period, string oper, int entryVal)
{



 DataTable dtRSI = new DataTable();      //table to return
    dtRSI.Columns.Add("Date");
    dtRSI.Columns.Add("Instrument");
    dtRSI.Columns.Add("Close");
    dtRSI.Columns.Add("RSI");

    //Load Datatable from database
    DataTable dt = new DataTable();
    dt = conn.ExtractDataFromDb(instrument);       

    int column = 1; //Close price

    //variables to RSI formula
    Queue<float> avgUp = new Queue<float>();
    Queue<float> avgDown = new Queue<float>();
    float close1, close2, rsi, rs;
    float avgUp1, avgUp2, newAvgUp, avgDown1, avgDown2, newAvgDown;

    string[] dateCloseRsi = new string[3];      //row of data to insert into new table
    string date;                                //date of calculation
    string[] splitDate = new string[2];

    //get first close
    close1 = float.Parse(dt.Rows[0][column].ToString());
    dt.Rows.RemoveAt(0);

    //get close for number of periods into the que-list
    for (int i = 1; i <= period; i++)
    {
        close2 = float.Parse(dt.Rows[0][column].ToString());

        //are todays close higher then yesterday?
        if (close2 > close1)
        {
            avgUp.Enqueue(close2 - close1);
            avgDown.Enqueue(0);
        }
        else if (close2<close1)
        {
            avgUp.Enqueue(0);
            avgDown.Enqueue(close1 - close2);
        }
        else
        {
            avgUp.Enqueue(0);
            avgDown.Enqueue(0);
        }

        close1 = close2;

        dt.Rows.RemoveAt(0);
    }


    //iterate datatable and calculate RSI
    foreach (DataRow rows in dt.Rows)
    {

        avgUp1 = float.Parse(avgUp.Average().ToString("n2"));       //calculate yesterdays avg difference on up days
        avgDown1 = float.Parse(avgDown.Average().ToString("n2"));   //calculate yesterdays avg difference on down days
        avgUp.Dequeue();
        avgDown.Dequeue();


        close2 = float.Parse(rows[column].ToString()); //todays close 
        //close today higher then yesterday?
        if (close2 > close1)
        {
            avgUp.Enqueue(close2 - close1);
            avgDown.Enqueue(0);
        }
        else if (close2 < close1)
        {
            avgDown.Enqueue(close1 - close2);
            avgUp.Enqueue(0);
        }
        else
        {
            avgUp.Enqueue(0);
            avgDown.Enqueue(0);
        }

        avgUp2 = float.Parse(avgUp.Average().ToString("n2"));           //todays avg difference on up days
        avgDown2 = float.Parse(avgDown.Average().ToString("n2"));       //todays avg difference on down days
        newAvgUp = ((avgUp1 * (period - 1)) + avgUp2) / period;         //yesterdays and todays avg diff value on up days
        newAvgDown = ((avgDown1 * (period - 1)) + avgDown2) / period;   //yesterdays and todays avg diff value on down days
        newAvgUp = float.Parse(newAvgUp.ToString("n2"));                //round to 2 decimals
        newAvgDown = float.Parse(newAvgDown.ToString("n2"));            //round to 2 decimals


        rs = newAvgUp / newAvgDown;                 //calc Relative Strength
        rs = float.Parse(rs.ToString("n2"));        //round to 2 decimals
        rsi = 100 - (100 / (1 + rs));               //Calc RSI
        rsi = float.Parse(rsi.ToString("n2"));      //round to 2 decimals

        close1 = close2;                            //todays close become yesterdays close for tomorrow

        //remove time from date
        date = rows[0].ToString();
        splitDate = date.Split(' ');
        date = splitDate[0];


        //add data to dtRSI
        DataRow rsiRow = dtRSI.NewRow();
        rsiRow["Date"] = date;
        rsiRow["Instrument"] = instrument;
        rsiRow["Close"] = rows[column];
        rsiRow["RSI"] = rsi;
        dtRSI.Rows.Add(rsiRow);

    }
        return dtRSI;               //returns a table with Date, Instrument, Close Price and RSI

}

Solution

  • Hello, here's a tested and verified C# class which generates 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);
              }
              
          }
      }
    

    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

    P.S. You need global variables to store prevous values, this is not an option for indicators like RSI, simple function work only for simple indicators like Simple Moving Average. All smoothed / Weighted indicators need buffers / global arrays to store data.