RSI与Wilder的RSI计算问题

3

我在获取平滑RSI时遇到了困难。下面的图片来自freestockcharts.com。计算使用以下代码。

public static double CalculateRsi(IEnumerable<double> closePrices)
{
    var prices = closePrices as double[] ?? closePrices.ToArray();

    double sumGain = 0;
    double sumLoss = 0;
    for (int i = 1; i < prices.Length; i++)
    {
        var difference = prices[i] - prices[i - 1];
        if (difference >= 0)
        {
            sumGain += difference;
        }
        else
        {
            sumLoss -= difference;
        }
    }

    if (sumGain == 0) return 0;
    if (Math.Abs(sumLoss) < Tolerance) return 100;

    var relativeStrength = sumGain / sumLoss;

    return 100.0 - (100.0 / (1 + relativeStrength));
}

https://stackoverflow.com/questions/...th-index-using-some-programming-language-js-c

这似乎是没有平滑处理的纯RSI。如何计算平滑后的RSI?我尝试将其更改以符合这两个网站的定义,但输出结果不正确。它几乎没有平滑处理。
tc2000 -> Indicators -> RSI_and_Wilder_s_RSI (Wilder's smoothing = Previous MA value + (1/n periods * (Close - Previous MA)))

priceactionlab -> wilders-cutlers-and-harris-relative-strength-index (RS = EMA(Gain(n), n)/EMA(Loss(n), n))

能否有人用一些样本数据进行计算?

Wilder的RSI与RSI 输入图像描述

2个回答

5
为了计算RSI,需要一个计算期。根据维基百科的介绍,通常使用14作为计算期。
因此,计算步骤如下:
周期1-13,RSI=0
周期14:
AverageGain = TotalGain / PeriodCount;
AverageLoss = TotalLoss / PeriodCount;
RS = AverageGain / AverageLoss;
RSI = 100 - 100 / (1 + RS);

第15期 - 至第(N)期:

if (Period(N)Change > 0
  AverageGain(N) = ((AverageGain(N - 1) * (PeriodCount - 1)) + Period(N)Change) / PeriodCount;
else
  AverageGain(N) = (AverageGain(N - 1) * (PeriodCount - 1)) / PeriodCount;

if (this.Change < 0)
  AverageLoss(N) = ((AverageLoss(N - 1) * (PeriodCount - 1)) + Math.Abs(Period(N)Change)) / PeriodCount;
else
  AverageLoss(N) = (AverageLoss(N - 1) * (PeriodCount - 1)) / PeriodCount;

RS = AverageGain / AverageLoss;
RSI = 100 - (100 / (1 + RS));

接下来,为了使数值更加平滑,您需要对RSI值应用特定周期的移动平均线。要做到这一点,从最后一个索引开始遍历RSI值,并基于前x个平滑期计算当前周期的平均值。

完成后,只需将值列表反转即可得到预期的顺序:

List<double> SmoothedRSI(IEnumerable<double> rsiValues, int smoothingPeriod)
{
  if (rsiValues.Count() <= smoothingPeriod)
    throw new Exception("Smoothing period too large or too few RSI values passed.");

  List<double> results = new List<double>();
  List<double> reversedRSIValues = rsiValues.Reverse().ToList();

  for (int i = 1; i < reversedRSIValues.Count() - smoothingPeriod - 1; i++)
    results.Add(reversedRSIValues.Subset(i, i + smoothingPeriod).Average());

  return results.Reverse().ToList();
}

子集方法只是一个简单的扩展方法,如下所示:
public static List<double> Subset(this List<double> values, int start, int end)
{
  List<double> results = new List<double>();

  for (int i = start; i <= end; i++)
    results.Add(values[i]);

  return results;
}

免责声明,我没有测试过这段代码,但它应该能让你了解平滑是如何应用的。


此答案未涉及问题中提出的Wilders平滑计算。 - Don F
@DonF - 请查看更新的答案。我没有测试其准确性,但它应该会给你一个想法,因为平滑是通过使用x个周期的移动平均值来计算的。 - RooiWillie

0

没有缓冲区/全局变量来存储数据,您无法获得准确的值。

这是一个平滑的指标,意味着它不仅使用14个条形图,而是所有条形图: 以下是一篇逐步文章,附有工作和验证的源代码,如果价格和可用条数相同,则生成完全相同的值(您只需要从您的来源加载价格数据):

已测试并验证:

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-calculating-reliable-rsi-values-programmatically-a6a604a06b77

P.S. 函数仅适用于简单指标(简单移动平均线),即使是指数移动平均线、真实波幅平均值也绝对需要全局变量来存储先前的值。


请不要在多个问题中发布相同的链接。如果您与您发布的链接有任何关联,您必须在回答中披露您的关联。如果没有这样的披露,您的帖子可能会被视为垃圾邮件。 - Hoppeduppeanut
你好,我是本篇文章的作者,在 medium 上也能看到我的署名,还有什么需要我披露的吗?甚至还有我的照片在上面。 - Besarion Turmanauli
1
如果您在答案中包含指向自己的外部文章、其他产品或服务的链接,则需要在答案本身中披露您是作者,否则可能会被视为自我推广,并相应处理。您可以[编辑]您的答案,说一些类似“我写了一篇关于[x]的文章,您可以在这里阅读”,通常这就足够了,只要您明确表明自己是作者即可。使用相同的个人资料图片或名称,或在个人资料中包含指向您网站的链接不足以构成充分的披露。 - Hoppeduppeanut
1
谢谢,我会做的! - Besarion Turmanauli
没问题,确保你对其他答案也做同样的事情! - Hoppeduppeanut

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