轻松快捷开发 MetaTrader 程序的函数库(第十五部分):品种对象集合

Artyom Trishkin | 1 十一月, 2019

内容


品种集合概念

我已在函数库论述的第三部分当中定义了构造对象集合类的概念。 在此,我坚持采用这种数据存储结构。 这意味着我们需要为品种集合创建一个列表。 该列表用于存储在上一篇文章中创建的“品种”类的衍生对象。 抽象品种的后代会阐明品种数据,并在程序中定义基本品种对象属性的可用性。 此类品种对象应按其分组的隶属关系(品种状态)加以区分。

为了定义一个品种所属的分组(品种状态),我们将创建自定义数据集 — 品种名称的数组,其中首先是所要搜索的类别。 如果在自定义类别中找不到品种(在任何用户定义的品种名称数组中均不存在其名称),则该类别按“保证金计算方法”(ENUM_SYMBOL_CALC_MODE)品种定义属性,令我们可以定义品种是否属于上面列出的某些类别。 换言之,我们将搜索分为两次检查:第一次按自定义类别中执行。 如果定义类别失败,则使用保证金计算方法搜索品种。 以前,我计划运用另一种方法 — 按照服务器上品种目录树的文件夹名称来定义。 但是此方法实在不可靠,因为文件夹可含有任意名称,并且同一品种可能位于不同的服务器上。 因此,我决定弃用它。
自定义类别将具有更高的优先级 — 如果用户希望将品种(例如,USDUSC)放置在“主要”类别中,那么尽管它是指导性的,但也没有什么理由可以阻止他/她这样做。

现在我们已经决定了类别,我们来为它们创建必要的抽象品种衍生类。 抽象品种本身已在上一篇文章中创建。
为了保存所有品种对象,我们需要用到自 CArrayObj标准库类继承的 CListObj 类,我们在第五篇文章中重新组织函数库类时已研究过它。 在基于函数库的程序中,可以选择要操控的品种列表:

  1. 仅一个 — 当前加载程序的品种
  2. 程序中指定的预定义品种集合
  3. 使用“市场观察”窗口中的交易品种列表
  4. 使用服务器上可用品种的完整列表

所以,为了访问操控品种,我们需要涵盖大多数必要的编程任务。
在此,我要提到两件事:

  1. 操控市场观察中的交易品种列表 — 在这种模式下,我们需要使用“市场观察”窗口事件的搜索功能 — 及时响应其刷新(从列表中添加/删除交易品种,并用鼠标排序),
  2. 在操控服务器上可用品种的完整列表时,我们需要分配一个常规处理来应对可能的大量可用品种,因为在首次启动时可以查看服务器上完整的品种列表。 此外,应创建品种对象接收所有属性。 该过程需要花费一些时间。 以我为例,在中等档次的笔记本电脑上创建所有服务器上的可用品种集合大约需要两分钟。

最初,在选择这种工作方法时,至少要警告用户有关集合初始化所需花费时间的信息。
我们会这样做的,并在后续文章中实现跟踪品种事件和市场观察窗口事件。 现在,我们来创建一个集合列表。

首先,我们创建另一个包含文件,其中将存储函数库所需的所有数据 — 自定义品种分组、文件、图像的数组,以及该函数库随后所需的其他数据集。
在函数的 \MQL5\Include\DoEasy\ 文件夹,创建一个新的 Datas.mqh 包含文件,并在其中添加一些我已经预先为若干个帐户准备好的数组,同时从市场观察窗口的树形结构中收集不同服务器上的品种分组数据:

//+------------------------------------------------------------------+
//|                                                        Datas.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Data sets                                                        |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Major Forex symbols                                              |
//+------------------------------------------------------------------+
string DataSymbolsFXMajors[]=
  {
   "AUDCAD","AUDCHF","AUDJPY","AUDNZD","AUDUSD","CADCHF","CADJPY","CHFJPY","EURAUD","EURCAD","EURCHF","EURGBP","EURJPY","EURNZD",
   "EURUSD","GBPAUD","GBPCAD","GBPCHF","GBPJPY","GBPNZD","GBPUSD","NZDCAD","NZDCHF","NZDJPY","NZDUSD","USDCAD","USDCHF","USDJPY"
  };
//+------------------------------------------------------------------+
//| Minor Forex symbols                                              |
//+------------------------------------------------------------------+
string DataSymbolsFXMinors[]=
  {
   "EURCZK","EURDKK","EURHKD","EURNOK","EURPLN","EURSEK","EURSGD","EURTRY","EURZAR","GBPSEK","GBPSGD"
      ,"NZDSGD","USDCZK","USDDKK","USDHKD","USDNOK","USDPLN","USDSEK","USDSGD","USDTRY","USDZAR"
  };
//+------------------------------------------------------------------+
//| Exotic Forex symbols                                             |
//+------------------------------------------------------------------+
string DataSymbolsFXExotics[]=
  {
   "EURMXN","USDCNH","USDMXN","EURTRY","USDTRY"
  };
//+------------------------------------------------------------------+
//| Forex RUB symbols                                                |
//+------------------------------------------------------------------+
string DataSymbolsFXRub[]=
  {
   "EURRUB","USDRUB"
  };
//+------------------------------------------------------------------+
//| Indicative Forex symbols                                         |
//+------------------------------------------------------------------+
string DataSymbolsFXIndicatives[]=
  {
   "EUREUC","USDEUC","USDUSC"
  };
//+------------------------------------------------------------------+
//| Metal symbols                                                    |
//+------------------------------------------------------------------+
string DataSymbolsMetalls[]=
  {
   "XAGUSD","XAUUSD"
  };
//+------------------------------------------------------------------+
//| Commodity symbols                                                |
//+------------------------------------------------------------------+
string DataSymbolsCommodities[]=
  {
   "BRN","WTI","NG"
  };
//+------------------------------------------------------------------+
//| Indices                                                          |
//+------------------------------------------------------------------+
string DataSymbolsIndexes[]=
  {
   "CAC40","HSI50","ASX200","STOXX50","NQ100","FTSE100","DAX30","IBEX35","SPX500","NIKK225"
   "Volatility 10 Index","Volatility 25 Index","Volatility 50 Index","Volatility 75 Index","Volatility 100 Index",
   "HF Volatility 10 Index","HF Volatility 50 Index","Crash 1000 Index","Boom 1000 Index","Step Index"
  };
//+------------------------------------------------------------------+
//| Cryptocurrency Symbols                                           |
//+------------------------------------------------------------------+
string DataSymbolsCrypto[]=
  {
   "BCHUSD","BTCEUR","BTCUSD","DSHUSD","EOSUSD","ETHEUR","ETHUSD","LTCUSD","XRPUSD"
  };
//+------------------------------------------------------------------+
//| Options                                                          |
//+------------------------------------------------------------------+
string DataSymbolsOptions[]=
  {
   "BO Volatility 10 Index","BO Volatility 25 Index","BO Volatility 50 Index","BO Volatility 75 Index","BO Volatility 100 Index"
  };
//+------------------------------------------------------------------+

我们从清单中可以看出,这只是添加了品种名称的枚举,一个品种分组所需数组在每个数组当中定义。 如果期望并有必要,您可以随意处理这些品种名称数组的内容,譬如将一些名称发送到另一个数组、添加/删除一些名称,等等。

抽象品种对象行为的更多“严格”测试可加深理解,使用 SYMBOL_MARGIN_LONG、SYMBOL_MARGIN_SHORT、SYMBOL_MARGIN_STOP、SYMBOL_MARGIN_LIMIT 和 SYMBOL_MARGIN_STOPLIMIT 常量不会涉及品种属性。 所以,我不得不利用 SymbolInfoMarginRate() 函数来实现接收属性。
由于一笔订单类型已发送到函数,因此我必须在 Defines.mqh 文件里为每种订单类型创建一个实数型自定义常量

//+------------------------------------------------------------------+
//| Symbol real properties                                           |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_PROP_DOUBLE
  {
   SYMBOL_PROP_BID = SYMBOL_PROP_INTEGER_TOTAL,             // Bid - the best price at which a symbol can be sold
   SYMBOL_PROP_BIDHIGH,                                     // The highest Bid price of the day
   SYMBOL_PROP_BIDLOW,                                      // The lowest Bid price of the day
   SYMBOL_PROP_ASK,                                         // Ask - best price, at which an instrument can be bought
   SYMBOL_PROP_ASKHIGH,                                     // The highest Ask price of the day
   SYMBOL_PROP_ASKLOW,                                      // The lowest Ask price of the day
   SYMBOL_PROP_LAST,                                        // The price at which the last deal was executed
   SYMBOL_PROP_LASTHIGH,                                    // The highest Last price of the day
   SYMBOL_PROP_LASTLOW,                                     // The lowest Last price of the day
   SYMBOL_PROP_VOLUME_REAL,                                 // Volume of the day
   SYMBOL_PROP_VOLUMEHIGH_REAL,                             // Maximum Volume within a day
   SYMBOL_PROP_VOLUMELOW_REAL,                              // Minimum Volume within a day
   SYMBOL_PROP_OPTION_STRIKE,                               // Option execution price
   SYMBOL_PROP_POINT,                                       // One point value
   SYMBOL_PROP_TRADE_TICK_VALUE,                            // SYMBOL_TRADE_TICK_VALUE_PROFIT value
   SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT,                     // Calculated tick value for a winning position
   SYMBOL_PROP_TRADE_TICK_VALUE_LOSS,                       // Calculated tick value for a losing position
   SYMBOL_PROP_TRADE_TICK_SIZE,                             // Minimum price change
   SYMBOL_PROP_TRADE_CONTRACT_SIZE,                         // Trade contract size
   SYMBOL_PROP_TRADE_ACCRUED_INTEREST,                      // Accrued interest
   SYMBOL_PROP_TRADE_FACE_VALUE,                            // Face value – initial bond value set by an issuer
   SYMBOL_PROP_TRADE_LIQUIDITY_RATE,                        // Liquidity rate – the share of an asset that can be used for a margin
   SYMBOL_PROP_VOLUME_MIN,                                  // Minimum volume for a deal
   SYMBOL_PROP_VOLUME_MAX,                                  // Maximum volume for a deal
   SYMBOL_PROP_VOLUME_STEP,                                 // Minimum volume change step for deal execution
   SYMBOL_PROP_VOLUME_LIMIT,                                // The maximum allowed total volume of an open position and pending orders in one direction (either buy or sell)
   SYMBOL_PROP_SWAP_LONG,                                   // Long swap value
   SYMBOL_PROP_SWAP_SHORT,                                  // Short swap value
   SYMBOL_PROP_MARGIN_INITIAL,                              // Initial margin
   SYMBOL_PROP_MARGIN_MAINTENANCE,                          // Maintenance margin for an instrument
   SYMBOL_PROP_MARGIN_LONG_INITIAL,                         // Initial margin requirement applicable to long positions
   SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL,                     // Initial margin requirement applicable to BuyStop orders
   SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL,                    // Initial margin requirement applicable to BuyLimit orders
   SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL,                // Initial margin requirement applicable to BuyStopLimit orders
   SYMBOL_PROP_MARGIN_LONG_MAINTENANCE,                     // Maintenance margin requirement applicable to long positions
   SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE,                 // Maintenance margin requirement applicable to BuyStop orders
   SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE,                // Maintenance margin requirement applicable to BuyLimit orders
   SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE,            // Maintenance margin requirement applicable to BuyStopLimit orders
   SYMBOL_PROP_MARGIN_SHORT_INITIAL,                        // Initial margin requirement applicable to short positions
   SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL,                    // Initial margin requirement applicable to SellStop orders
   SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL,                   // Initial margin requirement applicable to SellLimit orders
   SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL,               // Initial margin requirement applicable to SellStopLimit orders
   SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE,                    // Maintenance margin requirement applicable to short positions
   SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE,                // Maintenance margin requirement applicable to SellStop orders
   SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE,               // Maintenance margin requirement applicable to SellLimit orders
   SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE,           // Maintenance margin requirement applicable to SellStopLimit orders
   SYMBOL_PROP_SESSION_VOLUME,                              // The total volume of deals in the current session
   SYMBOL_PROP_SESSION_TURNOVER,                            // The total turnover in the current session
   SYMBOL_PROP_SESSION_INTEREST,                            // The total volume of open positions
   SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,                   // The total volume of Buy orders at the moment
   SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,                  // The total volume of Sell orders at the moment
   SYMBOL_PROP_SESSION_OPEN,                                // Open price of the session
   SYMBOL_PROP_SESSION_CLOSE,                               // Close price of the session
   SYMBOL_PROP_SESSION_AW,                                  // The average weighted price of the session
   SYMBOL_PROP_SESSION_PRICE_SETTLEMENT,                    // The settlement price of the current session
   SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN,                     // Minimum allowable price value for the session 
   SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX,                     // Maximum allowable price value for the session
   SYMBOL_PROP_MARGIN_HEDGED                                // Size of a contract or margin for one lot of hedged positions (oppositely directed positions at one symbol).
  };
#define SYMBOL_PROP_DOUBLE_TOTAL     (58)                   // Total number of real properties
#define SYMBOL_PROP_DOUBLE_SKIP      (0)                    // Number of real symbol properties not used in sorting
//+------------------------------------------------------------------+

相应地,实数型属性的总数已增加到 58(替换以前的 47 个)。

我还不得不在枚举中添加相应的可用于品种排序条件的常量:

//+------------------------------------------------------------------+
//| Possible symbol sorting criteria                                 |
//+------------------------------------------------------------------+
#define FIRST_SYM_DBL_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP)
#define FIRST_SYM_STR_PROP          (SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP+SYMBOL_PROP_DOUBLE_TOTAL-SYMBOL_PROP_DOUBLE_SKIP)
enum ENUM_SORT_SYMBOLS_MODE
  {
//--- Sort by integer properties
   SORT_BY_SYMBOL_STATUS = 0,                               // Sort by symbol status
   SORT_BY_SYMBOL_CUSTOM,                                   // Sort by custom symbol property
   SORT_BY_SYMBOL_CHART_MODE,                               // Sort by price type for constructing bars – Bid or Last (from the ENUM_SYMBOL_CHART_MODE enumeration)
   SORT_BY_SYMBOL_EXIST,                                    // Sort by the flag that a symbol with such a name exists
   SORT_BY_SYMBOL_SELECT,                                   // Sort by the flag indicating that a symbol is selected in Market Watch
   SORT_BY_SYMBOL_VISIBLE,                                  // Sort by the flag indicating that a selected symbol is displayed in Market Watch
   SORT_BY_SYMBOL_SESSION_DEALS,                            // Sort by the number of deals in the current session 
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS,                       // Sort by the total number of current buy orders
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS,                      // Sort by the total number of current sell orders
   SORT_BY_SYMBOL_VOLUME,                                   // Sort by last deal volume
   SORT_BY_SYMBOL_VOLUMEHIGH,                               // Sort by maximum volume for a day
   SORT_BY_SYMBOL_VOLUMELOW,                                // Sort by minimum volume for a day
   SORT_BY_SYMBOL_TIME,                                     // Sort by the last quote time
   SORT_BY_SYMBOL_DIGITS,                                   // Sort by a number of decimal places
   SORT_BY_SYMBOL_DIGITS_LOT,                               // Sort by a number of decimal places in a lot
   SORT_BY_SYMBOL_SPREAD,                                   // Sort by spread in points
   SORT_BY_SYMBOL_SPREAD_FLOAT,                             // Sort by floating spread
   SORT_BY_SYMBOL_TICKS_BOOKDEPTH,                          // Sort by a maximum number of requests displayed in the market depth
   SORT_BY_SYMBOL_TRADE_CALC_MODE,                          // Sort by contract price calculation method (from the ENUM_SYMBOL_CALC_MODE enumeration)
   SORT_BY_SYMBOL_TRADE_MODE,                               // Sort by order execution type (from the ENUM_SYMBOL_TRADE_MODE enumeration)
   SORT_BY_SYMBOL_START_TIME,                               // Sort by an instrument trading start date (usually used for futures)
   SORT_BY_SYMBOL_EXPIRATION_TIME,                          // Sort by an instrument trading end date (usually used for futures)
   SORT_BY_SYMBOL_TRADE_STOPS_LEVEL,                        // Sort by the minimum indent from the current close price (in points) for setting Stop orders
   SORT_BY_SYMBOL_TRADE_FREEZE_LEVEL,                       // Sort by trade operation freeze distance (in points)
   SORT_BY_SYMBOL_TRADE_EXEMODE,                            // Sort by trade execution mode (from the ENUM_SYMBOL_TRADE_EXECUTION enumeration)
   SORT_BY_SYMBOL_SWAP_MODE,                                // Sort by swap calculation model (from the ENUM_SYMBOL_SWAP_MODE enumeration)
   SORT_BY_SYMBOL_SWAP_ROLLOVER3DAYS,                       // Sort by week day for accruing a triple swap (from the ENUM_DAY_OF_WEEK enumeration)
   SORT_BY_SYMBOL_MARGIN_HEDGED_USE_LEG,                    // Sort by the calculation mode of a hedged margin using the larger leg (Buy or Sell)
   SORT_BY_SYMBOL_EXPIRATION_MODE,                          // Sort by flags of allowed order expiration modes
   SORT_BY_SYMBOL_FILLING_MODE,                             // Sort by flags of allowed order filling modes
   SORT_BY_SYMBOL_ORDER_MODE,                               // Sort by flags of allowed order types
   SORT_BY_SYMBOL_ORDER_GTC_MODE,                           // Sort by StopLoss and TakeProfit orders lifetime
   SORT_BY_SYMBOL_OPTION_MODE,                              // Sort by option type (from the ENUM_SYMBOL_OPTION_MODE enumeration)
   SORT_BY_SYMBOL_OPTION_RIGHT,                             // Sort by option right (Call/Put) (from the ENUM_SYMBOL_OPTION_RIGHT enumeration)
//--- Sort by real properties
   SORT_BY_SYMBOL_BID = FIRST_SYM_DBL_PROP,                 // Sort by Bid
   SORT_BY_SYMBOL_BIDHIGH,                                  // Sort by maximum Bid for a day
   SORT_BY_SYMBOL_BIDLOW,                                   // Sort by minimum Bid for a day
   SORT_BY_SYMBOL_ASK,                                      // Sort by Ask
   SORT_BY_SYMBOL_ASKHIGH,                                  // Sort by maximum Ask for a day
   SORT_BY_SYMBOL_ASKLOW,                                   // Sort by minimum Ask for a day
   SORT_BY_SYMBOL_LAST,                                     // Sort by the last deal price
   SORT_BY_SYMBOL_LASTHIGH,                                 // Sort by maximum Last for a day
   SORT_BY_SYMBOL_LASTLOW,                                  // Sort by minimum Last for a day
   SORT_BY_SYMBOL_VOLUME_REAL,                              // Sort by Volume for a day
   SORT_BY_SYMBOL_VOLUMEHIGH_REAL,                          // Sort by maximum Volume for a day
   SORT_BY_SYMBOL_VOLUMELOW_REAL,                           // Sort by minimum Volume for a day
   SORT_BY_SYMBOL_OPTION_STRIKE,                            // Sort by an option execution price
   SORT_BY_SYMBOL_POINT,                                    // Sort by a single point value
   SORT_BY_SYMBOL_TRADE_TICK_VALUE,                         // Sort by SYMBOL_TRADE_TICK_VALUE_PROFIT value
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT,                  // Sort by a calculated tick price for a profitable position
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_LOSS,                    // Sort by a calculated tick price for a loss-making position
   SORT_BY_SYMBOL_TRADE_TICK_SIZE,                          // Sort by a minimum price change
   SORT_BY_SYMBOL_TRADE_CONTRACT_SIZE,                      // Sort by a trading contract size
   SORT_BY_SYMBOL_TRADE_ACCRUED_INTEREST,                   // Sort by accrued interest
   SORT_BY_SYMBOL_TRADE_FACE_VALUE,                         // Sort by face value
   SORT_BY_SYMBOL_TRADE_LIQUIDITY_RATE,                     // Sort by liquidity rate
   SORT_BY_SYMBOL_VOLUME_MIN,                               // Sort by a minimum volume for performing a deal
   SORT_BY_SYMBOL_VOLUME_MAX,                               // Sort by a maximum volume for performing a deal
   SORT_BY_SYMBOL_VOLUME_STEP,                              // Sort by a minimum volume change step for deal execution
   SORT_BY_SYMBOL_VOLUME_LIMIT,                             // Sort by a maximum allowed aggregate volume of an open position and pending orders in one direction
   SORT_BY_SYMBOL_SWAP_LONG,                                // Sort by a long swap value
   SORT_BY_SYMBOL_SWAP_SHORT,                               // Sort by a short swap value
   SORT_BY_SYMBOL_MARGIN_INITIAL,                           // Sort by an initial margin
   SORT_BY_SYMBOL_MARGIN_MAINTENANCE,                       // Sort by a maintenance margin for an instrument
   SORT_BY_SYMBOL_MARGIN_LONG_INITIAL,                      // Sort by initial margin requirement applicable to Long orders
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_INITIAL,                  // Sort by initial margin requirement applicable to BuyStop orders
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_INITIAL,                 // Sort by initial margin requirement applicable to BuyLimit orders
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_INITIAL,             // Sort by initial margin requirement applicable to BuyStopLimit orders
   SORT_BY_SYMBOL_MARGIN_LONG_MAINTENANCE,                  // Sort by maintenance margin requirement applicable to Long orders
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_MAINTENANCE,              // Sort by maintenance margin requirement applicable to BuyStop orders
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_MAINTENANCE,             // Sort by maintenance margin requirement applicable to BuyLimit orders
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_MAINTENANCE,         // Sort by maintenance margin requirement applicable to BuyStopLimit orders
   SORT_BY_SYMBOL_MARGIN_SHORT_INITIAL,                     // Sort by initial margin requirement applicable to Short orders
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_INITIAL,                 // Sort by initial margin requirement applicable to SellStop orders
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_INITIAL,                // Sort by initial margin requirement applicable to SellLimit orders
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_INITIAL,            // Sort by initial margin requirement applicable to SellStopLimit orders
   SORT_BY_SYMBOL_MARGIN_SHORT_MAINTENANCE,                 // Sort by maintenance margin requirement applicable to Short orders
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_MAINTENANCE,             // Sort by maintenance margin requirement applicable to SellStop orders
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_MAINTENANCE,            // Sort by maintenance margin requirement applicable to SellLimit orders
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_MAINTENANCE,        // Sort by maintenance margin requirement applicable to SellStopLimit orders
   SORT_BY_SYMBOL_SESSION_VOLUME,                           // Sort by summary volume of the current session deals
   SORT_BY_SYMBOL_SESSION_TURNOVER,                         // Sort by the summary turnover of the current session
   SORT_BY_SYMBOL_SESSION_INTEREST,                         // Sort by the summary open interest
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME,                // Sort by the current volume of Buy orders
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME,               // Sort by the current volume of Sell orders
   SORT_BY_SYMBOL_SESSION_OPEN,                             // Sort by a session Open price
   SORT_BY_SYMBOL_SESSION_CLOSE,                            // Sort by a session Close price
   SORT_BY_SYMBOL_SESSION_AW,                               // Sort by an average weighted price of the current session
   SORT_BY_SYMBOL_SESSION_PRICE_SETTLEMENT,                 // Sort by a settlement price of the current session
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN,                  // Sort by a minimum price of the current session 
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX,                  // Sort by a maximum price of the current session
   SORT_BY_SYMBOL_MARGIN_HEDGED,                            // Sort by a contract size or a margin value per one lot of hedged positions
//--- Sort by string properties
   SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                // Sort by a symbol name
   SORT_BY_SYMBOL_BASIS,                                    // Sort by an underlying asset of a derivative
   SORT_BY_SYMBOL_CURRENCY_BASE,                            // Sort by a base currency of a symbol
   SORT_BY_SYMBOL_CURRENCY_PROFIT,                          // Sort by a profit currency
   SORT_BY_SYMBOL_CURRENCY_MARGIN,                          // Sort by a margin currency
   SORT_BY_SYMBOL_BANK,                                     // Sort by a feeder of the current quote
   SORT_BY_SYMBOL_DESCRIPTION,                              // Sort by a symbol string description
   SORT_BY_SYMBOL_FORMULA,                                  // Sort by the formula used for custom symbol pricing
   SORT_BY_SYMBOL_ISIN,                                     // Sort by the name of a symbol in the ISIN system
   SORT_BY_SYMBOL_PAGE,                                     // Sort by an address of the web page containing symbol information
   SORT_BY_SYMBOL_PATH                                      // Sort by a path in the symbol tree
  };
//+------------------------------------------------------------------+

鉴于我们正在编辑 Defines.mqh 文件,我们在添加所有必要内容的同时会解释为什么要这么做。

我们需要一个计时器来刷新集合中所有品种的数据。 注意,我们需要更刷新有品种的报价数据,这也许会令品种事件类中跟踪它们的数据发生变化(在下一篇文章中有更多论述)。 在计时器中,我们还需要检查“市场观察”窗口中的交易品种列表,以便响应其刷新,并及时更新集合列表。
我们需要更频繁地更新报价数据、品种的其余数据、及其在“市场观察”窗口中的列表。 这意味着我们的品种集合需要两个计时器 — 报价数据计时器,和执行其他品种列表操作的计时器。

我们来为这两个品种集合计时器添加必要的宏替换:

//--- Symbol collection timer 1 parameters
#define COLLECTION_SYM_PAUSE1          (100)                      // Pause of the symbol collection timer 1 in milliseconds (for scanning market watch symbols)
#define COLLECTION_SYM_COUNTER_STEP1   (16)                       // Increment of the symbol timer 1 counter
#define COLLECTION_SYM_COUNTER_ID1     (3)                        // Symbol timer 1 counter ID
//--- Symbol collection timer 2 parameters
#define COLLECTION_SYM_PAUSE2          (300)                      // Pause of the symbol collection timer 2 in milliseconds (for events of the market watch symbol list)
#define COLLECTION_SYM_COUNTER_STEP2   (16)                       // Increment of the symbol timer 2 counter
#define COLLECTION_SYM_COUNTER_ID2     (4)                        // Symbol timer 2 counter ID

这些数据之间的差异仅在于每个计时器的暂停和它们的 ID — 第一个计时器的暂停时间为 100 毫秒,而第二个计时器的暂停时间为 300 毫秒。

我们为每个集合分配了单独的 ID。 品种集合也不例外。
我们为其列表设置一个自定义的 ID

//--- Collection list IDs
#define COLLECTION_HISTORY_ID          (0x7778+1)                 // Historical collection list ID
#define COLLECTION_MARKET_ID           (0x7778+2)                 // Market collection list ID
#define COLLECTION_EVENTS_ID           (0x7778+3)                 // Event collection list ID
#define COLLECTION_ACCOUNT_ID          (0x7778+4)                 // Account collection list ID
#define COLLECTION_SYMBOLS_ID          (0x7778+5)                 // Symbol collection list ID

在上一篇文章里,为了定义用于“市场观察”窗口中高亮的交易品种的背景颜色,和显示其字符串描述的颜色,我们将颜色与 clrWhite 比较 — 如果属性值超过颜色的“长整数”值,则认定未设置背景色

      property==SYMBOL_PROP_BACKGROUND_COLOR    ?  TextByLanguage("Цвет фона символа в Market Watch","Background color of symbol in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         #ifdef __MQL5__
         (this.GetProperty(property)>clrWhite  ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+::ColorToString((color)this.GetProperty(property),true))
         #else TextByLanguage(": Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :

不过i,根据开发者的说法,这是不正确的 — “市场观察”窗口中品种背景的“长整数”值可能会超过“白色”的“长整数”值。 意即在某些情况下,此检查会返回错误的结果。
为了正确识别背景色的缺失,我们需要将属性值与 CLR_DEFAULT 和 CLR_NONE 值进行比较。
我们利用宏替换来设置“默认”颜色(MQL5 和 MQL4 中已经存在“缺失” CLR_NONE 颜色):

//--- Symbol parameters
#define CLR_DEFAULT                    (0xFF000000)               // Default color
//+------------------------------------------------------------------+

结果就是,Defines 文件里宏替换的部分现在如下所示:

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
//--- Describe the function with the error line number
#define DFUN_ERR_LINE                  (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ")
#define DFUN                           (__FUNCTION__+": ")        // "Function description"
#define COUNTRY_LANG                   ("Russian")                // Country language
#define END_TIME                       (D'31.12.3000 23:59:59')   // End date for account history data requests
#define TIMER_FREQUENCY                (16)                       // Minimal frequency of the library timer in milliseconds
//--- Parameters of the orders and deals collection timer
#define COLLECTION_ORD_PAUSE           (250)                      // Orders and deals collection timer pause in milliseconds
#define COLLECTION_ORD_COUNTER_STEP    (16)                       // Increment of the orders and deals collection timer counter
#define COLLECTION_ORD_COUNTER_ID      (1)                        // Orders and deals collection timer counter ID
//--- Parameters of the account collection timer
#define COLLECTION_ACC_PAUSE           (1000)                     // Account collection timer pause in milliseconds
#define COLLECTION_ACC_COUNTER_STEP    (16)                       // Account timer counter increment
#define COLLECTION_ACC_COUNTER_ID      (2)                        // Account timer counter ID
//--- Symbol collection timer 1 parameters
#define COLLECTION_SYM_PAUSE1          (100)                      // Pause of the symbol collection timer 1 in milliseconds (for scanning market watch symbols)
#define COLLECTION_SYM_COUNTER_STEP1   (16)                       // Increment of the symbol timer 1 counter
#define COLLECTION_SYM_COUNTER_ID1     (3)                        // Symbol timer 1 counter ID
//--- Symbol collection timer 2 parameters
#define COLLECTION_SYM_PAUSE2          (300)                      // Pause of the symbol collection timer 2 in milliseconds (for events of the market watch symbol list)
#define COLLECTION_SYM_COUNTER_STEP2   (16)                       // Increment of the symbol timer 2 counter
#define COLLECTION_SYM_COUNTER_ID2     (4)                        // Symbol timer 2 counter ID
//--- Collection list IDs
#define COLLECTION_HISTORY_ID          (0x7778+1)                 // Historical collection list ID
#define COLLECTION_MARKET_ID           (0x7778+2)                 // Market collection list ID
#define COLLECTION_EVENTS_ID           (0x7778+3)                 // Event collection list ID
#define COLLECTION_ACCOUNT_ID          (0x7778+4)                 // Account collection list ID
#define COLLECTION_SYMBOLS_ID          (0x7778+5)                 // Symbol collection list ID
//--- Data parameters for file operations
#define DIRECTORY                      ("DoEasy\\")               // Library directory for storing object folders
//--- Symbol parameters
#define CLR_DEFAULT                    (0xFF000000)               // Default color
//+------------------------------------------------------------------+

我已经提到过操控品种集合的模式:当前品种、程序中预定义的品种列表、“市场观察”窗口、以及服务器上可用品种的完整列表 。
我们在枚举中设置所有这些模式:

//+------------------------------------------------------------------+
//| Data for working with symbols                                    |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                                    // Work with the current symbol only
   SYMBOLS_MODE_DEFINES,                                    // Work with the specified symbol list
   SYMBOLS_MODE_MARKET_WATCH,                               // Work with the Market Watch window symbols
   SYMBOLS_MODE_ALL                                         // Work with the full symbol list
  };
//+------------------------------------------------------------------+

在上一篇文章中,我们辨别了用于进行品种排序的品种类别。 在本文的最开头,我们实验了一种稍微扩充的品种类别列表,我们将会用到它。

我们添加品种类别(状态)的枚举:

//+------------------------------------------------------------------+
//| Abstract symbol type (status)                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_STATUS
  {
   SYMBOL_STATUS_FX,                                        // Forex symbol
   SYMBOL_STATUS_FX_MAJOR,                                  // Major Forex symbol
   SYMBOL_STATUS_FX_MINOR,                                  // Minor Forex symbol
   SYMBOL_STATUS_FX_EXOTIC,                                 // Exotic Forex symbol
   SYMBOL_STATUS_FX_RUB,                                    // Forex symbol/RUB
   SYMBOL_STATUS_METAL,                                     // Metal
   SYMBOL_STATUS_INDEX,                                     // Index
   SYMBOL_STATUS_INDICATIVE,                                // Indicative
   SYMBOL_STATUS_CRYPTO,                                    // Cryptocurrency symbol
   SYMBOL_STATUS_COMMODITY,                                 // Commodity symbol
   SYMBOL_STATUS_EXCHANGE,                                  // Exchange symbol
   SYMBOL_STATUS_FUTURES,                                   // Futures
   SYMBOL_STATUS_CFD,                                       // CFD
   SYMBOL_STATUS_STOCKS,                                    // Security
   SYMBOL_STATUS_BONDS,                                     // Bond
   SYMBOL_STATUS_OPTION,                                    // Option
   SYMBOL_STATUS_COLLATERAL,                                // Non-tradable asset
   SYMBOL_STATUS_CUSTOM,                                    // Custom symbol
   SYMBOL_STATUS_COMMON                                     // General category
  };
//+------------------------------------------------------------------+

我们已为品种集合准备好了数据,并在 Defines.mqh 文件中进行了所有修改。

所做的修改还影响到我们在上一篇文章中创建的 CSymbol 类。
鉴于我们现在利用 SymbolInfoMarginRate() 函数接收有关各种订单类型的保证金比率数据,而通过链接传递给该函数的变量则用于从其返回数值,我们现在需要创建这些变量。

假设我们有八笔定单,且它们当中的每一笔有两个比率类型(初始保证金比率和维持保证金比率),则应有 16 个变量用于接收这些数值。 所以,创建由两个嵌套结构组成的结构会更加便利:在第一个结构中,定义了两个双精度变量以便存储初始保证金和维持保证金的比率,而 第二个包含第一个声明的结构,用于存储按订单类型所获比率的数据。

我们在 Symbol.mqh 品种类文件的 CSymbol 类私有部分中声明这些结构,以及含有第二种结构类型的类成员变量

//+------------------------------------------------------------------+
//| Abstract symbol class                                            |
//+------------------------------------------------------------------+
class CSymbol : public CObject
  {
private:
   struct SMarginRate
     {
      double         Initial;          // initial margin rate
      double         Maintenance;      // maintenance margin rate
     };
   struct SMarginRateMode
     {
      SMarginRate    Long;             // MarginRate of long positions
      SMarginRate    Short;            // MarginRate of short positions
      SMarginRate    BuyStop;          // MarginRate of BuyStop orders
      SMarginRate    BuyLimit;         // MarginRate of BuyLimit orders
      SMarginRate    BuyStopLimit;     // MarginRate of BuyStopLimit orders
      SMarginRate    SellStop;         // MarginRate of SellStop orders
      SMarginRate    SellLimit;        // MarginRate of SellLimit orders
      SMarginRate    SellStopLimit;    // MarginRate of SellStopLimit orders
     };
   SMarginRateMode   m_margin_rate;                                  // Margin ratio structure

我们在类的私有部分补充为所有品种属性填写比率的方法所有存储品种比率的结构变量的初始化方法。 还有,我们要添加两个辅助方法来接收当前星期值,和“双精度”数值中的小数位数

   SMarginRateMode   m_margin_rate;                                  // Margin ratio structure
   MqlTick           m_tick;                                         // Symbol tick structure
   MqlBookInfo       m_book_info_array[];                            // Array of the market depth data structures
   string            m_symbol_name;                                  // Symbol name
   long              m_long_prop[SYMBOL_PROP_INTEGER_TOTAL];         // Integer properties
   double            m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL];        // Real properties
   string            m_string_prop[SYMBOL_PROP_STRING_TOTAL];        // String properties
   int               m_digits_currency;                              // Number of decimal places in an account currency
   int               m_global_error;                                 // Global error code
//--- Return the index of the array the symbol's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_SYMBOL_PROP_DOUBLE property)  const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL;                                    }
   int               IndexProp(ENUM_SYMBOL_PROP_STRING property)  const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_DOUBLE_TOTAL;           }
//--- (1) Fill in all the "margin ratio" symbol properties, (2) initialize the ratios
   bool              MarginRates(void);
   void              InitMarginRates(void);
//--- Reset all symbol object data
   void              Reset(void);
//--- Return the current day of the week
   ENUM_DAY_OF_WEEK  CurrentDayOfWeek(void)              const;
//--- Returns the number of decimal places in the 'double' value
   int               GetDigits(const double value)       const;
public:

在类的同一私有部分当中,声明返回每种定单类型保证金比率数据的方法:

//--- Get and return real properties of a selected symbol from its parameters
   double            SymbolBidHigh(void)                 const;
   double            SymbolBidLow(void)                  const;
   double            SymbolVolumeReal(void)              const;
   double            SymbolVolumeHighReal(void)          const;
   double            SymbolVolumeLowReal(void)           const;
   double            SymbolOptionStrike(void)            const;
   double            SymbolTradeAccruedInterest(void)    const;
   double            SymbolTradeFaceValue(void)          const;
   double            SymbolTradeLiquidityRate(void)      const;
   double            SymbolMarginHedged(void)            const;
   bool              SymbolMarginLong(void);         
   bool              SymbolMarginShort(void);        
   bool              SymbolMarginBuyStop(void);      
   bool              SymbolMarginBuyLimit(void);     
   bool              SymbolMarginBuyStopLimit(void); 
   bool              SymbolMarginSellStop(void);     
   bool              SymbolMarginSellLimit(void);    
   bool              SymbolMarginSellStopLimit(void);
//--- Get and return string properties of a selected symbol from its parameters

有时,程序需要知道服务器上是否存在某个品种。 这可以按品种名称搜索来完成。 我们已有 Exist() 方法,可按类品种返回此类数据。 重载该方法,令其可以按所传递的品名返回数据。 为此,在类的私有部分中声明另一个方法调用

//--- Search for a symbol and return the flag indicating its presence on the server
   bool              Exist(void)                         const;
   bool              Exist(const string name)            const;

并且,在类的受保护部分中声明重载方法。 该方法依据 MQL5 或 MQL4 程序类型,按其名称返回品种数值

protected:
//--- Protected parametric constructor
                     CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name);

//--- Get and return integer properties of a selected symbol from its parameters
   bool              SymbolExists(const string name)     const;
   long              SymbolExists(void)                  const;

在类的公开部分的描述属性方法的部分中,声明用于显示品种简介的虚方法
我们在衍生类中实现此虚方法,在衍生类中厘清品种对象的数据设置。

//+------------------------------------------------------------------+
//| Description of symbol object properties                          |
//+------------------------------------------------------------------+
//--- Get description of a symbol (1) integer, (2) real and (3) string properties
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_STRING property);


//--- Send description of symbol properties to the journal (full_prop=true - all properties, false - only supported ones)
   void              Print(const bool full_prop=false);
//--- Display a short symbol description in the journal (implementation in the descendants)
   virtual void      PrintShort(void) {;}

//--- Compare CSymbol objects by all possible properties (for sorting lists by a specified symbol object property)

在上一篇文章中,我们在实现品种对象时添加了一些服务方法。
我们再来添加一些其他方法,用于返回报价交易时段的开始和结束时间,以及用于返回时段整点小时、分钟和秒数的私有方法,和返回 “HH:MM:SS” 格式的时段说明方法

//--- (1) Add, (2) remove a symbol from the Market Watch window, (3) return the data synchronization flag by a symbol
   bool              SetToMarketWatch(void)                       const { return ::SymbolSelect(this.m_symbol_name,true);                                   }
   bool              RemoveFromMarketWatch(void)                  const { return ::SymbolSelect(this.m_symbol_name,false);                                  }
   bool              IsSynchronized(void)                         const { return ::SymbolIsSynchronized(this.m_symbol_name);                                }
//--- Return the (1) start and (2) end time of the week day's quote session, (3) the start and end time of the required quote session
   long              SessionQuoteTimeFrom(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE)   const;
   long              SessionQuoteTimeTo(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE)     const;
   bool              GetSessionQuote(const uint session_index,ENUM_DAY_OF_WEEK day_of_week,datetime &from,datetime &to);
//--- Return the (1) start and (2) end time of the week day's trading session, (3) the start and end time of the required trading session
   long              SessionTradeTimeFrom(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE)   const;
   long              SessionTradeTimeTo(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE)     const;
   bool              GetSessionTrade(const uint session_index,ENUM_DAY_OF_WEEK day_of_week,datetime &from,datetime &to);
//--- (1) Arrange a (1) subscription to the market depth, (2) close the market depth, (3) fill in the market depth data to the structure array
   bool              BookAdd(void)                                const;
   bool              BookClose(void)                              const;
//--- Return (1) a session duration description in the hh:mm:ss format, number of (1) hours, (2) minutes and (3) seconds in the session duration time
   string            SessionDurationDescription(const ulong duration_sec) const;
private:
   int               SessionHours(const ulong duration_sec)       const;
   int               SessionMinutes(const ulong duration_sec)     const;
   int               SessionSeconds(const ulong duration_sec)     const;
public:
//+------------------------------------------------------------------+

在公开部分中,用于简化访问品种对象属性的方法分区,添加了第二种形式的调用方法,该方法返回服务器上的品种存在标志( 之前,我们声明了私有重载方法,按其名称在服务器上查找品种,并返回搜索结果的标志)

//+------------------------------------------------------------------+
//| Methods of a simplified access to the order object properties    |
//+------------------------------------------------------------------+
//--- Integer properties
   long              Status(void)                                 const { return this.GetProperty(SYMBOL_PROP_STATUS);                                      }
   bool              IsCustom(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM);                                }
   color             ColorBackground(void)                        const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR);                     }
   ENUM_SYMBOL_CHART_MODE ChartMode(void)                         const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE);          }
   bool              IsExist(void)                                const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST);                                 }
   bool              IsExist(const string name)                   const { return this.SymbolExists(name);                                                   }
   bool              IsSelect(void)                               const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT);                                }
   bool              IsVisible(void)                              const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE);                               }
   long              SessionDeals(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS);                               }

在公开部分中,用于简化访问品种实数型属性的方法分区,添加了返回所有保证金比率的方法:

//--- Real properties
   double            Bid(void)                                    const { return this.GetProperty(SYMBOL_PROP_BID);                                         }
   double            BidHigh(void)                                const { return this.GetProperty(SYMBOL_PROP_BIDHIGH);                                     }
   double            BidLow(void)                                 const { return this.GetProperty(SYMBOL_PROP_BIDLOW);                                      }
   double            Ask(void)                                    const { return this.GetProperty(SYMBOL_PROP_ASK);                                         }
   double            AskHigh(void)                                const { return this.GetProperty(SYMBOL_PROP_ASKHIGH);                                     }
   double            AskLow(void)                                 const { return this.GetProperty(SYMBOL_PROP_ASKLOW);                                      }
   double            Last(void)                                   const { return this.GetProperty(SYMBOL_PROP_LAST);                                        }
   double            LastHigh(void)                               const { return this.GetProperty(SYMBOL_PROP_LASTHIGH);                                    }
   double            LastLow(void)                                const { return this.GetProperty(SYMBOL_PROP_LASTLOW);                                     }
   double            VolumeReal(void)                             const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL);                                 }
   double            VolumeHighReal(void)                         const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL);                             }
   double            VolumeLowReal(void)                          const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL);                              }
   double            OptionStrike(void)                           const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE);                               }
   double            Point(void)                                  const { return this.GetProperty(SYMBOL_PROP_POINT);                                       }
   double            TradeTickValue(void)                         const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE);                            }
   double            TradeTickValueProfit(void)                   const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT);                     }
   double            TradeTickValueLoss(void)                     const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS);                       }
   double            TradeTickSize(void)                          const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE);                             }
   double            TradeContractSize(void)                      const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE);                         }
   double            TradeAccuredInterest(void)                   const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST);                      }
   double            TradeFaceValue(void)                         const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE);                            }
   double            TradeLiquidityRate(void)                     const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE);                        }
   double            LotsMin(void)                                const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN);                                  }
   double            LotsMax(void)                                const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX);                                  }
   double            LotsStep(void)                               const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP);                                 }
   double            VolumeLimit(void)                            const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT);                                }
   double            SwapLong(void)                               const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG);                                   }
   double            SwapShort(void)                              const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT);                                  }
   double            MarginInitial(void)                          const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL);                              }
   double            MarginMaintenance(void)                      const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE);                          }
   double            MarginLongInitial(void)                      const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL);                         }
   double            MarginBuyStopInitial(void)                   const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL);                     }
   double            MarginBuyLimitInitial(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL);                    }
   double            MarginBuyStopLimitInitial(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL);                }
   double            MarginLongMaintenance(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE);                     }
   double            MarginBuyStopMaintenance(void)               const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE);                 }
   double            MarginBuyLimitMaintenance(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE);                }
   double            MarginBuyStopLimitMaintenance(void         const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE);            }
   double            MarginShortInitial(void)                     const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL);                        }
   double            MarginSellStopInitial(void)                  const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL);                    }
   double            MarginSellLimitInitial(void)                 const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL);                   }
   double            MarginSellStopLimitInitial(void)             const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL);               }
   double            MarginShortMaintenance(void)                 const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE);                    }
   double            MarginSellStopMaintenance(void)              const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE);                }
   double            MarginSellLimitMaintenance(void)             const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE);               }
   double            MarginSellStopLimitMaintenance(void)         const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE);           }
   double            SessionVolume(void)                          const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME);                              }
   double            SessionTurnover(void)                        const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER);                            }
   double            SessionInterest(void)                        const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST);                            }
   double            SessionBuyOrdersVolume(void)                 const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME);                   }
   double            SessionSellOrdersVolume(void)                const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME);                  }
   double            SessionOpen(void)                            const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN);                                }
   double            SessionClose(void)                           const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE);                               }
   double            SessionAW(void)                              const { return this.GetProperty(SYMBOL_PROP_SESSION_AW);                                  }
   double            SessionPriceSettlement(void)                 const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT);                    }
   double            SessionPriceLimitMin(void)                   const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN);                     }
   double            SessionPriceLimitMax(void)                   const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX);                     }
   double            MarginHedged(void)                           const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED);                               }
   double            NormalizedPrice(const double price)          const;
//--- String properties

若要获取有关交易品种属性的数据,请确保在“市场观察”窗口中选择了该交易品种。 在某些情况下,可能在窗口中未选择该品种但仍需要其属性的情况。 针对这种情况,我们需要创建标志来指示是否在市场观察窗口中选择该交易品种,然后才能访问其属性。 接着,进行如下操作:如果未选择该交易品种,则选择,获取其属性,并在“市场观察”窗口中隐藏该品种。 如果该品种已被选择,则只需获取其属性。

另外,针对 MQL5,我们需要初始化保证金比率数据,并在类构造函数中为其填写数值。 MQL4 没有此类数据,并且其数值在初始化后保持为零。
另外,添加调用将这些属性保存在类属性字段中的方法

为达此目的,请在类构造函数中添加必要的代码:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CSymbol::CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name) : m_global_error(ERR_SUCCESS)
  {
   this.m_symbol_name=name;
   if(!this.Exist())
     {
      ::Print(DFUN_ERR_LINE,"\"",this.m_symbol_name,"\"",": ",TextByLanguage("Ошибка. Такого символа нет на сервере","Error. There is no such symbol on the server"));
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
     }
   bool select=::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SELECT);
   ::ResetLastError();
   if(!select)
     {
      if(!this.SetToMarketWatch())
        {
         this.m_global_error=::GetLastError();
         ::Print(DFUN_ERR_LINE,"\"",this.m_symbol_name,"\": ",TextByLanguage("Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in the market watch. Error: "),this.m_global_error);
        }
     }
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,"\"",this.m_symbol_name,"\": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
     }
//--- Initialize data
   ::ZeroMemory(this.m_tick);
   this.Reset();
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.InitMarginRates();
   ::ResetLastError();
#ifdef __MQL5__
   if(!this.MarginRates())
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",TextByLanguage("Не удалось получить коэффициенты взимания маржи. Ошибка: ","Failed to get margin rates. Error: "),this.m_global_error);
      return;
     }
#endif 
   
//--- Save integer properties
   this.m_long_prop[SYMBOL_PROP_STATUS]                                       = symbol_status;
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;
   this.m_long_prop[SYMBOL_PROP_SELECT]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SELECT);
   this.m_long_prop[SYMBOL_PROP_VISIBLE]                                      = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VISIBLE);
   this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                                = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_DEALS);
   this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS);
   this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]                          = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS);
   this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMEHIGH);
   this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                                    = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMELOW);
   this.m_long_prop[SYMBOL_PROP_DIGITS]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_DIGITS);
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_SPREAD_FLOAT]                                 = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD_FLOAT);
   this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TICKS_BOOKDEPTH);
   this.m_long_prop[SYMBOL_PROP_TRADE_MODE]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_MODE);
   this.m_long_prop[SYMBOL_PROP_START_TIME]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_START_TIME);
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_TIME);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_EXEMODE]                                = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_EXEMODE);
   this.m_long_prop[SYMBOL_PROP_SWAP_ROLLOVER3DAYS]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SWAP_ROLLOVER3DAYS);
   this.m_long_prop[SYMBOL_PROP_EXIST]                                        = this.SymbolExists();
   this.m_long_prop[SYMBOL_PROP_CUSTOM]                                       = this.SymbolCustom();
   this.m_long_prop[SYMBOL_PROP_MARGIN_HEDGED_USE_LEG]                        = this.SymbolMarginHedgedUseLEG();
   this.m_long_prop[SYMBOL_PROP_ORDER_MODE]                                   = this.SymbolOrderMode();
   this.m_long_prop[SYMBOL_PROP_FILLING_MODE]                                 = this.SymbolOrderFillingMode();
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_MODE]                              = this.SymbolExpirationMode();
   this.m_long_prop[SYMBOL_PROP_ORDER_GTC_MODE]                               = this.SymbolOrderGTCMode();
   this.m_long_prop[SYMBOL_PROP_OPTION_MODE]                                  = this.SymbolOptionMode();
   this.m_long_prop[SYMBOL_PROP_OPTION_RIGHT]                                 = this.SymbolOptionRight();
   this.m_long_prop[SYMBOL_PROP_BACKGROUND_COLOR]                             = this.SymbolBackgroundColor();
   this.m_long_prop[SYMBOL_PROP_CHART_MODE]                                   = this.SymbolChartMode();
   this.m_long_prop[SYMBOL_PROP_TRADE_CALC_MODE]                              = this.SymbolCalcMode();
   this.m_long_prop[SYMBOL_PROP_SWAP_MODE]                                    = this.SymbolSwapMode();

//--- Save real properties
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_POINT)]                      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_POINT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]            = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]        = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_CONTRACT_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)]                = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_STEP);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_LIMIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)]                  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_LONG);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_SHORT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)]         = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_TURNOVER);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_INTEREST);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_OPEN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]              = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_CLOSE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_AW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                        = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                        = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                       = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                    = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                     = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMEHIGH_REAL)]            = this.SymbolVolumeHighReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMELOW_REAL)]             = this.SymbolVolumeLowReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]              = this.SymbolOptionStrike();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_ACCRUED_INTEREST)]     = this.SymbolTradeAccruedInterest();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_FACE_VALUE)]           = this.SymbolTradeFaceValue();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_LIQUIDITY_RATE)]       = this.SymbolTradeLiquidityRate();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_HEDGED)]              = this.SymbolMarginHedged();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_INITIAL)]              = this.m_margin_rate.Long.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL)]          = this.m_margin_rate.BuyStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL)]         = this.m_margin_rate.BuyLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL)]     = this.m_margin_rate.BuyStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE)]          = this.m_margin_rate.Long.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE)]      = this.m_margin_rate.BuyStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE)]     = this.m_margin_rate.BuyLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE)] = this.m_margin_rate.BuyStopLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_INITIAL)]             = this.m_margin_rate.Short.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL)]         = this.m_margin_rate.SellStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL)]        = this.m_margin_rate.SellLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL)]    = this.m_margin_rate.SellStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE)]         = this.m_margin_rate.Short.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE)]     = this.m_margin_rate.SellStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE)]    = this.m_margin_rate.SellLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;

//--- Save string properties
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_NAME)]                       = this.m_symbol_name;
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_BASE)]              = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_BASE);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_PROFIT)]            = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_PROFIT);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_MARGIN)]            = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_MARGIN);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_DESCRIPTION)]                = ::SymbolInfoString(this.m_symbol_name,SYMBOL_DESCRIPTION);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_PATH)]                       = ::SymbolInfoString(this.m_symbol_name,SYMBOL_PATH);
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_BASIS)]                      = this.SymbolBasis();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_BANK)]                       = this.SymbolBank();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_ISIN)]                       = this.SymbolISIN();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_FORMULA)]                    = this.SymbolFormula();
   this.m_string_prop[this.IndexProp(SYMBOL_PROP_PAGE)]                       = this.SymbolPage();
//--- Save additional integer properties
   this.m_long_prop[SYMBOL_PROP_DIGITS_LOTS]                                  = this.SymbolDigitsLot();
//---
   if(!select)
      this.RemoveFromMarketWatch();
  }
//+------------------------------------------------------------------+

现在我们需要编写所有已声明方法的实现。

填写所有存储保证金比率变量的方法实现位于类的实体之外:

//+------------------------------------------------------------------+
//| Fill in the margin ratio variables                               |
//+------------------------------------------------------------------+
bool CSymbol::MarginRates(void)
  {
   bool res=true;
   #ifdef __MQL5__
      res &=this.SymbolMarginLong();
      res &=this.SymbolMarginBuyStop();
      res &=this.SymbolMarginBuyLimit();
      res &=this.SymbolMarginBuyStopLimit();
      res &=this.SymbolMarginShort();
      res &=this.SymbolMarginSellStop();
      res &=this.SymbolMarginSellLimit();
      res &=this.SymbolMarginSellStopLimit();
   #else 
      this.InitMarginRates();
      res=false;
   #endif 
   return res;
  }
//+------------------------------------------------------------------+

MQL5 方法仅调用方法从品种属性里读取比率数据,并将其写入相应的结构变量。 累积所有方法返回的结果,并返回给调用程序。 该方法将在下面讨论。 对于 MQL4,所有结构字段都简单地置零。

初始化属性结构里保证金比率字段的方法:

//+------------------------------------------------------------------+
//| Initialize margin ratios                                         |
//+------------------------------------------------------------------+
void CSymbol::InitMarginRates(void)
  {
   this.m_margin_rate.Long.Initial=0;           this.m_margin_rate.Long.Maintenance=0;
   this.m_margin_rate.BuyStop.Initial=0;        this.m_margin_rate.BuyStop.Maintenance=0;
   this.m_margin_rate.BuyLimit.Initial=0;       this.m_margin_rate.BuyLimit.Maintenance=0;
   this.m_margin_rate.BuyStopLimit.Initial=0;   this.m_margin_rate.BuyStopLimit.Maintenance=0;
   this.m_margin_rate.Short.Initial=0;          this.m_margin_rate.Short.Maintenance=0;
   this.m_margin_rate.SellStop.Initial=0;       this.m_margin_rate.SellStop.Maintenance=0;
   this.m_margin_rate.SellLimit.Initial=0;      this.m_margin_rate.SellLimit.Maintenance=0;
   this.m_margin_rate.SellStopLimit.Initial=0;  this.m_margin_rate.SellStopLimit.Maintenance=0;
  }
//+------------------------------------------------------------------+

m_margin_rate 结构的所有字段仅在此处重置。

实现调用方法的第二种形式,该方法返回服务器上品种存在的标志:

//+------------------------------------------------------------------+
//| Return the symbol existence flag                                 |
//+------------------------------------------------------------------+
long CSymbol::SymbolExists(void) const
  {
   return(#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXIST) #else this.Exist() #endif);
  }
//+------------------------------------------------------------------+
bool CSymbol::SymbolExists(const string name) const
  {
   return(#ifdef __MQL5__ (bool)::SymbolInfoInteger(name,SYMBOL_EXIST) #else this.Exist(name) #endif);
  }
//+------------------------------------------------------------------+

在此,对于 MQL5,返回 SYMBOL_EXIST 品种属性,而对于 MQL4,使用第二种 Exist(const string name) 方法的调用形式,在服务器上搜索一个品种

填充结构中所有订单类型的保证金比率的方法实现:

//+------------------------------------------------------------------+
//| Fill in the margin ratios for long positions                     |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginLong(void) 
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_BUY,this.m_margin_rate.Long.Initial,this.m_margin_rate.Long.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Fill in the margin ratios for short positions                    |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginShort(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_SELL,this.m_margin_rate.Short.Initial,this.m_margin_rate.Short.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Fill in the margin ratios for BuyStop orders                     |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginBuyStop(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_BUY_STOP,this.m_margin_rate.BuyStop.Initial,this.m_margin_rate.BuyStop.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Fill in the margin ratios for BuyLimit orders                    |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginBuyLimit(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_BUY_LIMIT,this.m_margin_rate.BuyLimit.Initial,this.m_margin_rate.BuyLimit.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Fill in the margin ratios for BuyStopLimit orders                |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginBuyStopLimit(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_BUY_STOP_LIMIT,this.m_margin_rate.BuyStopLimit.Initial,this.m_margin_rate.BuyStopLimit.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Fill in the margin ratios for SellStop orders                    |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginSellStop(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_SELL_STOP,this.m_margin_rate.SellStop.Initial,this.m_margin_rate.SellStop.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Fill in the margin ratios for SellLimit orders                   |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginSellLimit(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_SELL_LIMIT,this.m_margin_rate.SellLimit.Initial,this.m_margin_rate.SellLimit.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+
//| Fill in the margin ratios for SellStopLimit orders               |
//+------------------------------------------------------------------+
bool CSymbol::SymbolMarginSellStopLimit(void)
  {
   return(#ifdef __MQL5__ ::SymbolInfoMarginRate(this.m_symbol_name,ORDER_TYPE_SELL_STOP_LIMIT,this.m_margin_rate.SellStopLimit.Initial,this.m_margin_rate.SellStopLimit.Maintenance) #else false #endif);
  }
//+------------------------------------------------------------------+

此处,对于 MQL5,调用 SymbolInfoMarginRate() 函数,在其中会填充 m_margin_rate 结构中的必需属性,且该函数返回操作结果。 对于 MQL4,返回 false

在返回“市场观察”窗口中返回品种背景描述的模块中,修改返回品种属性说明的方法:

      property==SYMBOL_PROP_BACKGROUND_COLOR    ?  TextByLanguage("Цвет фона символа в Market Watch","Background color of symbol in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         #ifdef __MQL5__
         (this.GetProperty(property)==CLR_DEFAULT || this.GetProperty(property)==CLR_NONE ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+::ColorToString((color)this.GetProperty(property),true))
         #else TextByLanguage(": Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :

以前,我们将颜色与白色(clrWhite)进行了比较,如果颜色值超过“白色”值,则认为未设置颜色。 我们已讨论了这种比较方法的缺点。 所以,将颜色与“默认值” 进行比较,或将其与“缺失颜色”进行比较,以便定义“市场观察”窗口中某交易品种是否指定了背景颜色。

将显示所有保证金比率说明的代码添加到返回品种 GetPropertyDescription(ENUM_SYMBOL_PROP_DOUBLE property) 实数型属性的方法中:

//--- Initial margin requirement of a Long position
      property==SYMBOL_PROP_MARGIN_LONG_INITIAL          ?  TextByLanguage("Коэффициент взимания начальной маржи по длинным позициям","Coefficient of margin initial charging for long positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
//--- Initial margin requirement of a Short position
      property==SYMBOL_PROP_MARGIN_SHORT_INITIAL     ?  TextByLanguage("Коэффициент взимания начальной маржи по коротким позициям","Coefficient of margin initial charging for short positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
//--- Maintenance margin requirement of a Long position
      property==SYMBOL_PROP_MARGIN_LONG_MAINTENANCE          ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по длинным позициям","Coefficient of margin maintenance charging for long positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
//--- Maintenance margin requirement of a Short position
      property==SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE          ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по коротким позициям","Coefficient of margin maintenance charging for short positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
//--- Initial margin requirements of Long orders
      property==SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL      ?  TextByLanguage("Коэффициент взимания начальной маржи по BuyStop ордерам","Coefficient of margin initial charging for BuyStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL     ?  TextByLanguage("Коэффициент взимания начальной маржи по BuyLimit ордерам","Coefficient of margin initial charging for BuyLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL ?  TextByLanguage("Коэффициент взимания начальной маржи по BuyStopLimit ордерам","Coefficient of margin initial charging for BuyStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
//--- Initial margin requirements of Short orders
      property==SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL      ?  TextByLanguage("Коэффициент взимания начальной маржи по SellStop ордерам","Coefficient of margin initial charging for SellStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL     ?  TextByLanguage("Коэффициент взимания начальной маржи по SellLimit ордерам","Coefficient of margin initial charging for SellLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL ?  TextByLanguage("Коэффициент взимания начальной маржи по SellStopLimit ордерам","Coefficient of margin initial charging for SellStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
//--- Maintenance margin requirements of Long orders
      property==SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE      ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по BuyStop ордерам","Coefficient of margin maintenance charging for BuyStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE     ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по BuyLimit ордерам","Coefficient of margin maintenance charging for BuyLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по BuyStopLimit ордерам","Coefficient of margin maintenance charging for BuyStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
//--- Maintenance margin requirements of Short orders
      property==SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE      ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по SellStop ордерам","Coefficient of margin maintenance charging for SellStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE     ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по SellLimit ордерам","Coefficient of margin maintenance charging for SellLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE ?  TextByLanguage("Коэффициент взимания поддерживающей маржи по SellStopLimit ордерам","Coefficient of margin maintenance charging for SellStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Не задан)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") #endif 
         )  :
   //---

实现第二种形式,调用方法在服务器上按名称搜索品种,并返回品种存在标记

//+-------------------------------------------------------------------------------+
//| Search for a symbol and return the flag indicating its presence on the server |
//+-------------------------------------------------------------------------------+
bool CSymbol::Exist(void) const
  {
   int total=::SymbolsTotal(false);
   for(int i=0;i<total;i++)
      if(::SymbolName(i,false)==this.m_symbol_name)
         return true;
   return false;
  }
//+------------------------------------------------------------------+
bool CSymbol::Exist(const string name) const
  {
   int total=::SymbolsTotal(false);
   for(int i=0;i<total;i++)
      if(::SymbolName(i,false)==name)
         return true;
   return false;
  }
//+------------------------------------------------------------------+

实现计算并返回小数位“双精度”数值的方法:

//+------------------------------------------------------------------+
//| Return the number of decimal places in the 'double' value        |
//+------------------------------------------------------------------+
int CSymbol::GetDigits(const double value) const
  {
   string val_str=(string)value;
   int len=::StringLen(val_str);
   int n=len-::StringFind(val_str,".",0)-1;
   if(::StringSubstr(val_str,len-1,1)=="0")
      n--;
   return n;
  }
//+------------------------------------------------------------------+

我们已在上一篇文章中讨论了该方法。 在此可以用一种单独的方法来简单实现,这是出于需要对若干数值进行重复计算 — 最小手数和手数步长。

从一天的开始返回报价时段的开始和结束时间,以及报价时段的开始和结束时间的方法实现:

//+------------------------------------------------------------------+
//| Return the quote session start time                              |
//| in seconds from the beginning of a day                           |
//+------------------------------------------------------------------+
long CSymbol::SessionQuoteTimeFrom(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE) const
  {
   MqlDateTime time={0};
   datetime from=0,to=0;
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return(::SymbolInfoSessionQuote(this.m_symbol_name,day,session_index,from,to) ? from : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the time in seconds since the day start                   |
//| up to the end of a quote session                                 |
//+------------------------------------------------------------------+
long CSymbol::SessionQuoteTimeTo(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE) const
  {
   MqlDateTime time={0};
   datetime from=0,to=0;
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return(::SymbolInfoSessionQuote(this.m_symbol_name,day,session_index,from,to) ? to : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the start and end time of a required quote session        |
//+------------------------------------------------------------------+
bool CSymbol::GetSessionQuote(const uint session_index,ENUM_DAY_OF_WEEK day_of_week,datetime &from,datetime &to)
  {
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return ::SymbolInfoSessionQuote(this.m_symbol_name,day,session_index,from,to);
  }
//+------------------------------------------------------------------+

时段索引星期值被传递给前两个方法,而第三个方法要另外接收 'datetime' 类型变量,该变量将存储利用 SymbolInfoSessionQuote() 函数接收的指定时段的开始和结束数据

为了更加便利,如果选择 -1 作为星期值,则时段数据取自当日。 时段索引应从零开始。 按 day_of_week 参数定义的时间,返回自该日开始至今的秒数。 所以,您可将累加该方法返回的距指定日开始的秒数,计算出实际的请求时间。

接收交易时段的方法以相同的方式实现:

//+------------------------------------------------------------------+
//| Return the trading session start time                            |
//| in seconds from the beginning of a day                           |
//+------------------------------------------------------------------+
long CSymbol::SessionTradeTimeFrom(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE) const
  {
   MqlDateTime time={0};
   datetime from=0,to=0;
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return(::SymbolInfoSessionTrade(this.m_symbol_name,day,session_index,from,to) ? from : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the time in seconds since the day start                   |
//| up to the end of a trading session                               |
//+------------------------------------------------------------------+
long CSymbol::SessionTradeTimeTo(const uint session_index,ENUM_DAY_OF_WEEK day_of_week=WRONG_VALUE) const
  {
   MqlDateTime time={0};
   datetime from=0,to=0;
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return(::SymbolInfoSessionTrade(this.m_symbol_name,day,session_index,from,to) ? to : WRONG_VALUE);
  }
//+------------------------------------------------------------------+
//| Return the start and end time of a required trading session      |
//+------------------------------------------------------------------+
bool CSymbol::GetSessionTrade(const uint session_index,ENUM_DAY_OF_WEEK day_of_week,datetime &from,datetime &to)
  {
   ENUM_DAY_OF_WEEK day=(day_of_week<0 || day_of_week>SATURDAY ? this.CurrentDayOfWeek() : day_of_week);
   return ::SymbolInfoSessionTrade(this.m_symbol_name,day,session_index,from,to);
  }
//+------------------------------------------------------------------+

这些方法与上述方法类似,不同之处在于,此处利用 SymbolInfoSessionTrade() 函数来接收所需的数据。

按当前星期值返回 ENUM_DAY_OF_WEEK 枚举值的方法实现:

//+------------------------------------------------------------------+
//| Return the current day of the week                               |
//+------------------------------------------------------------------+
ENUM_DAY_OF_WEEK CSymbol::CurrentDayOfWeek(void) const
  {
   MqlDateTime time={0};
   ::TimeCurrent(time);
   return(ENUM_DAY_OF_WEEK)time.day_of_week;
  }
//+------------------------------------------------------------------+

这一切都很简单:声明日期和时间结构,访问 TimeCurrent() 函数,该函数的第二个调用形式填充传递给该函数的日期和时间结构,最后返回 已填充结构中的星期值

返回指定时段持续时间秒数的方法实现:

//+------------------------------------------------------------------+
//| Return the number of seconds in a session duration time          |
//+------------------------------------------------------------------+
int CSymbol::SessionSeconds(const ulong duration_sec) const
  {
   return int(duration_sec % 60);
  }
//+------------------------------------------------------------------+

该方法接收秒数除以该时间跨度的分钟数,并返回余数

返回指定时段的持续时间分钟数的方法实现:

//+------------------------------------------------------------------+
//| Return the number of minutes in a session duration time          |
//+------------------------------------------------------------------+
int CSymbol::SessionMinutes(const ulong duration_sec) const
  {
   return int((duration_sec-this.SessionSeconds(duration_sec)) % 3600)/60;
  }
//+------------------------------------------------------------------+

该方法接收秒数,并返回计算出的时间跨度分钟数,排除不足一分钟的秒数

返回指定时段持续时间小时数的方法实现

//+------------------------------------------------------------------+
//| Return the number of hours in a session duration time            |
//+------------------------------------------------------------------+
int CSymbol::SessionHours(const ulong duration_sec) const
  {
   return int(duration_sec-this.SessionSeconds(duration_sec)-this.SessionMinutes(duration_sec))/3600;
  }
//+------------------------------------------------------------------+

该方法接收秒数,并返回计算出的时间跨度小时数,排除不足一分钟的秒数,和不足一小时的分钟数

以 “ HH:MM:SS” 格式返回时段持续时间描述的方法实现:

//+---------------------------------------------------------------------+
//| Return the description of a session duration in the hh:mm:ss format |
//+---------------------------------------------------------------------+
string CSymbol::SessionDurationDescription(const ulong duration_sec) const
  {
   int sec=this.SessionSeconds(duration_sec);
   int min=this.SessionMinutes(duration_sec);
   int hour=this.SessionHours(duration_sec);
   return ::IntegerToString(hour,2,'0')+":"+::IntegerToString(min,2,'0')+":"+::IntegerToString(sec,2,'0');
  }
//+------------------------------------------------------------------+

在此,我们只需以秒为单位获取时段持续时间以及计算出的时段持续时间秒数分钟数小时数,然后利用 IntegerToString() 函数 以“小时:分钟:秒钟”的格式显示格式化的消息,时、分、秒的字符串宽度均等于两位,若时、分、秒中只有一位数字,不足两位数字,则以“ 0” 填充。
例如,如果我们得到小时为 2,则显示为 02。

由于我们稍微修改了品种对象的状态,因此需要修改显示品种对象状态说明的方法:

//+------------------------------------------------------------------+
//| Return the status description                                    |
//+------------------------------------------------------------------+
string CSymbol::GetStatusDescription() const
  {
   return
     (
      this.Status()==SYMBOL_STATUS_FX           ? TextByLanguage("Форекс символ","Forex symbol")                  :
      this.Status()==SYMBOL_STATUS_FX_MAJOR     ? TextByLanguage("Форекс символ-мажор","Forex major symbol")      :
      this.Status()==SYMBOL_STATUS_FX_MINOR     ? TextByLanguage("Форекс символ-минор","Forex minor symbol")      :
      this.Status()==SYMBOL_STATUS_FX_EXOTIC    ? TextByLanguage("Форекс символ-экзотик","Forex Exotic Symbol")   :
      this.Status()==SYMBOL_STATUS_FX_RUB       ? TextByLanguage("Форекс символ/рубль","Forex symbol RUB")        :
      this.Status()==SYMBOL_STATUS_METAL        ? TextByLanguage("Металл","Metal")                                :
      this.Status()==SYMBOL_STATUS_INDEX        ? TextByLanguage("Индекс","Index")                                :
      this.Status()==SYMBOL_STATUS_INDICATIVE   ? TextByLanguage("Индикатив","Indicative")                        :
      this.Status()==SYMBOL_STATUS_CRYPTO       ? TextByLanguage("Криптовалютный символ","Crypto symbol")         :
      this.Status()==SYMBOL_STATUS_COMMODITY    ? TextByLanguage("Товарный символ","Commodity symbol")            :
      this.Status()==SYMBOL_STATUS_EXCHANGE     ? TextByLanguage("Биржевой символ","Exchange symbol")             : 
      this.Status()==SYMBOL_STATUS_FUTURES      ? TextByLanguage("Фьючерс","Futures")                             : 
      this.Status()==SYMBOL_STATUS_CFD          ? TextByLanguage("Контракт на разницу","Contract For Difference") : 
      this.Status()==SYMBOL_STATUS_STOCKS       ? TextByLanguage("Ценная бумага","Stocks")                        : 
      this.Status()==SYMBOL_STATUS_BONDS        ? TextByLanguage("Облигация","Bonds")                             : 
      this.Status()==SYMBOL_STATUS_OPTION       ? TextByLanguage("Опцион","Option")                               : 
      this.Status()==SYMBOL_STATUS_COLLATERAL   ? TextByLanguage("Неторгуемый актив","Collateral")                : 
      this.Status()==SYMBOL_STATUS_CUSTOM       ? TextByLanguage("Пользовательский символ","Custom symbol")       :
      this.Status()==SYMBOL_STATUS_COMMON       ? TextByLanguage("Символ общей группы","Common group symbol")     :
      ::EnumToString((ENUM_SYMBOL_STATUS)this.Status())
     );
  }
//+------------------------------------------------------------------+

在刷新所有品种数据的方法中,对于 MQL5,要给所有定单和持仓类型添加接收保证金比率
对于 MQL4,由于在 MQL4 中未用到它们,所以在类构造函数中开始初始化之后,它们的值仍为零:

//+------------------------------------------------------------------+
//| Update all symbol data that can change                           |
//+------------------------------------------------------------------+
void CSymbol::Refresh(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();

      return;
     }
#ifdef __MQL5__
   ::ResetLastError();
   if(!this.MarginRates())
     {
      this.m_global_error=::GetLastError();
      return;
     }
#endif 
//--- Update integer properties
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;
   this.m_long_prop[SYMBOL_PROP_SELECT]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SELECT);
   this.m_long_prop[SYMBOL_PROP_VISIBLE]                                      = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VISIBLE);
   this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                                = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_DEALS);
   this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS);
   this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]                          = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS);
   this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMEHIGH);
   this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                                    = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMELOW);
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TICKS_BOOKDEPTH);
   this.m_long_prop[SYMBOL_PROP_START_TIME]                                   = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_START_TIME);
   this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_TIME);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
   this.m_long_prop[SYMBOL_PROP_BACKGROUND_COLOR]                             = this.SymbolBackgroundColor();
//--- Update real properties
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]            = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]        = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_CONTRACT_SIZE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)]                = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_STEP);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_LIMIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)]                  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_LONG);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_SHORT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_INITIAL);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)]         = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_MAINTENANCE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_TURNOVER);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_INTEREST);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_OPEN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]              = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_CLOSE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_AW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                        = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                        = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                       = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                    = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                     = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMEHIGH_REAL)]            = this.SymbolVolumeHighReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMELOW_REAL)]             = this.SymbolVolumeLowReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]              = this.SymbolOptionStrike();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_ACCRUED_INTEREST)]     = this.SymbolTradeAccruedInterest();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_FACE_VALUE)]           = this.SymbolTradeFaceValue();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_LIQUIDITY_RATE)]       = this.SymbolTradeLiquidityRate();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_HEDGED)]              = this.SymbolMarginHedged();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_INITIAL)]              = this.m_margin_rate.Long.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL)]          = this.m_margin_rate.BuyStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL)]         = this.m_margin_rate.BuyLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL)]     = this.m_margin_rate.BuyStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE)]          = this.m_margin_rate.Long.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE)]      = this.m_margin_rate.BuyStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE)]     = this.m_margin_rate.BuyLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE)] = this.m_margin_rate.BuyStopLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_INITIAL)]             = this.m_margin_rate.Short.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL)]         = this.m_margin_rate.SellStop.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL)]        = this.m_margin_rate.SellLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL)]    = this.m_margin_rate.SellStopLimit.Initial;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE)]         = this.m_margin_rate.Short.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE)]     = this.m_margin_rate.SellStop.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE)]    = this.m_margin_rate.SellLimit.Maintenance;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;
  }
//+------------------------------------------------------------------+

此处针对 MQL5,我们调用 MarginRates() 方法接收保证金比率数据。 如果至少有一个比率没收,方法返回 false,只需将错误代码写入类中存储错误代码的变量里,然后退出该方法且无需消息。
由于该方法工作于计时器当中,因此日志中不会显示错误消息。 万一收到错误的数据,日志会迅速被有关同一错误的垃圾邮件充斥。 鉴于此错误代码始终可在 CEngine 类中得到,因此我们留至该类来负责接收和处理它。
在该方法的最后,将所有获得的比率数据写入相应的品种对象属性字段中。

出于相同的原因,从报价数据刷新方法中删除在日志中显示错误消息的代码

//+------------------------------------------------------------------+
//| Update quote data by a symbol                                    |
//+------------------------------------------------------------------+
void CSymbol::RefreshRates(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
      return;
     }
//--- Update integer properties
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
//--- Update real properties
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                        = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                        = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                       = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                    = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                     = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]              = this.SymbolOptionStrike();
  }  
//+------------------------------------------------------------------+

现在,该方法将如下所示:

//+------------------------------------------------------------------+
//| Update quote data by a symbol                                    |
//+------------------------------------------------------------------+
void CSymbol::RefreshRates(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return;
     }
//--- Update integer properties
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;
   this.m_long_prop[SYMBOL_PROP_SPREAD]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
   this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
   this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
//--- Update real properties
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                        = this.m_tick.ask;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                        = this.m_tick.bid;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                       = this.m_tick.last;
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                    = this.SymbolBidHigh();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                     = this.SymbolBidLow();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]                = this.SymbolVolumeReal();
   this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]              = this.SymbolOptionStrike();
  }  
//+------------------------------------------------------------------+

抽象 CSymbol 品种类的改进既已完毕。

我们已针对该类方法进行了足够分量的修改,但有些次要修改并未在此进行说明,因为这些基本上只是属性描述方法中的一些拼写和“语义”错误。 您可以在附件中找到它们。

现在,我们需要创建抽象品种基类的衍生对象,将它们按类别划分,然后将其放置到品种对象集合当中。

“品种”基础抽象对象的衍生对象

我们后撤一步看看上述品种类别。 此外,我们要为 CSymbol 基类的相应衍生类设置名称:

总共,我们有 19 个派生类。 我们举利用外汇品种类创建对象作为示例。

\MQL5\Include\DoEasy\Objects\Symbols\ 函数库文件夹中,使用名为 SymbolFX.mqh 的文件创建新的类 CSymbolFXCSymbol 抽象品种类将用作其基类。
声明类操作所需的所有方法:

//+------------------------------------------------------------------+
//|                                                     SymbolFX.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Symbol.mqh"
//+------------------------------------------------------------------+
//| Forex symbol                                                     |
//+------------------------------------------------------------------+
class CSymbolFX : public CSymbol
  {
public:
//--- Constructor
                     CSymbolFX(const string name) : CSymbol(SYMBOL_STATUS_FX,name) {}
//--- Supported integer properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_INTEGER property);
//--- Supported real properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property);
//--- Supported string properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_STRING property);
//--- Display a short symbol description in the journal
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+

类构造函数用于接收品种名称,而类构造函数的初始化列表用于发送 “外汇品种“类别(其状态)给其基类,随同创建时传递给 CSymbolFX 类构造函数的品种名称

在基类中声明了对象可支持的整数型实数型字符串型属性的虚拟方法,而它们的实现则是在衍生类中执行。 虚拟的 PrintShort() 方法也在基类中声明,并在衍生类中实现,该方法在日志中显示品种数据的摘要。

几乎所有衍生类方法都是相似的,且可在基类中实现而无需衍生类。 不过,在这种情况下,我们舍弃了为每个品种组的变化制定方法的灵活性。 所以,我决定通过衍生类对类别进行划分,以便能够针对每个衍生类分别定制,这理应更加简单、快捷。

按品种对象返回支持整数型属性标志的方法实现:

//+------------------------------------------------------------------+
//| Return 'true' if a symbol supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CSymbolFX::SupportProperty(ENUM_SYMBOL_PROP_INTEGER property)
  {
   if(property==SYMBOL_PROP_EXIST
   #ifdef __MQL4__                                 ||
      property==SYMBOL_PROP_CUSTOM                 ||
      property==SYMBOL_PROP_SESSION_DEALS          ||
      property==SYMBOL_PROP_SESSION_BUY_ORDERS     ||
      property==SYMBOL_PROP_SESSION_SELL_ORDERS    ||
      property==SYMBOL_PROP_VOLUME                 ||
      property==SYMBOL_PROP_VOLUMEHIGH             ||
      property==SYMBOL_PROP_VOLUMELOW              ||
      property==SYMBOL_PROP_TICKS_BOOKDEPTH        ||
      property==SYMBOL_PROP_OPTION_MODE            ||
      property==SYMBOL_PROP_OPTION_RIGHT           ||
      property==SYMBOL_PROP_BACKGROUND_COLOR 
   #endif         
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

已验证整数型属性被传递到方法。 如果所传递属性是“品种存在”,则返回 false— 如果创建了一个品种,则该品种存在,且我们无需将该属性显示在日志中,也不用于搜索和排序。 所有其他检查仅针对 MQL4。 针对 MQL4,如果已知所传递的品种属性不支持,则返回 False
如果检查属性时枚举属性中不存在所传递的属性,则不支持该属性。 返回 true

按品种对象返回支持实数型属性标志的方法实现:

//+------------------------------------------------------------------+
//| Return 'true' if a symbol supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CSymbolFX::SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property)
  {
   if(
     #ifdef __MQL5__
      (this.ChartMode()==SYMBOL_CHART_MODE_BID     && 
        (
         property==SYMBOL_PROP_LAST                ||
         property==SYMBOL_PROP_LASTHIGH            ||
         property==SYMBOL_PROP_LASTLOW
        )
      )                                            ||
      (this.ChartMode()==SYMBOL_CHART_MODE_LAST    && 
        (
         property==SYMBOL_PROP_BID                 ||
         property==SYMBOL_PROP_BIDHIGH             ||
         property==SYMBOL_PROP_BIDLOW              ||
         property==SYMBOL_PROP_ASK                 ||
         property==SYMBOL_PROP_ASKHIGH             ||
         property==SYMBOL_PROP_ASKLOW
        )
      )
     //--- __MQL4__
     #else 
      property==SYMBOL_PROP_ASKHIGH                            ||
      property==SYMBOL_PROP_ASKLOW                             ||
      property==SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT            ||
      property==SYMBOL_PROP_TRADE_TICK_VALUE_LOSS              ||
      property==SYMBOL_PROP_LAST                               ||
      property==SYMBOL_PROP_LASTHIGH                           ||
      property==SYMBOL_PROP_LASTLOW                            ||
      property==SYMBOL_PROP_VOLUME_LIMIT                       ||
      property==SYMBOL_PROP_MARGIN_LONG_INITIAL                ||
      property==SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL            ||
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL           ||
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL       ||
      property==SYMBOL_PROP_MARGIN_LONG_MAINTENANCE            ||
      property==SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE        ||
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE       ||
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE   ||
      property==SYMBOL_PROP_MARGIN_SHORT_INITIAL               ||
      property==SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL           ||
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL          ||
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL      ||
      property==SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE           ||
      property==SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE       ||
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE      ||
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE  ||
      property==SYMBOL_PROP_SESSION_VOLUME                     ||
      property==SYMBOL_PROP_SESSION_TURNOVER                   ||
      property==SYMBOL_PROP_SESSION_INTEREST                   ||
      property==SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME          ||
      property==SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME         ||
      property==SYMBOL_PROP_SESSION_OPEN                       ||
      property==SYMBOL_PROP_SESSION_CLOSE                      ||
      property==SYMBOL_PROP_SESSION_AW                         ||
      property==SYMBOL_PROP_SESSION_PRICE_SETTLEMENT           ||
      property==SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN            ||
      property==SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX            ||
      property==SYMBOL_PROP_VOLUME_REAL                        ||
      property==SYMBOL_PROP_VOLUMEHIGH_REAL                    ||
      property==SYMBOL_PROP_VOLUMELOW_REAL                     ||
      property==SYMBOL_PROP_OPTION_STRIKE                      ||
      property==SYMBOL_PROP_TRADE_ACCRUED_INTEREST             ||
      property==SYMBOL_PROP_TRADE_FACE_VALUE                   ||
      property==SYMBOL_PROP_TRADE_LIQUIDITY_RATE
     #endif         
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

这里的逻辑与前面的方法相同。 但首先,针对 MQL5 检查获得的属性。 如果它是最后成交价格属性(最后)之一,而图表基于出价(Bid),则在这种情况下所有这些属性均不支持且等于零。
如果图表基于最后价格(Last),则按出价(Bid)属性同样操作 — 所有出价(Bid)属性均不支持。
对于 MQL4,与上一个方法完全相同 — 将已知不支持的品种属性传递给该方法时,返回 false

按品种对象返回支持字符串型属性标志的方法实现:

//+------------------------------------------------------------------+
//| Return 'true' if a symbol supports a passed                      |
//| string property, otherwise return 'false'                        |
//+------------------------------------------------------------------+
bool CSymbolFX::SupportProperty(ENUM_SYMBOL_PROP_STRING property)
  {
   if(
   #ifdef __MQL5__ 
      property==SYMBOL_PROP_FORMULA && !this.IsCustom()
   #else
      property==SYMBOL_PROP_BASIS                  || 
      property==SYMBOL_PROP_BANK                   ||
      property==SYMBOL_PROP_ISIN                   ||
      property==SYMBOL_PROP_FORMULA                ||
      property==SYMBOL_PROP_PAGE
   #endif         
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

此处的一切都是相似的:对于 MQL5如果所传递的属性是“用于自定义品种计算的公式”,而品种并非自定义 ,返回 false— 不支持该属性。 接下来,我们检查已知不支持的 MQL4 品种属性,如果传递了 MQL4 不支持的属性,则返回 false

在日志里显示品种简要说明的方法:

//+------------------------------------------------------------------+
//| Display a short symbol description in the journal                |
//+------------------------------------------------------------------+
void CSymbolFX::PrintShort(void)
  {
   ::Print(this.GetStatusDescription()+" "+this.Name());
  }
//+------------------------------------------------------------------+

该方法仅在一行里显示品种状态文字描述其名称

其余的衍生类以相同的方式构造,且拥有相同的方法和相同的实现。
区别在于自定义品种类 — MQL4 中不存在此品种类型,因此所有检查仅针对 MQL5:

//+------------------------------------------------------------------+
//|                                                 SymbolCustom.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Symbol.mqh"
//+------------------------------------------------------------------+
//| Custom symbol                                                    |
//+------------------------------------------------------------------+
class CSymbolCustom : public CSymbol
  {
public:
//--- Constructor
                     CSymbolCustom(const string name) : CSymbol(SYMBOL_STATUS_CUSTOM,name) {}
//--- Supported integer properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_INTEGER property);
//--- Supported real properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property);
//--- Supported string properties of a symbol
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_STRING property);
//--- Display a short symbol description in the journal
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+
//| Return 'true' if a symbol supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CSymbolCustom::SupportProperty(ENUM_SYMBOL_PROP_INTEGER property)
  {
   if(property==SYMBOL_PROP_EXIST) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if a symbol supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CSymbolCustom::SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property)
  {
   if(
      (this.ChartMode()==SYMBOL_CHART_MODE_BID     && 
        (
         property==SYMBOL_PROP_LAST                ||
         property==SYMBOL_PROP_LASTHIGH            ||
         property==SYMBOL_PROP_LASTLOW
        )
      )                                            ||
      (this.ChartMode()==SYMBOL_CHART_MODE_LAST    && 
        (
         property==SYMBOL_PROP_BID                 ||
         property==SYMBOL_PROP_BIDHIGH             ||
         property==SYMBOL_PROP_BIDLOW              ||
         property==SYMBOL_PROP_ASK                 ||
         property==SYMBOL_PROP_ASKHIGH             ||
         property==SYMBOL_PROP_ASKLOW
        )
      )
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if a symbol supports a passed                      |
//| string property, otherwise return 'false'                        |
//+------------------------------------------------------------------+
bool CSymbolCustom::SupportProperty(ENUM_SYMBOL_PROP_STRING property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Display a short symbol description in the journal                |
//+------------------------------------------------------------------+
void CSymbolCustom::PrintShort(void)
  {
   ::Print(this.GetStatusDescription()+" "+this.Name());
  }
//+------------------------------------------------------------------+

CSymbol 衍生类的开发到此完毕。
其余衍生类的实现可在文章末尾和附件中找到。

鉴于我们需要针对品种集合进行搜索和排序,因此我们应该为此创建所有必要的功能。 打开 \MQL5\Include\DoEasy\Services\ 函数库文件夹中的 Select.mqh 文件,并在其内进行增减。
首先,包含抽象品种类

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| 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"
//+------------------------------------------------------------------+

在类的公开部分中声明所有用于搜索和排序的必要方法

//+------------------------------------------------------------------+
//| Class for sorting objects meeting the criterion                  |
//+------------------------------------------------------------------+
class CSelect
  {
private:
   //--- Method for comparing two values
   template<typename T>
   static bool       CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode);
public:
//+------------------------------------------------------------------+
//| Methods of working with orders                                   |
//+------------------------------------------------------------------+
   //--- Return the list of orders with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the order index with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property);
   static int        FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property);
   //--- Return the order index with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property);
   static int        FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with events                                   |
//+------------------------------------------------------------------+
   //--- Return the list of events with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the event index with the maximum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property);
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property);
   static int        FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property);
   //--- Return the event index with the minimum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property);
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property);
   static int        FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with accounts                                 |
//+------------------------------------------------------------------+
   //--- Return the list of accounts with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the event index with the maximum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property);
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property);
   static int        FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property);
   //--- Return the event index with the minimum value of the event's (1) integer, (2) real and (3) string properties
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property);
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property);
   static int        FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property);
//+------------------------------------------------------------------+
//| Methods of working with symbols                                  |
//+------------------------------------------------------------------+
   //--- Return the list of symbols with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the symbol index with the maximum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property);
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property);
   static int        FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property);
   //--- Return the symbol index with the minimum value of the order's (1) integer, (2) real and (3) string properties
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property);
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property);
   static int        FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

我们在类的实体之外编写其实现:

//+------------------------------------------------------------------+
//| Methods of working with symbol lists                             |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of symbols with one integer                      |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_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++)
     {
      CSymbol *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 symbols with one real                         |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_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++)
     {
      CSymbol *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 symbols with one string                       |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_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++)
     {
      CSymbol *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 listed symbol index                                   |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CSymbol *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CSymbol *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 listed symbol index                                   |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CSymbol *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CSymbol *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 listed symbol index                                   |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CSymbol *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CSymbol *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 listed symbol index                                   |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMin(CArrayObj* list_source,ENUM_SYMBOL_PROP_INTEGER property)
  {
   int index=0;
   CSymbol *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CSymbol *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 listed symbol index                                   |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMin(CArrayObj* list_source,ENUM_SYMBOL_PROP_DOUBLE property)
  {
   int index=0;
   CSymbol *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CSymbol *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 listed symbol index                                   |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindSymbolMin(CArrayObj* list_source,ENUM_SYMBOL_PROP_STRING property)
  {
   int index=0;
   CSymbol *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      CSymbol *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;
  }
//+------------------------------------------------------------------+

每个集合类我们都有类似的方法。 在函数库论述的第三部分中创建 CSelect 类时,已研究过它们。 所以,我们勿需在此赘述。

现在创建品种集合类的一切准备就绪。
程序当中有四种模式可用于处理品种集合:

  1. 操控单一品种,
  2. 操控品种列表,
  3. 操控市场观察窗口,
  4. 操控服务器上的完整可用品种列表。

为了使特征集合类“知晓”所要操控的品种,我们将运用以下方案:
在程序设置中设定操控品种的操作方法。 这可能是四个已声明操作模式之一。
程序还应该遵循相同原理,用函数库函数填充字符串数组:

  • 如果是操控单一品种,则数组仅包含当前品种,
  • 如果使用位于程序设置中的自定义品种列表,且定义了以逗号分隔的多个品种,则数组中将填充品种名称的字符串;如果字符串中仅设置了一个当前品种,或列表为空,则用当前品种进行操控
  • 如果是操控市场观察,则在唯一的数组单元格中设置 “MARKET_WATCH”,替代品种名称
  • 如果操控服务器上的完整品种列表,则在数组中设置 “ALL”,替代品种名称

所有这些都是自动完成的。 用户仅需要选择操控品种集合的必要模式,并在设置中为预定义品种创建至少一个字符串数组,或单个字符串。


品种集合类

在 \MQL5\Include\DoEasy\Collections\ 函数库文件夹中,在 SymbolsCollection.mqh 文件中创建新的 CSymbolsCollection 类。
CObject 标准函数库对象类将作为其基类。

将所有必需的类文件包含到新创建的文件中:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, 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\Symbols\Symbol.mqh"
#include "..\Objects\Symbols\SymbolFX.mqh"
#include "..\Objects\Symbols\SymbolFXMajor.mqh"
#include "..\Objects\Symbols\SymbolFXMinor.mqh"
#include "..\Objects\Symbols\SymbolFXExotic.mqh"
#include "..\Objects\Symbols\SymbolFXRub.mqh"
#include "..\Objects\Symbols\SymbolMetall.mqh"
#include "..\Objects\Symbols\SymbolIndex.mqh"
#include "..\Objects\Symbols\SymbolIndicative.mqh"
#include "..\Objects\Symbols\SymbolCrypto.mqh"
#include "..\Objects\Symbols\SymbolCommodity.mqh"
#include "..\Objects\Symbols\SymbolExchange.mqh"
#include "..\Objects\Symbols\SymbolFutures.mqh"
#include "..\Objects\Symbols\SymbolCFD.mqh"
#include "..\Objects\Symbols\SymbolStocks.mqh"
#include "..\Objects\Symbols\SymbolBonds.mqh"
#include "..\Objects\Symbols\SymbolOption.mqh"
#include "..\Objects\Symbols\SymbolCollateral.mqh"
#include "..\Objects\Symbols\SymbolCustom.mqh"
#include "..\Objects\Symbols\SymbolCommon.mqh"
//+------------------------------------------------------------------+
//| Collection of historical orders and deals                        |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   
public:
//--- Constructor
                     CSymbolsCollection();

  };
//+------------------------------------------------------------------+

将已经成为函数库集合类标准的类成员值和方法添加进来:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, 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\Symbols\Symbol.mqh"
#include "..\Objects\Symbols\SymbolFX.mqh"
#include "..\Objects\Symbols\SymbolFXMajor.mqh"
#include "..\Objects\Symbols\SymbolFXMinor.mqh"
#include "..\Objects\Symbols\SymbolFXExotic.mqh"
#include "..\Objects\Symbols\SymbolFXRub.mqh"
#include "..\Objects\Symbols\SymbolMetall.mqh"
#include "..\Objects\Symbols\SymbolIndex.mqh"
#include "..\Objects\Symbols\SymbolIndicative.mqh"
#include "..\Objects\Symbols\SymbolCrypto.mqh"
#include "..\Objects\Symbols\SymbolCommodity.mqh"
#include "..\Objects\Symbols\SymbolExchange.mqh"
#include "..\Objects\Symbols\SymbolFutures.mqh"
#include "..\Objects\Symbols\SymbolCFD.mqh"
#include "..\Objects\Symbols\SymbolStocks.mqh"
#include "..\Objects\Symbols\SymbolBonds.mqh"
#include "..\Objects\Symbols\SymbolOption.mqh"
#include "..\Objects\Symbols\SymbolCollateral.mqh"
#include "..\Objects\Symbols\SymbolCustom.mqh"
#include "..\Objects\Symbols\SymbolCommon.mqh"
//+------------------------------------------------------------------+
//| Collection of historical orders and deals                        |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   CListObj          m_list_all_symbols;     // The list of all symbol objects
   
public:
//--- Return the full collection list 'as is'
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_all_symbols;                                      }
//--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)    { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }

//--- Constructor
                     CSymbolsCollection();
   
  };
//+------------------------------------------------------------------+

以前在创建集合时,我们已研究过所有这些变量和方法。 勿需于此再次讨论它们。

将操控品种集合类的其余变量和方法添加进来:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, 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\Symbols\Symbol.mqh"
#include "..\Objects\Symbols\SymbolFX.mqh"
#include "..\Objects\Symbols\SymbolFXMajor.mqh"
#include "..\Objects\Symbols\SymbolFXMinor.mqh"
#include "..\Objects\Symbols\SymbolFXExotic.mqh"
#include "..\Objects\Symbols\SymbolFXRub.mqh"
#include "..\Objects\Symbols\SymbolMetall.mqh"
#include "..\Objects\Symbols\SymbolIndex.mqh"
#include "..\Objects\Symbols\SymbolIndicative.mqh"
#include "..\Objects\Symbols\SymbolCrypto.mqh"
#include "..\Objects\Symbols\SymbolCommodity.mqh"
#include "..\Objects\Symbols\SymbolExchange.mqh"
#include "..\Objects\Symbols\SymbolFutures.mqh"
#include "..\Objects\Symbols\SymbolCFD.mqh"
#include "..\Objects\Symbols\SymbolStocks.mqh"
#include "..\Objects\Symbols\SymbolBonds.mqh"
#include "..\Objects\Symbols\SymbolOption.mqh"
#include "..\Objects\Symbols\SymbolCollateral.mqh"
#include "..\Objects\Symbols\SymbolCustom.mqh"
#include "..\Objects\Symbols\SymbolCommon.mqh"
//+------------------------------------------------------------------+
//| Collection of historical orders and deals                        |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   CListObj          m_list_all_symbols;     // The list of all symbol objects
   ENUM_SYMBOLS_MODE m_mode_list;            // Mode of working with symbol lists
   int               m_delta_symbol;         // Difference in the number of symbols compared to the previous check
   int               m_last_num_symbol;      // Number of symbols in the Market Watch window during the previous check
   int               m_global_error;         // Global error code
//--- Return the flag of a symbol object presence by its name in the list of all symbols
   bool              IsPresentSymbolInList(const string symbol_name);
//--- Create the symbol object and place it to the list
   bool              CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name);
//--- Return the type of a used symbol list (Market watch/Server)
   ENUM_SYMBOLS_MODE TypeSymbolsList(const string &symbol_used_array[]);

//--- Define a symbol affiliation with a group by name and return it
   ENUM_SYMBOL_STATUS SymbolStatus(const string symbol_name)      const;
//--- Return a symbol affiliation with a category by custom criteria
   ENUM_SYMBOL_STATUS StatusByCustomPredefined(const string symbol_name)  const;
//--- Return a symbol affiliation with categories by margin calculation
   ENUM_SYMBOL_STATUS StatusByCalcMode(const string symbol_name)  const;
//--- Return a symbol affiliation with pre-defined (1) majors, (2) minors, (3) exotics, (4) RUB,
//--- (5) indicatives, (6) metals, (7) commodities, (8) indices, (9) cryptocurrency, (10) options
   bool              IsPredefinedFXMajor(const string name)       const;
   bool              IsPredefinedFXMinor(const string name)       const;
   bool              IsPredefinedFXExotic(const string name)      const;
   bool              IsPredefinedFXRUB(const string name)         const;
   bool              IsPredefinedIndicative(const string name)    const;
   bool              IsPredefinedMetall(const string name)        const;
   bool              IsPredefinedCommodity(const string name)     const;
   bool              IsPredefinedIndex(const string name)         const;
   bool              IsPredefinedCrypto(const string name)        const;
   bool              IsPredefinedOption(const string name)        const;

//--- Search for a symbol and return the flag indicating its presence on the server
   bool              Exist(const string name)                     const;
   
public:
//--- Return the full collection list 'as is'
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_all_symbols;                                      }
//--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)    { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::BySymbolProperty(this.GetList(),property,value,mode); }
//--- Return the number of new symbols in the Market Watch window
   int               NewSymbols(void)    const                                                              { return this.m_delta_symbol;                                           }
//--- Return the mode of working with symbol lists
   ENUM_SYMBOLS_MODE ModeSymbolsList(void)                        const                                     { return this.m_mode_list;                                              }
//--- Constructor
                     CSymbolsCollection();
   
//--- Set the list of used symbols
   bool              SetUsedSymbol(const string &symbol_used_array[]);
//--- Update (1) all, (2) quote data of the collection symbols
   void              Refresh(void);
   void              RefreshRates(void);
  };
//+------------------------------------------------------------------+

该类应知道要操控哪个品种类:当前品种,指定的品种集合,位于“市场观察”窗口中的品种列表,或服务器上所有品种的完整列表。 m_mode_list 类成员变量保存品种操控模式列表当中之一。
当操控市场观察的品种列表时,我们需要不断跟踪此列表(将在下一篇文章中实现),掌握上次检查期间的品种数量,并相应地关注市场观察列表增减的名单变化数量,以便及时重新排列 m_list_all_symbols 交易品种集合列表,并继续精准地操控所有品种。
与以前的集合一样,我们引入了一个新的类成员变量,该变量保存错误代码,您可以在 CEngine 函数库基类中查看和处理该错误代码。
当创建一个新的品种对象,并将其添加到集合之中时,我们需要确保列表中没有重复的品种。 这是通过 IsPresentSymbolInList() 方法完成的。
CreateNewSymbol() 方法用于创建新品种,并将其添加到集合之中。
鉴于在品种集合已分派了四种品种列表操控模式,因此 TypeSymbolsList() 方法用于定义将会使用的模式。<br14 /> 在创建新品种之前,我们首先要为其定义和设置一个组(或用户为其分配的组)。 SymbolStatus() 方法用于定义品种组(其状态)。
定义品种状态时,首先利用 StatusByCustomPredefined() 方法在指定的自定义数组中搜索其名称。
如果该方法返回的状态是“一般”,则品种状态由 StatusByCalcMode() 方法的保证金计算类型定义。
其他方法用于按用户数组进行搜索,并定义品种所属的自定义类别。 这些方法返回的标志指示品种是否属于主要、次要、指数和其他组。
Exist() 方法返回服务器上存在该品种的标志。
NewSymbols() 方法返回从“市场观察”窗口添加或删除的新交易品种的数量,而 ModeSymbolsList() 方法返回四种操控模式列表之一(当前品种,预定义品种集合,市场观察列表,以及服务器上的品种总数)给调用程序。
SetUsedSymbol() 方法接受品种数组,或操作模式的描述,以及通过数组传递的品种列表,并创建品种集合列表。
Refresh() 方法刷新品种集合之中所有变化的数据,而 RefreshRates() 方法仅更新所有集合品种的引用数据。 这两种方法都从 CEngine 函数库主对象的计时器中调用。

在此阶段,我们定义了使用品种集合类所需的所有方法。 我们观察一下它们的结构。

实现类构造函数:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSymbolsCollection::CSymbolsCollection(void) : m_last_num_symbol(0),m_delta_symbol(0),m_mode_list(SYMBOLS_MODE_CURRENT)
  {
   this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_NAME);
   this.m_list_all_symbols.Clear();
   this.m_list_all_symbols.Type(COLLECTION_SYMBOLS_ID);
  }
//+------------------------------------------------------------------+

在构造函数初始化清单中,初始化上一次检查期间的品种数量当前与之前品种数量的差值,以及设置品种列表操控模式为“操控当前品种”
在类的实体之中,设置按名称对品种集合列表进行排序清除列表,并为其分配“品种集合列表” ID

刷新所有品种集合中所有数据的方法:

//+------------------------------------------------------------------+
//| Update all collection symbol data                                |
//+------------------------------------------------------------------+
void CSymbolsCollection::Refresh(void)
  {
   int total=this.m_list_all_symbols.Total();
   if(total==0)
      return;
   for(int i=0;i<total;i++)
     {
      CSymbol *symbol=this.m_list_all_symbols.At(i);
      if(symbol==NULL)
         continue;
      symbol.Refresh();
     }
  }
//+------------------------------------------------------------------+

按所有集合中品种的数量循环, 从集合列表中获取下一个品种,并调用上一篇文章中研究过的 CSymbol 类的 Refresh() 方法更新其数据

刷新所有集合中品种报价数据的方法:

//+------------------------------------------------------------------+
//| Update quote data of the collection symbols                      |
//+------------------------------------------------------------------+
void CSymbolsCollection::RefreshRates(void)
  {
   int total=this.m_list_all_symbols.Total();
   if(total==0)
      return;
   for(int i=0;i<total;i++)
     {
      CSymbol *symbol=this.m_list_all_symbols.At(i);
      if(symbol==NULL)
         continue;
      symbol.RefreshRates();
     }
  }
//+------------------------------------------------------------------+

按所有集合中品种的数量循环, 从集合列表中获取下一个品种,并调用 CSymbol 类的 Refresh() 方法更新其数据

创建新品种对象,并将其放置在品种集合列表中的方法:

//+------------------------------------------------------------------+
//| Create a symbol object and place it to the list                  |
//+------------------------------------------------------------------+
bool CSymbolsCollection::CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name)
  {
   if(this.IsPresentSymbolInList(name))
     {
      return true;
     }
   if(#ifdef __MQL5__ !::SymbolInfoInteger(name,SYMBOL_EXIST) #else !this.Exist(name) #endif )
     {
      string t1=TextByLanguage("Ошибка входных данных: нет символа ","Input error: no ");
      string t2=TextByLanguage(" на сервере"," symbol on the server");
      ::Print(DFUN,t1,name,t2);
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
      return false;
     }
   CSymbol *symbol=NULL;
   switch(symbol_status)
     {
      case SYMBOL_STATUS_FX         :  symbol=new CSymbolFX(name);         break;   // Forex symbol
      case SYMBOL_STATUS_FX_MAJOR   :  symbol=new CSymbolFXMajor(name);    break;   // Major Forex symbol
      case SYMBOL_STATUS_FX_MINOR   :  symbol=new CSymbolFXMinor(name);    break;   // Minor Forex symbol
      case SYMBOL_STATUS_FX_EXOTIC  :  symbol=new CSymbolFXExotic(name);   break;   // Exotic Forex symbol
      case SYMBOL_STATUS_FX_RUB     :  symbol=new CSymbolFXRub(name);      break;   // Forex symbol/RUR
      case SYMBOL_STATUS_METAL      :  symbol=new CSymbolMetall(name);     break;   // Metal
      case SYMBOL_STATUS_INDEX      :  symbol=new CSymbolIndex(name);      break;   // Index
      case SYMBOL_STATUS_INDICATIVE :  symbol=new CSymbolIndicative(name); break;   // Indicative
      case SYMBOL_STATUS_CRYPTO     :  symbol=new CSymbolCrypto(name);     break;   // Cryptocurrency symbol
      case SYMBOL_STATUS_COMMODITY  :  symbol=new CSymbolCommodity(name);  break;   // Commodity
      case SYMBOL_STATUS_EXCHANGE   :  symbol=new CSymbolExchange(name);   break;   // Exchange symbol
      case SYMBOL_STATUS_FUTURES    :  symbol=new CSymbolFutures(name);    break;   // Futures
      case SYMBOL_STATUS_CFD        :  symbol=new CSymbolCFD(name);        break;   // CFD
      case SYMBOL_STATUS_STOCKS     :  symbol=new CSymbolStocks(name);     break;   // Stock
      case SYMBOL_STATUS_BONDS      :  symbol=new CSymbolBonds(name);      break;   // Bond
      case SYMBOL_STATUS_OPTION     :  symbol=new CSymbolOption(name);     break;   // Option
      case SYMBOL_STATUS_COLLATERAL :  symbol=new CSymbolCollateral(name); break;   // Non-tradable asset
      case SYMBOL_STATUS_CUSTOM     :  symbol=new CSymbolCustom(name);     break;   // Custom symbol
      default                       :  symbol=new CSymbolCommon(name);     break;   // The rest
     }
   if(symbol==NULL)
     {
      ::Print(DFUN,TextByLanguage("Не удалось создать объект-символ ","Failed to create symbol object "),name);
      return false;
     }
   if(!this.m_list_all_symbols.Add(symbol))
     {
      string t1=TextByLanguage("Не удалось добавить символ ","Failed to add ");
      string t2=TextByLanguage(" в список"," symbol to the list");
      ::Print(DFUN,t1,name,t2);
      delete symbol;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

方法需要传递品种状态名称如果该品种已经存在于品种集合列表之中则返回 true "silently" —没有 错误,但由于该品种已存在,因此无需再次添加该品种。
接着,通过品种名称在服务器上检查是否存在该品种。 如果没有这个品种,则显示品种缺失消息,将“未知符号”值分配给错误代码,然后返回 false
如果该品种存在,则根据传递给方法的状态创建一个新的品种对象。 为了创建品种对象,要利用与所传递的状态相对应的抽象品种的衍生类。 若对象创建错误的情况下,则显示相应的消息并返回 false
如果新品种对象创建成功,将其添加到品种集合列表,并在添加成功的情况下返回 true ,或在不成功时,显示一条错误消息,然后返回 false

返回品种在集合列表中存标志的方法实现:

//+------------------------------------------------------------------+
//| Return the symbol object presence flag                           |
//| by its name in the list of all symbols                           |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPresentSymbolInList(const string symbol_name)
  {
   CArrayObj *list=dynamic_cast<CListObj*>(&this.m_list_all_symbols);
   list.Sort(SORT_BY_SYMBOL_NAME);
   list=CSelect::BySymbolProperty(list,SYMBOL_PROP_NAME,symbol_name,EQUAL);
   return(list==NULL || list.Total()==0 ? false : true);
  }
//+------------------------------------------------------------------+

将所需品种的名称传递给方法,然后列表按品种名称所传递的名称进行排序。 如果列表不为空,意即发现含有该名称的品种 — 返回 true。 否则,返回 false — 列表中没有该品种。

设置品种集合列表的方法实现:

//+------------------------------------------------------------------+
//| Set the list of used symbols                                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::SetUsedSymbol(const string &symbol_used_array[])
  {
   this.m_mode_list=this.TypeSymbolsList(symbol_used_array);
   this.m_list_all_symbols.Clear();
   this.m_list_all_symbols.Sort(SORT_BY_SYMBOL_NAME);
   //--- Use only the current symbol
   if(this.m_mode_list==SYMBOLS_MODE_CURRENT)
     {
      string name=::Symbol();
      ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
      return this.CreateNewSymbol(status,name);
     }
   else
     {
      bool res=true;
      //--- Use the pre-defined symbol list
      if(this.m_mode_list==SYMBOLS_MODE_DEFINES)
        {
         int total=::ArraySize(symbol_used_array);
         for(int i=0;i<total;i++)
           {
            string name=symbol_used_array[i];
            ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
            bool add=this.CreateNewSymbol(status,name);
            res &=add;
            if(!add) 
               continue;
           }
         return res;
        }
      //--- Use the full list of the server symbols
      else if(this.m_mode_list==SYMBOLS_MODE_ALL)
        {
         int total=::SymbolsTotal(false);
         for(int i=0;i<total;i++)
           {
            string name=::SymbolName(i,false);
            ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
            bool add=this.CreateNewSymbol(status,name);
            res &=add;
            if(!add) 
               continue;
           }
         return res;
        }
      //--- Use the symbol list from the Market Watch window
      else if(this.m_mode_list==SYMBOLS_MODE_MARKET_WATCH)
        {
         int total=::SymbolsTotal(true);
         for(int i=0;i<total;i++)
           {
            string name=::SymbolName(i,true);
            ENUM_SYMBOL_STATUS status=this.SymbolStatus(name);
            bool add=this.CreateNewSymbol(status,name);
            res &=add;
            if(!add) 
               continue;
           }
         return res;
        }
     }
   return false;
  }
//+------------------------------------------------------------------+

该方法根据传递给它的品种数组 的内容来创建品种集合列表。 首先,定义操控品种列表的方法(“品种/一组自定义品种/市场观察/完整列表”之一),然后清除列表 按名称排序
若是 仅用当前品种

若是 使用预定义品种列表

其余两种模式的处理方式类似于使用预定义列表的方式。 不过,代替将数组传递给该方法,品种即可取自市场观察或者取自服务器上的完整品种列表

返回品种列表操控模式的方法实现:

//+------------------------------------------------------------------+
//|Return the type of a used symbol list (Market watch/Server)       |
//+------------------------------------------------------------------+
ENUM_SYMBOLS_MODE CSymbolsCollection::TypeSymbolsList(const string &symbol_used_array[])
  {
   int total=::ArraySize(symbol_used_array);
   if(total<1)
      return SYMBOLS_MODE_CURRENT;
   string type=::StringSubstr(symbol_used_array[0],13);
   return
     (
      type=="MARKET_WATCH" ? SYMBOLS_MODE_MARKET_WATCH   :
      type=="ALL"          ? SYMBOLS_MODE_ALL            :
      (total==1 && symbol_used_array[0]==::Symbol() ? SYMBOLS_MODE_CURRENT : SYMBOLS_MODE_DEFINES)
     );
  }
//+------------------------------------------------------------------+

方法接收品种名称数组,或操控模式列表。
如果传递了一个空数组,则返回的操作模式仅用当前品种

接下来,自其零号单元接收数组内容,然后,

按名称返回携带品种从属分组关系的方法实现:

//+------------------------------------------------------------------+
//| Define a symbol affiliation with a group by name and return it   |
//+------------------------------------------------------------------+
ENUM_SYMBOL_STATUS CSymbolsCollection::SymbolStatus(const string symbol_name) const
  {
   ENUM_SYMBOL_STATUS status=this.StatusByCustomPredefined(symbol_name);
   return(status==SYMBOL_STATUS_COMMON ? this.StatusByCalcMode(symbol_name) : status);
  }
//+------------------------------------------------------------------+

品种名称传递给方法。 接着,检查与指定自定义组的隶属关系如果返回“一般组”状态按“保证金计算模式”品种属性搜索分组。 结果则为,返回获得的状态。 顺便说一下,根据利润和保证金计算模式,它可以等于分组之一,或其可以保留在一般品种组之中。
结果就是,该品种的状态为自定义组。 如果自定义组中不存在该品种,则可按保证金计算方法将其分配至一个组。 如果为其定义分组失败,它将保留在一般组中。

返回品种从属某个自定义组标志的方法实现:

//+------------------------------------------------------------------+
//| Return a symbol affiliation with majors                          |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedFXMajor(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXMajors);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXMajors[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with minors                          |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedFXMinor(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXMinors);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXMinors[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with exotic symbols                  |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedFXExotic(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXExotics);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXExotics[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with RUB symbols                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedFXRUB(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXRub);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXRub[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with indicative symbols              |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedIndicative(const string name) const
  {
   int total=::ArraySize(DataSymbolsFXIndicatives);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsFXIndicatives[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with metals                          |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedMetall(const string name) const
  {
   int total=::ArraySize(DataSymbolsMetalls);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsMetalls[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with commodities                     |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedCommodity(const string name) const
  {
   int total=::ArraySize(DataSymbolsCommodities);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsCommodities[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with indices                         |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedIndex(const string name) const
  {
   int total=::ArraySize(DataSymbolsIndexes);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsIndexes[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with a cryptocurrency                |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedCrypto(const string name) const
  {
   int total=::ArraySize(DataSymbolsCrypto);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsCrypto[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Return a symbol affiliation with options                         |
//+------------------------------------------------------------------+
bool CSymbolsCollection::IsPredefinedOption(const string name) const
  {
   int total=::ArraySize(DataSymbolsOptions);
   for(int i=0;i<total;i++)
      if(name==DataSymbolsOptions[i])
         return true;
   return false;
  }
//+------------------------------------------------------------------+

依据(已检查的自定义组)方法名称,在所对应的自定义数组中搜索传递给该方法的品种名称。 如果在数组中找到这个品种,返回 true。 否则 — false

该方法依据其在自定义组中存在与否返回品种状态:

//+------------------------------------------------------------------+
//| Return a category by custom criteria                             |
//+------------------------------------------------------------------+
ENUM_SYMBOL_STATUS CSymbolsCollection::StatusByCustomPredefined(const string symbol_name) const
  {
   return
     (
      this.IsPredefinedFXMajor(symbol_name)     ?  SYMBOL_STATUS_FX_MAJOR     :
      this.IsPredefinedFXMinor(symbol_name)     ?  SYMBOL_STATUS_FX_MINOR     :
      this.IsPredefinedFXExotic(symbol_name)    ?  SYMBOL_STATUS_FX_EXOTIC    :
      this.IsPredefinedFXRUB(symbol_name)       ?  SYMBOL_STATUS_FX_RUB       :
      this.IsPredefinedOption(symbol_name)      ?  SYMBOL_STATUS_OPTION       :
      this.IsPredefinedCommodity(symbol_name)   ?  SYMBOL_STATUS_COMMODITY    :
      this.IsPredefinedCrypto(symbol_name)      ?  SYMBOL_STATUS_CRYPTO       :
      this.IsPredefinedMetall(symbol_name)      ?  SYMBOL_STATUS_METAL        :
      this.IsPredefinedIndex(symbol_name)       ?  SYMBOL_STATUS_INDEX        :
      this.IsPredefinedIndicative(symbol_name)  ?  SYMBOL_STATUS_INDICATIVE   :
      SYMBOL_STATUS_COMMON
     );
  }  
//+------------------------------------------------------------------+

品种名称传递给,并使用上述方法在每个自定义组中逐一检查是否存在。 一旦品种出现在任何组中,则返回发现该品种所对应的分组状态。 如果在任何组中均未找到该品种,则返回“一般品种组”状态

该方法按保证金计算方法返回品种隶属关系分组:

//+------------------------------------------------------------------+
//|Return affiliation with a margin calculation category             |
//+------------------------------------------------------------------+
ENUM_SYMBOL_STATUS CSymbolsCollection::StatusByCalcMode(const string symbol_name) const
  {
   ENUM_SYMBOL_CALC_MODE calc_mode=(ENUM_SYMBOL_CALC_MODE)::SymbolInfoInteger(symbol_name,SYMBOL_TRADE_CALC_MODE);
   return
     (
      calc_mode==SYMBOL_CALC_MODE_EXCH_OPTIONS_MARGIN                                                                               ?  SYMBOL_STATUS_OPTION       :
      calc_mode==SYMBOL_CALC_MODE_SERV_COLLATERAL                                                                                   ?  SYMBOL_STATUS_COLLATERAL   :
      calc_mode==SYMBOL_CALC_MODE_FUTURES                                                                                           ?  SYMBOL_STATUS_FUTURES      :
      calc_mode==SYMBOL_CALC_MODE_CFD           || calc_mode==SYMBOL_CALC_MODE_CFDINDEX || calc_mode==SYMBOL_CALC_MODE_CFDLEVERAGE  ?  SYMBOL_STATUS_CFD          :
      calc_mode==SYMBOL_CALC_MODE_FOREX         || calc_mode==SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE                                    ?  SYMBOL_STATUS_FX           :
      calc_mode==SYMBOL_CALC_MODE_EXCH_STOCKS   || calc_mode==SYMBOL_CALC_MODE_EXCH_STOCKS_MOEX                                     ?  SYMBOL_STATUS_STOCKS       :
      calc_mode==SYMBOL_CALC_MODE_EXCH_BONDS    || calc_mode==SYMBOL_CALC_MODE_EXCH_BONDS_MOEX                                      ?  SYMBOL_STATUS_BONDS        :
      calc_mode==SYMBOL_CALC_MODE_EXCH_FUTURES  || calc_mode==SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS                                   ?  SYMBOL_STATUS_FUTURES      :
      SYMBOL_STATUS_COMMON
     );
  }
//+------------------------------------------------------------------+

方法接收品种名称,接着我们接收品种的保证金计算方法,然后根据获得的值返回品种状态如果未能识别任何计算方法,则返回“一般品种组”状态

该方法在服务器上查找品种,并返回其存在标志:

//+---------------------------------------------------------------------------------+
//| Search for a symbol and return the flag indicating its presence on the server   |
//+---------------------------------------------------------------------------------+
bool CSymbolsCollection::Exist(const string name) const
  {
   int total=::SymbolsTotal(false);
   for(int i=0;i<total;i++)
      if(::SymbolName(i,false)==name)
         return true;
   return false;
  }
//+------------------------------------------------------------------+

品种集合类已准备就绪。 现在我们需要启用它。 与往常一样,在 CEngine 类中执行启动并处理。 我们要对其进行必要的修改。

将品种集合类文件包含到 CEngine 类文件中,并声明品种集合对象,以及必要的方法:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, 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"
//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
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
   CArrayObj            m_list_counters;                 // List of timer counters
   int                  m_global_error;                  // Global error code
   bool                 m_first_start;                   // First launch flag
   bool                 m_is_hedge;                      // Hedge account flag
   bool                 m_is_tester;                     // Flag of working in the tester
   bool                 m_is_market_trade_event;         // Account trading event flag
   bool                 m_is_history_trade_event;        // Account history trading event flag
   bool                 m_is_account_event;              // Account change event flag
   ENUM_TRADE_EVENT     m_last_trade_event;              // Last account trading event
   ENUM_ACCOUNT_EVENT   m_last_account_event;            // Last event in the account properties
//--- Return the counter index by id
   int                  CounterIndex(const int id) const;
//--- Return the (1) first launch flag, (2) presence of the flag in the trading event
   bool                 IsFirstStart(void);
//--- Work with (1) order, deal and position, (2) account events
   void                 TradeEventsControl(void);
   void                 AccountEventsControl(void);
//--- (1) Working with a symbol collection and (2) symbol list events in the market watch window
   void                 SymbolEventsControl(void);
   void                 MarketWatchEventsControl(void);
//--- Return the last (1) market pending order, (2) market order, (3) last position, (4) position by ticket
   COrder              *GetLastMarketPending(void);
   COrder              *GetLastMarketOrder(void);
   COrder              *GetLastPosition(void);
   COrder              *GetPosition(const ulong ticket);
//--- Return the last (1) removed pending order, (2) historical market order, (3) historical order (market or pending one) by its ticket
   COrder              *GetLastHistoryPending(void);
   COrder              *GetLastHistoryOrder(void);
   COrder              *GetHistoryOrder(const ulong ticket);
//--- Return the (1) first and the (2) last historical market orders from the list of all position orders, (3) the last deal
   COrder              *GetFirstOrderPosition(const ulong position_id);
   COrder              *GetLastOrderPosition(const ulong position_id);
   COrder              *GetLastDeal(void);
public:
   //--- Return the list of market (1) positions, (2) pending orders and (3) market orders
   CArrayObj           *GetListMarketPosition(void);
   CArrayObj           *GetListMarketPendings(void);
   CArrayObj           *GetListMarketOrders(void);
   //--- Return the list of historical (1) orders, (2) removed pending orders, (3) deals, (4) all position market orders by its id
   CArrayObj           *GetListHistoryOrders(void);
   CArrayObj           *GetListHistoryPendings(void);
   CArrayObj           *GetListDeals(void);
   CArrayObj           *GetListAllOrdersByPosID(const ulong position_id);
//--- Return the list of (1) accounts, (2) account events, (3) account change event by its index in the list
//--- (4) the current account, (5) event description
   CArrayObj           *GetListAllAccounts(void)                        { return this.m_accounts.GetList();                   }
   CArrayInt           *GetListAccountEvents(void)                      { return this.m_accounts.GetListChanges();            }
   ENUM_ACCOUNT_EVENT   GetAccountEventByIndex(const int index)         { return this.m_accounts.GetEvent(index);             }
   CAccount            *GetAccountCurrent(void);
   string               GetAccountEventDescription(ENUM_ACCOUNT_EVENT event);
//--- Return the list of used symbols
   CArrayObj           *GetListAllUsedSymbols(void)                     { return this.m_symbols.GetList();                    }
   
//--- Return the list of order, deal and position events
   CArrayObj           *GetListAllOrdersEvents(void)                    { return this.m_events.GetList();                     }
//--- Reset the last trading event
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Return the (1) last trading event, (2) the last event in the account properties, (3) hedging account flag, (4) flag of working in the tester
   ENUM_TRADE_EVENT     LastTradeEvent(void)                      const { return this.m_last_trade_event;                     }
   ENUM_ACCOUNT_EVENT   LastAccountEvent(void)                    const { return this.m_last_account_event;                   }
   bool                 IsHedge(void)                             const { return this.m_is_hedge;                             }
   bool                 IsTester(void)                            const { return this.m_is_tester;                            }
   bool                 IsAccountsEvent(void)                     const { return this.m_accounts.IsAccountEvent();            }
//--- Return an account event code
   int                  GetAccountEventsCode(void)                const { return this.m_accounts.GetEventCode();              }
//--- Return CEngine global error code
   int                  GetError(void)                            const { return this.m_global_error;                         }
//--- Create the timer counter
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Timer
   void                 OnTimer(void);
//--- Set the list of used symbols
   bool                 SetUsedSymbols(const string &array_symbols[])   { return this.m_symbols.SetUsedSymbol(array_symbols); }
//--- Constructor/destructor
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

SymbolEventsControl() 方法用于更新所有集合之中品种的报价数据,而 MarketWatchEventsControl() 方法用于更新所有集合之中品种的其余数据,并跟踪“市场观察”窗口中的事件(将在下一篇文章的品种集合事件类中研究)。
GetListAllUsedSymbols() 方法利用 CSymbolsCollection 类的 GetList() 方法将品种集合的完整列表返回给调用程序。
SetUsedSymbols() 方法调用 CSymbolsCollection 类的同名 SetUsedSymbol() 方法,其中会把程序中用到的所有品种按顺序将其品种对象填充到集合列表。

我们来研究这些方法的结构。

在类构造函数中,创建 firstsecond 品种集合计时器的计数器。在前一个计时器中,我们将刷新所有集合内品种的报价数据,而在第二个当中,我们将更新其余的品种数据,并管理市场观察窗口事件。

//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_last_trade_event(TRADE_EVENT_NO_EVENT),m_last_account_event(ACCOUNT_EVENT_NO_EVENT),m_global_error(ERR_SUCCESS)
  {
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE);
   this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE);
   
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1);
   this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2);
   
   ::ResetLastError();
   #ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   #endif 
  }
//+------------------------------------------------------------------+

将操控两个品种集合的计时器代码添加到类 OnTimer() 处理程序中:

//+------------------------------------------------------------------+
//| CEngine timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Timer of the collections of historical orders and deals, as well as of market orders and positions
   int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If unpaused, work with the order, deal and position collections events
            if(counter.IsTimeDone())
               this.TradeEventsControl();
           }
         //--- If this is a tester, work with collection events by tick
         else
            this.TradeEventsControl();
        }
     }
//--- Account collection timer
   index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If unpaused, work with the account collection events
            if(counter.IsTimeDone())
               this.AccountEventsControl();
           }
         //--- If this is a tester, work with collection events by tick
         else
            this.AccountEventsControl();
        }
     }
     
//--- Timer 1 of the symbol collection (updating symbol quote data in the collection)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If the pause is over, update quote data of all symbols in the collection
            if(counter.IsTimeDone())
               this.SymbolEventsControl();
           }
         //--- In case of a tester, update quote data of all collection symbols by tick
         else
            this.SymbolEventsControl();
        }
     }
//--- Timer 2 of the symbol collection (updating all data of all symbols in the collection and track symbol search events in the market watch window)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- If this is not a tester
         if(!this.IsTester())
           {
            //--- If the pause is over
            if(counter.IsTimeDone())
              {
               //--- update data of all symbols in the collection
               this.m_symbols.Refresh();
               //--- When workign with the market watch list, check the market watch window events
               if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH)
                  this.MarketWatchEventsControl();
              }
           }
         //--- In case of a tester, update data of all collection symbols by tick
         else
            this.m_symbols.Refresh();
        }
     }
  }
//+------------------------------------------------------------------+

整个逻辑在代码注释中均已提供。 在这里啰嗦没有任何意义。

在目前的实现中,操控品种集合的方法仅调用 CSymbolsCollection 类的 RefreshRates 方法来刷新所有集合内品种的报价数据

//+------------------------------------------------------------------+
//| Working with a symbol collection                                 |
//+------------------------------------------------------------------+
void CEngine::SymbolEventsControl(void)
  {
   this.m_symbols.RefreshRates();
  }
//+------------------------------------------------------------------+

在目前的实现中,调用“市场观察”窗口事件的方法检查它是否已从策略测试器中调用,如果是在策略测试器运行则退出(在策略测试器中跟踪市场观察窗口事件没有意义):

//+------------------------------------------------------------------+
//| Working with symbol list events in the market watch window       |
//+------------------------------------------------------------------+
void CEngine::MarketWatchEventsControl(void)
  {
   if(this.IsTester())
      return;
//--- Tracking Market Watch window events

//---
  }
//+------------------------------------------------------------------+

在下一篇文章中,我们将完成所有与品种集合事件相关的工作。

所有这些都必须修改 CEngine 类。

鉴于我们需要将所需用到的品种模式传递给品种集合类,因此我们需要定义操作模式并正确填充品种数组的函数,以便将其传递给 CEngine 函数库的主要对象。

从 \MQL5\Include\DoEasy\Services\ 打开 DELib.mqh 服务函数文件,并在其中添加所需的函数:

//+------------------------------------------------------------------+
//| Prepare the symbol array for a symbol collection                 |
//+------------------------------------------------------------------+
bool CreateUsedSymbolsArray(const ENUM_SYMBOLS_MODE mode_used_symbols,string defined_used_symbols,string &used_symbols_array[])
  {
   //--- When working with the current symbol
   if(mode_used_symbols==SYMBOLS_MODE_CURRENT)
     {
      //--- Write the name of the current symbol to the only array cell
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=Symbol();
      return true;
     }
   //--- If working with a predefined symbol set (from the defined_used_symbols string)
   else if(mode_used_symbols==SYMBOLS_MODE_DEFINES)
     {
      //--- Set a comma as a separator
      string separator=",";
      //--- Replace erroneous separators with correct ones
      if(StringFind(defined_used_symbols,";")>WRONG_VALUE)  StringReplace(defined_used_symbols,";",separator);   
      if(StringFind(defined_used_symbols,":")>WRONG_VALUE)  StringReplace(defined_used_symbols,":",separator); 
      if(StringFind(defined_used_symbols,"|")>WRONG_VALUE)  StringReplace(defined_used_symbols,"|",separator);   
      if(StringFind(defined_used_symbols,"/")>WRONG_VALUE)  StringReplace(defined_used_symbols,"/",separator); 
      if(StringFind(defined_used_symbols,"\\")>WRONG_VALUE) StringReplace(defined_used_symbols,"\\",separator);  
      if(StringFind(defined_used_symbols,"'")>WRONG_VALUE)  StringReplace(defined_used_symbols,"'",separator); 
      if(StringFind(defined_used_symbols,"-")>WRONG_VALUE)  StringReplace(defined_used_symbols,"-",separator);   
      if(StringFind(defined_used_symbols,"`")>WRONG_VALUE)  StringReplace(defined_used_symbols,"`",separator);
      //--- Delete as long as there are spaces
      while(StringFind(defined_used_symbols," ")>WRONG_VALUE && !IsStopped()) 
         StringReplace(defined_used_symbols," ","");
      //--- As soon as there are double separators (after removing spaces between them), replace them with a separator
      while(StringFind(defined_used_symbols,separator+separator)>WRONG_VALUE && !IsStopped())
         StringReplace(defined_used_symbols,separator+separator,separator);
      //--- If a single separator remains before the first symbol in the string, replace it with a space
      if(StringFind(defined_used_symbols,separator)==0) 
         StringSetCharacter(defined_used_symbols,0,32);
      //--- If a single separator remains after the last symbol in the string, replace it with a space
      if(StringFind(defined_used_symbols,separator)==StringLen(defined_used_symbols)-1)
         StringSetCharacter(defined_used_symbols,StringLen(defined_used_symbols)-1,32);
      //--- Remove all redundant things to the left and right
      #ifdef __MQL5__
         StringTrimLeft(defined_used_symbols);
         StringTrimRight(defined_used_symbols);
      //---  __MQL4__
      #else 
         defined_used_symbols=StringTrimLeft(defined_used_symbols);
         defined_used_symbols=StringTrimRight(defined_used_symbols);
      #endif 
      //--- Prepare the array 
      ArrayResize(used_symbols_array,0);
      ResetLastError();
      //--- divide the string by separators (comma) and add all found substrings to the array
      int n=StringSplit(defined_used_symbols,StringGetCharacter(separator,0),used_symbols_array);
      //--- if nothing is found, display the appropriate message (working with the current symbol is selected automatically)
      if(n<1)
        {
         string err=
           (n==0  ?  
            DFUN_ERR_LINE+TextByLanguage("Ошибка. Строка предопределённых символов пустая, будет использоваться ","Error. String of predefined symbols empty, symbol will be used: ")+Symbol() :
            DFUN_ERR_LINE+TextByLanguage("Не удалось подготовить массив используемых символов. Ошибка ","Failed to create array of used characters. Error ")+(string)GetLastError()
           );
         Print(err);
         return false;
        }
     }
   //--- If working with the Market Watch window or the full list
   else
     {
      //--- Add the (mode_used_symbols) working mode to the only array cell
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=EnumToString(mode_used_symbols);
     }
   return true;
  }
//+------------------------------------------------------------------+

该方法接收操控品种集合的模式,该模式可以在程序配置中设定,也可以硬性定义(如果不需要选择模式),用逗号分隔的品种列表(您打算处理的,或空字符串),和添加品种列表的数组或操控模式(用于操控市场观察和服务器上完整品种列表),将其发送到 CEngine 类,以便设置操控品种的函数库模式。
函数的所有动作,以及数组作用均直接在清单中有所详述了。 单独研究它们没有意义。

品种集合类的开发既已完毕。 一切准备就绪,可以进行测试。

品种集合测试

为了测试集合,我们将利用来自上一篇文章 中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\ 下,命名为 Part15\TestDoEasyPart15_1.mq5

在输入中,加入选择操控品种集合模式和一个存储自定义品种列表的字符串变量,如果令您可在设置中选择操控模式:

//--- input variables
input ulong             InpMagic             =  123;  // Magic number
input double            InpLots              =  0.1;  // Lots
input uint              InpStopLoss          =  50;   // StopLoss in points
input uint              InpTakeProfit        =  50;   // TakeProfit in points
input uint              InpDistance          =  50;   // Pending orders distance (points)
input uint              InpDistanceSL        =  50;   // StopLimit orders distance (points)
input uint              InpSlippage          =  0;    // Slippage in points
input double            InpWithdrawal        =  10;   // Withdrawal funds (in tester)
input uint              InpButtShiftX        =  40;   // Buttons X shift 
input 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)
input ENUM_SYMBOLS_MODE InpModeUsedSymbols   =  SYMBOLS_MODE_CURRENT;   // Mode of used symbols list
input string            InpUsedSymbols       =  "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY";  // List of used symbols (comma - separator)

//--- global variables

在全局变量列表中,添加保存自定义品种列表的变量保存传递给函数库的品种列表的字符串数组

//--- global variables
CEngine        engine;
#ifdef __MQL5__
CTrade         trade;
#endif 
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ulong          magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           slippage;
bool           trailing_on;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
string         used_symbols;
string         array_used_symbols[];
//+------------------------------------------------------------------+

在 EA 的 OnInit() 处理程序中,将自定义列表分配给用于存储它的变量填写已用过品种数组,然后将其发送到函数库

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Calling the function displays the list of enumeration constants in the journal 
//--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity
   //EnumNumbersTest();

//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
   
//--- Fill in the array of used symbols
   used_symbols=InpUsedSymbols;
   CreateUsedSymbolsArray(InpModeUsedSymbols,used_symbols,array_used_symbols);

//--- Set the type of the used symbol list in the symbol collection
   engine.SetUsedSymbols(array_used_symbols);

//--- Check and remove remaining EA graphical objects

上面已研究过的 SetUsedSymbols() 函数创建要发送到品种集合类的品种数组。 根据所选模式,数组含有当前品种或自定义品种列表,或含有操控“市场观察”窗口,或服务器上完整品种列表模式的字符串描述。

在 OnInit() 处理程序的末尾,添加代码以便快速查验由品种集合类创建的品种列表:

//--- Set CTrade trading class parameters
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Fast check of the symbol object collection
   CArrayObj *list=engine.GetListAllUsedSymbols();
   CSymbol *symbol=NULL;
   if(list!=NULL)
     {
      int total=list.Total();
      for(int i=0;i<total;i++)
        {
         symbol=list.At(i);
         if(symbol==NULL)
            continue;
         symbol.Refresh();
         symbol.RefreshRates();
         symbol.PrintShort();
         if(InpModeUsedSymbols<SYMBOLS_MODE_MARKET_WATCH)
            symbol.Print();
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

至此,我们得到所有集合内的完整品种列表按所获列表的循环中,从中获取下一个品种更新所有数据,并在日志中打印出该品种的说明。 首先,显示简要。 然后,如果未选择“操控市场观察窗口”或“操控服务器上的完整品种列表”模式则在日志中显示品种属性的完整描述。

在终端窗口中启动 EA,然后在配置中选择“操控市场观察窗口中品种”模式。 结果就是,由品种集合类创建的所有集合列表内品种的简述均显示在日志中:

2019.06.27 10:01:52.756 Stock ALNU
2019.06.27 10:01:52.756 Stock SU25075RMFS1
2019.06.27 10:01:52.756 Bond SU46022RMFS8
2019.06.27 10:01:52.756 Bond SU26214RMFS5
2019.06.27 10:01:52.756 Stock AESL
2019.06.27 10:01:52.756 Stock 123456.bin
2019.06.27 10:01:52.756 Stock ARMD
2019.06.27 10:01:52.757 Bond SU46018RMFS6
2019.06.27 10:01:52.757 Stock GAZP
2019.06.27 10:01:52.757 Metal XAUUSD
2019.06.27 10:01:52.757 Stock EURRUB_TOD
2019.06.27 10:01:52.757 Stock GBPRUB_TOM
2019.06.27 10:01:52.757 Futures Si-9.19
2019.06.27 10:01:52.757 Futures RTS-3.20
2019.06.27 10:01:52.758 Minor Forex symbol USDNOK
2019.06.27 10:01:52.758 Major Forex symbol USDJPY
2019.06.27 10:01:52.758 Major Forex symbol EURUSD
2019.06.27 10:01:52.758 Minor Forex symbol USDCZK
2019.06.27 10:01:52.758 Major Forex symbol USDCAD
2019.06.27 10:01:52.758 Minor Forex symbol USDZAR
2019.06.27 10:01:52.758 Minor Forex symbol USDSEK
2019.06.27 10:01:52.758 Major Forex symbol AUDUSD
2019.06.27 10:01:52.758 Minor Forex symbol USDDKK
2019.06.27 10:01:52.758 Major Forex symbol NZDUSD
2019.06.27 10:01:52.759 Minor Forex symbol USDPLN
2019.06.27 10:01:52.759 Major Forex symbol GBPUSD
2019.06.27 10:01:52.759 Forex symbol USDRUR
2019.06.27 10:01:52.759 Exotic Forex symbol USDMXN
2019.06.27 10:01:52.759 Forex symbol USDHUF
2019.06.27 10:01:52.759 Minor Forex symbol USDTRY
2019.06.27 10:01:52.759 Minor Forex symbol USDHKD
2019.06.27 10:01:52.760 Major Forex symbol USDCHF
2019.06.27 10:01:52.760 Minor Forex symbol USDSGD

现在,我们检查一下品种集合中按指定值搜索。
重命名 EA 并将其保存在 \MQL5\Experts\TestDoEasy\ 之下,名称为 Part15\TestDoEasyPart15_2.mq5

修改 OnInit() 处理程序中集合内品种列表的快速检查代码。 将品种列表移至日志,仅保留更新集合内品种数据,并添加用于接收集合内品种多头和空头持仓的最大/最小持仓利率,和最大/最小点差。 将获得的数据输入日志:

//--- Set CTrade trading class parameters
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Fast check of the symbol object collection
   CArrayObj *list=engine.GetListAllUsedSymbols();
   CSymbol *symbol=NULL;
   if(list!=NULL)
     {
      int total=list.Total();
      for(int i=0;i<total;i++)
        {
         symbol=list.At(i);
         if(symbol==NULL)
            continue;
         symbol.Refresh();     
         symbol.RefreshRates();
        }
     }
//--- Get the minimum and maximum values
   //--- get the current account properties (we need the number of decimal places for the account currency)
   CAccount *account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      int index_min=0, index_max=0, dgc=(int)account.CurrencyDigits();
      //--- If working with the Market Watch window, leave only visible symbols in the list
      if(InpModeUsedSymbols==SYMBOLS_MODE_MARKET_WATCH)
         list=CSelect::BySymbolProperty(list,SYMBOL_PROP_VISIBLE,true,EQUAL);
      
      //--- min/max swap long
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SWAP_LONG);  // symbol index in the collection list with the minimum swap long
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SWAP_LONG);  // symbol index in the collection list with the maximum swap long
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Minimum swap long for a symbol ",symbol.Name()," = ",NormalizeDouble(symbol.SwapLong(),dgc));
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Maximum swap long for a symbol ",symbol.Name()," = ",NormalizeDouble(symbol.SwapLong(),dgc));
        }
      
      //--- min/max swap short
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SWAP_SHORT); // symbol index in the collection list with the minimum swap short
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SWAP_SHORT); // symbol index in the collection list with the maximum swap short
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Minimum swap short for a symbol ",symbol.Name()," = ",NormalizeDouble(symbol.SwapShort(),dgc));
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Maximum swap short for a symbol ",symbol.Name()," = ",NormalizeDouble(symbol.SwapShort(),dgc));
        }
      
      //--- min/max spread
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SPREAD);     // symbol index in the collection list with the minimum spread
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SPREAD);     // symbol index in the collection list with the maximum spread
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Minimum symbol spread ",symbol.Name()," = ",symbol.Spread());
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Maximum symbol spread ",symbol.Name()," = ",symbol.Spread());
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

在终端图表上编译并启动 EA。 在配置中选择操控服务器上的完整品种列表。 当创建服务器上所有品种集合列表之后(需要花费一些时间),有关集合内所有品种的多头与空头的最大/最小持仓利率,以及最大/最小点差的数据将显示在日志当中:

2019.06.27 10:36:28.885 Minimum long position swap for USDZAR = -192.9
2019.06.27 10:36:28.885 Maximum long position swap for USDMXN = 432.7
2019.06.27 10:36:28.886 Minimum short position swap for XAUUSD = -17.8
2019.06.27 10:36:28.886 Maximum short position swap for USDMXN = 200.0
2019.06.27 10:36:28.886 Minimum spread for SU52001RMFS3 = 0
2019.06.27 10:36:28.886 Maximum spread for GBPRUB_TOM = 3975


下一步是什么?

在下一篇文章中,我们将开发品种集合事件类。

文后附有当前版本含糊库的所有文件,以及测试 EA 文件,供您测试和下载。
请在评论中留下您的问题、意见和建议。

返回目录

系列中的前几篇文章:

第一部分 概念,数据管理
第二部分 历史订单和成交集合
第三部分 在场订单和持仓集合,安排搜索
第四部分 交易事件, 概念
第五部分 交易事件类和集合。 将事件发送至程序
第六部分 净持帐户事件
第七部分 StopLimit 挂单激活事件,为订单和持仓修改事件准备功能
第八部分 订单和持仓修改事件
第九部分 与 MQL4 的兼容性 - 准备数据
第十部分 与 MQL4 的兼容性 - 开仓和激活挂单事件
第十一部分 与 MQL4 的兼容性 - 平仓事件
第十二部分 帐户对象类和帐户对象集合
第十三部分 账户对象事件
第十四部分 品种对象