根据Kraken OHLC计算RSI

3
我希望能够完全复制cryptowatch.de(我这里是LTC-EUR)上的RSI值,因此我使用了stockcharts.com提供的计算RSI的方法,并用Javascript(node)编写了计算器。

到目前为止,我的代码是:

// 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,但结果仍然错误。
4个回答

2

我知道已经过去很久了,但是我刚刚遇到了这个问题并找到了正确的解决方法。这让我花费了很长时间来弄清楚,所以在C#中享受吧。

步骤1.您正在从past [0]到now [x]接收来自API的值。对于“Close/14”,您必须计算“close”值(利润/损失)的差异,类似于以下内容:

            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

第二步,计算您的初始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;

步骤三:将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;
                }
            }

第四步。开始计算RSI:
            var rs = avgGain / avgLoss;
            var rsi = 100 - (100 / (1 + rs));

这将给出与Kraken RSI图表相同的值(+/- 0.05,取决于您的更新频率)。

结果图像1


1

如何正确计算?(也可以发布其他编程语言的帖子)

好的,让我添加一个来自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时间框架))。


0

你可能在平滑循环中错过了avgLoss是收益时,avgGain是亏损时的平滑处理,即:

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

@SimonW。stockcharts的公式使用250个平滑周期。Cryptowatch可能没有。您是否尝试过使用Stockcharts数据并查看是否获得与Stockcharts结果相同的RSI? - jingx
举个例子,如果你看一下@user3666197的回答,它似乎使用了一个14天的滚动平滑周期。 - jingx

0

在编程中,您需要缓冲区来存储先前的值,换句话说,您需要全局变量,而不仅仅是函数(除非您正在为简单的指标(如SMA)创建函数)。

如需详细的逐步说明,请参阅我撰写的一篇长文,您可以在此处查看: https://turmanauli.medium.com/a-step-by-step-guide-for-calculating-reliable-rsi-values-programmatically-a6a604a06b77

以下是一个最终类(C#),它经过测试和验证,可生成100%准确的RSI值:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接