English Русский 中文 Español Deutsch 日本語
Outras classes na biblioteca DoEasy (Parte 66): classe-coleção de Sinais MQL5.com

Outras classes na biblioteca DoEasy (Parte 66): classe-coleção de Sinais MQL5.com

MetaTrader 5Exemplos | 10 maio 2021, 11:01
590 0
Artyom Trishkin
Artyom Trishkin

Sumário


Ideia

No último artigo, criamos uma classe de um objeto-sinal que era um sinal dos muitos transmitidos no serviço Sinais MQL5.com.
Hoje vamos criar uma classe-coleção de sinais disponíveis no banco de dados de sinais e que podem ser obtidos usando a função SignalBaseSelect(), indicando o índice do sinal de que precisamos.
A coleção permitirá armazenar todos os sinais disponíveis no banco de dados numa lista conveniente para pesquisa e classificação. Poderemos encontrar e retornar listas de sinais por propriedades, por exemplo, podemos obter listas apenas de sinais gratuitos, ou vice-versa, apenas sinais pagos, também podemos classificá-los por parâmetros, por exemplo, por rentabilidade, ou obter imediatamente o índice do sinal na lista com um parâmetro igual, maior ou menor que o valor especificado. Sabendo de antemão o nome do sinal de que necessitamos, podemos localizá-lo rapidamente na coleção para usá-lo posteriormente.
Na classe-coleção, geramos o recurso para assinar um sinal selecionado na coleção ou para cancelar a assinatura de um sinal que já foi assinado na conta atual.

Além de trabalhar com objetos do serviço Sinais MQL5.com, vamos melhorar a classe do objeto-instantâneo do livro de ofertas, para isso, adicionaremos propriedades adicionais que nos permitirão calcular separadamente os volumes de ordens de compra e venda ao criar o objeto-instantâneo do livro de ofertas. Isso evitará que o usuário final realize cálculos adicionais ao trabalhar com o livro de ofertas, pois saberemos imediatamente os volumes totais de cada instantâneo do livro de ofertas - tanto para compra quanto para venda -, o que permitirá não recorrer a pesquisas adicionais de ordens de compra e venda no livro de ofertas com a soma de seus volumes ao criar estratégias.


Aprimorando as classes da biblioteca

Como já aconteceu, iremos adicionar imediatamente todas as novas mensagens da biblioteca ao arquivo \MQL5\Include\DoEasy\Data.mqh.
Primeiro escrevemos os índices das novas mensagens:

//--- CMarketBookSnapshot
   MSG_MBOOK_SNAP_TEXT_SNAPSHOT,                      // DOM snapshot
   MSG_MBOOK_SNAP_VOLUME_BUY,                         // Buy volume
   MSG_MBOOK_SNAP_VOLUME_SELL,                        // Sell volume
   
//--- 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

...

//--- 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
   MSG_SIGNAL_MQL5_TEXT_SUBSCRIBERS,                  // Subscribers
   
//--- CMQLSignalsCollection
   MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION, // Collection of MQL5.com Signals service signals
   MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_PAID,           // Paid signals
   MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_FREE,           // Free signals
   MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_NEW,            // New signal added to collection
   MSG_MQLSIG_COLLECTION_ERR_FAILED_GET_SIGNAL,       // Failed to receive signal from collection
   MSG_SIGNAL_INFO_PARAMETERS,                        // Signal copying parameters
   MSG_SIGNAL_INFO_EQUITY_LIMIT,                      // Percentage for converting deal volume
   MSG_SIGNAL_INFO_SLIPPAGE,                          // Market order slippage when synchronizing positions and copying deals
   MSG_SIGNAL_INFO_VOLUME_PERCENT,                    // Limitation on signal equity
   MSG_SIGNAL_INFO_CONFIRMATIONS_DISABLED,            // Enable synchronization without confirmation dialog
   MSG_SIGNAL_INFO_COPY_SLTP,                         // Copy Stop Loss and Take Profit
   MSG_SIGNAL_INFO_DEPOSIT_PERCENT,                   // Limit by deposit
   MSG_SIGNAL_INFO_ID,                                // Signal ID
   MSG_SIGNAL_INFO_SUBSCRIPTION_ENABLED,              // Enable copying deals by subscription
   MSG_SIGNAL_INFO_TERMS_AGREE,                       // Agree to the terms of use of the Signals service
   MSG_SIGNAL_INFO_NAME,                              // Signal name
   MSG_SIGNAL_INFO_SIGNALS_PERMISSION,                // Allow using signals for program
   MSG_SIGNAL_INFO_TEXT_SIGNAL_SUBSCRIBED,            // Subscribed to signal
   MSG_SIGNAL_INFO_TEXT_SIGNAL_UNSUBSCRIBED,          // Unsubscribed from signal
   MSG_SIGNAL_INFO_ERR_SIGNAL_NOT_ALLOWED,            // Signal service disabled for program
   MSG_SIGNAL_INFO_TEXT_CHECK_SETTINGS,               // Please check program settings (Common-->Allow modification of Signals settings)
   
  };
//+------------------------------------------------------------------+

Em seguida, escrevemos os textos da mensagem correspondentes aos índices recém-adicionados:

//--- CMarketBookSnapshot
   {"Снимок стакана цен","Depth of Market Snapshot"},
   {"Объём на покупку","Buy Volume"},
   {"Объём на продажу","Sell Volume"},
   
//--- 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"},
   {"Подписчиков","Subscribers"},
   
//--- CMQLSignalsCollection
   {"Коллекция сигналов сервиса сигналов mql5.com","Collection of signals from the mql5.com signal service"},
   {"Платных сигналов","Paid signals"},
   {"Бесплатных сигналов","Free signals"},
   {"Новый сигнал добавлен в коллекцию","New signal added to collection"},
   {"Не удалось получить сигнал из коллекции","Failed to get signal from collection"},
   {"Параметры копирования сигнала","Signal copying parameters"},
   {"Процент для конвертации объема сделки","Equity limit"},
   {"Проскальзывание, с которым выставляются рыночные ордера при синхронизации позиций и копировании сделок","Slippage (used when placing market orders in synchronization of positions and copying of trades)"},
   {"Ограничение по средствам для сигнала","Maximum percent of deposit used"},
   {"Разрешение синхронизации без показа диалога подтверждения","Allow synchronization without confirmation dialog"},
   {"Копирование Stop Loss и Take Profit","Copy Stop Loss and Take Profit"},
   {"Ограничение по депозиту","Deposit percent"},
   {"Идентификатор сигнала","Signal ID"},
   {"Разрешение на копирование сделок по подписке","Permission to copy trades by subscription"},
   {"Согласие с условиями использования сервиса \"Сигналы\"","Agree to the terms of use of the \"Signals\" service"},
   {"Имя сигнала","Signal name"},
   {"Разрешение на работу с сигналами для программы","Permission to work with signals for the program"},
   {"Осуществлена подписка на сигнал","Signal subscribed"},
   {"Осуществлена отписка от сигнала","Signal unsubscribed"},
   {"Работа с сервисом сигналов для программы не разрешена","Work with the \"Signals\" service is not allowed for the program"},
   {
    "Пожалуйста, проверьте настройки программы (Общие --> Разрешить изменение настроек Сигналов)",
    "Please check the program settings (Common --> Allow modification of Signals settings)"
   },
   
  };
//+---------------------------------------------------------------------+

Frequentemente, ao enviar mensagens para o log, especialmente mensagens de depuração, no início da mensagem indicamos o nome do método a partir do qual essa mensagem foi enviada. No artigo 19, criamos uma classe de mensagens de biblioteca. Mas por enquanto não o estamos usando ativamente - apenas indicamos os índices de mensagens que precisam ser enviados para o log usando a função padrão Print(). Como em breve iniciaremos uma nova seção da biblioteca para trabalhar com gráficos, passaremos gradualmente a trabalhar com essa classe para exibir mensagens da biblioteca. Hoje vamos adicionar uma sobrecarga do método ToLog(), para que possamos transferir adicionalmente a mensagem "fonte" ao método - o método da classe ou a função do programa a partir do qual esse método é chamado. Assim, teremos duas variantes do método ToLog() que nos permitem exibir mensagens com e sem indicação de função ou método original.

Abrimos o arquivo \MQL5\Include\DoEasy\Services\Message.mqh e escrevemos a declaração do método sobrecarregado:

//--- (1,2) display a message in the journal by ID, (3) to e-mail, (4) to a mobile device
   static void       ToLog(const int msg_id,const bool code=false);
   static void       ToLog(const string source,const int msg_id,const bool code=false);
   static bool       ToMail(const string message,const string subject=NULL);
   static bool       Push(const string message);
//--- (1) send a file to FTP, (2) return an error code
   static bool       ToFTP(const string filename,const string ftp_path=NULL);
   static int        GetError(void)                { return CMessage::m_global_error;  }

Fora do corpo da classe escrevemos sua implementação:

//+------------------------------------------------------------------+
//| Display a message in the journal by a message ID                 |
//+------------------------------------------------------------------+
void CMessage::ToLog(const int msg_id,const bool code=false)
  {
   CMessage::GetTextByID(msg_id);
   ::Print(m_text,(!code || msg_id>ERR_USER_ERROR_FIRST-1 ? "" : " "+CMessage::Retcode(msg_id)));
  }
//+------------------------------------------------------------------+
//| Display a message in the journal by a message ID                 |
//+------------------------------------------------------------------+
void CMessage::ToLog(const string source,const int msg_id,const bool code=false)
  {
   CMessage::GetTextByID(msg_id);
   ::Print(source,m_text,(!code || msg_id>ERR_USER_ERROR_FIRST-1 ? "" : " "+CMessage::Retcode(msg_id)));
  }
//+------------------------------------------------------------------+

Como podemos ver, ao contrário da primeira forma de chamada de método, em sua segunda forma, é adicionado outro um parâmetro de entrada ao qual será transferidoo nome do método ou da função a partir do qual o método ToLog() é chamado, e que será registrado no log antes da mensagem.

Voltaremos a esta classe em artigos futuros para fazer melhorias ao transferir todas as classes da biblioteca que permitem exibir mensagens usando esta classe.

Modificamos a classe CMBookSnapshot no arquivo \MQL5\Include\DoEasy\Objects\Book\MarketBookSnapshot.mqh.

Na seção privada da classe escrevemos as variáveis-membros da classe para armazenar os volumes cumulativos do instantâneo do livro de ofertas para compra e para venda:

//+------------------------------------------------------------------+
//| "DOM snapshot" class                                             |
//+------------------------------------------------------------------+
class CMBookSnapshot : public CBaseObj
  {
private:
   string            m_symbol;                  // Symbol
   long              m_time;                    // Snapshot time
   int               m_digits;                  // Symbol's Digits
   long              m_volume_buy;              // DOM buy volume
   long              m_volume_sell;             // DOM sell volume
   double            m_volume_buy_real;         // DOM buy volume with an increased accuracy
   double            m_volume_sell_real;        // DOM sell volume with an increased accuracy
   CArrayObj         m_list;                    // List of DOM order objects
public:

Na seção de métodos para acesso simplificado às propriedades do objeto-instantâneo do livro de ofertas da seção pública da classe vamos escrever os métodos que retornam essas propriedades recém-adicionadas da classe, e os métodos para exibir sua descrição:

//+----------------------------------------------------------------------+ 
//|Methods of a simplified access to the DOM snapshot object properties  |
//+----------------------------------------------------------------------+
//--- Set (1) a symbol, (2) a DOM snapshot time and (3) the specified time for all DOM orders
   void              SetSymbol(const string symbol)   { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); }
   void              SetTime(const long time_msc)     { this.m_time=time_msc; }
   void              SetTimeToOrders(const long time_msc);
//--- Return (1) a DOM symbol, (2) symbol's Digits and (3) a snapshot time
//--- (4) buy and (5) sell DOM snapshot volume
//--- with increased accuracy for (6) buying and (7) selling,
   string            Symbol(void)               const { return this.m_symbol;             }
   int               Digits(void)               const { return this.m_digits;             }
   long              Time(void)                 const { return this.m_time;               }
   long              VolumeBuy(void)            const { return this.m_volume_buy;         }
   long              VolumeSell(void)           const { return this.m_volume_sell;        }
   double            VolumeBuyReal(void)        const { return this.m_volume_buy_real;    }
   double            VolumeSellReal(void)       const { return this.m_volume_sell_real;   }
   
//--- Return the description of DOM (1) buy and (2) sell volume
   string            VolumeBuyDescription(void);
   string            VolumeSellDescription(void);
   
  };
//+------------------------------------------------------------------+

Ao criar um novo objeto-instantâneo do livro de ofertas, examinamos todas suas ordens num loop, criamos os objetos dessas ordens e os adicionamos à lista. Agora precisamos levar em consideração os tipos de ordens no construtor da classe e imediatamente adicionar ao valor das variáveis, que armazenam o volume total das ordens de compra e venda, o volume da ordem atual, dependendo do tipo. Assim, cada variável armazenará o volume total das ordens de compra ou venda imediatamente após a criação do objeto-instantâneo do livro de ofertas.

Vamos adicionar essas alterações ao construtor paramétrico da classe:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time)
  {
   //--- Set a symbol
   this.SetSymbol(symbol);
   //--- Clear the list
   this.m_list.Clear();
   //--- In the loop by the structure array
   int total=::ArraySize(book_array);
   this.m_volume_buy=this.m_volume_sell=0;
   this.m_volume_buy_real=this.m_volume_sell_real=0;
   for(int i=0;i<total;i++)
     {
      //--- Create order objects of the current DOM snapshot depending on the order type
      CMarketBookOrd *mbook_ord=NULL;
      switch(book_array[i].type)
        {
         case BOOK_TYPE_BUY         : mbook_ord=new CMarketBookBuy(this.m_symbol,book_array[i]);         break;
         case BOOK_TYPE_SELL        : mbook_ord=new CMarketBookSell(this.m_symbol,book_array[i]);        break;
         case BOOK_TYPE_BUY_MARKET  : mbook_ord=new CMarketBookBuyMarket(this.m_symbol,book_array[i]);   break;
         case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(this.m_symbol,book_array[i]);  break;
         default: break;
        }
      if(mbook_ord==NULL)
         continue;
      //--- Set the DOM snapshot time for the order
      mbook_ord.SetTime(this.m_time);

      //--- Set the sorted list flag for the list (by the price value) and add the current order object to it
      //--- If failed to add the object to the DOM order list, remove the order object
      this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE);
      if(!this.m_list.InsertSort(mbook_ord))
         delete mbook_ord;
      //--- If the order object is successfully added to the DOM order list, supplement the total snapshot volumes
      else
        {
         switch(mbook_ord.TypeOrd())
           {
            case BOOK_TYPE_BUY         : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_SELL        : 
              this.m_volume_sell+=mbook_ord.Volume(); 
              this.m_volume_sell_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_BUY_MARKET  : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_SELL_MARKET : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            default: break;
           }
        }
     }
  }
//+------------------------------------------------------------------+

Aqui: primeiro, inicializamos as variáveis que armazenam os volumes totais de todas as ordens para o instantâneo do livro de ofertas para compra e venda. Em seguida, no corpo do loop percorrendo todas as ordens do livro de ofertas, dependendo do tipo de ordem, adicionamos o volume do pedido atual às variáveis, que armazenam os volumes totais e que correspondem ao tipo de ordem. Assim, no final do loop, os volumes cumulativos de compra e venda serão armazenados em todas as variáveis para todas as ordens do instantâneo do livro de ofertas.

Nos métodos, que exibem no log uma breve descrição de objeto e todas as propriedades do objeto, adicionamos a saída dos volumes totais de compra e venda:

//+------------------------------------------------------------------+
//| Display a short description of the object in the journal         |
//+------------------------------------------------------------------+
void CMBookSnapshot::PrintShort(void)
  {
   string vol_buy="Buy vol: "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy());
   string vol_sell="Sell vol: "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell());   
   ::Print(this.Header()," ",vol_buy,", ",vol_sell," ("+TimeMSCtoString(this.m_time),")");
  }
//+------------------------------------------------------------------+
//| Display object properties in the journal                         |
//+------------------------------------------------------------------+
void CMBookSnapshot::Print(void)
  {
   string vol_buy=CMessage::Text(MSG_MBOOK_SNAP_VOLUME_BUY)+": "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy());
   string vol_sell=CMessage::Text(MSG_MBOOK_SNAP_VOLUME_SELL)+": "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell());
   ::Print(this.Header(),": ",vol_buy,", ",vol_sell," ("+TimeMSCtoString(this.m_time),"):");
   this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE);
   for(int i=this.m_list.Total()-1;i>WRONG_VALUE;i--)
     {
      CMarketBookOrd *ord=this.m_list.At(i);
      if(ord==NULL)
         continue;
      ::Print("- ",ord.Header());
     }
  }
//+------------------------------------------------------------------+

Fora do corpo da classe, vamos escrever a implementação de dois novos métodos que retornam descrições de volumes do livro de ofertas para compra e para venda:

//+------------------------------------------------------------------+
//| Return the DOM buy volume description                            |
//+------------------------------------------------------------------+
string CMBookSnapshot::VolumeBuyDescription(void)
  {
   return(CMessage::Text(MSG_MBOOK_SNAP_VOLUME_BUY)+": "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy()));
  }
//+------------------------------------------------------------------+
//| Return the DOM sell volume description                           |
//+------------------------------------------------------------------+
string CMBookSnapshot::VolumeSellDescription(void)
  {
   return(CMessage::Text(MSG_MBOOK_SNAP_VOLUME_SELL)+": "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell()));
  }
//+------------------------------------------------------------------+

Em ambos os métodos, o valor do volume é verificado com maior precisão, e, se o valor for maior que zero, o cabeçalho + este valor do volume (real) será retornado, caso contrário, um valor inteiro.

À classe-coleção das séries de instantâneos dos livros de ofertas CMBookSeriesCollection no arquivo \MQL5\Include\DoEasy\Collections\BookSeriesCollection.mqh,
à sua seção pública adicionamos métodos que retornam listas de acordo com os critérios especificados quanto a propriedades de objetos de lista:

public:
//--- Return (1) itself and (2) the DOM series collection list
   CMBookSeriesCollection *GetObject(void)                              { return &this;               }
   CArrayObj              *GetList(void)                                { return &this.m_list;        }
   //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj              *GetList(ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByMBookProperty(this.GetList(),property,value,mode);  }
   CArrayObj              *GetList(ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode);  }
   CArrayObj              *GetList(ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode);  }
//--- Return the number of DOM series in the 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

Na classe de objeto-ordem do livro de ofertas CMQLSignal no arquivo \MQL5\Include\DoEasy\Objects\MQLSignalBase\MQLSignal.mqh modificamos o método PrintShort() através do valor-sinalizador que indica a necessidade de exibir um hífen antes da descrição do objeto:

//--- 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(const bool dash=false);
//--- Return the object short name
   virtual string    Header(const bool shrt=false);

E vamos editar o corpo do método:

//+------------------------------------------------------------------+
//| Display a short description of the object in the journal         |
//+------------------------------------------------------------------+
void CMQLSignal::PrintShort(const bool dash=false)
  {
   ::Print
     (
      (dash ? "- " : ""),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),
      ", ",CMessage::Text(MSG_SIGNAL_MQL5_TEXT_SUBSCRIBERS),": ",this.Subscribers()
     );
  }
//+------------------------------------------------------------------+

Aqui: dependendo do valor passado, um hífen é exibido na frente da descrição do objeto, ou não exibido, e no final da descrição o valor sobre o número de assinantes por sinal é adicionado.

No final do corpo da classe, escrevemos um novo método para assinar um sinal descrito por este objeto:

//--- Return the account type name
   string            TradeModeDescription(void);
   
//--- Subscribe to a signal
   bool              Subscribe(void)                        { return ::SignalSubscribe(this.ID()); }
   
  };
//+------------------------------------------------------------------+

No construtor da classe consertamos a inicialização da variável armazenando o status da assinatura do sinal:

//+------------------------------------------------------------------+
//| 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]            = (::SignalInfoGetInteger(SIGNAL_INFO_ID)==signal_id);
   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);
  }
//+------------------------------------------------------------------+

Anteriormente, ele era inicializado com o valor false. Agora vamos inicializá-lo usando o resultado da comparação do identificador do objeto-sinal com o identificador do sinal assinado atual. Se houver uma assinatura para qualquer sinal, então este sinal será "atualmente assinado" e terá um identificador de sinal do banco de dados de Sinais MQL5.com, que comparamos. Se eles forem iguais, este sinal está assinado - o resultado da comparação será true, caso contrário, false.

Como estamos criando uma nova coleção hoje, precisamos definir nosso próprio identificador para ela. No arquivo \MQL5\Include\DoEasy\Defines.mqh escrevemos o identificador da coleção de sinais do serviço Sinais MQL5.com:

//--- 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
#define COLLECTION_MQL5_SIGNALS_ID     (0x7785)                   // MQL5 signals collection list ID
//--- Data parameters for file operations

Para trabalhar plenamente com a coleção de sinais do serviço Sinais MQL5.com, precisamos criar métodos para pesquisar e classificar por propriedades de objetos-sinais. Para cada coleção, criamos nossos próprios métodos de pesquisa e classificação. Eles são todos idênticos e nós os descrevemos em detalhes no terceiro artigo sobre a biblioteca.

No arquivo da classe CSelect para pesquisa e classificação, localizado em \MQL5\Include\DoEasy\Services\Select.mqh,
anexamos o arquivo de classe do objeto-sinal mql5 e declararemos novos métodos para trabalhar com uma coleção de objetos-sinais:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\SeriesDE.mqh"
#include "..\Objects\Indicators\Buffer.mqh"
#include "..\Objects\Indicators\IndicatorDE.mqh"
#include "..\Objects\Indicators\DataInd.mqh"
#include "..\Objects\Ticks\DataTick.mqh"
#include "..\Objects\Book\MarketBookOrd.mqh"
#include "..\Objects\MQLSignalBase\MQLSignal.mqh"
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Methods of working with MQL5 signal data                         |
//+------------------------------------------------------------------+
   //--- Return the list of MQL5 signals with one of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the MQL5 signal index in the list with the maximum value of the (1) integer, (2) real and (3) string properties
   static int        FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property);
   static int        FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property);
   static int        FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property);
   //--- Return the MQL5 signal in the list with the minimum value of (1) integer, (2) real and (3) string property
   static int        FindMQLSignalMin(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property);
   static int        FindMQLSignalMin(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property);
   static int        FindMQLSignalMin(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

Fora do corpo da classe, escrevemos sua implementação:

//+------------------------------------------------------------------+
//| Methods of working with MQL5 signal data                         |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of MQL5 signals with one integer                 |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CMQLSignal *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of MQL5 signals with one real                    |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CMQLSignal *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of MQL5 signals with one string                  |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CMQLSignal *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the MQL5 signal index in the list                         |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CMQLSignal *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMQLSignal *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the MQL5 signal index in the list                         |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CMQLSignal *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMQLSignal *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the MQL5 signal index in the list                         |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CMQLSignal *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMQLSignal *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the MQL5 signal index in the list                         |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindMQLSignalMin(CArrayObj* list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property)
  {
   int index=0;
   CMQLSignal *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMQLSignal *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the MQL5 signal index in the list                         |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindMQLSignalMin(CArrayObj* list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property)
  {
   int index=0;
   CMQLSignal *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMQLSignal *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the MQL5 signal index in the list                         |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindMQLSignalMin(CArrayObj* list_source,ENUM_SIGNAL_MQL5_PROP_STRING property)
  {
   int index=0;
   CMQLSignal *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMQLSignal *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

Como já foi mencionado mais de uma vez, todos esses métodos, que são idênticos para cada uma das classes de objetos da biblioteca, e você pode mais uma vez saber sobre eles no artigo 3.

Estamos prontos para criar uma classe-coleção de objetos-sinais do serviço Sinais MQL5.com.

Classe-coleção de objetos-sinais mql5

No diretório da biblioteca \MQL5\Include\DoEasy\Collections\ criamos uma nova classe CMQLSignalsCollection no arquivo MQLSignalsCollection.mqh.

Ao arquivo da classe anexamos todos os arquivos de classe necessários para sua operação:

//+------------------------------------------------------------------+
//|                                         MQLSignalsCollection.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 "..\Services\Select.mqh"
#include "..\Objects\MQLSignalBase\MQLSignal.mqh"
//+------------------------------------------------------------------+

A classe deve ser herdada do objeto base de todos os objetos da biblioteca:

//+------------------------------------------------------------------+
//| MQL5 signal object collection                                    |
//+------------------------------------------------------------------+
class CMQLSignalsCollection : public CBaseObj
  {
  }

Vamos examinar todo o corpo da classe e, em seguida, analisar seus métodos constituintes:

//+------------------------------------------------------------------+
//|                                         MQLSignalsCollection.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 "..\Services\Select.mqh"
#include "..\Objects\MQLSignalBase\MQLSignal.mqh"
//+------------------------------------------------------------------+
//| MQL5 signal object collection                                    |
//+------------------------------------------------------------------+
class CMQLSignalsCollection : public CBaseObj
  {
private:
   CListObj                m_list;                                   // List of MQL5 signal objects
   int                     m_signals_base_total;                     // Number of signals in the MQL5 signal database
//--- Subscribe to a signal
   bool                    Subscribe(const long signal_id);
//--- Set the flag allowing synchronization without confirmation dialog
   bool                    CurrentSetConfirmationsDisableFlag(const bool flag);
//--- Set the flag of copying Stop Loss and Take Profit
   bool                    CurrentSetSLTPCopyFlag(const bool flag);
//--- Set the flag allowing the copying of signals by subscription
   bool                    CurrentSetSubscriptionEnabledFlag(const bool flag);
public:
//--- Return (1) itself and (2) the DOM series collection list
   CMQLSignalsCollection  *GetObject(void)                              { return &this;                  }
   CArrayObj              *GetList(void)                                { return &this.m_list;           }
   //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj              *GetList(ENUM_SIGNAL_MQL5_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByMQLSignalProperty(this.GetList(),property,value,mode);  }
   CArrayObj              *GetList(ENUM_SIGNAL_MQL5_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMQLSignalProperty(this.GetList(),property,value,mode);  }
   CArrayObj              *GetList(ENUM_SIGNAL_MQL5_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMQLSignalProperty(this.GetList(),property,value,mode);  }
//--- Return the number of MQL5 signal objects in the list
   int                     DataTotal(void)                        const { return this.m_list.Total();    }
//--- Return the pointer to the MQL5 signal object (1) by ID, (2) by name and (3) by index in the list
   CMQLSignal             *GetMQLSignal(const long id);
   CMQLSignal             *GetMQLSignal(const string name);
   CMQLSignal             *GetMQLSignal(const int index)                { return this.m_list.At(index);  }

//--- Create the collection list of MQL5 signal objects
   bool                    CreateCollection(void);
//--- Update the collection list of MQL5 signal objects
   void                    Refresh(const bool messages=true);

//--- Display (1) the complete and (2) short collection description in the journal
   void                    Print(void);
   void                    PrintShort(const bool list=false,const bool paid=true,const bool free=true);
   
//--- Constructor
                           CMQLSignalsCollection();
//--- Subscribe to a signal by (1) ID and (2) signal name
   bool                    SubscribeByID(const long signal_id);
   bool                    SubscribeByName(const string signal_name);
   
//+----------------------------------------------------------------------------+
//| Methods of working with the current signal the subscription is active for  |
//+----------------------------------------------------------------------------+
//--- Return the flag allowing working with the signal service
   bool                    ProgramIsAllowed(void)                 { return (bool)::MQLInfoInteger(MQL_SIGNALS_ALLOWED); }
//--- Unsubscribe from the current signal
   bool                    CurrentUnsubscribe(void);

//--- Set the percentage for converting deal volume
   bool                    CurrentSetEquityLimit(const double value);
//--- Set the market order slippage used when synchronizing positions and copying deals
   bool                    CurrentSetSlippage(const double value);
//--- Set deposit limitations (in %)
   bool                    CurrentSetDepositPercent(const int value);

//--- Return the percentage for converting deal volume
   double                  CurrentEquityLimit(void)               { return ::SignalInfoGetDouble(SIGNAL_INFO_EQUITY_LIMIT);                  }
//--- Return the market order slippage used when synchronizing positions and copying deals
   double                  CurrentSlippage(void)                  { return ::SignalInfoGetDouble(SIGNAL_INFO_SLIPPAGE);                      }
//--- Return the flag allowing synchronization without confirmation dialog 
   bool                    CurrentConfirmationsDisableFlag(void)  { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_CONFIRMATIONS_DISABLED); }
//--- Return the flag of copying Stop Loss and Take Profit
   bool                    CurrentSLTPCopyFlag(void)              { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_COPY_SLTP);              }
//--- Return deposit limitations (in %)
   int                     CurrentDepositPercent(void)            { return (int)::SignalInfoGetInteger(SIGNAL_INFO_DEPOSIT_PERCENT);         }
//--- Return the flag allowing the copying of signals by subscription
   bool                    CurrentSubscriptionEnabledFlag(void)   { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_SUBSCRIPTION_ENABLED);   }
//--- Return the limitation by funds for a signal
   double                  CurrentVolumePercent(void)             { return ::SignalInfoGetDouble(SIGNAL_INFO_VOLUME_PERCENT);                }
//--- Return the signal ID
   long                    CurrentID(void)                        { return ::SignalInfoGetInteger(SIGNAL_INFO_ID);                           }
//--- Return the flag of agreeing to the terms of use of the Signals service
   bool                    CurrentTermsAgreeFlag(void)            { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_TERMS_AGREE);            }
//--- Return the signal name
   string                  CurrentName(void)                      { return ::SignalInfoGetString(SIGNAL_INFO_NAME);                          }

//--- Enable synchronization without the confirmation dialog
   bool                    CurrentSetConfirmationsDisableON(void) { return this.CurrentSetConfirmationsDisableFlag(true);                    }
//--- Disable synchronization without the confirmation dialog
   bool                    CurrentSetConfirmationsDisableOFF(void){ return this.CurrentSetConfirmationsDisableFlag(false);                   }
//--- Enable copying Stop Loss and Take Profit
   bool                    CurrentSetSLTPCopyON(void)             { return this.CurrentSetSLTPCopyFlag(true);                                }
//--- Disable copying Stop Loss and Take Profit
   bool                    CurrentSetSLTPCopyOFF(void)            { return this.CurrentSetSLTPCopyFlag(false);                               }
//--- Enable copying deals by subscription
   bool                    CurrentSetSubscriptionEnableON(void)   { return this.CurrentSetSubscriptionEnabledFlag(true);                     }
//--- Disable copying deals by subscription
   bool                    CurrentSetSubscriptionEnableOFF(void)  { return this.CurrentSetSubscriptionEnabledFlag(false);                    }

//--- Return the description of enabling working with signals for the launched program
   string                  ProgramIsAllowedDescription(void);
//--- Return the percentage description for converting the deal volume
   string                  CurrentEquityLimitDescription(void);
//--- Return the description of the market order slippage used when synchronizing positions and copying deals
   string                  CurrentSlippageDescription(void);
//--- Return the description of the limitation by funds for a signal
   string                  CurrentVolumePercentDescription(void);
//--- Return the description of the flag allowing synchronization without confirmation dialog
   string                  CurrentConfirmationsDisableFlagDescription(void);
//--- Return the description of the flag of copying Stop Loss and Take Profit
   string                  CurrentSLTPCopyFlagDescription(void);
//--- Return the description of the deposit limitations (in %)
   string                  CurrentDepositPercentDescription(void);
//--- Return the description of the flag allowing the copying of signals by subscription
   string                  CurrentSubscriptionEnabledFlagDescription(void);
//--- Return the description of the signal ID
   string                  CurrentIDDescription(void);
//--- Return the description of the flag of agreeing to the terms of use of the Signals service
   string                  CurrentTermsAgreeFlagDescription(void);
//--- Return the description of the signal name
   string                  CurrentNameDescription(void);
//--- Display the parameters of signal copying settings in the journal
   void                    CurrentSubscriptionParameters(void);
//---
  };
//+------------------------------------------------------------------+

A seção privada da classe contém um objeto de lista na qual armazenaremos objetos-sinais mql5 e variáveis e métodos auxiliares.

A seção pública da classe contém métodos de trabalho com a coleção de lista de objetos padrão para todos os objetos da biblioteca e dois métodos para assinar um sinal selecionado por identificador e nome. Também na seção pública da classe estão localizados os métodos para trabalhar com o sinal atual assinado.

Vamos analisar a implementação de alguns dos métodos.

No construtor da classe limpamos a lista-coleção, definimos o sinalizador de lista classificada para ele, definimos a lista para o identificador da coleção de objetos-sinais mql5, escrevemos o número total de sinais no banco de dados de Sinais MQL5.com e chamamos o método de criação de coleção.

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CMQLSignalsCollection::CMQLSignalsCollection()
  {
   this.m_list.Clear();
   this.m_list.Sort();
   this.m_list.Type(COLLECTION_MQL5_SIGNALS_ID);
   this.m_signals_base_total=::SignalBaseTotal();
   this.CreateCollection();
  }
//+------------------------------------------------------------------+

Como não iremos atualizar e controlar automaticamente a lista de sinais por meio da biblioteca, então, em princípio, basta criarmos apenas um método de atualização da lista, e nele todos os sinais disponíveis no banco de dados serão lidos e escritos na lista de coleção. O próprio usuário precisará chamar o método de atualização Refresh() antes de receber quaisquer dados da coleção e, se desejado, ter uma lista atualizada de sinais do banco de dados de Sinais MQL5.com. Mas também teremos um método para criar uma coleção, só que como compatibilidade com um conjunto de métodos típicos de coleção de biblioteca. No próprio método, a lista será simplesmente apagada e o método de atualização da coleção será chamado. Após a primeira chamada do método Refresh() do método de criação da coleção, a lista-coleção será preenchido e então poderemos trabalhar com esta lista. Se precisarmos atualizar a lista-coleção em busca de possíveis novos sinais, só precisamos chamar o método Refresh() antes de acessar a lista-coleção.

Método de criação de coleção:

//+------------------------------------------------------------------+
//| Create the collection list of MQL5 signal objects                |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::CreateCollection(void)
  {
   this.m_list.Clear();
   this.Refresh(false);
   if(m_list.Total()>0)
     {
      ::Print(CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION)," ",CMessage::Text(MSG_LIB_TEXT_TS_TEXT_CREATED_OK));
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Aqui: limpamos a lista-coleção de sinais, preenchemos a lista com sinais do banco de dados de Sinais MQL5.com e, se o número de sinais na lista-coleção for maior que zero, ou seja, a lista está preenchida, então exibiremos uma mensagem sobre a criação bem-sucedida da lista-coleção e retornaremos true.
Caso contrário, devolveremos false.

Método de atualização da lista-coleção:

//+------------------------------------------------------------------+
//| Update the collection list of MQL5 signal objects                |
//+------------------------------------------------------------------+
void CMQLSignalsCollection::Refresh(const bool messages=true)
  {
   this.m_signals_base_total=::SignalBaseTotal();
   //--- loop through all signals in the signal database
   for(int i=0;i<this.m_signals_base_total;i++) 
     { 
      //--- Select a signal from the signal database by the loop index
      if(!::SignalBaseSelect(i))
         continue;
      //--- Get the current signal ID and
      //--- create a new MQL5 signal object based on it
      long id=::SignalBaseGetInteger(SIGNAL_BASE_ID);
      CMQLSignal *signal=new CMQLSignal(id);
      if(signal==NULL)
         continue;
      //--- Set the sorting flag for the list by signal ID and,
      //--- if such a signal is already present in the collection list,
      //--- remove the created object and go to the next loop iteration
      m_list.Sort(SORT_BY_SIGNAL_MQL5_ID);
      if(this.m_list.Search(signal)!=WRONG_VALUE)
        {
         delete signal;
         continue;
        }
      //--- If failed to add a new signal object to the collection list,
      //--- remove the created object and go to the next loop iteration
      if(!this.m_list.InsertSort(signal))
        {
         delete signal;
         continue;
        }
      //--- If an MQL5 signal object is successfully added to the collection
      //--- and the new object message flag is set in the parameters passed to the method,
      //--- display a message about a newly found signal
      else if(messages)
        {
         ::Print(DFUN,CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_NEW),":");
         signal.PrintShort(true);
        }
     } 
  }
//+------------------------------------------------------------------+

A lógica do método é descrita em detalhes nos comentários ao código. Resumindo: o sinalizador indicando necessidade de relatar um novo sinal encontrado é passado para o método. Visto que o método não limpa a lista-coleção, apenas um sinal recém-encontrado pode ser adicionado a ele. Se o sinalizador de mensagem for definido, após a adição bem-sucedida de um novo objeto-sinal à lista, uma mensagem sobre o novo sinal encontrado será exibida no log.

No momento, este é o método mais simples que não implementa a atualização dos parâmetros dos sinais existentes - eles podem ser atualizados de forma independente acessando o objeto-sinal por identificador e configurando novos valores nas propriedades. Posteriormente adicionaremos uma atualização automática dos parâmetros dos sinais existentes por tempo e, se a classe-coleção de Sinais MQL5.com estiver em demanda, criaremos para ela o envio de eventos sobre novos sinais e sobre alterações nos parâmetros dos sinais monitorados.

Método que retorna um ponteiro para um objeto-sinal mql5 pelo identificador de sinal:

//+------------------------------------------------------------------+
//| Return the pointer to an MQL5 signal object by an ID             |
//+------------------------------------------------------------------+
CMQLSignal *CMQLSignalsCollection::GetMQLSignal(const long id)
  {
   CArrayObj *list=GetList(SIGNAL_MQL5_PROP_ID,id,EQUAL);
   return(list!=NULL ? list.At(0) : NULL);
  }
//+------------------------------------------------------------------+

Obtemos uma lista de objetos-sinais mql5 por identificador de sinal e retornamos ou o único objeto da lista resultante, ou NULL.

Método que retorna um ponteiro para um objeto-sinal mql5 pelo nome do sinal:

//+------------------------------------------------------------------+
//| Return the pointer to an MQL5 signal object by a name            |
//+------------------------------------------------------------------+
CMQLSignal *CMQLSignalsCollection::GetMQLSignal(const string name)
  {
   CArrayObj *list=GetList(SIGNAL_MQL5_PROP_NAME,name,EQUAL);
   return(list!=NULL ? list.At(0) : NULL);
  }
//+------------------------------------------------------------------+

Obtemos uma lista de objetos-sinais mql5 por nome de sinal e retornamos ou o único objeto da lista resultante, ou NULL.

O método que registra no log uma descrição completa da coleção:

//+------------------------------------------------------------------+
//| Display complete collection description to the journal           |
//+------------------------------------------------------------------+
void CMQLSignalsCollection::Print(void)
  {
   ::Print(CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION),":");
   for(int i=0;i<this.m_list.Total();i++)
     {
      CMQLSignal *signal=this.m_list.At(i);
      if(signal==NULL)
         continue;
      signal.Print();
     }
  }
//+------------------------------------------------------------------+

Primeiro, será exibido o cabeçalho e, depois, num loop através da lista-coleção obtemos o próximo objeto-sinal mql5 e exibimos sua descrição completa.

O método que registra no log uma breve descrição da coleção:

//+------------------------------------------------------------------+
//| Display the short collection description in the journal          |
//+------------------------------------------------------------------+
void CMQLSignalsCollection::PrintShort(const bool list=false,const bool paid=true,const bool free=true)
  {
   //--- Display the header in the journal
   ::Print(CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION),":");
   //--- If the list is full, display short descriptions of all signals in the collection 
   //--- according to the flags indicating the necessity to display paid and free signals
   if(list)
      for(int i=0;i<this.m_list.Total();i++)
        {
         CMQLSignal *signal=this.m_list.At(i);
         if(signal==NULL || (signal.Price()>0 && !paid) || (signal.Price()==0 && !free))
            continue;
         signal.PrintShort(true);
        }
   //--- If not the signal list
   else
     {
      //--- Sort the list by signal price
      this.m_list.Sort(SORT_BY_SIGNAL_MQL5_PRICE);
      //--- Get the list of free signals and their number
      CArrayObj *list_free=this.GetList(SIGNAL_MQL5_PROP_PRICE,0,EQUAL);
      int num_free=(list_free==NULL ? 0 : list_free.Total());
      //--- Sort the list by signal price
      this.m_list.Sort(SORT_BY_SIGNAL_MQL5_PRICE);
      //--- Get the list of paid signals and their number
      CArrayObj *list_paid=this.GetList(SIGNAL_MQL5_PROP_PRICE,0,MORE);
      int num_paid=(list_paid==NULL ? 0 : list_paid.Total());
      //--- Display the number of free and paid signals in the collection in the journal
      ::Print
        (
         "- ",CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_FREE),": ",(string)num_free,
         ", ",CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_PAID),": ",(string)num_paid
        );
     }
  }
//+------------------------------------------------------------------+

Este método imprime mensagens e listas diferentes no log, dependendo dos sinalizadores passados.

Primeiro, será exibido o cabeçalho e, depois, se o sinalizador da lista estiver definido, no log serão impressas breves descrições dos sinais na coleção. Isso leva em consideração os sinalizadores pagos e sinais gratuitos. Dependendo do seu estado, todos os sinais, os pagos ou apenas os gratuitos são exibidos no log.
Se precisarmos exibir a descrição, não como uma lista , então, após o cabeçalho, será exibido o número total de sinais gratuitos e pagos na lista-coleção.

Métodos de implementação de assinatura de sinal (método privado) e cancelação de assinatura de sinal existente (método público):

//+------------------------------------------------------------------+
//| Subscribe to a signal                                            |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::Subscribe(const long signal_id)
  {
//--- If working with signals is disabled for a program,
//--- display the appropriate message and the recommendation to check the program settings
   if(!this.ProgramIsAllowed())
     {
      ::Print(DFUN,CMessage::Text(MSG_SIGNAL_INFO_ERR_SIGNAL_NOT_ALLOWED));
      ::Print(DFUN,CMessage::Text(MSG_SIGNAL_INFO_TEXT_CHECK_SETTINGS));
      return false;
     }
//--- If failed to subscribe to a signal, display the error message and return 'false'
   ::ResetLastError();
   if(!::SignalSubscribe(signal_id))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
//--- Subscription successful. Display the successful signal subscription message and return 'true'
   ::Print(CMessage::Text(MSG_SIGNAL_INFO_TEXT_SIGNAL_SUBSCRIBED)," ID ",(string)this.CurrentID()," \"",CurrentName(),"\"");
   return true;
  }
//+------------------------------------------------------------------+
//| Unsubscribe from a subscribed signal                             |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::CurrentUnsubscribe(void)
  {
//--- If working with signals is disabled for a program,
//--- display the appropriate message and the recommendation to check the program settings
   if(!this.ProgramIsAllowed())
     {
      ::Print(DFUN,CMessage::Text(MSG_SIGNAL_INFO_ERR_SIGNAL_NOT_ALLOWED));
      ::Print(DFUN,CMessage::Text(MSG_SIGNAL_INFO_TEXT_CHECK_SETTINGS));
      return false;
     }
//--- Remember an ID and a name of the current signal
   ::ResetLastError();
   long id=this.CurrentID();
   string name=this.CurrentName();
//--- If the ID is zero (no subscription), return 'true'
   if(id==0)
      return true;
//--- If failed to unsubscribe from a signal, display the error message and return 'false'
   if(!::SignalUnsubscribe())
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
//--- Unsubscribed from a signal successfully. Display the message about successfully unsubscribing from a signal and return 'true'
   ::Print(CMessage::Text(MSG_SIGNAL_INFO_TEXT_SIGNAL_UNSUBSCRIBED)," ID ",(string)id," \"",name,"\"");
   return true;
  }
//+------------------------------------------------------------------+

A lógica dos métodos é comentada em detalhes na lista de métodos.

Método público que assina um sinal por identificador de sinal:

//+------------------------------------------------------------------+
//| Subscribe to a signal by ID                                      |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::SubscribeByID(const long signal_id)
  {
   CMQLSignal *signal=GetMQLSignal(signal_id);
   if(signal==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_MQLSIG_COLLECTION_ERR_FAILED_GET_SIGNAL),": ",signal_id);
      return false;
     }
   return this.Subscribe(signal.ID());
  }
//+------------------------------------------------------------------+

Aqui: obtemos um ponteiro para o objeto-sinal mql5 na lista-coleção pelo identificador passado para o método e retornamos o resultado do método privado de assinatura do sinal discutido acima.

Método público que assina um sinal pelo nome do sinal:

//+------------------------------------------------------------------+
//| Subscribe to a signal by signal name                             |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::SubscribeByName(const string signal_name)
  {
   CMQLSignal *signal=GetMQLSignal(signal_name);
   if(signal==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_MQLSIG_COLLECTION_ERR_FAILED_GET_SIGNAL),": \"",signal_name,"\"");
      return false;
     }
   return this.Subscribe(signal.ID());
  }
//+------------------------------------------------------------------+

Aqui: obtemos um ponteiro para o objeto-sinal mql5 na lista-coleção pelo nome do sinal passado para o método (o nome deve ser conhecido com antecedência) e retornamos o resultado do método privado de assinatura do sinal discutido acima.

Métodos para definir valores para copiar sinais de negociação:

//+------------------------------------------------------------------+
//| Set the percentage for converting a deal volume                  |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::CurrentSetEquityLimit(const double value) 
  {
   ::ResetLastError();
   if(!::SignalInfoSetDouble(SIGNAL_INFO_EQUITY_LIMIT,value))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Define the slippage used to set                                  |
//| market orders when synchronizing positions and copying deals     |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::CurrentSetSlippage(const double value) 
  {
   ::ResetLastError();
   if(!::SignalInfoSetDouble(SIGNAL_INFO_SLIPPAGE,value))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Set the flag enabling synchronization                            |
//| without confirmation dialog                                      |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::CurrentSetConfirmationsDisableFlag(const bool flag) 
  {
   ::ResetLastError();
   if(!::SignalInfoSetInteger(SIGNAL_INFO_CONFIRMATIONS_DISABLED,flag))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Set the flag of copying Stop Loss and Take Profit                |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::CurrentSetSLTPCopyFlag(const bool flag) 
  {
   ::ResetLastError();
   if(!::SignalInfoSetInteger(SIGNAL_INFO_COPY_SLTP,flag))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Set deposit limitations (in %)                                   |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::CurrentSetDepositPercent(const int value) 
  {
   ::ResetLastError();
   if(!::SignalInfoSetInteger(SIGNAL_INFO_DEPOSIT_PERCENT,value))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Set the flag allowing the copying of signals by subscription     |
//+------------------------------------------------------------------+
bool CMQLSignalsCollection::CurrentSetSubscriptionEnabledFlag(const bool flag) 
  {
   ::ResetLastError();
   if(!::SignalInfoSetInteger(SIGNAL_INFO_SUBSCRIPTION_ENABLED,flag))
     {
      CMessage::ToLog(DFUN,::GetLastError(),true);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Aqui, todos os métodos usam funções de definição de valor SignalInfoSetDouble() e SignalInfoSetInteger(). Se o valor for definido sem sucesso, os métodos exibirão uma descrição do erro e retornarão false. Se o valor for definido com sucesso, os métodos retornarão true.

Métodos que retornam descrições das configurações para copiar sinais de negociação:

//+------------------------------------------------------------------+
//| Return the description of the permission to work with signals    |
//| for the currently launched program                               |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::ProgramIsAllowedDescription(void)
  {
   return
     (
      CMessage::Text(MSG_SIGNAL_INFO_SIGNALS_PERMISSION)+": "+
      (this.ProgramIsAllowed() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
     );
  }
//+------------------------------------------------------------------+
//| Return the percentage description for converting the deal volume |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentEquityLimitDescription(void) 
  {
   return CMessage::Text(MSG_SIGNAL_INFO_EQUITY_LIMIT)+": "+::DoubleToString(this.CurrentEquityLimit(),2)+"%";
  }
//+------------------------------------------------------------------+
//| Return the description of the slippage used to set               |
//| market orders when synchronizing positions and copying deals     |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentSlippageDescription(void) 
  {
   return CMessage::Text(MSG_SIGNAL_INFO_SLIPPAGE)+": "+CMessage::Text(MSG_LIB_TEXT_BAR_SPREAD)+" * "+::DoubleToString(this.CurrentSlippage(),2);
  }
//+------------------------------------------------------------------+
//| Return the description of the limitation by funds for a signal   |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentVolumePercentDescription(void)
  {
   return CMessage::Text(MSG_SIGNAL_INFO_VOLUME_PERCENT)+": "+::DoubleToString(this.CurrentVolumePercent(),2)+"%";
  }
//+------------------------------------------------------------------+
//| Return the description of the flag enabling synchronization      |
//| without confirmation dialog                                      |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentConfirmationsDisableFlagDescription(void) 
  {
   return
     (
      CMessage::Text(MSG_SIGNAL_INFO_CONFIRMATIONS_DISABLED)+": "+
      (this.CurrentConfirmationsDisableFlag() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
     );
  }
//+-------------------------------------------------------------------------+
//| Return the description of the flag of copying Stop Loss and Take Profit |
//+-------------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentSLTPCopyFlagDescription(void) 
  {
   return
     (
      CMessage::Text(MSG_SIGNAL_INFO_COPY_SLTP)+": "+
      (this.CurrentSLTPCopyFlag() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
     );
  }
//+------------------------------------------------------------------+
//| Return the description of the deposit limitations (in %)         |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentDepositPercentDescription(void) 
  {
   return CMessage::Text(MSG_SIGNAL_INFO_DEPOSIT_PERCENT)+": "+(string)this.CurrentDepositPercent()+"%";
  }
//+------------------------------------------------------------------+
//| Return the description of the flag enabling                      |
//| deal copying by subscription                                     |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentSubscriptionEnabledFlagDescription(void) 
  {
   return
     (
      CMessage::Text(MSG_SIGNAL_INFO_SUBSCRIPTION_ENABLED)+": "+
      (this.CurrentSubscriptionEnabledFlag() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
     );
  }
//+------------------------------------------------------------------+
//| Return the signal ID description                                 |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentIDDescription(void)
  {
   return CMessage::Text(MSG_SIGNAL_INFO_ID)+": "+(this.CurrentID()>0 ? (string)this.CurrentID() : CMessage::Text(MSG_LIB_PROP_EMPTY));
  }
//+------------------------------------------------------------------+
//| Return the description of the flag indicating                    |
//| consent to the terms of use of the Signals service               |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentTermsAgreeFlagDescription(void)
  {
   return
     (
      CMessage::Text(MSG_SIGNAL_INFO_TERMS_AGREE)+": "+
      (this.CurrentTermsAgreeFlag() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
     );
  }
//+------------------------------------------------------------------+
//| Return the description of the signal name                        |
//+------------------------------------------------------------------+
string CMQLSignalsCollection::CurrentNameDescription(void)
  {
   return CMessage::Text(MSG_SIGNAL_INFO_NAME)+": "+(this.CurrentName()!="" ? this.CurrentName() : CMessage::Text(MSG_LIB_PROP_EMPTY));
  }
//+------------------------------------------------------------------+

Cada método cria e retorna uma string com o título da descrição do parâmetro e seu valor atual.

Método que exibe no log os parâmetros das configurações para copiar os sinais de negociação:

//+------------------------------------------------------------------+
//| Display the parameters of signal copying settings in the journal |
//+------------------------------------------------------------------+
void CMQLSignalsCollection::CurrentSubscriptionParameters(void)
  {
   ::Print("============= ",CMessage::Text(MSG_SIGNAL_INFO_PARAMETERS)," =============");
   ::Print(this.ProgramIsAllowedDescription());
   ::Print(this.CurrentTermsAgreeFlagDescription());
   ::Print(this.CurrentSubscriptionEnabledFlagDescription());
   ::Print(this.CurrentConfirmationsDisableFlagDescription());
   ::Print(this.CurrentSLTPCopyFlagDescription());
   ::Print(this.CurrentSlippageDescription());
   ::Print(this.CurrentEquityLimitDescription());
   ::Print(this.CurrentDepositPercentDescription());
   ::Print(this.CurrentVolumePercentDescription());
   ::Print(this.CurrentIDDescription());
   ::Print(this.CurrentNameDescription());
   ::Print("");
  }  
//+------------------------------------------------------------------+

Primeiro, o cabeçalho é exibido e, em seguida, um por um, são impressas todas as configurações para copiar os sinais de negociação retornados pelos métodos correspondentes descritos acima.

Assim concluímos a criação da classe-coleção de objetos-sinais mql5.
No futuro, podemos voltar a vê-lo para fazer alterações, mas, por enquanto, não há como saber se vais ser relevante e vamos deixá-lo assim.

Para conectar a coleção de classes de sinais de negociação com o "mundo externo", precisamos adicionar métodos para trabalhar com ela na classe do objeto da biblioteca principal CEngine no arquivo \MQL5\Include\DoEasy\Engine.mqh.

Vamos anexar o arquivo da classe-coleção de sinais de negociação ao arquivo de classe do objeto CEngine e declaramos um objeto da classe-coleção de sinais mql5:

//+------------------------------------------------------------------+
//|                                                       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 "Collections\MQLSignalsCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+
//| 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
   CMQLSignalsCollection m_signals_mql5;                 // Collection of MQL5.com Signals service signals
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CPause               m_pause;                         // Pause object
   CArrayObj            m_list_counters;                 // List of timer counters

Na seção pública da classe declaramos e implementamos novos métodos para trabalhar com a classe-coleção de instantâneos do livro de ofertas e métodos para trabalhar com uma coleção de sinais de negociação:

//--- 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 (1) the DOM series of a specified symbol, the DOM (2) by index and (3) by time in milliseconds
   CMBookSeries        *GetMBookSeries(const string symbol)                            { return this.m_book_series.GetMBookseries(symbol);   }
   CMBookSnapshot      *GetMBook(const string symbol,const int index)                  { return this.m_book_series.GetMBook(symbol,index);   }
   CMBookSnapshot      *GetMBook(const string symbol,const long time_msc)              { return this.m_book_series.GetMBook(symbol,time_msc);}
   
//--- Return the volume of a (1) buy and (2) sell DOM specified by symbol and index
   long                 MBookVolumeBuy(const string symbol,const int index);
   long                 MBookVolumeSell(const string symbol,const int index);
//--- Return the increased precision volume of a (1) buy and (2) sell DOM specified by symbol and index
   double               MBookVolumeBuyReal(const string symbol,const int index);
   double               MBookVolumeSellReal(const string symbol,const int index);
//--- Return the volume of a (1) buy and (2) sell DOM specified by symbol and time in milliseconds
   long                 MBookVolumeBuy(const string symbol,const long time_msc);
   long                 MBookVolumeSell(const string symbol,const long time_msc);
//--- Return the increased precision volume of a (1) buy and (2) sell DOM specified by symbol and time in milliseconds
   double               MBookVolumeBuyReal(const string symbol,const long time_msc);
   double               MBookVolumeSellReal(const string symbol,const long time_msc);
   

//--- Return (1) the collection of mql5.com Signals service signals and (2) the list of signals from the mql5.com Signals service signal collection
   CMQLSignalsCollection *GetSignalsMQL5Collection(void)                               { return &this.m_signals_mql5;                        }
   CArrayObj           *GetListSignalsMQL5(void)                                       { return this.m_signals_mql5.GetList();               }
//--- Return the list of (1) paid and (2) free signals
   CArrayObj           *GetListSignalsMQL5Paid(void)                    { return this.m_signals_mql5.GetList(SIGNAL_MQL5_PROP_PRICE,0,MORE); }
   CArrayObj           *GetListSignalsMQL5Free(void)                    { return this.m_signals_mql5.GetList(SIGNAL_MQL5_PROP_PRICE,0,EQUAL);}

//--- (1) Create and (2) update the collection of mql5.com Signals service signals
   bool                 SignalsMQL5Create(void)                                        { return this.m_signals_mql5.CreateCollection();      } 
   void                 SignalsMQL5Refresh(void)                                       { this.m_signals_mql5.Refresh();                      }
//--- Subscribe to a signal by (1) ID and (2) signal name
   bool                 SignalsMQL5Subscribe(const long signal_id)                     { return this.m_signals_mql5.SubscribeByID(signal_id);}
   bool                 SignalsMQL5Subscribe(const string signal_name)                 { return this.m_signals_mql5.SubscribeByName(signal_name);}
//--- Unsubscribe from the current signal
   bool                 SignalsMQL5Unsubscribe(void)                                   { return this.m_signals_mql5.CurrentUnsubscribe();    }
//--- Return (1) ID and (2) the name of the current signal subscription is performed to
   long                 SignalsMQL5CurrentID(void)                                     { return this.m_signals_mql5.CurrentID();             }
   string               SignalsMQL5CurrentName(void)                                   { return this.m_signals_mql5.CurrentName();           }
     
//--- Set the percentage for converting deal volume
   bool                 SignalsMQL5CurrentSetEquityLimit(const double value)  { return this.m_signals_mql5.CurrentSetEquityLimit(value);     }
//--- Set the market order slippage used when synchronizing positions and copying deals
   bool                 SignalsMQL5CurrentSetSlippage(const double value)     { return this.m_signals_mql5.CurrentSetSlippage(value);        }
//--- Set deposit limitations (in %)
   bool                 SignalsMQL5CurrentSetDepositPercent(const int value)  { return this.m_signals_mql5.CurrentSetDepositPercent(value);  }
//--- Enable synchronization without the confirmation dialog
   bool                 SignalsMQL5CurrentSetConfirmationsDisableON(void)     { return this.m_signals_mql5.CurrentSetConfirmationsDisableON();}
//--- Disable synchronization without the confirmation dialog
   bool                 SignalsMQL5CurrentSetConfirmationsDisableOFF(void)    { return this.m_signals_mql5.CurrentSetConfirmationsDisableOFF();}
//--- Enable copying Stop Loss and Take Profit
   bool                 SignalsMQL5CurrentSetSLTPCopyON(void)                 { return this.m_signals_mql5.CurrentSetSLTPCopyON();           }
//--- Disable copying Stop Loss and Take Profit
   bool                 SignalsMQL5CurrentSetSLTPCopyOFF(void)                { return this.m_signals_mql5.CurrentSetSLTPCopyOFF();          }
//--- Enable copying deals by subscription
   bool                 SignalsMQL5CurrentSetSubscriptionEnableON(void)       { return this.m_signals_mql5.CurrentSetSubscriptionEnableON(); }
//--- Disable copying deals by subscription
   bool                 SignalsMQL5CurrentSetSubscriptionEnableOFF(void)      { return this.m_signals_mql5.CurrentSetSubscriptionEnableOFF();}
   
//--- Display (1) the complete, (2) short collection description in the journal and (3) parameters of the signal copying settings
   void                 SignalsMQL5Print(void)                                         { m_signals_mql5.Print();                             }
   void                 SignalsMQL5PrintShort(const bool list=false,const bool paid=true,const bool free=true)
                                                                                       { m_signals_mql5.PrintShort(list,paid,free);          }
   void                 SignalsMQL5CurrentSubscriptionParameters(void)                 { this.m_signals_mql5.CurrentSubscriptionParameters();}

//--- Return (1) the buffer collection and (2) the buffer list from the collection 

Os métodos implementados retornam o resultado da chamada dos métodos com o mesmo nome das coleções correspondentes.

Vamos considerar a implementação dos métodos que retornam os volumes especificados dos instantâneos dos livros de ofertas especificados:

//+------------------------------------------------------------------+
//| Return the buy volume of a DOM                                   |
//| specified by symbol and index                                    |
//+------------------------------------------------------------------+
long CEngine::MBookVolumeBuy(const string symbol,const int index)
  {
   CMBookSnapshot *mbook=this.GetMBook(symbol,index);
   return(mbook!=NULL ? mbook.VolumeBuy() : 0);
  }
//+------------------------------------------------------------------+
//| Return the sell volume of a DOM                                  |
//| specified by symbol and index                                    |
//+------------------------------------------------------------------+
long CEngine::MBookVolumeSell(const string symbol,const int index)
  {
   CMBookSnapshot *mbook=this.GetMBook(symbol,index);
   return(mbook!=NULL ? mbook.VolumeSell() : 0);
  }
//+------------------------------------------------------------------+
//| Return the extended accuracy buy volume                          |
//| of a DOM specified by symbol and index                           |
//+------------------------------------------------------------------+
double CEngine::MBookVolumeBuyReal(const string symbol,const int index)
  {
   CMBookSnapshot *mbook=this.GetMBook(symbol,index);
   return(mbook!=NULL ? mbook.VolumeBuyReal() : 0);
  }
//+------------------------------------------------------------------+
//| Return the extended accuracy sell volume                         |
//| of a DOM specified by symbol and index                           |
//+------------------------------------------------------------------+
double CEngine::MBookVolumeSellReal(const string symbol,const int index)
  {
   CMBookSnapshot *mbook=this.GetMBook(symbol,index);
   return(mbook!=NULL ? mbook.VolumeSellReal() : 0);
  }
//+------------------------------------------------------------------+
//| Return the buy volume of a DOM                                   |
//| specified by symbol and time in milliseconds                     |
//+------------------------------------------------------------------+
long CEngine::MBookVolumeBuy(const string symbol,const long time_msc)
  {
   CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc);
   return(mbook!=NULL ? mbook.VolumeBuy() : 0);
  }
//+------------------------------------------------------------------+
//| Return the sell volume of a DOM                                  |
//| specified by symbol and time in milliseconds                     |
//+------------------------------------------------------------------+
long CEngine::MBookVolumeSell(const string symbol,const long time_msc)
  {
   CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc);
   return(mbook!=NULL ? mbook.VolumeSell() : 0);
  }
//+------------------------------------------------------------------+
//| Return the extended accuracy buy volume                          |
//| of a DOM specified by symbol and time in milliseconds            |
//+------------------------------------------------------------------+
double CEngine::MBookVolumeBuyReal(const string symbol,const long time_msc)
  {
   CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc);
   return(mbook!=NULL ? mbook.VolumeBuyReal() : 0);
  }
//+------------------------------------------------------------------+
//| Return the extended accuracy sell volume                         |
//| of a DOM specified by symbol and time in milliseconds            |
//+------------------------------------------------------------------+
double CEngine::MBookVolumeSellReal(const string symbol,const long time_msc)
  {
   CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc);
   return(mbook!=NULL ? mbook.VolumeSellReal() : 0);
  }
//+------------------------------------------------------------------+

Tudo é simples. A lógica de todos os métodos é idêntica: primeiro obtemos o objeto-instantâneo do livro de ofertas da lista-coleção segundo símbolo e índice ou tempo em milissegundos usando os métodos GetMBook() implementados anteriormente, depois, retornamos o volume do livro de ofertas que corresponde ao objeto-instantâneo do livro de ofertas obtido, ou zero se o objeto não puder ser obtido.

Todas essas são melhorias e mudanças para hoje.

Teste

Testamos a criação da coleção de Sinais MQL5.com. Vamos testar assim:
Receberemos uma lista completa do banco de dados de Sinais, exibiremos apenas a lista de sinais gratuitos e, em seguida, encontraremos o sinal mais lucrativo e o assinaremos. Se a assinatura for bem-sucedida, exibiremos os parâmetros do sinal assinado e os parâmetros para copiar os sinais de negociação que foram definidos durante a assinatura do sinal. No próximo tick, vamos cancelar a assinatura do sinal assinado.

Para testar, vamos pegar o Expert Advisor do artigo anterior
e o salvamos na nova pasta \MQL5\Experts\TestDoEasy\Part66\ com o novo nome TestDoEasyPart66.mq5.

À lista de parâmetros de entrada do Expert Advisor adicionamos um parâmetro de configuração que permite escolher se desejamos trabalhar com o serviço Sinais MQL5.com no Expert Advisor:

//--- input variables
input    ushort            InpMagic             =  123;  // Magic number
input    double            InpLots              =  0.1;  // Lots
input    uint              InpStopLoss          =  150;  // StopLoss in points
input    uint              InpTakeProfit        =  150;  // TakeProfit in points
input    uint              InpDistance          =  50;   // Pending orders distance (points)
input    uint              InpDistanceSL        =  50;   // StopLimit orders distance (points)
input    uint              InpDistancePReq      =  50;   // Distance for Pending Request's activate (points)
input    uint              InpBarsDelayPReq     =  5;    // Bars delay for Pending Request's activate (current timeframe)
input    uint              InpSlippage          =  5;    // Slippage in points
input    uint              InpSpreadMultiplier  =  1;    // Spread multiplier for adjusting stop-orders by StopLevel
input    uchar             InpTotalAttempts     =  5;    // Number of trading attempts
sinput   double            InpWithdrawal        =  10;   // Withdrawal funds (in tester)

sinput   uint              InpButtShiftX        =  0;    // Buttons X shift 
sinput   uint              InpButtShiftY        =  10;   // Buttons Y shift 

input    uint              InpTrailingStop      =  50;   // Trailing Stop (points)
input    uint              InpTrailingStep      =  20;   // Trailing Step (points)
input    uint              InpTrailingStart     =  0;    // Trailing Start (points)
input    uint              InpStopLossModify    =  20;   // StopLoss for modification (points)
input    uint              InpTakeProfitModify  =  60;   // TakeProfit for modification (points)

sinput   ENUM_SYMBOLS_MODE InpModeUsedSymbols   =  SYMBOLS_MODE_CURRENT;            // Mode of used symbols list
sinput   string            InpUsedSymbols       =  "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY";  // List of used symbols (comma - separator)
sinput   ENUM_TIMEFRAMES_MODE InpModeUsedTFs    =  TIMEFRAMES_MODE_LIST;            // Mode of used timeframes list
sinput   string            InpUsedTFs           =  "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator)

sinput   ENUM_INPUT_YES_NO InpUseBook           =  INPUT_YES;                       // Use Depth of Market
sinput   ENUM_INPUT_YES_NO InpUseMqlSignals     =  INPUT_YES;                       // Use signal service
sinput   ENUM_INPUT_YES_NO InpUseSounds         =  INPUT_YES;                       // Use sounds

//--- global variables

No último artigo, fizemos todas as verificações para trabalhar com sinais no manipulador OnInit() do Expert Advisor. Hoje vamos trabalhar em OnTick().
Por isso, removemos o bloco de código de teste anterior no manipulador OnInit() pois já é desnecessário:

   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);
  }
//+------------------------------------------------------------------+

No manipulador OnTick() escrevemos um novo bloco de código de teste, em que todas as condições de teste especificadas por nós no início desta seção são atendidas:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove EA graphical objects by an object name prefix
   ObjectsDeleteAll(0,prefix);
   Comment("");
//--- Deinitialize library
   engine.OnDeinit();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Handle the NewTick event in the library
   engine.OnTick(rates_data);

//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Working in the timer
      PressButtonsControl();        // Button pressing control
      engine.EventsHandling();      // Working with events
     }

//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing pending orders
     }
   
//--- Search for available signals in the database and check the ability to subscribe to a signal by its name
   static bool done=false;
   //--- If the first launch and working with signals is enabled in EA custom settings
   if(InpUseMqlSignals && !done)
     {
      //--- Display the list of all free signals in the journal
      Print("");
      engine.GetSignalsMQL5Collection().PrintShort(true,false,true);
      //--- Get the list of free signals
      CArrayObj *list=engine.GetListSignalsMQL5Free();
      //--- If the list is obtained
      if(list!=NULL)
        {
         //--- Find a signal with the maximum growth in % in the list
         int index_max_gain=CSelect::FindMQLSignalMax(list,SIGNAL_MQL5_PROP_GAIN);
         CMQLSignal *signal_max_gain=list.At(index_max_gain);
         //--- If the signal is found
         if(signal_max_gain!=NULL)
           {
            //--- Display the full signal description in the journal
            signal_max_gain.Print();
            //--- If managed to subscribe to a signal
            if(engine.SignalsMQL5Subscribe(signal_max_gain.ID()))
              {
               //--- Set subscription parameters
               //--- Enable copying deals by subscription
               engine.SignalsMQL5CurrentSetSubscriptionEnableON();
               //--- Set synchronization without the confirmation dialog
               engine.SignalsMQL5CurrentSetConfirmationsDisableOFF();
               //--- Set copying Stop Loss and Take Profit
               engine.SignalsMQL5CurrentSetSLTPCopyON();
               //--- Set the market order slippage used when synchronizing positions and copying deals
               engine.SignalsMQL5CurrentSetSlippage(2);
               //--- Set the percentage for converting deal volume
               engine.SignalsMQL5CurrentSetEquityLimit(50);
               //--- Set deposit limitations (in %)
               engine.SignalsMQL5CurrentSetDepositPercent(70);
               //--- Display subscription parameters in the journal
               engine.SignalsMQL5CurrentSubscriptionParameters();
              }
           }
        }
      done=true;
      return;
     }
   //--- If a signal subscription is active,  unsubscribe
   if(engine.SignalsMQL5CurrentID()>0)
     {
      engine.SignalsMQL5Unsubscribe();
     }
//---
  }
//+------------------------------------------------------------------+

Todo o novo bloco de código foi comentado em detalhes. Se você tiver alguma dúvida, pode perguntar na discussão do artigo.

Na função de inicialização da biblioteca OnInitDoEasy() escrevemos um bloco de código, em que uma coleção de lista de sinais de negociação é criada e o sinalizador para permitir a cópia de sinais de negociação por assinatura é definido:

//--- 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();
   
//--- Create the collection of mql5.com Signals service signals
//--- If working with signals is enabled and the signal collection is created
   if(InpUseMqlSignals && engine.SignalsMQL5Create())
     {
      //--- Enable copying deals by subscription
      engine.SignalsMQL5CurrentSetSubscriptionEnableON();
      //--- Check created MQL5 signal objects of the Signals service - display the short collection description in the journal
      engine.SignalsMQL5PrintShort();
     }
//--- If working with signals is not enabled or failed to create the signal collection, 
//--- disable copying deals by subscription
   else
      engine.SignalsMQL5CurrentSetSubscriptionEnableOFF();

//--- Create resource text files

Vamos compilar o Expert Advisor e iniciá-lo no gráfico do símbolo, após configurá-lo para o símbolo/timeframe atual e definir o sinalizador indicando necessidade de trabalhar com sinais de negociação do serviço Sinais MQL5.com:


Na guia "Geral" da janela de configurações do Expert Advisor, certifique-se de marcar a caixa "Permitir a alteração das configurações de sinais":


Sem isso, o EA não poderá trabalhar com o serviço Sinais MQL5.com.

Após iniciar o Expert Advisor, uma mensagem sobre a criação bem-sucedida de uma coleção de sinais e sua breve descrição será exibida no log:

Collection of MQL5.com Signals service signals created successfully
Collection of MQL5.com Signals service signals:
- Free signals: 195, Paid signals: 805

A seguir, uma lista completa de sinais gratuitos será exibida. Como são muitos, mostrarei apenas uma parte como exemplo:

Collection of MQL5.com Signals service signals:
- Signal "GBPUSD EXPERT 23233". Author login: mbt_trader, ID 919099, Growth: 3.30, Drawdown: 11.92, Price: 0.00, Subscribers: 0
- Signal "Willian". Author login: Desg, ID 917396, Growth: 12.69, Drawdown: 15.50, Price: 0.00, Subscribers: 0
- Signal "VahidVHZ1366". Author login: 39085485, ID 921427, Growth: 34.36, Drawdown: 12.84, Price: 0.00, Subscribers: 0
- Signal "Vikings". Author login: Myxx, ID 921040, Growth: 7.05, Drawdown: 2.22, Price: 0.00, Subscribers: 2
- Signal "VantageFX Sunphone Dragon". Author login: sunphone, ID 916421, Growth: 537.89, Drawdown: 39.06, Price: 0.00, Subscribers: 21
- Signal "Forex money maker free". Author login: Yggdrasills, ID 916328, Growth: 44.66, Drawdown: 61.15, Price: 0.00, Subscribers: 0
...
...
...
- Signal "Nine Pairs ST". Author login: ebi.pilehvar, ID 935603, Growth: 25.92, Drawdown: 26.41, Price: 0.00, Subscribers: 2
- Signal "FBS140". Author login: mohammeeeedali, ID 949720, Growth: 42.14, Drawdown: 23.11, Price: 0.00, Subscribers: 2
- Signal "StopTheFourthAddition". Author login: pinheirodps, ID 934990, Growth: 41.78, Drawdown: 28.03, Price: 0.00, Subscribers: 2
- Signal "The art of Forex". Author login: Myxx, ID 801685, Growth: 196.39, Drawdown: 40.95, Price: 0.00, Subscribers: 59
- Signal "Bongsanmaskdance1803". Author login: kim25801863, ID 936062, Growth: 12.53, Drawdown: 10.31, Price: 0.00, Subscribers: 0
- Signal "Prospector Scalper EA". Author login: robots4forex, ID 435626, Growth: 334.76, Drawdown: 43.93, Price: 0.00, Subscribers: 215
- Signal "ADS MT5". Author login: vluxus, ID 478235, Growth: 295.68, Drawdown: 40.26, Price: 0.00, Subscribers: 92

Em seguida, exibiremos uma descrição completa do sinal encontrado com o aumento máximo em porcentagem e mensagem sobre a assinatura bem-sucedida:

============= Beginning of parameter list (Signal from the MQL5.com Signal service) =============
Account type: Demo
Publication date: 2020.07.02 16:29
Monitoring start date: 2020.07.02 16:29
Date of the latest update of the trading statistics: 2021.03.07 15:11
ID: 784584
Trading account leverage: 33
Trading result in pips: -19248988
Position in the Rating of Signals: 872
Number of subscribers: 6
Number of trades: 1825
Status of account subscription to a signal: No
------
Account balance: 12061.98
Account equity: 12590.32
Account growth in %: 1115.93
Maximum drawdown: 70.62
Signal subscription price: 0.00
Signal ROI (Return on Investment) in %: 1169.19
------
Author login: "tradewai.com"
Broker (company) name: "MetaQuotes Software Corp."
Broker server: "MetaQuotes-Demo"
Name: "Tradewai"
Account currency: "USD"
============= End of parameter list (Signal from the MQL5.com Signal service) =============

Subscribed to signal ID 784584 "Tradewai"

Depois disso, os parâmetros de assinatura serão exibidos:

============= Signal copying parameters =============
Allow using signals for program: Yes
Agree to the terms of use of the Signals service: Yes
Enable copying deals by subscription: Yes
Enable synchronization without confirmation dialog: No
Copying Stop Loss and Take Profit: Yes
Market order slippage when synchronizing positions and copying deals: Spread * 2.00
Percentage for converting deal volume: 50.00%
Limit by deposit: 70%
Limitation on signal equity: 7.00%
Signal ID: 784584
Signal name: Tradewai

e quando chegar o próximo tick, receberemos uma mensagem sobre um cancelamento bem-sucedido do sinal.

Unsubscribed from the signal ID 784584 "Tradewai"


O que vem agora?

No próximo artigo, começaremos a desenvolver a funcionalidade de biblioteca para trabalhar com gráficos - gráficos de símbolos.

Todos os arquivos da versão atual da biblioteca e o arquivo do EA de teste para MQL5 estão anexados abaixo. Você pode baixá-los e testar tudo sozinho.
Gostaria de ressaltar que o teste para trabalhar com sinais num Expert Advisor de teste é precisamente um teste para trabalhar com sinais e não é apropriado para ser usado na forma em que é implementado no Expert Advisor anexo. Veja que este é apenas um exemplo sem nenhuma carga útil.
O exemplo dado no Expert Advisor apenas permite compreender o que deve ser feito ao implementar seus próprios desenvolvimentos com base na biblioteca e suas classes para trabalhar com o serviço Sinais MQL5.com.

Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.

Complementos

*Artigos desta série:

Trabalhando com preços na biblioteca DoEasy (Parte 62): atualização em tempo real da série de ticks, preparação para trabalhar com o livro de ofertas Trabalhando preços na biblioteca DoEasy (Parte 63): livro de ofertas, classe de ordem abstrata do livro de ofertas
Trabalhando com preços na biblioteca DoEasy (Parte 64): livro de ofertas, classes do objeto-instantâneo e objeto-série de instantâneos do livro de ofertas
Trabalhando com preços e sinais na biblioteca DoEasy (Parte 65): coleção de livros de ofertas e classe para trabalhar com sinais MQL5.com

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/9146

Arquivos anexados |
MQL5.zip (3917.08 KB)
Redes Neurais de Maneira Fácil (Parte 12): Dropout Redes Neurais de Maneira Fácil (Parte 12): Dropout
Como a próxima etapa no estudo das redes neurais, eu sugiro considerar os métodos de aumentar a convergência durante o treinamento da rede neural. Existem vários desses métodos. Neste artigo, nós consideraremos um deles intitulado Dropout.
Trabalhando com preços e sinais na biblioteca DoEasy (Parte 65): coleção de livros de ofertas e classe para trabalhar com sinais MQL5.com Trabalhando com preços e sinais na biblioteca DoEasy (Parte 65): coleção de livros de ofertas e classe para trabalhar com sinais MQL5.com
Neste artigo, criaremos uma classe-coleção de livros de ofertas para todos os símbolos e começaremos a desenvolver a funcionalidade para trabalhar com o serviço de sinais MQL5.com - criaremos uma classe objeto-sinal.
Aprendizado de máquina em sistemas de negociação baseados em grade e martingale. Deveríamos apostar nele? Aprendizado de máquina em sistemas de negociação baseados em grade e martingale. Deveríamos apostar nele?
Este artigo apresentará ao leitor a técnica de aprendizado de máquina para negociação baseada em grade e martingale. Para minha surpresa, essa abordagem, por algum motivo, não é afetada de forma alguma na rede global. Após ler o artigo, podemos criar nossos próprios bots.
Técnicas úteis e exóticas para a negociação automatizada Técnicas úteis e exóticas para a negociação automatizada
Neste artigo, eu demonstrarei algumas técnicas muito interessantes e úteis para a negociação automatizada. Algumas delas podem ser familiares para você. Eu tentarei cobrir os métodos mais interessantes e explicarei por que vale a pena usá-los. Além disso, eu mostrarei o que essas técnicas podem fazer na prática. Nós criaremos Expert Advisors e testaremos todas as técnicas descritas usando as cotações históricas.