问题
I am developing a small trading robot as an exercise. He receives stock prices day after day (represented as iterations).
Here's what my Trade
class looks like:
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
};
As you can see from my last two attributes, I wish to implement an Exponential Moving Average as part of a Trend Following Algorithm.
But I think I didn't quite understand how to implement it; here's my calcEMA
function that simply calculates the EMA
:
int Trade::calcEMA()
{
return ((this->currentStock_ - this->lastEMA_
* (2/(this->daysInTrading_ + 1)))
+ this->lastEMA_);
}
But when my stock values (passed in a file) are like such:
1000, 1100, 1200, 1300, 1400, 1500, 1400, 1300, 1200, 1100, 1000
As to make sure my EMA makes sense, and well... it does not !
Where did I go wrong on the operation?
Aditionally, what value should I give lastEMA
if it's the first time I call calcEMA
?
回答1:
I believe you are missing a parentheses in you calcEMA function. It helps to break the expression up into smaller expressions with temporary variables to hold intermediate results.
int Trade::calcEMA()
{
auto mult = 2/(timePeriod_ + 1);
auto rslt = (currentStock_ - lastEMA_) * mult + lastEMA_;
return rslt;
}
Also, as PaulMcKenzie pointed out in the comments, you are using ints to do floating point math. You should consider using floats or doubles to avoid truncation.
An EMA like yours is defined for a time period, called timePeriod_ above. While daysInTrading_ is less or equal to timePeriod_, lastEMA_ should just be set to a normal average. Once daysInTrading_ is greater than your timePeriod_ you can start calling your calcEMA function with lastEMA_ properly initialized. Remember to update lastEMA_ after each call to calcEMA. Here is a possible implementation:
#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:
The operation is wrong, as you noticed.
Disclaimer I got this algorithm from wikipedia, and as such might no be accurate. Here (page 3) might be a better one, but I can't judge, I never used those algorithms and so have no idea what I'm talking about :)
c(EMA) = y(EMA) + a * (c(price) - y(EMA))
c(EMA)
is current EMAy(EMA)
is previous EMAa
is some "random" value between 0 and 1c(price)
is current price
But you did almost the same thing:
c(EMA) = (c(price) - y(EMA) * b) + y(EMA)
I don't know why you did 2 / daysInTrading_ + 1
, but this will not always be a value between 0 and 1 (actually, it might even be most of the time 0, because those are all intergers).
You put a parenthesis at the wrong place (after b
, and not after y(EMA)
).
So the operation will now look like this:
lastEMA_ + 0.5 * (currentStock_ - lastEMA_)
For the first lastEMA_
, according to Wikipedia:
S1 is undefined. S1 may be initialized in a number of different ways, most commonly by setting S11 [First element in the list], though other techniques exist, such as setting S1 to an average of the first 4 or 5 observations.
The importance of the S1 initialisations effect on the resultant moving average depends on α; smaller α values make the choice of S1 relatively more important than larger α values, since a higher α discounts older observations faster.
回答3:
There are generally two accepted forms of EMA.
The traditional:
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
来源:https://stackoverflow.com/questions/37300684/implementing-exponential-moving-average-in-c