oslib/tst/PST/P1_MQL5EMD/MQL5/Indicators/EMDloose.mqh

672 lines
21 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:15:18 +02:00
//------------------------------------------------------------------------------------
// [C]EMD[_2].mqh
// 2.01, 2012, victorg
// Forecasting modification (loose) Copyright (c) 2012-2020, victorg, Marketeer
// https://www.mql5.com/en/users/marketeer
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
// The Empirical Mode Decomposition (EMD).
//------------------------------------------------------------------------------------
class CEMD
{
private:
int N; // Input and output data size
int nIMF; // IMF (Intrinsic Mode Functions) counter
double Mean; // Mean of input data
int MaxIMF; // Maximum number of IMF
int MaxIter; // Maximum number of iterations
double Eps; // Accuracy comparison of floating-point numbers
double Trs; // Threshold for calculating the zero crossings
double Retr; // Number of successful retries
double IMFResult[]; // Result
double X[]; // X-coordinate for the TimeSeries. X[]=0,1,2,...,N-1.
double Imf[]; // Temporary array to calculate the IMF
double XMax[]; // x of local maxima
double YMax[]; // y of local maxima
double XMin[]; // x of local minima
double YMin[]; // y of local minima
double EnvUpp[]; // Upper envelope
double EnvLow[]; // Lower envelope
public:
void CEMD(void);
int decomp(const double &y[], const int extrapolate = 0); // Decomposition and optional extrapolation
void getIMF(double &x[], const int nn, const bool reverse = false) const; // Returns IMF with the index nn
int getN(void) const
{
return nIMF;
}
double getMean(void) const
{
return Mean;
}
private:
int arrayprepare(void);
int extrcounter(double &y[]);
int extrema(double &y[], int &nmax, double &xmax[], double &ymax[], int &nmin,
double &xmin[], double &ymin[], int &nzer);
int splineInterp(double &x[], double &y[], int n, double &x2[], double &y2[],
int btype = 0);
};
//------------------------------------------------------------------------------------
// Constructor.
//------------------------------------------------------------------------------------
void CEMD::CEMD(void)
{
MaxIMF = 16; // The maximum number of IMF
MaxIter = 1000; // The maximum number of iterations
Eps = 8 * DBL_EPSILON; // Accuracy comparison of floating-point numbers
Trs = 4 * DBL_EPSILON; // Threshold for calculating the zero crossings
Retr = 7; // Number of successful retries
}
//------------------------------------------------------------------------------------
// Decomposition.
//------------------------------------------------------------------------------------
int CEMD::decomp(const double &y[], const int extrapolate = 0)
{
int i, j, iter, nmax, nmin, nzer, pmin, pmax, pzer, count, valid;
double a;
N = ArraySize(y);
if(N < 6)
{
Print(__FUNCTION__, ": Error! Insufficient length of the input data.");
return (-1);
}
int Nf = N; // preserve actual number of input data points
N += extrapolate;
i = arrayprepare();
if(i < 0)
{
Print(__FUNCTION__, ": Error! ArrayResize() error.");
return (-2);
}
for(i = 0; i < N; i++)
X[i] = i;
Mean = 0;
for(i = 0; i < Nf; i++)
Mean += (y[i] - Mean) / (i + 1.0); // Mean (average) of input data
for(i = 0; i < N; i++)
{
a = y[MathMin(i, Nf - 1)] - Mean;
Imf[i] = a; // Input data minus mean
IMFResult[i] = a; // Input data minus mean
}
// The loop of decomposition
nIMF = 0;
while(nIMF++ < MaxIMF)
{
j = extrcounter(Imf); // Counting of the extrema
if(j < 2)
break; // If less than two extremas then the end of decomposition
// Loop of creation IMF
iter = 0;
count = 0;
pmin = INT_MAX;
pmax = INT_MAX;
pzer = INT_MAX;
while(iter++ < MaxIter)
{
// Find local extremas. Result --> XMin[],YMin[],XMax[],YMax[]
valid = extrema(Imf, nmax, XMax, YMax, nmin, XMin, YMin, nzer);
// Stopping criterion
if((nmax == pmax) && (nmin == pmin) && (nzer == pzer) && (valid == 1))
count++;
else
{
pmax = nmax;
pmin = nmin;
pzer = nzer;
count = 0;
}
if(count >= Retr)
break; // End of loop
// Upper and Lower envelope
if(nmax < 2)
for(i = 0; i < N; i++)
EnvUpp[i] = Imf[i];
else
splineInterp(XMax, YMax, nmax, X, EnvUpp);
if(nmin < 2)
for(i = 0; i < N; i++)
EnvLow[i] = Imf[i];
else
splineInterp(XMin, YMin, nmin, X, EnvLow);
// Create current IMF
for(i = 0; i < N; i++)
{
Imf[i] -= EnvUpp[i] +
(EnvLow[i] - EnvUpp[i]) * 0.5; // (EnvLow[i]+EnvUpp[i])/2.0
}
}
if(iter >= MaxIter)
Print(__FUNCTION__, ": Warning! Reached the maximum number of iterations.");
// Check IMF
j = extrcounter(Imf); // Counting of the extrema
if(j < 1)
break; // If monotonic then the end of decomposition
// Saving results
if(ArrayResize(IMFResult, N * (nIMF + 1)) != N * (nIMF + 1)) // Resize for current IMF
{
Print(__FUNCTION__, ": Error! ArrayResize() error.");
return (-2);
}
for(i = 0; i < N; i++)
{
IMFResult[i + N * nIMF] = Imf[i]; // Save current IMF
a = IMFResult[i] - Imf[i];
IMFResult[i] = a; // For the following calculations
Imf[i] = a; // For the following calculations
}
}
// Resize the array and save residue
if(ArrayResize(IMFResult, N * (nIMF + 1)) != N * (nIMF + 1))
{
Print(__FUNCTION__ + ": Error! ArrayResize() error.");
return (-2);
}
for(i = 0; i < N; i++)
{
IMFResult[i + N * nIMF] = IMFResult[i]; // IMF number nIMF is the residue
IMFResult[i] = y[MathMin(i, Nf - 1)] - Mean; // IMF number 0 is the input data minus Mean
}
if(nIMF >= MaxIMF)
Print(__FUNCTION__, ": Warning! Reached the maximum number of IMF.");
return (0);
}
//------------------------------------------------------------------------------------
// Returns the IMF with an index of nn.
// Input:
// nn - IMF number:
// nn=0; - Input data minus mean;
// nn=1; - IMF number 1;
// . . .
// nn=nIMF-1; - IMF number nIMF-1;
// nn=nIMF; - Residue of decomposition.
// Output:
// x[] - IMF number nn.
//------------------------------------------------------------------------------------
void CEMD::getIMF(double &x[], const int nn, const bool reverse = false) const
{
int i, k;
k = ArraySize(x);
if(k > N)
k = N;
if(nn < 0 || nn > nIMF || nIMF == 0)
for(i = 0; i < k; i++)
x[i] = 0.0;
else
for(i = 0; i < k; i++)
x[i] = IMFResult[i + N * nn];
if(reverse)
ArrayReverse(x);
}
//------------------------------------------------------------------------------------
// Extrema counter.
//------------------------------------------------------------------------------------
int CEMD::extrcounter(double &y[])
{
int i, j;
double a, b, c;
a = y[0];
b = y[1];
j = 0;
for(i = 1; i < N - 1; i++)
{
c = y[i + 1];
if(MathAbs(b - c) > DBL_MIN + Eps * MathMax(MathAbs(b), MathAbs(c))) // b != c
{
if(((b > c) && (b > a)) || ((b < c) && (b < a)))
j++;
a = b;
b = c;
}
}
return (j);
}
//------------------------------------------------------------------------------------
// Set the size of arrays.
//------------------------------------------------------------------------------------
int CEMD::arrayprepare(void)
{
if(ArrayResize(IMFResult, N) != N)
return (-1);
if(ArrayResize(X, N) != N)
return (-1);
if(ArrayResize(Imf, N) != N)
return (-1);
if(ArrayResize(XMax, N + 4) != N + 4)
return (-1);
if(ArrayResize(YMax, N + 4) != N + 4)
return (-1);
if(ArrayResize(XMin, N + 4) != N + 4)
return (-1);
if(ArrayResize(YMin, N + 4) != N + 4)
return (-1);
if(ArrayResize(EnvUpp, N) != N)
return (-1);
if(ArrayResize(EnvLow, N) != N)
return (-1);
return (0);
}
//------------------------------------------------------------------------------------
// Find local extremas and creation of extra boundary points.
// Return:
// If IMF is valid then return 1 else return 0.
//------------------------------------------------------------------------------------
int CEMD::extrema(double &y[], int &nmax, double &xmax[], double &ymax[],
int &nmin, double &xmin[], double &ymin[], int &nzer)
{
int i, k, nb, sig, ret;
double a, b, c, d;
sig = 0;
nzer = 0;
for(i = 0; i < N; i++)
{
if((y[i] > Trs) && (sig < 1))
{
if(sig == -1)
nzer++;
sig = 1;
}
if((y[i] < -Trs) && (sig > -1))
{
if(sig == 1)
nzer++;
sig = -1;
}
}
nmax = 0;
nmin = 0;
ret = 1;
a = y[0];
b = y[1];
k = 1;
for(i = 1; i < N - 1; i++)
{
c = y[i + 1];
if(MathAbs(b - c) > DBL_MIN + Eps * MathMax(MathAbs(b), MathAbs(c))) // b != c
{
if((b > c) && (b > a))
{
xmax[nmax + 2] = 0.5 * (k + i);
d = 0.5 * (y[k] + y[i]);
ymax[2 + nmax++] = d;
if(d < 0)
ret = 0;
}
else if((b < c) && (b < a))
{
xmin[nmin + 2] = 0.5 * (k + i);
d = 0.5 * (y[k] + y[i]);
ymin[2 + nmin++] = d;
if(d > 0)
ret = 0;
}
a = b;
b = c;
k = i + 1;
}
}
if(ret == 1)
{
k = nzer - nmax - nmin;
if((k > 1) && (k < -1))
ret = 0;
}
// extra boundary points
nb = 2;
while(nmin < (nb + 1) && nmax < (nb + 1))
nb--;
if(nb < 2)
{
for(i = 0; i < nmin; i++)
{
xmin[i + nb] = xmin[i + 2];
ymin[i + nb] = ymin[i + 2];
}
for(i = 0; i < nmax; i++)
{
xmax[i + nb] = xmax[i + 2];
ymax[i + nb] = ymax[i + 2];
}
}
if(nb < 1)
return (ret);
if(xmax[nb] < xmin[nb])
{
if(y[0] > ymin[nb])
{
if(2 * xmax[nb] - xmin[2 * nb - 1] > 0)
{
for(i = 0; i < nb; i++)
{
xmax[i] = -xmax[2 * nb - 1 - i];
ymax[i] = ymax[2 * nb - 1 - i];
xmin[i] = -xmin[2 * nb - 1 - i];
ymin[i] = ymin[2 * nb - 1 - i];
}
}
else
{
for(i = 0; i < nb; i++)
{
xmax[i] = 2 * xmax[nb] - xmax[2 * nb - i];
ymax[i] = ymax[2 * nb - i];
xmin[i] = 2 * xmax[nb] - xmin[2 * nb - 1 - i];
ymin[i] = ymin[2 * nb - 1 - i];
}
}
}
else
{
for(i = 0; i < nb; i++)
{
xmax[i] = -xmax[2 * nb - 1 - i];
ymax[i] = ymax[2 * nb - 1 - i];
}
for(i = 0; i < nb - 1; i++)
{
xmin[i] = -xmin[2 * nb - 2 - i];
ymin[i] = ymin[2 * nb - 2 - i];
}
xmin[nb - 1] = 0;
ymin[nb - 1] = y[0];
}
}
else
{
if(y[0] < ymax[nb])
{
if(2 * xmin[nb] - xmax[2 * nb - 1] > 0)
{
for(i = 0; i < nb; i++)
{
xmax[i] = -xmax[2 * nb - 1 - i];
ymax[i] = ymax[2 * nb - 1 - i];
xmin[i] = -xmin[2 * nb - 1 - i];
ymin[i] = ymin[2 * nb - 1 - i];
}
}
else
{
for(i = 0; i < nb; i++)
{
xmax[i] = 2 * xmin[nb] - xmax[2 * nb - 1 - i];
ymax[i] = ymax[2 * nb - 1 - i];
xmin[i] = 2 * xmin[nb] - xmin[2 * nb - i];
ymin[i] = ymin[2 * nb - i];
}
}
}
else
{
for(i = 0; i < nb; i++)
{
xmin[i] = -xmin[2 * nb - 1 - i];
ymin[i] = ymin[2 * nb - 1 - i];
}
for(i = 0; i < nb - 1; i++)
{
xmax[i] = -xmax[2 * nb - 2 - i];
ymax[i] = ymax[2 * nb - 2 - i];
}
xmax[nb - 1] = 0;
ymax[nb - 1] = y[0];
}
}
nmin += nb - 1;
nmax += nb - 1;
if(xmax[nmax] < xmin[nmin])
{
if(y[N - 1] < ymax[nmax])
{
if(2 * xmin[nmin] - xmax[nmax - nb + 1] < (N - 1))
{
for(i = 0; i < nb; i++)
{
xmax[nmax + 1 + i] = 2 * (N - 1) - xmax[nmax - i];
ymax[nmax + 1 + i] = ymax[nmax - i];
xmin[nmin + 1 + i] = 2 * (N - 1) - xmin[nmin - i];
ymin[nmin + 1 + i] = ymin[nmin - i];
}
}
else
{
for(i = 0; i < nb; i++)
{
xmax[nmax + 1 + i] = 2 * xmin[nmin] - xmax[nmax - i];
ymax[nmax + 1 + i] = ymax[nmax - i];
xmin[nmin + 1 + i] = 2 * xmin[nmin] - xmin[nmin - 1 - i];
ymin[nmin + 1 + i] = ymin[nmin - 1 - i];
}
}
}
else
{
for(i = 0; i < nb; i++)
{
xmin[nmin + 1 + i] = 2 * (N - 1) - xmin[nmin - i];
ymin[nmin + 1 + i] = ymin[nmin - i];
}
for(i = 0; i < nb - 1; i++)
{
xmax[nmax + 2 + i] = 2 * (N - 1) - xmax[nmax - i];
ymax[nmax + 2 + i] = ymax[nmax - i];
}
xmax[nmax + 1] = N - 1;
ymax[nmax + 1] = y[N - 1];
}
}
else
{
if(y[N - 1] > ymin[nmin])
{
if(2 * xmax[nmax] - xmin[nmin - nb + 1] < (N - 1))
{
for(i = 0; i < nb; i++)
{
xmax[nmax + 1 + i] = 2 * (N - 1) - xmax[nmax - i];
ymax[nmax + 1 + i] = ymax[nmax - i];
xmin[nmin + 1 + i] = 2 * (N - 1) - xmin[nmin - i];
ymin[nmin + 1 + i] = ymin[nmin - i];
}
}
else
{
for(i = 0; i < nb; i++)
{
xmax[nmax + 1 + i] = 2 * xmax[nmax] - xmax[nmax - 1 - i];
ymax[nmax + 1 + i] = ymax[nmax - 1 - i];
xmin[nmin + 1 + i] = 2 * xmax[nmax] - xmin[nmin - i];
ymin[nmin + 1 + i] = ymin[nmin - i];
}
}
}
else
{
for(i = 0; i < nb; i++)
{
xmax[nmax + 1 + i] = 2 * (N - 1) - xmax[nmax - i];
ymax[nmax + 1 + i] = ymax[nmax - i];
}
for(i = 0; i < nb - 1; i++)
{
xmin[nmin + 2 + i] = 2 * (N - 1) - xmin[nmin - i];
ymin[nmin + 2 + i] = ymin[nmin - i];
}
xmin[nmin + 1] = N - 1;
ymin[nmin + 1] = y[N - 1];
}
}
nmin = nmin + nb + 1;
nmax = nmax + nb + 1;
return (ret);
}
//------------------------------------------------------------------------------------
// Cubic spline Interpolation.
// Input:
// x[] - Abscissa of input data points. The elements of the array x
// must be strictly monotone increasing.
// y[] - Ordinate of input data points.
// n - Number of input data points.
// x2[] - Abscissa of spline function points. The elements of the array x
// must be strictly monotone increasing. x2 interval = [x[0],x[n-1]].
// btype - Boundary points type. 0-natural spline, 1-parabolic.
// Output:
// y2[] - Spline function.
// Return:
// 0 - No errors.
// -1 - Arguments has wrong size.
// -2 - ArrayResize() error.
// Notes:
// Based on ALGLIB.
//------------------------------------------------------------------------------------
int CEMD::splineInterp(double &x[], double &y[], int n, double &x2[],
double &y2[], int btype = 0)
{
int i, n2, intervalindex, pointindex;
bool havetoadvance;
double c0, c1, c2, c3, a, bb, w, w2, w3, fa, fb, da, db;
double t, a1[], a2[], a3[], b[], d[];
n2 = ArraySize(x2);
if(n > ArraySize(x) || n > ArraySize(y) || n2 > ArraySize(y2) || n < 2 || n2 < 1)
{
Print(__FUNCTION__, ": Error! Arguments has wrong size.");
return (-1);
}
ArrayInitialize(y2, 0);
if(ArrayResize(a1, n) != n)
{
Print(__FUNCTION__, ": Error! ArrayResize() error.");
return (-2);
}
if(ArrayResize(a2, n) != n)
{
Print(__FUNCTION__, ": Error! ArrayResize() error.");
return (-2);
}
if(ArrayResize(a3, n) != n)
{
Print(__FUNCTION__, ": Error! ArrayResize() error.");
return (-2);
}
if(ArrayResize(b, n) != n)
{
Print(__FUNCTION__, ": Error! ArrayResize() error.");
return (-2);
}
if(ArrayResize(d, n) != n)
{
Print(__FUNCTION__, ": Error! ArrayResize() error.");
return (-2);
}
for(i = 1; i <= n - 2; i++)
{
a1[i] = x[i + 1] - x[i];
a2[i] = 2 * (x[i + 1] - x[i - 1]);
a3[i] = x[i] - x[i - 1];
b[i] = 3 * (y[i] - y[i - 1]) / (x[i] - x[i - 1]) * (x[i + 1] - x[i]) +
3 * (y[i + 1] - y[i]) / (x[i + 1] - x[i]) * (x[i] - x[i - 1]);
}
if(btype == 1 && n == 2)
{
d[0] = (y[1] - y[0]) / (x[1] - x[0]);
d[1] = d[0];
}
else
{
if(btype == 1)
{
a1[0] = 0;
a2[0] = 1;
a3[0] = 1;
b[0] = 2 * (y[1] - y[0]) / (x[1] - x[0]);
a1[n - 1] = 1;
a2[n - 1] = 1;
a3[n - 1] = 0;
b[n - 1] = 2 * (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
}
else
{
a1[0] = 0;
a2[0] = 2;
a3[0] = 1;
b[0] = 3 * (y[1] - y[0]) / (x[1] - x[0]);
a1[n - 1] = 1;
a2[n - 1] = 2;
a3[n - 1] = 0;
b[n - 1] = 3 * (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
}
for(i = 1; i <= n - 1; i++)
{
t = a1[i] / a2[i - 1];
a2[i] = a2[i] - t * a3[i - 1];
b[i] = b[i] - t * b[i - 1];
}
d[n - 1] = b[n - 1] / a2[n - 1];
for(i = n - 2; i >= 0; i--)
d[i] = (b[i] - a3[i] * d[i + 1]) / a2[i];
}
c0 = 0;
c1 = 0;
c2 = 0;
c3 = 0;
a = 0;
bb = 0;
intervalindex = -1;
pointindex = 0;
for(;;)
{
if(pointindex >= n2)
break;
t = x2[pointindex];
havetoadvance = false;
if(intervalindex == -1)
havetoadvance = true;
else if(intervalindex < n - 2)
havetoadvance = (t >= bb);
if(havetoadvance)
{
intervalindex = intervalindex + 1;
a = x[intervalindex];
bb = x[intervalindex + 1];
w = bb - a;
w2 = w * w;
w3 = w * w2;
fa = y[intervalindex];
fb = y[intervalindex + 1];
da = d[intervalindex];
db = d[intervalindex + 1];
c0 = fa;
c1 = da;
c2 = (3 * (fb - fa) - 2 * da * w - db * w) / w2;
c3 = (2 * (fa - fb) + da * w + db * w) / w3;
continue;
}
t = t - a;
y2[pointindex] = c0 + t * (c1 + t * (c2 + t * c3));
pointindex = pointindex + 1;
}
return (0);
}
//------------------------------------------------------------------------------------