MQL's OOP notes: Arrayed indicator buffers based on operators overloading
![Stanislav Korotky Stanislav Korotky](https://c.mql5.com/avatar/2010/10/4CA7CFA0-1F0C.jpg)
double buffer1[]; double buffer2[]; double buffer3[];
Why not to write simply:
???
for(int i = 0; i < Bars; ++i) { for(int j = 0; j < ArrayRange(buffers, 0); ++j) { buffers[j][i] = iClose(Symbols[j], 0 , i) * getIndex(j); } }
All these years and up to the current moment, you need to replace the inner loop with N assignments for different buffers.
The indicator will have 4 buffers, 3 of which are simple moving averages with given periods and the 4-th is a weighted sum of them.
class Indicator { private: double buffer[]; int cursor; public: Indicator(int i) { SetIndexBuffer(i, buffer); }
It holds an array of doubles for later binding as indicator buffer in the class contructor. The cursor is an integer pointer for this buffer. We want the class to behave exactly as an array, this is why we need to overload operator[].
Indicator *operator[](int b) { cursor = (int)b; return GetPointer(this); }
double operator=(double x) { buffer[cursor] = x; return x; }
This is where the bar index (cursor) saved in the previous method is used to update an element in the buffer. Among assigment operator there mus be overloaded many other arithmetic operators in order to mimic ordinary array.
double operator+(double x) const { return buffer[cursor] + x; } double operator-(double x) const { return buffer[cursor] - x; } double operator*(double x) const { return buffer[cursor] * x; } double operator/(double x) const { return buffer[cursor] / x; } double operator+=(double x) { buffer[cursor] += x; return buffer[cursor]; } double operator-=(double x) { buffer[cursor] -= x; return buffer[cursor]; } double operator*=(double x) { buffer[cursor] *= x; return buffer[cursor]; } double operator/=(double x) { buffer[cursor] /= x; return buffer[cursor]; } };
Now let us define another class which manages an array of the objects of the class Indicator. Remember? - We're after an array of buffers.
class IndicatorArray { private: Indicator *array[]; public: IndicatorArray(int n) { ArrayResize(array, n); for(int i = 0; i < n; ++i) { array[i] = new Indicator(i); } } virtual ~IndicatorArray() { int n = ArraySize(array); for(int i = 0; i < n; ++i) { if(CheckPointer(array[i]) == POINTER_DYNAMIC) { delete array[i]; } } ArrayResize(array, 0); } Indicator *operator[](int n) const { return array[n]; } int size() const { return ArraySize(array); } };
Having these 2 classes we can start coding the indicator itself (I use old-fashined event handlers for simplicity).
#property indicator_chart_window #property indicator_buffers 4 #property indicator_color1 Red #property indicator_color2 Green #property indicator_color3 Blue #property indicator_color4 Yellow #property indicator_width4 3 #property strict extern int Period1 = 5; extern int Period2 = 11; extern int Period3 = 21; IndicatorArray buffers(4); // 1 single line magic! int periods[3]; double periodsSum; int init() { periods[0] = Period1; periods[1] = Period2; periods[2] = Period3; periodsSum = 1.0/Period1 + 1.0/Period2 + 1.0/Period3; SetIndexLabel(0, "MA " + IntegerToString(Period1)); SetIndexLabel(1, "MA " + IntegerToString(Period2)); SetIndexLabel(2, "MA " + IntegerToString(Period3)); SetIndexLabel(3, "MA SUM"); return 0; } int start() { int limit = MathMax(Bars - IndicatorCounted(), 1); for(int i = limit - 1; i >= 0; --i) { buffers[3][i] = 0; for(int j = 0; j < 3; ++j) { buffers[j][i] = iMA(_Symbol, _Period, periods[j], 0, MODE_EMA, PRICE_TYPICAL, i); buffers[3][i] += buffers[j][i] / periods[j]; } buffers[3][i] /= periodsSum; } return 0; }
Comment(buffers[3][0]);
will output strange integer number, which is obviously not a value of the 0-th element. If you try
the things get even worse - you'll get a code generation error (older versions of compiler generate bad ex4, while newer can't compile the code).
Comment(DoubleToString(buffers[3][0] + 0, _Digits));
double operator[](int b) { return buffer[b]; }
but we can't do that, because such method is already defined with different return type (remember Indicator *operator[](int b)?). We can use the following trick here:
double operator[](uint b) { return buffer[b]; }
class IndicatorGetter { private: Indicator *owner; int cursor; public: IndicatorGetter(Indicator &o) { owner = GetPointer(o); } double operator[](int b) { return owner[(uint)b]; } };
And the class Indicator creates an instance of IndicatorGetter internally in its contructor and makes it available outside via special method edit - here is the updated Indicator definition:
class Indicator { private: double buffer[]; int cursor; IndicatorGetter *instance; public: Indicator(int i) { SetIndexBuffer(i, buffer); instance = new IndicatorGetter(this); } IndicatorGetter *edit() const { return instance; } virtual ~Indicator() { delete instance; } ...
Finally, for convenience the helper class is accompanied by the class IndicatorArrayGetter supporting an array metaphor for the helper buffers, in the same way as IndicatorArray does for Indicator objects. Please, consult with the source code for further details. Also note that not all operators are overloaded in the example: if you use other ones, you need to extend the classes.