Индекс относительной силы

Я пытаюсь рассчитать индекс относительной силы RSI для финансового инструмента. Когда я сравниваю свой расчет с расчетом, выполненным с помощью коммерческого программного обеспечения, они не выглядят одинаково. Я не могу понять, что я делаю неправильно. Кто-нибудь может помочь?
формула RSI:

{100} - (100/(1+РС)). Где RS — это AvgGain (N периодов)/AvgLoss (N периодов)

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

}

person Carl Decks    schedule 08.10.2017    source источник
comment
Вопросы типа «почему мой код не работает», за которыми следует массивный дамп кода, обычно не одобряются. Каков ожидаемый результат? какой реальный выход? Что вы отбросили в качестве возможных причин, по которым ваш код не работает должным образом? Как вы думаете, что не так? Чему вы научились, отлаживая свой код? Короче говоря, какие усилия вы предприняли, пытаясь найти решение?   -  person InBetween    schedule 08.10.2017


Ответы (1)


Здравствуйте, вот протестированный и проверенный класс C#, который генерирует значения RSI со 100% точностью:

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);
          }
          
      }
  }

Для получения подробных пошаговых инструкций я написал длинную статью, вы можете ознакомиться с ней здесь: https://turmanauli.medium.com/a-step-by-step-guide-for-calculatory-reliable-rsi-values-programmatically-a6a604a06b77

P.S. Вам нужны глобальные переменные для хранения предыдущих значений, это не вариант для таких индикаторов, как RSI, простая функция работает только для простых индикаторов, таких как Simple Moving Average. Всем сглаженным/взвешенным индикаторам нужны буферы/глобальные массивы для хранения данных.

person ბესო თურმანაული    schedule 17.03.2021