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

6

我正在开发一个小型交易机器人作为练习。它每天都会接收股票价格(表示为迭代)。

这是我的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赋什么值?


1
2/(this->daysInTrading_ + 1) -- 这会截断结果,因为这是整数除法。这是你想要做的吗? - PaulMcKenzie
3个回答

4
我认为“calcEMA”函数中缺少一个括号。可以将表达式分解成较小的表达式,使用临时变量保存中间结果,像这样:

我相信你在“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;
}

3
操作有误,正如您所注意到的。
免责声明:我从wikipedia获得了这个算法,因此可能不准确。 这里(第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_,根据维基百科:

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

对于移动平均数的结果,S1初始值的重要性取决于α; 较小的α值使得S1的选择相对于较大的α值更加重要,因为较高的α会更快地折价旧观测值。


非常感谢您的帮助,我已经阅读了以下论文:http://www.cis.umac.mo/~fstasp/paper/jetwi2011.pdf,关于趋势跟踪算法,并在第3页看到了原理。另外,a应该取什么值?是真正的0到1之间的随机值,还是有其背后的解释? - user5448913
1
@ChristopherK。我所说的“随机”并不是指真正的随机,而是指任何你选择的东西。这实际上取决于你想要什么。请参见引用(我使用a,维基百科使用alpha)。 - Rakete1111
1
@ChristopherK。使用您提供的算法进行翻译,可能比维基百科更准确 :) - Rakete1111

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

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