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;
}