diff --git a/.clang-format b/.clang-format index 3442bc765..d64b4533c 100644 --- a/.clang-format +++ b/.clang-format @@ -2,3 +2,4 @@ Language: Cpp BasedOnStyle: Google ColumnLimit: 120 +IndentPPDirectives: BeforeHash diff --git a/Candle.struct.h b/Candle.struct.h index 3a260c578..74e9c9bab 100644 --- a/Candle.struct.h +++ b/Candle.struct.h @@ -26,8 +26,8 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Forward class declaration. @@ -262,9 +262,12 @@ struct CandleOCTOHLC : CandleOHLC { #endif /** - * Initializes candle with a given start time, lenght in seconds, first tick's timestamp and its price. + * Initializes candle with a given start time, length in seconds, first tick's timestamp and its price. */ void Init(int _start_time, int _length, long _timestamp_ms = -1, T _price = 0) { + if (_start_time < 0) { + Print("Error!"); + } is_complete = false; start_time = _start_time; length = _length; @@ -279,7 +282,16 @@ struct CandleOCTOHLC : CandleOHLC { */ void Update(long _timestamp_ms, T _price) { if (!ContainsTimeMs(_timestamp_ms)) { - Print("Error: Cannot update candle. Given time doesn't fit in candle's time-frame!"); + Print("Error: Cannot update candle. Given time doesn't fit in candle's time-frame! Given time ", _timestamp_ms, + ", but candle range is ", (long)start_time * 1000, " - ", (long)(start_time + length) * 1000, "."); + long _ms; + if (_timestamp_ms < (long)start_time * 1000) { + _ms = (long)start_time * 1000 - _timestamp_ms; + Print("Looks like given time is ", _ms, " ms (", (double)_ms / 1000, " s) before the candle starts."); + } else { + _ms = _timestamp_ms - (long)(start_time + length) * 1000; + Print("Looks like given time is ", _ms, " ms (", (double)_ms / 1000, "s) after the candle ends."); + } DebugBreak(); } @@ -288,6 +300,7 @@ struct CandleOCTOHLC : CandleOHLC { if (_is_init || _timestamp_ms < open_timestamp_ms) { open_timestamp_ms = _timestamp_ms; THIS_ATTR open = _price; + start_time = int(_timestamp_ms / 1000); } if (_is_init || _timestamp_ms > close_timestamp_ms) { close_timestamp_ms = _timestamp_ms; diff --git a/Dict.enum.h b/Dict.enum.h index 99474def2..c746f87e3 100644 --- a/Dict.enum.h +++ b/Dict.enum.h @@ -26,12 +26,12 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif -#define DICT_GROW_UP_PERCENT_DEFAULT 25 -#define DICT_PERFORMANCE_PROBLEM_AVG_CONFLICTS 10 +#define DICT_GROW_UP_PERCENT_DEFAULT 100 +#define DICT_PERFORMANCE_PROBLEM_AVG_CONFLICTS 20 /** * Whether Dict operates in yet uknown mode, as dict or as list. diff --git a/Dict.mqh b/Dict.mqh index 5d30563ce..f123cf539 100644 --- a/Dict.mqh +++ b/Dict.mqh @@ -90,6 +90,8 @@ class Dict : public DictBase { } void Clear() { + _DictSlots_ref = new DictSlotsRef(); + for (unsigned int i = 0; i < (unsigned int)ArraySize(THIS_ATTR _DictSlots_ref.DictSlots); ++i) { if (THIS_ATTR _DictSlots_ref.DictSlots[i].IsUsed()) THIS_ATTR _DictSlots_ref.DictSlots[i].SetFlags(0); } @@ -210,7 +212,7 @@ class Dict : public DictBase { /** * Inserts value into given array of DictSlots. */ - bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V value, bool allow_resize) { + bool InsertInto(DictSlotsRef*& dictSlotsRef, const K key, V value, bool allow_resize) { if (THIS_ATTR _mode == DictModeUnknown) THIS_ATTR _mode = DictModeDict; else if (THIS_ATTR _mode != DictModeDict) { @@ -315,7 +317,7 @@ class Dict : public DictBase { /** * Inserts hashless value into given array of DictSlots. */ - bool InsertInto(DictSlotsRef& dictSlotsRef, V value) { + bool InsertInto(DictSlotsRef*& dictSlotsRef, V value) { if (THIS_ATTR _mode == DictModeUnknown) THIS_ATTR _mode = DictModeList; else if (THIS_ATTR _mode != DictModeList) { @@ -353,6 +355,18 @@ class Dict : public DictBase { MathMax(10, (int)((float)ArraySize(THIS_ATTR _DictSlots_ref.DictSlots) * ((float)(percent + 100) / 100.0f)))); } + public: + /** + * Ensures that there is at least given number of slots in dict. + */ + bool Reserve(int _size) { + if (_size <= ArraySize(THIS_ATTR _DictSlots_ref.DictSlots)) { + return true; + } + return Resize(_size); + } + + protected: /** * Shrinks or expands array of DictSlots. */ @@ -362,7 +376,7 @@ class Dict : public DictBase { return true; } - DictSlotsRef new_DictSlots; + DictSlotsRef* new_DictSlots = new DictSlotsRef(); if (ArrayResize(new_DictSlots.DictSlots, new_size) == -1) return false; @@ -389,6 +403,8 @@ class Dict : public DictBase { // Freeing old DictSlots array. ArrayFree(THIS_ATTR _DictSlots_ref.DictSlots); + delete THIS_ATTR _DictSlots_ref; + THIS_ATTR _DictSlots_ref = new_DictSlots; return true; diff --git a/DictBase.mqh b/DictBase.mqh index 7a17246d7..187191f2d 100644 --- a/DictBase.mqh +++ b/DictBase.mqh @@ -62,12 +62,13 @@ class DictBase { _mode = DictModeUnknown; _flags = 0; overflow_listener = nullptr; + _DictSlots_ref = new DictSlotsRef(); } /** * Destructor. */ - ~DictBase() {} + ~DictBase() { delete _DictSlots_ref; } DictIteratorBase Begin() { // Searching for first item index. @@ -106,7 +107,7 @@ class DictBase { /** * Returns slot by key. */ - DictSlot* GetSlotByKey(DictSlotsRef& dictSlotsRef, const K _key, unsigned int& position) { + DictSlot* GetSlotByKey(DictSlotsRef*& dictSlotsRef, const K _key, unsigned int& position) { unsigned int numSlots = ArraySize(dictSlotsRef.DictSlots); if (numSlots == 0) return NULL; @@ -137,7 +138,7 @@ class DictBase { /** * Returns slot by position. */ - DictSlot* GetSlotByPos(DictSlotsRef& dictSlotsRef, const unsigned int position) { + DictSlot* GetSlotByPos(DictSlotsRef*& dictSlotsRef, const unsigned int position) { return dictSlotsRef.DictSlots[position].IsUsed() ? &dictSlotsRef.DictSlots[position] : NULL; } @@ -335,9 +336,9 @@ class DictBase { protected: /** - * Array of DictSlots. + * Pointer to array of DictSlots. */ - DictSlotsRef _DictSlots_ref; + DictSlotsRef* _DictSlots_ref; DictOverflowListener overflow_listener; unsigned int overflow_listener_max_conflicts; @@ -378,17 +379,50 @@ class DictBase { /** * Specialization of hashing function. */ - unsigned int Hash(unsigned int x) { return x; } + unsigned int Hash(float x) { return (unsigned int)((unsigned long)x * 10000 % 10000); } /** * Specialization of hashing function. */ - unsigned int Hash(int x) { return (unsigned int)x; } + unsigned int Hash(int value) { + value ^= (value >> 8); + value ^= (value << 3); + value ^= (value >> 9); + value ^= (value >> 4); + value ^= (value << 6); + value ^= (value >> 14); + return value; + } /** * Specialization of hashing function. */ - unsigned int Hash(float x) { return (unsigned int)((unsigned long)x * 10000 % 10000); } + unsigned int Hash(unsigned int value) { return Hash((int)value); } + + /** + * Specialization of hashing function. + */ + unsigned int Hash(long value) { + value ^= (value >> 33); + value ^= (value << 21); + value ^= (value >> 17); + + // Step 2: Combine upper and lower 32 bits to form a 32-bit hash + long hash = (int)(value ^ (value >> 32)); + + // Step 3: Further bit manipulation to spread the bits + hash ^= (hash >> 16); + hash *= 0x85ebca6b; // A large prime number + hash ^= (hash >> 13); + hash *= 0xc2b2ae35; // Another large prime number + hash ^= (hash >> 16); + return int(value >> 32); + } + + /** + * Specialization of hashing function. + */ + unsigned int Hash(unsigned long value) { return Hash((unsigned long)value); } }; #endif diff --git a/DictObject.mqh b/DictObject.mqh index 4a851cb31..63db36d87 100644 --- a/DictObject.mqh +++ b/DictObject.mqh @@ -21,8 +21,8 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif #include "Convert.basic.h" @@ -105,6 +105,8 @@ class DictObject : public DictBase { } void Clear() { + _DictSlots_ref = new DictSlotsRef(); + for (unsigned int i = 0; i < (unsigned int)ArraySize(this PTR_DEREF _DictSlots_ref.DictSlots); ++i) { this PTR_DEREF _DictSlots_ref.DictSlots[i].SetFlags(0); } @@ -210,7 +212,7 @@ class DictObject : public DictBase { /** * Inserts value into given array of DictSlots. */ - bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) { + bool InsertInto(DictSlotsRef*& dictSlotsRef, const K key, V& value, bool allow_resize) { if (THIS_ATTR _mode == DictModeUnknown) THIS_ATTR _mode = DictModeDict; else if (THIS_ATTR _mode != DictModeDict) { @@ -315,7 +317,7 @@ class DictObject : public DictBase { /** * Inserts hashless value into given array of DictSlots. */ - bool InsertInto(DictSlotsRef& dictSlotsRef, V& value) { + bool InsertInto(DictSlotsRef*& dictSlotsRef, V& value) { if (this PTR_DEREF _mode == DictModeUnknown) this PTR_DEREF _mode = DictModeList; else if (this PTR_DEREF _mode != DictModeList) { @@ -354,6 +356,18 @@ class DictObject : public DictBase { 10, (int)((float)ArraySize(this PTR_DEREF _DictSlots_ref.DictSlots) * ((float)(percent + 100) / 100.0f)))); } + public: + /** + * Ensures that there is at least given number of slots in dict. + */ + bool Reserve(int _size) { + if (_size <= ArraySize(THIS_ATTR _DictSlots_ref.DictSlots)) { + return true; + } + return Resize(_size); + } + + protected: /** * Shrinks or expands array of DictSlots. */ @@ -364,7 +378,7 @@ class DictObject : public DictBase { return true; } - DictSlotsRef new_DictSlots; + DictSlotsRef* new_DictSlots = new DictSlotsRef(); int i; @@ -389,7 +403,9 @@ class DictObject : public DictBase { // Freeing old DictSlots array. ArrayFree(this PTR_DEREF _DictSlots_ref.DictSlots); - this PTR_DEREF _DictSlots_ref = new_DictSlots; + delete THIS_ATTR _DictSlots_ref; + + THIS_ATTR _DictSlots_ref = new_DictSlots; return true; } diff --git a/DictSlotsRef.h b/DictSlotsRef.h index ff90c8b17..32da49a6e 100644 --- a/DictSlotsRef.h +++ b/DictSlotsRef.h @@ -26,8 +26,8 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -42,7 +42,8 @@ template class DictSlot; template -struct DictSlotsRef { +class DictSlotsRef { + public: ARRAY(DictSlot, DictSlots); // Incremental index for dict operating in list mode. @@ -61,14 +62,13 @@ struct DictSlotsRef { _avg_conflicts = 0; } - void operator=(DictSlotsRef& r) { - Util::ArrayCopy(DictSlots, r.DictSlots); - _list_index = r._list_index; - _num_used = r._num_used; - _num_conflicts = r._num_conflicts; - _avg_conflicts = r._avg_conflicts; - } + private: + /** + * Private assignment operator to avoid invalid copying. + */ + void operator=(DictSlotsRef& r) {} + public: /** * Adds given number of conflicts for an insert action, so we can store average number of conflicts. */ diff --git a/DictStruct.mqh b/DictStruct.mqh index 042e91afb..5b5c6e1c9 100644 --- a/DictStruct.mqh +++ b/DictStruct.mqh @@ -22,7 +22,7 @@ // Prevents processing this includes file for the second time. #ifndef __MQL__ -#pragma once + #pragma once #endif // Includes. @@ -90,6 +90,9 @@ class DictStruct : public DictBase { } void Clear() { + delete _DictSlots_ref; + _DictSlots_ref = new DictSlotsRef(); + for (unsigned int i = 0; i < (unsigned int)ArraySize(THIS_ATTR _DictSlots_ref.DictSlots); ++i) { THIS_ATTR _DictSlots_ref.DictSlots[i].SetFlags(0); } @@ -256,7 +259,7 @@ class DictStruct : public DictBase { /** * Inserts value into given array of DictSlots. */ - bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) { + bool InsertInto(DictSlotsRef*& dictSlotsRef, const K key, V& value, bool allow_resize) { if (THIS_ATTR _mode == DictModeUnknown) THIS_ATTR _mode = DictModeDict; else if (THIS_ATTR _mode != DictModeDict) { @@ -361,7 +364,7 @@ class DictStruct : public DictBase { /** * Inserts hashless value into given array of DictSlots. */ - bool InsertInto(DictSlotsRef& dictSlotsRef, V& value) { + bool InsertInto(DictSlotsRef*& dictSlotsRef, V& value) { if (THIS_ATTR _mode == DictModeUnknown) THIS_ATTR _mode = DictModeList; else if (THIS_ATTR _mode != DictModeList) { @@ -398,6 +401,18 @@ class DictStruct : public DictBase { MathMax(10, (int)((float)ArraySize(THIS_ATTR _DictSlots_ref.DictSlots) * ((float)(percent + 100) / 100.0f)))); } + public: + /** + * Ensures that there is at least given number of slots in dict. + */ + bool Reserve(int _size) { + if (_size <= ArraySize(THIS_ATTR _DictSlots_ref.DictSlots)) { + return true; + } + return Resize(_size); + } + + protected: /** * Shrinks or expands array of DictSlots. */ @@ -407,9 +422,11 @@ class DictStruct : public DictBase { return true; } - DictSlotsRef new_DictSlots; + DictSlotsRef* new_DictSlots = new DictSlotsRef(); - if (ArrayResize(new_DictSlots.DictSlots, new_size) == -1) return false; + if (ArrayResize(new_DictSlots.DictSlots, new_size) == -1) { + return false; + } int i; @@ -430,7 +447,9 @@ class DictStruct : public DictBase { } } // Freeing old DictSlots array. - ArrayFree(THIS_ATTR _DictSlots_ref.DictSlots); + ArrayFree(THIS_ATTR _DictSlots_ref PTR_DEREF DictSlots); + + delete THIS_ATTR _DictSlots_ref; THIS_ATTR _DictSlots_ref = new_DictSlots; diff --git a/Indicator/Indicator.h b/Indicator/Indicator.h index 3b2fe6f99..a91a5cdb0 100644 --- a/Indicator/Indicator.h +++ b/Indicator/Indicator.h @@ -691,10 +691,13 @@ class Indicator : public IndicatorData { } if (_LastError != ERR_SUCCESS) { - datetime _bar_dt = (datetime)_bar_time; - Print("Error: Code ", _LastError, " while trying to retrieve entry at shift ", _rel_shift, " (absolute ", - ToAbsShift(_rel_shift), "), mode ", _mode, ", time ", _bar_dt); - DebugBreak(); + if (_LastError != 4806) { + // Error occured and it's not "4806 Requested data not found". + datetime _bar_dt = (datetime)_bar_time; + Print("Error: Code ", _LastError, " while trying to retrieve entry at shift ", _rel_shift, " (absolute ", + ToAbsShift(_rel_shift), "), mode ", _mode, ", time ", _bar_dt); + DebugBreak(); + } } } THIS_ATTR GetEntryAlter(_entry, _rel_shift); diff --git a/Indicator/IndicatorCandle.h b/Indicator/IndicatorCandle.h index d9fe084a2..7b8ad1c86 100644 --- a/Indicator/IndicatorCandle.h +++ b/Indicator/IndicatorCandle.h @@ -25,8 +25,8 @@ #define INDICATOR_CANDLE_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -45,7 +45,7 @@ #include "TickBarCounter.h" #ifndef INDI_CANDLE_HISTORY_SIZE -#define INDI_CANDLE_HISTORY_SIZE 86400 + #define INDI_CANDLE_HISTORY_SIZE 86400 #endif // Indicator modes. @@ -96,11 +96,11 @@ class IndicatorCandle : public Indicator { */ IndicatorCandle(const TS& _icparams, const IndicatorDataParams& _idparams, IndicatorData* _indi_src = NULL, int _indi_mode = 0) - : Indicator(_icparams, _idparams, _indi_src, _indi_mode), history(INDI_CANDLE_HISTORY_SIZE) { + : Indicator(_icparams, _idparams, _indi_src, _indi_mode), history(THIS_PTR, INDI_CANDLE_HISTORY_SIZE) { Init(); } IndicatorCandle(ENUM_INDICATOR_TYPE _itype = INDI_CANDLE, int _shift = 0, string _name = "") - : Indicator(_itype, _shift, _name), history(INDI_CANDLE_HISTORY_SIZE) { + : Indicator(_itype, _shift, _name), history(THIS_PTR, INDI_CANDLE_HISTORY_SIZE) { Init(); } @@ -338,7 +338,14 @@ class IndicatorCandle : public Indicator { /** * Called when data source emits new entry (new one in ascending order). */ - void OnDataSourceEntry(IndicatorDataEntry& entry) override { + void OnDataSourceEntry(IndicatorDataEntry& entry, + ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) override { + Indicator::OnDataSourceEntry(entry, type); + + if (type != INDI_EMITTED_ENTRY_TYPE_TICK) { + return; + } + // Parent indicator (e.g., Indi_TickMt) emitted an entry containing tick's // ask and bid price. As an abstract class, we really don't know how to // update/create candles so we just pass the entry into history's @@ -346,6 +353,20 @@ class IndicatorCandle : public Indicator { history.GetItemProvider() PTR_DEREF OnTick(&history, entry.timestamp * 1000, (float)entry[0], (float)entry[1]); }; + /** + * Called when data source expects to emit given number of entries for given type. + * + * Called e.g., from Tick indicator in order Candle indicator to enlarge + * possible history size by given number of entries. We have to do that, + * because otherwise, we could end up with OnCalculate() working on partial + * history candles. + */ + void OnDataSourceWillEmitEntries(ENUM_INDI_EMITTED_ENTRY_TYPE _type, int _num_entries) override { + if (_type == INDI_EMITTED_ENTRY_TYPE_CANDLE) { + idata.Reserve(_num_entries); + } + } + /** * Returns value storage of given kind. */ diff --git a/Indicator/IndicatorCandle.provider.h b/Indicator/IndicatorCandle.provider.h index ccecbd14e..1b45a6cac 100644 --- a/Indicator/IndicatorCandle.provider.h +++ b/Indicator/IndicatorCandle.provider.h @@ -25,8 +25,8 @@ #define INDICATOR_CANDLE_PROVIDER_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -34,7 +34,7 @@ #include "../Storage/ItemsHistory.h" /** - * Regenerates candles and updates exising candles from new ticks. Subclasses by IndicatorTf, IndicatorRenko. + * Regenerates candles and updates exising candles from new ticks. Subclassed by IndicatorTf, IndicatorRenko. */ template class ItemsHistoryCandleProvider : public ItemsHistoryItemProvider> { @@ -56,11 +56,19 @@ class ItemsHistoryCandleProvider : public ItemsHistoryItemProvider, ItemsHistoryCandleProvider>* _history, long _from_time_ms, + bool GetItems(ItemsHistory, ItemsHistoryCandleProvider>* _history, long _from_time_ms, ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(CandleOCTOHLC, _out_arr)) { // Method is called if there is a missing item (candle) in the history. We need to regenerate it. - Print("Error: Retrieving items by this item provider is not implemented!"); - DebugBreak(); + return false; + } + + /** + * Retrieves items between given indices (both indices inclusive). Should return false if retrieving items by this + * method is not available. + */ + bool GetItems(ItemsHistory, ItemsHistoryCandleProvider>* _history, int _start_index, + int _end_index, ARRAY_REF(CandleOCTOHLC, _out_arr)) { + return false; } }; diff --git a/Indicator/IndicatorData.h b/Indicator/IndicatorData.h index 3fc41671b..01befb765 100644 --- a/Indicator/IndicatorData.h +++ b/Indicator/IndicatorData.h @@ -25,8 +25,8 @@ #define INDICATOR_DATA_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Forward class declaration. @@ -1454,6 +1454,7 @@ class IndicatorData : public IndicatorBase { virtual ENUM_TIMEFRAMES GetTf() { return GetCandle() PTR_DEREF GetTf(); } /** + /** * Traverses source indicators' hierarchy and tries to find Ask, Bid, Spread, * Volume and Tick Volume-featured indicator. IndicatorTick satisfies such * requirements. @@ -1813,10 +1814,21 @@ class IndicatorData : public IndicatorBase { /** * Sends entry to listening indicators. */ - void EmitEntry(IndicatorDataEntry& entry) { + void EmitEntry(IndicatorDataEntry& _entry, ENUM_INDI_EMITTED_ENTRY_TYPE _type = INDI_EMITTED_ENTRY_TYPE_PARENT) { for (int i = 0; i < ArraySize(listeners); ++i) { if (listeners[i].ObjectExists()) { - listeners[i] REF_DEREF OnDataSourceEntry(entry); + listeners[i].Ptr().OnDataSourceEntry(_entry, _type); + } + } + } + + /** + * Sends information about expected number of emitted entries to listening indicators. + */ + void WillEmitEntries(ENUM_INDI_EMITTED_ENTRY_TYPE _type, int _num_entries) { + for (int i = 0; i < ArraySize(listeners); ++i) { + if (listeners[i].ObjectExists()) { + listeners[i].Ptr().OnDataSourceWillEmitEntries(_type, _num_entries); } } } @@ -1880,7 +1892,18 @@ class IndicatorData : public IndicatorBase { /** * Called when data source emits new entry (historic or future one). */ - virtual void OnDataSourceEntry(IndicatorDataEntry& entry){}; + virtual void OnDataSourceEntry(IndicatorDataEntry& entry, + ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) {} + + /** + * Called when data source expects to emit given number of entries for given type. + * + * Called e.g., from Tick indicator in order Candle indicator to enlarge + * possible history size by given number of entries. We have to do that, + * because otherwise, we could end up with OnCalculate() working on partial + * history candles. + */ + virtual void OnDataSourceWillEmitEntries(ENUM_INDI_EMITTED_ENTRY_TYPE _type, int _num_entries) {} /** * Called when new tick is retrieved from attached data source. diff --git a/Indicator/IndicatorRenko.h b/Indicator/IndicatorRenko.h index cea1ff7a0..89d459b46 100644 --- a/Indicator/IndicatorRenko.h +++ b/Indicator/IndicatorRenko.h @@ -30,8 +30,8 @@ #define INDICATOR_RENKO_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -171,97 +171,103 @@ class IndicatorRenko : public IndicatorCandle>::OnDataSourceEntry(entry, type); - if (entry.timestamp < last_entry_ts) { - Print("Error: IndicatorRenko doesn't support sending entries in non-ascending order!"); - DebugBreak(); - } + if (type != INDI_EMITTED_ENTRY_TYPE_TICK) { + return; + } + /* + @todo Move logic into ItemsHistoryRenkoCandleProvider class. - // We'll be updating candle from bid price. - double _price = entry[1]; + if (entry.timestamp < last_entry_ts) { + Print("Error: IndicatorRenko doesn't support sending entries in non-ascending order!"); + DebugBreak(); + } - CandleOCTOHLC _candle; - CandleOCTOHLC _last_completed_candle; - ENUM_INDI_RENKO_CANDLE_TYPE _last_completed_candle_type; + // We'll be updating candle from bid price. + double _price = entry[1]; - if (last_completed_candle_ts != 0) { - _last_completed_candle = icdata.GetByKey(last_completed_candle_ts); - _last_completed_candle_type = GetCandleType(_last_completed_candle); - } else { - _last_completed_candle_type = INDI_RENKO_CANDLE_TYPE_NONE; - } + CandleOCTOHLC _candle; + CandleOCTOHLC _last_completed_candle; + ENUM_INDI_RENKO_CANDLE_TYPE _last_completed_candle_type; - if (last_incomplete_candle_ts != 0) { - // There is previous candle. Retrieving and updating it. - _candle = icdata.GetByKey(last_incomplete_candle_ts); - _candle.Update(entry.timestamp, _price); + if (last_completed_candle_ts != 0) { + _last_completed_candle = icdata.GetByKey(last_completed_candle_ts); + _last_completed_candle_type = GetCandleType(_last_completed_candle); + } else { + _last_completed_candle_type = INDI_RENKO_CANDLE_TYPE_NONE; + } - // Checking for close price difference. - if (RenkoConditionMet(_last_completed_candle_type, _candle, _price)) { - // Closing current candle. - _candle.is_complete = true; - } + if (last_incomplete_candle_ts != 0) { + // There is previous candle. Retrieving and updating it. + _candle = icdata.GetByKey(last_incomplete_candle_ts); + _candle.Update(entry.timestamp, _price); + + // Checking for close price difference. + if (RenkoConditionMet(_last_completed_candle_type, _candle, _price)) { + // Closing current candle. + _candle.is_complete = true; + } - // Updating candle. - icdata.Add(_candle, last_incomplete_candle_ts); + // Updating candle. + icdata.Add(_candle, last_incomplete_candle_ts); - Print("Updated Candle: ", _candle.ToString()); + Print("Updated Candle: ", _candle.ToString()); - if (_candle.is_complete) { - last_completed_candle_ts = last_incomplete_candle_ts; - last_incomplete_candle_ts = 0; - } + if (_candle.is_complete) { + last_completed_candle_ts = last_incomplete_candle_ts; + last_incomplete_candle_ts = 0; + } + } else { + // There is no incomplete candle, creating one. + if (last_completed_candle_ts != 0) { + // Price of the last candle will be used to initialize open price for new, incomplete candle. + double _last_close_price = _last_completed_candle.close; + _candle = CandleOCTOHLC(_last_close_price, _last_close_price, _last_close_price, _last_close_price, + entry.timestamp, entry.timestamp); + // Current price will be added to newly created incomplete candle. + _candle.Update(entry.timestamp, _price); } else { - // There is no incomplete candle, creating one. - if (last_completed_candle_ts != 0) { - // Price of the last candle will be used to initialize open price for new, incomplete candle. - double _last_close_price = _last_completed_candle.close; - _candle = CandleOCTOHLC(_last_close_price, _last_close_price, _last_close_price, _last_close_price, - entry.timestamp, entry.timestamp); - // Current price will be added to newly created incomplete candle. - _candle.Update(entry.timestamp, _price); - } else { - // There was no completed candle. Creating new, incomplete candle from current price. - _candle = CandleOCTOHLC(_price, _price, _price, _price, entry.timestamp, entry.timestamp); - } + // There was no completed candle. Creating new, incomplete candle from current price. + _candle = CandleOCTOHLC(_price, _price, _price, _price, entry.timestamp, entry.timestamp); + } - _candle.is_complete = false; + _candle.is_complete = false; - // Creating new candle. - icdata.Add(_candle, entry.timestamp); + // Creating new candle. + icdata.Add(_candle, entry.timestamp); - Print("Added candle: ", _candle.ToString(), " now there is ", icdata.Size(), " candles in the buffer."); + Print("Added candle: ", _candle.ToString(), " now there is ", icdata.Size(), " candles in the buffer."); - last_incomplete_candle_ts = entry.timestamp; - } + last_incomplete_candle_ts = entry.timestamp; + } - static int iteration = 0; + static int iteration = 0; - ++iteration; + ++iteration; - Print("Iteration: ", iteration); + Print("Iteration: ", iteration); - if (iteration > 1793) { - // Print(icdata.ToJSON()); - } + if (iteration > 1793) { + // Print(icdata.ToJSON()); + } - Print("Last Incomplete Time: ", TimeToString(last_incomplete_candle_ts, TIME_DATE | TIME_MINUTES | - TIME_SECONDS), " (", last_incomplete_candle_ts, ")"); Print("Last Incomplete Candle: ", - icdata.GetByKey(last_incomplete_candle_ts).ToString()); Print("Last Completed Time: ", - TimeToString(last_completed_candle_ts, TIME_DATE | TIME_MINUTES | TIME_SECONDS), " (", last_completed_candle_ts, - ")"); Print("Last Completed Candle: ", icdata.GetByKey(last_completed_candle_ts).ToString()); + Print("Last Incomplete Time: ", TimeToString(last_incomplete_candle_ts, TIME_DATE | TIME_MINUTES | + TIME_SECONDS), " (", last_incomplete_candle_ts, ")"); Print("Last Incomplete Candle: ", + icdata.GetByKey(last_incomplete_candle_ts).ToString()); Print("Last Completed Time: ", + TimeToString(last_completed_candle_ts, TIME_DATE | TIME_MINUTES | TIME_SECONDS), " (", last_completed_candle_ts, + ")"); Print("Last Completed Candle: ", icdata.GetByKey(last_completed_candle_ts).ToString()); - // Updating tick & bar indices. Bar time is time of the last completed candle. - // Print(last_completed_candle_ts); - counter.OnTick(last_completed_candle_ts); + // Updating tick & bar indices. Bar time is time of the last completed candle. + // Print(last_completed_candle_ts); + counter.OnTick(last_completed_candle_ts); - Print("---------"); + Print("---------"); - last_entry_ts = entry.timestamp; - */ + last_entry_ts = entry.timestamp; + */ }; /** diff --git a/Indicator/IndicatorRenko.provider.h b/Indicator/IndicatorRenko.provider.h index e987f12ba..63c483a80 100644 --- a/Indicator/IndicatorRenko.provider.h +++ b/Indicator/IndicatorRenko.provider.h @@ -25,8 +25,8 @@ #define INDICATOR_RENKO_PROVIDER_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif /** @@ -68,12 +68,24 @@ class ItemsHistoryRenkoCandleProvider : public ItemsHistoryCandleProvider { * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we * want previous or next items from selected starting point. */ - void GetItems(ItemsHistory, ItemsHistoryRenkoCandleProvider>* _history, long _from_time_ms, + bool GetItems(ItemsHistory, ItemsHistoryRenkoCandleProvider>* _history, long _from_time_ms, ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(CandleOCTOHLC, _out_arr)) { // Method is called if there is a missing item (candle) in the history. We need to regenerate it. Print("IndicatorRenko is not yet implemented!"); DebugBreak(); + return false; + } + + /** + * Retrieves items between given indices (both indices inclusive). Should return false if retrieving items by this + * method is not available. + */ + bool GetItems(ItemsHistory, ItemsHistoryRenkoCandleProvider>* _history, int _start_index, + int _end_index, ARRAY_REF(CandleOCTOHLC, _out_arr)) { + Print("IndicatorRenko is not yet implemented!"); + DebugBreak(); + return false; } }; diff --git a/Indicator/IndicatorTf.h b/Indicator/IndicatorTf.h index 8f9f90ee9..f00021569 100644 --- a/Indicator/IndicatorTf.h +++ b/Indicator/IndicatorTf.h @@ -25,11 +25,12 @@ #define INDICATOR_TF_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. +#include "../Chart.struct.tf.h" #include "IndicatorCandle.h" #include "IndicatorTf.provider.h" @@ -47,8 +48,7 @@ class IndicatorTf : public IndicatorCandle(THIS_ATTR iparams.GetSecsPerCandle(), THIS_PTR)); + history.SetItemProvider(new ItemsHistoryTfCandleProvider(ChartTf::TfToSeconds(GetTf()), THIS_PTR)); } public: @@ -59,26 +59,35 @@ class IndicatorTf : public IndicatorCandle>(_icparams, _idparams) { + IndicatorTf(ENUM_TIMEFRAMES_INDEX _tfi = 0) { + SetTf(ChartTf::IndexToTf(_tfi)); Init(); } + /** + * Class constructor with parameters. + */ + IndicatorTf(TFP& _icparams, const IndicatorDataParams& _idparams) { Init(); } + /** * Gets indicator's time-frame. */ ENUM_TIMEFRAMES GetTf() override { return THIS_ATTR iparams.tf.GetTf(); } + /** + * Sets indicator's time-frame. + */ + void SetTf(ENUM_TIMEFRAMES _tf) { THIS_ATTR iparams.tf.SetTf(_tf); } + /** * Returns current tick index (incremented every OnTick()). */ diff --git a/Indicator/IndicatorTf.provider.h b/Indicator/IndicatorTf.provider.h index a59ae9c08..555ffb9b9 100644 --- a/Indicator/IndicatorTf.provider.h +++ b/Indicator/IndicatorTf.provider.h @@ -25,8 +25,8 @@ #define INDICATOR_TF_PROVIDER_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif /** @@ -116,28 +116,58 @@ class ItemsHistoryTfCandleProvider : public ItemsHistoryCandleProvider { /** * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we - * want previous or next items from selected starting point. + * want previous or next items from selected starting point. Should return false if retrieving items by this method + * is not available. */ - void GetItems(ItemsHistory, ItemsHistoryTfCandleProvider>* _history, long _from_time_ms, + bool GetItems(ItemsHistory, ItemsHistoryTfCandleProvider>* _history, long _from_time_ms, ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(CandleOCTOHLC, _out_arr)) { // Method is called if there is a missing item (candle) in the history. We need to regenerate it. if (_from_time_ms != 0) { // In order to (re)generate candle, we need to fetch ticks within a fixed time-frame and then update that candle // with all fetched ticks. For IndicatorTf, there is no difference between (re)generating backwards or forwards, // as candles are time-framed. The only problem is that there may be missing candles in fetched time-frames. We - // just need to skip such time-frame and fetch ticks for next time-frame. In order to determine + // just need to skip such time-frame and fetch ticks for next time-frame. IndicatorData* _indi_tick = indi PTR_DEREF GetTick(); // Ticks to form a candle. static ARRAY(TickTAB, _ticks); + if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { + // For backward direction we need to start from the beginning of the candle as _from_time_ms determines it's + // ending. + //_ticks_to_ms = _ticks_from_ms + (_candle_length_ms - 1); + } else { + //_ticks_to_ms = _ticks_from_ms - (_candle_length_ms - 1); + } + + bool _is_first_item = true; + while (_num_items > 0) { // Calculating time from which and to which we want to retrieve ticks to form a candle. int _ticks_from_s = GetCandleTimeFromTimeMs(_from_time_ms, spc); long _ticks_from_ms = (long)_ticks_from_s * 1000; long _candle_length_ms = (long)spc * 1000; - long _ticks_to_ms = _ticks_from_ms + _candle_length_ms - 1; + long _ticks_to_ms; + + _ticks_to_ms = _ticks_from_ms + (_candle_length_ms - 1); + + if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + // Backwards. + if (_is_first_item) { + // As _from_time_ms in backward direction is next candle time - 1ms + // then we need to include it in our calculations. + long _new_start_ms = _from_time_ms - (_candle_length_ms - 1); + long _new_end_ms = _from_time_ms; + + _ticks_from_ms = _new_start_ms; + _ticks_from_s = int(_new_start_ms / 1000); + _from_time_ms = _new_start_ms; + _ticks_to_ms = _new_end_ms; + + _is_first_item = false; + } + } // We will try to fetch history by two methods. // 1. By time range if IndicatorTick supports that way. @@ -159,7 +189,13 @@ class ItemsHistoryTfCandleProvider : public ItemsHistoryCandleProvider { // Adding candle to the output array. ArrayPushObject(_out_arr, _candle); - --_num_items; + } + + // Even if we don't form an item (a candle), we assume we've done one item. + --_num_items; + + if (_num_items % 10000 == 0) { + Print(_num_items, " left to process..."); } if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { @@ -172,6 +208,17 @@ class ItemsHistoryTfCandleProvider : public ItemsHistoryCandleProvider { Print("Error: GetItems() for IndicatorTf can only work with given _from_time_ms!"); DebugBreak(); } + + return true; + } + + /** + * Retrieves items between given indices (both indices inclusive). Should return false if retrieving items by this + * method is not available. + */ + bool GetItems(ItemsHistory, ItemsHistoryTfCandleProvider>* _history, int _start_index, + int _end_index, ARRAY_REF(CandleOCTOHLC, _out_arr)) { + return false; } /** diff --git a/Indicator/IndicatorTf.struct.h b/Indicator/IndicatorTf.struct.h index dd466eda3..606023e17 100644 --- a/Indicator/IndicatorTf.struct.h +++ b/Indicator/IndicatorTf.struct.h @@ -26,8 +26,8 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -36,25 +36,8 @@ /* Structure for IndicatorTf class parameters. */ struct IndicatorTfParams : IndicatorParams { ChartTf tf; - - /* - @todo - - unsigned int spc; // Seconds per candle. - */ - // Struct constructor. - IndicatorTfParams(string _name, ENUM_TIMEFRAMES _tf) : IndicatorParams(_name), tf(_tf) {} - - // Getters. - unsigned int GetSecsPerCandle() { return tf.GetInSeconds(); } - - /* - @todo - // Setters. - void SetSecsPerCandle(unsigned int _spc) { spc = _spc; } - */ - + IndicatorTfParams(string _name, ENUM_TIMEFRAMES _tf) : IndicatorParams(_name) { tf.SetTf(_tf); } // Copy constructor. IndicatorTfParams(const IndicatorTfParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) { THIS_REF = _params; diff --git a/Indicator/IndicatorTick.h b/Indicator/IndicatorTick.h index 29c298696..f9bbfbb9e 100644 --- a/Indicator/IndicatorTick.h +++ b/Indicator/IndicatorTick.h @@ -25,8 +25,8 @@ #define INDICATOR_TICK_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -83,7 +83,7 @@ class IndicatorTick : public Indicator { */ IndicatorTick(string _symbol, const TS& _itparams, const IndicatorDataParams& _idparams, IndicatorData* _indi_src = NULL, int _indi_mode = 0) - : Indicator(_itparams, _idparams, _indi_src, _indi_mode) { + : Indicator(_itparams, _idparams, _indi_src, _indi_mode), history(THIS_PTR) { itparams = _itparams; if (_indi_src != NULL) { THIS_ATTR SetDataSource(_indi_src, _indi_mode); @@ -92,7 +92,7 @@ class IndicatorTick : public Indicator { Init(); } IndicatorTick(string _symbol, ENUM_INDICATOR_TYPE _itype = INDI_CANDLE, int _shift = 0, string _name = "") - : Indicator(_itype, _shift, _name) { + : Indicator(_itype, _shift, _name), history(THIS_PTR) { symbol = _symbol; Init(); } diff --git a/Indicator/IndicatorTick.provider.h b/Indicator/IndicatorTick.provider.h index 2a2dccd4b..1b4e5a62d 100644 --- a/Indicator/IndicatorTick.provider.h +++ b/Indicator/IndicatorTick.provider.h @@ -25,8 +25,8 @@ #define INDICATOR_TICK_PROVIDER_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -57,12 +57,21 @@ class ItemsHistoryTickProvider : public ItemsHistoryItemProvider> { /** * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we - * want previous or next items from selected starting point. + * want previous or next items from selected starting point. Should return false if retrieving items by this method + * is not available. */ - void GetItems(ItemsHistory, ItemsHistoryTickProvider>* _history, long _from_time_ms, + bool GetItems(ItemsHistory, ItemsHistoryTickProvider>* _history, long _from_time_ms, ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(TickTAB, _out_arr)) { - // Method is called if there is a missing item (tick) in the history. We need to regenerate it. - indi PTR_DEREF FetchHistoryByStartTimeAndCount(_from_time_ms, _dir, _num_items, _out_arr); + return false; + } + + /** + * Retrieves items between given indices (both indices inclusive). Should return false if retrieving items by this + * method is not available. + */ + bool GetItems(ItemsHistory, ItemsHistoryTickProvider>* _history, int _start_index, int _end_index, + ARRAY_REF(TickTAB, _out_arr)) { + return false; } /** diff --git a/Indicator/tests/classes/IndicatorTfDummy.h b/Indicator/tests/classes/IndicatorTfDummy.h index e3e7ab430..0648a1908 100644 --- a/Indicator/tests/classes/IndicatorTfDummy.h +++ b/Indicator/tests/classes/IndicatorTfDummy.h @@ -25,8 +25,8 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -44,12 +44,6 @@ struct IndicatorTfDummyParams : IndicatorTfParams { */ class IndicatorTfDummy : public IndicatorTf { public: - /* - @todo - - IndicatorTfDummy(unsigned int _spc) : IndicatorTf(_spc) {} - */ - IndicatorTfDummy(ENUM_TIMEFRAMES _tf) : IndicatorTf(IndicatorTfDummyParams(_tf), IndicatorDataParams()) { Init(); } IndicatorTfDummy(ENUM_TIMEFRAMES_INDEX _tfi) : IndicatorTf(IndicatorTfDummyParams(ChartTf::IndexToTf(_tfi)), IndicatorDataParams()) { @@ -63,11 +57,17 @@ class IndicatorTfDummy : public IndicatorTf { string GetName() override { return "IndicatorTfDummy(" + iparams.tf.GetString() + ")"; } - void OnDataSourceEntry(IndicatorDataEntry& entry) override { + void OnDataSourceEntry(IndicatorDataEntry& entry, + ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) override { + IndicatorTf::OnDataSourceEntry(entry, type); + + if (type != INDI_EMITTED_ENTRY_TYPE_TICK) { + return; + } + // When overriding OnDataSourceEntry() we have to remember to call parent // method, because IndicatorCandle also need to invoke it in order to // create/update matching candle. - IndicatorTf::OnDataSourceEntry(entry); #ifdef __debug_indicator__ Print(GetFullName(), " got new tick at ", entry.timestamp, diff --git a/IndicatorLegacy.h b/IndicatorLegacy.h index 188d02fd7..31ee7226e 100644 --- a/IndicatorLegacy.h +++ b/IndicatorLegacy.h @@ -4,86 +4,138 @@ */ #ifndef __MQL__ -#pragma once + #pragma once #endif +// Includes. +#include "Platform.h" + #ifdef INDICATOR_LEGACY_VERSION_MT4 -#define INDICATOR_LEGACY_VERSION_DEFINED + #define INDICATOR_LEGACY_VERSION_DEFINED #endif #ifdef INDICATOR_LEGACY_VERSION_MT5 -#define INDICATOR_LEGACY_VERSION_DEFINED + #define INDICATOR_LEGACY_VERSION_DEFINED #endif -#ifndef INDICATOR_LEGACY_VERSION_DEFINED -#define INDICATOR_LEGACY_VERSION_MT5 -#define INDICATOR_LEGACY_VERSION_DEFINED +#ifdef INDICATOR_STANDALONE_VERSION_LONG + #define INDICATOR_STANDALONE_VERSION_DEFINED #endif -#ifdef __MQL4__ +#ifdef INDICATOR_STANDALONE_VERSION_SHORT + #define INDICATOR_STANDALONE_VERSION_DEFINED +#endif -#include -#include -#include -#include -#include +#ifdef INDICATOR_STANDALONE_VERSION_DEFINED + +/** + * Wrapper for future OnInit(). We may need to init indicator with candle and + * tick inidicator in the hierarchy. + */ +int OnInit() { + int _result = OnInitOriginal(); + Platform::AddWithDefaultBindings(INDICATOR_STANDALONE_INDI_PTR, Symbol(), (ENUM_TIMEFRAMES)Period()); + return _result; +} + + #define OnInit OnInitOriginal + + // In standalone mode without legacy mode we also need to wrap OnCalculate() as + // we need to call Platform::OnCalculate(). The only difference is that we + // don't need to change buffer's AsSeries flag in MT5. + #ifndef INDICATOR_LEGACY_VERSION_DEFINED + #ifndef INDICATOR_LEGACY_EMIT_ONCALCULATE_WRAPPER + #define INDICATOR_LEGACY_EMIT_ONCALCULATE_WRAPPER + #endif + + // No work required for buffers. + #define INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER + #define INDICATOR_LEGACY_VERSION_RELEASE_BUFFER + #ifdef INDICATOR_STANDALONE_VERSION_LONG + // We don't want to write two OnCalculate() wrappers. One is enough. + #define INDICATOR_LEGACY_VERSION_LONG + #endif // INDICATOR_STANDALONE_VERSION_LONG + #ifdef INDICATOR_STANDALONE_VERSION_SHORT + // We don't want to write two OnCalculate() wrappers. One is enough. + #define INDICATOR_LEGACY_VERSION_SHORT + #endif // INDICATOR_STANDALONE_VERSION_LONG + #endif // INDICATOR_LEGACY_VERSION_DEFINED + +#endif // INDICATOR_STANDALONE_VERSION_DEFINED #ifndef INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER -#define INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER + #define INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER #endif #ifndef INDICATOR_LEGACY_VERSION_RELEASE_BUFFER -#define INDICATOR_LEGACY_VERSION_RELEASE_BUFFER + #define INDICATOR_LEGACY_VERSION_RELEASE_BUFFER #endif -#ifdef INDICATOR_LEGACY_VERSION_MT5 +#ifdef INDICATOR_LEGACY_EMIT_ONCALCULATE_WRAPPER -#ifndef INDICATOR_LEGACY_VERSION_SHORT + #include + #include + #include + #include + #include + + #ifdef INDICATOR_LEGACY_VERSION_SHORT /** - * Replacement for future OHLC-based OnCalculate(). + * Wrapper for future price-based OnCalculate(). */ -int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], - const double& high[], const double& low[], const double& close[], const long& tick_volume[], - const long& volume[], const int& spread[]) { +int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double& price[]) { // We need to call Platform::Tick() and maybe also IndicatorData::EmitHistory() before. Platform::OnCalculate(rates_total, prev_calculated); INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER; - int _num_calculated = - OnCalculateMT5(rates_total, prev_calculated, time, open, high, low, close, tick_volume, volume, spread); + // NOTE: If compiler sees an error here about 'time' parameter conversion + // then you probably must do: + // #define INDICATOR_LEGACY_VERSION_LONG + // before including IndicatorLegacy.h + int _num_calculated = OnCalculateMT5(rates_total, prev_calculated, begin, price); INDICATOR_LEGACY_VERSION_RELEASE_BUFFER; return _num_calculated; } -#else + #endif // INDICATOR_LEGACY_VERSION_SHORT + + #ifdef INDICATOR_LEGACY_VERSION_LONG /** - * Replacement for future price-based OnCalculate(). + * Wrapper for future OHLC-based OnCalculate(). */ -int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double& price[]) { +int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], + const double& high[], const double& low[], const double& close[], const long& tick_volume[], + const long& volume[], const int& spread[]) { // We need to call Platform::Tick() and maybe also IndicatorData::EmitHistory() before. Platform::OnCalculate(rates_total, prev_calculated); - INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER; - - // NOTE: If compiler sees an error here about parameter conversion then you - // probably must do: + // NOTE: If compiler sees an error here about parameter conversion + // then you probably must do: // #define INDICATOR_LEGACY_VERSION_SHORT // before including IndicatorLegacy.h - int _num_calculated = OnCalculateMT5(rates_total, prev_calculated, begin, price); + INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER; + + int _num_calculated = + OnCalculateMT5(rates_total, prev_calculated, time, open, high, low, close, tick_volume, volume, spread); INDICATOR_LEGACY_VERSION_RELEASE_BUFFER; return _num_calculated; } -#endif + #endif // INDICATOR_LEGACY_VERSION_LONG -#define OnCalculate OnCalculateMT5 + #define OnCalculate OnCalculateMT5 + +#endif // INDICATOR_LEGACY_EMIT_ONCALCULATE_WRAPPER + +#ifdef __MQL4__ + #ifdef INDICATOR_LEGACY_VERSION_MT5 /** * Wrapper class to be used by MQL4 code to allow calling MQL5's indicator functions like iMA() in MQL4. @@ -188,10 +240,10 @@ int CopyBuffer(int _handle, int _mode, int _start, int _count, double& _buffer[] return _num_copied; } -/** - * Defines wrapper class and global iNAME() indicator function (e.g., iMA(), iATR()). - */ -// Print(#FN_NAME " key = ", _key); \ + /** + * Defines wrapper class and global iNAME() indicator function (e.g., iMA(), iATR()). + */ + // Print(#FN_NAME " key = ", _key); \ #define DEFINE_LEGACY_INDICATOR(FN_NAME, BUILTIN_NAME, TYPED_PARAMS_COMMA, TYPED_PARAMS_NO_UDL_SEMICOLON, UNTYPED_PARAMS_COMMA_KEY, UNTYPED_PARAMS_COMMA_VALUES, ASSIGNMENTS_COMMA, UNTYPED_PARAMS_NO_UDL_COMMA_VALUES) \ class BUILTIN_NAME##Legacy : public IndicatorLegacy { \ TYPED_PARAMS_NO_UDL_SEMICOLON; \ @@ -218,92 +270,95 @@ int FN_NAME(TYPED_PARAMS_COMMA) { \ return PTR_ATTRIB(_indi.Ptr(), GetHandle()); \ } -/** - * 1-parameter helper for DEFINE_LEGACY_INDICATOR. - */ -#define DEFINE_LEGACY_INDICATOR_1(FN_NAME, INDI_NAME, T1, N1) \ - DEFINE_LEGACY_INDICATOR(INDI_NAME, T1 _##N1, T1 N1, _##N1, _##N1, N1(_##N1), N1); - -/** - * 2-parameter helper for DEFINE_LEGACY_INDICATOR. - */ -#define DEFINE_LEGACY_INDICATOR_2(FN_NAME, INDI_NAME, T1, N1, T2, N2) \ - DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, T1 _##N1 COMMA T2 _##N2, T1 N1 SEMICOLON T2 N2, _##N1 COMMA _##N2, \ - _##N1 COMMA _##N2, N1(_##N1) COMMA N2(_##N2), N1 COMMA N2); - -/** - * 3-parameter helper for DEFINE_LEGACY_INDICATOR. - */ -#define DEFINE_LEGACY_INDICATOR_3(FN_NAME, INDI_NAME, T1, N1, T2, N2, T3, N3) \ - DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3, \ - T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3, _##N1 COMMA _##N2 COMMA _##N3, \ - _##N1 COMMA _##N2 COMMA _##N3, N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3), \ - N1 COMMA N2 COMMA N3); - -/** - * 4-parameter helper for DEFINE_LEGACY_INDICATOR. - */ -#define DEFINE_LEGACY_INDICATOR_4(FN_NAME, INDI_NAME) \ - DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4, \ - T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4, _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4, \ - N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4), N1 COMMA N2 COMMA N3 COMMA N4); - -/** - * 5-parameter helper for DEFINE_LEGACY_INDICATOR. - */ -#define DEFINE_LEGACY_INDICATOR_5(FN_NAME, INDI_NAME) \ - DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4 COMMA T5 _##N5, \ - T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4 SEMICOLON T5 N5, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5, \ - N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4) COMMA N5(_##N5), \ - N1 COMMA N2 COMMA N3 COMMA N4 COMMA N5); - -/** - * 6-parameter helper for DEFINE_LEGACY_INDICATOR. - */ -#define DEFINE_LEGACY_INDICATOR_6(FN_NAME, INDI_NAME) \ - DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, \ - T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4 COMMA T5 _##N5 COMMA T6 _##N6, \ - T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4 SEMICOLON T5 N5 SEMICOLON T6 N6, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6, \ - N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4) COMMA N5(_##N5) COMMA N6(_##N6), \ - N1 COMMA N2 COMMA N3 COMMA N4 COMMA N5 COMMA N6); - -/** - * 7-parameter helper for DEFINE_LEGACY_INDICATOR. - */ -#define DEFINE_LEGACY_INDICATOR_7(FN_NAME, INDI_NAME) \ - DEFINE_LEGACY_INDICATOR( \ - FN_NAME, INDI_NAME, \ - T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4 COMMA T5 _##N5 COMMA T6 _##N6 COMMA T7 _##N7, \ - T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4 SEMICOLON T5 N5 SEMICOLON T6 N6 SEMICOLON T7 N7, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6 COMMA _##N7, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6 COMMA _##N7, \ - N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4) COMMA N5(_##N5) COMMA N6(_##N6) COMMA N7(_##N7), \ - N1 COMMA N2 COMMA N3 COMMA N4 COMMA N5 COMMA N6 COMMA N7); - -/** - * 8-parameter helper for DEFINE_LEGACY_INDICATOR. - */ -#define DEFINE_LEGACY_INDICATOR_8(FN_NAME, INDI_NAME) \ - DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, \ - T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4 COMMA T5 _##N5 COMMA T6 _##N6 COMMA T7 \ - _##N7 COMMA T8 _##N8, \ - T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4 SEMICOLON T5 N5 SEMICOLON T6 N6 \ - SEMICOLON T7 N7 SEMICOLON T8 N8, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6 COMMA _##N7 COMMA _##N8, \ - _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6 COMMA _##N7 COMMA _##N8, \ - N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4) COMMA N5(_##N5) COMMA N6(_##N6) \ - COMMA N7(_##N7) COMMA N8(_##N8), \ - N1 COMMA N2 COMMA N3 COMMA N4 COMMA N5 COMMA N6 COMMA N7 COMMA N8); - -/** - * Replacement for future StringConcatenate(). - */ -#define StringConcatenate StringConcatenateMT5 + /** + * 1-parameter helper for DEFINE_LEGACY_INDICATOR. + */ + #define DEFINE_LEGACY_INDICATOR_1(FN_NAME, INDI_NAME, T1, N1) \ + DEFINE_LEGACY_INDICATOR(INDI_NAME, T1 _##N1, T1 N1, _##N1, _##N1, N1(_##N1), N1); + + /** + * 2-parameter helper for DEFINE_LEGACY_INDICATOR. + */ + #define DEFINE_LEGACY_INDICATOR_2(FN_NAME, INDI_NAME, T1, N1, T2, N2) \ + DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, T1 _##N1 COMMA T2 _##N2, T1 N1 SEMICOLON T2 N2, _##N1 COMMA _##N2, \ + _##N1 COMMA _##N2, N1(_##N1) COMMA N2(_##N2), N1 COMMA N2); + + /** + * 3-parameter helper for DEFINE_LEGACY_INDICATOR. + */ + #define DEFINE_LEGACY_INDICATOR_3(FN_NAME, INDI_NAME, T1, N1, T2, N2, T3, N3) \ + DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3, \ + T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3, _##N1 COMMA _##N2 COMMA _##N3, \ + _##N1 COMMA _##N2 COMMA _##N3, N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3), \ + N1 COMMA N2 COMMA N3); + + /** + * 4-parameter helper for DEFINE_LEGACY_INDICATOR. + */ + #define DEFINE_LEGACY_INDICATOR_4(FN_NAME, INDI_NAME) \ + DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4, \ + T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4, _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4, \ + N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4), \ + N1 COMMA N2 COMMA N3 COMMA N4); + + /** + * 5-parameter helper for DEFINE_LEGACY_INDICATOR. + */ + #define DEFINE_LEGACY_INDICATOR_5(FN_NAME, INDI_NAME) \ + DEFINE_LEGACY_INDICATOR(FN_NAME, INDI_NAME, \ + T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4 COMMA T5 _##N5, \ + T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4 SEMICOLON T5 N5, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5, \ + N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4) COMMA N5(_##N5), \ + N1 COMMA N2 COMMA N3 COMMA N4 COMMA N5); + + /** + * 6-parameter helper for DEFINE_LEGACY_INDICATOR. + */ + #define DEFINE_LEGACY_INDICATOR_6(FN_NAME, INDI_NAME) \ + DEFINE_LEGACY_INDICATOR( \ + FN_NAME, INDI_NAME, T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4 COMMA T5 _##N5 COMMA T6 _##N6, \ + T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4 SEMICOLON T5 N5 SEMICOLON T6 N6, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6, \ + N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4) COMMA N5(_##N5) COMMA N6(_##N6), \ + N1 COMMA N2 COMMA N3 COMMA N4 COMMA N5 COMMA N6); + + /** + * 7-parameter helper for DEFINE_LEGACY_INDICATOR. + */ + #define DEFINE_LEGACY_INDICATOR_7(FN_NAME, INDI_NAME) \ + DEFINE_LEGACY_INDICATOR( \ + FN_NAME, INDI_NAME, \ + T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4 COMMA T5 _##N5 COMMA T6 _##N6 COMMA T7 _##N7, \ + T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4 SEMICOLON T5 N5 SEMICOLON T6 N6 SEMICOLON T7 N7, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6 COMMA _##N7, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6 COMMA _##N7, \ + N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4) COMMA N5(_##N5) COMMA N6(_##N6) COMMA N7(_##N7), \ + N1 COMMA N2 COMMA N3 COMMA N4 COMMA N5 COMMA N6 COMMA N7); + + /** + * 8-parameter helper for DEFINE_LEGACY_INDICATOR. + */ + #define DEFINE_LEGACY_INDICATOR_8(FN_NAME, INDI_NAME) \ + DEFINE_LEGACY_INDICATOR( \ + FN_NAME, INDI_NAME, \ + T1 _##N1 COMMA T2 _##N2 COMMA T3 _##N3 COMMA T4 _##N4 COMMA T5 _##N5 COMMA T6 _##N6 COMMA T7 _##N7 COMMA T8 \ + _##N8, \ + T1 N1 SEMICOLON T2 N2 SEMICOLON T3 N3 SEMICOLON T4 N4 SEMICOLON T5 N5 SEMICOLON T6 N6 SEMICOLON T7 N7 \ + SEMICOLON T8 N8, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6 COMMA _##N7 COMMA _##N8, \ + _##N1 COMMA _##N2 COMMA _##N3 COMMA _##N4 COMMA _##N5 COMMA _##N6 COMMA _##N7 COMMA _##N8, \ + N1(_##N1) COMMA N2(_##N2) COMMA N3(_##N3) COMMA N4(_##N4) COMMA N5(_##N5) COMMA N6(_##N6) COMMA N7(_##N7) \ + COMMA N8(_##N8), \ + N1 COMMA N2 COMMA N3 COMMA N4 COMMA N5 COMMA N6 COMMA N7 COMMA N8); + + /** + * Replacement for future StringConcatenate(). + */ + #define StringConcatenate StringConcatenateMT5 /** * MQL4 wrapper of MQL5's StringConcatenate(). @@ -395,66 +450,66 @@ DEFINE_LEGACY_INDICATOR_2(iAD, iAD, string, symbol, int, period); // int iATR(string symbol, ENUM_TIMEFRAMES period, int ma_period); DEFINE_LEGACY_INDICATOR_3(iATR, iATR, string, symbol, int, period, int, ma_period); -// int iRSI(string symbol, ENUM_TIMEFRAMES period, int ma_period, int applied_price); -#define T1 string -#define N1 symbol -#define T2 int -#define N2 period -#define T3 int -#define N3 ma_period -#define T4 int -#define N4 applied_price + // int iRSI(string symbol, ENUM_TIMEFRAMES period, int ma_period, int applied_price); + #define T1 string + #define N1 symbol + #define T2 int + #define N2 period + #define T3 int + #define N3 ma_period + #define T4 int + #define N4 applied_price DEFINE_LEGACY_INDICATOR_4(iRSI, iRSI) -#undef T1 -#undef N1 -#undef T2 -#undef N2 -#undef T3 -#undef N3 -#undef T4 -#undef N4 -#undef T5 -#undef N5 -#undef T6 -#undef N6 - -// int iMA(string symbol, ENUM_TIMEFRAMES period, int ma_period, int ma_shift, ENUM_MA_METHOD ma_method, -#define T1 string -#define N1 symbol -#define T2 int -#define N2 period -#define T3 int -#define N3 ma_period -#define T4 int -#define N4 ma_shift -#define T5 int -#define N5 ma_method -#define T6 int -#define N6 applied_price + #undef T1 + #undef N1 + #undef T2 + #undef N2 + #undef T3 + #undef N3 + #undef T4 + #undef N4 + #undef T5 + #undef N5 + #undef T6 + #undef N6 + + // int iMA(string symbol, ENUM_TIMEFRAMES period, int ma_period, int ma_shift, ENUM_MA_METHOD ma_method, + #define T1 string + #define N1 symbol + #define T2 int + #define N2 period + #define T3 int + #define N3 ma_period + #define T4 int + #define N4 ma_shift + #define T5 int + #define N5 ma_method + #define T6 int + #define N6 applied_price DEFINE_LEGACY_INDICATOR_6(iMA, iMA) -#undef T1 -#undef N1 -#undef T2 -#undef N2 -#undef T3 -#undef N3 -#undef T4 -#undef N4 -#undef T5 -#undef N5 -#undef T6 -#undef N6 - -#endif // INDICATOR_LEGACY_VERSION_MT5 -#endif // __MQL4__ + #undef T1 + #undef N1 + #undef T2 + #undef N2 + #undef T3 + #undef N3 + #undef T4 + #undef N4 + #undef T5 + #undef N5 + #undef T6 + #undef N6 + + #endif // INDICATOR_LEGACY_VERSION_MT5 +#endif // __MQL4__ #ifdef __MQL5__ -#ifdef INDICATOR_LEGACY_VERSION_MT4 + #ifdef INDICATOR_LEGACY_VERSION_MT4 -/** - * Replacement for future StringConcatenate(). - */ -#define StringConcatenate StringConcatenateMT4 + /** + * Replacement for future StringConcatenate(). + */ + #define StringConcatenate StringConcatenateMT4 /** * MQL5 wrapper of MQL4's StringConcatenate(). @@ -522,5 +577,5 @@ string StringConcatenateMT4(string& _result, A _a) { return (string)_a; } -#endif // INDICATOR_LEGACY_VERSION_MT4 -#endif // __MQL5__ + #endif // INDICATOR_LEGACY_VERSION_MT4 +#endif // __MQL5__ diff --git a/Indicators/Account/Indi_AccountStats.mqh b/Indicators/Account/Indi_AccountStats.mqh index ece3896dc..10f2946c8 100644 --- a/Indicators/Account/Indi_AccountStats.mqh +++ b/Indicators/Account/Indi_AccountStats.mqh @@ -191,14 +191,13 @@ class Indi_AccountStats : public Indicator { /** * Called when data source emits new entry (historic or future one). */ - virtual void OnDataSourceEntry(IndicatorDataEntry &entry/*, ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT*/) { - /* + virtual void OnDataSourceEntry(IndicatorDataEntry &entry, + ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) override { Indicator::OnDataSourceEntry(entry, type); if (type != INDI_EMITTED_ENTRY_TYPE_CANDLE) { return; } - */ // New candle means that account stats for current index 0 will be that we // will now extract and store in the buffers. diff --git a/Indicators/Indi_Bands.mqh b/Indicators/Indi_Bands.mqh index 9300cb9c5..fcd390dbd 100644 --- a/Indicators/Indi_Bands.mqh +++ b/Indicators/Indi_Bands.mqh @@ -176,33 +176,33 @@ class Indi_Bands : public Indicator { /** * OnCalculate() method for Bands indicator. */ - static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_SHORT, ValueStorage &ExtMLBuffer, - ValueStorage &ExtTLBuffer, ValueStorage &ExtBLBuffer, - ValueStorage &ExtStdDevBuffer, int InpBandsPeriod, int InpBandsShift, - double InpBandsDeviations) { + static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_SHORT, ValueStorage &_ExtMLBuffer, + ValueStorage &_ExtTLBuffer, ValueStorage &_ExtBLBuffer, + ValueStorage &_ExtStdDevBuffer, int _InpBandsPeriod, int _InpBandsShift, + double _InpBandsDeviations) { int ExtBandsPeriod, ExtBandsShift; double ExtBandsDeviations; int ExtPlotBegin = 0; - if (InpBandsPeriod < 2) { + if (_InpBandsPeriod < 2) { ExtBandsPeriod = 20; PrintFormat("Incorrect value for input variable InpBandsPeriod=%d. Indicator will use value=%d for calculations.", - InpBandsPeriod, ExtBandsPeriod); + _InpBandsPeriod, ExtBandsPeriod); } else - ExtBandsPeriod = InpBandsPeriod; - if (InpBandsShift < 0) { + ExtBandsPeriod = _InpBandsPeriod; + if (_InpBandsShift < 0) { ExtBandsShift = 0; PrintFormat("Incorrect value for input variable InpBandsShift=%d. Indicator will use value=%d for calculations.", - InpBandsShift, ExtBandsShift); + _InpBandsShift, ExtBandsShift); } else - ExtBandsShift = InpBandsShift; - if (InpBandsDeviations == 0.0) { + ExtBandsShift = _InpBandsShift; + if (_InpBandsDeviations == 0.0) { ExtBandsDeviations = 2.0; PrintFormat( "Incorrect value for input variable InpBandsDeviations=%f. Indicator will use value=%f for calculations.", - InpBandsDeviations, ExtBandsDeviations); + _InpBandsDeviations, ExtBandsDeviations); } else - ExtBandsDeviations = InpBandsDeviations; + ExtBandsDeviations = _InpBandsDeviations; if (rates_total < ExtPlotBegin) return (0); //--- indexes draw begin settings, when we've recieved previous begin @@ -221,13 +221,13 @@ class Indi_Bands : public Indicator { //--- main cycle for (int i = pos; i < rates_total && !IsStopped(); i++) { //--- middle line - ExtMLBuffer[i] = Indi_MA::SimpleMA(i, ExtBandsPeriod, price); + _ExtMLBuffer[i] = Indi_MA::SimpleMA(i, ExtBandsPeriod, price); //--- calculate and write down StdDev - ExtStdDevBuffer[i] = StdDev_Func(i, price, ExtMLBuffer, ExtBandsPeriod); + _ExtStdDevBuffer[i] = StdDev_Func(i, price, _ExtMLBuffer, ExtBandsPeriod); //--- upper line - ExtTLBuffer[i] = ExtMLBuffer[i] + ExtBandsDeviations * ExtStdDevBuffer[i].Get(); + _ExtTLBuffer[i] = _ExtMLBuffer[i] + ExtBandsDeviations * _ExtStdDevBuffer[i].Get(); //--- lower line - ExtBLBuffer[i] = ExtMLBuffer[i] - ExtBandsDeviations * ExtStdDevBuffer[i].Get(); + _ExtBLBuffer[i] = _ExtMLBuffer[i] - ExtBandsDeviations * _ExtStdDevBuffer[i].Get(); } //--- OnCalculate done. Return new prev_calculated. return (rates_total); diff --git a/Indicators/Indi_MA.mqh b/Indicators/Indi_MA.mqh index e3cdbd9a8..f3f1801ab 100644 --- a/Indicators/Indi_MA.mqh +++ b/Indicators/Indi_MA.mqh @@ -24,7 +24,6 @@ #ifndef INDI_MA_MQH #define INDI_MA_MQH -// Includes. #include "../Dict.mqh" #include "../DictObject.mqh" #include "../Indicator/Indicator.h" @@ -33,20 +32,6 @@ #include "../Storage/ValueStorage.h" #include "../String.mqh" -#ifndef __MQL4__ -// Defines global functions (for MQL4 backward compability). -double iMA(string _symbol, int _tf, int _ma_period, int _ma_shift, int _ma_method, int _ap, int _shift) { - ResetLastError(); - return Indi_MA::iMA(_symbol, (ENUM_TIMEFRAMES)_tf, _ma_period, _ma_shift, (ENUM_MA_METHOD)_ma_method, - (ENUM_APPLIED_PRICE)_ap, _shift); -} -double iMAOnArray(double &_arr[], int _total, int _period, int _ma_shift, int _ma_method, int _abs_shift, - IndicatorCalculateCache *_cache = NULL) { - ResetLastError(); - return Indi_MA::iMAOnArray(_arr, _total, _period, _ma_shift, _ma_method, _abs_shift, _cache); -} -#endif - // Structs. struct IndiMAParams : IndicatorParams { unsigned int period; @@ -543,6 +528,55 @@ class Indi_MA : public Indicator { return (rates_total); } + static int LinearWeightedMAOnBuffer(const int rates_total, const int prev_calculated, const int begin, + const int period, const ARRAY_REF(double, price), ARRAY_REF(double, buffer), + int &weight_sum) { + int i, k; + + // Check period. + if (period <= 1 || period > (rates_total - begin)) return (0); + // Save as_series flags. + bool as_series_price = ArrayGetAsSeries(price); + bool as_series_buffer = ArrayGetAsSeries(buffer); + + ArraySetAsSeries(price, false); + ArraySetAsSeries(buffer, false); + // Calculate start position. + int start_position; + + if (prev_calculated == 0) { + // First calculation or number of bars was changed. + // Set empty value for first bars. + start_position = period + begin; + + for (i = 0; i < start_position; i++) buffer[i] = 0.0; + // Calculate first visible value. + double first_value = 0; + int wsum = 0; + + for (i = begin, k = 1; i < start_position; i++, k++) { + first_value += k * price[i]; + wsum += k; + } + + buffer[start_position - 1] = first_value / wsum; + weight_sum = wsum; + } else + start_position = prev_calculated - 1; + // Main loop. + for (i = start_position; i < rates_total; i++) { + double sum = 0; + + for (int j = 0; j < period; j++) sum += (period - j) * price[i - j]; + + buffer[i] = sum / weight_sum; + } + // Restore as_series flags. + ArraySetAsSeries(price, as_series_price); + ArraySetAsSeries(buffer, as_series_buffer); + return (rates_total); + } + static int SmoothedMAOnBuffer(const int rates_total, const int prev_calculated, const int begin, const int period, ValueStorage &price, ValueStorage &buffer) { int i; @@ -776,4 +810,26 @@ class Indi_MA : public Indicator { iparams.applied_array = _applied_price; } }; + +#ifdef __MQL4__ +// MQL4 version of the method doesn't have last parameter. +int LinearWeightedMAOnBuffer(const int rates_total, const int prev_calculated, const int begin, const int period, + const double &price[], double &buffer[]) { + int _weight_sum; + return Indi_MA::LinearWeightedMAOnBuffer(rates_total, prev_calculated, begin, period, price, buffer, _weight_sum); +} +#else // !__MQL__4 +// Defines global functions (for MQL4 backward compability). +double iMA(string _symbol, int _tf, int _ma_period, int _ma_shift, int _ma_method, int _ap, int _shift) { + ResetLastError(); + return Indi_MA::iMA(_symbol, (ENUM_TIMEFRAMES)_tf, _ma_period, _ma_shift, (ENUM_MA_METHOD)_ma_method, + (ENUM_APPLIED_PRICE)_ap, _shift); +} +double iMAOnArray(double &_arr[], int _total, int _period, int _ma_shift, int _ma_method, int _abs_shift, + IndicatorCalculateCache *_cache = NULL) { + ResetLastError(); + return Indi_MA::iMAOnArray(_arr, _total, _period, _ma_shift, _ma_method, _abs_shift, _cache); +} +#endif // __MQL4__ + #endif // INDI_MA_MQH diff --git a/Indicators/Tf/Indi_TfMt.h b/Indicators/Tf/Indi_TfMt.h new file mode 100644 index 000000000..ae2f16811 --- /dev/null +++ b/Indicators/Tf/Indi_TfMt.h @@ -0,0 +1,100 @@ +//+------------------------------------------------------------------+ +//| 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 + * Tf-based candle indicator for MT. + */ + +#ifndef __MQL__ + // Allows the preprocessor to include a header file when it is needed. + #pragma once +#endif + +// Includes. +#include "../../Indicator/IndicatorCandle.h" +#include "Indi_TfMt.provider.h" + +// Params for MT Tf-based candle indicator. +struct Indi_TfMtParams : IndicatorTfParams { + Indi_TfMtParams(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : IndicatorTfParams("IndicatorTf", _tf) {} +}; + +/** + * Tf-based candle indicator for MT. + */ +class Indi_TfMt : public IndicatorCandle> { + protected: + // Time-frame used to create candles. + ENUM_TIMEFRAMES tf; + + /* Protected methods */ + + /** + * Initialize class. + * + * Called on constructor. + */ + void Init() { history.SetItemProvider(new ItemsHistoryTfMtCandleProvider(THIS_PTR)); } + + public: + /* Special methods */ + + /** + * Class constructor with timeframe enum. + */ + Indi_TfMt(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) { + tf = _tf; + Init(); + } + + /** + * Class constructor with timeframe index. + */ + Indi_TfMt(ENUM_TIMEFRAMES_INDEX _tfi = 0) { + tf = ChartTf::IndexToTf(_tfi); + Init(); + } + + /** + * Class constructor with parameters. + */ + Indi_TfMt(Indi_TfMtParams& _icparams, const IndicatorDataParams& _idparams) { Init(); } + + /** + * Gets indicator's time-frame. + */ + ENUM_TIMEFRAMES GetTf() override { return tf; } + + /** + * Returns current tick index (incremented every OnTick()). + */ + int GetTickIndex() override { return history.GetItemProvider() PTR_DEREF GetTickIndex(); } + + /** + * Returns the number of bars on the chart decremented by iparams.shift. + */ + int GetBars() override { + // Will return number of bars prepended and appended to the history, + // even if those bars were cleaned up because of history's candle limit. + return ::Bars(GetSymbol(), GetTf()) - iparams.shift; + } +}; diff --git a/Indicators/Tf/Indi_TfMt.provider.h b/Indicators/Tf/Indi_TfMt.provider.h new file mode 100644 index 000000000..55e9cf0e3 --- /dev/null +++ b/Indicators/Tf/Indi_TfMt.provider.h @@ -0,0 +1,177 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 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 . + * + */ + +#ifndef __MQL__ + // Allows the preprocessor to include a header file when it is needed. + #pragma once +#endif + +/** + * Candle grouping and regeneration for time-frame based candles. + */ +template +class ItemsHistoryTfMtCandleProvider : public ItemsHistoryCandleProvider { + // Pointer to Tf Indicator, e.g., Indi_TfMt or IndicatorTfDummy. Used to fetch data by fixed timeframe candles. + IndicatorData* indi; + + // Current tick index. Effectively a number of ticks generated by attached Tick indicator. + int tick_index; + + public: + /** + * Constructor. + */ + ItemsHistoryTfMtCandleProvider(IndicatorData* _indi_tf) : indi(_indi_tf), tick_index(0) {} + + /** + * Called when new tick was emitted from IndicatorTick-based source. + */ + virtual void OnTick(ItemsHistory, ItemsHistoryTfMtCandleProvider>* _history, long _time_ms, + float _ask, float _bid) { + ++tick_index; + + // Seconds per candle calculated from TF. + int _spc = (int)ChartTf::TfToSeconds(indi PTR_DEREF GetTf()); + + Print("Indi_TfMt's history: New tick: ", TimeToString(_time_ms / 1000, TIME_DATE | TIME_MINUTES | TIME_SECONDS), + ", ", _ask, ", ", _bid); + + // We know that tick's timestamp will be ahead of the last tick and thus + // inside or ahead of the last created candle. In order to retrieve last + // valid candle, we need to use ItemsHistory::GetItemByShift(0) to check if + // we have to update last or create/append new candle. + CandleOCTOHLC _candle; + + // Will regenerate candles up to the last added candle ever. We have to + // call it, because some of the previous actions may have removed some of + // the recent candles. Note that OnTick() advances its _time_ms in + // ascending order, so all we need to most recent history. + // + // Note that EnsureShiftExists() may return false when there never been any + // candle added. + _history PTR_DEREF EnsureShiftExists(0); + + if (_history PTR_DEREF TryGetItemByShift(0, _candle, false) && _candle.ContainsTimeMs(_time_ms)) { + // Time given fits in the last added candle's time-frame, updating the candle with given price. + _candle.Update(_time_ms, _bid); + + // Storing candle in the history. + _history PTR_DEREF Update(_candle, _history PTR_DEREF GetShiftIndex(0)); + } else { + CandleOCTOHLC _candle_tmp; + + // We don't want to regenerate history, because at the start there will bo no candle however. + if (_history PTR_DEREF TryGetItemByShift(0, _candle_tmp, false)) { + // Print("Completed candle: ", _candle_tmp.ToString()); + // Print("Real candle: ", iOpen(NULL, Period(), 1), " ", iHigh(NULL, Period(), 1), " ", + // iLow(NULL, Period(), 1), " ", ChartStatic::iClose(NULL, (ENUM_TIMEFRAMES)Period(), 1)); + // Print("--"); + } + + // Either there is no candle at shift 0 or given time doesn't fit in the #0 candle's time-frame. + _candle.Init(GetCandleTimeFromTimeMs(_time_ms, _spc), _spc, _time_ms, _bid); + + // Adding candle as the most recent item in the history. It will now become the candle at shift 0. + _history PTR_DEREF Append(_candle); + } + } + + /** + * Returns current tick index. Effectively a number of ticks generated by + * attached IndicatorTick. + */ + int GetTickIndex() { return tick_index; } + + /** + * Returns start time of the candle (the place it's on the chart) for the given tick's time in milliseconds. + */ + int GetCandleTimeFromTimeMs(long _time_ms, int _length_in_secs) { + return (int)((_time_ms - _time_ms % ((long)_length_in_secs * 1000)) / 1000); + } + + /** + * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we + * want previous or next items from selected starting point. Should return false if retrieving items by this method + * is not available. + */ + bool GetItems(ItemsHistory, ItemsHistoryTfMtCandleProvider>* _history, long _from_time_ms, + ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(CandleOCTOHLC, _out_arr)) { + return false; + } + + /** + * Retrieves items between given indices (both indices inclusive). Should return false if retrieving items by this + * method is not available. + */ + bool GetItems(ItemsHistory, ItemsHistoryTfMtCandleProvider>* _history, int _start_index, + int _end_index, ARRAY_REF(CandleOCTOHLC, _out_arr)) { + Print("Indi_TfMt::GetItems()"); + + // Converting absolute indices into MT shifts. + int _num_bars = _end_index - _start_index + 1; + // Current candle index. Could be 0 if no candles have been added or if there is only one candle added so far. + int _current_index = _history PTR_DEREF GetCurrentIndex(); + int _start_shift = _current_index - _start_index; + int _end_shift = _current_index - _end_index; + + Print("Indi_TfMt::GetItems(): Will fetch ", _num_bars, " bars between shift ", _start_shift, " and ", _end_shift); + + // Seconds per candle calculated from TF. + int _spc = (int)ChartTf::TfToSeconds(indi PTR_DEREF GetTf()); + + // Static, reusable array of rates. + static ARRAY(MqlRates, _rates); + int _count = _end_index - _start_index + 1; + ArrayResize(_rates, _count); + + Print("CopyRates(", indi PTR_DEREF GetSymbol(), ", ", EnumToString(indi PTR_DEREF GetTf()), ", ", _start_index, + ", ", _count, ", ...)"); + + // As GetItems() will only be called for missing candles, we can just ask MT for OHLCs and return them. + // Note that we have to specify most recent shift and count of history ticks to return. + // Also note that CopyRates() will insert oldest candle at the start of _rates array, so we have to inverse items in + // the array in order all candles be from most recent to the oldest ones. + int _num_copied = CopyRates(indi PTR_DEREF GetSymbol(), indi PTR_DEREF GetTf(), _end_shift, _count, _rates); + ArrayResize(_out_arr, _num_copied); + + // Acknowledging indicator that we will emit _num_copied number of candles. + _history PTR_DEREF GetIndicator() + PTR_DEREF OnDataSourceWillEmitEntries(INDI_EMITTED_ENTRY_TYPE_CANDLE, _num_copied); + + for (int i = 0; i < _num_copied; ++i) { + MqlRates _rate = _rates[i]; + int _start_secs = (int)(long)_rate.time; + CandleOCTOHLC _candle(_rate.open, _rate.high, _rate.low, _rate.close, _start_secs, _spc, + (long)_start_secs * 1000, long(_start_secs + _spc) * 1000 - 1, (int)_rate.tick_volume); + // Reversing output, so most recent candle will be at start. + _out_arr[_num_copied - i - 1] = _candle; + } + + ArrayResize(_rates, 0); + return true; + } + + /** + * Returns information about item provider. + */ + string const ToString() override { return "Indi_TfMt candle provider on " + indi PTR_DEREF GetFullName(); } +}; diff --git a/Indicators/Tick/Indi_TickMt.mqh b/Indicators/Tick/Indi_TickMt.mqh index 72300e85d..0bd2f29a9 100644 --- a/Indicators/Tick/Indi_TickMt.mqh +++ b/Indicators/Tick/Indi_TickMt.mqh @@ -26,8 +26,8 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif // Includes. @@ -127,60 +127,11 @@ class Indi_TickMt : public IndicatorTick, _out_ticks)) { + bool FetchHistoryByTimeRange(long _from_ms, long _to_ms, ARRAY_REF(TickTAB, _out_ticks)) { ArrayResize(_out_ticks, 0); -#ifdef __MQL4__ - // Searching from current bar to older ones. - int _shift; - - if (_to_ms <= _cache_fetch_history_shift_to_ms) { - _shift = _cache_fetch_history_shift_shift; - } else { - _shift = 0; - } - - string _symbol = GetSymbol(); - - while (true) { - double _time = (double)iTime(_symbol, PERIOD_M1, _shift); - - if (_time == 0) { - // Invalid time. - break; - } - - long _time_ms = (long)_time * 1000; - - if (_time_ms > _to_ms) { - // No yet get into valid time range. - ++_shift; - continue; - } - - if (_time_ms < _from_ms) { - // No more ticks. - break; - } - - TickTAB _tick_o(_time_ms, iOpen(_Symbol, PERIOD_M1, _shift)); - TickTAB _tick_h(_time_ms, iHigh(_Symbol, PERIOD_M1, _shift)); - TickTAB _tick_l(_time_ms, iLow(_Symbol, PERIOD_M1, _shift)); - TickTAB _tick_c(_time_ms, iClose(_Symbol, PERIOD_M1, _shift)); - ArrayPushObject(_out_ticks, _tick_o); - ArrayPushObject(_out_ticks, _tick_h); - ArrayPushObject(_out_ticks, _tick_l); - ArrayPushObject(_out_ticks, _tick_c); - ++_shift; - } - - if (_shift != -1) { - _cache_fetch_history_shift_to_ms = _to_ms; - _cache_fetch_history_shift_shift = _shift; - } - - return ArraySize(_out_ticks) != 0; -#else +#ifdef __MQL5__ + // In MQL5 we firstly try to fetch ticks by CopyTicksRange. static MqlTick _tmp_ticks[]; ArrayResize(_tmp_ticks, 0); @@ -196,20 +147,61 @@ class Indi_TickMt : public IndicatorTick _tick(_tmp_ticks[i]); -#ifdef __debug_verbose__ + #ifdef __debug_verbose__ Print("Fetched tick at ", TimeToString(_tmp_ticks[i].time, TIME_DATE | TIME_MINUTES | TIME_SECONDS), ": ", _tmp_ticks[i].ask, ", ", _tmp_ticks[i].bid); -#endif + #endif ArrayPushObject(_out_ticks, _tick); } - return true; + if (_num_copied > 0) { + return true; + } else { + // No ticks found. We will try to create ticks from historic OHLC values. + break; + } } } + #endif - // To many tries. Probably no ticks at the given range. - return false; + // In MQL4 or MQL5 (if there's no history returned by CopyTicksRange) we + // try to create and return OHLC ticks by calling iOpen/iHigh/iLow/iClose + // for each bar in history. Candle indicator will form candles from those + // 4 ticks per bar. + + string _symbol = GetSymbol(); + ARRAY(MqlRates, _rates); + + if (!CopyRates(_symbol, PERIOD_M1, (datetime)(int)(_from_ms / 1000), (datetime)(int)(_to_ms / 1000), _rates)) { + // Failure. + return false; + } + + if (ArraySize(_rates) == 0) { + // 0 ticks is OK. + return true; + } + + ArrayResize(_out_ticks, ArraySize(_rates) * 4); // Number of ticks * OHLC(4). + + int _tick_idx = 0; + + for (int i = 0; i < ArraySize(_rates); ++i) { + long _time_ms = (long)_rates[i].time * 1000; + // Note that we modify the time in order to simulate real open/close time. + TickTAB _tick_o(_time_ms + 0, _rates[i].open, _rates[i].open); + TickTAB _tick_h(_time_ms + 1, _rates[i].high, _rates[i].high); + TickTAB _tick_l(_time_ms + 2, _rates[i].low, _rates[i].low); + TickTAB _tick_c(_time_ms + 3, _rates[i].close, _rates[i].close); + + _out_ticks[_tick_idx++] = _tick_o; + _out_ticks[_tick_idx++] = _tick_h; + _out_ticks[_tick_idx++] = _tick_l; + _out_ticks[_tick_idx++] = _tick_c; + } + + return ArraySize(_out_ticks) != 0; } /** @@ -219,11 +211,6 @@ class Indi_TickMt : public IndicatorTick _tick(0, 0); IndicatorDataEntry _entry(TickToEntry(TimeCurrent(), _tick)); - EmitEntry(_entry); + EmitEntry(_entry, INDI_EMITTED_ENTRY_TYPE_TICK); // Appending tick into the history. AppendEntry(_entry); - return false; + return; } -#ifdef __debug_verbose__ + #ifdef __debug_verbose__ Print("CpyT: ", TimeToString(_tmp_ticks[0].time, TIME_DATE | TIME_MINUTES | TIME_SECONDS), " = ", _tmp_ticks[0].bid, " (", _tmp_ticks[0].time, ")"); Print("RlCl: ", TimeToString(::iTime(GetSymbol(), PERIOD_CURRENT, 0), TIME_DATE | TIME_MINUTES | TIME_SECONDS), " = ", ::iClose(GetSymbol(), PERIOD_CURRENT, 0)); -#endif + #endif double _ask = _tmp_ticks[0].ask; double _bid = _tmp_ticks[0].bid; + + #endif + + MqlTick _tick_data; + SymbolInfoTick(GetSymbol(), _tick_data); + + double _ask = _tick_data.ask; + double _bid = _tick_data.bid; + // long _time = _tmp_ticks[0].time; long _time = TimeCurrent(); #endif TickAB _tick(_ask, _bid); IndicatorDataEntry _entry(TickToEntry(_time, _tick)); - EmitEntry(_entry); + EmitEntry(_entry, INDI_EMITTED_ENTRY_TYPE_TICK); // Appending tick into the history. AppendEntry(_entry); - return true; + return _ask != WRONG_VALUE && _bid != WRONG_VALUE; } }; diff --git a/Object.mqh b/Object.mqh index ca74ee781..cc27a1ad8 100644 --- a/Object.mqh +++ b/Object.mqh @@ -24,8 +24,6 @@ #ifndef OBJECT_MQH #define OBJECT_MQH -#define nullptr NULL - // Includes. #include "Object.enum.h" #include "Object.extern.h" @@ -37,14 +35,11 @@ * Class to deal with objects. */ class Object : public Dynamic { - protected: - void *obj; long id; public: - /** * Class constructor. */ diff --git a/Platform.h b/Platform.h index d7bd669fd..8acf63a4e 100644 --- a/Platform.h +++ b/Platform.h @@ -21,13 +21,13 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once -// Includes. -#include "Deal.enum.h" -#include "Order.struct.h" -#include "Platform.define.h" + // Includes. + #include "Deal.enum.h" + #include "Order.struct.h" + #include "Platform.define.h" /** * Extern declarations for C++. @@ -46,17 +46,21 @@ extern int Bars(CONST_REF_TO(string) _symbol, ENUM_TIMEFRAMES _tf); * Current platform's static methods. */ +#include "DrawIndicator.mqh" #include "Flags.h" #include "Indicator/IndicatorData.h" #include "Indicator/tests/classes/IndicatorTfDummy.h" #include "Std.h" #ifdef __MQLBUILD__ -#include "Indicators/Tick/Indi_TickMt.mqh" -#define PLATFORM_DEFAULT_INDICATOR_TICK Indi_TickMt + #include "Indicators/Tf/Indi_TfMt.h" + #include "Indicators/Tick/Indi_TickMt.mqh" + #define PLATFORM_DEFAULT_INDICATOR_TICK Indi_TickMt + #define PLATFORM_DEFAULT_INDICATOR_TF Indi_TfMt #else -#include "Indicators/Tick/Indi_TickProvider.h" -#define PLATFORM_DEFAULT_INDICATOR_TICK Indi_TickProvider + #include "Indicators/Tick/Indi_TickProvider.h" + #define PLATFORM_DEFAULT_INDICATOR_TICK Indi_TickRandom + #define PLATFORM_DEFAULT_INDICATOR_TF IndicatorTfDummy #endif #include "SymbolInfo.struct.static.h" @@ -167,12 +171,6 @@ class Platform { ++global_tick_index; } - /** - * Checks whether we had a tick inside previous Tick() invocation. - */ - static bool HadTick() { return last_tick_result; } - - /** * Called by indicators' OnCalculate() method in order to prepare history via * IndicatorData::EmitHistory() and to call Tick() for each OnCalculate() @@ -181,31 +179,10 @@ class Platform { * via Platform::Add...(). */ static void OnCalculate(const int rates_total, const int prev_calculated) { - if (!emitted_history) { - for (DictStructIterator> _iter = indis.Begin(); _iter.IsValid(); ++_iter) { - EmitHistory(_iter.Value().Ptr()); - } - emitted_history = true; - } - // We're ready for a tick. Tick(); } - /** - * Emits history for parent indicators in hierarchy and then for the indicator itself. - */ - static void EmitHistory(IndicatorData *_indi) { - IndicatorData *_parent = _indi PTR_DEREF GetDataSource(false); - - if (_parent != nullptr) { - EmitHistory(_parent); - } - - _indi PTR_DEREF EmitHistory(); - } - - /** * Returns dictionary of added indicators (keyed by unique id). */ @@ -375,7 +352,7 @@ class Platform { string _key = Util::MakeKey("PlatformIndicatorCandle", _symbol, (int)_tf); IndicatorData *_indi_candle; if (!Objects::TryGet(_key, _indi_candle)) { - _indi_candle = Objects::Set(_key, new IndicatorTfDummy(_tf)); + _indi_candle = Objects::Set(_key, new PLATFORM_DEFAULT_INDICATOR_TF(_tf)); // Adding indicator to list of default indicators in order to tick it on every Tick() call. Ref _ref = _indi_candle; @@ -488,7 +465,6 @@ class Platform { static void SetPeriod(ENUM_TIMEFRAMES _period) { period = _period; } }; -bool Platform::emitted_history = false; bool Platform::initialized = false; bool Platform::last_tick_result = false; DateTime Platform::time = (datetime)0; diff --git a/Std.h b/Std.h index 5b690be18..a5005583d 100644 --- a/Std.h +++ b/Std.h @@ -21,9 +21,9 @@ */ #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once -#include "Math.define.h" + // Allows the preprocessor to include a header file when it is needed. + #pragma once + #include "Math.define.h" #endif // Includes. @@ -31,122 +31,124 @@ // Data types. #ifdef __cplusplus -#include -#include -#include -#include + #include + #include + #include + #include #endif #ifndef __MQL__ -#define __FUNCSIG__ __FUNCTION__ + #define __FUNCSIG__ __FUNCTION__ #endif #ifdef __MQL__ -#define ASSIGN_TO_THIS(TYPE, VALUE) ((TYPE)this) = ((TYPE)VALUE) + #define ASSIGN_TO_THIS(TYPE, VALUE) ((TYPE)this) = ((TYPE)VALUE) #else -#define ASSIGN_TO_THIS(TYPE, VALUE) ((TYPE&)*this) = ((TYPE&)VALUE) + #define ASSIGN_TO_THIS(TYPE, VALUE) ((TYPE&)*this) = ((TYPE&)VALUE) #endif // Pointers. #ifdef __MQL__ -#define GET_PTR(obj) GetPointer(obj) -#define THIS_ATTR -#define THIS_PTR (&this) -#define THIS_REF this -#define PTR_DEREF . -#define PTR_ATTRIB(O, A) O.A -#define PTR_ATTRIB2(O, A, B) O.A.B -#define PTR_TO_REF(PTR) PTR -#define MAKE_REF_FROM_PTR(TYPE, NAME, PTR) TYPE* NAME = PTR -#define REF_DEREF .Ptr(). -#define int64 long + #define GET_PTR(obj) GetPointer(obj) + #define THIS_ATTR + #define THIS_PTR (&this) + #define THIS_REF this + #define PTR_DEREF . + #define PTR_ATTRIB(O, A) O.A + #define PTR_ATTRIB2(O, A, B) O.A.B + #define PTR_TO_REF(PTR) PTR + #define MAKE_REF_FROM_PTR(TYPE, NAME, PTR) TYPE* NAME = PTR + #define REF_DEREF .Ptr(). + #define int64 long + #define nullptr NULL #else -#define GET_PTR(obj) (*obj) -#define THIS_ATTR this-> -#define THIS_PTR (this) -#define THIS_REF (*this) -#define PTR_DEREF -> -#define PTR_ATTRIB(O, A) O->A -#define PTR_ATTRIB2(O, A, B) O->A->B -#define PTR_TO_REF(PTR) (*PTR) -#define MAKE_REF_FROM_PTR(TYPE, NAME, PTR) TYPE& NAME = PTR -#define REF_DEREF .Ptr()-> -#define int64 long long + #define GET_PTR(obj) (*obj) + #define THIS_ATTR this-> + #define THIS_PTR (this) + #define THIS_REF (*this) + #define PTR_DEREF -> + #define PTR_ATTRIB(O, A) O->A + #define PTR_ATTRIB2(O, A, B) O->A->B + #define PTR_TO_REF(PTR) (*PTR) + #define MAKE_REF_FROM_PTR(TYPE, NAME, PTR) TYPE& NAME = PTR + #define REF_DEREF .Ptr()-> + #define int64 long long #endif // References. #ifdef __cplusplus -#define REF(X) (&X) + #define REF(X) (&X) #else -#define REF(X) X& + #define REF(X) X& #endif // Arrays and references to arrays. #define _COMMA , #ifdef __MQL__ -#define ARRAY_DECLARATION_BRACKETS [] + #define ARRAY_DECLARATION_BRACKETS [] #else -// C++'s _cpp_array is an object, so no brackets are needed. -#define ARRAY_DECLARATION_BRACKETS + // C++'s _cpp_array is an object, so no brackets are needed. + #define ARRAY_DECLARATION_BRACKETS #endif #ifdef __MQL__ -/** - * Reference to object. - */ -#define CONST_REF_TO(T) const T + /** + * Reference to object. + */ + #define CONST_REF_TO(T) const T -/** - * Reference to the array. - * - * @usage - * ARRAY_REF(, ) - */ -#define ARRAY_TYPE(T) T[] -#define ARRAY_REF(T, N) REF(T) N ARRAY_DECLARATION_BRACKETS -#define FIXED_ARRAY_REF(T, N, S) ARRAY_REF(T, N) + /** + * Reference to the array. + * + * @usage + * ARRAY_REF(, ) + */ + #define ARRAY_TYPE(T) T[] + #define ARRAY_REF(T, N) REF(T) N ARRAY_DECLARATION_BRACKETS + #define FIXED_ARRAY_REF(T, N, S) ARRAY_REF(T, N) -#define CONST_ARRAY_REF(T, N) const N ARRAY_DECLARATION_BRACKETS + #define CONST_ARRAY_REF(T, N) const N ARRAY_DECLARATION_BRACKETS -/** - * Array definition. - * - * @usage - * ARRAY(, ) - */ -#define ARRAY(T, N) T N[] + /** + * Array definition. + * + * @usage + * ARRAY(, ) + */ + #define ARRAY(T, N) T N[] #else -/** - * Reference to object. - */ -#define CONST_REF_TO(T) const T& + /** + * Reference to object. + */ + #define CONST_REF_TO(T) const T& /** - * Reference to the array. - * - * @usage - * ARRAY_REF(, ) - */ -#define ARRAY_TYPE(T) _cpp_array -#define ARRAY_REF(T, N) ARRAY_TYPE(T) & N -#define FIXED_ARRAY_REF(T, N, S) T(&N)[S] +* Reference to the array. +* +* @usage +* ARRAY_REF(, ) +*/ + #define ARRAY_TYPE(T) _cpp_array + #define ARRAY_REF(T, N) ARRAY_TYPE(T) & N + #define FIXED_ARRAY_REF(T, N, S) T(&N)[S] -#define CONST_ARRAY_REF(T, N) const _cpp_array& N + #define CONST_ARRAY_REF(T, N) const _cpp_array& N + #define CONST_ARRAY_REF(T, N) const _cpp_array& N -/** - * Array definition. - * - * @usage - * ARRAY(, ) - */ -#define ARRAY(T, N) ::_cpp_array N + /** + * Array definition. + * + * @usage + * ARRAY(, ) + */ + #define ARRAY(T, N) ::_cpp_array N #endif // typename(T) #ifndef __MQL__ -#define typename(T) typeid(T).name() + #define typename(T) typeid(T).name() #endif // C++ array class. @@ -238,19 +240,19 @@ class _cpp_array { void setIsSeries(bool _isSeries) { m_isSeries = _isSeries; } }; -#ifdef EMSCRIPTEN -#include + #ifdef EMSCRIPTEN + #include -#define REGISTER_ARRAY_OF(N, T, D) \ - EMSCRIPTEN_BINDINGS(N) { \ - emscripten::register_vector(D "CppVector"); \ - emscripten::class_<_cpp_array>(D) \ - .constructor() \ - .function("Push", &_cpp_array::push) \ - .function("Size", &_cpp_array::size); \ - } + #define REGISTER_ARRAY_OF(N, T, D) \ + EMSCRIPTEN_BINDINGS(N) { \ + emscripten::register_vector(D "CppVector"); \ + emscripten::class_<_cpp_array>(D) \ + .constructor() \ + .function("Push", &_cpp_array::push) \ + .function("Size", &_cpp_array::size); \ + } -#endif + #endif template class _cpp_array; @@ -273,14 +275,14 @@ class color { // MQL defines. #ifndef __MQL__ -#define WHOLE_ARRAY -1 // For processing the entire array. + #define WHOLE_ARRAY -1 // For processing the entire array. #endif // Converts string into C++-style string pointer. #ifdef __MQL__ -#define C_STR(S) S + #define C_STR(S) S #else -#define C_STR(S) cstring_from(S) + #define C_STR(S) cstring_from(S) inline const char* cstring_from(const std::string& _value) { return _value.c_str(); } #endif @@ -298,19 +300,19 @@ inline bool IsNull(const string& str) { return str == ""; } * STRUCT_ENUM(, ) */ #ifdef __MQL4__ -#define STRUCT_ENUM(S, E) E + #define STRUCT_ENUM(S, E) E #else -#define STRUCT_ENUM(S, E) S::E + #define STRUCT_ENUM(S, E) S::E #endif #ifndef __MQL__ -// Additional enum values for ENUM_SYMBOL_INFO_DOUBLE -#define SYMBOL_MARGIN_LIMIT ((ENUM_SYMBOL_INFO_DOUBLE)46) -#define SYMBOL_MARGIN_MAINTENANCE ((ENUM_SYMBOL_INFO_DOUBLE)43) -#define SYMBOL_MARGIN_LONG ((ENUM_SYMBOL_INFO_DOUBLE)44) -#define SYMBOL_MARGIN_SHORT ((ENUM_SYMBOL_INFO_DOUBLE)45) -#define SYMBOL_MARGIN_STOP ((ENUM_SYMBOL_INFO_DOUBLE)47) -#define SYMBOL_MARGIN_STOPLIMIT ((ENUM_SYMBOL_INFO_DOUBLE)48) + // Additional enum values for ENUM_SYMBOL_INFO_DOUBLE + #define SYMBOL_MARGIN_LIMIT ((ENUM_SYMBOL_INFO_DOUBLE)46) + #define SYMBOL_MARGIN_MAINTENANCE ((ENUM_SYMBOL_INFO_DOUBLE)43) + #define SYMBOL_MARGIN_LONG ((ENUM_SYMBOL_INFO_DOUBLE)44) + #define SYMBOL_MARGIN_SHORT ((ENUM_SYMBOL_INFO_DOUBLE)45) + #define SYMBOL_MARGIN_STOP ((ENUM_SYMBOL_INFO_DOUBLE)47) + #define SYMBOL_MARGIN_STOPLIMIT ((ENUM_SYMBOL_INFO_DOUBLE)48) #endif template @@ -363,14 +365,14 @@ template <> _NULL_VALUE::operator string() const { return _empty_string; } -#define NULL_STRING "" + #define NULL_STRING "" #else -#define NULL_VALUE NULL -#define NULL_STRING NULL + #define NULL_VALUE NULL + #define NULL_STRING NULL #endif #ifndef __MQL__ -#include "Chart.enum.h" + #include "Chart.enum.h" /** * Returns currently selected period for platform. */ @@ -392,9 +394,9 @@ extern ENUM_TIMEFRAMES Period(); #define SET_BUFFER_AS_SERIES_FOR_TARGET(A) ArraySetAsSeries(A, false); #ifdef __MQL4__ -#define SET_BUFFER_AS_SERIES_FOR_HOST(A) ArraySetAsSeries(A, true); + #define SET_BUFFER_AS_SERIES_FOR_HOST(A) ArraySetAsSeries(A, true); #else -#define SET_BUFFER_AS_SERIES_FOR_HOST(A) ArraySetAsSeries(A, false); + #define SET_BUFFER_AS_SERIES_FOR_HOST(A) ArraySetAsSeries(A, false); #endif // Ensures that we do RELEASE_BUFFERx after ACQUIRE_BUFFERx. @@ -430,109 +432,141 @@ struct AsSeriesReleaseEnsurer { #define SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(NUM_BUFFS) _as_series_release_ensurer.done(NUM_BUFFS); // Acquiring buffer is preparing it to be used as in MQL5. -#define ACQUIRE_BUFFER1(A) \ - SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ +#define ACQUIRE_BUFFER1_NO_ENSURE(A) SET_BUFFER_AS_SERIES_FOR_TARGET(A); +#define ACQUIRE_BUFFER2_NO_ENSURE(A, B) \ + SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(B); +#define ACQUIRE_BUFFER3_NO_ENSURE(A, B, C) \ + SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(C); +#define ACQUIRE_BUFFER4_NO_ENSURE(A, B, C, D) \ + SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(D); +#define ACQUIRE_BUFFER5_NO_ENSURE(A, B, C, D, E) \ + SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(E); +#define ACQUIRE_BUFFER6_NO_ENSURE(A, B, C, D, E, F) \ + SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(E); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(F); +#define ACQUIRE_BUFFER7_NO_ENSURE(A, B, C, D, E, F, G) \ + SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(E); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(F); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(G); +#define ACQUIRE_BUFFER8_NO_ENSURE(A, B, C, D, E, F, G, H) \ + SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(E); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(F); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(G); \ + SET_BUFFER_AS_SERIES_FOR_TARGET(H); + +#define ACQUIRE_BUFFER1(A) \ + ACQUIRE_BUFFER1_NO_ENSURE(A); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_BEGIN(1); -#define ACQUIRE_BUFFER2(A, B) \ - SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ +#define ACQUIRE_BUFFER2(A, B) \ + ACQUIRE_BUFFER2_NO_ENSURE(A, B); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_BEGIN(2); #define ACQUIRE_BUFFER3(A, B, C) \ - SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ + ACQUIRE_BUFFER3_NO_ENSURE(A, B, C); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_BEGIN(3); -#define ACQUIRE_BUFFER4(A, B, C, D) \ - SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ +#define ACQUIRE_BUFFER4(A, B, C, D) \ + ACQUIRE_BUFFER4_NO_ENSURE(A, B, C, D); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_BEGIN(4); -#define ACQUIRE_BUFFER5(A, B, C, D, E) \ - SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(E); \ +#define ACQUIRE_BUFFER5(A, B, C, D, E) \ + ACQUIRE_BUFFER5_NO_ENSURE(A, B, C, D, E); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_BEGIN(5); -#define ACQUIRE_BUFFER6(A, B, C, D, E, F) \ - SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(E); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(F); \ +#define ACQUIRE_BUFFER6(A, B, C, D, E, F) \ + ACQUIRE_BUFFER6_NO_ENSURE(A, B, C, D, E, F); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_BEGIN(6); -#define ACQUIRE_BUFFER7(A, B, C, D, E, F, G) \ - SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(E); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(F); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(G); \ +#define ACQUIRE_BUFFER7(A, B, C, D, E, F, G) \ + ACQUIRE_BUFFER7_NO_ENSURE(A, B, C, D, E, F, G); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_BEGIN(7); -#define ACQUIRE_BUFFER8(A, B, C, D, E, F, G, H) \ - SET_BUFFER_AS_SERIES_FOR_TARGET(A); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(B); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(C); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(D); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(E); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(F); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(G); \ - SET_BUFFER_AS_SERIES_FOR_TARGET(H); \ +#define ACQUIRE_BUFFER8(A, B, C, D, E, F, G, H) \ + ACQUIRE_BUFFER8_NO_ENSURE(A, B, C, D, E, F, G, H); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_BEGIN(8); // Releasing buffer is setting its AsSeries as the default in the host language. -#define RELEASE_BUFFER1(A) \ - SET_BUFFER_AS_SERIES_FOR_HOST(A); \ +#define RELEASE_BUFFER1_NO_ENSURE(A) SET_BUFFER_AS_SERIES_FOR_HOST(A) +#define RELEASE_BUFFER2_NO_ENSURE(A, B) \ + SET_BUFFER_AS_SERIES_FOR_HOST(A); \ + SET_BUFFER_AS_SERIES_FOR_HOST(B); +#define RELEASE_BUFFER3_NO_ENSURE(A, B, C) \ + SET_BUFFER_AS_SERIES_FOR_HOST(A); \ + SET_BUFFER_AS_SERIES_FOR_HOST(B); \ + SET_BUFFER_AS_SERIES_FOR_HOST(C); +#define RELEASE_BUFFER4_NO_ENSURE(A, B, C, D) \ + SET_BUFFER_AS_SERIES_FOR_HOST(A); \ + SET_BUFFER_AS_SERIES_FOR_HOST(B); \ + SET_BUFFER_AS_SERIES_FOR_HOST(C); \ + SET_BUFFER_AS_SERIES_FOR_HOST(D); +#define RELEASE_BUFFER5_NO_ENSURE(A, B, C, D, E) \ + SET_BUFFER_AS_SERIES_FOR_HOST(A); \ + SET_BUFFER_AS_SERIES_FOR_HOST(B); \ + SET_BUFFER_AS_SERIES_FOR_HOST(C); \ + SET_BUFFER_AS_SERIES_FOR_HOST(D); \ + SET_BUFFER_AS_SERIES_FOR_HOST(E); +#define RELEASE_BUFFER6_NO_ENSURE(A, B, C, D, E, F) \ + SET_BUFFER_AS_SERIES_FOR_HOST(A); \ + SET_BUFFER_AS_SERIES_FOR_HOST(B); \ + SET_BUFFER_AS_SERIES_FOR_HOST(C); \ + SET_BUFFER_AS_SERIES_FOR_HOST(D); \ + SET_BUFFER_AS_SERIES_FOR_HOST(E); \ + SET_BUFFER_AS_SERIES_FOR_HOST(F); +#define RELEASE_BUFFER7_NO_ENSURE(A, B, C, D, E, F, G) \ + SET_BUFFER_AS_SERIES_FOR_HOST(A); \ + SET_BUFFER_AS_SERIES_FOR_HOST(B); \ + SET_BUFFER_AS_SERIES_FOR_HOST(C); \ + SET_BUFFER_AS_SERIES_FOR_HOST(D); \ + SET_BUFFER_AS_SERIES_FOR_HOST(E); \ + SET_BUFFER_AS_SERIES_FOR_HOST(F); \ + SET_BUFFER_AS_SERIES_FOR_HOST(G); +#define RELEASE_BUFFER8_NO_ENSURE(A, B, C, D, E, F, G, H) \ + SET_BUFFER_AS_SERIES_FOR_HOST(A); \ + SET_BUFFER_AS_SERIES_FOR_HOST(B); \ + SET_BUFFER_AS_SERIES_FOR_HOST(C); \ + SET_BUFFER_AS_SERIES_FOR_HOST(D); \ + SET_BUFFER_AS_SERIES_FOR_HOST(E); \ + SET_BUFFER_AS_SERIES_FOR_HOST(F); \ + SET_BUFFER_AS_SERIES_FOR_HOST(G); \ + SET_BUFFER_AS_SERIES_FOR_HOST(H); + +#define RELEASE_BUFFER1(A) \ + RELEASE_BUFFER1_NO_ENSURE(A); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(1); -#define RELEASE_BUFFER2(A, B) \ - SET_BUFFER_AS_SERIES_FOR_HOST(A); \ - SET_BUFFER_AS_SERIES_FOR_HOST(B); \ +#define RELEASE_BUFFER2(A, B) \ + RELEASE_BUFFER2_NO_ENSURE(A, B); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(2); -#define RELEASE_BUFFER3(A, B, C) \ - SET_BUFFER_AS_SERIES_FOR_HOST(A); \ - SET_BUFFER_AS_SERIES_FOR_HOST(B); \ - SET_BUFFER_AS_SERIES_FOR_HOST(C); \ +#define RELEASE_BUFFER3(A, B, C) \ + RELEASE_BUFFER3_NO_ENSURE(A, B, C); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(3); -#define RELEASE_BUFFER4(A, B, C, D) \ - SET_BUFFER_AS_SERIES_FOR_HOST(A); \ - SET_BUFFER_AS_SERIES_FOR_HOST(B); \ - SET_BUFFER_AS_SERIES_FOR_HOST(C); \ - SET_BUFFER_AS_SERIES_FOR_HOST(D); \ +#define RELEASE_BUFFER4(A, B, C, D) \ + RELEASE_BUFFER4_NO_ENSURE(A, B, C, D); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(4); -#define RELEASE_BUFFER5(A, B, C, D, E) \ - SET_BUFFER_AS_SERIES_FOR_HOST(A); \ - SET_BUFFER_AS_SERIES_FOR_HOST(B); \ - SET_BUFFER_AS_SERIES_FOR_HOST(C); \ - SET_BUFFER_AS_SERIES_FOR_HOST(D); \ - SET_BUFFER_AS_SERIES_FOR_HOST(E); \ +#define RELEASE_BUFFER5(A, B, C, D, E) \ + RELEASE_BUFFER5_NO_ENSURE(A, B, C, D, E); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(5); -#define RELEASE_BUFFER6(A, B, C, D, E, F) \ - SET_BUFFER_AS_SERIES_FOR_HOST(A); \ - SET_BUFFER_AS_SERIES_FOR_HOST(B); \ - SET_BUFFER_AS_SERIES_FOR_HOST(C); \ - SET_BUFFER_AS_SERIES_FOR_HOST(D); \ - SET_BUFFER_AS_SERIES_FOR_HOST(E); \ - SET_BUFFER_AS_SERIES_FOR_HOST(F); \ +#define RELEASE_BUFFER6(A, B, C, D, E, F) \ + RELEASE_BUFFER6_NO_ENSURE(A, B, C, D, E, F); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(6); -#define RELEASE_BUFFER7(A, B, C, D, E, F, G) \ - SET_BUFFER_AS_SERIES_FOR_HOST(A); \ - SET_BUFFER_AS_SERIES_FOR_HOST(B); \ - SET_BUFFER_AS_SERIES_FOR_HOST(C); \ - SET_BUFFER_AS_SERIES_FOR_HOST(D); \ - SET_BUFFER_AS_SERIES_FOR_HOST(E); \ - SET_BUFFER_AS_SERIES_FOR_HOST(F); \ - SET_BUFFER_AS_SERIES_FOR_HOST(G); \ +#define RELEASE_BUFFER7(A, B, C, D, E, F, G) \ + RELEASE_BUFFER7_NO_ENSURE(A, B, C, D, E, F, G); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(7); -#define RELEASE_BUFFER8(A, B, C, D, E, F, G, H) \ - SET_BUFFER_AS_SERIES_FOR_HOST(A); \ - SET_BUFFER_AS_SERIES_FOR_HOST(B); \ - SET_BUFFER_AS_SERIES_FOR_HOST(C); \ - SET_BUFFER_AS_SERIES_FOR_HOST(D); \ - SET_BUFFER_AS_SERIES_FOR_HOST(E); \ - SET_BUFFER_AS_SERIES_FOR_HOST(F); \ - SET_BUFFER_AS_SERIES_FOR_HOST(G); \ - SET_BUFFER_AS_SERIES_FOR_HOST(H); \ +#define RELEASE_BUFFER8(A, B, C, D, E, F, G, H) \ + RELEASE_BUFFER8_NO_ENSURE(A, B, C, D, E, F, G, H); \ SET_BUFFER_AS_SERIES_RELEASE_ENSURER_END(8); diff --git a/Storage/ItemsHistory.h b/Storage/ItemsHistory.h index f946fbea3..1420a5076 100644 --- a/Storage/ItemsHistory.h +++ b/Storage/ItemsHistory.h @@ -25,8 +25,8 @@ #define ITEMS_HISTORY_H #ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once + // Allows the preprocessor to include a header file when it is needed. + #pragma once #endif #include "../DictStruct.mqh" @@ -56,13 +56,21 @@ class ItemsHistoryItemProvider : public Dynamic { /** * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we - * want previous or next items from selected starting point. + * want previous or next items from selected starting point. Should return false if retrieving items by this method + * is not available. */ - void GetItems(ItemsHistory>* _history, long _from_time_ms, + bool GetItems(ItemsHistory>* _history, long _from_time_ms, ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(IV, _out_arr)) { - // Method is called if there is a missing item (candle) in the history. We need to regenerate it. - Print("Error: Retrieving items by this item provider is not implemented!"); - DebugBreak(); + return false; + } + + /** + * Retrieves items between given indices (both indices inclusive). Should return false if retrieving items by this + * method is not available. + */ + bool GetItems(ItemsHistory>* _history, int _start_index, int _end_index, + ARRAY_REF(IV, _out_arr)) { + return false; } /** @@ -77,7 +85,7 @@ class ItemsHistoryItemProvider : public Dynamic { /** * Returns information about item provider. */ - virtual string const ToString() { return "Abstract items history item provider."; } + string const ToString() override { return "Abstract items history item provider."; } }; /** @@ -92,6 +100,9 @@ class ItemsHistoryItemProvider : public Dynamic { */ template class ItemsHistory { + // Indicator the history is provided for. + IndicatorData* indi; + // Provides items from bound provider. Ref item_provider; @@ -128,13 +139,16 @@ class ItemsHistory { /** * Constructor */ - ItemsHistory(unsigned int _history_max_size = 0) - : history_max_size(_history_max_size), + ItemsHistory(IndicatorData* _indi, unsigned int _history_max_size = 0) + : indi(_indi), + history_max_size(_history_max_size), current_index(0), first_valid_index(0), first_valid_index_ever(0), last_valid_index(0), - peak_size(0) {} + peak_size(0) { + history.SetMaxConflicts(25); + } /** * Returns item provider. @@ -155,12 +169,27 @@ class ItemsHistory { } */ + /** + * Returns indicator the history is provided for. + */ + IndicatorData* GetIndicator() { return indi; } + /** * Returns maximum number of items that occupied the history. Could be used e.g., to determine how many bars could be * retrieved from history and past the history. */ unsigned int GetPeakSize() { return peak_size; } + /** + * Increments maximum size of historic items. + */ + void ReserveAdditionalHistoryMaxSize(unsigned int size) { history_max_size += size; } + + /** + * Changes maximum size of historic items. + */ + void SetHistoryMaxSize(unsigned int size) { history_max_size += size; } + /** * Will regenerate items from item provider. "_dir" indicates if we have to prepend or append items. */ @@ -169,80 +198,111 @@ class ItemsHistory { Print("RegenerateHistory(", _from_index, ", ", _to_index, ", ", EnumToString(_dir), "), ", GetInfo()); #endif - static ARRAY(IV, _items); // Items generated by provider. - ArrayResize(_items, 0); - int _item_count = _to_index - _from_index + 1; - long _from_time_ms; - IV _item; - // Calculating time to be passed to GetItems(). - if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { - if (history.Size() == 0) { -#ifdef __debug_items_history__ - Print("RegenerateHistory: Getting initial time from item provider"); -#endif - - // Time from we'll be getting items will be the time of the first possible item/candle/tick. - _from_time_ms = item_provider REF_DEREF GetInitialTimeMs(); - } else { -#ifdef __debug_items_history__ - Print("RegenerateHistory: Getting last valid item at index ", last_valid_index); -#endif - - // Time will be the time of last valid item + item's length + 1ms. - // Note that ticks have length of 0ms, so next tick could be at least 1ms after the previous tick. - _item = GetItemByIndex(last_valid_index); - _from_time_ms = _item.GetTimeMs() + _item.GetLengthMs() + 1; - } - - // long _current_time_ms = TimeCurrent() * 1000; + // Static buffer for items generated by the item provider. + static ARRAY(IV, _items); + ArrayResize(_items, 0); - if (_from_time_ms > (long)TimeCurrent() * 1000) { - // There won't be items in the future. - return; - } - } else if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { - if (history.Size() == 0) { - // Time from we'll be getting items will be the time of the first possible item/candle/tick - 1ms. - _from_time_ms = item_provider REF_DEREF GetInitialTimeMs() - 1; - } else { -#ifdef __debug_items_history__ - Print("RegenerateHistory: Getting first valid item at index ", first_valid_index); -#endif + // Currently we only support getting items via start -> end indices. - // Time will be the time of the first valid item - 1ms. - _item = GetItemByIndex(first_valid_index); - _from_time_ms = _item.GetTimeMs() - 1; - } - } else { - Print("Error: We shouldn't be here!"); - DebugBreak(); - return; - } - - item_provider REF_DEREF GetItems(THIS_PTR, _from_time_ms, _dir, _item_count, _items); + item_provider REF_DEREF GetItems(THIS_PTR, _from_index, _to_index, _items); if (ArraySize(_items) != _item_count) { -// There's really no problem if number of generated items are less than -// requested. -// @todo However, if there's too many calls for RegenerateHistory then we -// need to find a way to make it exit earlier. #ifdef __debug_items_history__ Print("RegenerateHistory: Notice: Requested ", _item_count, " historic items, got ", ArraySize(_items), ". from index = ", _from_index, ", to index = ", _to_index, ", dir = ", EnumToString(_dir), " (", GetInfo(), ")"); #endif - return; } - for (int i = 0; i < _item_count; ++i) { - if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + int i; + + if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + for (i = 0; i < _item_count; ++i) { Append(_items[i], false); - } else if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { + } + } else if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { + // Our _items array contain most recent items at the start, so we have to iterate from the end in order to prepend + // items. + if (peak_size <= 1) { + // It's the first time we're trying to retrieve historic items. We need to make sure all history will fit in the + // buffer. Note that we use <= 1, because Tick indicator could already have appeneded first candle. + history_max_size = MathMax(history_max_size, history.Size() + _item_count); + } + for (i = 0; i < _item_count; ++i) { Prepend(_items[i], false); } } + + /* + + int _item_count = _to_index - _from_index + 1; + long _from_time_ms; + IV _item; + + // Calculating time to be passed to GetItems(). + if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + if (history.Size() == 0) { + #ifdef __debug_items_history__ + Print("RegenerateHistory: Getting initial time from item provider"); + #endif + + // Time from we'll be getting items will be the time of the first possible item/candle/tick. + _from_time_ms = item_provider REF_DEREF GetInitialTimeMs(); + } else { + #ifdef __debug_items_history__ + Print("RegenerateHistory: Getting last valid item at index ", last_valid_index); + #endif + + // Time will be the time of last valid item + item's length + 1ms. + // Note that ticks have length of 0ms, so next tick could be at least 1ms after the previous tick. + _item = GetItemByIndex(last_valid_index); + _from_time_ms = _item.GetTimeMs() + _item.GetLengthMs() + 1; + } + + long _current_time_ms = TimeCurrent() * 1000; + + if (_from_time_ms > (long)TimeCurrent() * 1000) { + // There won't be items in the future. + return; + } + } else if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { + if (history.Size() == 0) { + // Time from we'll be getting items will be the time of the first possible item/candle/tick - 1ms. + _from_time_ms = item_provider REF_DEREF GetInitialTimeMs() - 1; + } else { + #ifdef __debug_items_history__ + Print("RegenerateHistory: Getting first valid item at index ", first_valid_index); + #endif + + // Time will be the time of the first valid item - 1ms. + _item = GetItemByIndex(first_valid_index); + _from_time_ms = _item.GetTimeMs() - 1; + } + } else { + Print("Error: We shouldn't be here!"); + DebugBreak(); + return; + } + + item_provider REF_DEREF GetItems(THIS_PTR, _from_time_ms, _dir, _item_count, _items); + + if (ArraySize(_items) != _item_count) { + // There's really no problem if number of generated items are less than + // requested. + // @todo However, if there's too many calls for RegenerateHistory then we + // need to find a way to make it exit earlier. + #ifdef __debug_items_history__ + Print("RegenerateHistory: Notice: Requested ", _item_count, " historic items, got ", ArraySize(_items), + ". from index = ", _from_index, ", to index = ", _to_index, ", dir = ", EnumToString(_dir), " (", + GetInfo(), + ")"); + #endif + return; + } + + */ } string GetInfo() { @@ -352,11 +412,11 @@ class ItemsHistory { */ bool EnsureShiftExists(int _shift) { if (history.Size() == 0) { - return false; + // return false; } #ifdef __debug_items_history__ - Print("EnsureShiftExists(", _shift, ")"); + // Print("EnsureShiftExists(", _shift, ")"); #endif int _index = GetShiftIndex(_shift); @@ -463,7 +523,13 @@ class ItemsHistory { return (datetime)0; } - return (datetime)(GetItemTimeByShiftMsc(_shift) / 1000); + datetime _dt = (datetime)(GetItemTimeByShiftMsc(_shift) / 1000); + +#ifdef __debug_items_history__ + Print("GetItemTimeByShift(", _shift, "), ", GetInfo(), " = ", _dt); +#endif + + return _dt; } /**