Рассчитать RSI на основе Kraken OHLC

Я хочу точно отразить значения RSI на cryptowatch.de (в моем случае LTC-EUR), я использовал сайт stockcharts.com , который объясняет, как рассчитать RSI, чтобы написать калькулятор на Javascript (узел).

Мой код на данный момент:

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

Но результат всегда далек от значений, которые отображаются на cryptowatch.de. , что случилось? Как правильно рассчитать? (Публикация на других языках программирования тоже возможна)

Спасибо @jingx, но результаты все равно неверны


person Simon    schedule 06.05.2018    source источник


Ответы (4)


Как правильно рассчитать? (Публикация на других языках программирования тоже возможна)

Что ж, позвольте мне добавить один такой, из модуля QuantFX

Можно встретить множество формулировок, некоторые с примерами, некоторые с наборами данных для проверки, поэтому позвольте мне выбрать один из них, используя numba.jit декорированный код Python с несколькими numpy векторизованными трюками:

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]

Учитывая, что вы хотели «точно отразить» чей-то график, существует самый безопасный режим для перекрестной проверки того, какую формулировку они фактически использовали для расчета RSI. Часто можно увидеть различия, поэтому "точное" соответствие требует своего рода расследования, что они фактически использовали для генерации своих данных (также обратите внимание на соответствующую административную разницу смещения UTC, если работает с D1 -период времени ).

person user3666197    schedule 07.05.2018

Я знаю, что это было время журнала, но у меня просто была эта проблема и я использовал правильную технику. Это заняло у меня очень много времени, чтобы понять, так что наслаждайтесь C #.

Шаг 1. Вы получаете значения от API с прошлого [0] до настоящего [x]. Для «Close / 14» вы должны рассчитать разницу при «закрытых» значениях (прибыль / убыток) примерно так:

            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

Шаг 2. Вычислите свое начальное значение RSI (часто называемое RSI StepOne), обратите внимание, что я не отменил полученные значения. Этот первоначальный расчет выполняется с использованием самых «старых» значений. _samples - это количество значений, которые вы изначально принимаете для расчета RSI, в моем случае это значение по умолчанию '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;

Шаг 3. Сгладьте средние значения с оставшимися значениями, полученными из 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;
                }
            }

Шаг 4. Время рассчитать RSI:

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

Это даст вам те же значения, что и Kraken на их диаграмме RSI (+/- 0,05, в зависимости от вашей частоты обновления).

resultimage 1

person Crypto    schedule 18.07.2020

Вероятно, вы пропустили сглаживание avgLoss, когда это усиление, и avgGain, когда это потеря, то есть в плавном цикле:

if (ch >= 0) {
  avgGain = (avgGain * 13 + ch) / 14;
  aveLoss = (aveLoss * 13) / 14;
} else {
  avgGain = (avgGain * 13) / 14;
  aveLoss = (aveLoss * 13 - ch) / 14;
}
person jingx    schedule 06.05.2018
comment
@SimonW. Формула фондовых диаграмм использует плавный период 250. cryptowatch может и нет. Вы пробовали данные фондовых диаграмм и смотрели, получили ли вы тот же RSI, что и результат фондовых диаграмм? - person jingx; 07.05.2018
comment
Показательный пример: если вы посмотрите на ответ @ user3666197, кажется, что используется скользящий плавный период 14. - person jingx; 07.05.2018

Вам нужны буферы для хранения предыдущих значений, другими словами, вам нужны глобальные переменные, а не только функции (если вы не создаете функцию для простого индикатора, такого как SMA).

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

ниже вы видите последний класс (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);
          }
          
      }
  }
person ბესო თურმანაული    schedule 16.03.2021