Outras classes na biblioteca DoEasy (Parte 66): classe-coleção de Sinais MQL5.com
Sumário
- Ideia
- Aprimorando as classes da biblioteca
- Classe-coleção de objetos-sinais mql5
- Teste
- O que vem agora?
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.
*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
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso