在 C++ 中实现指数移动平均线

Posted

技术标签:

【中文标题】在 C++ 中实现指数移动平均线【英文标题】:Implementing Exponential Moving Average in C++ 【发布时间】:2016-05-18 13:11:59 【问题描述】:

我正在开发一个小型交易机器人作为练习。他日复一日地收到股票价格(表示为迭代)。

这是我的Trade 类的样子:

class   Trade

private:
  int                   capital_;
  int                   days_; // Total number of days of available stock prices                                       
  int                   daysInTrading_; // Increments as days go by.                                                   
  std::list<int>        stockPrices_; // Contains stock prices day after day.                                          
  int                   currentStock_; // Current stock we are dealing with.                                           
  int                   lastStock_; // Last stock dealt with                                                           
  int                   trend_; // Either -1; 0; 1 depending on the trend.                                           
  int                   numOfStocks_; // Number of stocks in our possession
  int                   EMA_; // Exponential Moving Average                                                            
  int                   lastEMA_; // Last EMA                                                                          

public:
    // functions
;

从我的最后两个属性可以看出,我希望将指数移动平均线作为趋势跟踪算法的一部分。

但我想我不太明白如何实现它;这是我的calcEMA 函数,它只计算EMA

int     Trade::calcEMA()

  return ((this->currentStock_ - this->lastEMA_
           * (2/(this->daysInTrading_ + 1)))
          + this->lastEMA_);

但是当我的股票值(在文件中传递)是这样的:

1000, 1100, 1200, 1300, 1400, 1500, 1400, 1300, 1200, 1100, 1000

为了确保我的 EMA 有意义,而且……它没有!

我的操作哪里出错了?

另外,如果这是我第一次调用calcEMA,我应该给lastEMA 什么值?

【问题讨论】:

2/(this-&gt;daysInTrading_ + 1) -- 这会截断,因为它是整数除法。这是你想做的吗? 【参考方案1】:

我相信您在“calcEMA”函数中缺少括号。如何将表达式分解为带有临时变量的更小的表达式来保存这样的中间结果?

int Trade::calcEMA()
       
   auto mult = 2/(timePeriod_ + 1);
   auto rslt = (currentStock_ - lastEMA_) * mult + lastEMA_;      
   return rslt;

此外,正如用户 PaulMcKenzie 在对您的问题的评论中指出的那样,您正在使用整数进行浮点计算。您可以考虑使用 float 或 double 以避免可能的截断。

以下是我的建议:

  像您这样的 EMA 是在一段时间内定义的。虽然daysInTrading 小于或等于timePeriod,但lastEMA 应设置为正常平均值。

  一旦daysInTrading 大于您的timePeriod,您就可以使用初始化的lastEMA 开始调用您的“calcEMA”函数。

  请记住在每次调用“calcEMA”函数后更新lastEMA

这是我的代码:

#include <vector>
#include <list>
#include <iostream>

// calculate a moving average
double calcMA (double previousAverage, 
    unsigned int previousNumDays, 
    double newStock) 
   auto rslt = previousNumDays * previousAverage + newStock;
   return rslt / (previousNumDays + 1.0);


// calculate an exponential moving average
double calcEMA (double previousAverage, 
    int timePeriod, 
    double newStock) 
   auto mult = 2.0 / (timePeriod + 1.0);
   auto rslt = (newStock - previousAverage) * mult + previousAverage;
   return rslt;


class Trade 
   unsigned int timePeriod_ = 5;
   double lastMA_ = 0.0;
   std::list<double> stockPrices_;
  
  public:     
   void addStock (double newStock) 
      stockPrices_.push_back(newStock);
      auto num_days = stockPrices_.size(); 

      if (num_days <= timePeriod_)
         lastMA_ = calcMA(lastMA_, num_days - 1, newStock);
      else
         lastMA_ = calcEMA(lastMA_, num_days - 1, newStock);
   

   double getAverage() const  return lastMA_; 
;


// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----    
int main() 
   std::vector<double> stocks =
     1000, 1100, 1200, 1300, 1400, 1500, 
         1400, 1300, 1200, 1100, 1000;

   Trade trade;
   for (auto stock : stocks)
      trade.addStock(stock);

   std::cout << "Average: " << trade.getAverage() << std::endl;

   return 0;

【讨论】:

【参考方案2】:

如你所见,操作是错误的。

免责声明我从wikipedia 得到这个算法,因此可能不准确。 Here(第 3 页)可能更好,但我无法判断,我从未使用过这些算法,所以不知道我在说什么 :)

c(EMA) = y(EMA) + a * (c(price) - y(EMA))

c(EMA) 是当前 EMA y(EMA) 是之前的 EMA a 是介于 0 和 1 之间的某个“随机”值 c(price)是当前价格

但你做了几乎相同的事情:

c(EMA) = (c(price) - y(EMA) * b) + y(EMA)

我不知道你为什么要2 / daysInTrading_ + 1,但这并不总是介于 0 和 1 之间的值(实际上,它甚至可能大部分时间都是 0,因为这些都是整数)。

你把括号放在错误的地方(b 之后,而不是y(EMA) 之后)。

所以操作现在看起来像这样:

lastEMA_ + 0.5 * (currentStock_ - lastEMA_)


对于第一个lastEMA_,根据Wikipedia:

S1 未定义。 S1 可以通过多种不同的方式初始化,最常见的是通过设置 S11 [列表中的第一个元素],尽管其他技术存在,例如将 S1 设置为前 4 或 5 个观察值的平均值。

S1 初始化效应对所得移动平均线的重要性取决于 α;较小的 α 值使 S1 的选择比较大的 α 值相对更重要,因为较高的 α 会更快地折扣较旧的观测值。

【讨论】:

非常感谢您的帮助,我已阅读以下论文:cis.umac.mo/~fstasp/paper/jetwi2011.pdf 关于趋势跟踪算法并在第 3 页看到了原理。另外,您应该接受什么价值?真的是 0 到 1 之间的随机值还是背后有解释? @ChristopherK。我的意思不是随机的,而是你选择的任何东西。这真的取决于你想拥有什么。查看引用(我使用a,***使用alpha) @ChristopherK。使用您链接的算法,它可能比***更准确:)【参考方案3】:

通常有两种公认的 EMA 形式。

传统的:

m = 2/(1+n)                               // where n >= 1
EMA = m * currentPrice + (1-m) * previousEMA

rf the Wilder:

m = 1/n                                   // where n >= 1
EMA Wilder = m * currentPrice + (1-m) * previousEMA

【讨论】:

以上是关于在 C++ 中实现指数移动平均线的主要内容,如果未能解决你的问题,请参考以下文章

如何计算postgres的指数移动平均线?

简单移动平均线加权移动平均线指数平滑移动平均

使用EXCEL计算并绘制MACD指标

在python中计算指数移动平均线

火花指数移动平均线

PySpark:计算指数移动平均线