DoEasy 函数库中的其他类(第六十六部分):MQL5.com 信号集合类
内容
概述
在上一篇文章中,我创建了信号对象类,它是 MQL5.com 信号服务中众多广播信号中的一个。
今天,我将在信号数据库中创建可用的信号集合类,可按指定的所需信号索引调用 SignalBaseSelect() 函数获得。
该集合能将数据库中存在的所有信号存储到列表,从而便于搜索和排序。 我们能够依据其各种属性来检测并返回信号列表。 举例来说,我们可以仅获取免费/付费信号的列表,并按参数之一对它们进行排序(例如信号盈利能力),或者立即得到列表中参数等于、大于或小于指定值的信号索引。 对所需信号的了解令我们能够在集合中快速找到它。
在集合类中,实现订阅选择的信号,或从当前帐户上退订信号的能力。
除了操控 MQL5.com 信号服务对象之外,我们还能通过添加额外的属性来改进市场深度(DOM)快照对象类,从而令我们能够在创建 DOM 快照对象时立即计算买卖订单的交易量。 这会令最终用户务须在操控 DOM 时进行额外的计算。 我们会立即知道每个 DOM 快照的总买卖量。 这意味着依据 DOM 及其交易量创建策略时,我们不必在 DOM 中另外搜索买卖订单及其后续订单的交易量总和。
改进库类
与往常一样,我们立即将所有新函数库消息添加到 \MQL5\Include\DoEasy\Data.mqh。
首先加入新的消息索引:
//--- CMarketBookSnapshot MSG_MBOOK_SNAP_TEXT_SNAPSHOT, // DOM snapshot MSG_MBOOK_SNAP_VOLUME_BUY, // Buy volume MSG_MBOOK_SNAP_VOLUME_SELL, // Sell volume //--- CMBookSeries MSG_MBOOK_SERIES_TEXT_MBOOKSERIES, // DOM snapshot series MSG_MBOOK_SERIES_ERR_ADD_TO_LIST, // Error. Failed to add DOM snapshot series to the list
...
//--- CMQLSignal MSG_SIGNAL_MQL5_TEXT_SIGNAL, // Signal MSG_SIGNAL_MQL5_TEXT_SIGNAL_MQL5, // MQL5.com Signals service signal MSG_SIGNAL_MQL5_TRADE_MODE, // Account type MSG_SIGNAL_MQL5_DATE_PUBLISHED, // Publication date MSG_SIGNAL_MQL5_DATE_STARTED, // Monitoring start date MSG_SIGNAL_MQL5_DATE_UPDATED, // Date of the latest update of the trading statistics MSG_SIGNAL_MQL5_ID, // ID MSG_SIGNAL_MQL5_LEVERAGE, // Trading account leverage MSG_SIGNAL_MQL5_PIPS, // Trading result in pips MSG_SIGNAL_MQL5_RATING, // Position in the signal rating MSG_SIGNAL_MQL5_SUBSCRIBERS, // Number of subscribers MSG_SIGNAL_MQL5_TRADES, // Number of trades MSG_SIGNAL_MQL5_SUBSCRIPTION_STATUS, // Status of account subscription to a signal MSG_SIGNAL_MQL5_EQUITY, // Account equity MSG_SIGNAL_MQL5_GAIN, // Account growth in % MSG_SIGNAL_MQL5_MAX_DRAWDOWN, // Maximum drawdown MSG_SIGNAL_MQL5_PRICE, // Signal subscription price MSG_SIGNAL_MQL5_ROI, // Signal ROI (Return on Investment) in % MSG_SIGNAL_MQL5_AUTHOR_LOGIN, // Author login MSG_SIGNAL_MQL5_BROKER, // Broker (company) name MSG_SIGNAL_MQL5_BROKER_SERVER, // Broker server MSG_SIGNAL_MQL5_NAME, // Name MSG_SIGNAL_MQL5_CURRENCY, // Account currency MSG_SIGNAL_MQL5_TEXT_GAIN, // Growth MSG_SIGNAL_MQL5_TEXT_DRAWDOWN, // Drawdown MSG_SIGNAL_MQL5_TEXT_SUBSCRIBERS, // Subscribers //--- CMQLSignalsCollection MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION, // Collection of MQL5.com Signals service signals MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_PAID, // Paid signals MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_FREE, // Free signals MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_NEW, // New signal added to collection MSG_MQLSIG_COLLECTION_ERR_FAILED_GET_SIGNAL, // Failed to receive signal from collection MSG_SIGNAL_INFO_PARAMETERS, // Signal copying parameters MSG_SIGNAL_INFO_EQUITY_LIMIT, // Percentage for converting deal volume MSG_SIGNAL_INFO_SLIPPAGE, // Market order slippage when synchronizing positions and copying deals MSG_SIGNAL_INFO_VOLUME_PERCENT, // Limitation on signal equity MSG_SIGNAL_INFO_CONFIRMATIONS_DISABLED, // Enable synchronization without confirmation dialog MSG_SIGNAL_INFO_COPY_SLTP, // Copy Stop Loss and Take Profit MSG_SIGNAL_INFO_DEPOSIT_PERCENT, // Limit by deposit MSG_SIGNAL_INFO_ID, // Signal ID MSG_SIGNAL_INFO_SUBSCRIPTION_ENABLED, // Enable copying deals by subscription MSG_SIGNAL_INFO_TERMS_AGREE, // Agree to the terms of use of the Signals service MSG_SIGNAL_INFO_NAME, // Signal name MSG_SIGNAL_INFO_SIGNALS_PERMISSION, // Allow using signals for program MSG_SIGNAL_INFO_TEXT_SIGNAL_SUBSCRIBED, // Subscribed to signal MSG_SIGNAL_INFO_TEXT_SIGNAL_UNSUBSCRIBED, // Unsubscribed from signal MSG_SIGNAL_INFO_ERR_SIGNAL_NOT_ALLOWED, // Signal service disabled for program MSG_SIGNAL_INFO_TEXT_CHECK_SETTINGS, // Please check program settings (Common-->Allow modification of Signals settings) }; //+------------------------------------------------------------------+
接下来,加入新索引对应的消息:
//--- 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)" }, }; //+---------------------------------------------------------------------+
在日志中显示消息(尤其是调试消息)时,我们通常会在消息本身的开头指明发送消息的方法名称。 在第十九篇文章中,我已开发了类库消息。 不过,我目前仅用它们来指定消息索引,消息会利用标准 Print() 函数显示在日志之中。 鉴于我打算开始一个操控图形新的函数库部分,我将逐渐转向利用这个类来显示函数库消息。 今天,我将在其内添加 ToLog() 方法重载,从而我们可以额外将消息“来源”传递给类的方法,或调用该方法的程序函数。 因此,我们将有 ToLog() 方法的两个变体,如此我们即可显示带有指定其源函数或方法的消息,而无需指定它。
打开 \MQL5\Include\DoEasy\Services\Message.mqh,并在其内加入重载方法的声明:
//--- (1,2) display a message in the journal by ID, (3) to e-mail, (4) to a mobile device static void ToLog(const int msg_id,const bool code=false); static void ToLog(const string source,const int msg_id,const bool code=false); static bool ToMail(const string message,const string subject=NULL); static bool Push(const string message); //--- (1) send a file to FTP, (2) return an error code static bool ToFTP(const string filename,const string ftp_path=NULL); static int GetError(void) { return CMessage::m_global_error; }
我们在类的主体之外编写它的实现:
//+------------------------------------------------------------------+ //| Display a message in the journal by a message ID | //+------------------------------------------------------------------+ void CMessage::ToLog(const int msg_id,const bool code=false) { CMessage::GetTextByID(msg_id); ::Print(m_text,(!code || msg_id>ERR_USER_ERROR_FIRST-1 ? "" : " "+CMessage::Retcode(msg_id))); } //+------------------------------------------------------------------+ //| Display a message in the journal by a message ID | //+------------------------------------------------------------------+ void CMessage::ToLog(const string source,const int msg_id,const bool code=false) { CMessage::GetTextByID(msg_id); ::Print(source,m_text,(!code || msg_id>ERR_USER_ERROR_FIRST-1 ? "" : " "+CMessage::Retcode(msg_id))); } //+------------------------------------------------------------------+
不同于调用方法的第一种形式,它的第二种形式还有另一个输入,其中包含调用 ToLog() 方法的方法或函数名称,在日志中会将其显示在消息之前。
我们将在后续文章中回到这个类,并在把显示所有库类消息转移至该方法时对其进行改进。
我们来改进 \MQL5\Include\DoEasy\Objects\Book\MarketBookSnapshot.mqh 中的 CMBookSnapshot 类。
在类的私密部分,添加类成员变量,存储 DOM 快照的总买卖量:
//+------------------------------------------------------------------+ //| "DOM snapshot" class | //+------------------------------------------------------------------+ class CMBookSnapshot : public CBaseObj { private: string m_symbol; // Symbol long m_time; // Snapshot time int m_digits; // Symbol's Digits long m_volume_buy; // DOM buy volume long m_volume_sell; // DOM sell volume double m_volume_buy_real; // DOM buy volume with an increased accuracy double m_volume_sell_real; // DOM sell volume with an increased accuracy CArrayObj m_list; // List of DOM order objects public:
在类中简化访问 DOM 快照对象属性的方法部分,加入返回这些新加类属性的方法,和显示其描述的方法:
//+----------------------------------------------------------------------+ //|Methods of a simplified access to the DOM snapshot object properties | //+----------------------------------------------------------------------+ //--- Set (1) a symbol, (2) a DOM snapshot time and (3) the specified time for all DOM orders void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); } void SetTime(const long time_msc) { this.m_time=time_msc; } void SetTimeToOrders(const long time_msc); //--- Return (1) a DOM symbol, (2) symbol's Digits and (3) a snapshot time //--- (4) buy and (5) sell DOM snapshot volume //--- with increased accuracy for (6) buying and (7) selling, string Symbol(void) const { return this.m_symbol; } int Digits(void) const { return this.m_digits; } long Time(void) const { return this.m_time; } long VolumeBuy(void) const { return this.m_volume_buy; } long VolumeSell(void) const { return this.m_volume_sell; } double VolumeBuyReal(void) const { return this.m_volume_buy_real; } double VolumeSellReal(void) const { return this.m_volume_sell_real; } //--- Return the description of DOM (1) buy and (2) sell volume string VolumeBuyDescription(void); string VolumeSellDescription(void); }; //+------------------------------------------------------------------+
当创建一个新的 DOM 快照对象时,我们在一个循环中能看到它的所有订单,为这些订单创建对象并将它们发送到列表中。 现在我们需要在类构造函数中考虑订单类型,并根据当前订单类型,将订单交易量立即分别累加到存储买入或卖出订单总量的变量之中。 因此,在创建 DOM 快照对象后,每个变量会立即存储买入亦或卖出订单的总交易量。
我们把这些改进加入类的参数型构造函数当中:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time) { //--- Set a symbol this.SetSymbol(symbol); //--- Clear the list this.m_list.Clear(); //--- In the loop by the structure array int total=::ArraySize(book_array); this.m_volume_buy=this.m_volume_sell=0; this.m_volume_buy_real=this.m_volume_sell_real=0; for(int i=0;i<total;i++) { //--- Create order objects of the current DOM snapshot depending on the order type CMarketBookOrd *mbook_ord=NULL; switch(book_array[i].type) { case BOOK_TYPE_BUY : mbook_ord=new CMarketBookBuy(this.m_symbol,book_array[i]); break; case BOOK_TYPE_SELL : mbook_ord=new CMarketBookSell(this.m_symbol,book_array[i]); break; case BOOK_TYPE_BUY_MARKET : mbook_ord=new CMarketBookBuyMarket(this.m_symbol,book_array[i]); break; case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(this.m_symbol,book_array[i]); break; default: break; } if(mbook_ord==NULL) continue; //--- Set the DOM snapshot time for the order mbook_ord.SetTime(this.m_time); //--- Set the sorted list flag for the list (by the price value) and add the current order object to it //--- If failed to add the object to the DOM order list, remove the order object this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE); if(!this.m_list.InsertSort(mbook_ord)) delete mbook_ord; //--- If the order object is successfully added to the DOM order list, supplement the total snapshot volumes else { switch(mbook_ord.TypeOrd()) { case BOOK_TYPE_BUY : this.m_volume_buy+=mbook_ord.Volume(); this.m_volume_buy_real+=mbook_ord.VolumeReal(); break; case BOOK_TYPE_SELL : this.m_volume_sell+=mbook_ord.Volume(); this.m_volume_sell_real+=mbook_ord.VolumeReal(); break; case BOOK_TYPE_BUY_MARKET : this.m_volume_buy+=mbook_ord.Volume(); this.m_volume_buy_real+=mbook_ord.VolumeReal(); break; case BOOK_TYPE_SELL_MARKET : this.m_volume_buy+=mbook_ord.Volume(); this.m_volume_buy_real+=mbook_ord.VolumeReal(); break; default: break; } } } } //+------------------------------------------------------------------+
首先,存储所有 DOM 快照买卖订单总交易量的变量进行初始化。 接着,在循环体中遍历所有 DOM 订单,根据订单类型,将当前订单交易量添加到存储总交易量的相应变量之中。 因此,遍历所有 DOM 快照订单的循环完毕后,总买卖交易量会存储在所有变量当中。
该方法在日志中显示对象简述,和所有对象属性,以及显示总买卖交易量:
//+------------------------------------------------------------------+ //| Display a short description of the object in the journal | //+------------------------------------------------------------------+ void CMBookSnapshot::PrintShort(void) { string vol_buy="Buy vol: "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy()); string vol_sell="Sell vol: "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell()); ::Print(this.Header()," ",vol_buy,", ",vol_sell," ("+TimeMSCtoString(this.m_time),")"); } //+------------------------------------------------------------------+ //| Display object properties in the journal | //+------------------------------------------------------------------+ void CMBookSnapshot::Print(void) { string vol_buy=CMessage::Text(MSG_MBOOK_SNAP_VOLUME_BUY)+": "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy()); string vol_sell=CMessage::Text(MSG_MBOOK_SNAP_VOLUME_SELL)+": "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell()); ::Print(this.Header(),": ",vol_buy,", ",vol_sell," ("+TimeMSCtoString(this.m_time),"):"); this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE); for(int i=this.m_list.Total()-1;i>WRONG_VALUE;i--) { CMarketBookOrd *ord=this.m_list.At(i); if(ord==NULL) continue; ::Print("- ",ord.Header()); } } //+------------------------------------------------------------------+
在类的主体之外,实现 两个新的方法返回 DOM 买卖交易量的说明:
//+------------------------------------------------------------------+ //| Return the DOM buy volume description | //+------------------------------------------------------------------+ string CMBookSnapshot::VolumeBuyDescription(void) { return(CMessage::Text(MSG_MBOOK_SNAP_VOLUME_BUY)+": "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy())); } //+------------------------------------------------------------------+ //| Return the DOM sell volume description | //+------------------------------------------------------------------+ string CMBookSnapshot::VolumeSellDescription(void) { return(CMessage::Text(MSG_MBOOK_SNAP_VOLUME_SELL)+": "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell())); } //+------------------------------------------------------------------+
两种方法均会检查增加的准确交易量。 如果超过零,则返回:标题 + 交易量(实数型),否则返回 — 整数型。
在 \MQL5\Include\DoEasy\Collections\BookSeriesCollection.mqh 里的 CMBookSeriesCollection DOM 快照序列集合类,在其公开部分,定义方法依据指定的列表属性作为条件,返回符合的列表:
public: //--- Return (1) itself and (2) the DOM series collection list CMBookSeriesCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } //--- Return the number of DOM series in the list int DataTotal(void) const { return this.m_list.Total(); } //--- Return the pointer to the DOM series object (1) by symbol and (2) by index in the list
在 \MQL5\Include\DoEasy\Objects\MQLSignalBase\MQLSignal.mqh 里的 CMQLSignal DOM 订单对象类中,补充带有标志值的 PrintShort() 方法,表示需要在对象说明之前显示连字符:
//--- Display the description of object properties in the journal (full_prop=true - all properties, false - supported ones only) void Print(const bool full_prop=false); //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false); //--- Return the object short name virtual string Header(const bool shrt=false);
我们在方法主体中进行修改:
//+------------------------------------------------------------------+ //| Display a short description of the object in the journal | //+------------------------------------------------------------------+ void CMQLSignal::PrintShort(const bool dash=false) { ::Print ( (dash ? "- " : ""),this.Header(true), " \"",this.Name(),"\". ", CMessage::Text(MSG_SIGNAL_MQL5_AUTHOR_LOGIN),": ",this.AuthorLogin(), ", ID ",this.ID(), ", ",CMessage::Text(MSG_SIGNAL_MQL5_TEXT_GAIN),": ",::DoubleToString(this.Gain(),2), ", ",CMessage::Text(MSG_SIGNAL_MQL5_TEXT_DRAWDOWN),": ",::DoubleToString(this.MaxDrawdown(),2), ", ",CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE),": ",::DoubleToString(this.Price(),2), ", ",CMessage::Text(MSG_SIGNAL_MQL5_TEXT_SUBSCRIBERS),": ",this.Subscribers() ); } //+------------------------------------------------------------------+
根据所传递的数值,在对象说明之前显示或不显示连字符。 订阅者数量 则于描述的最后设置。
在类主体的最后,添加依据对象描述执行信号订阅的新方法:
//--- Return the account type name string TradeModeDescription(void); //--- Subscribe to a signal bool Subscribe(void) { return ::SignalSubscribe(this.ID()); } }; //+------------------------------------------------------------------+
在类构造函数中,修复存储信号订阅状态的变量的初始化:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CMQLSignal::CMQLSignal(const long signal_id) { this.m_long_prop[SIGNAL_MQL5_PROP_ID] = signal_id; this.m_long_prop[SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS] = (::SignalInfoGetInteger(SIGNAL_INFO_ID)==signal_id); this.m_long_prop[SIGNAL_MQL5_PROP_TRADE_MODE] = ::SignalBaseGetInteger(SIGNAL_BASE_TRADE_MODE); this.m_long_prop[SIGNAL_MQL5_PROP_DATE_PUBLISHED] = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_PUBLISHED); this.m_long_prop[SIGNAL_MQL5_PROP_DATE_STARTED] = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_STARTED); this.m_long_prop[SIGNAL_MQL5_PROP_DATE_UPDATED] = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_UPDATED); this.m_long_prop[SIGNAL_MQL5_PROP_LEVERAGE] = ::SignalBaseGetInteger(SIGNAL_BASE_LEVERAGE); this.m_long_prop[SIGNAL_MQL5_PROP_PIPS] = ::SignalBaseGetInteger(SIGNAL_BASE_PIPS); this.m_long_prop[SIGNAL_MQL5_PROP_RATING] = ::SignalBaseGetInteger(SIGNAL_BASE_RATING); this.m_long_prop[SIGNAL_MQL5_PROP_SUBSCRIBERS] = ::SignalBaseGetInteger(SIGNAL_BASE_SUBSCRIBERS); this.m_long_prop[SIGNAL_MQL5_PROP_TRADES] = ::SignalBaseGetInteger(SIGNAL_BASE_TRADES); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_BALANCE)] = ::SignalBaseGetDouble(SIGNAL_BASE_BALANCE); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_EQUITY)] = ::SignalBaseGetDouble(SIGNAL_BASE_EQUITY); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_GAIN)] = ::SignalBaseGetDouble(SIGNAL_BASE_GAIN); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_MAX_DRAWDOWN)] = ::SignalBaseGetDouble(SIGNAL_BASE_MAX_DRAWDOWN); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_PRICE)] = ::SignalBaseGetDouble(SIGNAL_BASE_PRICE); this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_ROI)] = ::SignalBaseGetDouble(SIGNAL_BASE_ROI); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_AUTHOR_LOGIN)] = ::SignalBaseGetString(SIGNAL_BASE_AUTHOR_LOGIN); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_BROKER)] = ::SignalBaseGetString(SIGNAL_BASE_BROKER); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_BROKER_SERVER)]= ::SignalBaseGetString(SIGNAL_BASE_BROKER_SERVER); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_NAME)] = ::SignalBaseGetString(SIGNAL_BASE_NAME); this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_CURRENCY)] = ::SignalBaseGetString(SIGNAL_BASE_CURRENCY); } //+------------------------------------------------------------------+
以前,它是以 false 进行初始化的。 现在,我们打算用 信号对象 ID与当前订阅的有效信号 ID 的比较结果来初始化它。 如果信号已经激活订阅,其 ID 将是来自 MQL5.com 信号数据库“当前已订阅”信号中之一。 如果它们相等,这意味着该信号的订阅处于激活状态 — 比较结果相等则为 true,否则 — false。
由于我正在这里开发一个新的集合,我需要为它定义一个自定义 ID。 在 \MQL5\Include\DoEasy\Defines.mqh 里,加入 MQL5.com 信号服务的信号集合 ID:
//--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID #define COLLECTION_SERIES_ID (0x777F) // Timeseries collection list ID #define COLLECTION_BUFFERS_ID (0x7780) // Indicator buffer collection list ID #define COLLECTION_INDICATORS_ID (0x7781) // Indicator collection list ID #define COLLECTION_INDICATORS_DATA_ID (0x7782) // Indicator data collection list ID #define COLLECTION_TICKSERIES_ID (0x7783) // Tick series collection list ID #define COLLECTION_MBOOKSERIES_ID (0x7784) // DOM series collection list ID #define COLLECTION_MQL5_SIGNALS_ID (0x7785) // MQL5 signals collection list ID //--- Data parameters for file operations
为了能够操控 MQL5.com 信号服务的信号集合,我们需要创建依据信号对象属性进行搜索和排序的方法。 为每个集合创建单独的搜索和排序方法。 所有方法都相同。 在第三篇文章中曾详细讲述过它们。
在 \MQL5\Include\DoEasy\Services\Select.mqh 文件里的 CSelect 类,包含 MQL5 信号对象类文件,并声明操控信号对象集合的新方法:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\Event.mqh" #include "..\Objects\Accounts\Account.mqh" #include "..\Objects\Symbols\Symbol.mqh" #include "..\Objects\PendRequest\PendRequest.mqh" #include "..\Objects\Series\SeriesDE.mqh" #include "..\Objects\Indicators\Buffer.mqh" #include "..\Objects\Indicators\IndicatorDE.mqh" #include "..\Objects\Indicators\DataInd.mqh" #include "..\Objects\Ticks\DataTick.mqh" #include "..\Objects\Book\MarketBookOrd.mqh" #include "..\Objects\MQLSignalBase\MQLSignal.mqh" //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Methods of working with MQL5 signal data | //+------------------------------------------------------------------+ //--- Return the list of MQL5 signals with one of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the MQL5 signal index in the list with the maximum value of the (1) integer, (2) real and (3) string properties static int FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property); static int FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property); static int FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property); //--- Return the MQL5 signal in the list with the minimum value of (1) integer, (2) real and (3) string property static int FindMQLSignalMin(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property); static int FindMQLSignalMin(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property); static int FindMQLSignalMin(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
我们在类主体之外编写它们的实现:
//+------------------------------------------------------------------+ //| Methods of working with MQL5 signal data | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return the list of MQL5 signals with one integer | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); int total=list_source.Total(); for(int i=0; i<total; i++) { CMQLSignal *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; long obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the list of MQL5 signals with one real | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CMQLSignal *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; double obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the list of MQL5 signals with one string | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByMQLSignalProperty(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CMQLSignal *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; string obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the MQL5 signal index in the list | //| with the maximum integer property value | //+------------------------------------------------------------------+ int CSelect::FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CMQLSignal *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMQLSignal *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); long obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the MQL5 signal index in the list | //| with the maximum real property value | //+------------------------------------------------------------------+ int CSelect::FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CMQLSignal *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMQLSignal *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); double obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the MQL5 signal index in the list | //| with the maximum string property value | //+------------------------------------------------------------------+ int CSelect::FindMQLSignalMax(CArrayObj *list_source,ENUM_SIGNAL_MQL5_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CMQLSignal *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMQLSignal *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); string obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the MQL5 signal index in the list | //| with the minimum integer property value | //+------------------------------------------------------------------+ int CSelect::FindMQLSignalMin(CArrayObj* list_source,ENUM_SIGNAL_MQL5_PROP_INTEGER property) { int index=0; CMQLSignal *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMQLSignal *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); long obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the MQL5 signal index in the list | //| with the minimum real property value | //+------------------------------------------------------------------+ int CSelect::FindMQLSignalMin(CArrayObj* list_source,ENUM_SIGNAL_MQL5_PROP_DOUBLE property) { int index=0; CMQLSignal *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMQLSignal *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); double obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the MQL5 signal index in the list | //| with the minimum string property value | //+------------------------------------------------------------------+ int CSelect::FindMQLSignalMin(CArrayObj* list_source,ENUM_SIGNAL_MQL5_PROP_STRING property) { int index=0; CMQLSignal *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMQLSignal *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); string obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+
正如已经提过的,所有这些方法(对于每个函数库对象类都是相同的)已被研究过多次了。 参看 第三篇文章获取更详细内容。
现在一切准备就绪,可以开发 MQL5.com 信号服务的信号对象集合类了。
MQL5 信号对象的集合类
在 \MQL5\Include\DoEasy\Collections\ 函数库文件夹之下,于 MQLSignalsCollection.mqh 文件里创建新类 CMQLSignalsCollection。
在类文件里,包含其操作所需的所有类文件:
//+------------------------------------------------------------------+ //| MQLSignalsCollection.mqh | //| Copyright 2021, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\MQLSignalBase\MQLSignal.mqh" //+------------------------------------------------------------------+
该类应从所有函数库对象的基准对象中派生:
//+------------------------------------------------------------------+ //| MQL5 signal object collection | //+------------------------------------------------------------------+ class CMQLSignalsCollection : public CBaseObj { }
我们来看看类主体,并分析一下它包含的方法:
//+------------------------------------------------------------------+ //| MQLSignalsCollection.mqh | //| Copyright 2021, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\MQLSignalBase\MQLSignal.mqh" //+------------------------------------------------------------------+ //| MQL5 signal object collection | //+------------------------------------------------------------------+ class CMQLSignalsCollection : public CBaseObj { private: CListObj m_list; // List of MQL5 signal objects int m_signals_base_total; // Number of signals in the MQL5 signal database //--- Subscribe to a signal bool Subscribe(const long signal_id); //--- Set the flag allowing synchronization without confirmation dialog bool CurrentSetConfirmationsDisableFlag(const bool flag); //--- Set the flag of copying Stop Loss and Take Profit bool CurrentSetSLTPCopyFlag(const bool flag); //--- Set the flag allowing the copying of signals by subscription bool CurrentSetSubscriptionEnabledFlag(const bool flag); public: //--- Return (1) itself and (2) the DOM series collection list CMQLSignalsCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_SIGNAL_MQL5_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMQLSignalProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_SIGNAL_MQL5_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMQLSignalProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_SIGNAL_MQL5_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMQLSignalProperty(this.GetList(),property,value,mode); } //--- Return the number of MQL5 signal objects in the list int DataTotal(void) const { return this.m_list.Total(); } //--- Return the pointer to the MQL5 signal object (1) by ID, (2) by name and (3) by index in the list CMQLSignal *GetMQLSignal(const long id); CMQLSignal *GetMQLSignal(const string name); CMQLSignal *GetMQLSignal(const int index) { return this.m_list.At(index); } //--- Create the collection list of MQL5 signal objects bool CreateCollection(void); //--- Update the collection list of MQL5 signal objects void Refresh(const bool messages=true); //--- Display (1) the complete and (2) short collection description in the journal void Print(void); void PrintShort(const bool list=false,const bool paid=true,const bool free=true); //--- Constructor CMQLSignalsCollection(); //--- Subscribe to a signal by (1) ID and (2) signal name bool SubscribeByID(const long signal_id); bool SubscribeByName(const string signal_name); //+----------------------------------------------------------------------------+ //| Methods of working with the current signal the subscription is active for | //+----------------------------------------------------------------------------+ //--- Return the flag allowing working with the signal service bool ProgramIsAllowed(void) { return (bool)::MQLInfoInteger(MQL_SIGNALS_ALLOWED); } //--- Unsubscribe from the current signal bool CurrentUnsubscribe(void); //--- Set the percentage for converting deal volume bool CurrentSetEquityLimit(const double value); //--- Set the market order slippage used when synchronizing positions and copying deals bool CurrentSetSlippage(const double value); //--- Set deposit limitations (in %) bool CurrentSetDepositPercent(const int value); //--- Return the percentage for converting deal volume double CurrentEquityLimit(void) { return ::SignalInfoGetDouble(SIGNAL_INFO_EQUITY_LIMIT); } //--- Return the market order slippage used when synchronizing positions and copying deals double CurrentSlippage(void) { return ::SignalInfoGetDouble(SIGNAL_INFO_SLIPPAGE); } //--- Return the flag allowing synchronization without confirmation dialog bool CurrentConfirmationsDisableFlag(void) { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_CONFIRMATIONS_DISABLED); } //--- Return the flag of copying Stop Loss and Take Profit bool CurrentSLTPCopyFlag(void) { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_COPY_SLTP); } //--- Return deposit limitations (in %) int CurrentDepositPercent(void) { return (int)::SignalInfoGetInteger(SIGNAL_INFO_DEPOSIT_PERCENT); } //--- Return the flag allowing the copying of signals by subscription bool CurrentSubscriptionEnabledFlag(void) { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_SUBSCRIPTION_ENABLED); } //--- Return the limitation by funds for a signal double CurrentVolumePercent(void) { return ::SignalInfoGetDouble(SIGNAL_INFO_VOLUME_PERCENT); } //--- Return the signal ID long CurrentID(void) { return ::SignalInfoGetInteger(SIGNAL_INFO_ID); } //--- Return the flag of agreeing to the terms of use of the Signals service bool CurrentTermsAgreeFlag(void) { return (bool)::SignalInfoGetInteger(SIGNAL_INFO_TERMS_AGREE); } //--- Return the signal name string CurrentName(void) { return ::SignalInfoGetString(SIGNAL_INFO_NAME); } //--- Enable synchronization without the confirmation dialog bool CurrentSetConfirmationsDisableON(void) { return this.CurrentSetConfirmationsDisableFlag(true); } //--- Disable synchronization without the confirmation dialog bool CurrentSetConfirmationsDisableOFF(void){ return this.CurrentSetConfirmationsDisableFlag(false); } //--- Enable copying Stop Loss and Take Profit bool CurrentSetSLTPCopyON(void) { return this.CurrentSetSLTPCopyFlag(true); } //--- Disable copying Stop Loss and Take Profit bool CurrentSetSLTPCopyOFF(void) { return this.CurrentSetSLTPCopyFlag(false); } //--- Enable copying deals by subscription bool CurrentSetSubscriptionEnableON(void) { return this.CurrentSetSubscriptionEnabledFlag(true); } //--- Disable copying deals by subscription bool CurrentSetSubscriptionEnableOFF(void) { return this.CurrentSetSubscriptionEnabledFlag(false); } //--- Return the description of enabling working with signals for the launched program string ProgramIsAllowedDescription(void); //--- Return the percentage description for converting the deal volume string CurrentEquityLimitDescription(void); //--- Return the description of the market order slippage used when synchronizing positions and copying deals string CurrentSlippageDescription(void); //--- Return the description of the limitation by funds for a signal string CurrentVolumePercentDescription(void); //--- Return the description of the flag allowing synchronization without confirmation dialog string CurrentConfirmationsDisableFlagDescription(void); //--- Return the description of the flag of copying Stop Loss and Take Profit string CurrentSLTPCopyFlagDescription(void); //--- Return the description of the deposit limitations (in %) string CurrentDepositPercentDescription(void); //--- Return the description of the flag allowing the copying of signals by subscription string CurrentSubscriptionEnabledFlagDescription(void); //--- Return the description of the signal ID string CurrentIDDescription(void); //--- Return the description of the flag of agreeing to the terms of use of the Signals service string CurrentTermsAgreeFlagDescription(void); //--- Return the description of the signal name string CurrentNameDescription(void); //--- Display the parameters of signal copying settings in the journal void CurrentSubscriptionParameters(void); //--- }; //+------------------------------------------------------------------+
在类的私密部分提供存储 MQL5 信号对象的列表对象,以及辅助变量和方法。
在类的公开部分,提供操控对象集合列表的标准方法,以及两种依据 ID 和名称选择信号并订阅的方法。 类的公开部分还提供了操控处于订阅激活状态的当前信号的方法。
我们来看看一些方法的实现。
在类的构造方法里,清除集合列表,为其设置已排序列表标志,为列表设置 MQL5 信号对象集合的 ID,写下 MQL5.com 信号数据库中的信号总数,并调用集合创建方法。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CMQLSignalsCollection::CMQLSignalsCollection() { this.m_list.Clear(); this.m_list.Sort(); this.m_list.Type(COLLECTION_MQL5_SIGNALS_ID); this.m_signals_base_total=::SignalBaseTotal(); this.CreateCollection(); } //+------------------------------------------------------------------+
由于我们不打算自动更新信号列表,也不会托管给函数库,故列表更新方法就足够了。 读取数据库中出现的所有信号,并在方法中将其发送集合列表。 如果用户希望从 MQL5.com 信号数据库中得到更新的信号列表,则从集合中接收任何数据之前,他们必须自己调用 Refresh() 更新方法。 不过,无论如何我们的集合创建方法都会提供与典型函数库一套集合方法的兼容性。 该方法本身将简单地清除列表,并调用集合更新方法。 从集合创建方法第一次调用 Refresh() 方法之后,集合列表就已被填充完毕,可以处理了。 如果为了搜索可能的新信号应该更新集合列表,只需在访问集合列表之前调用 Refresh() 方法。
集合创建方法:
//+------------------------------------------------------------------+ //| Create the collection list of MQL5 signal objects | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CreateCollection(void) { this.m_list.Clear(); this.Refresh(false); if(m_list.Total()>0) { ::Print(CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION)," ",CMessage::Text(MSG_LIB_TEXT_TS_TEXT_CREATED_OK)); return true; } return false; } //+------------------------------------------------------------------+
此处,我们清除信号集合列表,并用来自 MQL5.com 信号数据库的信号填充。 如果集合列表中的信号数量超过零(列表已满),显示集合列表创建成功的消息,并返回 true。
否则,返回 false。
更新集合列表的方法:
//+------------------------------------------------------------------+ //| Update the collection list of MQL5 signal objects | //+------------------------------------------------------------------+ void CMQLSignalsCollection::Refresh(const bool messages=true) { this.m_signals_base_total=::SignalBaseTotal(); //--- loop through all signals in the signal database for(int i=0;i<this.m_signals_base_total;i++) { //--- Select a signal from the signal database by the loop index if(!::SignalBaseSelect(i)) continue; //--- Get the current signal ID and //--- create a new MQL5 signal object based on it long id=::SignalBaseGetInteger(SIGNAL_BASE_ID); CMQLSignal *signal=new CMQLSignal(id); if(signal==NULL) continue; //--- Set the sorting flag for the list by signal ID and, //--- if such a signal is already present in the collection list, //--- remove the created object and go to the next loop iteration m_list.Sort(SORT_BY_SIGNAL_MQL5_ID); if(this.m_list.Search(signal)!=WRONG_VALUE) { delete signal; continue; } //--- If failed to add a new signal object to the collection list, //--- remove the created object and go to the next loop iteration if(!this.m_list.InsertSort(signal)) { delete signal; continue; } //--- If an MQL5 signal object is successfully added to the collection //--- and the new object message flag is set in the parameters passed to the method, //--- display a message about a newly found signal else if(messages) { ::Print(DFUN,CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_NEW),":"); signal.PrintShort(true); } } } //+------------------------------------------------------------------+
该方法的逻辑在代码注释中已有详述。 简言之:该方法接收标志,指示检测到新信号之后是否需要通知。 由于该方法不会清除集合列表,因此只能将新发现的信号添加到其中。 如果消息标志已设置,则日志里会显示有关新检测到信号,并成功添加到列表中的消息。
目前,这是最简单的方法,务须提供更新现有信号的参数 — 您能够在您的程序中自行更新它们,或依据其 ID 访问信号对象,并为其属性设置新值。 稍后,我将添加按时间参数自动更新现有信号。 如果 MQL5.com 信号集合类是刚需,我将实现发送有关新信号的事件,并更改跟踪信号的参数。
该方法依据信号 ID 返回指向 MQL5 信号对象的指针:
//+------------------------------------------------------------------+ //| Return the pointer to an MQL5 signal object by an ID | //+------------------------------------------------------------------+ CMQLSignal *CMQLSignalsCollection::GetMQLSignal(const long id) { CArrayObj *list=GetList(SIGNAL_MQL5_PROP_ID,id,EQUAL); return(list!=NULL ? list.At(0) : NULL); } //+------------------------------------------------------------------+
依据信号 ID 获取 MQL5 信号对象列表, 并从得到的列表中返回单一对象,或 NULL。
该方法依据信号名称返回指向 MQL5 信号对象的指针:
//+------------------------------------------------------------------+ //| Return the pointer to an MQL5 signal object by a name | //+------------------------------------------------------------------+ CMQLSignal *CMQLSignalsCollection::GetMQLSignal(const string name) { CArrayObj *list=GetList(SIGNAL_MQL5_PROP_NAME,name,EQUAL); return(list!=NULL ? list.At(0) : NULL); } //+------------------------------------------------------------------+
依据信号名称获取 MQL5 信号对象列表,并从得到的列表中返回单一对象,或者返回 NULL。
该方法将完整的集合列表反馈到日志:
//+------------------------------------------------------------------+ //| Display complete collection description to the journal | //+------------------------------------------------------------------+ void CMQLSignalsCollection::Print(void) { ::Print(CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION),":"); for(int i=0;i<this.m_list.Total();i++) { CMQLSignal *signal=this.m_list.At(i); if(signal==NULL) continue; signal.Print(); } } //+------------------------------------------------------------------+
标题会首先显示。 然后,循环遍历集合列表,获取下一个 MQL 信号对象,并显示其完整描述。
该方法返回显示在日志里的集合列表简要:
//+------------------------------------------------------------------+ //| Display the short collection description in the journal | //+------------------------------------------------------------------+ void CMQLSignalsCollection::PrintShort(const bool list=false,const bool paid=true,const bool free=true) { //--- Display the header in the journal ::Print(CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_MQL5_SIGNAL_COLLECTION),":"); //--- If the list is full, display short descriptions of all signals in the collection //--- according to the flags indicating the necessity to display paid and free signals if(list) for(int i=0;i<this.m_list.Total();i++) { CMQLSignal *signal=this.m_list.At(i); if(signal==NULL || (signal.Price()>0 && !paid) || (signal.Price()==0 && !free)) continue; signal.PrintShort(true); } //--- If not the signal list else { //--- Sort the list by signal price this.m_list.Sort(SORT_BY_SIGNAL_MQL5_PRICE); //--- Get the list of free signals and their number CArrayObj *list_free=this.GetList(SIGNAL_MQL5_PROP_PRICE,0,EQUAL); int num_free=(list_free==NULL ? 0 : list_free.Total()); //--- Sort the list by signal price this.m_list.Sort(SORT_BY_SIGNAL_MQL5_PRICE); //--- Get the list of paid signals and their number CArrayObj *list_paid=this.GetList(SIGNAL_MQL5_PROP_PRICE,0,MORE); int num_paid=(list_paid==NULL ? 0 : list_paid.Total()); //--- Display the number of free and paid signals in the collection in the journal ::Print ( "- ",CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_FREE),": ",(string)num_free, ", ",CMessage::Text(MSG_MQLSIG_COLLECTION_TEXT_SIGNALS_PAID),": ",(string)num_paid ); } } //+------------------------------------------------------------------+
根据所传递的标志,该方法在日志中显示各种消息和列表。
标题会排第一。 如果列表标志已设置,则日志会显示来自集合信号的简述。 会考虑付费信号和免费信号的标志。 取决于它们的状态,日志显示所有信号,或仅显示付费信号,或仅显示免费信号。
如果不显示列表说明,则标题后面是集合列表中免费和付费信号的总数。
该方法订阅信号(私密方法),和取消订阅(公开方法):
//+------------------------------------------------------------------+ //| Subscribe to a signal | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::Subscribe(const long signal_id) { //--- If working with signals is disabled for a program, //--- display the appropriate message and the recommendation to check the program settings if(!this.ProgramIsAllowed()) { ::Print(DFUN,CMessage::Text(MSG_SIGNAL_INFO_ERR_SIGNAL_NOT_ALLOWED)); ::Print(DFUN,CMessage::Text(MSG_SIGNAL_INFO_TEXT_CHECK_SETTINGS)); return false; } //--- If failed to subscribe to a signal, display the error message and return 'false' ::ResetLastError(); if(!::SignalSubscribe(signal_id)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Subscription successful. Display the successful signal subscription message and return 'true' ::Print(CMessage::Text(MSG_SIGNAL_INFO_TEXT_SIGNAL_SUBSCRIBED)," ID ",(string)this.CurrentID()," \"",CurrentName(),"\""); return true; } //+------------------------------------------------------------------+ //| Unsubscribe from a subscribed signal | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentUnsubscribe(void) { //--- If working with signals is disabled for a program, //--- display the appropriate message and the recommendation to check the program settings if(!this.ProgramIsAllowed()) { ::Print(DFUN,CMessage::Text(MSG_SIGNAL_INFO_ERR_SIGNAL_NOT_ALLOWED)); ::Print(DFUN,CMessage::Text(MSG_SIGNAL_INFO_TEXT_CHECK_SETTINGS)); return false; } //--- Remember an ID and a name of the current signal ::ResetLastError(); long id=this.CurrentID(); string name=this.CurrentName(); //--- If the ID is zero (no subscription), return 'true' if(id==0) return true; //--- If failed to unsubscribe from a signal, display the error message and return 'false' if(!::SignalUnsubscribe()) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Unsubscribed from a signal successfully. Display the message about successfully unsubscribing from a signal and return 'true' ::Print(CMessage::Text(MSG_SIGNAL_INFO_TEXT_SIGNAL_UNSUBSCRIBED)," ID ",(string)id," \"",name,"\""); return true; } //+------------------------------------------------------------------+
该方法的逻辑已在方法清单里有详述。
依据信号 ID 订阅信号的公开方法:
//+------------------------------------------------------------------+ //| Subscribe to a signal by ID | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::SubscribeByID(const long signal_id) { CMQLSignal *signal=GetMQLSignal(signal_id); if(signal==NULL) { ::Print(DFUN,CMessage::Text(MSG_MQLSIG_COLLECTION_ERR_FAILED_GET_SIGNAL),": ",signal_id); return false; } return this.Subscribe(signal.ID()); } //+------------------------------------------------------------------+
此处,我们依据传递给方法的 ID 获取指向集合列表中 MQL5 信号对象的指针,并返回上述信号订阅私密方法的结果。
依据信号名称订阅信号的公开方法:
//+------------------------------------------------------------------+ //| Subscribe to a signal by signal name | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::SubscribeByName(const string signal_name) { CMQLSignal *signal=GetMQLSignal(signal_name); if(signal==NULL) { ::Print(DFUN,CMessage::Text(MSG_MQLSIG_COLLECTION_ERR_FAILED_GET_SIGNAL),": \"",signal_name,"\""); return false; } return this.Subscribe(signal.ID()); } //+------------------------------------------------------------------+
此处,我们依据传递给方法的信号名称获得指向集合列表中 MQL5 信号对象的指针(名称应事先知道),并返回上述信号订阅私密方法的结果。
该方法设置信号跟单值:
//+------------------------------------------------------------------+ //| Set the percentage for converting a deal volume | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetEquityLimit(const double value) { ::ResetLastError(); if(!::SignalInfoSetDouble(SIGNAL_INFO_EQUITY_LIMIT,value)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Define the slippage used to set | //| market orders when synchronizing positions and copying deals | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetSlippage(const double value) { ::ResetLastError(); if(!::SignalInfoSetDouble(SIGNAL_INFO_SLIPPAGE,value)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Set the flag enabling synchronization | //| without confirmation dialog | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetConfirmationsDisableFlag(const bool flag) { ::ResetLastError(); if(!::SignalInfoSetInteger(SIGNAL_INFO_CONFIRMATIONS_DISABLED,flag)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Set the flag of copying Stop Loss and Take Profit | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetSLTPCopyFlag(const bool flag) { ::ResetLastError(); if(!::SignalInfoSetInteger(SIGNAL_INFO_COPY_SLTP,flag)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Set deposit limitations (in %) | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetDepositPercent(const int value) { ::ResetLastError(); if(!::SignalInfoSetInteger(SIGNAL_INFO_DEPOSIT_PERCENT,value)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+ //| Set the flag allowing the copying of signals by subscription | //+------------------------------------------------------------------+ bool CMQLSignalsCollection::CurrentSetSubscriptionEnabledFlag(const bool flag) { ::ResetLastError(); if(!::SignalInfoSetInteger(SIGNAL_INFO_SUBSCRIPTION_ENABLED,flag)) { CMessage::ToLog(DFUN,::GetLastError(),true); return false; } return true; } //+------------------------------------------------------------------+
在所有方法里用到的设置数值的函数 SignalInfoSetDouble() 和 SignalInfoSetInteger()。 如果数值设置未成功,则这些方法会显示错误描述,并返回 false。 如果设置成功,方法返回 true。
该方法返回所设置信号跟单参数的说明:
//+------------------------------------------------------------------+ //| Return the description of the permission to work with signals | //| for the currently launched program | //+------------------------------------------------------------------+ string CMQLSignalsCollection::ProgramIsAllowedDescription(void) { return ( CMessage::Text(MSG_SIGNAL_INFO_SIGNALS_PERMISSION)+": "+ (this.ProgramIsAllowed() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ); } //+------------------------------------------------------------------+ //| Return the percentage description for converting the deal volume | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentEquityLimitDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_EQUITY_LIMIT)+": "+::DoubleToString(this.CurrentEquityLimit(),2)+"%"; } //+------------------------------------------------------------------+ //| Return the description of the slippage used to set | //| market orders when synchronizing positions and copying deals | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentSlippageDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_SLIPPAGE)+": "+CMessage::Text(MSG_LIB_TEXT_BAR_SPREAD)+" * "+::DoubleToString(this.CurrentSlippage(),2); } //+------------------------------------------------------------------+ //| Return the description of the limitation by funds for a signal | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentVolumePercentDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_VOLUME_PERCENT)+": "+::DoubleToString(this.CurrentVolumePercent(),2)+"%"; } //+------------------------------------------------------------------+ //| Return the description of the flag enabling synchronization | //| without confirmation dialog | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentConfirmationsDisableFlagDescription(void) { return ( CMessage::Text(MSG_SIGNAL_INFO_CONFIRMATIONS_DISABLED)+": "+ (this.CurrentConfirmationsDisableFlag() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ); } //+----------------------------------------------------------------------------+ //| Return the description of the flag of copying Stop Loss and Take Profit | //+----------------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentSLTPCopyFlagDescription(void) { return ( CMessage::Text(MSG_SIGNAL_INFO_COPY_SLTP)+": "+ (this.CurrentSLTPCopyFlag() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ); } //+------------------------------------------------------------------+ //| Return the description of the deposit limitations (in %) | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentDepositPercentDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_DEPOSIT_PERCENT)+": "+(string)this.CurrentDepositPercent()+"%"; } //+------------------------------------------------------------------+ //| Return the description of the flag enabling | //| deal copying by subscription | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentSubscriptionEnabledFlagDescription(void) { return ( CMessage::Text(MSG_SIGNAL_INFO_SUBSCRIPTION_ENABLED)+": "+ (this.CurrentSubscriptionEnabledFlag() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ); } //+------------------------------------------------------------------+ //| Return the signal ID description | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentIDDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_ID)+": "+(this.CurrentID()>0 ? (string)this.CurrentID() : CMessage::Text(MSG_LIB_PROP_EMPTY)); } //+------------------------------------------------------------------+ //| Return the description of the flag indicating | //| consent to the terms of use of the Signals service | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentTermsAgreeFlagDescription(void) { return ( CMessage::Text(MSG_SIGNAL_INFO_TERMS_AGREE)+": "+ (this.CurrentTermsAgreeFlag() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ); } //+------------------------------------------------------------------+ //| Return the description of the signal name | //+------------------------------------------------------------------+ string CMQLSignalsCollection::CurrentNameDescription(void) { return CMessage::Text(MSG_SIGNAL_INFO_NAME)+": "+(this.CurrentName()!="" ? this.CurrentName() : CMessage::Text(MSG_LIB_PROP_EMPTY)); } //+------------------------------------------------------------------+
在每个方法中创建含有参数说明标题及其当前值的字符串。
该方法在日志中显示所设置的信号跟单参数:
//+------------------------------------------------------------------+ //| Display the parameters of signal copying settings in the journal | //+------------------------------------------------------------------+ void CMQLSignalsCollection::CurrentSubscriptionParameters(void) { ::Print("============= ",CMessage::Text(MSG_SIGNAL_INFO_PARAMETERS)," ============="); ::Print(this.ProgramIsAllowedDescription()); ::Print(this.CurrentTermsAgreeFlagDescription()); ::Print(this.CurrentSubscriptionEnabledFlagDescription()); ::Print(this.CurrentConfirmationsDisableFlagDescription()); ::Print(this.CurrentSLTPCopyFlagDescription()); ::Print(this.CurrentSlippageDescription()); ::Print(this.CurrentEquityLimitDescription()); ::Print(this.CurrentDepositPercentDescription()); ::Print(this.CurrentVolumePercentDescription()); ::Print(this.CurrentIDDescription()); ::Print(this.CurrentNameDescription()); ::Print(""); } //+------------------------------------------------------------------+
首先显示标题,然后逐一显示信号跟单的所有参数。 参数由上面研究过的相应方法返回。
MQL5 信号对象集合类的创建至此完毕。
我也许在以后会改进它。 不过,我将暂时保留这种方式,直到我能判定其需求是否很强烈。
为了将交易信号集合类与“外部世界”连接起来,我们需要在 \MQL5\Include\DoEasy\Engine.mqh 函数库主对象类 CEngine 中开发完成操控它们的方法。
将交易信号集合类的文件集中到 CEngine 对象类文件之中,并声明 MQL5 信号集合类对象:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "Collections\TimeSeriesCollection.mqh" #include "Collections\BuffersCollection.mqh" #include "Collections\IndicatorsCollection.mqh" #include "Collections\TickSeriesCollection.mqh" #include "Collections\BookSeriesCollection.mqh" #include "Collections\MQLSignalsCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CTimeSeriesCollection m_time_series; // Timeseries collection CBuffersCollection m_buffers; // Collection of indicator buffers CIndicatorsCollection m_indicators; // Indicator collection CTickSeriesCollection m_tick_series; // Collection of tick series CMBookSeriesCollection m_book_series; // Collection of DOM series CMQLSignalsCollection m_signals_mql5; // Collection of MQL5.com Signals service signals CResourceCollection m_resource; // Resource list CTradingControl m_trading; // Trading management object CPause m_pause; // Pause object CArrayObj m_list_counters; // List of timer counters
在该类的公开部分,声明并实现操控 DOM 快照集合类的新方法,和操控交易信号集合的方法:
//--- Update the DOM series of a specified symbol void MBookSeriesRefresh(const string symbol,const long time_msc) { this.m_book_series.Refresh(symbol,time_msc); } //--- Return (1) the DOM series of a specified symbol, the DOM (2) by index and (3) by time in milliseconds CMBookSeries *GetMBookSeries(const string symbol) { return this.m_book_series.GetMBookseries(symbol); } CMBookSnapshot *GetMBook(const string symbol,const int index) { return this.m_book_series.GetMBook(symbol,index); } CMBookSnapshot *GetMBook(const string symbol,const long time_msc) { return this.m_book_series.GetMBook(symbol,time_msc);} //--- Return the volume of a (1) buy and (2) sell DOM specified by symbol and index long MBookVolumeBuy(const string symbol,const int index); long MBookVolumeSell(const string symbol,const int index); //--- Return the increased precision volume of a (1) buy and (2) sell DOM specified by symbol and index double MBookVolumeBuyReal(const string symbol,const int index); double MBookVolumeSellReal(const string symbol,const int index); //--- Return the volume of a (1) buy and (2) sell DOM specified by symbol and time in milliseconds long MBookVolumeBuy(const string symbol,const long time_msc); long MBookVolumeSell(const string symbol,const long time_msc); //--- Return the increased precision volume of a (1) buy and (2) sell DOM specified by symbol and time in milliseconds double MBookVolumeBuyReal(const string symbol,const long time_msc); double MBookVolumeSellReal(const string symbol,const long time_msc); //--- Return (1) the collection of mql5.com Signals service signals and (2) the list of signals from the mql5.com Signals service signal collection CMQLSignalsCollection *GetSignalsMQL5Collection(void) { return &this.m_signals_mql5; } CArrayObj *GetListSignalsMQL5(void) { return this.m_signals_mql5.GetList(); } //--- Return the list of (1) paid and (2) free signals CArrayObj *GetListSignalsMQL5Paid(void) { return this.m_signals_mql5.GetList(SIGNAL_MQL5_PROP_PRICE,0,MORE); } CArrayObj *GetListSignalsMQL5Free(void) { return this.m_signals_mql5.GetList(SIGNAL_MQL5_PROP_PRICE,0,EQUAL);} //--- (1) Create and (2) update the collection of mql5.com Signals service signals bool SignalsMQL5Create(void) { return this.m_signals_mql5.CreateCollection(); } void SignalsMQL5Refresh(void) { this.m_signals_mql5.Refresh(); } //--- Subscribe to a signal by (1) ID and (2) signal name bool SignalsMQL5Subscribe(const long signal_id) { return this.m_signals_mql5.SubscribeByID(signal_id);} bool SignalsMQL5Subscribe(const string signal_name) { return this.m_signals_mql5.SubscribeByName(signal_name);} //--- Unsubscribe from the current signal bool SignalsMQL5Unsubscribe(void) { return this.m_signals_mql5.CurrentUnsubscribe(); } //--- Return (1) ID and (2) the name of the current signal subscription is performed to long SignalsMQL5CurrentID(void) { return this.m_signals_mql5.CurrentID(); } string SignalsMQL5CurrentName(void) { return this.m_signals_mql5.CurrentName(); } //--- Set the percentage for converting deal volume bool SignalsMQL5CurrentSetEquityLimit(const double value) { return this.m_signals_mql5.CurrentSetEquityLimit(value); } //--- Set the market order slippage used when synchronizing positions and copying deals bool SignalsMQL5CurrentSetSlippage(const double value) { return this.m_signals_mql5.CurrentSetSlippage(value); } //--- Set deposit limitations (in %) bool SignalsMQL5CurrentSetDepositPercent(const int value) { return this.m_signals_mql5.CurrentSetDepositPercent(value); } //--- Enable synchronization without the confirmation dialog bool SignalsMQL5CurrentSetConfirmationsDisableON(void) { return this.m_signals_mql5.CurrentSetConfirmationsDisableON();} //--- Disable synchronization without the confirmation dialog bool SignalsMQL5CurrentSetConfirmationsDisableOFF(void) { return this.m_signals_mql5.CurrentSetConfirmationsDisableOFF();} //--- Enable copying Stop Loss and Take Profit bool SignalsMQL5CurrentSetSLTPCopyON(void) { return this.m_signals_mql5.CurrentSetSLTPCopyON(); } //--- Disable copying Stop Loss and Take Profit bool SignalsMQL5CurrentSetSLTPCopyOFF(void) { return this.m_signals_mql5.CurrentSetSLTPCopyOFF(); } //--- Enable copying deals by subscription bool SignalsMQL5CurrentSetSubscriptionEnableON(void) { return this.m_signals_mql5.CurrentSetSubscriptionEnableON(); } //--- Disable copying deals by subscription bool SignalsMQL5CurrentSetSubscriptionEnableOFF(void) { return this.m_signals_mql5.CurrentSetSubscriptionEnableOFF();} //--- Display (1) the complete, (2) short collection description in the journal and (3) parameters of the signal copying settings void SignalsMQL5Print(void) { m_signals_mql5.Print(); } void SignalsMQL5PrintShort(const bool list=false,const bool paid=true,const bool free=true) { m_signals_mql5.PrintShort(list,paid,free); } void SignalsMQL5CurrentSubscriptionParameters(void) { this.m_signals_mql5.CurrentSubscriptionParameters();} //--- Return (1) the buffer collection and (2) the buffer list from the collection
所实现的方法返回调用同名集合相应方法的结果。
我们看一下实现返回指定 DOM 快照的指定交易量的方法:
//+------------------------------------------------------------------+ //| Return the buy volume of a DOM | //| specified by symbol and index | //+------------------------------------------------------------------+ long CEngine::MBookVolumeBuy(const string symbol,const int index) { CMBookSnapshot *mbook=this.GetMBook(symbol,index); return(mbook!=NULL ? mbook.VolumeBuy() : 0); } //+------------------------------------------------------------------+ //| Return the sell volume of a DOM | //| specified by symbol and index | //+------------------------------------------------------------------+ long CEngine::MBookVolumeSell(const string symbol,const int index) { CMBookSnapshot *mbook=this.GetMBook(symbol,index); return(mbook!=NULL ? mbook.VolumeSell() : 0); } //+------------------------------------------------------------------+ //| Return the extended accuracy buy volume | //| of a DOM specified by symbol and index | //+------------------------------------------------------------------+ double CEngine::MBookVolumeBuyReal(const string symbol,const int index) { CMBookSnapshot *mbook=this.GetMBook(symbol,index); return(mbook!=NULL ? mbook.VolumeBuyReal() : 0); } //+------------------------------------------------------------------+ //| Return the extended accuracy sell volume | //| of a DOM specified by symbol and index | //+------------------------------------------------------------------+ double CEngine::MBookVolumeSellReal(const string symbol,const int index) { CMBookSnapshot *mbook=this.GetMBook(symbol,index); return(mbook!=NULL ? mbook.VolumeSellReal() : 0); } //+------------------------------------------------------------------+ //| Return the buy volume of a DOM | //| specified by symbol and time in milliseconds | //+------------------------------------------------------------------+ long CEngine::MBookVolumeBuy(const string symbol,const long time_msc) { CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc); return(mbook!=NULL ? mbook.VolumeBuy() : 0); } //+------------------------------------------------------------------+ //| Return the sell volume of a DOM | //| specified by symbol and time in milliseconds | //+------------------------------------------------------------------+ long CEngine::MBookVolumeSell(const string symbol,const long time_msc) { CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc); return(mbook!=NULL ? mbook.VolumeSell() : 0); } //+------------------------------------------------------------------+ //| Return the extended accuracy buy volume | //| of a DOM specified by symbol and time in milliseconds | //+------------------------------------------------------------------+ double CEngine::MBookVolumeBuyReal(const string symbol,const long time_msc) { CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc); return(mbook!=NULL ? mbook.VolumeBuyReal() : 0); } //+------------------------------------------------------------------+ //| Return the extended accuracy sell volume | //| of a DOM specified by symbol and time in milliseconds | //+------------------------------------------------------------------+ double CEngine::MBookVolumeSellReal(const string symbol,const long time_msc) { CMBookSnapshot *mbook=this.GetMBook(symbol,time_msc); return(mbook!=NULL ? mbook.VolumeSellReal() : 0); } //+------------------------------------------------------------------+
此处的一切都很简单。 所有方法背后的逻辑都是相同的:首先,我们利用先前实现的 GetMBook() 方法依据品种和索引,或以毫秒为单位的时间,从集合列表中获取 DOM 快照对象,然后从所获 DOM 快照对象方法返回对应 DOM 的交易量,如果获取对象失败则返回零值。
这些都是至今的改进和变化。
测试
我们来测试 MQL5.com 信号 集合的创建。 测试应如下执行:
从信号数据库中获取完整列表,仅显示免费信号列表,在列表中找到最有盈利潜力的信号,并订阅它。 如果订阅成功,我们将显示当前信号的参数,和订阅时设置的信号跟单参数。 在下一次即时报价时取消订阅当前信号。
为了执行测试,我将取用上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part66\ 中,命名为 TestDoEasyPart66.mq5。
在 EA 的输入列表里含有允许用户选择操控 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
在之前的文章中,所有操控信号的检查都是在 EA 的OnInit() 处理程序中完成的。 今天,我们将在 OnTick() 中操作。
因此,我们从 OnInit() 处理程序中删除不必要的测试代码模块:
CArrayObj *list=new CArrayObj(); if(list!=NULL) { //--- request the total number of signals in the signal database int total=SignalBaseTotal(); //--- loop through all signals for(int i=0;i<total;i++) { //--- select a signal for further operation if(!SignalBaseSelect(i)) continue; long id=SignalBaseGetInteger(SIGNAL_BASE_ID); CMQLSignal *signal=new CMQLSignal(id); if(signal==NULL) continue; if(!list.Add(signal)) { delete signal; continue; } } //--- display all profitable free signals with a non-zero number of subscribers Print(""); static bool done=false; for(int i=0;i<list.Total();i++) { CMQLSignal *signal=list.At(i); if(signal==NULL) continue; if(signal.Price()>0 || signal.Subscribers()==0) continue; //--- The very first suitable signal is fully displayed in the journal if(!done) { signal.Print(); done=true; } //--- Short descriptions are displayed for the rest else signal.PrintShort(); } delete list; } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
OnTick() 处理程序定义了一个新的测试代码模块,满足本章节开头讲述的所有测试条件:
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Remove EA graphical objects by an object name prefix ObjectsDeleteAll(0,prefix); Comment(""); //--- Deinitialize library engine.OnDeinit(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Handle the NewTick event in the library engine.OnTick(rates_data); //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Working in the timer PressButtonsControl(); // Button pressing control engine.EventsHandling(); // Working with events } //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); // Trailing positions TrailingOrders(); // Trailing pending orders } //--- Search for available signals in the database and check the ability to subscribe to a signal by its name static bool done=false; //--- If the first launch and working with signals is enabled in EA custom settings if(InpUseMqlSignals && !done) { //--- Display the list of all free signals in the journal Print(""); engine.GetSignalsMQL5Collection().PrintShort(true,false,true); //--- Get the list of free signals CArrayObj *list=engine.GetListSignalsMQL5Free(); //--- If the list is obtained if(list!=NULL) { //--- Find a signal with the maximum growth in % in the list int index_max_gain=CSelect::FindMQLSignalMax(list,SIGNAL_MQL5_PROP_GAIN); CMQLSignal *signal_max_gain=list.At(index_max_gain); //--- If the signal is found if(signal_max_gain!=NULL) { //--- Display the full signal description in the journal signal_max_gain.Print(); //--- If managed to subscribe to a signal if(engine.SignalsMQL5Subscribe(signal_max_gain.ID())) { //--- Set subscription parameters //--- Enable copying deals by subscription engine.SignalsMQL5CurrentSetSubscriptionEnableON(); //--- Set synchronization without the confirmation dialog engine.SignalsMQL5CurrentSetConfirmationsDisableOFF(); //--- Set copying Stop Loss and Take Profit engine.SignalsMQL5CurrentSetSLTPCopyON(); //--- Set the market order slippage used when synchronizing positions and copying deals engine.SignalsMQL5CurrentSetSlippage(2); //--- Set the percentage for converting deal volume engine.SignalsMQL5CurrentSetEquityLimit(50); //--- Set deposit limitations (in %) engine.SignalsMQL5CurrentSetDepositPercent(70); //--- Display subscription parameters in the journal engine.SignalsMQL5CurrentSubscriptionParameters(); } } } done=true; return; } //--- If a signal subscription is active, unsubscribe if(engine.SignalsMQL5CurrentID()>0) { engine.SignalsMQL5Unsubscribe(); } //--- } //+------------------------------------------------------------------+
整个新代码模块已有详细注释。 如有任何疑问,请在评论中提问。
在 OnInitDoEasy() 函数库初始化函数中,添加代码模块,在其中创建交易信号集合列表,并设置启用订阅信号跟单的标志:
//--- Create tick series of all used symbols engine.TickSeriesCreateAll(); //--- Check created tick series - display descriptions of all created tick series in the journal engine.GetTickSeriesCollection().Print(); //--- Check created DOM series - display descriptions of all created DOM series in the journal engine.GetMBookSeriesCollection().Print(); //--- Create the collection of mql5.com Signals service signals //--- If working with signals is enabled and the signal collection is created if(InpUseMqlSignals && engine.SignalsMQL5Create()) { //--- Enable copying deals by subscription engine.SignalsMQL5CurrentSetSubscriptionEnableON(); //--- Check created MQL5 signal objects of the Signals service - display the short collection description in the journal engine.SignalsMQL5PrintShort(); } //--- If working with signals is not enabled or failed to create the signal collection, //--- disable copying deals by subscription else engine.SignalsMQL5CurrentSetSubscriptionEnableOFF(); //--- Create resource text files
编译 EA 并在交易品种图表上启动它,同时初步设置在当前交易品种/时间帧上操作,并激活操控 MQL5.com 信号服务的交易信号的标志:

在 EA 设置窗口的通用选项卡中,选中“允许修改信号设置”:

否则,EA 将无法操控 MQL5.com 信号。
启动 EA 后,日志会显示有关创建信号集合成功的消息,及其简述:
Collection of MQL5.com Signals service signals created successfully Collection of MQL5.com Signals service signals: - Free signals: 195, Paid signals: 805
然后显示免费信号的完整列表。 由于数量众多,我仅展示其中的一部分作为示例:
Collection of MQL5.com Signals service signals: - Signal "GBPUSD EXPERT 23233". Author login: mbt_trader, ID 919099, Growth: 3.30, Drawdown: 11.92, Price: 0.00, Subscribers: 0 - Signal "Willian". Author login: Desg, ID 917396, Growth: 12.69, Drawdown: 15.50, Price: 0.00, Subscribers: 0 - Signal "VahidVHZ1366". Author login: 39085485, ID 921427, Growth: 34.36, Drawdown: 12.84, Price: 0.00, Subscribers: 0 - Signal "Vikings". Author login: Myxx, ID 921040, Growth: 7.05, Drawdown: 2.22, Price: 0.00, Subscribers: 2 - Signal "VantageFX Sunphone Dragon". Author login: sunphone, ID 916421, Growth: 537.89, Drawdown: 39.06, Price: 0.00, Subscribers: 21 - Signal "Forex money maker free". Author login: Yggdrasills, ID 916328, Growth: 44.66, Drawdown: 61.15, Price: 0.00, Subscribers: 0 ... ... ... - Signal "Nine Pairs ST". Author login: ebi.pilehvar, ID 935603, Growth: 25.92, Drawdown: 26.41, Price: 0.00, Subscribers: 2 - Signal "FBS140". Author login: mohammeeeedali, ID 949720, Growth: 42.14, Drawdown: 23.11, Price: 0.00, Subscribers: 2 - Signal "StopTheFourthAddition". Author login: pinheirodps, ID 934990, Growth: 41.78, Drawdown: 28.03, Price: 0.00, Subscribers: 2 - Signal "The art of Forex". Author login: Myxx, ID 801685, Growth: 196.39, Drawdown: 40.95, Price: 0.00, Subscribers: 59 - Signal "Bongsanmaskdance1803". Author login: kim25801863, ID 936062, Growth: 12.53, Drawdown: 10.31, Price: 0.00, Subscribers: 0 - Signal "Prospector Scalper EA". Author login: robots4forex, ID 435626, Growth: 334.76, Drawdown: 43.93, Price: 0.00, Subscribers: 215 - Signal "ADS MT5". Author login: vluxus, ID 478235, Growth: 295.68, Drawdown: 40.26, Price: 0.00, Subscribers: 92
接着,我们得到检测到的信号的完整描述,以 % 为单位的最大涨幅,和成功订阅消息:
============= Beginning of parameter list (Signal from the MQL5.com Signal service) ============= Account type: Demo Publication date: 2020.07.02 16:29 Monitoring start date: 2020.07.02 16:29 Date of the latest update of the trading statistics: 2021.03.07 15:11 ID: 784584 Trading account leverage: 33 Trading result in pips: -19248988 Position in the Rating of Signals: 872 Number of subscribers: 6 Number of trades: 1825 Status of account subscription to a signal: No ------ Account balance: 12061.98 Account equity: 12590.32 Account growth in %: 1115.93 Maximum drawdown: 70.62 Signal subscription price: 0.00 Signal ROI (Return on Investment) in %: 1169.19 ------ Author login: "tradewai.com" Broker (company) name: "MetaQuotes Software Corp." Broker server: "MetaQuotes-Demo" Name: "Tradewai" Account currency: "USD" ============= End of parameter list (Signal from the MQL5.com Signal service) ============= Subscribed to signal ID 784584 "Tradewai"
之后显示订阅参数:
============= Signal copying parameters ============= Allow using signals for program: Yes Agree to the terms of use of the Signals service: Yes Enable copying deals by subscription: Yes Enable synchronization without confirmation dialog: No Copying Stop Loss and Take Profit: Yes Market order slippage when synchronizing positions and copying deals: Spread * 2.00 Percentage for converting deal volume: 50.00% Limit by deposit: 70% Limitation on signal equity: 7.00% Signal ID: 784584 Signal name: Tradewai
在下一次即时报价时,我们获得有关成功取消订阅信号的消息。
Unsubscribed from the signal ID 784584 "Tradewai"
下一步是什么?
在下一篇文章中,我将开始开发操控品种图表的函数库功能。
以下是该函数库当前版本的所有文件,以及 MQL5 的测试 EA 文件,供您测试和下载。
请注意,该测试 EA 在此处显示的操控信号仅用于测试目的。
EA 中提供的示例,仅给出了一种总体思路,基于函数库及其类来实现操控 MQL5.com 信号服务的自定义解决方案。
请您在评论中留下问题和建议。
*该系列的前几篇文章:
DoEasy 函数库中的价格(第六十二部分):实时更新即时报价序列,为操控市场深度做准备
DoEasy 函数库中的价格(第六十三部分):市场深度及其抽象请求类
DoEasy 函数库中的价格(第六十四部分):市场深度,DOM 快照类和快照序列对象
DoEasy 函数库中的价格(第六十五部分):市场深度集合并操控 MQL5.com 信号的类
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/9146
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
网格和马丁格尔交易系统中的机器学习。 您敢为其打赌吗?
实用且奇特的自动交易技术
嗨,先生,我在 WhatsApp 上与您聊天
我不通过 WhatsApp 交流
Boa noite!
欢迎来到巴西。
由于我对 mql5 的了解非常有限,所以我希望您能帮助我解决在编译过程中遇到的问题......我还没有找到解决方法。
我对你们的出色工作表示赞赏和感谢,你们的代码对我帮助很大。
Boa noite!
欢迎来到巴西。
Estou tendo impedimento na hora de compilar o código e como meu conhecimento de mql5 é muito básico, resta pedir sua ajuda para superar os problemas na compilação...não sei resolver.
我对你们的出色工作表示赞赏和感谢,你们的代码对我帮助很大。
在 Trading.mqh 中,您需要将一些方法移到受保护部分,以便派生类可以访问它们。现在它们在类的私有部分。这个错误是我不注意造成的,但旧的编译器漏掉了。更新终端后,这个错误就能被发现了。
在 Trading.mqh 文件的第 84 - 89 行,您应该进行以下更改:
在 Trading.mqh 文件中,一些方法需要移到受保护部分,以便派生类可以访问它们。现在它们在类的私有部分。这个错误是我无意中犯的,但旧的编译器漏掉了。更新终端后,这个错误开始被检测到。
在 Trading.mqh 文件的第 84 - 89 行,您需要进行以下更改:
并在第 155 - 181 行作出以下更改:
并在第 155 - 181 行作类似修改:之后,一切都会编译成功。
更正后的文件附在本帖后。
更正后的文件附在本帖后。