pbo-cscv-engine/Include/PBO/MACDp.mqh
2026-08-17 18:35:20 +00:00

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);
}
//+------------------------------------------------------------------+