Прочие классы в библиотеке DoEasy (Часть 66): Класс-коллекция Сигналов MQL5.com
Artyom Trishkin | 12 марта, 2021
Содержание
- Концепция
- Доработка классов библиотеки
- Класс-коллекция объектов-mql5-сигналов
- Тестирование
- Что дальше
Концепция
В прошлой статье мы создали класс объекта-сигнала, представляющего собой один сигнал из множества, транслируемых в сервисе Сигналы MQL5.com.
Сегодня создадим класс-коллекцию сигналов, доступных в базе сигналов, и которые можно получить при помощи функции SignalBaseSelect(), указав индекс нужного нам сигнала.
Коллекция будет позволять хранить все имеющиеся в базе сигналы в удобном для поиска и сортировки списке. Мы сможем находить и возвращать списки сигналов по их различным свойствам, например, сможем получить списки только бесплатных, или наоборот — только платных сигналов, отсортировать их по одному из параметров, например, по прибыльности сигнала, либо сразу получить индекс сигнала в списке с параметром равным, больше, или меньше заданного значения. Зная заранее имя нужного нам сигнала, мы сможем быстро найти его в коллекции для дальнейшей с ним работы.
В классе-коллекции организуем возможность подписаться на выбранный в коллекции сигнал, либо отписаться от сигнала, на который уже осуществлена подписка на текущем аккаунте.
Помимо работы с объектами сервиса Сигналов MQL5.com доработаем класс объекта-снимка стакана цен — добавим в него дополнительные свойства, позволяющие сразу же при создании объекта-снимка стакана цен рассчитать по отдельности объёмы заявок на покупку и на продажу. Это избавит конечного пользователя от проведения дополнительных расчётов при работе со стаканом цен — мы сразу же будем знать совокупные объёмы каждого снимка стакана цен — как на покупку, так и на продажу, что позволит не прибегать к дополнительному поиску заявок Buy и Sell в стакане цен с последующим суммированием их объёмов при создании стратегий с использованием стакана цен и его объёмов.
Доработка классов библиотеки
Как уже сложилось, сразу же добавим все новые сообщения библиотеки в файл \MQL5\Include\DoEasy\Data.mqh.
Сначала впишем индексы новых сообщений:
//--- CMarketBookSnapshot MSG_MBOOK_SNAP_TEXT_SNAPSHOT, // Снимок стакана цен MSG_MBOOK_SNAP_VOLUME_BUY, // Объём на покупку MSG_MBOOK_SNAP_VOLUME_SELL, // Объём на продажу //--- CMBookSeries MSG_MBOOK_SERIES_TEXT_MBOOKSERIES, // Серия снимков стакана цен MSG_MBOOK_SERIES_ERR_ADD_TO_LIST, // Ошибка. Не удалось добавить серию снимков стакана цен в список
...
//--- CMQLSignal MSG_SIGNAL_MQL5_TEXT_SIGNAL, // Сигнал MSG_SIGNAL_MQL5_TEXT_SIGNAL_MQL5, // Сигнал сервиса сигналов mql5.com MSG_SIGNAL_MQL5_TRADE_MODE, // Тип счета MSG_SIGNAL_MQL5_DATE_PUBLISHED, // Дата публикации MSG_SIGNAL_MQL5_DATE_STARTED, // Дата начала мониторинга MSG_SIGNAL_MQL5_DATE_UPDATED, // Дата последнего обновления торговой статистики MSG_SIGNAL_MQL5_ID, // ID MSG_SIGNAL_MQL5_LEVERAGE, // Плечо торгового счета MSG_SIGNAL_MQL5_PIPS, // Результат торговли в пипсах MSG_SIGNAL_MQL5_RATING, // Позиция в рейтинге сигналов MSG_SIGNAL_MQL5_SUBSCRIBERS, // Количество подписчиков MSG_SIGNAL_MQL5_TRADES, // Количество трейдов MSG_SIGNAL_MQL5_SUBSCRIPTION_STATUS, // Состояние подписки счёта на этот сигнал MSG_SIGNAL_MQL5_EQUITY, // Средства на счете MSG_SIGNAL_MQL5_GAIN, // Прирост счета в процентах MSG_SIGNAL_MQL5_MAX_DRAWDOWN, // Максимальная просадка MSG_SIGNAL_MQL5_PRICE, // Цена подписки на сигнал MSG_SIGNAL_MQL5_ROI, // Значение ROI (Return on Investment) сигнала в % MSG_SIGNAL_MQL5_AUTHOR_LOGIN, // Логин автора MSG_SIGNAL_MQL5_BROKER, // Наименование брокера (компании) MSG_SIGNAL_MQL5_BROKER_SERVER, // Сервер брокера MSG_SIGNAL_MQL5_NAME, // Имя MSG_SIGNAL_MQL5_CURRENCY, // Валюта счета MSG_SIGNAL_MQL5_TEXT_GAIN, // Прирост MSG_SIGNAL_MQL5_TEXT_DRAWDOWN, // Просадка MSG_SIGNAL_MQL5_TEXT_SUBSCRIBERS, // Подписчиков //--- CMQLSignalsCollection MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION, // Коллекция сигналов сервиса сигналов mql5.com MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_PAID, // Платных сигналов MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_FREE, // Бесплатных сигналов MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_NEW, // Новый сигнал добавлен в коллекцию MSG_MQLSIG_COLLECTION_ERR_FAILED_GET_SIGNAL, // Не удалось получить сигнал из коллекции MSG_SIGNAL_INFO_PARAMETERS, // Параметры копирования сигнала MSG_SIGNAL_INFO_EQUITY_LIMIT, // Процент для конвертации объема сделки MSG_SIGNAL_INFO_SLIPPAGE, // Проскальзывание, с которым выставляются рыночные ордера при синхронизации позиций и копировании сделок MSG_SIGNAL_INFO_VOLUME_PERCENT, // Ограничение по средствам для сигнала MSG_SIGNAL_INFO_CONFIRMATIONS_DISABLED, // Разрешение синхронизации без показа диалога подтверждения MSG_SIGNAL_INFO_COPY_SLTP, // Копирование Stop Loss и Take Profit MSG_SIGNAL_INFO_DEPOSIT_PERCENT, // Ограничение по депозиту MSG_SIGNAL_INFO_ID, // Идентификатор сигнала MSG_SIGNAL_INFO_SUBSCRIPTION_ENABLED, // Разрешение на копирование сделок по подписке MSG_SIGNAL_INFO_TERMS_AGREE, // Согласие с условиями использования сервиса "Сигналы" MSG_SIGNAL_INFO_NAME, // Имя сигнала MSG_SIGNAL_INFO_SIGNALS_PERMISSION, // Разрешение на работу с сигналами для программы MSG_SIGNAL_INFO_TEXT_SIGNAL_SUBSCRIBED, // Осуществлена подписка на сигнал MSG_SIGNAL_INFO_TEXT_SIGNAL_UNSUBSCRIBED, // Осуществлена отписка от сигнала MSG_SIGNAL_INFO_ERR_SIGNAL_NOT_ALLOWED, // Работа с сервисом сигналов для программы не разрешена MSG_SIGNAL_INFO_TEXT_CHECK_SETTINGS, // Пожалуйста, проверьте настройки программы (Общие-->Разрешить изменение настроек Сигналов) }; //+------------------------------------------------------------------+
Затем впишем тексты сообщений, соответствующие вновь добавленным индексам:
//--- 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)" }, }; //+---------------------------------------------------------------------+
Часто при выводе сообщений в журнал, особенно отладочных, в начале сообщения мы указываем название метода, из которого было послано это сообщение. В статье 19 нами был создан класс сообщений библиотеки. Но пока мы не очень активно его используем — только указываем индексы сообщений, которые необходимо вывести в журнал при помощи стандартной функции Print(). Так как в скором времени мы начнём новый раздел библиотеки по работе с графикой, то постепенно будем переходить на работу с этим классом для вывода сообщений из библиотеки. Сегодня добавим в него перегрузку метода ToLog(), чтобы можно было дополнительно передать в метод "источник" сообщения — тот метод класса или функцию программы, из которой был вызван этот метод. Таким образом, у нас будут два варианта метода ToLog(), позволяющие выводить сообщения с указанием его исходной функции или метода и без такого указания.
Откроем файл \MQL5\Include\DoEasy\Services\Message.mqh и допишем в него объявление перегруженного метода:
//--- (1,2) выводит сообщение в журнал по идентификатору, (3) на e-mail, (4) на мобильное устройство 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) отправляет файл на FTP, (2) возвращает код ошибки static bool ToFTP(const string filename,const string ftp_path=NULL); static int GetError(void) { return CMessage::m_global_error; }
За пределами тела класса напишем его реализацию:
//+------------------------------------------------------------------+ //| Выводит сообщение в журнал по идентификатору сообщения | //+------------------------------------------------------------------+ 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))); } //+------------------------------------------------------------------+ //| Выводит сообщение в журнал по идентификатору сообщения | //+------------------------------------------------------------------+ 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))); } //+------------------------------------------------------------------+
Как видим, в отличии от первой формы вызова метода во второй его форме добавлен ещё один входной параметр, в котором и будет передаваться наименование метода или функции, из которых был вызван метод ToLog(), и которое будет выводиться в журнал перед сообщением.
К данному классу мы ещё вернёмся в последующих статьях для внесения в него доработок при переводе всех классов библиотеки на вывод сообщений посредством этого класса.
Доработаем класс CMBookSnapshot в файле \MQL5\Include\DoEasy\Objects\Book\MarketBookSnapshot.mqh.
В приватной секции класса впишем переменные-члены класса для хранения совокупных объёмов снимка стакана цен на покупку и продажу:
//+------------------------------------------------------------------+ //| Класс "Снимок стакана цен" | //+------------------------------------------------------------------+ class CMBookSnapshot : public CBaseObj { private: string m_symbol; // Символ long m_time; // Время снимка int m_digits; // Digits символа long m_volume_buy; // Объём стакана на покупку long m_volume_sell; // Объём стакана на продажу double m_volume_buy_real; // Объём стакана на покупку с повышенной точностью double m_volume_sell_real; // Объём стакана на продажу с повышенной точностью CArrayObj m_list; // Список объектов-заявок стакана цен public:
В разделе методов упрощённого доступа к свойствам объекта-снимка стакана цен публичной секции класса впишем методы, возвращающие эти вновь добавленные свойства класса, и методы для вывода их описания:
//+------------------------------------------------------------------+ //|Методы упрощённого доступа к свойствам объекта-снимка стакана цен | //+------------------------------------------------------------------+ //--- Устанавливает (1) символ, (2) время снимка стакана цен, (3) указанное время всем заявкам в стакане 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); //--- Возвращает (1) символ стакана цен, (2) Digits символа, (3) время снимка //--- объём снимка стакана цен (4) на покупку, (5) на продажу, //--- с повышенной точностью (6) на покупку, (7) на продажу, 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; } //--- Возвращает описание объёма стакана (1) на покупку, (2) на продажу string VolumeBuyDescription(void); string VolumeSellDescription(void); }; //+------------------------------------------------------------------+
При создании нового объекта-снимка стакана цен мы просматриваем в цикле все его заявки, создаём объекты этих заявок и складываем их в список. Сейчас нам нужно в конструкторе класса учитывать типы заявок и сразу же прибавлять к значению переменных, хранящих совокупный объём заявок на покупку и продажу, объём текущей заявки в зависимости от её типа. Таким образом, каждая переменная в итоге будет хранить общий объём заявок либо на покупку, либо на продажу сразу же при создании объекта-снимка стакана цен.
Впишем эти доработки в параметрический конструктор класса:
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time) { //--- Устанавливаем символ this.SetSymbol(symbol); //--- Очищаем список this.m_list.Clear(); //--- В цикле по массиву структур 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++) { //--- Создаём объекты-заявки текущего снимка стакана цен в зависимости от типа заявки 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; //--- Устанавливаем заявке время снимка стакана цен mbook_ord.SetTime(this.m_time); //--- Ставим списку флаг сортированного списка (по значению цены) и добавляем в него текущий объект-заявку //--- Если не удалось добавить объект в список заявок стакана цен - удаляем объект-заявку this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE); if(!this.m_list.InsertSort(mbook_ord)) delete mbook_ord; //--- Если объект-заявка успешно добавлен в список заявок снимка стакана - дополняем общие объёмы снимка 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; } } } } //+------------------------------------------------------------------+
Здесь: в первую очередь инициализируем переменные, хранящие общие объёмы всех заявок снимка стакана цен на покупку и продажу. Затем — уже в теле цикла по всем заявкам стакана, в зависимости от типа заявки, добавляем к переменным, хранящим общие объёмы, и соответствующие типу заявки, объём текущей заявки. Таким образом, у нас по окончании цикла по всем заявкам снимка стакана цен во всех переменных будут храниться совокупные объёмы на покупку и на продажу.
В методы, выводящие в журнал краткое описание объекта и все свойства объекта, добавим вывод общих объёмов на покупку и продажу:
//+------------------------------------------------------------------+ //| Выводит в журнал краткое описание объекта | //+------------------------------------------------------------------+ 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),")"); } //+------------------------------------------------------------------+ //| Выводит в журнал свойства объекта | //+------------------------------------------------------------------+ 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()); } } //+------------------------------------------------------------------+
За пределами тела класса напишем реализацию двух новых методов, возвращающих описания объёмов стакана на покупку и на продажу:
//+------------------------------------------------------------------+ //| Возвращает описание объёма стакана на покупку | //+------------------------------------------------------------------+ string CMBookSnapshot::VolumeBuyDescription(void) { return(CMessage::Text(MSG_MBOOK_SNAP_VOLUME_BUY)+": "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy())); } //+------------------------------------------------------------------+ //| Возвращает описание объёма стакана на продажу | //+------------------------------------------------------------------+ string CMBookSnapshot::VolumeSellDescription(void) { return(CMessage::Text(MSG_MBOOK_SNAP_VOLUME_SELL)+": "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell())); } //+------------------------------------------------------------------+
В обоих методах проверяется значение объёма с повышенной точностью и, если значение больше нуля, то возвращается заголовок + это значение объёма (вещественное), иначе — целочисленное значение.
В класс-коллекцию серий снимков стаканов цен CMBookSeriesCollection в файле \MQL5\Include\DoEasy\Collections\BookSeriesCollection.mqh,
в его публичную секцию добавим методы, возвращающие списки по указанным критериям свойств объектов списка:
public: //--- Возвращает (1) себя, (2) список-коллекцию серий стаканов цен CMBookSeriesCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } //--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию 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); } //--- Возвращает количество серий стаканов цен в списке int DataTotal(void) const { return this.m_list.Total(); } //--- Возвращает указатель на объект серий стаканов цен (1) по символу, (2) по индексу в списке
В классе объекта-заявки стакана цен CMQLSignal в файле \MQL5\Include\DoEasy\Objects\MQLSignalBase\MQLSignal.mqh дополним метод PrintShort() входным значением-флагом, указывающим на необходимость вывода дефиса перед описанием объекта:
//--- Выводит в журнал описание свойств объекта (full_prop=true - все свойства, false - только поддерживаемые) void Print(const bool full_prop=false); //--- Выводит в журнал краткое описание объекта virtual void PrintShort(const bool dash=false); //--- Возвращает краткое наименование объекта virtual string Header(const bool shrt=false);
И внесём правки в тело метода:
//+------------------------------------------------------------------+ //| Выводит в журнал краткое описание объекта | //+------------------------------------------------------------------+ 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() ); } //+------------------------------------------------------------------+
Здесь: в зависимости от переданного значения перед описанием объекта выводится дефис либо не выводится, и в самом конце описания добавлено значение о количестве подписчиков на сигнал.
В самом конце тела класса впишем новый метод, осуществляющий подписку на сигнал, описываемый данным объектом:
//--- Возвращает наименование типа счёта string TradeModeDescription(void); //--- Осуществляет подписку на сигнал bool Subscribe(void) { return ::SignalSubscribe(this.ID()); } }; //+------------------------------------------------------------------+
В конструкторе класса поправим инициализацию переменной, хранящей статус подписки на сигнал:
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+
Ранее она инициализировалась значением false. Теперь же мы будем её инициализировать результатом сравнения идентификатора объекта-сигнала с идентификатором текущего подписанного сигнала. Если на какой-либо сигнал есть подписка, то этот сигнал будет "текущим подписанным", и он будет иметь идентификатор сигнала из базы Сигналов MQL5.com, которые мы и сравниваем. Если они равны, то значит на этот сигнал оформлена подписка — результат сравнения будет равен true, иначе — false.
Так как сегодня создаём новую коллекцию, то нам необходимо определить для неё свой идентификатор. В файле \MQL5\Include\DoEasy\Defines.mqh впишем идентификатор коллекции сигналов сервиса Сигналы MQL5.com:
//--- Идентификаторы списков коллекций #define COLLECTION_HISTORY_ID (0x777A) // Идентификатор списка исторической коллекции #define COLLECTION_MARKET_ID (0x777B) // Идентификатор списка рыночной коллекции #define COLLECTION_EVENTS_ID (0x777C) // Идентификатор списка коллекции событий #define COLLECTION_ACCOUNT_ID (0x777D) // Идентификатор списка коллекции аккаунтов #define COLLECTION_SYMBOLS_ID (0x777E) // Идентификатор списка коллекции символов #define COLLECTION_SERIES_ID (0x777F) // Идентификатор списка коллекции таймсерий #define COLLECTION_BUFFERS_ID (0x7780) // Идентификатор списка коллекции индикаторных буферов #define COLLECTION_INDICATORS_ID (0x7781) // Идентификатор списка коллекции индикаторов #define COLLECTION_INDICATORS_DATA_ID (0x7782) // Идентификатор списка коллекции индикаторных данных #define COLLECTION_TICKSERIES_ID (0x7783) // Идентификатор списка коллекции тиковых серий #define COLLECTION_MBOOKSERIES_ID (0x7784) // Идентификатор списка коллекции серий стаканов цен #define COLLECTION_MQL5_SIGNALS_ID (0x7785) // Идентификатор списка коллекции mql5-сигналов //--- Параметры данных для файловых операций
Для полноценной работы с коллекцией сигналов сервиса Сигналы MQL5.com нам нужно создать методы для поиска и сортировки по свойствам объектов-сигналов. Для каждой коллекции мы создаём свои методы поиска и сортировки. Все они идентичны друг другу и описывались нами подробно в третьей статье описания библиотеки.
В файле класса CSelect для поиска и сортировки, находящегося в расположении \MQL5\Include\DoEasy\Services\Select.mqh,
подключим файл класса объекта-mql5-сигнала и объявим новые методы для работы с коллекцией объектов-сигналов:
//+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #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" //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| Методы работы с данными mql5-сигналов | //+------------------------------------------------------------------+ //--- Возвращает список mql5-сигналов, где одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию 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); //--- Возвращает индекс mql5-сигнала в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства 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); //--- Возвращает индекс mql5-сигнала в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства 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); //--- }; //+------------------------------------------------------------------+
За пределами тела класса напишем их реализацию:
//+------------------------------------------------------------------+ //| Методы работы с данными mql5-сигналов | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Возвращает список mql5-сигналов, где одно из целочисленных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает список mql5-сигналов, где одно из вещественных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает список mql5-сигналов, где одно из строковых | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает индекс mql5-сигнала в списке | //| с максимальным значением целочисленного свойства | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает индекс mql5-сигнала в списке | //| с максимальным значением вещественного свойства | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает индекс mql5-сигнала в списке | //| с максимальным значением строкового свойства | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает индекс mql5-сигнала в списке | //| с минимальным значением целочисленного свойства | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает индекс mql5-сигнала в списке | //| с минимальным значением вещественного свойства | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Возвращает индекс mql5-сигнала в списке | //| с минимальным значением строкового свойства | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+
Как уже не раз упоминалось, все эти методы, идентичные для каждого из классов объектов библиотеки, рассматривались нами не раз, и можно ещё раз ознакомиться с подробными пояснениями их работы в статье №3.
У нас всё готово для создания класса-коллекции объектов-сигналов сервиса Сигналы MQL5.com.
Класс-коллекция объектов-mql5-сигналов
В каталоге библиотеки\MQL5\Include\DoEasy\Collections\ создадим новый класс CMQLSignalsCollection в файле MQLSignalsCollection.mqh.
К файлу класса подключим все файлы классов, необходимых для его работы:
//+------------------------------------------------------------------+ //| MQLSignalsCollection.mqh | //| Copyright 2021, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\MQLSignalBase\MQLSignal.mqh" //+------------------------------------------------------------------+
Класс должен быть унаследован от базового объекта всех объектов библиотеки:
//+------------------------------------------------------------------+ //| Коллекция объектов-mql5-сигналов | //+------------------------------------------------------------------+ class CMQLSignalsCollection : public CBaseObj { }
Рассмотрим тело класса целиком, а затем разберём составляющие его методы:
//+------------------------------------------------------------------+ //| MQLSignalsCollection.mqh | //| Copyright 2021, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\MQLSignalBase\MQLSignal.mqh" //+------------------------------------------------------------------+ //| Коллекция объектов-mql5-сигналов | //+------------------------------------------------------------------+ class CMQLSignalsCollection : public CBaseObj { private: CListObj m_list; // Список объектов-mql5-сигналов int m_signals_base_total; // Количество сигналов в базе сигналов mql5 //--- Осуществляет подписку на сигнал bool Subscribe(const long signal_id); //--- Устанавливает флаг разрешения синхронизации без показа диалога подтверждения bool CurrentSetConfirmationsDisableFlag(const bool flag); //--- Устанавливает флаг копирования Stop Loss и Take Profit bool CurrentSetSLTPCopyFlag(const bool flag); //--- Устанавливает флаг разрешения на копирование сделок по подписке bool CurrentSetSubscriptionEnabledFlag(const bool flag); public: //--- Возвращает (1) себя, (2) список-коллекцию серий стаканов цен CMQLSignalsCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } //--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию 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); } //--- Возвращает количество объектов-mql5-сигналов в списке int DataTotal(void) const { return this.m_list.Total(); } //--- Возвращает указатель на объект-mql5-сигнал (1) по идентификатору, (2) по имени, (3) по индексу в списке CMQLSignal *GetMQLSignal(const long id); CMQLSignal *GetMQLSignal(const string name); CMQLSignal *GetMQLSignal(const int index) { return this.m_list.At(index); } //--- Создаёт список-коллекцию объектов-mql5-сигналов bool CreateCollection(void); //--- Обновляет список-коллекцию объектов-mql5-сигналов void Refresh(const bool messages=true); //--- Выводит в журнал (1) полное, (2) краткое описание коллекции void Print(void); void PrintShort(const bool list=false,const bool paid=true,const bool free=true); //--- Конструктор CMQLSignalsCollection(); //--- Осуществляет подписку на сигнал по (1) идентификатору, (2) имени сигнала bool SubscribeByID(const long signal_id); bool SubscribeByName(const string signal_name); //+------------------------------------------------------------------+ //| Методы работы с текущим сигналом, на который оформлена подписка | //+------------------------------------------------------------------+ //--- Возвращает флаг разрешения работы с сервисом сигналов bool ProgramIsAllowed(void) { return (bool)::MQLInfoInteger(MQL_SIGNALS_ALLOWED); } //--- Осуществляет отписку от текущего подписанного сигнала bool CurrentUnsubscribe(void); //--- Устанавливает процент для конвертации объема сделки bool CurrentSetEquityLimit(const double value); //--- Устанавливает величину проскальзывания, с которой выставляются рыночные ордера при синхронизации позиций и копировании сделок bool CurrentSetSlippage(const double value); //--- Устанавливает ограничения по депозиту (в %) bool CurrentSetDepositPercent(const int value); //--- Возвращает процент для конвертации объема сделки double CurrentEquityLimit(void) { return ::SignalInfoGetDouble(SIGNAL_INFO_EQUITY_LIMIT); } //--- Возвращает величину проскальзывания, с которой выставляются рыночные ордера при синхронизации позиций и копировании сделок double CurrentSlippage(void) { return ::SignalInfoGetDouble(SIGNAL_INFO_SLIPPAGE); } //--- Возвращает флаг разрешения синхронизации без показа диалога подтверждения bool CurrentConfirmationsDisableFlag(void) { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_CONFIRMATIONS_DISABLED); } //--- Возвращает флаг копирования Stop Loss и Take Profit bool CurrentSLTPCopyFlag(void) { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_COPY_SLTP); } //--- Возвращает ограничения по депозиту (в %) int CurrentDepositPercent(void) { return (int)::SignalInfoGetInteger(SIGNAL_INFO_DEPOSIT_PERCENT); } //--- Возвращает флаг разрешения на копирование сделок по подписке bool CurrentSubscriptionEnabledFlag(void) { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_SUBSCRIPTION_ENABLED); } //--- Возвращает значение ограничения по средствам для сигнала double CurrentVolumePercent(void) { return ::SignalInfoGetDouble(SIGNAL_INFO_VOLUME_PERCENT); } //--- Возвращает id сигнала long CurrentID(void) { return ::SignalInfoGetInteger(SIGNAL_INFO_ID); } //--- Возвращает флаг согласия с условиями использования сервиса "Сигналы" bool CurrentTermsAgreeFlag(void) { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_TERMS_AGREE); } //--- Возвращает имя сигнала string CurrentName(void) { return ::SignalInfoGetString(SIGNAL_INFO_NAME); } //--- Устанавливает разрешение синхронизации без показа диалога подтверждения bool CurrentSetConfirmationsDisableON(void) { return this.CurrentSetConfirmationsDisableFlag(true); } //--- Устанавливает запрет синхронизации без показа диалога подтверждения bool CurrentSetConfirmationsDisableOFF(void){ return this.CurrentSetConfirmationsDisableFlag(false); } //--- Устанавливает разрешение копирования Stop Loss и Take Profit bool CurrentSetSLTPCopyON(void) { return this.CurrentSetSLTPCopyFlag(true); } //--- Устанавливает запрет копирования Stop Loss и Take Profit bool CurrentSetSLTPCopyOFF(void) { return this.CurrentSetSLTPCopyFlag(false); } //--- Устанавливает разрешение на копирование сделок по подписке bool CurrentSetSubscriptionEnableON(void) { return this.CurrentSetSubscriptionEnabledFlag(true); } //--- Устанавливает запрет на копирование сделок по подписке bool CurrentSetSubscriptionEnableOFF(void) { return this.CurrentSetSubscriptionEnabledFlag(false); } //--- Возвращает описание разрешения работы с сигналами для данной запущенной программы string ProgramIsAllowedDescription(void); //--- Возвращает описание процента для конвертации объема сделки string CurrentEquityLimitDescription(void); //--- Возвращает описание проскальзывания, с которой выставляются рыночные ордера при синхронизации позиций и копировании сделок string CurrentSlippageDescription(void); //--- Возвращает описание значения ограничения по средствам для сигнала string CurrentVolumePercentDescription(void); //--- Возвращает описание флага разрешения синхронизации без показа диалога подтверждения string CurrentConfirmationsDisableFlagDescription(void); //--- Возвращает описание флага копирования Stop Loss и Take Profit string CurrentSLTPCopyFlagDescription(void); //--- Возвращает описание ограничения по депозиту (в %) string CurrentDepositPercentDescription(void); //--- Возвращает описание флага разрешения на копирование сделок по подписке string CurrentSubscriptionEnabledFlagDescription(void); //--- Возвращает описание id сигнала string CurrentIDDescription(void); //--- Возвращает описание флага согласия с условиями использования сервиса "Сигналы" string CurrentTermsAgreeFlagDescription(void); //--- Возвращает описание имени сигнала string CurrentNameDescription(void); //--- Выводит в журнал параметры настроек копирования сигналов void CurrentSubscriptionParameters(void); //--- }; //+------------------------------------------------------------------+
В приватной секции класса расположен объект списка, в котором будем хранить объекты-mql5-сигналы, и вспомогательные переменные и методы.
В публичной секции класса расположены стандартные для всех объектов библиотеки методы работы со списком-коллекцией объектов и два метода для подписки на выбранный сигнал по его идентификатору и имени. Также в публичной секции класса расположены методы для работы с текущим сигналом, на который оформлена подписка.
Разберём реализацию некоторых методов.
В конструкторе класса очищаем список-коллекцию, устанавливаем для него флаг сортированного списка, устанавливаем списку идентификатор коллекции объектов-mql5-сигналов, записываем общее количество сигналов в базе Сигналов MQL5.com и вызываем метод создания коллекции.
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ 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(); } //+------------------------------------------------------------------+
Так как список сигналов мы не будем автоматически обновлять и контролировать средствами библиотеки, то нам в принципе достаточно создать только метод обновления списка, в котором будут считываться все имеющиеся в базе сигналы и записываться в список-коллекцию. Пользователю самому необходимо будет вызывать метод обновления Refresh() перед получением каких-либо данных из коллекции и при желании иметь свежий список сигналов из базы Сигналов MQL5.com. Но метод создания коллекции у нас тоже будет — лишь в качестве совместимости с набором типичных методов коллекций библиотеки. В самом же методе просто будет очищаться список и вызываться метод обновления коллекции. После первого вызова метода Refresh() из метода создания коллекции список-коллекция будет заполнен, и далее можно уже работать с этим списком. При необходимости обновить список-коллекцию в поисках возможных новых сигналов нужно просто вызвать метод Refresh() перед обращением к списку-коллекции.
Метод создания коллекции:
//+------------------------------------------------------------------+ //| Создаёт список-коллекцию объектов-mql5-сигналов | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+
Здесь: очищаем список-коллекцию сигналов, заполняем список сигналами из базы Сигналов MQL5.com и, если количество сигналов в списке-коллекции больше нуля — т.е., список заполнен, то выводим сообщение об успешном создании списка-коллекции и возвращаем true.
В противном случае возвращаем false.
Метод обновления списка-коллекции:
//+------------------------------------------------------------------+ //| Обновляет список-коллекцию объектов-mql5-сигналов | //+------------------------------------------------------------------+ void CMQLSignalsCollection::Refresh(const bool messages=true) { this.m_signals_base_total=::SignalBaseTotal(); //--- цикл по всем сигналам в базе сигналов for(int i=0;i<this.m_signals_base_total;i++) { //--- Выбираем сигнал из базы сигналов по индексу цикла if(!::SignalBaseSelect(i)) continue; //--- Получаем идентификатор текущего сигнала и //--- на его основании создаём новый объект-mql5-сигнал long id=::SignalBaseGetInteger(SIGNAL_BASE_ID); CMQLSignal *signal=new CMQLSignal(id); if(signal==NULL) continue; //--- Устанавливаем списку флаг сортировки по идентификатору сигналов и, //--- если такой сигнал уже есть в списке-коллекции - //--- удаляем созданный объект и идём на следующую итерацию цикла m_list.Sort(SORT_BY_SIGNAL_MQL5_ID); if(this.m_list.Search(signal)!=WRONG_VALUE) { delete signal; continue; } //--- Если новый объект-сигнал не удалось добавить в список-коллекцию - //--- удаляем созданный объект и идём на следующую итерацию цикла if(!this.m_list.InsertSort(signal)) { delete signal; continue; } //--- Если объект-mql5-сигнал успешно добавлен в коллекцию //--- и установлен флаг сообщения о новом объекте в переданных в метод параметрах - //--- выводим сообщение о новом найденном сигнале else if(messages) { ::Print(DFUN,CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_NEW),":"); signal.PrintShort(true); } } } //+------------------------------------------------------------------+
Логика метода подробно описана в комментариях к коду. Вкратце: в метод передаётся флаг необходимости сообщить о новом найденном сигнале. Так как метод не очищает список-коллекцию, то только новый найденный сигнал может быть добавлен в него. Если флаг сообщения установлен, то при успешном добавлении нового объекта-сигнала в список, в журнал будет выведено сообщение о новом найденном сигнале.
На данный момент это простейший метод, в котором не реализовано обновление параметров существующих сигналов — их можно будет обновить самостоятельно в своих программах с помощью доступа к объекту-сигналу по его идентификатору и установке новых значений в его свойства. Позже мы добавим автоматическое обновление параметров существующих сигналов по времени и, если класс-коллекция Сигналов MQL5.com будет востребован, то мы создадим для него отправку событий о новых сигналах и об изменении параметров отслеживаемых сигналов.
Метод, возвращающий указатель на объект-mql5-сигнал по идентификатору сигнала:
//+------------------------------------------------------------------+ //| Возвращает указатель на объект-mql5-сигнал по идентификатору | //+------------------------------------------------------------------+ CMQLSignal *CMQLSignalsCollection::GetMQLSignal(const long id) { CArrayObj *list=GetList(SIGNAL_MQL5_PROP_ID,id,EQUAL); return(list!=NULL ? list.At(0) : NULL); } //+------------------------------------------------------------------+
Получаем список объектов-mql5-сигналов по идентификатору сигнала и возвращаем либо единственный объект из полученного списка, либо NULL.
Метод, возвращающий указатель на объект-mql5-сигнал по имени сигнала:
//+------------------------------------------------------------------+ //| Возвращает указатель на объект-mql5-сигнал по имени | //+------------------------------------------------------------------+ CMQLSignal *CMQLSignalsCollection::GetMQLSignal(const string name) { CArrayObj *list=GetList(SIGNAL_MQL5_PROP_NAME,name,EQUAL); return(list!=NULL ? list.At(0) : NULL); } //+------------------------------------------------------------------+
Получаем список объектов-mql5-сигналов по имени сигнала и возвращаем либо единственный объект из полученного списка, либо NULL.
Метод, выводящий в журнал полное описание коллекции:
//+------------------------------------------------------------------+ //| Выводит в журнал полное описание коллекции | //+------------------------------------------------------------------+ 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(); } } //+------------------------------------------------------------------+
Сначала выводится заголовок, а затем, в цикле по списку-коллекции получаем очередной объект-mql5-сигнал и выводим его полное описание.
Метод, выводящий в журнал краткое описание коллекции:
//+------------------------------------------------------------------+ //| Выводит в журнал краткое описание коллекции | //+------------------------------------------------------------------+ void CMQLSignalsCollection::PrintShort(const bool list=false,const bool paid=true,const bool free=true) { //--- Выводим в журнал заголовок ::Print(CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION),":"); //--- Если полный список - выводим в журнал краткие описания всех сигналов в коллекции //--- в соответствии с флагами необходимости вывода платных и бесплатных сигналов 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); } //--- Если не список сигналов else { //--- Сортируем список по цене сигнала this.m_list.Sort(SORT_BY_SIGNAL_MQL5_PRICE); //--- Получаем список бесплатных сигналов и их количество CArrayObj *list_free=this.GetList(SIGNAL_MQL5_PROP_PRICE,0,EQUAL); int num_free=(list_free==NULL ? 0 : list_free.Total()); //--- Сортируем список по цене сигнала this.m_list.Sort(SORT_BY_SIGNAL_MQL5_PRICE); //--- Получаем список платных сигналов и их количество CArrayObj *list_paid=this.GetList(SIGNAL_MQL5_PROP_PRICE,0,MORE); int num_paid=(list_paid==NULL ? 0 : list_paid.Total()); //--- Распечатываем в журнал количество бесплатных и платных сигналов в коллекции ::Print ( "- ",CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_FREE),": ",(string)num_free, ", ",CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_PAID),": ",(string)num_paid ); } } //+------------------------------------------------------------------+
В этом методе, в зависимости от переданных флагов, распечатываются в журнал разные сообщения и списки.
Сначала выводится заголовок, затем, если установлен флаг списка, то в журнал распечатываются краткие описания сигналов в коллекции. При этом учитываются флаги платных и бесплатных сигналов. В зависимости от их состояния в журнал выводятся либо все сигналы, либо только платные, либо только бесплатные.
Если выводить нужно описание не списком, то после заголовка выводится общее количество бесплатных и платных сигналов в списке-коллекции.
Методы, осуществляющие подписку на сигнал (приватный метод) и отписку от существующего сигнала (публичный метод):
//+------------------------------------------------------------------+ //| Осуществляет подписку на сигнал | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::Subscribe(const long signal_id) { //--- Если для программы не установлено разрешение на работу с сигналами - //--- выводим об этом сообщение и рекомендацию проверить настройки программы 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; } //--- Если подписка на сигнал не удалась - выводим сообщение об ошибкеи возвращаем false ::ResetLastError(); if(!::SignalSubscribe(signal_id)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Подписка успешна. Выводим сообщение об успешной подписке на сигнал и возвращаем true ::Print(CMessage::Text(MSG_SIGNAL_INFO_TEXT_SIGNAL_SUBSCRIBED)," ID ",(string)this.CurrentID()," \"",CurrentName(),"\""); return true; } //+------------------------------------------------------------------+ //| Осуществляет отписку от подписанного сигнала | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentUnsubscribe(void) { //--- Если для программы не установлено разрешение на работу с сигналами - //--- выводим об этом сообщение и рекомендацию проверить настройки программы 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; } //--- Запоминаем идентификатор и имя текущего подписанного сигнала ::ResetLastError(); long id=this.CurrentID(); string name=this.CurrentName(); //--- Если идентификатор равен нулю (нет подписки) - возвращаем true if(id==0) return true; //--- Если отписаться от сигнала не удалось - выводим сообщение об ошибкеи возвращаем false if(!::SignalUnsubscribe()) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Успешно отписались от сигнала. Выводим сообщение об успешной отписке от сигнала и возвращаем true ::Print(CMessage::Text(MSG_SIGNAL_INFO_TEXT_SIGNAL_UNSUBSCRIBED)," ID ",(string)id," \"",name,"\""); return true; } //+------------------------------------------------------------------+
Логика работы методов подробно прокомментирована в листинге методов.
Публичный метод, осуществляющий подписку на сигнал по идентификатору сигнала:
//+------------------------------------------------------------------+ //| Осуществляет подписку на сигнал по идентификатору | //+------------------------------------------------------------------+ 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()); } //+------------------------------------------------------------------+
Здесь: получаем указатель на объект-mql5-сигнал в списке-коллекции по переданному в метод идентификатору и возвращаем результат работы приватного метода подписки на сигнал, рассмотренному нами выше.
Публичный метод, осуществляющий подписку на сигнал по имени сигнала:
//+------------------------------------------------------------------+ //| Осуществляет подписку на сигнал по имени сигнала | //+------------------------------------------------------------------+ 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()); } //+------------------------------------------------------------------+
Здесь: получаем указатель на объект-mql5-сигнал в списке-коллекции по переданному в метод имени сигнала (имя должно быть заранее известно) и возвращаем результат работы приватного метода подписки на сигнал, рассмотренному нами выше.
Методы установки значений копирования торговых сигналов:
//+------------------------------------------------------------------+ //| Устанавливает процент для конвертации объема сделки | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetEquityLimit(const double value) { ::ResetLastError(); if(!::SignalInfoSetDouble(SIGNAL_INFO_EQUITY_LIMIT,value)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Устанавливает величину проскальзывания, с которой выставляются | //| рыночные ордера при синхронизации позиций и копировании сделок | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetSlippage(const double value) { ::ResetLastError(); if(!::SignalInfoSetDouble(SIGNAL_INFO_SLIPPAGE,value)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Устанавливает флаг разрешения синхронизации | //| без показа диалога подтверждения | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetConfirmationsDisableFlag(const bool flag) { ::ResetLastError(); if(!::SignalInfoSetInteger(SIGNAL_INFO_CONFIRMATIONS_DISABLED,flag)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Устанавливает флаг копирования Stop Loss и 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; } //+------------------------------------------------------------------+ //| Устанавливает ограничения по депозиту (в %) | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetDepositPercent(const int value) { ::ResetLastError(); if(!::SignalInfoSetInteger(SIGNAL_INFO_DEPOSIT_PERCENT,value)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Устанавливает флаг разрешения на копирование сделок по подписке | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetSubscriptionEnabledFlag(const bool flag) { ::ResetLastError(); if(!::SignalInfoSetInteger(SIGNAL_INFO_SUBSCRIPTION_ENABLED,flag)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+
Здесь во всех методах используются функции установки значений SignalInfoSetDouble() и SignalInfoSetInteger(). При неудачной установке значения методы выводят описание ошибки и возвращают false. При успешной установке значения методы возвращают true.
Методы, возвращающие описания параметров настройки копирования торговых сигналов:
//+------------------------------------------------------------------+ //| Возвращает описание разрешения работы с сигналами | //| для данной запущенной программы | //+------------------------------------------------------------------+ 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)) ); } //+------------------------------------------------------------------+ //| Возвращает описание процента для конвертации объема сделки | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentEquityLimitDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_EQUITY_LIMIT)+": "+::DoubleToString(this.CurrentEquityLimit(),2)+"%"; } //+------------------------------------------------------------------+ //| Возвращает описание проскальзывания, с которой выставляются | //| рыночные ордера при синхронизации позиций и копировании сделок | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentSlippageDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_SLIPPAGE)+": "+CMessage::Text(MSG_LIB_TEXT_BAR_SPREAD)+" * "+::DoubleToString(this.CurrentSlippage(),2); } //+------------------------------------------------------------------+ //| Возвращает описание значения ограничения по средствам для сигнала| //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentVolumePercentDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_VOLUME_PERCENT)+": "+::DoubleToString(this.CurrentVolumePercent(),2)+"%"; } //+------------------------------------------------------------------+ //| Возвращает описание флага разрешения синхронизации | //| без показа диалога подтверждения | //+------------------------------------------------------------------+ 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)) ); } //+------------------------------------------------------------------+ //| Возвращает описание флага копирования Stop Loss и 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)) ); } //+------------------------------------------------------------------+ //| Возвращает описание ограничения по депозиту (в %) | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentDepositPercentDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_DEPOSIT_PERCENT)+": "+(string)this.CurrentDepositPercent()+"%"; } //+------------------------------------------------------------------+ //| Возвращает описание флага разрешения | //| на копирование сделок по подписке | //+------------------------------------------------------------------+ 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)) ); } //+------------------------------------------------------------------+ //| Возвращает описание id сигнала | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentIDDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_ID)+": "+(this.CurrentID()>0 ? (string)this.CurrentID() : CMessage::Text(MSG_LIB_PROP_EMPTY)); } //+------------------------------------------------------------------+ //| Возвращает описание флага согласия | //| с условиями использования сервиса "Сигналы" | //+------------------------------------------------------------------+ 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)) ); } //+------------------------------------------------------------------+ //| Возвращает описание имени сигнала | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentNameDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_NAME)+": "+(this.CurrentName()!="" ? this.CurrentName() : CMessage::Text(MSG_LIB_PROP_EMPTY)); } //+------------------------------------------------------------------+
В каждом методе создаётся и возвращается строка с заголовком описания параметра и его текущим значением.
Метод, выводящий в журнал параметры настроек копирования торговых сигналов:
//+------------------------------------------------------------------+ //| Выводит в журнал параметры настроек копирования сигналов | //+------------------------------------------------------------------+ 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(""); } //+------------------------------------------------------------------+
Сначала выводится заголовок, а затем поочерёдно распечатываются все параметры настройки копирования торговых сигналов, возвращаемые соответствующими вышерассмотренными методами.
На этом создание класса-коллекции объектов-mql5-сигналов завершено.
В последующем мы возможно к нему ещё вернёмся для доработок, но пока нет возможности знать его востребованность, и мы оставим его таким.
Для связи класса-коллекции торговых сигналов с "внешним миром" нам необходимо дописать методы для работы с ним в класс главного объекта библиотеки CEngine в файле \MQL5\Include\DoEasy\Engine.mqh.
Подключим файл класса-коллекции торговых сигналов к файлу класса объекта CEngine и объявим объект класса-коллекции mql5-сигналов:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #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" //+------------------------------------------------------------------+ //| Класс-основа библиотеки | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Коллекция исторических ордеров и сделок CMarketCollection m_market; // Коллекция рыночных ордеров и сделок CEventsCollection m_events; // Коллекция событий CAccountsCollection m_accounts; // Коллекция аккаунтов CSymbolsCollection m_symbols; // Коллекция символов CTimeSeriesCollection m_time_series; // Коллекция таймсерий CBuffersCollection m_buffers; // Коллекция индикаторных буферов CIndicatorsCollection m_indicators; // Коллекция индикаторов CTickSeriesCollection m_tick_series; // Коллекция тиковых серий CMBookSeriesCollection m_book_series; // Коллекция серий стаканов цен CMQLSignalsCollection m_signals_mql5; // Коллекция сигналов сервиса сигналов mql5.com CResourceCollection m_resource; // Список ресурсов CTradingControl m_trading; // Объект управления торговлей CPause m_pause; // Объект "Пауза" CArrayObj m_list_counters; // Список счётчиков таймера
В публичной секции класса объявим и реализуем новые методы для работы с классом-коллекцией снимков стаканов цен и методы для работы с коллекцией торговых сигналов:
//--- Обновляет серию стакана цен указанного символа void MBookSeriesRefresh(const string symbol,const long time_msc) { this.m_book_series.Refresh(symbol,time_msc); } //--- Возвращает (1) серию стакана цен указанного символа, стакан цен (2) по индексу, (3) по времени в миллисекундах 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);} //--- Возвращает объём указанного по символу и индексу стакана цен (1) на покупку, (2) на продажу long MBookVolumeBuy(const string symbol,const int index); long MBookVolumeSell(const string symbol,const int index); //--- Возвращает объём с повышенной точностью указанного по символу и индексу стакана цен (1) на покупку, (2) на продажу double MBookVolumeBuyReal(const string symbol,const int index); double MBookVolumeSellReal(const string symbol,const int index); //--- Возвращает объём указанного по символу и времени в миллисекундах стакана цен (1) на покупку, (2) на продажу long MBookVolumeBuy(const string symbol,const long time_msc); long MBookVolumeSell(const string symbol,const long time_msc); //--- Возвращает объём с повышенной точностью указанного по символу и времени в миллисекундах стакана цен (1) на покупку, (2) на продажу double MBookVolumeBuyReal(const string symbol,const long time_msc); double MBookVolumeSellReal(const string symbol,const long time_msc); //--- Возвращает (1) коллекцию сигналов сервиса сигналов mql5.com, (2) список сигналов из коллекции сигналов сервиса сигналов mql5.com CMQLSignalsCollection *GetSignalsMQL5Collection(void) { return &this.m_signals_mql5; } CArrayObj *GetListSignalsMQL5(void) { return this.m_signals_mql5.GetList(); } //--- Возвращает список (1) платных, (2) бесплатных сигналов 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) Создаёт, (2) обновляет коллекцию сигналов сервиса сигналов mql5.com bool SignalsMQL5Create(void) { return this.m_signals_mql5.CreateCollection(); } void SignalsMQL5Refresh(void) { this.m_signals_mql5.Refresh(); } //--- Осуществляет подписку на сигнал по (1) идентификатору, (2) имени сигнала 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);} //--- Осуществляет отписку от текущего сигнала bool SignalsMQL5Unsubscribe(void) { return this.m_signals_mql5.CurrentUnsubscribe(); } //--- Возвращает (1) идентификатор, (2) имя текущего сигнала, на который осуществлена подписка long SignalsMQL5CurrentID(void) { return this.m_signals_mql5.CurrentID(); } string SignalsMQL5CurrentName(void) { return this.m_signals_mql5.CurrentName(); } //--- Устанавливает процент для конвертации объема сделки bool SignalsMQL5CurrentSetEquityLimit(const double value) { return this.m_signals_mql5.CurrentSetEquityLimit(value); } //--- Устанавливает величину проскальзывания, с которой выставляются рыночные ордера при синхронизации позиций и копировании сделок bool SignalsMQL5CurrentSetSlippage(const double value) { return this.m_signals_mql5.CurrentSetSlippage(value); } //--- Устанавливает ограничения по депозиту (в %) bool SignalsMQL5CurrentSetDepositPercent(const int value) { return this.m_signals_mql5.CurrentSetDepositPercent(value); } //--- Устанавливает разрешение синхронизации без показа диалога подтверждения bool SignalsMQL5CurrentSetConfirmationsDisableON(void) { return this.m_signals_mql5.CurrentSetConfirmationsDisableON();} //--- Устанавливает запрет синхронизации без показа диалога подтверждения bool SignalsMQL5CurrentSetConfirmationsDisableOFF(void) { return this.m_signals_mql5.CurrentSetConfirmationsDisableOFF();} //--- Устанавливает разрешение копирования Stop Loss и Take Profit bool SignalsMQL5CurrentSetSLTPCopyON(void) { return this.m_signals_mql5.CurrentSetSLTPCopyON(); } //--- Устанавливает запрет копирования Stop Loss и Take Profit bool SignalsMQL5CurrentSetSLTPCopyOFF(void) { return this.m_signals_mql5.CurrentSetSLTPCopyOFF(); } //--- Устанавливает разрешение на копирование сделок по подписке bool SignalsMQL5CurrentSetSubscriptionEnableON(void) { return this.m_signals_mql5.CurrentSetSubscriptionEnableON(); } //--- Устанавливает запрет на копирование сделок по подписке bool SignalsMQL5CurrentSetSubscriptionEnableOFF(void) { return this.m_signals_mql5.CurrentSetSubscriptionEnableOFF();} //--- Выводит в журнал (1) полное, (2) краткое описание коллекции, (3) параметры настроек копирования сигналов 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();} //--- Возвращает (1) коллекцию буферов, (2) список буферов из коллекции
Реализованные методы возвращают результат вызова одноимённых методов соответствующих коллекций.
Рассмотрим реализацию методов, возвращающих указанные объёмы указанных снимков стаканов цен:
//+------------------------------------------------------------------+ //| Возвращает объём на покупку стакана цен, | //| указанного по символу и индексу | //+------------------------------------------------------------------+ long CEngine::MBookVolumeBuy(const string symbol,const int index) { CMBookSnapshot *mbook=this.GetMBook(symbol,index); return(mbook!=NULL ? mbook.VolumeBuy() : 0); } //+------------------------------------------------------------------+ //| Возвращает объём на продажу стакана цен, | //| указанного по символу и индексу | //+------------------------------------------------------------------+ long CEngine::MBookVolumeSell(const string symbol,const int index) { CMBookSnapshot *mbook=this.GetMBook(symbol,index); return(mbook!=NULL ? mbook.VolumeSell() : 0); } //+------------------------------------------------------------------+ //| Возвращает объём на покупку с повышенной точностью | //| стакана цен, указанного по символу и индексу | //+------------------------------------------------------------------+ double CEngine::MBookVolumeBuyReal(const string symbol,const int index) { CMBookSnapshot *mbook=this.GetMBook(symbol,index); return(mbook!=NULL ? mbook.VolumeBuyReal() : 0); } //+------------------------------------------------------------------+ //| Возвращает объём на продажу с повышенной точностью | //| стакана цен,указанного по символу и индексу | //+------------------------------------------------------------------+ double CEngine::MBookVolumeSellReal(const string symbol,const int index) { CMBookSnapshot *mbook=this.GetMBook(symbol,index); return(mbook!=NULL ? mbook.VolumeSellReal() : 0); } //+------------------------------------------------------------------+ //| Возвращает объём на покупку стакана цен, | //| указанного по символу и времени в миллисекундах | //+------------------------------------------------------------------+ long CEngine::MBookVolumeBuy(const string symbol,const long time_msc) { CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc); return(mbook!=NULL ? mbook.VolumeBuy() : 0); } //+------------------------------------------------------------------+ //| Возвращает объём на продажу стакана цен, | //| указанного по символу и времени в миллисекундах | //+------------------------------------------------------------------+ long CEngine::MBookVolumeSell(const string symbol,const long time_msc) { CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc); return(mbook!=NULL ? mbook.VolumeSell() : 0); } //+------------------------------------------------------------------+ //| Возвращает объём на покупку с повышенной точностью | //| стакана цен, указанного по символу и времени в миллисекундах | //+------------------------------------------------------------------+ double CEngine::MBookVolumeBuyReal(const string symbol,const long time_msc) { CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc); return(mbook!=NULL ? mbook.VolumeBuyReal() : 0); } //+------------------------------------------------------------------+ //| Возвращает объём на продажу с повышенной точностью | //| стакана цен,указанного по символу и времени в миллисекундах | //+------------------------------------------------------------------+ double CEngine::MBookVolumeSellReal(const string symbol,const long time_msc) { CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc); return(mbook!=NULL ? mbook.VolumeSellReal() : 0); } //+------------------------------------------------------------------+
Здесь всё просто. Логика всех методов идентична: сначала получаем из списка-коллекции объект-снимок стакана цен по символу и индексу или времени в миллисекундах с помощью ранее реализованных методов GetMBook(), затем возвращаем соответствующий методу объём стакана цен из полученного объекта-снимка стакана цен, либо ноль, если объект получить не удалось.
Это все доработки и изменения на сегодня.
Тестирование
Протестируем создание коллекции Сигналов MQL5.com. Тестировать будем так:
Получим полный список из базы Сигналов, выведем список только бесплатных сигналов, затем найдём в этом списке самый прибыльный сигнал и подпишемся на него. При удачном оформлении подписки выведем параметры подписанного сигнала и параметры копирования торговых сигналов, установленные при подписке на сигнал. На следующем тике отпишемся от подписанного сигнала.
Для тестирования возьмём советник из прошлой статьи
и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part66\ под новым именем TestDoEasyPart66.mq5.
В список входных параметров советника добавим параметр настройки, позволяющий выбрать необходимость работы с сервисом Сигналы MQL5.com в советнике:
//--- 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
В прошлой статье мы все проверки работы с сигналами делали в обработчике OnInit() советника. Сегодня будем работать уже в OnTick().
Поэтому удалим уже ненужный прошлый тестовый блок кода из обработчика OnInit():
CArrayObj *list=new CArrayObj(); if(list!=NULL) { //--- запрашиваем общее количество сигналов в базе int total=SignalBaseTotal(); //--- цикл по всем сигналам for(int i=0;i<total;i++) { //--- выбираем сигнал для дальнейшей работы 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; } } //--- выводим все прибыльные бесплатные сигналы с ненулевым количеством подписчиков 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; //--- Самый первый подходящий сигнал распечатываем полностью в журнале if(!done) { signal.Print(); done=true; } //--- Для остальных выводим краткие описания else signal.PrintShort(); } delete list; } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
В обработчик OnTick() впишем новый тестовый блок кода, в котором выполняются все условия теста, оговорённые нами в самом начале этого раздела:
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Удаление графических объектов советника по префиксу имени объектов ObjectsDeleteAll(0,prefix); Comment(""); //--- Деинициализация библиотеки engine.OnDeinit(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Обработка события NewTick в библиотеке engine.OnTick(rates_data); //--- Если работа в тестере if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Работа в таймере PressButtonsControl(); // Контроль нажатия кнопок engine.EventsHandling(); // Работа с событиями } //--- Если установлен флаг трейлинга if(trailing_on) { TrailingPositions(); // Трейлинг позиций TrailingOrders(); // Трейлинг отложенных ордеров } //--- Поиск доступных сигналов в базе и проверка возможности подписаться на сигнал по его имени static bool done=false; //--- Если первый запуск и работа с сигналами разрешена в пользовательских настройках советника if(InpUseMqlSignals && !done) { //--- Выводим в журнал список всех бесплатных сигналов Print(""); engine.GetSignalsMQL5Collection().PrintShort(true,false,true); //--- Получаем список только бесплатных сигналов CArrayObj *list=engine.GetListSignalsMQL5Free(); //--- Если список получен if(list!=NULL) { //--- Находим в списке сигнал с максимальным приростом в процентах int index_max_gain=CSelect::FindMQLSignalMax(list,SIGNAL_MQL5_PROP_GAIN); CMQLSignal *signal_max_gain=list.At(index_max_gain); //--- Если сигнал найден if(signal_max_gain!=NULL) { //--- Выведем в журнал полное описание сигнала signal_max_gain.Print(); //--- Если удалось подписаться на сигнал if(engine.SignalsMQL5Subscribe(signal_max_gain.ID())) { //--- Устанавливаем параметры подписки //--- Разрешаем копирование сделок по подписке engine.SignalsMQL5CurrentSetSubscriptionEnableON(); //--- Устанавливаем запрет синхронизации без показа диалога подтверждения engine.SignalsMQL5CurrentSetConfirmationsDisableOFF(); //--- Устанавливаем разрешение копирования Stop Loss и Take Profit engine.SignalsMQL5CurrentSetSLTPCopyON(); //--- Устанавливаем величину проскальзывания, с которой выставляются рыночные ордера при синхронизации позиций и копировании сделок engine.SignalsMQL5CurrentSetSlippage(2); //--- Устанавливаем процент для конвертации объема сделки engine.SignalsMQL5CurrentSetEquityLimit(50); //--- Устанавливаем ограничения по депозиту (в %) engine.SignalsMQL5CurrentSetDepositPercent(70); //--- Выводим в журнал параметры подписки engine.SignalsMQL5CurrentSubscriptionParameters(); } } } done=true; return; } //--- Если есть подписка на сигнал - отписываемся от него if(engine.SignalsMQL5CurrentID()>0) { engine.SignalsMQL5Unsubscribe(); } //--- } //+------------------------------------------------------------------+
Весь новый блок кода достаточно подробно прокомментирован. Если по нему возникли какие-либо вопросы, то их можно задать в обсуждении к статье.
В функции инициализации библиотеки OnInitDoEasy() впишем блок кода, в котором создаётся список-коллекция торговых сигналов и устанавливается флаг разрешения копирования торговых сигналов по подписке:
//--- Создание тиковых серий всех используемых символов engine.TickSeriesCreateAll(); //--- Проверка созданных тиковых серий - выводим в журнал описания всех созданных тиковых серий engine.GetTickSeriesCollection().Print(); //--- Проверка созданных серий стаканов цен - выводим в журнал описания всех созданных серий стаканов цен engine.GetMBookSeriesCollection().Print(); //--- Создание коллекции сигналов сервиса сигналов mql5.com //--- Если работа с сигналами разрешена и коллекция сигналов создана if(InpUseMqlSignals && engine.SignalsMQL5Create()) { //--- Устанавливаем разрешение на копирование сделок по подписке engine.SignalsMQL5CurrentSetSubscriptionEnableON(); //--- Проверка созданных объектов-mql5-сигналов сервиса сигналов - выводим в журнал короткое описание коллекции engine.SignalsMQL5PrintShort(); } //--- Если работа с сигналами не разрешена или не удалось создать коллекцию сигналов - //--- устанавливаем запрет на копирование сделок по подписке else engine.SignalsMQL5CurrentSetSubscriptionEnableOFF(); //--- Создание тестовых файлов ресурсов
Скомпилируем советник и запустим его на графике символа, предварительно задав в настройках работу на текущем символе/таймфрейме и установив флаг необходимости работы с торговыми сигналами сервиса Сигналы MQL5.com:
Во вкладке "Общие" окна настроек советника обязательно нужно установить галочку на пункте "Разрешить изменение настроек Сигналов":
Без этого советнику будет запрещено работать с сервисом Сигналов MQL5.com.
После запуска советника, в журнал будет выведено сообщение об успешном создании коллекции сигналов и её краткое описание:
Коллекция сигналов сервиса сигналов mql5.com создана успешно Коллекция сигналов сервиса сигналов mql5.com: - Бесплатных сигналов: 195, Платных сигналов: 805
Далее будет выведен полный список бесплатных сигналов. Так как их много, то приведу в пример только часть:
Коллекция сигналов сервиса сигналов mql5.com: - Сигнал "GBPUSD EXPERT 23233". Логин автора: mbt_trader, ID 919099, Прирост: 3.30, Просадка: 11.92, Цена: 0.00, Подписчиков: 0 - Сигнал "Willian". Логин автора: Desg, ID 917396, Прирост: 12.69, Просадка: 15.50, Цена: 0.00, Подписчиков: 0 - Сигнал "VahidVHZ1366". Логин автора: 39085485, ID 921427, Прирост: 34.36, Просадка: 12.84, Цена: 0.00, Подписчиков: 0 - Сигнал "Vikings". Логин автора: Myxx, ID 921040, Прирост: 7.05, Просадка: 2.22, Цена: 0.00, Подписчиков: 2 - Сигнал "VantageFX Sunphone Dragon". Логин автора: sunphone, ID 916421, Прирост: 537.89, Просадка: 39.06, Цена: 0.00, Подписчиков: 21 - Сигнал "Forex money maker free". Логин автора: Yggdrasills, ID 916328, Прирост: 44.66, Просадка: 61.15, Цена: 0.00, Подписчиков: 0 ... ... ... - Сигнал "Nine Pairs ST". Логин автора: ebi.pilehvar, ID 935603, Прирост: 25.92, Просадка: 26.41, Цена: 0.00, Подписчиков: 2 - Сигнал "FBS140". Логин автора: mohammeeeedali, ID 949720, Прирост: 42.14, Просадка: 23.11, Цена: 0.00, Подписчиков: 2 - Сигнал "StopTheFourthAddition". Логин автора: pinheirodps, ID 934990, Прирост: 41.78, Просадка: 28.03, Цена: 0.00, Подписчиков: 2 - Сигнал "The art of Forex". Логин автора: Myxx, ID 801685, Прирост: 196.39, Просадка: 40.95, Цена: 0.00, Подписчиков: 59 - Сигнал "Bongsanmaskdance1803". Логин автора: kim25801863, ID 936062, Прирост: 12.53, Просадка: 10.31, Цена: 0.00, Подписчиков: 0 - Сигнал "Prospector Scalper EA". Логин автора: robots4forex, ID 435626, Прирост: 334.76, Просадка: 43.93, Цена: 0.00, Подписчиков: 215 - Сигнал "ADS MT5". Логин автора: vluxus, ID 478235, Прирост: 295.68, Просадка: 40.26, Цена: 0.00, Подписчиков: 92
Затем нам будет выведено полное описание найденного сигнала с максимальным приростом в процентах и сообщение об успешной подписке на него:
============= Начало списка параметров (Сигнал сервиса сигналов mql5.com) ============= Тип счета: Демонстрационный счёт Дата публикации: 2020.07.02 16:29 Дата начала мониторинга: 2020.07.02 16:29 Дата последнего обновления торговой статистики: 2021.03.07 15:11 ID: 784584 Плечо торгового счета: 33 Результат торговли в пипсах: -19248988 Позиция в рейтинге сигналов: 872 Количество подписчиков: 6 Количество трейдов: 1825 Состояние подписки счёта на этот сигнал: Нет ------ Баланс счета: 12061.98 Средства на счете: 12590.32 Прирост счета в процентах: 1115.93 Максимальная просадка: 70.62 Цена подписки на сигнал: 0.00 Значение ROI (Return on Investment) сигнала в %: 1169.19 ------ Логин автора: "tradewai.com" Наименование брокера (компании): "MetaQuotes Software Corp." Сервер брокера: "MetaQuotes-Demo" Имя: "Tradewai" Валюта счета: "USD" ============= Конец списка параметров (Сигнал сервиса сигналов mql5.com) ============= Осуществлена подписка на сигнал ID 784584 "Tradewai"
После этого будут выведены параметры подписки:
============= Параметры копирования сигнала ============= Разрешение на работу с сигналами для программы: Да Согласие с условиями использования сервиса "Сигналы": Да Разрешение на копирование сделок по подписке: Да Разрешение синхронизации без показа диалога подтверждения: Нет Копирование Stop Loss и Take Profit: Да Проскальзывание, с которым выставляются рыночные ордера при синхронизации позиций и копировании сделок: Спред * 2.00 Процент для конвертации объема сделки: 50.00% Ограничение по депозиту: 70% Ограничение по средствам для сигнала: 7.00% Идентификатор сигнала: 784584 Имя сигнала: Tradewai
а при наступлении следующего тика мы получим сообщение об успешной отписке от сигнала.
Осуществлена отписка от сигнала ID 784584 "Tradewai"
Что дальше
В следующей статье начнём разрабатывать функционал библиотеки для работы с чартами — графиками символов.
Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для MQL5. Их можно скачать и протестировать всё самостоятельно.
Хочу отметить, что тест работы с сигналами в тестовом советнике является именно тестом работы с сигналами, и не пригоден для его использования в том виде, в котором он реализован в прилагаемом советнике. Прошу учитывать, что это всего лишь пример без какой-либо полезной нагрузки.
Приведённый в советнике пример даёт лишь понимание общего направления действий при реализации собственных разработок на основе библиотеки и её классов для работы с сервисом Сигналов MQL5.com.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
*Статьи этой серии:
Работа с ценами в библиотеке DoEasy (Часть 62): Реалтайм-обновление тиковых серий, подготовка к работе со стаканом цен
Работа с ценами в библиотеке DoEasy (Часть 63): Стакан цен, класс абстрактной заявки стакана цен
Работа с ценами в библиотеке DoEasy (Часть 64): Стакан цен, классы объекта-снимка и объекта-серии снимков стакана цен
Работа с ценами и Сигналами в библиотеке DoEasy (Часть 65): Коллекция стаканов и класс для работы с Сигналами MQL5.com