diff --git a/Indicator/Indicator.struct.cache.h b/Indicator/Indicator.struct.cache.h new file mode 100644 index 000000000..dba8e9a8e --- /dev/null +++ b/Indicator/Indicator.struct.cache.h @@ -0,0 +1,268 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2023, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * IndicatorBufferValueStorage class. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Refs.mqh" +#include "../Storage/ValueStorage.h" + +/** + * Holds buffers used to cache values calculated via OnCalculate methods. + */ +template +class IndicatorCalculateCache : public Dynamic { + public: + // Total number of calculated values. + int prev_calculated; + + // Number of prices to use. + int total; + + // Whether cache was initialized with price buffer. + bool initialized; + + // Buffer to store input prices. Won't be deleted! + ValueStorage *price_buffer; + + // Buffer to store input open prices. Won't be deleted! + ValueStorage *price_open_buffer; + + // Buffer to store input high prices. Won't be deleted! + ValueStorage *price_high_buffer; + + // Buffer to store input low prices. Won't be deleted! + ValueStorage *price_low_buffer; + + // Buffer to store input close prices. Won't be deleted! + ValueStorage *price_close_buffer; + + // Buffers used for OnCalculate calculations. + ARRAY(IValueStorage *, buffers); + + // Auxiliary caches related to this one. + ARRAY(IndicatorCalculateCache *, subcaches); + + /** + * Constructor. + */ + IndicatorCalculateCache(int _buffers_size = 0) { + prev_calculated = 0; + total = 0; + initialized = false; + Resize(_buffers_size); + } + + /** + * Destructor. + */ + ~IndicatorCalculateCache() { + int i; + + for (i = 0; i < ArraySize(buffers); ++i) { + if (buffers[i] != NULL) { + delete buffers[i]; + } + } + + for (i = 0; i < ArraySize(subcaches); ++i) { + if (subcaches[i] != NULL) { + delete subcaches[i]; + } + } + } + + /** + * Returns size of the current price buffer. + */ + int GetTotal() { return price_buffer != NULL ? ArraySize(price_buffer) : ArraySize(price_open_buffer); } + + /** + * Returns number of already calculated prices (bars). + */ + int GetPrevCalculated() { return prev_calculated; } + + /** + * Whether cache have any buffer. + */ + bool HasBuffers() { return ArraySize(buffers) != 0; } + + /** + * Returns number of added buffers. + */ + int NumBuffers() { return ArraySize(buffers); } + + /** + * Returns existing or new cache as a child of current one. Useful when indicator uses other indicators and requires + * unique caches for them. + */ + IndicatorCalculateCache *GetSubCache(int _idx) { + if (_idx >= ArraySize(subcaches)) { + ArrayResize(subcaches, _idx + 1, 10); + } + + if (subcaches[_idx] == NULL) { + subcaches[_idx] = new IndicatorCalculateCache(); + } + + return subcaches[_idx]; + } + + /** + * Add buffer of the given type. Usage: AddBuffer() + */ + template + int AddBuffer(int _num_buffers = 1) { + IValueStorage *_ptr; + + while (_num_buffers-- > 0) { + _ptr = new T(); + ArrayPushObject(buffers, _ptr); + } + + return ArraySize(buffers) - 1; + } + + /** + * Returns given calculation buffer. + */ + template + ValueStorage *GetBuffer(int _index) { + return (ValueStorage *)buffers[_index]; + } + + /** + * Returns main price buffer. + */ + ValueStorage *GetPriceBuffer() { return price_buffer; } + + /** + * Returns given price buffer. + */ + ValueStorage *GetPriceBuffer(ENUM_APPLIED_PRICE _applied_price) { + switch (_applied_price) { + case PRICE_OPEN: + return price_open_buffer; + case PRICE_HIGH: + return price_high_buffer; + case PRICE_LOW: + return price_low_buffer; + case PRICE_CLOSE: + return price_close_buffer; + } + return NULL; + } + + /** + * Sets price buffer for later use. + */ + void SetPriceBuffer(ValueStorage &_price, int _total = 0) { + price_buffer = &_price; + + if (_total == 0) { + _total = _price.Size(); + } + + total = _total; + + // Cache is ready to be used. + initialized = true; + } + + /** + * Sets price buffers for later use. + */ + void SetPriceBuffer(ValueStorage &_price_open, ValueStorage &_price_high, ValueStorage &_price_low, + ValueStorage &_price_close, int _total = 0) { + price_open_buffer = &_price_open; + price_high_buffer = &_price_high; + price_low_buffer = &_price_low; + price_close_buffer = &_price_close; + + if (_total == 0) { + _total = _price_open.Size(); + } + + total = _total; + + // Cache is ready to be used. + initialized = true; + } + + /** + * Resizes all buffers. + */ + void Resize(int _buffers_size) { + for (int i = 0; i < ArraySize(buffers); ++i) { + buffers[i].Resize(_buffers_size, 65535); + } + } + + /** + * Retrieves cached value from the given buffer. + */ + template + D GetValue(int _buffer_index, int _shift) { + return GetBuffer(_buffer_index).Fetch(_shift).Get(); + } + + /** + * Retrieves _shift-th (0 = most recent) cached value from the given buffer. + * + * @todo Return DBL_MAX in case the index is out of array boundary. + */ + template + D GetTailValue(int _buffer_index, int _shift) { + ValueStorage *_buff = GetBuffer(_buffer_index); + int _index = _buff.IsSeries() ? _shift : (ArraySize(_buff) - _shift - 1); + return _buff[_index].Get(); + } + + /** + * Updates prev_calculated value used by indicator's OnCalculate method. + */ + void SetPrevCalculated(int _prev_calculated) { + if (_prev_calculated == 0) { + ResetPrevCalculated(); + } else { + prev_calculated = _prev_calculated; + } + } + + /** + * Resets prev_calculated value used by indicator's OnCalculate method. + */ + void ResetPrevCalculated() { prev_calculated = 0; } + + /** + * Returns prev_calculated value used by indicator's OnCalculate method. + */ + int GetPrevCalculated(int _prev_calculated) { return prev_calculated; } +}; diff --git a/Indicator/Indicator.struct.signal.h b/Indicator/Indicator.struct.signal.h new file mode 100644 index 000000000..b90060014 --- /dev/null +++ b/Indicator/Indicator.struct.signal.h @@ -0,0 +1,149 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2023, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Includes Indicator's signal structs. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Forward declaration. +struct ChartParams; +struct IndicatorDataEntry; +struct IndicatorDataParams; +struct IndicatorParams; + +// Includes. +#include "Indicator.struct.h" + +/* Structure for indicator signals. */ +struct IndicatorSignal { + /* Define enumeration for indicator signals. */ + enum ENUM_INDICATOR_SIGNAL { + INDICATOR_SIGNAL_NONE = 0 << 0, // (None) + INDICATOR_SIGNAL_CROSSOVER = 1 << 0, // Values crossed over. + INDICATOR_SIGNAL_DIVERGENCE = 1 << 1, // Divergence between values and prices. + INDICATOR_SIGNAL_GT_PRICE = 1 << 2, // Last value greater than price. + INDICATOR_SIGNAL_INC = 1 << 3, // Last value increased. + INDICATOR_SIGNAL_LAST2SAME = 1 << 4, // Last 2 values are in the same direction. + INDICATOR_SIGNAL_PEAK = 1 << 5, // Last value is at peak. + INDICATOR_SIGNAL_VOLATILE = 1 << 6, // Last value change is more volatile. + }; + + unsigned int signals; // Store signals (@see: ENUM_INDICATOR_SIGNAL). + + // Constructors. + IndicatorSignal(int _signals = 0) : signals(_signals) {} + IndicatorSignal(ARRAY_REF(IndicatorDataEntry, _data), IndicatorDataParams &_idp, string _symbol, ENUM_TIMEFRAMES _tf, + int _m1 = 0, int _m2 = 0) + : signals(0) { + CalcSignals(_data, _idp, _symbol, _tf, _m1, _m2); + } + // Main methods. + // Calculate signal values. + void CalcSignals(ARRAY_REF(IndicatorDataEntry, _data), IndicatorDataParams &_idp, string _symbol, ENUM_TIMEFRAMES _tf, + int _m1 = 0, int _m2 = 0) { + int _size = ArraySize(_data); + // INDICATOR_SIGNAL_CROSSOVER + bool _is_cross = false; + if (_m1 != _m2) { + bool _is_cross_dl = (_data[0][_m2] - _data[0][_m1]) < 0 && (_data[_size - 1][_m2] - _data[_size - 1][_m1] > 0); + bool _is_cross_up = (_data[0][_m2] - _data[0][_m1]) > 0 && (_data[_size - 1][_m2] - _data[_size - 1][_m1] < 0); + _is_cross = _is_cross_dl || _is_cross_up; + } else { + if (_size >= 4) { + // @todo + } + } + SetSignal(INDICATOR_SIGNAL_CROSSOVER, _is_cross); + // INDICATOR_SIGNAL_DIVERGENCE + int _shift0 = ChartStatic::iBarShift(_symbol, _tf, _data[0].timestamp); + int _shift1 = ChartStatic::iBarShift(_symbol, _tf, _data[_size - 1].timestamp); + double _price_w0 = ChartStatic::iPrice(PRICE_WEIGHTED, _symbol, _tf, _shift0); + double _price_w1 = ChartStatic::iPrice(PRICE_WEIGHTED, _symbol, _tf, _shift1); + SetSignal(INDICATOR_SIGNAL_DIVERGENCE, + ((_price_w0 - _price_w1 > 0) && (_data[0][_m1] - _data[_size - 1][_m1]) < 0) || + ((_price_w0 - _price_w1) < 0 && (_data[0][_m1] - _data[_size - 1][_m1]) > 0)); + // INDICATOR_SIGNAL_GT_PRICE + bool _v_gt_p = false; + if (_idp.Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDVRANGE)) == IDATA_RANGE_PRICE) { + _v_gt_p = _data[0][_m1] > _price_w0 || _data[0][_m2] > _price_w0; + } else { + // @todo + } + SetSignal(INDICATOR_SIGNAL_GT_PRICE, _v_gt_p); + // INDICATOR_SIGNAL_INC + SetSignal(INDICATOR_SIGNAL_INC, _data[0][_m1] > _data[1][_m1]); + // INDICATOR_SIGNAL_LAST2SAME + if (_size > 2) { + bool _is_dec = _data[0][_m1] < _data[1][_m1] && _data[1][_m1] < _data[2][_m1]; + bool _is_inc = _data[0][_m1] > _data[1][_m1] && _data[1][_m1] > _data[2][_m1]; + SetSignal(INDICATOR_SIGNAL_LAST2SAME, _is_dec || _is_inc); + } + // INDICATOR_SIGNAL_PEAK + bool _is_peak_max = true, _is_peak_min = true; + for (int j = 1; j < _size && (_is_peak_max || _is_peak_min); j++) { + _is_peak_max &= _data[0][_m1] > _data[j][_m1]; + _is_peak_min &= _data[0][_m1] < _data[j][_m1]; + } + SetSignal(INDICATOR_SIGNAL_PEAK, _is_peak_max || _is_peak_min); + // INDICATOR_SIGNAL_VOLATILE + bool _is_vola = true; + double _diff0 = fabs(_data[0][_m1] - _data[1][_m1]); + for (int k = 1; k < _size - 1 && _is_vola; k++) { + _is_vola &= _diff0 > fabs(_data[k][_m1] - _data[k + 1][_m1]); + } + SetSignal(INDICATOR_SIGNAL_VOLATILE, _is_vola); + } + // Signal methods for bitwise operations. + /* Getters */ + bool CheckSignals(unsigned int _flags) { return (signals & _flags) != 0; } + bool CheckSignalsXor(unsigned int _flags) { return (signals ^ _flags) != 0; } + bool CheckSignalsAll(unsigned int _flags) { return (signals & _flags) == _flags; } + bool CheckSignalsXorAll(unsigned int _flags) { return (signals ^ _flags) == _flags; } + unsigned int GetSignals() { return signals; } + /* Setters */ + void AddSignals(unsigned int _flags) { signals |= _flags; } + void RemoveSignals(unsigned int _flags) { signals &= ~_flags; } + void SetSignal(ENUM_INDICATOR_SIGNAL _flag, bool _value = true) { + if (_value) { + AddSignals(_flag); + } else { + RemoveSignals(_flag); + } + } + void SetSignals(unsigned int _flags) { signals = _flags; } + // Serializers. + SerializerNodeType Serialize(Serializer &_s) { + // _s.Pass(this, "signals", signals, SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); + int _size = sizeof(int) * 8; + for (int i = 0; i < _size; i++) { + int _value = CheckSignals(1 << i) ? 1 : 0; + _s.Pass(this, (string)(i + 1), _value, SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); + } + return SerializerNodeObject; + } +}; diff --git a/Trade.mqh b/Trade.mqh index bb6fc778f..79e8af70c 100644 --- a/Trade.mqh +++ b/Trade.mqh @@ -1810,7 +1810,8 @@ HistorySelect(0, TimeCurrent()); // Select history for access. bool AddTask(TaskEntry &_tentry) { bool _is_valid = _tentry.IsValid(); if (_is_valid) { - tasks.Add(new TaskObject(_tentry, THIS_PTR, THIS_PTR)); + TaskObject _taskobj(_tentry, THIS_PTR, THIS_PTR); + tasks.Add(&_taskobj); } return _is_valid; }