83 lines
3.7 KiB
MQL5
83 lines
3.7 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| MACDp.mqh |
|
|
//| Moving Average Convergence Divergence on Price |
|
|
//| Astralys LLC |
|
|
//| |
|
|
//| The distance between the close and its own n-period simple moving |
|
|
//| average: |
|
|
//| |
|
|
//| MACDp_t(n) = Close_t - (1/n) * sum_{i=0..n-1} Close_{t-i} |
|
|
//| |
|
|
//| A moving average crossover is a picture. This is the same idea |
|
|
//| expressed as a number, which is what makes it optimisable: the |
|
|
//| crossover is the special case where the value passes through zero.|
|
|
//| |
|
|
//| NORMALISATION. In raw form the value is expressed in price units, |
|
|
//| so a threshold of 41 points means something very different on an |
|
|
//| index at 1,400 and on the same index at 6,000. Over a long sample |
|
|
//| of a growing market the indicator is not stationary, and the grid |
|
|
//| ends up testing an object that changes nature halfway through. |
|
|
//| Dividing by the moving average turns it into a percentage |
|
|
//| deviation, which is comparable across the whole sample. |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Astralys LLC"
|
|
#property link "https://pulsar-terminal.com"
|
|
#property version "1.00"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Simple moving average of the last n values ending at i. |
|
|
//+------------------------------------------------------------------+
|
|
double MacdpSma(const double &price[], const int i, const int n)
|
|
{
|
|
if(n <= 0 || i < n - 1)
|
|
return(0.0);
|
|
|
|
double acc = 0.0;
|
|
for(int k = 0; k < n; k++)
|
|
acc += price[i - k];
|
|
|
|
return(acc / n);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| MACD on price over a whole series. |
|
|
//| |
|
|
//| price[] input series, index 0 = oldest |
|
|
//| n lookback period of the moving average |
|
|
//| normalise true -> percentage deviation from the average |
|
|
//| false -> raw distance in price units |
|
|
//| out[] output, same size as price[]. The first n-1 values are |
|
|
//| set to EMPTY_VALUE and must be skipped by the caller. |
|
|
//| |
|
|
//| Returns the index of the first valid value, or -1 on error. |
|
|
//+------------------------------------------------------------------+
|
|
int MACDPrice(const double &price[], const int n, const bool normalise, double &out[])
|
|
{
|
|
const int size = ArraySize(price);
|
|
if(size <= 0 || n <= 1 || n > size)
|
|
return(-1);
|
|
|
|
if(ArrayResize(out, size) != size)
|
|
return(-1);
|
|
|
|
for(int i = 0; i < n - 1; i++)
|
|
out[i] = EMPTY_VALUE;
|
|
|
|
for(int i = n - 1; i < size; i++)
|
|
{
|
|
const double sma = MacdpSma(price, i, n);
|
|
|
|
if(!normalise)
|
|
{
|
|
out[i] = price[i] - sma;
|
|
continue;
|
|
}
|
|
|
|
// A flat window cannot happen on real prices, but a zero average
|
|
// would blow up the ratio, so it is guarded rather than trusted.
|
|
out[i] = (sma > 0.0) ? (price[i] - sma) / sma * 100.0 : 0.0;
|
|
}
|
|
|
|
return(n - 1);
|
|
}
|
|
//+------------------------------------------------------------------+
|