Prices and Signals in DoEasy library (Part 65): Depth of Market collection and the class for working with MQL5.com Signals
Contents
Concept
We already have the functionality for working with a DOM of any symbol — in the previous articles, I have created the classes of the DOM abstract order objects and its descendants, the DOM snapshot class and the DOM snapshot series class. Now it remains to create a common storage for DOM snapshot series objects — the snapshot series collection class, which is to store all these series with a convenient access to any DOM snapshot stored in the collection lists and auto update (adding new snapshots and removing old ones) for supporting specified series size.
Apart from creating the DOM snapshot series collection class, I will also start the new library section — other library classes.
I will start with the functionality for working with the MQL5.com signal service, namely I will create the signal object class storing all data of a single signal broadcast by the MQL5.com Signals service.
Improving library classes
Let's add the new messages to the library right away. In \MQL5\Include\DoEasy\Data.mqh, add new message indices:
//--- CMBookSeries MSG_MBOOK_SERIES_TEXT_MBOOKSERIES, // DOM snapshot series MSG_MBOOK_SERIES_ERR_ADD_TO_LIST, // Error. Failed to add DOM snapshot series to the list //--- CMBookSeriesCollection MSG_MB_COLLECTION_TEXT_MBCOLLECTION, // DOM snapshot series collection //--- CMQLSignal MSG_SIGNAL_MQL5_TEXT_SIGNAL, // Signal MSG_SIGNAL_MQL5_TEXT_SIGNAL_MQL5, // MQL5.com Signals service signal MSG_SIGNAL_MQL5_TRADE_MODE, // Account type MSG_SIGNAL_MQL5_DATE_PUBLISHED, // Publication date MSG_SIGNAL_MQL5_DATE_STARTED, // Monitoring start date MSG_SIGNAL_MQL5_DATE_UPDATED, // Date of the latest update of the trading statistics MSG_SIGNAL_MQL5_ID, // ID MSG_SIGNAL_MQL5_LEVERAGE, // Trading account leverage MSG_SIGNAL_MQL5_PIPS, // Trading result in pips MSG_SIGNAL_MQL5_RATING, // Position in the signal rating MSG_SIGNAL_MQL5_SUBSCRIBERS, // Number of subscribers MSG_SIGNAL_MQL5_TRADES, // Number of trades MSG_SIGNAL_MQL5_SUBSCRIPTION_STATUS, // Status of account subscription to a signal MSG_SIGNAL_MQL5_EQUITY, // Account equity MSG_SIGNAL_MQL5_GAIN, // Account growth in % MSG_SIGNAL_MQL5_MAX_DRAWDOWN, // Maximum drawdown MSG_SIGNAL_MQL5_PRICE, // Signal subscription price MSG_SIGNAL_MQL5_ROI, // Signal ROI (Return on Investment) in % MSG_SIGNAL_MQL5_AUTHOR_LOGIN, // Author login MSG_SIGNAL_MQL5_BROKER, // Broker (company) name MSG_SIGNAL_MQL5_BROKER_SERVER, // Broker server MSG_SIGNAL_MQL5_NAME, // Name MSG_SIGNAL_MQL5_CURRENCY, // Account currency MSG_SIGNAL_MQL5_TEXT_GAIN, // Growth MSG_SIGNAL_MQL5_TEXT_DRAWDOWN, // Drawdown }; //+------------------------------------------------------------------+
and message texts corresponding to newly added indices:
//--- CMBookSeries {"Серия снимков стакана цен","Series of shots of the Depth of Market"}, {"Ошибка. Не удалось добавить серию снимков стакана цен в список","Error. Failed to add a shots series of the Depth of Market to the list"}, //--- CMBookSeriesCollection {"Коллекция серий снимков стакана цен","Collection of series of the Depth of Market shot"}, //--- CMQLSignal {"Сигнал","Signal"}, {"Сигнал сервиса сигналов MQL5.com","Signal from MQL5.com signal service"}, {"Тип счета","Account type"}, {"Дата публикации","Publication date"}, {"Дата начала мониторинга","Monitoring starting date"}, {"Дата последнего обновления торговой статистики","The date of the last update of the signal's trading statistics"}, {"ID","ID"}, {"Плечо торгового счета","Account leverage"}, {"Результат торговли в пипсах","Profit in pips"}, {"Позиция в рейтинге сигналов","Position in rating"}, {"Количество подписчиков","Number of subscribers"}, {"Количество трейдов","Number of trades"}, {"Состояние подписки счёта на этот сигнал","Account subscription status for this signal"}, {"Средства на счете","Account equity"}, {"Прирост счета в процентах","Account gain"}, {"Максимальная просадка","Account maximum drawdown"}, {"Цена подписки на сигнал","Signal subscription price"}, {"Значение ROI (Return on Investment) сигнала в %","Return on Investment (%)"}, {"Логин автора","Author login"}, {"Наименование брокера (компании)","Broker name (company)"}, {"Сервер брокера","Broker server"}, {"Имя","Name"}, {"Валюта счета","Base currency"}, {"Прирост","Gain"}, {"Просадка","Drawdown"}, }; //+---------------------------------------------------------------------+
Since I am going to develop a new collection today, we need to set its ID. In the ID section in \MQL5\Include\DoEasy\Defines.mqh, add the DOM snapshot series collection ID:
//--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID #define COLLECTION_SERIES_ID (0x777F) // Timeseries collection list ID #define COLLECTION_BUFFERS_ID (0x7780) // Indicator buffer collection list ID #define COLLECTION_INDICATORS_ID (0x7781) // Indicator collection list ID #define COLLECTION_INDICATORS_DATA_ID (0x7782) // Indicator data collection list ID #define COLLECTION_TICKSERIES_ID (0x7783) // Tick series collection list ID #define COLLECTION_MBOOKSERIES_ID (0x7784) // DOM series collection list ID //--- Data parameters for file operations
Let's improve the file of the DOM snapshot series object class. In some instances, the object description should be displayed once, while sometimes, we need to display descriptions of all DOM snapshot series at once. In this case, the list looks more visually appealing if a hyphen is added before the description of each series. In the file of the DOM snapshot series class \MQL5\Include\DoEasy\Objects\Book\MBookSeries.mqh, add the changes in the description of the methods:
//--- Display (1) description and (2) short description of a DOM snapshot series void Print(const bool dash=false); void PrintShort(const bool dash=false); //--- Constructors
Simply use the flag indicating the necessity of displaying a hyphen before the object description.
Add the same changes in the implementation of the methods:
//+------------------------------------------------------------------+ //| Display the description of the DOM snapshot series in the journal| //+------------------------------------------------------------------+ void CMBookSeries::Print(const bool dash=false) { string txt= ( CMessage::Text(MSG_TICKSERIES_REQUIRED_HISTORY_DAYS)+(string)this.RequiredUsedDays()+", "+ CMessage::Text(MSG_LIB_TEXT_TS_ACTUAL_DEPTH)+(string)this.DataTotal() ); ::Print((dash ? "- " : ""),this.Header(),": ",txt); } //+------------------------------------------------------------------+ //| Display a short description of a DOM snapshot series | //+------------------------------------------------------------------+ void CMBookSeries::PrintShort(const bool dash=false) { ::Print((dash ? "- " : ""),this.Header()); } //+------------------------------------------------------------------+
By default, the flag is set to false, so no hyphen is displayed before the object description.
DOM collection class
We have all the necessary objects for creating the DOM collection. Namely, we have the DOM order object represented by the MqlBookInfo structure in the terminal. When the BookEvent event arrives, the OnBookEvent() handler is called. A symbol, on which the DOM change event has occurred, is specified as its parameter. We are able to receive all current DOM data to the MqlBookInfo structure array. The collection of this data in the library is described by the DOM snapshot object featuring order objects we obtained when handling the DOM change event. During each DOM change, we create a new snapshot object we add to the list. Such a list is represented by the DOM snapshot series object. We create such lists for each symbol we have subscribed to in order to receive DOM events. The lists are created in real time (unfortunately, the terminal has no DOM change history for a symbol). As a result, we now need to combine all existing lists into a single DOM collection object.
The symbol DOM collection is to store constantly updated DOM snapshot lists allowing us to create the history of DOM changes for each symbol while the program is running. We will be able to get data on any snapshot present in the collection lists. Besides, we will retrieve any order out of each snapshot. We will be able to search for the required data, sort out the lists by specified criteria and conduct statistical studies with the available collection lists.
In \MQL5\Include\DoEasy\Collections\, create the new class CMBookSeriesCollection in BookSeriesCollection.mqh.
The class of the basic object of all CBaseObj library objects should be used as the base one, while all the necessary files should be included into the class listing:
//+------------------------------------------------------------------+ //| BookSeriesCollection.mqh | //| Copyright 2021, MetaQuotes Software Corp. | //| https://MQL5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://MQL5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Objects\Book\MBookSeries.mqh" #include "..\Objects\Symbols\Symbol.mqh" //+------------------------------------------------------------------+ //| Collection of symbol DOM series | //+------------------------------------------------------------------+ class CMBookSeriesCollection : public CBaseObj { }
Let's have a look at the class body and its methods, and analyze the implementation of the methods and their purpose:
//+------------------------------------------------------------------+ //| Collection of symbol DOM series | //+------------------------------------------------------------------+ class CMBookSeriesCollection : public CBaseObj { private: CListObj m_list; // List of used symbol DOM series //--- Return the DOM series index by a symbol name int IndexMBookSeries(const string symbol); public: //--- Return (1) itself, (2) DOM series collection list and (3) the number of DOM series in the list CMBookSeriesCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } int DataTotal(void) const { return this.m_list.Total(); } //--- Return the pointer to the DOM series object (1) by symbol and (2) by index in the list CMBookSeries *GetMBookseries(const string symbol); CMBookSeries *GetMBookseries(const int index) { return this.m_list.At(index);} //--- Create symbol DOM series collection list bool CreateCollection(const CArrayObj *list_symbols,const uint required=0); //--- Set the flag of using the DOM series of (1) a specified symbol and (2) all symbols void SetAvailableMBookSeries(const string symbol,const bool flag=true); void SetAvailableMBookSeries(const bool flag=true); //--- Return the flag of using the DOM series of (1) a specified symbol and (2) all symbols bool IsAvailableMBookSeries(const string symbol); bool IsAvailableMBookSeries(void); //--- Set the number of DOM history days of (1) a specified symbol and (2) all symbols bool SetRequiredUsedDays(const string symbol,const uint required=0); bool SetRequiredUsedDays(const uint required=0); //--- Return the DOM snapshot object of a specified symbol (1) by index and (2) by time in milliseconds CMBookSnapshot *GetMBook(const string symbol,const int index); CMBookSnapshot *GetMBook(const string symbol,const long time_msc); //--- Update the DOM series of a specified symbol bool Refresh(const string symbol,const long time_msc); //--- Display (1) the complete and (2) short collection description in the journal void Print(void); void PrintShort(void); //--- Constructor CMBookSeriesCollection(); }; //+------------------------------------------------------------------+
The method descriptions make it clear that the collection itself is represented by m_list, which is to store series objects of snapshots of DOMs for different symbols represented by the CMBookSeries class. These are the symbols featuring DOM snapshot objects represented by the CMBookSnapshot class.
Let's consider the implementation of the class methods in more details.
In the class constructor, clear the collection list, set the sorted list flag to the list and assign the DOM series collection list ID:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CMBookSeriesCollection::CMBookSeriesCollection() { this.m_list.Clear(); this.m_list.Sort(); this.m_list.Type(COLLECTION_MBOOKSERIES_ID); } //+------------------------------------------------------------------+
By default, all series lists are sorted in the collection list by symbol name of DOM snapshot series.
The method of creating the symbol DOM series collection list:
//+------------------------------------------------------------------+ //| Create symbol DOM series collection list | //+------------------------------------------------------------------+ bool CMBookSeriesCollection::CreateCollection(const CArrayObj *list_symbols,const uint required=0) { //--- If an empty list of symbol objects is passed, exit if(list_symbols==NULL) return false; //--- Get the number of symbol objects in the passed list int total=list_symbols.Total(); //--- Clear the DOM series collection list this.m_list.Clear(); //--- In a loop by all symbol objects for(int i=0;i<total;i++) { //--- get the next symbol object CSymbol *symbol_obj=list_symbols.At(i); //--- if failed to get a symbol object, move on to the next one in the list if(symbol_obj==NULL) continue; //--- Create a new DOM series object CMBookSeries *bookseries=new CMBookSeries(symbol_obj.Name(),required); //--- If failed to create the DOM series object, move on to the next symbol in the list if(bookseries==NULL) continue; //--- Set the sorted list flag for the DOM series collection list this.m_list.Sort(); //--- If the object with the same symbol name is already present in the DOM series collection list, remove the DOM series object if(this.m_list.Search(bookseries)>WRONG_VALUE) delete bookseries; //--- otherwise, there is no object with such a symbol name in the collection yet else { //--- if failed to add the DOM series object to the collection list, remove the series object, //--- inform of that and return 'false' if(!this.m_list.Add(bookseries)) { delete bookseries; ::Print(DFUN,"\"",symbol_obj.Name(),"\": ",CMessage::Text(MSG_MBOOK_SERIES_ERR_ADD_TO_LIST)); return false; } } } //--- Collection created successfully, return 'true' return true; } //+------------------------------------------------------------------+
All method logic is described in detail in its listing. Briefly, the method receives the list of all symbols used in the program and the number of DOM snapshots than can be stored in the collection lists. In the loop by the list passed to the method, receive the next symbol object from the list, create a new DOM snapshot series object by the symbol and add it to the collection list. As a result, we get the list of snapshot series objects of DOMs for different symbols — the ones present in the list passed to the method. Each created snapshot series list is initially empty.
The lists created in the list collection are filled in the method of updating the snapshot series list of a DOM of a specified symbol:
//+------------------------------------------------------------------+ //| Update the snapshot series list of a specified symbol DOM | //+------------------------------------------------------------------+ bool CMBookSeriesCollection::Refresh(const string symbol,const long time_msc) { CMBookSeries *bookseries=this.GetMBookseries(symbol); if(bookseries==NULL) return false; return bookseries.Refresh(time_msc); } //+------------------------------------------------------------------+
Since the OnBookEvent() handler is activated for each symbol, the update method is used only for one symbol from the collection rather than for the entire collection. The method receives the name of a symbol, on which the DOM change event happened, and the time of registering a DOM change event in milliseconds. Further on, we receive the DOM snashot series object by a symbol passed to the method and return the Refresh() method result of the DOM snapshot series object we have considered in the previous article.
The method of returning the DOM series index by symbol name:
//+------------------------------------------------------------------+ //| Return the DOM series index by a symbol name | //+------------------------------------------------------------------+ int CMBookSeriesCollection::IndexMBookSeries(const string symbol) { const CMBookSeries *obj=new CMBookSeries(symbol==NULL || symbol=="" ? ::Symbol() : symbol); if(obj==NULL) return WRONG_VALUE; this.m_list.Sort(); int index=this.m_list.Search(obj); delete obj; return index; } //+------------------------------------------------------------------+
Here, we create a new temporary DOM snapshot series object with a symbol specified in the inputs. Set the sorted list flag (the search is performed only in the sorted list) and obtain the object index with the same symbol in the list. Make sure to remove the temporary object and return the obtained index. If the DOM snapshot series object with a specified symbol is present in the list, the Search() method return its index, otherwise, the method returns -1.
The method returning the specified symbol DOM series object:
//+------------------------------------------------------------------+ //| Return the specified symbol DOM series object | //+------------------------------------------------------------------+ CMBookSeries *CMBookSeriesCollection::GetMBookseries(const string symbol) { int index=this.IndexMBookSeries(symbol); return this.m_list.At(index); } //+------------------------------------------------------------------+
Here we use the method specified above to get the index of the DOM snapshot series object in the list by symbol and return the pointer to the object in the list by the specified index. If the object with a specified symbol is not present in the list (the index is -1), the At() method returns NULL.
To use the functionality of working with DOMs, we need to subscribe to receive DOM change events for each symbol. This feature is already present in the symbol object class. Besides, the DOM snapshot series object features the flag indicating the need to enable/disable handling of DOM change events. In other words, even if subscription to BookEvent events is active, we may temporarily disable working with DOM on this symbol using the flag. Use the following method to set the flag indicating the necessity to handle BookEvent events by symbol:
//+------------------------------------------------------------------+ //| Set the flag of using a series | //| of a specified symbol's DOM | //+------------------------------------------------------------------+ void CMBookSeriesCollection::SetAvailableMBookSeries(const string symbol,const bool flag=true) { CMBookSeries *bookseries=this.GetMBookseries(symbol); if(bookseries==NULL) return; bookseries.SetAvailable(flag); } //+------------------------------------------------------------------+
Here we receive the series object of a DOM for a specified symbol and set the flag passed to the method for it.
The default flag value is true.
If our program works with multiple symbols, while we need to manage the DOM snapshot series flags mentioned above, then the method defining the flag value for all symbols used in the program at once can be of help:
//+------------------------------------------------------------------+ //|Set the flag of using the DOM series for all symbols | //+------------------------------------------------------------------+ void CMBookSeriesCollection::SetAvailableMBookSeries(const bool flag=true) { for(int i=0;i<this.m_list.Total();i++) { CMBookSeries *bookseries=this.m_list.At(i); if(bookseries==NULL) continue; bookseries.SetAvailable(flag); } } //+------------------------------------------------------------------+
Here, in the loop by the total number of DOM snapshot series objects in the collection, get the next series list and set the flag passed to the method for it. The default flag value is true.
The methods opposite to those considered above return the values of flags for working with the series list of either a specified symbol or all of them at once.
The method returning the flag of using the DOM series of a specified symbol:
//+------------------------------------------------------------------+ //|Return the flag of using the DOM series of a specified symbol | //+------------------------------------------------------------------+ bool CMBookSeriesCollection::IsAvailableMBookSeries(const string symbol) { CMBookSeries *bookseries=this.GetMBookseries(symbol); if(bookseries==NULL) return false; return bookseries.IsAvailable(); } //+------------------------------------------------------------------+
Here, we obtain the object series of DOM snapshots by a symbol passed to the method and return the flag value specified for the object.
The method returning the flag of using the DOM series of all symbols:
//+------------------------------------------------------------------+ //| Return the flag of using the DOM series of all symbols | //+------------------------------------------------------------------+ bool CMBookSeriesCollection::IsAvailableMBookSeries(void) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CMBookSeries *bookseries=this.m_list.At(i); if(bookseries==NULL) continue; res &=bookseries.IsAvailable(); } return res; } //+------------------------------------------------------------------+
The method returns true only if the flag indicating the necessity of the symbol use is activated for each of the used symbols in their DOM series. Here, in the loop by all collection series objects, we obtain the next series and add the series flag value to the variable. If the flag is set to false for at least one of the symbols, the method returns false.
The method setting the number of snapshots in the DOM history of the specified symbol:
//+------------------------------------------------------------------+ //| Set the number of snapshots in history | //| of a specified symbol's DOM | //+------------------------------------------------------------------+ bool CMBookSeriesCollection::SetRequiredUsedDays(const string symbol,const uint required=0) { CMBookSeries *bookseries=this.GetMBookseries(symbol); if(bookseries==NULL) return false; bookseries.SetRequiredUsedDays(required); return true; } //+------------------------------------------------------------------+
The method is used to limit the total number of DOM snapshots for each of the symbols and avoid spending extra memory on outdated data. Here we obtain the snapshot series list of a specified symbol and set the maximum number of snapshots in the list for it. If failed to obtain the snapshot series list, return false. If the required number is successfully set, return true.
The method returning the number of snapshots in the DOM history of all symbols:
//+------------------------------------------------------------------+ //|Set the number of snapshots in the DOM history of all symbols | //+------------------------------------------------------------------+ bool CMBookSeriesCollection::SetRequiredUsedDays(const uint required=0) { bool res=true; for(int i=0;i<this.m_list.Total();i++) { CMBookSeries *bookseries=this.m_list.At(i); if(bookseries==NULL) { res &=false; continue; } bookseries.SetRequiredUsedDays(required); } return res; } //+------------------------------------------------------------------+
The method sets a similar number of required DOM history data for all symbols used in the program at once.
In the loop by the total number of series objects in the collection, receive the next DOM snapshot series object and set the specified amount of data for it. If failed to obtain the series, false is added to the result. Thus, if at least one series fails to set the necessary amount, the method returns false. Upon the loop completion, return the result obtained in the res variable.
The method returning the DOM snapshot object of a specified symbol by index:
//+------------------------------------------------------------------+ //|Return the DOM snapshot object of a specified symbol by index | //+------------------------------------------------------------------+ CMBookSnapshot *CMBookSeriesCollection::GetMBook(const string symbol,const int index) { CMBookSeries *bookseries=this.GetMBookseries(symbol); if(bookseries==NULL) return NULL; return bookseries.GetMBookByListIndex(index); } //+------------------------------------------------------------------+
Here, we receive the DOM snapshot series object of a specified symbol and return the pointer to the DOM snapshot object by a specified index using the GetMBookByListIndex() method of the CMBookSeries class described in the previous article.
The method returning the DOM snapshot object of a specified symbol by time in milliseconds:
//+------------------------------------------------------------------+ //| Return the DOM snapshot object of a specified symbol | //| by time in milliseconds | //+------------------------------------------------------------------+ CMBookSnapshot *CMBookSeriesCollection::GetMBook(const string symbol,const long time_msc) { CMBookSeries *bookseries=this.GetMBookseries(symbol); if(bookseries==NULL) return NULL; return bookseries.GetMBook(time_msc); } //+------------------------------------------------------------------+
Here, we receive the DOM snapshot series object of a specified symbol and return the pointer to the DOM snapshot object by a specified index using the GetMBook() method of the CMBookSeries class described in the previous article.
The distinguishing feature of the method is that, in order to get the pointer to the DOM snapshot object, we need to know its exact time in milliseconds.
The method displaying the full description of the DOM snapshot series collection in the journal:
//+------------------------------------------------------------------+ //| Display complete collection description to the journal | //+------------------------------------------------------------------+ void CMBookSeriesCollection::Print(void) { ::Print(CMessage::Text(MSG_MB_COLLECTION_TEXT_MBCOLLECTION),":"); for(int i=0;i<this.m_list.Total();i++) { CMBookSeries *bookseries=this.m_list.At(i); if(bookseries==NULL) continue; bookseries.Print(true); } } //+------------------------------------------------------------------+
First, display the header, next in the loop, obtain each subsequent DOM series object of the collection and display its full description. In the Print() method of the snapshot series object, we pass true so that the method sets a hyphen before the description.
The method returning the short collection list to the journal:
//+------------------------------------------------------------------+ //| Display the short collection description in the journal | //+------------------------------------------------------------------+ void CMBookSeriesCollection::PrintShort(void) { ::Print(CMessage::Text(MSG_MB_COLLECTION_TEXT_MBCOLLECTION),":"); for(int i=0;i<this.m_list.Total();i++) { CMBookSeries *bookseries=this.m_list.At(i); if(bookseries==NULL) continue; bookseries.PrintShort(true); } } //+------------------------------------------------------------------+
The method is identical to the one described above except that the method of displaying a short description of the DOM series object is used to display the collection series description.
Let's improve the class of the CEngine library main object in \MQL5\Include\DoEasy\Engine.mqh.
Include the DOM snapshot series collection clas into the file:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://MQL5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://MQL5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "Collections\TimeSeriesCollection.mqh" #include "Collections\BuffersCollection.mqh" #include "Collections\IndicatorsCollection.mqh" #include "Collections\TickSeriesCollection.mqh" #include "Collections\BookSeriesCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+
In the class object list, add a new object — the DOM snapshot series collection class:
//+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CTimeSeriesCollection m_time_series; // Timeseries collection CBuffersCollection m_buffers; // Collection of indicator buffers CIndicatorsCollection m_indicators; // Indicator collection CTickSeriesCollection m_tick_series; // Collection of tick series CMBookSeriesCollection m_book_series; // Collection of DOM series CResourceCollection m_resource; // Resource list CTradingControl m_trading; // Trading management object CPause m_pause; // Pause object CArrayObj m_list_counters; // List of timer counters int m_global_error; // Global error code bool m_first_start; // First launch flag bool m_is_hedge; // Hedge account flag bool m_is_tester; // Flag of working in the tester bool m_is_market_trade_event; // Account trading event flag bool m_is_history_trade_event; // Account history trading event flag bool m_is_account_event; // Account change event flag bool m_is_symbol_event; // Symbol change event flag ENUM_TRADE_EVENT m_last_trade_event; // Last account trading event int m_last_account_event; // Last event in the account properties int m_last_symbol_event; // Last event in the symbol properties ENUM_PROGRAM_TYPE m_program; // Program type string m_name; // Program name
Add the new BookEvent event handler. In the method setting the list of used symbols in the symbol collection, apart from passing the number of days for the tick series, add passing the maximum number of DOM snapshots:
//--- (1) Timer, event handler (2) NewTick, (3) Calculate, (4) BookEvent, (4) Deinit void OnTimer(SDataCalculate &data_calculate); void OnTick(SDataCalculate &data_calculate,const uint required=0); int OnCalculate(SDataCalculate &data_calculate,const uint required=0); bool OnBookEvent(const string &symbol); void OnDeinit(void); //--- Set the list of used symbols in the symbol collection and create the collection of symbol timeseries bool SetUsedSymbols(const string &array_symbols[],const uint required_ticks=0,const uint required_books=0);
In the list of methods for working with the collection of tick series, add the new methods for simplifying the work with the tick series collection and write the methods for working with the DOM series collection:
//--- Return (1) the tick series collection, (2) the list of tick series from the tick series collection CTickSeriesCollection *GetTickSeriesCollection(void) { return &this.m_tick_series; } CArrayObj *GetListTickSeries(void) { return this.m_tick_series.GetList(); } //--- Create (1) a specified tick series and (2) all tick series bool TickSeriesCreate(const string symbol,const uint required=0) { return this.m_tick_series.CreateTickSeries(symbol,required); } bool TickSeriesCreateAll(const uint required=0) { return this.m_tick_series.CreateTickSeriesAll(required); } //--- Update (1) a tick series of a specified symbol, (2) all symbols and (3) all symbols except the current one void TickSeriesRefresh(const string symbol) { this.m_tick_series.Refresh(symbol); } void TickSeriesRefreshAll(void) { this.m_tick_series.Refresh(); } void TickSeriesRefreshAllExceptCurrent(void) { this.m_tick_series.RefreshExpectCurrent(); } //--- Return (1) the DOM series collection and (2) the list of DOM series from the DOM series collection CMBookSeriesCollection *GetMBookSeriesCollection(void) { return &this.m_book_series; } CArrayObj *GetListMBookSeries(void) { return this.m_book_series.GetList(); } //--- Update the DOM series of a specified symbol void MBookSeriesRefresh(const string symbol,const long time_msc) { this.m_book_series.Refresh(symbol,time_msc); } //--- Return the DOM series of a specified symbol CMBookSeries *GetMBookSeries(const string symbol) { return this.m_book_series.GetMBookseries(symbol); }
All these methods simply call the methods of the corresponding collections of the same name.
Implement the OnBookEvent() handler of the library outside the class body:
//+------------------------------------------------------------------+ //| BookEvent event handler | //+------------------------------------------------------------------+ bool CEngine::OnBookEvent(const string &symbol) { CSymbol *sym=this.m_symbols.GetSymbolObjByName(symbol); if(sym==NULL || !sym.BookdepthSubscription()) return false; return this.m_book_series.Refresh(sym.Name(),sym.Time()); } //+------------------------------------------------------------------+
Since the OnBookEvent() handler is always activated during the DOM change event on a single symbol only, we can define the symbol inside the handler itself.
The method receives the name of a symbol, at which the DOM change event occurred. Next, we receive the appropriate symbol object from the symbol collection class and return the result of the method for updating the appropriate series of DOM snapshots. Besides, we specify the time in milliseconds to be set for a newly created DOM snapshot in the Refresh() method.
Let's improve the method setting the list of used symbols in the symbol collection:
//+------------------------------------------------------------------+ //| Set the list of used symbols in the symbol collection | //| and create the collections of timeseries, tick series | //| and symbol DOM series | //+------------------------------------------------------------------+ bool CEngine::SetUsedSymbols(const string &array_symbols[],const uint required_ticks=0,const uint required_books=0) { bool res=this.m_symbols.SetUsedSymbols(array_symbols); CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL) return false; res&=this.m_time_series.CreateCollection(list); res&=this.m_tick_series.CreateCollection(list,required_ticks); res&=this.m_book_series.CreateCollection(list,required_books); return res; } //+------------------------------------------------------------------+
Now the method receives the number of days of tick series and the maximum number of DOM snapshots in their series.
Here we added the creation of DOM snapshot series collection for all symbols.
This concludes the improvement of the classes for working with DOM series.
In the future, I may get back to this topic for further improvements but now let's deal with other necessary library functionality.
MQL5.com Signals service is a copy-trading service allowing you to automatically copy provider's deals on your trading account.
Here, the MQL5 signal object is the trading account where trading operations are monitored to be publicly transmitted — a signal source.
A signal source has its own parameters that can be obtained using the SignalBaseGetInteger(), SignalBaseGetDouble() and SignalBaseGetString() functions. Besides, there are subscription parameters — signal copying for a specific account. These parameters can be obtained using the SignalInfoGetDouble(), SignalInfoGetInteger() and SignalInfoGetString() functions.
An MQL5 signal object is to describe one signal source available in the terminal for subscription. It is possible to subscribe to only one signal from one account but we still can have the full list of all signals (MQL5 signal objects) available for subscription with the ability to sort them by any property, compare and subscribe to a selected signal. This is a very convenient and useful functionality for those willing to benefit from the MQL5.com Signals service.
MQL5 signal object class
In \MQL5\Include\DoEasy\Defines.mqh, add the enumerations of integer, real and string properties of the MQL5 signal object:
//+------------------------------------------------------------------+ //| Data for working with MQL5.com signals | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| The list of possible signal events | //+------------------------------------------------------------------+ #define SIGNAL_MQL5_EVENTS_NEXT_CODE (MBOOK_ORD_EVENTS_NEXT_CODE+1) // The code of the next event after the last signal event code //+------------------------------------------------------------------+ //| MQL5 signal integer properties | //+------------------------------------------------------------------+ enum ENUM_SIGNAL_MQL5_PROP_INTEGER { SIGNAL_MQL5_PROP_TRADE_MODE = 0, // Account type SIGNAL_MQL5_PROP_DATE_PUBLISHED, // Signal publication date SIGNAL_MQL5_PROP_DATE_STARTED, // Start date of signal monitoring SIGNAL_MQL5_PROP_DATE_UPDATED, // Date of the latest update of the signal trading statistics SIGNAL_MQL5_PROP_ID, // Signal ID SIGNAL_MQL5_PROP_LEVERAGE, // Trading account leverage SIGNAL_MQL5_PROP_PIPS, // Trading result in pips SIGNAL_MQL5_PROP_RATING, // Position in the signal rating SIGNAL_MQL5_PROP_SUBSCRIBERS, // Number of subscribers SIGNAL_MQL5_PROP_TRADES, // Number of trades SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS, // Status of account subscription to a signal }; #define SIGNAL_MQL5_PROP_INTEGER_TOTAL (11) // Total number of integer properties #define SIGNAL_MQL5_PROP_INTEGER_SKIP (0) // Number of integer DOM properties not used in sorting //+------------------------------------------------------------------+ //| MQL5 signal real properties | //+------------------------------------------------------------------+ enum ENUM_SIGNAL_MQL5_PROP_DOUBLE { SIGNAL_MQL5_PROP_BALANCE = SIGNAL_MQL5_PROP_INTEGER_TOTAL, // Account balance SIGNAL_MQL5_PROP_EQUITY, // Account equity SIGNAL_MQL5_PROP_GAIN, // Account growth in % SIGNAL_MQL5_PROP_MAX_DRAWDOWN, // Maximum drawdown SIGNAL_MQL5_PROP_PRICE, // Signal subscription price SIGNAL_MQL5_PROP_ROI, // Signal's ROI (Return on Investment) in % }; #define SIGNAL_MQL5_PROP_DOUBLE_TOTAL (6) // Total number of real properties #define SIGNAL_MQL5_PROP_DOUBLE_SKIP (0) // Number of real properties not used in sorting //+------------------------------------------------------------------+ //| MQL5 signal string properties | //+------------------------------------------------------------------+ enum ENUM_SIGNAL_MQL5_PROP_STRING { SIGNAL_MQL5_PROP_AUTHOR_LOGIN = (SIGNAL_MQL5_PROP_INTEGER_TOTAL+SIGNAL_MQL5_PROP_DOUBLE_TOTAL), // Signal author's login SIGNAL_MQL5_PROP_BROKER, // Broker (company) name SIGNAL_MQL5_PROP_BROKER_SERVER, // Broker server SIGNAL_MQL5_PROP_NAME, // Signal name SIGNAL_MQL5_PROP_CURRENCY, // Signal account currency }; #define SIGNAL_MQL5_PROP_STRING_TOTAL (5) // Total number of string properties //+------------------------------------------------------------------+
I am not going to introduce the list of possible mql5 signal events yet. Instead, I will only introduce a constant specifying the code of the next event after MQL5 signal event codes. I will most probably change this further on.
To be able to search and sort by the properties of MQL5 signals, define the enumeration of all possible sorting criteria:
//+------------------------------------------------------------------+ //| Possible sorting criteria of MQL5 signals | //+------------------------------------------------------------------+ #define FIRST_SIGNAL_MQL5_DBL_PROP (SIGNAL_MQL5_PROP_INTEGER_TOTAL-SIGNAL_MQL5_PROP_INTEGER_SKIP) #define FIRST_SIGNAL_MQL5_STR_PROP (SIGNAL_MQL5_PROP_INTEGER_TOTAL-SIGNAL_MQL5_PROP_INTEGER_SKIP+SIGNAL_MQL5_PROP_DOUBLE_TOTAL-SIGNAL_MQL5_PROP_DOUBLE_SKIP) enum ENUM_SORT_SIGNAL_MQL5_MODE { //--- Sort by integer properties SORT_BY_SIGNAL_MQL5_TRADE_MODE = 0, // Sort by signal type SORT_BY_SIGNAL_MQL5_DATE_PUBLISHED, // Sort by signal publication date SORT_BY_SIGNAL_MQL5_DATE_STARTED, // Sort by signal monitoring start date SORT_BY_SIGNAL_MQL5_DATE_UPDATED, // Sort by date of the latest update of the signal trading statistics SORT_BY_SIGNAL_MQL5_ID, // Sort by signal ID SORT_BY_SIGNAL_MQL5_LEVERAGE, // Sort by trading account leverage SORT_BY_SIGNAL_MQL5_PIPS, // Sort by trading result in pips SORT_BY_SIGNAL_MQL5_RATING, // Sort by a position in the signal rating SORT_BY_SIGNAL_MQL5_SUBSCRIBERS, // Sort by the number of subscribers SORT_BY_SIGNAL_MQL5_TRADES, // Sort by the number of trades SORT_BY_SIGNAL_MQL5_SUBSCRIPTION_STATUS, // Sort by the status of account subscription to a signal //--- Sort by real properties SORT_BY_SIGNAL_MQL5_BALANCE = FIRST_SIGNAL_MQL5_DBL_PROP, // Sort by account balance SORT_BY_SIGNAL_MQL5_EQUITY, // Sort by account equity SORT_BY_SIGNAL_MQL5_GAIN, // Sort by account growth in % SORT_BY_SIGNAL_MQL5_MAX_DRAWDOWN, // Sort by maximum drawdown SORT_BY_SIGNAL_MQL5_PRICE, // Sort by signal subscription price SORT_BY_SIGNAL_MQL5_ROI, // Sort by ROI //--- Sort by string properties SORT_BY_SIGNAL_MQL5_AUTHOR_LOGIN = FIRST_SIGNAL_MQL5_STR_PROP, // Sort by signal author login SORT_BY_SIGNAL_MQL5_BROKER, // Sort by Broker (company) name SORT_BY_SIGNAL_MQL5_BROKER_SERVER, // Sort by broker server SORT_BY_SIGNAL_MQL5_NAME, // Sort by signal name SORT_BY_SIGNAL_MQL5_CURRENCY, // Sort by signal account currency }; //+------------------------------------------------------------------+
This is a set of the most necessary parameters for creating a new class of the new library object.
In \MQL5\Include\DoEasy\Objects\, create a new folder MQLSignalBase\ featuring a new file MQLSignal.mqh of the CMQLSignal class.
The class of the basic object of all CBaseObj library objects should be used as the base class:
//+------------------------------------------------------------------+ //| MQLSignal.mqh | //| Copyright 2021, MetaQuotes Software Corp. | //| https://MQL5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://MQL5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Abstract MQL5 signal class | //+------------------------------------------------------------------+ class CMQLSignal : public CBaseObj { }
Let's consider the class composition and implementation of its methods:
//+------------------------------------------------------------------+ //| MQLSignal.mqh | //| Copyright 2021, MetaQuotes Software Corp. | //| https://MQL5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://MQL5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Abstract MQL5 signal class | //+------------------------------------------------------------------+ class CMQLSignal : public CBaseObj { private: long m_long_prop[SIGNAL_MQL5_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[SIGNAL_MQL5_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[SIGNAL_MQL5_PROP_STRING_TOTAL]; // String properties //--- Return the index of the array the (1) double and (2) string properties are actually located at int IndexProp(ENUM_SIGNAL_MQL5_PROP_DOUBLE property) const { return(int)property-SIGNAL_MQL5_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_SIGNAL_MQL5_PROP_STRING property) const { return(int)property-SIGNAL_MQL5_PROP_INTEGER_TOTAL-SIGNAL_MQL5_PROP_DOUBLE_TOTAL; } public: //--- Set object's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_SIGNAL_MQL5_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_SIGNAL_MQL5_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_SIGNAL_MQL5_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_SIGNAL_MQL5_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_SIGNAL_MQL5_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_SIGNAL_MQL5_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return itself CMQLSignal *GetObject(void) { return &this;} //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_SIGNAL_MQL5_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_SIGNAL_MQL5_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_SIGNAL_MQL5_PROP_STRING property) { return true; } //--- Get description of (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_INTEGER property); string GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_DOUBLE property); string GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_STRING property); //--- Display the description of object properties in the journal (full_prop=true - all properties, false - supported ones only) void Print(const bool full_prop=false); //--- Display a short description of the object in the journal virtual void PrintShort(void); //--- Return the object short name virtual string Header(const bool shrt=false); //--- Compare CMQLSignal objects by a specified property (to sort the list by an MQL5 signal object) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CMQLSignal objects by all properties (to search for equal MQL5 signal objects) bool IsEqual(CMQLSignal* compared_obj) const; //--- Constructors CMQLSignal(){;} CMQLSignal(const long signal_id); public: //+------------------------------------------------------------------+ //| Methods of simplified access to MQL5 signal object | //+------------------------------------------------------------------+ //--- Returns the dates of (1) publication, (2) monitoring start, (3) trading statistics last update, //--- (4) ID, (5) trading account leverage, (6) trading result in pips, (7) position in the signal rating, //--- (8) number of subscribers, (9) number of trades, (10) account type, (11) flag of the current account subscription to a signal datetime DatePublished(void) const { return (datetime)this.GetProperty(SIGNAL_MQL5_PROP_DATE_PUBLISHED); } datetime DateStarted(void) const { return (datetime)this.GetProperty(SIGNAL_MQL5_PROP_DATE_STARTED); } datetime DateUpdated(void) const { return (datetime)this.GetProperty(SIGNAL_MQL5_PROP_DATE_UPDATED); } long ID(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_ID); } long Leverage(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_LEVERAGE); } long Pips(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_PIPS); } long Rating(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_RATING); } long Subscribers(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_SUBSCRIBERS); } long Trades(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_TRADES); } long TradeMode(void) const { return (long)this.GetProperty(SIGNAL_MQL5_PROP_TRADE_MODE); } bool SubscriptionStatus(void)const { return (bool)this.GetProperty(SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS); } //--- Return (1) the account balance, (2) account funds, (3) account growth in %, //--- (4) maximum drawdown, (5) subscription price and (6) signal ROI value (Return on Investment) in % double Balance(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_BALANCE); } double Equity(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_EQUITY); } double Gain(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_GAIN); } double MaxDrawdown(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_MAX_DRAWDOWN); } double Price(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_PRICE); } double ROI(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_ROI); } //--- Return (1) signal author's login, (2) broker (company) name, //--- (3) broker server, (4) signal name and (5) signal account currency string AuthorLogin(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_AUTHOR_LOGIN); } string Broker(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_BROKER); } string BrokerServer(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_BROKER_SERVER); } string Name(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_NAME); } string Currency(void) const { return this.GetProperty(SIGNAL_MQL5_PROP_CURRENCY); } //--- Set the dates of (1) publication, (2) monitoring start, (3) trading statistics last update, //--- (4) ID, (5) trading account leverage, (6) trading result in pips, (7) position in the signal rating, //--- (8) number of subscribers, (9) number of trades, (10) account type, (11) flag of the current account subscription to a signal void SetDatePublished(const datetime date) { this.SetProperty(SIGNAL_MQL5_PROP_DATE_PUBLISHED,date); } void SetDateStarted(const datetime date) { this.SetProperty(SIGNAL_MQL5_PROP_DATE_STARTED,date); } void SetDateUpdated(const datetime date) { this.SetProperty(SIGNAL_MQL5_PROP_DATE_UPDATED,date); } void SetID(const long id) { this.SetProperty(SIGNAL_MQL5_PROP_ID,id); } void SetLeverage(const long value) { this.SetProperty(SIGNAL_MQL5_PROP_LEVERAGE,value); } void SetPips(const long value) { this.SetProperty(SIGNAL_MQL5_PROP_PIPS,value); } void SetRating(const long value) { this.SetProperty(SIGNAL_MQL5_PROP_RATING,value); } void SetSubscribers(const long value) { this.SetProperty(SIGNAL_MQL5_PROP_SUBSCRIBERS,value); } void SetTrades(const long value) { this.SetProperty(SIGNAL_MQL5_PROP_TRADES,value); } void SetTradeMode(const long mode) { this.SetProperty(SIGNAL_MQL5_PROP_TRADE_MODE,mode); } void SetSubscriptionStatus(const bool flag) { this.SetProperty(SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS,flag); } //--- Set (1) the account balance, (2) account funds, (3) account growth in %, //--- (4) maximum drawdown, (5) subscription price and (6) signal ROI value (Return on Investment) in % void SetBalance(const double value) { this.SetProperty(SIGNAL_MQL5_PROP_BALANCE,value); } void SetEquity(const double value) { this.SetProperty(SIGNAL_MQL5_PROP_EQUITY,value); } void SetGain(const double value) { this.SetProperty(SIGNAL_MQL5_PROP_GAIN,value); } void SetMaxDrawdown(const double value) { this.SetProperty(SIGNAL_MQL5_PROP_MAX_DRAWDOWN,value); } void SetPrice(const double value) { this.SetProperty(SIGNAL_MQL5_PROP_PRICE,value); } void SetROI(const double value) { this.SetProperty(SIGNAL_MQL5_PROP_ROI,value); } //--- Set (1) signal author's login, (2) broker (company) name, //--- (3) broker server, (4) signal name and (5) signal account currency void SetAuthorLogin(const string value) { this.SetProperty(SIGNAL_MQL5_PROP_AUTHOR_LOGIN,value); } void SetBroker(const string value) { this.SetProperty(SIGNAL_MQL5_PROP_BROKER,value); } void SetBrokerServer(const string value) { this.SetProperty(SIGNAL_MQL5_PROP_BROKER_SERVER,value); } void SetName(const string value) { this.SetProperty(SIGNAL_MQL5_PROP_NAME,value); } void SetCurrency(const string value) { this.SetProperty(SIGNAL_MQL5_PROP_CURRENCY,value); } //--- Return the account type name string TradeModeDescription(void); }; //+------------------------------------------------------------------+
The arrays for storing integer, real and string object properties and methods returning the real index of the object real and string properties are declared in the private section.
In the public section of the class, we can see the standard methods of setting and receiving object properties, searching and sorting, displaying object descriptions and class constructors.
The public section also features the methods for a simplified access to object properties — the methods simply have self-explanatory names so that library users are able to access any property without the need to memorize the names of all object enumerations.
The composition of the object classes was considered in details in the first part of the library description. Let's have a look at the implementation of the class methods.
The parametric class constructor receives the signal ID. All object properties are then filled with values obtained from the appropriate functions (considering that this signal is selected):
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CMQLSignal::CMQLSignal(const long signal_id) { this.m_long_prop[SIGNAL_MQL5_PROP_ID] = signal_id; this.m_long_prop[SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS] = false; this.m_long_prop[SIGNAL_MQL5_PROP_TRADE_MODE] = ::SignalBaseGetInteger(SIGNAL_BASE_TRADE_MODE); this.m_long_prop[SIGNAL_MQL5_PROP_DATE_PUBLISHED] = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_PUBLISHED); this.m_long_prop[SIGNAL_MQL5_PROP_DATE_STARTED] = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_STARTED); this.m_long_prop[SIGNAL_MQL5_PROP_DATE_UPDATED] = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_UPDATED); this.m_long_prop[SIGNAL_MQL5_PROP_LEVERAGE] = ::SignalBaseGetInteger(SIGNAL_BASE_LEVERAGE); this.m_long_prop[SIGNAL_MQL5_PROP_PIPS] = ::SignalBaseGetInteger(SIGNAL_BASE_PIPS); this.m_long_prop[SIGNAL_MQL5_PROP_RATING] = ::SignalBaseGetInteger(SIGNAL_BASE_RATING); this.m_long_prop[SIGNAL_MQL5_PROP_SUBSCRIBERS] = ::SignalBaseGetInteger(SIGNAL_BASE_SUBSCRIBERS); this.m_long_prop[SIGNAL_MQL5_PROP_TRADES] = ::SignalBaseGetInteger(SIGNAL_BASE_TRADES); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_BALANCE)] = ::SignalBaseGetDouble(SIGNAL_BASE_BALANCE); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_EQUITY)] = ::SignalBaseGetDouble(SIGNAL_BASE_EQUITY); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_GAIN)] = ::SignalBaseGetDouble(SIGNAL_BASE_GAIN); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_MAX_DRAWDOWN)] = ::SignalBaseGetDouble(SIGNAL_BASE_MAX_DRAWDOWN); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_PRICE)] = ::SignalBaseGetDouble(SIGNAL_BASE_PRICE); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_ROI)] = ::SignalBaseGetDouble(SIGNAL_BASE_ROI); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_AUTHOR_LOGIN)] = ::SignalBaseGetString(SIGNAL_BASE_AUTHOR_LOGIN); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_BROKER)] = ::SignalBaseGetString(SIGNAL_BASE_BROKER); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_BROKER_SERVER)]= ::SignalBaseGetString(SIGNAL_BASE_BROKER_SERVER); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_NAME)] = ::SignalBaseGetString(SIGNAL_BASE_NAME); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_CURRENCY)] = ::SignalBaseGetString(SIGNAL_BASE_CURRENCY); } //+------------------------------------------------------------------+
The signal subscription flag is set to false — subscription to a selected signal is performed from another class.
The method for comparing MQL5 signal objects by a specified property:
//+----------------------------------------------------------------------+ //| Compare CMQLSignal objects with each other by the specified property | //+----------------------------------------------------------------------+ int CMQLSignal::Compare(const CObject *node,const int mode=0) const { const CMQLSignal *obj_compared=node; //--- compare integer properties of two objects if(mode<SIGNAL_MQL5_PROP_INTEGER_TOTAL) { long value_compared=obj_compared.GetProperty((ENUM_SIGNAL_MQL5_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_SIGNAL_MQL5_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare real properties of two objects else if(mode<SIGNAL_MQL5_PROP_DOUBLE_TOTAL+SIGNAL_MQL5_PROP_INTEGER_TOTAL) { double value_compared=obj_compared.GetProperty((ENUM_SIGNAL_MQL5_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_SIGNAL_MQL5_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare string properties of two objects else if(mode<SIGNAL_MQL5_PROP_DOUBLE_TOTAL+SIGNAL_MQL5_PROP_INTEGER_TOTAL+SIGNAL_MQL5_PROP_STRING_TOTAL) { string value_compared=obj_compared.GetProperty((ENUM_SIGNAL_MQL5_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_SIGNAL_MQL5_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+
In brief, the method receives an object for comparison as well as a property, by which two objects are to be compared. Integer, real or string properties of two objects are compared depending on the passed property. The comparison result of -1, 1 or 0 (less than, greater than, equal) is returned.
The method for comparing MQL5 signal objects by all properties:
//+------------------------------------------------------------------+ //| Compare CMQLSignal objects by all properties | //+------------------------------------------------------------------+ bool CMQLSignal::IsEqual(CMQLSignal *compared_obj) const { int beg=0, end=SIGNAL_MQL5_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_SIGNAL_MQL5_PROP_INTEGER prop=(ENUM_SIGNAL_MQL5_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=SIGNAL_MQL5_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_SIGNAL_MQL5_PROP_DOUBLE prop=(ENUM_SIGNAL_MQL5_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=SIGNAL_MQL5_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_SIGNAL_MQL5_PROP_STRING prop=(ENUM_SIGNAL_MQL5_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
In three loops by three object properties, compare each subsequent property of two objects and, if the properties of two objects are not equal, return false. After comparing all properties of two objects in three arrays, return true if non-equal properties are not found.
The methods returning the description of the specified integer, real and string object property:
//+------------------------------------------------------------------+ //| Return description of object's integer property | //+------------------------------------------------------------------+ string CMQLSignal::GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_INTEGER property) { return ( property==SIGNAL_MQL5_PROP_TRADE_MODE ? CMessage::Text(MSG_SIGNAL_MQL5_TRADE_MODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.TradeModeDescription() ) : property==SIGNAL_MQL5_PROP_DATE_PUBLISHED ? CMessage::Text(MSG_SIGNAL_MQL5_DATE_PUBLISHED)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.DatePublished()) ) : property==SIGNAL_MQL5_PROP_DATE_STARTED ? CMessage::Text(MSG_SIGNAL_MQL5_DATE_STARTED)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.DateStarted()) ) : property==SIGNAL_MQL5_PROP_DATE_UPDATED ? CMessage::Text(MSG_SIGNAL_MQL5_DATE_UPDATED)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.DateUpdated()) ) : property==SIGNAL_MQL5_PROP_ID ? CMessage::Text(MSG_SIGNAL_MQL5_ID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==SIGNAL_MQL5_PROP_LEVERAGE ? CMessage::Text(MSG_SIGNAL_MQL5_LEVERAGE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==SIGNAL_MQL5_PROP_PIPS ? CMessage::Text(MSG_SIGNAL_MQL5_PIPS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==SIGNAL_MQL5_PROP_RATING ? CMessage::Text(MSG_SIGNAL_MQL5_RATING)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==SIGNAL_MQL5_PROP_SUBSCRIBERS ? CMessage::Text(MSG_SIGNAL_MQL5_SUBSCRIBERS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==SIGNAL_MQL5_PROP_TRADES ? CMessage::Text(MSG_SIGNAL_MQL5_TRADES)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS ? CMessage::Text(MSG_SIGNAL_MQL5_SUBSCRIPTION_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : "" ); } //+------------------------------------------------------------------+ //| Return description of object's real property | //+------------------------------------------------------------------+ string CMQLSignal::GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_DOUBLE property) { return ( property==SIGNAL_MQL5_PROP_BALANCE ? CMessage::Text(MSG_ACC_PROP_BALANCE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==SIGNAL_MQL5_PROP_EQUITY ? CMessage::Text(MSG_SIGNAL_MQL5_EQUITY)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==SIGNAL_MQL5_PROP_GAIN ? CMessage::Text(MSG_SIGNAL_MQL5_GAIN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==SIGNAL_MQL5_PROP_MAX_DRAWDOWN ? CMessage::Text(MSG_SIGNAL_MQL5_MAX_DRAWDOWN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==SIGNAL_MQL5_PROP_PRICE ? CMessage::Text(MSG_SIGNAL_MQL5_PRICE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==SIGNAL_MQL5_PROP_ROI ? CMessage::Text(MSG_SIGNAL_MQL5_ROI)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : "" ); } //+------------------------------------------------------------------+ //| Return description of object's string property | //+------------------------------------------------------------------+ string CMQLSignal::GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_STRING property) { return ( property==SIGNAL_MQL5_PROP_AUTHOR_LOGIN ? CMessage::Text(MSG_SIGNAL_MQL5_AUTHOR_LOGIN)+": \""+this.GetProperty(property)+"\"" : property==SIGNAL_MQL5_PROP_BROKER ? CMessage::Text(MSG_SIGNAL_MQL5_BROKER)+": \""+this.GetProperty(property)+"\"" : property==SIGNAL_MQL5_PROP_BROKER_SERVER ? CMessage::Text(MSG_SIGNAL_MQL5_BROKER_SERVER)+": \""+this.GetProperty(property)+"\"" : property==SIGNAL_MQL5_PROP_NAME ? CMessage::Text(MSG_SIGNAL_MQL5_NAME)+": \""+this.GetProperty(property)+"\"" : property==SIGNAL_MQL5_PROP_CURRENCY ? CMessage::Text(MSG_SIGNAL_MQL5_CURRENCY)+": \""+this.GetProperty(property)+"\"" : "" ); } //+------------------------------------------------------------------+
Depending on the property passed to the method, the string with its description and value is created and returned.
The method displaying all MQL5 signal object properties in the journal:
//+------------------------------------------------------------------+ //| Display object properties in the journal | //+------------------------------------------------------------------+ void CMQLSignal::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); int beg=0, end=SIGNAL_MQL5_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_SIGNAL_MQL5_PROP_INTEGER prop=(ENUM_SIGNAL_MQL5_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=SIGNAL_MQL5_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_SIGNAL_MQL5_PROP_DOUBLE prop=(ENUM_SIGNAL_MQL5_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=SIGNAL_MQL5_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_SIGNAL_MQL5_PROP_STRING prop=(ENUM_SIGNAL_MQL5_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n"); } //+------------------------------------------------------------------+
A description of each subsequent object property is displayed in three loops by integer, real and string properties using the appropriate GetPropertyDescription() method.
The method returning the short name of the MQL5 signal object:
//+------------------------------------------------------------------+ //| Return the object short name | //+------------------------------------------------------------------+ string CMQLSignal::Header(const bool shrt=false) { return(shrt ? CMessage::Text(MSG_SIGNAL_MQL5_TEXT_SIGNAL) : CMessage::Text(MSG_SIGNAL_MQL5_TEXT_SIGNAL_MQL5)); } //+------------------------------------------------------------------+
The method receives the flag specifying either short or long description. The long description is shown by default:
The signal of the MQL5.com Signal service
If the flag is true, a short description is displayed:
Signal
The method displaying the short object description in the journal:
//+------------------------------------------------------------------+ //| Display a short description of the object in the journal | //+------------------------------------------------------------------+ void CMQLSignal::PrintShort(void) { ::Print ( this.Header(true), " \"",this.Name(),"\". ", CMessage::Text(MSG_SIGNAL_MQL5_AUTHOR_LOGIN),": ",this.AuthorLogin(), ", ID ",this.ID(), ", ",CMessage::Text(MSG_SIGNAL_MQL5_TEXT_GAIN),": ",::DoubleToString(this.Gain(),2), ", ",CMessage::Text(MSG_SIGNAL_MQL5_TEXT_DRAWDOWN),": ",::DoubleToString(this.MaxDrawdown(),2), ", ",CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE),": ",::DoubleToString(this.Price(),2) ); } //+------------------------------------------------------------------+
The short Signal header is created first. Some parameters displayed in the terminal journal are added to it.
For example, it may look like this:
Signal "DemoForArticle". Author login: login, ID XXXXXX, Growth: XX.XX, Drawdown: XX.XX, Price: XX.XX
The method returning the account type name:
//+------------------------------------------------------------------+ //| Return the account type name | //+------------------------------------------------------------------+ string CMQLSignal::TradeModeDescription(void) { return ( this.TradeMode()==0 ? CMessage::Text(MSG_ACC_TRADE_MODE_REAL) : this.TradeMode()==1 ? CMessage::Text(MSG_ACC_TRADE_MODE_DEMO) : this.TradeMode()==2 ? CMessage::Text(MSG_ACC_TRADE_MODE_CONTEST) : CMessage::Text(MSG_ACC_TRADE_MODE_UNKNOWN) ); } //+------------------------------------------------------------------+
Depending on a signal source account, a string with an account type (real, demo or contest) is returned.
This completes the creation of the MQL5 signal object.
Test
To perform the test, let's use the EA from the previous article and save it to \MQL5\Experts\TestDoEasy\Part65\ as TestDoEasyPart65.mq5.
The DOM snapshot series collection class is now available from the main library object, while the MQL5 signal object is not connected to CEngine yet. Therefore, let's replace the inclusion string of the DOM snapshot series object class
#include <DoEasy\Objects\Book\MBookSeries.mqh>
with the one for the inclusion of the MQL5 signal object:
//+------------------------------------------------------------------+ //| TestDoEasyPart65.mq5 | //| Copyright 2021, MetaQuotes Software Corp. | //| https://MQL5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://MQL5.com/en/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> #include <DoEasy\Objects\MQLSignalBase\MQLSignal.mqh> //--- enums
Remove the declaration of the DOM snapshot series object from the list of EA global variables:
//--- global variables CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool pressed_pending_buy; bool pressed_pending_buy_limit; bool pressed_pending_buy_stop; bool pressed_pending_buy_stoplimit; bool pressed_pending_close_buy; bool pressed_pending_close_buy2; bool pressed_pending_close_buy_by_sell; bool pressed_pending_sell; bool pressed_pending_sell_limit; bool pressed_pending_sell_stop; bool pressed_pending_sell_stoplimit; bool pressed_pending_close_sell; bool pressed_pending_close_sell2; bool pressed_pending_close_sell_by_buy; bool pressed_pending_delete_all; bool pressed_pending_close_all; bool pressed_pending_sl; bool pressed_pending_tp; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string array_used_symbols[]; string array_used_periods[]; bool testing; uchar group1; uchar group2; double g_point; int g_digits; //--- CMBookSeries book_series; //+------------------------------------------------------------------+
MQL5 help features an example of receiving the list of profitable free signals with non-zero number of subscribers:
void OnStart() { //--- request the total number of signals in the signal database int total=SignalBaseTotal(); //--- loop through all signals for(int i=0;i<total;i++) { //--- select a signal for further operation if(SignalBaseSelect(i)) { //--- receive signal properties long id =SignalBaseGetInteger(SIGNAL_BASE_ID); // signal ID long pips =SignalBaseGetInteger(SIGNAL_BASE_PIPS); // trading result in pips long subscr=SignalBaseGetInteger(SIGNAL_BASE_SUBSCRIBERS); // number of subscribers string name =SignalBaseGetString(SIGNAL_BASE_NAME); // signal name double price =SignalBaseGetDouble(SIGNAL_BASE_PRICE); // signal subscription price string curr =SignalBaseGetString(SIGNAL_BASE_CURRENCY); // signal currency //--- display all profitable free signals with a non-zero number of subscribers if(price==0.0 && pips>0 && subscr>0) PrintFormat("id=%d, name=\"%s\", currency=%s, pips=%d, subscribers=%d",id,name,curr,pips,subscr); } else PrintFormat("Signal selection error. Error code=%d",GetLastError()); } }
Let's do the same with the help of the new MQL5 signal object. Write the following code block in the EA's OnInit() handler:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- Set EA global variables prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; testing=engine.IsTester(); for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq); bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq); g_point=SymbolInfoDouble(NULL,SYMBOL_POINT); g_digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS); //--- Initialize random group numbers group1=0; group2=0; srand(GetTickCount()); //--- Initialize DoEasy library OnInitDoEasy(); //--- Check and remove remaining EA graphical objects if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Create the button panel if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- Set trailing activation button status ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Reset states of the buttons for working using pending requests for(int i=0;i<14;i++) { ButtonState(butt_data[i].name+"_PRICE",false); ButtonState(butt_data[i].name+"_TIME",false); } //--- Check playing a standard sound by macro substitution and a custom sound by description engine.PlaySoundByDescription(SND_OK); //--- Wait for 600 milliseconds engine.Pause(600); engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2")); CArrayObj *list=new CArrayObj(); if(list!=NULL) { //--- request the total number of signals in the signal database int total=SignalBaseTotal(); //--- loop through all signals for(int i=0;i<total;i++) { //--- select a signal for further operation if(!SignalBaseSelect(i)) continue; long id=SignalBaseGetInteger(SIGNAL_BASE_ID); CMQLSignal *signal=new CMQLSignal(id); if(signal==NULL) continue; if(!list.Add(signal)) { delete signal; continue; } } //--- display all profitable free signals with a non-zero number of subscribers Print(""); static bool done=false; for(int i=0;i<list.Total();i++) { CMQLSignal *signal=list.At(i); if(signal==NULL) continue; if(signal.Price()>0 || signal.Subscribers()==0) continue; //--- The very first suitable signal is fully displayed in the journal if(!done) { signal.Print(); done=true; } //--- Short descriptions are displayed for the rest else signal.PrintShort(); } delete list; } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
At first glance, the resulting amount of code is larger than the code from the help. But this is a temporary phenomenon... As soon as the signal collection class is ready, it will be much more concise. In the meantime, I will use the code to check whether the MQL5 signal object works correctly.
Since we now have access to the DOM snapshot series collection class from the main library object, the OnBookEvent() handler has undergone some changes. Now it looks as follows:
//+------------------------------------------------------------------+ //| OnBookEvent function | //+------------------------------------------------------------------+ void OnBookEvent(const string& symbol) { static bool first=true; //--- Leave if failed to update the symbol snapshot series if(!engine.OnBookEvent(symbol)) return; //--- Work by the current symbol if(symbol==Symbol()) { //--- Get the DOM snapshot series of the current symbol CMBookSeries *book_series=engine.GetMBookSeries(symbol); if(book_series==NULL) return; //--- Get the last DOM snapshot object from the DOM snapshot series object CMBookSnapshot *book=book_series.GetLastMBook(); if(book==NULL) return; //--- Get the very first and last DOM order objects from the DOM snapshot object CMarketBookOrd *ord_0=book.GetMBookByListIndex(0); CMarketBookOrd *ord_N=book.GetMBookByListIndex(book.DataTotal()-1); if(ord_0==NULL || ord_N==NULL) return; //--- Display the time of the current DOM snapshot in the chart comment, //--- the maximum number of displayed orders in DOM for a symbol, //--- the obtained number of orders in the current DOM snapshot, //--- the total number of DOM snapshots set in the series list and //--- the highest and lowest orders of the current DOM snapshot Comment ( DFUN,book.Symbol(),": ",TimeMSCtoString(book.Time()), //", symbol book size=",sym.TicksBookdepth(), ", last book data total: ",book.DataTotal(), ", series books total: ",book_series.DataTotal(), "\nMax: ",ord_N.Header(),"\nMin: ",ord_0.Header() ); //--- Display the first DOM snapshot in the journal if(first) { //--- series description book_series.Print(); //--- snapshot description book.Print(); first=false; } } } //+------------------------------------------------------------------+
Let's make some corrections in the OnInitDoEasy() function as well. Now, creation of the tick series of all used symbols is implemented as a direct access to the appropriate method of the library main object. Besides, I have added the check of created DOM series:
//--- Check created timeseries - display descriptions of all created timeseries in the journal //--- (true - only created ones, false - created and declared ones) engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions //--- Create tick series of all used symbols engine.TickSeriesCreateAll(); //--- Check created tick series - display descriptions of all created tick series in the journal engine.GetTickSeriesCollection().Print(); //--- Check created DOM series - display descriptions of all created DOM series in the journal engine.GetMBookSeriesCollection().Print();
Compile the EA and launch it having preliminary set in the settings the usage of two symbols and the current chart period:
Data on the created DOM snapshot collection, full data on the very first suitable signal and short data on all profitable free signals are to be displayed in the journal. The end of the list features the data on the snapshot series and the first obtained DOM snapshot of the current chart:
Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10428.13 USD, 1:100, Hedge, MetaTrader 5 demo --- Initializing "DoEasy" library --- Working with predefined symbol list. The number of used symbols: 2 "AUDUSD" "EURUSD" Working with the current timeframe only: H1 AUDUSD symbol timeseries: - Timeseries "AUDUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5385 EURUSD symbol timeseries: - Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6310 Tick series "AUDUSD": Requested number of days: 1, Historical data created: 294221 Tick series "EURUSD": Requested number of days: 1, Historical data created: 216048 DOM snapshot series collection: - "AUDUSD" DOM snapshot series: Requested number of days: 1, Actual history depth: 0 "EURUSD" DOM snapshot series: Requested number of days: 1, Actual history depth: 0 Subscribed to Depth of Market AUDUSD Subscribed to Depth of Market EURUSD Library initialization time: 00:00:25.015 ============= Beginning of parameter list (Signal from the MQL5.com Signal service) ============= Account type: Demo Publication date: 2018.06.04 16:45 Monitoring start date: 2018.06.04 16:45 Date of the latest update of the trading statistics: 2021.02.20 02:01 ID: 435626 Trading account leverage: 200 Trading result in pips: -72124 Position in the Rating of Signals: 12 Number of subscribers: 132 Number of trades: 7866 Status of account subscription to a signal: No ------ Account balance: 43144.27 Account equity: 43150.55 Account growth in %: 331.44 Maximum drawdown: 43.93 Signal subscription price: 0.00 Signal ROI (Return on Investment) in %: 331.51 ------ Author login: "robots4forex" Broker (company) name: "MetaQuotes Software Corp." Broker server: "MetaQuotes-Demo" Name: "Prospector Scalper EA" Account currency: "GBP" ============= End of parameter list (Signal from the MQL5.com Signal service) ============= Signal "ADS MT5". Author login: vluxus, ID 478235, Growth: 251.84, Drawdown: 40.26, Price: 0.00 Signal "Sparrow USD ForexClub c 01012019". Author login: Tradotrade, ID 519975, Growth: 12.45, Drawdown: 14.98, Price: 0.00 Signal "RAZRED v03". Author login: joaoluiz_sa, ID 545382, Growth: 96.17, Drawdown: 28.18, Price: 0.00 Signal "NRDemo". Author login: v3sare, ID 655353, Growth: 2.94, Drawdown: 25.37, Price: 0.00 Signal "Amega 1000085182". Author login: AmegaTrust, ID 722001, Growth: 1.92, Drawdown: 5.13, Price: 0.00 Signal "Wns My strategy". Author login: WaldeliN, ID 727851, Growth: 103.69, Drawdown: 47.01, Price: 0.00 Signal "Bk EA". Author login: worknet, ID 749557, Growth: 506.88, Drawdown: 59.78, Price: 0.00 Signal "ROBIN 24". Author login: juanca034, ID 752873, Growth: 926.29, Drawdown: 30.25, Price: 0.00 Signal "Deny Forex". Author login: deny.mendonca, ID 759729, Growth: 149.06, Drawdown: 39.24, Price: 0.00 Signal "T Strategy". Author login: tonarino210, ID 760343, Growth: 28.87, Drawdown: 20.37, Price: 0.00 Signal "IC MT5 Demo". Author login: InvestForce, ID 760539, Growth: 67.01, Drawdown: 35.99, Price: 0.00 Signal "Gridingale". Author login: Myxx, ID 766073, Growth: 21.10, Drawdown: 15.55, Price: 0.00 Signal "Marcos Monteiro". Author login: slovenwill, ID 783988, Growth: 85.08, Drawdown: 17.59, Price: 0.00 Signal "Multi currency trend". Author login: mj2019, ID 785447, Growth: 54.42, Drawdown: 18.52, Price: 0.00 Signal "W7 901074879 Campeonato MT5". Author login: dramos236, ID 787269, Growth: 91.99, Drawdown: 21.20, Price: 0.00 Signal "Ramon Fx". Author login: viniciusramon18, ID 788732, Growth: 54.31, Drawdown: 9.46, Price: 0.00 Signal "Douglas demo w7". Author login: douglas.o.carne, ID 792392, Growth: 219.94, Drawdown: 43.61, Price: 0.00 "Suelen" signal. Author login: suelenacca, ID 794655, Growth: 67.40, Drawdown: 20.97, Price: 0.00 Signal "Conquers". Author login: borgesti, ID 795133, Growth: 37.23, Drawdown: 11.09, Price: 0.00 Signal "Conta demo torneio". Author login: Tiagoximenes, ID 798798, Growth: 42.36, Drawdown: 17.94, Price: 0.00 Signal "Conta demo de mil". Author login: Tiagoximenes, ID 798802, Growth: 132.02, Drawdown: 27.87, Price: 0.00 Signal "The art of Forex". Author login: Myxx, ID 801685, Growth: 170.29, Drawdown: 40.95, Price: 0.00 Signal "BB29 ICM". Author login: desmondpylow, ID 806971, Growth: 2.28, Drawdown: 41.60, Price: 0.00 Signal "Prometheus". Author login: g0079, ID 808538, Growth: 91.44, Drawdown: 22.98, Price: 0.00 Signal "Prueba robot 2 0 automatico". Author login: richwolfcompany, ID 809986, Growth: 76.76, Drawdown: 44.45, Price: 0.00 Signal "Deep Takeover Hedge StressTest 5M Candle". Author login: johnnypasado, ID 811819, Growth: 10.08, Drawdown: 13.58, Price: 0.00 Signal "Campeonato". Author login: AndreAutotecnic, ID 812233, Growth: 87.47, Drawdown: 13.79, Price: 0.00 Signal "OPM PRO". Author login: herinata, ID 812856, Growth: 38.55, Drawdown: 32.35, Price: 0.00 Signal "Slowly but surely 2". Author login: gyurmanz, ID 815467, Growth: 53.73, Drawdown: 13.08, Price: 0.00 Signal "Beef Waves". Author login: vladimir0005, ID 819055, Growth: 50.46, Drawdown: 32.69, Price: 0.00 Signal "Adriano Garcia". Author login: agarcia_ag, ID 823082, Growth: 111.62, Drawdown: 36.00, Price: 0.00 Signal "Max ScalperSpeed MT5". Author login: paran1615, ID 824333, Growth: 74.51, Drawdown: 40.62, Price: 0.00 Signal "SyH". Author login: gtrader2017, ID 826520, Growth: 42.78, Drawdown: 36.81, Price: 0.00 Signal "ECmp5s free". Author login: VallaLorenzo, ID 830456, Growth: 146.90, Drawdown: 27.64, Price: 0.00 Signal "MaxScalperSpeed MT5". Author login: paran1615, ID 835890, Growth: 64.33, Drawdown: 35.14, Price: 0.00 Signal "YEARNSIGNALS". Author login: yearnsignal2k19, ID 837512, Growth: 11.10, Drawdown: 2.54, Price: 0.00 Signal "AGS test 2". Author login: alireza.akbari, ID 838427, Growth: 7.93, Drawdown: 10.89, Price: 0.00 Signal "Waldeli003". Author login: WaldeliN, ID 838605, Growth: 32.98, Drawdown: 5.54, Price: 0.00 Signal "Michele". Author login: michele-m-r, ID 843351, Growth: 49.27, Drawdown: 13.90, Price: 0.00 Signal "SNAILER". Author login: 8F117EE2, ID 843458, Growth: 83.65, Drawdown: 11.86, Price: 0.00 Signal "Juniornicks". Author login: juniornicks, ID 845611, Growth: 100.25, Drawdown: 43.93, Price: 0.00 Signal "Black Hunter". Author login: christianlara, ID 845761, Growth: 51.94, Drawdown: 24.44, Price: 0.00 Signal "Master dizicheh1". Author login: awdtghuoilp, ID 857594, Growth: 5.04, Drawdown: 37.93, Price: 0.00 Signal "EUROS". Author login: Marketsystem, ID 858449, Growth: 5.31, Drawdown: 2.94, Price: 0.00 Signal "Scalpers risk10 pairs7 leverage100". Author login: leron34, ID 861750, Growth: 27.98, Drawdown: 20.53, Price: 0.00 Signal "EUREKA". Author login: Edmed933, ID 861927, Growth: 59.89, Drawdown: 7.32, Price: 0.00 Signal "Nadando Com Tubaroes". Author login: jun152, ID 862191, Growth: 21.18, Drawdown: 5.45, Price: 0.00 Signal "Demo using a grid system". Author login: RyanAfriansyah, ID 865900, Growth: 20.56, Drawdown: 8.38, Price: 0.00 Signal "Pilares". Author login: ValterCezar, ID 866672, Growth: 29.87, Drawdown: 18.96, Price: 0.00 Signal "EUROUSD". Author login: fxtrader036, ID 866719, Growth: 303.28, Drawdown: 40.70, Price: 0.00 Signal "LanzPower 25 S". Author login: sirlanz, ID 868027, Growth: 36.64, Drawdown: 45.53, Price: 0.00 Signal "Amadeu Volpato Desafio Internacional". Author login: Amadeu1971, ID 868928, Growth: 19.79, Drawdown: 12.57, Price: 0.00 Signal "Fernando correia W7". Author login: nandooo_123-hotmail, ID 870169, Growth: 41.70, Drawdown: 25.16, Price: 0.00 Signal "MAK GO". Author login: 9489631, ID 870413, Growth: 469.22, Drawdown: 36.31, Price: 0.00 Signal "Adriano Garcia W7bt 4 Pilares". Author login: agarcia_ag, ID 871868, Growth: 42.84, Drawdown: 13.19, Price: 0.00 Signal "Albertofxsemstop". Author login: albertosuga, ID 871969, Growth: 27.84, Drawdown: 19.36, Price: 0.00 Signal "BetoSTCDemo". Author login: betoabcsp, ID 872141, Growth: 29.03, Drawdown: 18.07, Price: 0.00 Signal "DESAFIOSEMSTOPLOSSCDS". Author login: cdsantos42, ID 873575, Growth: 19.47, Drawdown: 13.24, Price: 0.00 Signal "MrGeek7421". Author login: KamranAhmadi, ID 873583, Growth: 86.74, Drawdown: 16.33, Price: 0.00 Signal "Douglastorneio2w7". Author login: douglas.o.carne, ID 876302, Growth: 18.13, Drawdown: 15.34, Price: 0.00 Signal "Douglasw7demo1". Author login: douglas.o.carne, ID 876303, Growth: 148.80, Drawdown: 26.47, Price: 0.00 Signal "Douglastorneio1w7". Author login: douglas.o.carne, ID 876932, Growth: 136.86, Drawdown: 41.86, Price: 0.00 Signal "Campeonato mundial sem stop". Author login: Lpontes835, ID 878082, Growth: 23.52, Drawdown: 14.93, Price: 0.00 Signal "ALPHA IA v3". Author login: avaalpha, ID 878517, Growth: 2.77, Drawdown: 0.77, Price: 0.00 Signal "Gold x10". Author login: DynamixFX, ID 878540, Growth: 6.47, Drawdown: 8.87, Price: 0.00 Signal "MultiBolbandsRealM5". Author login: 11BREATH11, ID 879072, Growth: 83.18, Drawdown: 20.09, Price: 0.00 Signal "Ticols Stable profit". Author login: ticols, ID 879609, Growth: -56.37, Drawdown: 68.68, Price: 0.00 Signal "EA SkyBot MultiPares CENT". Author login: 4PerformanceFx, ID 882222, Growth: 248.38, Drawdown: 41.29, Price: 0.00 Signal "Trader Unity M15 100 rec". Author login: crifalo, ID 882268, Growth: 24.36, Drawdown: 26.11, Price: 0.00 Signal "Mad Piper Bill Millin". Author login: DiXOVERS, ID 882495, Growth: 251.06, Drawdown: 38.78, Price: 0.00 Signal "ProfitGuy STAR M Demo". Author login: justbond, ID 882847, Growth: 27.45, Drawdown: 24.89, Price: 0.00 Signal "EA GrayRock". Author login: serggray, ID 883235, Growth: 49.42, Drawdown: 28.68, Price: 0.00 Signal "FX FLASH". Author login: tradedeal, ID 883322, Growth: 9.17, Drawdown: 2.88, Price: 0.00 Signal "Optimizer". Author login: alama1, ID 884765, Growth: 73.53, Drawdown: 28.58, Price: 0.00 Signal "NnaFX Demo 02". Author login: 12259468, ID 886070, Growth: 136.64, Drawdown: 30.54, Pric: 0.00 Signal "Phantom5000 DEMO". Author login: JosephSmith, ID 887046, Growth: 43.41, Drawdown: 17.73, Price: 0.00 Signal "Art of Forex MadCat The G". Author login: The_G, ID 888018, Growth: 215.67, Drawdown: 40.86, Price: 0.00 Signal "ICMarkets MT5 AK 05". Author login: A.Klimkovsky, ID 889370, Growth: 13.03, Drawdown: 8.55, Price: 0.00 Signal "ProfitGuy STAR G Demo". Author login: justbond, ID 890551, Growth: 58.84, Drawdown: 58.80, Price: 0.00 Signal "BetoSemStopDM". Author login: betoabcsp, ID 892251, Growth: 94.96, Drawdown: 6.30, Price: 0.00 Signal "Smart Grid 26980 Demo". Author login: tm3912, ID 892313, Growth: 86.30, Drawdown: 37.19, Price: 0.00 Signal "Rel Vigor PSar n Red Dragon n mad max". Author login: DynamixFX, ID 892523, Growth: 73.74, Drawdown: 38.55, Price: 0.00 Signal "SolangeL". Author login: Mulherdeletras1, ID 894019, Growth: 108.40, Drawdown: 20.84, Price: 0.00 Signal "Paulotbone 1". Author login: paulotbone, ID 894062, Growth: 35.14, Drawdown: 23.93, Price: 0.00 Signal "GOLD MASTER". Author login: THEICD, ID 894983, Growth: 218.90, Drawdown: 22.80, Price: 0.00 Signal "Fxfrance". Author login: fxfrance, ID 895838, Growth: 369.48, Drawdown: 41.48, Price: 0.00 Signal "Marcos Paulo Serigatti". Author login: mpm4rcos, ID 895960, Growth: 45.62, Drawdown: 18.21, Price: 0.00 Signal "Roma Forex Desafio 4 Pilares". Author login: Jhonesroma, ID 896016, Growth: 29.60, Drawdown: 43.45, Price: 0.00 Signal "BOSBM OTC Test 5W". Author login: houli, ID 896563, Growth: 144.65, Drawdown: 22.94, Price: 0.00 Signal "FSTickTrade". Author login: onlyforsignup, ID 897751, Growth: 24.68, Drawdown: 16.20, Price: 0.00 Signal "Sun AI". Author login: Myxx, ID 899179, Growth: 16.82, Drawdown: 21.07, Price: 0.00 Signal "Equinox Demo". Author login: Kratoner, ID 905773, Growth: 44.46, Drawdown: 20.55, Price: 0.00 Signal "STRAGA Tornado VM1". Author login: stragapede, ID 906398, Growth: 36.70, Drawdown: 26.00, Price: 0.00 Signal "DiegoT". Author login: DiegoTT, ID 910230, Growth: 28.20, Drawdown: 11.49, Price: 0.00 Signal "Breaking Bad". Author login: Myxx, ID 911569, Growth: 7.63, Drawdown: 8.42, Price: 0.00 Signal "TradesaovivoBr". Author login: dhyegorodrigo1988, ID 913924, Growth: 16.77, Drawdown: 5.60, Price: 0.00 Signal "VantageFX Sunphone Dragon". Author login: sunphone, ID 916421, Growth: 345.56, Drawdown: 36.04, Price: 0.00 Signal "BYQS121". Author login: kadirryildiz, ID 916600, Growth: 32.85, Drawdown: 4.18, Price: 0.00 Signal "FBSLevelUP". Author login: manoj, ID 919106, Growth: 15.29, Drawdown: 10.49, Price: 0.00 Signal "Omega". Author login: zyntherius74, ID 922043, Growth: 70.18, Drawdown: 25.42, Price: 0.00 Signal "Best Bingo". Author login: kalnov-an, ID 926010, Growth: 59.90, Drawdown: 20.63, Price: 0.00 Signal "LCF 1". Author login: yosuf, ID 931735, Growth: 22.42, Drawdown: 36.26, Price: 0.00 Signal "Bao365". Author login: bao365, ID 933208, Growth: 28.41, Drawdown: 11.49, Price: 0.00 The "EURUSD" DOM snapshot series: Requested number of days: 1, Actual history depth: 1 "EURUSD" DOM snapshot (2021.02.24 22:22:54.654): - Sell order: 1.21576 [250.00] - Sell order: 1.21567 [100.00] - Sell order: 1.21566 [50.00] - Sell order: 1.21565 [36.00] - Buy order: 1.21563 [36.00] - Buy order: 1.21561 [50.00] - Buy order: 1.21559 [100.00] - Buy order: 1.21555 [250.00]
What's next?
In the next article, I will create a collection of MQL5 signals.
All files of the current version of the library are attached below together with the test EA file for MQL5 for you to test and download.
Leave your questions and suggestions in the comments.
*Previous articles within the series:
Prices in DoEasy library (Part 62): Updating tick series in real time, preparation for working with Depth of Market
Prices in DoEasy library (Part 63): Depth of Market and its abstract request class
Prices in DoEasy library (Part 64): Depth of Market, classes of DOM snapshot and snapshot series objects
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/9095
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use