Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XV): coleção de objetos-símbolo

12 novembro 2019, 13:46
Artyom Trishkin
0
3 468

Sumário


Conceito de coleção de símbolos

Na terceira parte da descrição da biblioteca já definimos o conceito de construção de classes-coleções de objetos, portanto não vamos nos afastar da estrutura de armazenamento de dados adotada. Assim, para a coleção de símbolo, precisamos criar a lista (na qual são armazenados os objetos descendentes da classe 'símbolo') criada no último artigo. Os descendentes de símbolos abstratos vão esclarecer os dados do símbolo e definir a disponibilidade das propriedades básicas do objeto-símbolo no programa. Além disso, esses objetos-símbolos vão ser distinguidos por sua afiliação a grupos (status do símbolo):

  • Símbolo Forex — todos os símbolos Forex que não se enquadram nas seguintes categorias de símbolos forex:
  • Símbolo Forex maior — categoria personalizada dos símbolos Forex mais usados
  • Símbolo Forex menor — categoria personalizada dos símbolos Forex menos usados
  • Símbolo Forex exótico — categoria personalizada dos símbolos Forex raramente usados
  • Símbolo Forex rublo — categoria personalizada dos símbolos Forex contendo rublo
  • Metal — categoria personalizada de símbolos-metal
  • Índice — categoria personalizada de símbolos-índices
  • Indicativo — categoria personalizada de símbolos-indicativos
  • Símbolo de criptomoeda — categoria definida pelo usuário de símbolos-criptomoeda
  • Símbolo de commodities — categoria definida pelo usuário de símbolos-commodities
  • Símbolo bolsista — todos os símbolos de bolsistas que não se enquadram nas seguintes categorias:
  • Futuros
  • Contrato por diferença (CFD)
  • Ações
  • Obrigações
  • Opções
  • Ativo não negociável
  • Símbolo personalizado
  • Categoria geral — símbolos que não se enquadram nas categorias acima

Para determinar a qual o grupo pertence o símbolo (seu status), criaremos conjuntos de dados personalizados — arrays contendo nomes de símbolos, nos quais procuraremos a categoria do símbolo ao qual ele deve pertencer. Se o símbolo não for encontrado na categoria de personalizados (seu nome não está disponível em todos os arrays contendo nomes de símbolos especificados pelo usuário), o símbolo será determinado por sua propriedade "método de cálculo de garantia" ( ENUM_SYMBOL_CALC_MODE), nela se pode determinar se um símbolo pertence a alguma das categorias listadas acima. Quer dizer, realizamos uma pesquisa em duas verificações: nas categorias, definidas pelo usuário, e se não conseguirmos definir a categoria, temos que defini-la usando o método de cálculo de garantia. Previamente, foi planejado usar outro método — definir pelo nome da pasta na qual está o símbolo na árvore de diretórios de símbolos no servidor. No entanto, esse método não é confiável, uma vez que os nomes de pastas podem estar em servidores diferentes para o mesmo símbolo, é por essa razão que preferi não usá-lo.
A categoria personalizada tem precedência — porque se o usuário desejar que o símbolo, por exemplo, USDUSC esteja na categoria "maior", é a sua escolha — e o símbolo estará presente, independentemente de ser indicativo.

Já decidimos sobre as categorias — para elas, criaremos as classes herdadas do símbolo abstrato necessárias, símbolo esse que criamos no último artigo.
Para armazenar todos os objetos dos símbolos, usamos a classe CListObj, herdada da classe de biblioteca padrão CArrayObj, que abordamos no quinto artigo ao discutir a reorganização das classes da biblioteca. Nos programas baseados nesta biblioteca, é possível escolher com qual lista de símbolo trabalhar:

  1. Apenas uma é o símbolo atual ao qual o programa está anexado
  2. Conjunto de símbolos predefinido especificado no programa
  3. Trabalho com a lista de símbolos que estão na janela 'Observação do Mercado'
  4. Trabalho com a lista completa de símbolos disponíveis no servidor

Assim, cobriremos a maioria das tarefas de programação necessárias para acessar os instrumentos-símbolos de trabalho.
Aqui é necessário sublinhar dois pontos:
Primeiro, este é um trabalho com uma lista de símbolos da Observação do Mercado — neste modo, será preciso usar a pesquisa de eventos na janela 'Observação do mercado' — para responder às alterações atempadamente (adicionar/remover um símbolo da lista e classificá-lo com o mouse).
Em segundo lugar, ao trabalhar com a lista completa de símbolos disponíveis no servidor, é necessário considerar o processamento normal do grande número possível de símbolos disponíveis — afinal, na primeira inicialização, será visível a lista completa de símbolos no servidor e serão criados objetos-símbolos para os quais será preciso obter todas as suas propriedades. Este processo não é rápido — quando uso um laptop comum, leva cerca de dois minutos criar toda a coleção de símbolos no servidor.
Inicialmente, acho que, ao escolher este método de trabalho, é necessário pelo menos alertar o usuário sobre a possivelmente longa coleta de informações iniciais.
Nos próximos artigos, abordaremos isso, bem como o acompanhamento de eventos de símbolos e de eventos da janela "Observação do mercado", mas, por enquanto, criemos uma lista-coleção.

Para começar, criemos outro arquivo incluído, nele armazenaremos todos os dados necessários para a biblioteca — array de grupos de símbolos personalizados, arquivos, imagens e outros conjuntos de dados necessários posteriormente para a biblioteca.
No diretório de localização da biblioteca \MQL5\Include\DoEasy\, criamos um novo arquivo de inclusão chamado Datas.mqh e imediatamente inserimos nele alguns arrays, que eu já havia preparado com antecedência, depois de ter iterado várias contas e de ter coletado dados - da estrutura em árvore da janela 'Observação do Mercado' - sobre os grupos de símbolos definidos em diferentes servidores:

//+------------------------------------------------------------------+
//|                                                        Datas.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
//+------------------------------------------------------------------+
//| Conjuntos de dados                                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Símbolos Forex Majors                                            |
//+------------------------------------------------------------------+
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"
  };
//+------------------------------------------------------------------+
//| Símbolos Forex Menors                                            |
//+------------------------------------------------------------------+
string DataSymbolsFXMinors[]=
  {
   "EURCZK","EURDKK","EURHKD","EURNOK","EURPLN","EURSEK","EURSGD","EURTRY","EURZAR","GBPSEK","GBPSGD"
      ,"NZDSGD","USDCZK","USDDKK","USDHKD","USDNOK","USDPLN","USDSEK","USDSGD","USDTRY","USDZAR"
  };
//+------------------------------------------------------------------+
//| Símbolos Forex Exotics                                           |
//+------------------------------------------------------------------+
string DataSymbolsFXExotics[]=
  {
   "EURMXN","USDCNH","USDMXN","EURTRY","USDTRY"
  };
//+------------------------------------------------------------------+
//| Símbolos Forex Rublo                                             |
//+------------------------------------------------------------------+
string DataSymbolsFXRub[]=
  {
   "EURRUB","USDRUB"
  };
//+------------------------------------------------------------------+
//| Símbolos Forex Indicativos                                       |
//+------------------------------------------------------------------+
string DataSymbolsFXIndicatives[]=
  {
   "EUREUC","USDEUC","USDUSC"
  };
//+------------------------------------------------------------------+
//| Símbolos-metais                                                  |
//+------------------------------------------------------------------+
string DataSymbolsMetalls[]=
  {
   "XAGUSD","XAUUSD"
  };
//+------------------------------------------------------------------+
//| Símbolos commodities                                             |
//+------------------------------------------------------------------+
string DataSymbolsCommodities[]=
  {
   "BRN","WTI","NG"
  };
//+------------------------------------------------------------------+
//| Índices                                                          |
//+------------------------------------------------------------------+
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"
  };
//+------------------------------------------------------------------+
//| Símbolos-criptomoedas                                            |
//+------------------------------------------------------------------+
string DataSymbolsCrypto[]=
  {
   "BCHUSD","BTCEUR","BTCUSD","DSHUSD","EOSUSD","ETHEUR","ETHUSD","LTCUSD","XRPUSD"
  };
//+------------------------------------------------------------------+
//| Opções                                                           |
//+------------------------------------------------------------------+
string DataSymbolsOptions[]=
  {
   "BO Volatility 10 Index","BO Volatility 25 Index","BO Volatility 50 Index","BO Volatility 75 Index","BO Volatility 100 Index"
  };
//+------------------------------------------------------------------+

Como se pode ver na lista, esta é apenas uma enumeração dos nomes dos símbolos que adicionamos aos arrays necessários que definem o grupo de símbolos que estão em cada um dos arrays. Se desejado e necessário, pode-se processar a composição destes arrays contendo nomes de símbolos — enviar algo para outro array, adicionar/remover algo etc.

Um teste mais "rígido" do comportamento do objeto-símbolo abstrato deixou claro que o uso das constantes SYMBOL_MARGIN_LONG, SYMBOL_MARGIN_SHORT, SYMBOL_MARGIN_STOP, SYMBOL_MARGIN_LIMIT e SYMBOL_MARGIN_STOPLIMIT não deu na obtenção da propriedade desejada. Por isso, foi necessário substituir a maneira de receber essas propriedades, e começar a fazê-lo através da função SymbolInfoMarginRate().
Como o tipo de ordem é enviado para essa função, eu tive que criar para cada um dos tipos de ordens, sua própria constante nas propriedades reais do objeto-símbolo no arquivo Defines.mqh:

//+------------------------------------------------------------------+
//| Propriedades reais do símbolo                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_PROP_DOUBLE
  {
   SYMBOL_PROP_BID = SYMBOL_PROP_INTEGER_TOTAL,             // Bid - melhor oferta de venda
   SYMBOL_PROP_BIDHIGH,                                     // Bid máximo do dia
   SYMBOL_PROP_BIDLOW,                                      // Bid máximo do dia
   SYMBOL_PROP_ASK,                                         // Ask - melhor oferta de compra
   SYMBOL_PROP_ASKHIGH,                                     // Ask máximo do dia
   SYMBOL_PROP_ASKLOW,                                      // Ask mínimo do dia
   SYMBOL_PROP_LAST,                                        // Preço da última transação
   SYMBOL_PROP_LASTHIGH,                                    // Last máximo do dia
   SYMBOL_PROP_LASTLOW,                                     // Last máximo do dia
   SYMBOL_PROP_VOLUME_REAL,                                 // Volume do dia
   SYMBOL_PROP_VOLUMEHIGH_REAL,                             // Volume máximo do dia
   SYMBOL_PROP_VOLUMELOW_REAL,                              // Volume mínimo do dia
   SYMBOL_PROP_OPTION_STRIKE,                               // Preço de exercício da opção
   SYMBOL_PROP_POINT,                                       // Valor de um ponto
   SYMBOL_PROP_TRADE_TICK_VALUE,                            // Valor SYMBOL_TRADE_TICK_VALUE_PROFIT
   SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT,                     // Valor calculado do tick para uma posição lucrativa
   SYMBOL_PROP_TRADE_TICK_VALUE_LOSS,                       // Valor calculado do tick para uma posição desfavorável
   SYMBOL_PROP_TRADE_TICK_SIZE,                             // Alteração mínima de preço
   SYMBOL_PROP_TRADE_CONTRACT_SIZE,                         // Tamanho do contrato de transação
   SYMBOL_PROP_TRADE_ACCRUED_INTEREST,                      // Juros corridos
   SYMBOL_PROP_TRADE_FACE_VALUE,                            //  Valor nominal é o valor inicial do título estabelecido pelo emissor
   SYMBOL_PROP_TRADE_LIQUIDITY_RATE,                        // O índice de liquidez é a proporção do valor do ativo que pode ser usada como garantia
   SYMBOL_PROP_VOLUME_MIN,                                  // Volume mínimo para realização da transação
   SYMBOL_PROP_VOLUME_MAX,                                  // Volume máximo para realização da transação
   SYMBOL_PROP_VOLUME_STEP,                                 // Incremento mínimo de alteração do volume para o realização da transação
   SYMBOL_PROP_VOLUME_LIMIT,                                // Volume total máximo permitido para posições abertas e ordens pendentes numa direção (compra ou venda)
   SYMBOL_PROP_SWAP_LONG,                                   // Valor de swap em long
   SYMBOL_PROP_SWAP_SHORT,                                  // Valor de swap em short
   SYMBOL_PROP_MARGIN_INITIAL,                              // Margem inicial
   SYMBOL_PROP_MARGIN_MAINTENANCE,                          // Coeficiente de cobrança da margem de manutenção do instrumento
   SYMBOL_PROP_MARGIN_LONG_INITIAL,                         // Margem inicial para posição long
   SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL,                     // Margem inicial para ordens BuyStop
   SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL,                    // Margem inicial para ordens BuyLimit
   SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL,                // Margem inicial para ordens BuyStopLimit
   SYMBOL_PROP_MARGIN_LONG_MAINTENANCE,                     // Coeficiente de cobrança da margem de manutenção para posições longas
   SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE,                 // Coeficiente de cobrança da margem de manutenção para ordens BuyStop
   SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE,                // Coeficiente de cobrança da margem de manutenção para ordens BuyLimit
   SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE,            // Coeficiente de cobrança da margem de manutenção para ordens BuyStopLimit
   SYMBOL_PROP_MARGIN_SHORT_INITIAL,                        // Margem inicial para posições curtas
   SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL,                    // Margem inicial para ordens SellStop
   SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL,                   // Margem inicial para ordens SellLimit
   SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL,               // Margem inicial para ordens SellStopLimit
   SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE,                    // Coeficiente de cobrança da margem de manutenção para posições curtas
   SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE,                // Coeficiente de cobrança da margem de manutenção para ordens SellStop
   SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE,               // Coeficiente de cobrança da margem de manutenção para ordens SellLimit
   SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE,           // Coeficiente de cobrança da margem de manutenção para ordens SellStopLimit
   SYMBOL_PROP_SESSION_VOLUME,                              // Volume total de transações na sessão atual
   SYMBOL_PROP_SESSION_TURNOVER,                            // Faturação total na sessão atual
   SYMBOL_PROP_SESSION_INTEREST,                            // Volume total de posições abertas
   SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,                   // Volume total de ordens de compra atual
   SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,                  // Volume total de ordens de venda atual
   SYMBOL_PROP_SESSION_OPEN,                                // Preço de abertura da sessão
   SYMBOL_PROP_SESSION_CLOSE,                               // Preço de fechamento da sessão
   SYMBOL_PROP_SESSION_AW,                                  // Preço médio ponderado da sessão
   SYMBOL_PROP_SESSION_PRICE_SETTLEMENT,                    // Preço de liquidação da sessão atual
   SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN,                     // Valor mínimo permitido da sessão 
   SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX,                     // Valor máximo permitido da sessão
   SYMBOL_PROP_MARGIN_HEDGED                                // Tamanho do contrato ou margem para um lote de posições sobrepostas (posições com várias direções segundo um mesmo símbolo).
  };
#define SYMBOL_PROP_DOUBLE_TOTAL     (58)                   // Número total de propriedades reais
#define SYMBOL_PROP_DOUBLE_SKIP      (0)                    // Número de propriedades reais do símbolo que não são usadas na classificação
//+------------------------------------------------------------------+

Consequentemente, a quantidade total de propriedades reais aumentou para 58 (em vez das 47 anteriores)

Da mesma maneira, tive que adicionar as constantes correspondentes à enumeração de possíveis critérios para classificar símbolos:

//+------------------------------------------------------------------+
//| Possíveis critérios para classificar símbolos                    |
//+------------------------------------------------------------------+
#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
  {
//--- Classificar por propriedades de número inteiro
   SORT_BY_SYMBOL_STATUS = 0,                               // Classificar por status do símbolo
   SORT_BY_SYMBOL_CUSTOM,                                   // Classificar por símbolo personalizado
   SORT_BY_SYMBOL_CHART_MODE,                               // Classificar por tipo de preço para construir barras – Bid ou Last (a partir da enumeração ENUM_SYMBOL_CHART_MODE)
   SORT_BY_SYMBOL_EXIST,                                    // Classifica pelo fato de existir um símbolo com o mesmo nome
   SORT_BY_SYMBOL_SELECT,                                   // Classificar pelo fato de o símbolo estar selecionado no Market Watch
   SORT_BY_SYMBOL_VISIBLE,                                  // Classificar pelo fato de o símbolo selecionado ser exibido no Market Watch
   SORT_BY_SYMBOL_SESSION_DEALS,                            // Classificar pelo número de transações na sessão atual
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS,                       // Classificar pelo número total de ordens de compra atualmente
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS,                      // Classificar pelo número total de ordens de venda atualmente
   SORT_BY_SYMBOL_VOLUME,                                   // Classificar pelo Volume - volume na última transação
   SORT_BY_SYMBOL_VOLUMEHIGH,                               // Classificar pelo Volume máximo do dia
   SORT_BY_SYMBOL_VOLUMELOW,                                // Classificar pelo Volume mínimo do dia
   SORT_BY_SYMBOL_TIME,                                     // Classificar pela hora da última cotação
   SORT_BY_SYMBOL_DIGITS,                                   // Classificar pelo número de casas decimais
   SORT_BY_SYMBOL_DIGITS_LOT,                               // Classificar pelo número de casas decimais no lote
   SORT_BY_SYMBOL_SPREAD,                                   // Classificar pelo tamanho do spread em pontos
   SORT_BY_SYMBOL_SPREAD_FLOAT,                             // Classificar por spread flutuante
   SORT_BY_SYMBOL_TICKS_BOOKDEPTH,                          // Classificar pelo número máximo de ordens exibidas no livro de ofertas
   SORT_BY_SYMBOL_TRADE_CALC_MODE,                          // Classificar pelo método de cálculo do valor do contrato (a partir da enumeração ENUM_SYMBOL_CALC_MODE)
   SORT_BY_SYMBOL_TRADE_MODE,                               // Classifica pelo tipo de execução da ordem (a partir da enumeração ENUM_SYMBOL_TRADE_MODE)
   SORT_BY_SYMBOL_START_TIME,                               // Classificar pela data de início da negociação do instrumento (geralmente usado para futuros)
   SORT_BY_SYMBOL_EXPIRATION_TIME,                          // Classificar pela data de término da negociação do instrumento (geralmente usado para futuros)
   SORT_BY_SYMBOL_TRADE_STOPS_LEVEL,                        // Classificar pelo recuo mínimo em pontos em relação ao preço de fechamento atual para posicionar ordens Stop
   SORT_BY_SYMBOL_TRADE_FREEZE_LEVEL,                       // Classificar pela distância de congelamento de operações (em pontos)
   SORT_BY_SYMBOL_TRADE_EXEMODE,                            // Classificar por modo de transação (a partir da enumeração ENUM_SYMBOL_TRADE_EXECUTION)
   SORT_BY_SYMBOL_SWAP_MODE,                                // Classificar pelo modelo de cálculo de swap (a partir da enumeração ENUM_SYMBOL_SWAP_MODE)
   SORT_BY_SYMBOL_SWAP_ROLLOVER3DAYS,                       // Classificar pelo dia da semana para calcular o swap triplo (a partir da enumeração ENUM_DAY_OF_WEEK)
   SORT_BY_SYMBOL_MARGIN_HEDGED_USE_LEG,                    // Classificar pelo modo de cálculo da margem de cobertura pelo lado maior (Buy ou Sell)
   SORT_BY_SYMBOL_EXPIRATION_MODE,                          // Classificar por sinalizadores dos modos de expiração de ordem permitidos
   SORT_BY_SYMBOL_FILLING_MODE,                             // Classificar por sinalizadores dos modos de preenchimento de ordem permitidos
   SORT_BY_SYMBOL_ORDER_MODE,                               //  Classificar por sinalizadores de tipos de ordem permitidos
   SORT_BY_SYMBOL_ORDER_GTC_MODE,                           // Classificar por expiração das ordens StopLoss e TakeProfit
   SORT_BY_SYMBOL_OPTION_MODE,                              // Classificar por tipo de opção (a partir da enumeração ENUM_SYMBOL_OPTION_MODE)
   SORT_BY_SYMBOL_OPTION_RIGHT,                             // Classificar por direito de opção (Call/Put) (a partir da enumeração ENUM_SYMBOL_OPTION_RIGHT)
//--- Classificar por propriedades reais
   SORT_BY_SYMBOL_BID = FIRST_SYM_DBL_PROP,                 // Classificar por Bid
   SORT_BY_SYMBOL_BIDHIGH,                                  // Classificar por Bid máximo do dia
   SORT_BY_SYMBOL_BIDLOW,                                   // Classificar por Bid mínimo do dia
   SORT_BY_SYMBOL_ASK,                                      // Classificar por Ask
   SORT_BY_SYMBOL_ASKHIGH,                                  // Classificar por Ask máximo do dia
   SORT_BY_SYMBOL_ASKLOW,                                   // Classificar por Ask mínimo do dia
   SORT_BY_SYMBOL_LAST,                                     // Classificar pelo preço da última transação concluída
   SORT_BY_SYMBOL_LASTHIGH,                                 // Classificar pelo Last máximo do dia
   SORT_BY_SYMBOL_LASTLOW,                                  // Classificar pelo Last mínimo do dia
   SORT_BY_SYMBOL_VOLUME_REAL,                              // Classificar pelo Volume do dia
   SORT_BY_SYMBOL_VOLUMEHIGH_REAL,                          // Classificar pelo Volume máximo do dia
   SORT_BY_SYMBOL_VOLUMELOW_REAL,                           // Classificar pelo Volume mínimo do dia
   SORT_BY_SYMBOL_OPTION_STRIKE,                            // Classificar pelo preço de exercício da opção
   SORT_BY_SYMBOL_POINT,                                    // Classificar pelo valor de um ponto
   SORT_BY_SYMBOL_TRADE_TICK_VALUE,                         // Classificar pelo valor SYMBOL_TRADE_TICK_VALUE_PROFIT
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT,                  // Classificar pelo valor calculado do tick para uma posição lucrativa
   SORT_BY_SYMBOL_TRADE_TICK_VALUE_LOSS,                    // Classificar pelo valor calculado do tick para uma posição desfavorável
   SORT_BY_SYMBOL_TRADE_TICK_SIZE,                          // Classificar pela alteração de preço mínima
   SORT_BY_SYMBOL_TRADE_CONTRACT_SIZE,                      // Classificar por tamanho do contrato comercial
   SORT_BY_SYMBOL_TRADE_ACCRUED_INTEREST,                   // Classificar por juros corridos
   SORT_BY_SYMBOL_TRADE_FACE_VALUE,                         // Classificar por valor nominal
   SORT_BY_SYMBOL_TRADE_LIQUIDITY_RATE,                     // Classificar por taxa de liquidez
   SORT_BY_SYMBOL_VOLUME_MIN,                               // Classificar pelo volume mínimo para realizar a transação
   SORT_BY_SYMBOL_VOLUME_MAX,                               // Classificar pelo volume máximo para realizar a transação
   SORT_BY_SYMBOL_VOLUME_STEP,                              // Classificar pelo passo mínimo de alteração do volumer para realizar a transação
   SORT_BY_SYMBOL_VOLUME_LIMIT,                             // Classificar pelo volume total máximo permitido de uma posição aberta e ordens pendentes numa direção
   SORT_BY_SYMBOL_SWAP_LONG,                                // Classificar pelo valor do swap em long
   SORT_BY_SYMBOL_SWAP_SHORT,                               // Classificar pelo valor do swap em short
   SORT_BY_SYMBOL_MARGIN_INITIAL,                           // Classificar pela margem inicial
   SORT_BY_SYMBOL_MARGIN_MAINTENANCE,                       // Classificar por margem de manutenção por instrumento
   SORT_BY_SYMBOL_MARGIN_LONG_INITIAL,                      // Classificar por margem inicial para posições longas
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_INITIAL,                  // Classificar por margem inicial para ordens BuyStop
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_INITIAL,                 // Classificar por margem inicial para ordens BuyLimit
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_INITIAL,             // Classificar por margem inicial para ordens BuyStopLimit
   SORT_BY_SYMBOL_MARGIN_LONG_MAINTENANCE,                  // Classificar por margem de manutenção para posições longas
   SORT_BY_SYMBOL_MARGIN_BUY_STOP_MAINTENANCE,              // Classificar por margem de manutenção para BuyStop
   SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_MAINTENANCE,             // Classificar por margem de manutenção para BuyLimit
   SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_MAINTENANCE,         // Classificar por margem de manutenção para BuyStopLimit
   SORT_BY_SYMBOL_MARGIN_SHORT_INITIAL,                     // Classificar por margem inicial para posições curtas
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_INITIAL,                 // Classificar por margem inicial para ordens SellStop
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_INITIAL,                // Classificar por margem inicial para ordens SellLimit
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_INITIAL,            // Classificar por margem inicial para ordens SellStopLimit
   SORT_BY_SYMBOL_MARGIN_SHORT_MAINTENANCE,                 // Classificar por margem de manutenção para posições curtas
   SORT_BY_SYMBOL_MARGIN_SELL_STOP_MAINTENANCE,             // Classificar por coeficiente de cobrança da margem de manutenção para SellStop
   SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_MAINTENANCE,            // Classificar por coeficiente de cobrança da margem de manutenção para SellLimit
   SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_MAINTENANCE,        // Classificar por coeficiente de cobrança da margem de manutenção para SellStopLimit
   SORT_BY_SYMBOL_SESSION_VOLUME,                           // Classificar pelo volume total de transações na sessão atual
   SORT_BY_SYMBOL_SESSION_TURNOVER,                         // Classificar pelo rotatividade total na sessão atual
   SORT_BY_SYMBOL_SESSION_INTEREST,                         // Classificar pelo volume total de posições atual
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME,                // Classificar pelo volume total de ordens no momento
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME,               // Classificar pelo volume total de ordens de venda atualmente
   SORT_BY_SYMBOL_SESSION_OPEN,                             // Classificar pelo preço de abertura da sessão
   SORT_BY_SYMBOL_SESSION_CLOSE,                            // Classificar pelo preço de fechamento da sessão
   SORT_BY_SYMBOL_SESSION_AW,                               // Classificar pelo preço médio ponderado da sessão
   SORT_BY_SYMBOL_SESSION_PRICE_SETTLEMENT,                 // Classificar pelo preço de entrega da sessão atual
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN,                  // Classificar pelo preço mínimo permitido por sessão 
   SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX,                  // Classificar pelo preço máximo permitido por sessão
   SORT_BY_SYMBOL_MARGIN_HEDGED,                            // Classificar pelo tamanho ou margem do contrato para um lote de posições sobrepostas
//--- Classificar por propriedades de string
   SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                // Classificar pelo nome do símbolo
   SORT_BY_SYMBOL_BASIS,                                    // Classificar pelo nome do ativo subjacente para o derivado
   SORT_BY_SYMBOL_CURRENCY_BASE,                            // Classificar pela moeda base do instrumento
   SORT_BY_SYMBOL_CURRENCY_PROFIT,                          // Classificar pela moeda de lucro
   SORT_BY_SYMBOL_CURRENCY_MARGIN,                          // Classificar pela moeda na qual é calculada a garantia
   SORT_BY_SYMBOL_BANK,                                     // Classificar pela fonte da cotação atual
   SORT_BY_SYMBOL_DESCRIPTION,                              // Classificar pela descrição de string do símbolo
   SORT_BY_SYMBOL_FORMULA,                                  // Classificar pela fórmula para plotar o preço do símbolo personalizado
   SORT_BY_SYMBOL_ISIN,                                     // Classificar pelo nome do símbolo no sistema de códigos internacionais de identificação de valores mobiliários - ISIN
   SORT_BY_SYMBOL_PAGE,                                     // Classificar pelo endereço da página da web com informações do símbolo
   SORT_BY_SYMBOL_PATH                                      // Classificar pelo caminho na árvore de símbolos
  };
//+------------------------------------------------------------------+

Bem, como estamos editando o arquivo Deines.mqh, inserimos imediatamente tudo o que precisamos nele, analisando simultaneamente o que estamos adicionando e por que é necessário.

Para uma coleção de símbolos, precisamos de um temporizador, porque precisamos atualizar os dados de todos os símbolos da coleção de acordo com o ele. Além disso tem a particularidade de que precisaremos atualizar os dados de cotação de todos os símbolos e os dados restantes — aqueles que podem ser alterados para serem rastreados na classe de eventos de símbolos (falaremos mais sobre isso no próximo artigo) e também precisamos verificar a lista de símbolos no temporizador na 'Observação do Mercado' — para atempadamente responder às alterações e atualizar a lista de coleções.
Precisamos atualizar os dados de cotação com mais frequência do que os dados de símbolo restantes e sua lista na janela 'Observação do Mercado'. Portanto, precisamos de dois temporizador para uma coleção de símbolos — um temporizador de dados de cotação e um temporizador para as ações restantes com listas de símbolos.

Inserimos as substituições de macro necessárias para dois temporizadores da coleção de símbolos:

//--- Parâmetros do temporizador1 da coleção de símbolos
#define COLLECTION_SYM_PAUSE1          (100)                      // Pausa do temporizador1 da coleção de símbolos em milissegundos (para rastreamento de símbolos na observação do mercado)
#define COLLECTION_SYM_COUNTER_STEP1   (16)                       // Incremento do temporizador1 de símbolos
#define COLLECTION_SYM_COUNTER_ID1     (3)                        // Identificador do contador do temporizador1 de símbolos
//--- Parâmetros do temporizador2 da coleção de símbolos
#define COLLECTION_SYM_PAUSE2          (300)                      // Pausa do temporizador2 de coleção de símbolos em milissegundos (para eventos da lista de símbolos na observação do mercado)
#define COLLECTION_SYM_COUNTER_STEP2   (16)                       // Incremento do contador do temporizador2 de símbolos
#define COLLECTION_SYM_COUNTER_ID2     (4)                        // Identificador do contador do temporizador2 de símbolos

A diferença entre estes dados está apenas na pausa para cada temporizador e seus identificadores — para o primeiro temporizador, a pausa será de 100 milissegundos, para o segundo, 300.

Para cada coleção, temos nosso próprio identificador. A coleção de símbolos não é exceção.
Atribuímos um identificador próprio à sua lista:

//--- Identificadores da lista de coleções
#define COLLECTION_HISTORY_ID          (0x7778+1)                 // Identificador da lista de coleção histórica
#define COLLECTION_MARKET_ID           (0x7778+2)                 // Identificador da lista de coleção de mercado
#define COLLECTION_EVENTS_ID           (0x7778+3)                 // Identificador da lista de coleção de eventos
#define COLLECTION_ACCOUNT_ID          (0x7778+4)                 // Identificador da coleção de contas
#define COLLECTION_SYMBOLS_ID          (0x7778+5)                 // Identificador da lista de coleção de símbolos

Para determinar a cor de fundo com a qual o símbolo é destacado na janela "Observação do mercado" e exibir sua descrição de string, no último artigo foi usada uma construção para comparar cores com a cor clrWhite — se o valor da propriedade for maior que o valor long da cor especificada, será considerado que a cor do plano de fundo não está definida:

      property==SYMBOL_PROP_BACKGROUND_COLOR    ?  TextByLanguage("Cor de fundo do símbolo na Observação do Mercado","Background color of the symbol in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
         #ifdef __MQL5__
         (this.GetProperty(property)>clrWhite  ?  TextByLanguage(": (Ausente)",": (Not set)") : ": "+::ColorToString((color)this.GetProperty(property),true))
         #else TextByLanguage(": Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :

No entanto, de acordo com os desenvolvedores, isso não é verdade, uma vez que o valor long da cor de plano de fundo do símbolo na janela 'Observação do Mercado', ao que parece, pode ser maior que o valor long da cor "branca". Isso significa que, em alguns casos, essa verificação não retornará o resultado correto.
Para identificar corretamente a ausência de uma cor de plano de fundo, é preciso comparar o valor da propriedade com o valor CLR_DEFAULT e CLR_NONE.
Definimos a substituição de macro do valor da cor padrão. (a cor 'ausente' CLR_NONE já existe em MQL5 e MQL4):

//--- Parâmetro de símbolos
#define CLR_DEFAULT                    (0xFF000000)               // Cor por padrão
//+------------------------------------------------------------------+

Como resultado, a seção de substituição de macro para o arquivo Defines.mqh agora fica assim:

//+------------------------------------------------------------------+
//| Substituição de macros                                           |
//+------------------------------------------------------------------+
//--- "Descrição da função com o número da string de erro"
#define DFUN_ERR_LINE                  (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Linha " : ", Line ")+(string)__LINE__+": ")
#define DFUN                           (__FUNCTION__+": ")        // "Descrição da função"
#define COUNTRY_LANG                   ("Russian")                // Idioma
#define END_TIME                       (D'31.12.3000 23:59:59')   // Data final para solicitações de dados do histórico da conta
#define TIMER_FREQUENCY                (16)                       // Frequência mínima do temporizador da biblioteca em milissegundos
//--- Parâmetro do temporizador da coleção de ordens e de transações
#define COLLECTION_ORD_PAUSE           (250)                      // Pausa do temporizador da coleção de ordens e de transações em milissegundos
#define COLLECTION_ORD_COUNTER_STEP    (16)                       // Incremento do contador do temporizador da coleção de ordens e de transações
#define COLLECTION_ORD_COUNTER_ID      (1)                        // Identificador do contador do temporizdor da coleção de ordens e de transações
//--- Parâmetros do temporizador da coleção de contas
#define COLLECTION_ACC_PAUSE           (1000)                     // Pausa do temporizador da coleção de contas em milissegundos
#define COLLECTION_ACC_COUNTER_STEP    (16)                       // Incremento do contador do temporizador de contas
#define COLLECTION_ACC_COUNTER_ID      (2)                        // Identificador do contador do temporizador de contas
//--- Parâmetros do temporizador1 da coleção de símbolos
#define COLLECTION_SYM_PAUSE1          (100)                      // Pausa do temporizador1 da coleção de símbolos em milissegundos (para rastreamento de símbolos na observação do mercado)
#define COLLECTION_SYM_COUNTER_STEP1   (16)                       // Incremento do temporizador1 de símbolos
#define COLLECTION_SYM_COUNTER_ID1     (3)                        // Identificador da conta do temporizador1 de símbolos
//--- Parâmetros do temporizador2 da coleção de símbolos
#define COLLECTION_SYM_PAUSE2          (300)                      // Pausa do temporizador2 da coleção de símbolos em milissegundos (para eventos da lista de símbolos na observação do mercado)
#define COLLECTION_SYM_COUNTER_STEP2   (16)                       // Incremento do contador do temporizador2 de símbolos
#define COLLECTION_SYM_COUNTER_ID2     (4)                        // Identificador da conta do temporizador2 de símbolos
//--- Identificadores da lista de coleções
#define COLLECTION_HISTORY_ID          (0x7778+1)                 // Identificador da lista de coleção histórica
#define COLLECTION_MARKET_ID           (0x7778+2)                 // Identificador da lista de coleção de mercado
#define COLLECTION_EVENTS_ID           (0x7778+3)                 // Identificador da lista de coleção de eventos
#define COLLECTION_ACCOUNT_ID          (0x7778+4)                 // Identificador da coleção de contas
#define COLLECTION_SYMBOLS_ID          (0x7778+5)                 // Identificador da lista de coleção de símbolos
//--- Parâmetros de dados para operações de arquivo
#define DIRECTORY                      ("DoEasy\\")               // Diretório da biblioteca para colocar as pastas dos objetos de classe
//--- Parâmetro de símbolos
#define CLR_DEFAULT                    (0xFF000000)               // Cor por padrão
//+------------------------------------------------------------------+

Acima, falamos sobre os modos para trabalhar com coleções de símbolos: com o símbolo atual, com uma lista de símbolos predefinida no programa, com a janela Observação do Mercado e com a lista completa de símbolos disponíveis no servidor.
Definimos todos esses modos na enumeração:

//+------------------------------------------------------------------+
//| Dados para trabalhar com símbolos                                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Modos de trabalho com símbolos                                   |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                                    // Trabalhar apenas com o símbolo atual
   SYMBOLS_MODE_DEFINES,                                    // Trabalhar com a lista de símbolos definida
   SYMBOLS_MODE_MARKET_WATCH,                               // Trabalhar com símbolo da janela Observação do mercado
   SYMBOLS_MODE_ALL                                         // Trabalhar com a lista completa de símbolos
  };
//+------------------------------------------------------------------+

No último artigo, identificamos categorias de símbolos pelo qual iremos distribuí-los. No início deste artigo, examinamos uma lista um pouco expandida de categorias de símbolos. Vamos usá-la.

Adicionamos uma enumeração de categorias (status) de símbolos:

//+------------------------------------------------------------------+
// Tipo (status) do símbolo abstrato                                 |
//+------------------------------------------------------------------+
enum ENUM_SYMBOL_STATUS
  {
   SYMBOL_STATUS_FX,                                        // Símbolo forex
   SYMBOL_STATUS_FX_MAJOR,                                  // Símbolo forex major
   SYMBOL_STATUS_FX_MINOR,                                  // Símbolo forex minor
   SYMBOL_STATUS_FX_EXOTIC,                                 // Símbolo exótico forex
   SYMBOL_STATUS_FX_RUB,                                    // Símbolo forex/rublo
   SYMBOL_STATUS_METAL,                                     // Metal
   SYMBOL_STATUS_INDEX,                                     // Índice
   SYMBOL_STATUS_INDICATIVE,                                // Indicativo
   SYMBOL_STATUS_CRYPTO,                                    // Símbolo de criptomoeda
   SYMBOL_STATUS_COMMODITY,                                 // Símbolo de commoditie
   SYMBOL_STATUS_EXCHANGE,                                  // Símbolo de bolsa
   SYMBOL_STATUS_FUTURES,                                   // Futuros
   SYMBOL_STATUS_CFD,                                       // Contrato por diferença
   SYMBOL_STATUS_STOCKS,                                    // Título
   SYMBOL_STATUS_BONDS,                                     // Obrigação
   SYMBOL_STATUS_OPTION,                                    // Opção
   SYMBOL_STATUS_COLLATERAL,                                // Ativo não negociável
   SYMBOL_STATUS_CUSTOM,                                    // Símbolo personalizado
   SYMBOL_STATUS_COMMON                                     // Categoria geral
  };
//+------------------------------------------------------------------+

Isso conclui a preparação de dados para a coleção de símbolos e as alterações no arquivo Defines.mqh.

As mudanças também afetaram a classe CSymbol que criamos no último artigo.
Como agora recebemos dados sobre coeficientes de cobrança de margem para diferentes tipos de ordens através da função SymbolInfoMarginRate(), e para retorno dos valores solicitados são usadas as variáveis passadas para a função por referência, então precisamos criar essas variáveis.

Se levarmos em conta que temos oito ordens e, para cada uma delas, podemos obter dois tipos de coeficientes — coeficiente de cobrança de margem inicial e o coeficiente de cobrança de margem de manutenção —, então deve haver 16 variáveis para obter todos esses valores. Portanto, é mais fácil e mais visual criar uma estrutura consistindo em duas estruturas aninhadas: na primeira estrutura serão definidas duas variáveis double para armazenar os coeficientes de cobrança das margens inicial e de manutenção, e na segunda estarão contidas as primeiras estruturas declaradas para armazenar dados sobre os tipos de ordens para os quais precisam ser obtidos os coeficientes.

No arquivo da classe-símbolo Symbol.mqh, na seção privada da classe CSymbol, declaramos essas estruturas e a variável-membro da classe com o tipo da segunda estrutura para acessar a estrutura:

//+------------------------------------------------------------------+
// Classe do símbolo abstrato                                       |
//+------------------------------------------------------------------+
class CSymbol : public CObject
  {
private:
   struct SMarginRate
     {
      double         Initial;          // coeficiente de cobrança de margem inicial
      double         Maintenance;      // coeficiente de cobrança da margem de manutenção
     };
   struct SMarginRateMode
     {
      SMarginRate    Long;             // MarginRate de posições longas
      SMarginRate    Short;            // MarginRate de posições curtas
      SMarginRate    BuyStop;          // MarginRate de ordens BuyStop
      SMarginRate    BuyLimit;         // MarginRate de ordens BuyLimit
      SMarginRate    BuyStopLimit;     // MarginRate de ordens BuyStopLimit
      SMarginRate    SellStop;         // MarginRate de ordens SellStop
      SMarginRate    SellLimit;        // MarginRate de ordens SellLimit
      SMarginRate    SellStopLimit;    // MarginRate de ordens SellStopLimit
     };
   SMarginRateMode   m_margin_rate;                                  // estrutura da taxa de cobrança da margem

À seção privada da classe, adicionamos o método que preenche todas as propriedades do símbolo para cada coeficiente de cobrança de margem, o método que inicializa as variáveis das estruturas que armazenam todos o os coeficientes de cobrança de margem, bem como dois métodos auxiliares para obter o dia atual da semana e para obter o número de casas decimais como valor double:

   SMarginRateMode   m_margin_rate;                                  // estrutura da taxa de cobrança da margem
   MqlTick           m_tick;                                         // Estrutura do tick do símbolo
   MqlBookInfo       m_book_info_array[];                            // Matriz de estruturas de dados do livro de ofertas
   string            m_symbol_name;                                  // Nome do símbolo
   long              m_long_prop[SYMBOL_PROP_INTEGER_TOTAL];         // Propriedades de número inteiro
   double            m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL];        // Propriedades reais
   string            m_string_prop[SYMBOL_PROP_STRING_TOTAL];        // Propriedades de string
   int               m_digits_currency;                              // Casas decimais da moeda da conta
   int               m_global_error;                                 // Código de erro global
//--- Retorna o índice da matriz na qual a propriedade (1) double e (2) a propriedade string do símbolo estão realmente localizadas
   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) Preenche todas as propriedades do símbolo "taxa de cobrança de margem", (2) inicializa as taxas
   bool              MarginRates(void);
   void              InitMarginRates(void);
//--- Zera todos os dados do objeto-símbolo
   void              Reset(void);
//--- Retorna o dia da semana atual
   ENUM_DAY_OF_WEEK  CurrentDayOfWeek(void)              const;
//--- Retorna o número de casas decimais num valor double
   int               GetDigits(const double value)       const;
public:

Neste caso, na seção privada da classe declararemos métodos que retornam dados sobre coeficientes de cobrança de margem para cada tipo de ordem:

//--- Obtém e retorna as propriedades reais do símbolo selecionado a partir de seus parâmetros
   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);
//--- Obtém e retorna propriedades de string do símbolo selecionado a partir de seus parâmetros

Às vezes, o programa precisa saber se o símbolo existe no servidor de acordo com o nome do símbolo. Nós já temos o método Exist(), que retorna essas informações sobre o símbolo da classe. Sobrecarregamos o método para que ele possa retornar dados de acordo com o nome do símbolo passado. Para isso, declaramos outra forma de chamada de método na seção privada da classe:

//--- Procura o símbolo e retorna um sinalizador indicando que existe no servidor
   bool              Exist(void)                         const;
   bool              Exist(const string name)            const;

E na seção protegida da classe declaramos o método sobrecarregado que retorna o valor da existência do símbolo por seu nome dependendo do tipo de programa (MQL5 ou MQL4):

protected:
//--- Construtor paramétrico protegido
                     CSymbol(ENUM_SYMBOL_STATUS symbol_status,const string name);

//--- Obtém e retorna as propriedades inteiras do símbolo selecionado a partir de seus parâmetros
   bool              SymbolExists(const string name)     const;
   long              SymbolExists(void)                  const;

Na seção pública da classe, em sua seção para métodos de descrição de propriedades declaramos um método virtual para registrar uma breve descrição do símbolo no log.
Nós implementamos esse método virtual nos herdeiros da classe, nos quais são definidas informações de esclarecimento sobre o objeto-símbolo.

//+------------------------------------------------------------------+
// Descrições das propriedades do objeto-símbolo                     |
//+------------------------------------------------------------------+
//--- Retorna uma descrição de uma propriedade - do símbolo - (1) inteira, (2) real e (3) de string
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_SYMBOL_PROP_STRING property);


//--- Exibe no log uma descrição das propriedades do símbolo (full_prop = true - todas as propriedades, false - apenas suportadas)
   void              Print(const bool full_prop=false);
//--- Exibe uma breve descrição do símbolo no log (implementação nos herdeiros)
   virtual void      PrintShort(void) {;}

//--- No que diz respeito ao objeto CSymbol, compara todas sua possíveis propriedades (para classificação de listas segundo a propriedade especificada símbolo-objeto)

No último artigo, ao implementar o objeto-símbolo, adicionamos vários métodos de serviço à seção pública da classe.
Adicionamos mais alguns métodos para retornar as horas de início e de término das seções de cotação e de negociação, bem como os métodos privados retornando um número inteiro de horas, de minutos e de segundos numa sessão e o método retornando uma descrição da duração da sessão no formato "HH:MM:SS" :

//--- (1) Adiciona, (2) remove um símbolo da janela Observação do mercado, (3) retorna o sinalizador de sincronização de dados do símbolo
   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);                                }
//--- Retorna (1) a hora de início e (2) a hora de término da sessão do dia da semana, (3) a hora de início e final do necessário
   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);
//--- Retorna (1) a hora de início e (2) a hora de término da sessão do dia da semana, (3) a hora de início e final do pregão requerido
   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) Realiza (1) assinatura do livro de ofertas, (2) fecha o livro de ofertas, (3) preenche os dados do livro de ofertas numa matriz de estruturas
   bool              BookAdd(void)                                const;
   bool              BookClose(void)                              const;
// --- Retorna (1) uma descrição da duração da sessão em hh:mm:ss, o número de (1) horas, (2) minutos e (3) segundos no tempo da duração da sessão
   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:
//+------------------------------------------------------------------+

Na seção pública, na seção de métodos para acesso simplificado às propriedades do objeto-símbolo adicionamos a segunda forma de chamada de método que retorna o sinalizador indicando que o símbolo existe no servidor (anteriormente declaramos um método sobrecarregado privado que procurava um símbolo no servidor pelo nome e retornava um sinalizador com o resultado da pesquisa)

//+------------------------------------------------------------------+
// Métodos para acesso simplificado às propriedades de um objeto-símbolo
//+------------------------------------------------------------------+
//--- Propriedades de número inteiro
   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);                               }

Na seção pública, na seção de métodos para acesso simplificado às propriedades reais do símbolo, adicionamos métodos que retornam todos os coeficientes de cobrança de margem:

//--- Propriedades reais
   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;
//--- Propriedades de string

Para obter informações sobre as propriedades do símbolo, é necessário que ele seja selecionado na janela "Observação do mercado". Mas pode acontecer que o símbolo não esteja selecionado na janela, mas seja necessário obter suas propriedades. Para fazer isso, precisamos criar um sinalizador indicando se o símbolo foi selecionado na janela 'Observação do mercado' antes de nos acessarmos as suas propriedades. Procedemos de acordo com o esquema: se o símbolo não estiver selecionado, selecionamo-lo, obtemos as propriedades e novamente ocultamos na janela 'Observação do mercado'. Se o símbolo já estiver selecionado, simplesmente obtemos suas propriedades.

Também precisamos inicializar os coeficientes de cobrança de margem no construtor da classe e preenchê-los para MQL5. Para MQL4, esses dados não existem e seus valores permanecem zero após a inicialização.
E adicionamos uma chamada aos métodos para salvar essas propriedades nos campos de propriedades da classe.

Para fazer isso, adicionamos o código necessário no construtor da classe:

//+------------------------------------------------------------------+
// Construtor paramétrico fechado                                    |
//+------------------------------------------------------------------+
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("Erro. Não existe esse símbolo no servidor","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("Falha ao colocar na Observação do Mercado. Erro: ","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("Falha ao obter preços atuais. Erro: ","Could not get current prices. Error: "),this.m_global_error);
     }
//--- Inicialização de dados
   ::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("Falha ao obter coeficientes de cobrança de margem. Erro: ","Failed to get margin rates. Error: "),this.m_global_error);
      return;
     }
#endif 
   
//--- Salvar propriedades de número inteiro
   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();

//--- Salvar propriedades reais
   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;

//--- Salvar propriedades de linha
   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();
//--- Salvar propriedades inteiras adicionais
   this.m_long_prop[SYMBOL_PROP_DIGITS_LOTS]                                  = this.SymbolDigitsLot();
//---
   if(!select)
      this.RemoveFromMarketWatch();
  }
//+------------------------------------------------------------------+

Agora precisamos escrever as implementações de todos os métodos declarados.

Fora do corpo da classe escrevemos a implementação do método que preenche todas as variáveis que armazenam coeficientes de cobrança de margem:

//+------------------------------------------------------------------+
// Preenche os arrays de taxa de cobrança da margem                  |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

No método para MQL5 simplesmente são chamados métodos que lêem dados sobre coeficientes (a partir das propriedades do símbolo) e os escrevem nas variáveis de estrutura correspondentes, enquanto o resultado do retorno de todos os métodos é resumido e retornado ao programa de chamada. Os métodos serão discutidos abaixo. Para MQL4 basta zerar todos os campos da estrutura.

Método de inicialização de campos da estrutura para as propriedades dos valores dos coeficientes de cobrança de margem:

//+------------------------------------------------------------------+
// Inicializa taxas de cobrança de margem                            |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

Aqui todos os campos da estrutura m_margin_rate são simplesmente redefinidos.

Implementação da segunda forma para o chamar o método que retorna o sinalizador que indica se o símbolo existe no servidor:

//+------------------------------------------------------------------+
// Retorna o sinalizador da existência do símbolo                    |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Neste caso, para MQL5 é retornada a propriedade do símbolo SYMBOL_EXIST, já para MQL4 é realizada a busca do símbolo no servidor com a ajuda do segundo método de chamada do método Exist(const string name).

Implementação de métodos que preenchem os coeficientes de cobrança de margem na estrutura para todos os tipos de ordens:

//+------------------------------------------------------------------+
//| Preenche índices de margem para posições longas                  |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Preenche índices de margem para posições curtas                  |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Preenche índices de margem para BuyStop                          |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Preenche índices de margem para BuyLimit                         |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Preenche índices de margem para BuyStopLimit                     |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Preenche índices de margem para SellStop                         |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Preenche índices de margem para SellLimit                        |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Preenche índices de margem para SellStopLimit                    |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Neste caso, para MQL5 é chamada a função SymbolInfoMarginRate() em que são preenchidas as propriedades necessárias que são armazenadas na estrutura m_margin_rate e é retornado o resultado da função. Para MQL4, retornamos false.

No método que retorna a descrição das propriedades inteiras do símbolo no bloco de retorno da descrição da cor de segundo plano para o símbolo na janela 'Observação do Mercado', fazemos as alterações:

      property==SYMBOL_PROP_BACKGROUND_COLOR    ?  TextByLanguage("Cor de fundo do símbolo na Observação do Mercado","Background color of the symbol in Market Watch")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
         #ifdef __MQL5__
         (this.GetProperty(property)==CLR_DEFAULT || this.GetProperty(property)==CLR_NONE ?  TextByLanguage(": (Ausente)",": (Not set)") : ": "+::ColorToString((color)this.GetProperty(property),true))
         #else TextByLanguage(": Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :

Anteriormente, comparávamos a cor com branco (clrWhite) e, se o valor da cor era maior que o valor da cor "branca", acreditava-se que a cor não estava definida. Já discutimos o porquê de esse método de comparação ser errado, portanto, aqui para determinar a ausência de uma determinada cor de plano de fundo para um símbolo na janela "Observação do mercado", compararemos a cor com a 'cor padrão' ou com a 'cor em falta'.

Para o método que retorna as descrições das propriedades reais do símbolo GetPropertyDescription (propriedade ENUM_SYMBOL_PROP_DOUBLE), adicionamos a saída da descrição de todos os coeficientes de cobrança de margem:

//--- Coeficiente de cobrança de margem inicial da posição Long
      property==SYMBOL_PROP_MARGIN_LONG_INITIAL          ?  TextByLanguage("Coeficiente de cobrança da margem inicial para posições longas","Coefficient of margin initial charging for long positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Coeficiente de cobrança de margem inicial da posição Short
      property==SYMBOL_PROP_MARGIN_SHORT_INITIAL     ?  TextByLanguage("Coeficiente de cobrança da margem inicial para posições curtas","Coefficient of margin initial charging for short positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Coeficiente de cobrança da margem de manutenção da posição Long
      property==SYMBOL_PROP_MARGIN_LONG_MAINTENANCE          ?  TextByLanguage("Coeficiente de cobrança da margem de manutenção para posições longas","Coefficient of margin maintenance charging for long positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Coeficiente de cobrança da margem de manutenção da posição Short
      property==SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE          ?  TextByLanguage("Coeficiente de cobrança da margem de manutenção para posições curtas","Coefficient of margin maintenance charging for short positions")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Coeficiente de cobrança de margem inicial da ordem Long
      property==SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL      ?  TextByLanguage("Coeficiente de cobrança da margem inicial para ordens BuyStop","Coefficient of margin initial charging for BuyStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL     ?  TextByLanguage("Coeficiente de cobrança da margem inicial para ordens BuyLimit","Coefficient of margin initial charging for BuyLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL ?  TextByLanguage("Coeficiente de cobrança da margem inicial para ordens BuyStopLimit","Coefficient of margin initial charging for BuyStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Coeficiente de cobrança de margem inicial da ordem Short
      property==SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL      ?  TextByLanguage("Coeficiente de cobrança da margem inicial para ordens SellStop","Coefficient of margin initial charging for SellStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL     ?  TextByLanguage("Coeficiente de cobrança da margem inicial para ordens SellLimit","Coefficient of margin initial charging for SellLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL ?  TextByLanguage("Coeficiente de cobrança da margem inicial para ordens SellStopLimit","Coefficient of margin initial charging for SellStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Taxa de margem de manutenção da ordem Long
      property==SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE      ?  TextByLanguage("Coeficiente de cobrança da margem inicial para ordens BuyStop","Coefficient of margin maintenance charging for BuyStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE     ?  TextByLanguage("Coeficiente de cobrança da margem de manutenção para ordens BuyLimit","Coefficient of margin maintenance charging for BuyLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE ?  TextByLanguage("Coeficiente de cobrança da margem de manutenção para ordens BuyStopLimit","Coefficient of margin maintenance charging for BuyStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
//--- Coeficiente de cobrança da margem de manutenção da ordem Short
      property==SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE      ?  TextByLanguage("Coeficiente de cobrança da margem de manutenção para ordens SellStop","Coefficient of margin maintenance charging for SellStop orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE     ?  TextByLanguage("Coeficiente de cobrança da margem de manutenção para ordens SellLimit","Coefficient of margin maintenance charging for SellLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
      property==SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE ?  TextByLanguage("Coeficiente de cobrança da margem de manutenção para ordens SellStopLimit","Coefficient of margin maintenance charging for SellStopLimit orders")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Propriedade não suportada",": Property is not support") :
          ": "+ 
          #ifdef __MQL5__ (this.GetProperty(property)==0  ?  TextByLanguage(": (Não definido)",": (Not set)") : (::DoubleToString(this.GetProperty(property),8)))
          #else TextByLanguage("Propriedade não suportada em MQL4","Property is not supported in MQL4") #endif 
         )  :
   //---

Implementação da segunda forma de chamar o método que procura o símbolo por seu nome no servidor e retorna o sinalizador que indica se o símbolo existe:

//+------------------------------------------------------------------+
//| Procura o símbolo e retorna um sinalizador indicando que existe no servidor        
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

Implementação do método que conta e retorna o número de casas decimais com um valor double:

//+------------------------------------------------------------------+
//| Retorna o número de casas decimais num valor double              |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

Discutimos esse método no último artigo. Aqui, ele foi simplesmente colocado num método separado, pois requer cálculos repetidos para vários valores — para o lote mínimo e para a etapa de alteração do lote.

Implementação de métodos que retornam a hora de início/término da sessão de cotação desde o início do dia:

//+------------------------------------------------------------------+
//| Retorna a hora de início da sessão                               |
//| em segundos desde o início do dia                                |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Retorna o tempo em segundos desde o início do dia                |
//| antes do final da sessão                                         |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Retorna o horário de início e término da sessão requerida        |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Para os dois primeiros métodos são passados o índice de sessão e odia da semana, para o terceiro são transferidas adicionalmente as variáveis de tipo datetime, em que são registrados os dados sobre o início e o fim da sessão requerida obtida através da função SymbolInfoSessionQuote().

Por conveniência, se -1 for transferido como o dia da semana, serão coletados os dados da sessão para o dia atual da semana. O índice da sessão deve começar do zero. A hora é retornada como o número de segundos desde o início do dia, determinado pelo parâmetro day_of_week. Assim, sempre é possível saber a hora real solicitada adicionando à hora do início do dia o número de segundos obtidos a partir do método.

Os métodos para obter os horários das sessões de negociação são implementados da mesma maneira:

//+------------------------------------------------------------------+
//| Retorna a hora de início da sessão de negociação                 |
//| em segundos desde o início do dia                                |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Retorna o tempo em segundos desde o início do dia                |
//| antes do final do pregão                                         |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| Retorna o horário de início e término da sessão de negociação requerida   
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Nestes métodos, tudo é semelhante aos descritos acima, exceto que aqui, para obter os dados necessários, é usada a função SymbolInfoSessionTrade().

Implementação de um método que retorna o dia atual da semana como um valor de enumeração ENUM_DAY_OF_WEEK:

//+------------------------------------------------------------------+
//| Retorna o dia atual da semana                                    |
//+------------------------------------------------------------------+
ENUM_DAY_OF_WEEK CSymbol::CurrentDayOfWeek(void) const
  {
   MqlDateTime time={0};
   ::TimeCurrent(time);
   return(ENUM_DAY_OF_WEEK)time.day_of_week;
  }
//+------------------------------------------------------------------+

Aqui tudo é simples, declaramos as estruturas da data e do tempo, acessamos a função TimeCurrent(), cuja segunda maneira de chamada preenche a estrutura da data e do tempo, transferida para a função, e, como resultado, retornamos o dia da semana a partir da estrutura preenchida.

Implementação do método que retorna o número de segundos no tempo da duração da sessão especificada:

//+------------------------------------------------------------------+
//| Retorna o número de segundos no tempo da duração da sessão       |
//+------------------------------------------------------------------+
int CSymbol::SessionSeconds(const ulong duration_sec) const
  {
   return int(duration_sec % 60);
  }
//+------------------------------------------------------------------+

Para o método é transferido o número de segundos e é retornado o restante da divisão pelo número de minutos neste intervalo de tempo.

Implementação do método que retorna o número de minutos no tempo da duração da sessão especificada:

//+------------------------------------------------------------------+
//| Retorna o número de minutos no tempo de duração da sessão        |
//+------------------------------------------------------------------+
int CSymbol::SessionMinutes(const ulong duration_sec) const
  {
   return int((duration_sec-this.SessionSeconds(duration_sec)) % 3600)/60;
  }
//+------------------------------------------------------------------+

Para o método é transferido o número de segundos e é retornado o número calculado de minutos neste intervalo de tempo exceto pelo número de segundosnão múltiplos de um minuto.

Implementação do método que retorna o número de horas no tempo da duração da sessão especificada:

//+------------------------------------------------------------------+
//| Retorna o número de horas no tempo da duração da sessão          |
//+------------------------------------------------------------------+
int CSymbol::SessionHours(const ulong duration_sec) const
  {
   return int(duration_sec-this.SessionSeconds(duration_sec)-this.SessionMinutes(duration_sec))/3600;
  }
//+------------------------------------------------------------------+

Para o método é transferido o número de segundos e é retornado o número de horas neste intervalo de tempo exceto pelo número de segundos não múltiplos de um minuto e o número de minutos, não múltiplos de uma hora.

Implementação do método que retorna uma descrição da duração da sessão como "HH:MM:SS":

//+------------------------------------------------------------------+
//| Retorna uma descrição da duração da sessão em hh:mm:ss           |
//+------------------------------------------------------------------+
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');
  }
//+------------------------------------------------------------------+

Aqui apenas obtemos a duração da sessão em segundos, obtemos a duração da sessão em segundos, minutos e horas, e exibimos uma mensagem com o formato Horas:Minutos:Segundos usando a função IntegerToString()com tamanho do comprimento da string para horas, minutos e segundos iguais a dois dígitos e o preenchimento "0", caso haja um único dígito nos valores de horas, minutos ou segundos.
Por exemplo, se recebermos 2 horas, será mostrado como 02.

Como modificamos levemente os status dos objetos-símbolos, corrigiremos o método que exibe a descrição desse status:

//+------------------------------------------------------------------+
//| Retorna a descrição do statu                                     |
//+------------------------------------------------------------------+
string CSymbol::GetStatusDescription() const
  {
   return
     (
      this.Status()==SYMBOL_STATUS_FX           ? TextByLanguage("Símbolo Forex","Forex symbol")                  :
      this.Status()==SYMBOL_STATUS_FX_MAJOR     ? TextByLanguage("Símbolo Forex major","Forex major symbol")      :
      this.Status()==SYMBOL_STATUS_FX_MINOR     ? TextByLanguage("Símbolo Forex minor","Forex minor symbol")      :
      this.Status()==SYMBOL_STATUS_FX_EXOTIC    ? TextByLanguage("Símbolo Forex exotic","Forex Exotic Symbol")   :
      this.Status()==SYMBOL_STATUS_FX_RUB       ? TextByLanguage("Símbolo Forex/Rublo","Forex symbol RUB")        :
      this.Status()==SYMBOL_STATUS_METAL        ? TextByLanguage("Metal","Metal")                                :
      this.Status()==SYMBOL_STATUS_INDEX        ? TextByLanguage("Índice","Index")                                :
      this.Status()==SYMBOL_STATUS_INDICATIVE   ? TextByLanguage("Indicativo","Indicative")                        :
      this.Status()==SYMBOL_STATUS_CRYPTO       ? TextByLanguage("Criptomoeda","Crypto symbol")         :
      this.Status()==SYMBOL_STATUS_COMMODITY    ? TextByLanguage("Símbolo Commoditie","Commodity symbol")            :
      this.Status()==SYMBOL_STATUS_EXCHANGE     ? TextByLanguage("Símbolo bolsista","Exchange symbol")             : 
      this.Status()==SYMBOL_STATUS_FUTURES      ? TextByLanguage("Futuros","Furures")                             : 
      this.Status()==SYMBOL_STATUS_CFD          ? TextByLanguage("Contrato por diferença","Contract For Difference") : 
      this.Status()==SYMBOL_STATUS_STOCKS       ? TextByLanguage("Título","Stocks")                        : 
      this.Status()==SYMBOL_STATUS_BONDS        ? TextByLanguage("Obrigação","Bonds")                             : 
      this.Status()==SYMBOL_STATUS_OPTION       ? TextByLanguage("Opção","Option")                               : 
      this.Status()==SYMBOL_STATUS_COLLATERAL   ? TextByLanguage("Ativo não negociável","Collateral")                : 
      this.Status()==SYMBOL_STATUS_CUSTOM       ? TextByLanguage("Símbolo personalizado","Custom symbol")       :
      this.Status()==SYMBOL_STATUS_COMMON       ? TextByLanguage("Símbolo de grupo compartilhado","Common group symbol")     :
      ::EnumToString((ENUM_SYMBOL_STATUS)this.Status())
     );
  }
//+------------------------------------------------------------------+

No método para atualizar todos os dados de símbolos, acrescentaremos, para MQL5, a obtenção de coeficientes de cobrança de margem para todos os tipos de ordens e posições.
Como, para MQL4, eles não são usados, seus valores serão zero após a inicialização no construtor da classe:

//+------------------------------------------------------------------+
//| Atualiza todos os dados de símbolos que podem mudar              |
//+------------------------------------------------------------------+
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 
//--- Atualizar propriedades de número inteiro
   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();
//--- Atualizar propriedades reais
   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;
  }
//+------------------------------------------------------------------+

Neste caso, para MQL5 chamamos imediatamente o método para obter dados sobre os coeficientes de cobrança de margem MarginRates(), e se pelo menos um dos coeficientes não tiver sido recebido (o método retornar false), então apenas escrevemos o código de erro na variável que armazena o código de erro da classe e, sem mensagem, saímos do método.
Não exibimos mensagem de erro no log, uma vez que o método opera no temporizador e, se ocorrer um recebimento incorreto de dados de repente, o log será rapidamente preenchido com mensagens de lixo sobre o mesmo erro. Além disso, como tal código de erro sempre pode ser obtido na classe CEngine, evitaremos a obrigação de recebê-lo e processá-lo.
No final do método todos os dados obtidos sobre os coeficientes são registrados nos campos das propriedades correspondentes do objeto-símbolo.

Pela mesma razão descrita acima, excluímos -do método para atualizar os dados da cotação- a string contendo a exibição da mensagem de erro no log:

//+------------------------------------------------------------------+
//| Atualiza os dados da cotação para o símbolo                      |
//+------------------------------------------------------------------+
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("Falha ao obter preços atuais. Erro: ","Could not get current prices. Error: "),this.m_global_error);
      return;
     }
//--- Atualizar propriedades de número inteiro
   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);
//--- Atualizar propriedades reais
   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();
  }  
//+------------------------------------------------------------------+

Agora o método ficará assim:

//+------------------------------------------------------------------+
//| Atualiza os dados da cotação para o símbolo                      |
//+------------------------------------------------------------------+
void CSymbol::RefreshRates(void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      return;
     }
//--- Atualizar propriedades de número inteiro
   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);
//--- Atualizar propriedades reais
   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();
  }  
//+------------------------------------------------------------------+

Isso conclui a modificação da classe de símbolo abstrato CSymbol.

Examinamos as mudanças mais importantes e significativas nos métodos de classe, com exceção das correções menores que foram feitas, mas, não descritas aqui, porque se tratam apenas de alguns erros ortográficos e "semânticos", principalmente nos métodos de descrição de propriedades. Adicionalmente, eles podem ser vistos nos arquivos anexados ao artigo.

Agora precisamos criar objetos herdeiros da classe base do símbolo abstrato. São precisamente esses objetos, distribuídos por categorias, que colocaremos na coleção de objetos-símbolos.

Objetos descendentes do objeto abstrato base 'símbolo'

Voltemos um pouco para examinar as categorias de símbolos que mencionamos e determinar os nomes respectivos das classes herdadas da classe base CSymbol:

  • Símbolo Forex — classe CSymbolFX
  • Símbolo Forex maior— classe CSymbolFXMajor
  • Símbolo Forex menor — classe CSymbolFXMinor
  • Símbolo Forex exótico — classe CSymbolFXExotic
  • Símbolo Forex rublo — classe CSymbolFXRub
  • Metal — classe CSymbolMetall
  • Índice — classe CSymbolIndex
  • Indicativo — classe CSymbolIndicative
  • Símbolo criptomoeda — classe CSymbolCrypto
  • Símbolo commoditie — classe CSymbolCommodity
  • Símbolo bolsista — classe CSymbolExchange
  • Futuro — classe CSymbolFutures
  • Contrato por diferença (CFD) — classe CSymbolCFD
  • Ação — classe CSymbolStocks
  • Obrigação — classe CSymbolBonds
  • Opção — classe CSymbolOption
  • Ativo não negociável — classe CSymbolCollateral
  • Símbolo personalizado — classe CSymbolCustom
  • Categoria geral — classe CSymbolCommon
No total, temos dezenove classes derivadas. Vejamos como criar uma classe com base no modelo de uma classe de símbolo Forex.

Na pasta da biblioteca \MQL5\Include\DoEasy\Objects\Symbols\, criamos a nova classe CSymbolFX com o nome do arquivo SymbolFX.mqh. Sua classe base deve ser a classe de símbolo abstrato CSymbol.
Declaramos imediatamente todos os métodos necessários para que a classe funcione:

//+------------------------------------------------------------------+
//|                                                     SymbolFX.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Arquivos incluídos                                               |
//+------------------------------------------------------------------+
#include "Symbol.mqh"
//+------------------------------------------------------------------+
//| Símbolo Forex                                                    |
//+------------------------------------------------------------------+
class CSymbolFX : public CSymbol
  {
public:
//--- Construtor
                     CSymbolFX(const string name) : CSymbol(SYMBOL_STATUS_FX,name) {}
//--- Propriedades de símbolo inteiro suportadas
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_INTEGER property);
//--- Propriedades reais do símbolo suportadas
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property);
//--- Propriedades de string do símbolo suportadas
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_STRING property);
//--- Exibe uma breve descrição do símbolo
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+

Para o construtor de classe transferimos o nome do símbolo, já na lista de inicialização do construtor de classe enviamos para a classe base a categoria de símbolo (seu status) 'Símbolo Forex' e o nome do símbolo que é passado ao construtor da classe CSymbolFX quando criado.

Métodos virtuais de manutenção de objeto com propriedades inteiras, reais e de string foram declarados na classe base, porém nós vamos implementá-los nas classes herdeiras. Método virtual PrintShort(), também declarado na classe base e implementado na classe herdeira, exibirá informações breves sobre o símbolo no log.

Olhando para o futuro, eu diria que quase todos esses métodos são quase os mesmos para todas as classes descendentes e podem ser feitos na classe base sem criar nenhuma classe descendente. Mas, neste caso, tal alteração nestes métodos para cada um dos grupos de símbolos iria se tornar menos flexível. Por isso, decidiu-se fazer a divisão em categorias através de classes herdeiras, para que, no futuro, se necessário, seja possível alterar cada uma dessas classes separadamente, o que é muito mais simples e rápido.

Implementação do método que retorna o sinalizador de suporte de objeto-símbolo de uma propriedade inteira:

//+------------------------------------------------------------------+
//| Retorna true se o símbolo suportar a propriedade                 |
//| inteira; caso contrário, retorna 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;
  }
//+------------------------------------------------------------------+

Para o método é transferida a propriedade inteira verificada e, para MQL5 e MQL4, se a propriedade transferida for "Existência do Símbolo", retornamos false , afinal, se um símbolo é criado, ele existe, e não precisamos que essa propriedade seja exibida no log, muito menos que seja pesquisada e classificada. Todas as outras verificações se aplicam somente a MQL4, enquanto é retornado false ao transferir uma propriedade de símbolo não suportada para um método em MQL4.
Se a propriedade transferida não estiver entre as propriedades listadas no teste, significa que ela é suportada, enquanto nós retornamos true.

Implementação do método que retorna o sinalizador de suporte de objeto-símbolo de uma propriedade real:

//+------------------------------------------------------------------+
//| Retorna true se o símbolo suportar a propriedade                 |
//| real; caso contrário, retorna 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;
  }
//+------------------------------------------------------------------+

Aqui a lógica é a mesma do método anterior. No entanto, primeiro, verificamos a propriedade resultante para MQL5: se ela for uma das propriedades do preço da última transação (Last) e o gráfico for construído com base em preços Bid, todas essas propriedades serão iguais a zero e, portanto, neste caso não serão suportadas.
Fazemos o mesmo com as propriedades do preço Bid quando o gráfico for baseado em preços Last, neste caso todas as propriedades do preço Bid não serão suportadas.
Para MQL4 agimos exatamente da mesma maneira que no método anterior — ao passar para o método uma propriedade de símbolo evidentemente não suportada, retornamos false.

Implementação do método que retorna o sinalizador de suporte de objeto-símbolo de uma propriedade de string:

//+------------------------------------------------------------------+
//| Retorna true se o símbolo suportar a propriedade                 |
//| string; retorna false caso contrário                             |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

Neste caso, tudo é o mesmo que nos dois anteriores, quer dizer, para MQL5, se a propriedade transferida for "Fórmula para Calcular Símbolo Personalizado" e este símbolo não for personalizado, retornamos false, uma vez que a propriedade não é suportada. Em seguida, verificamos as propriedades dos símbolo obviamente não suportadas para MQL4 e retornamos false se transferida uma propriedade não suportada em MQL4.

Método para exibir no log uma breve descrição do símbolo:

//+------------------------------------------------------------------+
//| Registra uma breve descrição do símbolo no log                   |
//+------------------------------------------------------------------+
void CSymbolFX::PrintShort(void)
  {
   ::Print(this.GetStatusDescription()+" "+this.Name());
  }
//+------------------------------------------------------------------+

O método simplesmente registra no log uma linha que consiste numa descrição de string do status do símbolo e dos seus nomes.

As outras classes derivadas são construídas exatamente da mesma maneira e têm os mesmos métodos com a mesma implementação.
A diferença está na classe de símbolo personalizado, pois esse tipo de símbolo definitivamente não está em MQL4, portanto, todas as verificações tem a ver apenas com MQL5:

//+------------------------------------------------------------------+
//|                                                 SymbolCustom.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Arquivos incluídos                                               |
//+------------------------------------------------------------------+
#include "Symbol.mqh"
//+------------------------------------------------------------------+
//| Símbolo personalizado                                            |
//+------------------------------------------------------------------+
class CSymbolCustom : public CSymbol
  {
public:
//--- Construtor
                     CSymbolCustom(const string name) : CSymbol(SYMBOL_STATUS_CUSTOM,name) {}
//--- Propriedades de símbolo inteiro suportadas
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_INTEGER property);
//--- Propriedades reais do símbolo suportadas
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property);
//--- Propriedades de string do símbolo suportadas
   virtual bool      SupportProperty(ENUM_SYMBOL_PROP_STRING property);
//--- Exibe uma breve descrição do símbolo
   virtual void      PrintShort(void);
  };
//+------------------------------------------------------------------+
//| Retorna true se o símbolo suportar a propriedade                 |
//| inteira; caso contrário, retorna false                           |
//+------------------------------------------------------------------+
bool CSymbolCustom::SupportProperty(ENUM_SYMBOL_PROP_INTEGER property)
  {
   if(property==SYMBOL_PROP_EXIST) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Retorna true se o símbolo suportar a propriedade                 |
//| real; caso contrário, retorna 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;
  }
//+------------------------------------------------------------------+
//| Retorna true se o símbolo suportar a propriedade                 |
//| string; retorna false caso contrário                             |
//+------------------------------------------------------------------+
bool CSymbolCustom::SupportProperty(ENUM_SYMBOL_PROP_STRING property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Registra uma breve descrição do símbolo no log                   |
//+------------------------------------------------------------------+
void CSymbolCustom::PrintShort(void)
  {
   ::Print(this.GetStatusDescription()+" "+this.Name());
  }
//+------------------------------------------------------------------+

Com isso, concluímos o desenvolvimento das classes herderias CSymbol.
A implementação das demais classes herdeiras pode ser vista no final do artigo nos arquivos anexados.

Como precisamos pesquisar e classificar a coleção de símbolos, devemos criar a funcionalidade necessária para isso. Abrimos o arquivo Select.mqh, localizado na pasta da biblioteca \MQL5\Include\DoEasy\Services\, e fazemos adições a ele.
Primeiro, inserimos a classe de símbolo abstrato:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Arquivos incluídos                                               |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+

Na seção pública da classe, declaramos todos os métodos necessários para pesquisar e classificar:

//+------------------------------------------------------------------+
//| Classe para selecionar objetos que atendem aos critérios         |
//+------------------------------------------------------------------+
class CSelect
  {
private:
   //--- Método para comparar duas quantidades
   template<typename T>
   static bool       CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode);
public:
//+------------------------------------------------------------------+
//| Métodos de trabalho com ordens                                   |
//+------------------------------------------------------------------+
   //--- Retorna uma lista de ordens nas quais uma das propriedades de (1) número inteiro, (2) real e (3) de string satisfaz o critério especificado
   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);
   //--- Retorna o índice da ordem na lista com o valor máximo de propriedade (1) inteira, (2) real e (3) propriedade de string da ordem
   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);
   //--- Retorna o índice da ordem na lista com o valor mínimo da propriedade (1) inteira, (2) real e (3) de string para a ordem
   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);
//+------------------------------------------------------------------+
//| Métodos de trabalho com eventos                                  |
//+------------------------------------------------------------------+
   //--- Retorna a lista dos eventos que têm uma das propriedades (1) inteiras, (2) reais e (3) de string que satisfaz o critério especificado
   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);
   //--- Retorna o índice do evento na lista com o valor máximo para a propriedade (1) inteira, (2) real e (3) de string do evento
   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);
   //--- Retorna o índice do evento na lista com o valor mínimo para a propriedade (1) inteira, (2) real e (3) de string do evento
   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);
//+------------------------------------------------------------------+
//| Métodos de trabalho com contas                                   |
//+------------------------------------------------------------------+
   //--- Retorna a lista das contas com uma das propriedades (1) inteiras, (2) reais e (3) de string que satisfaz o critério especificado
   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);
   //--- Retorna o índice do evento na lista com o valor máximo para a propriedade (1) inteira, (2) real e (3) de string do evento
   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);
   //--- Retorna o índice do evento na lista com o valor mínimo para a propriedade (1) inteira, (2) real e (3) de string do evento
   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);
//+------------------------------------------------------------------+
//| Métodos de trabalho com símbolos                                 |
//+------------------------------------------------------------------+
   //--- Retorna a lista dos símbolos com uma das propriedades (1) inteiras, (2) reais e (3) de string que satisfaz o critério especificado
   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);
   //--- Retorna o índice do símbolo na lista com o valor máximo para a propriedade (1) inteira, (2) real e (3) de string da ordem
   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);
   //--- Retorna o índice do símbolo na lista com o valor mínimo para a propriedade (1) inteira, (2) real e (3) de string da ordem
   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);
//---
  };
//+------------------------------------------------------------------+

Fora da classe, escrevemos sua implementação:

//+------------------------------------------------------------------+
//| Métodos de trabalho com listas de símbolos                       |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Retorna uma lista de símbolos contendo uma das propriedades inteiras 
//| que atendem ao critério especificado                             |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna uma lista de símbolos contendo uma das propriedades reais|
//| que atendem ao critério especificado                             |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna uma lista de símbolos contendo uma das propriedades de string          
//| que atendem ao critério especificado                             |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna o índice de um símbolo numa lista                        |
//| com valor máximo da propriedade inteira                          |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna o índice de um símbolo numa lista                        |
//| com valor máximo da propriedade real                             |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna o índice de um símbolo numa lista                        |
//| com valor máximo da propriedade de string                        |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna o índice de um símbolo numa lista                        |
//| com valor mínimo da propriedade inteira                          |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna o índice de um símbolo numa lista                        |
//| com valor mínimo da propriedade real                             |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna o índice de um símbolo numa lista                        |
//| com valor mínimo da propriedade de string                        |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

Temos esse tipo de métodos para cada classe-coleção, além disso, seu funcionamento foi considerado na terceira parte da descrição da biblioteca ao criar a classe CSelect, por isso não vamos nos debruçar na sua análise.

Agora estamos prontos para criar a classe-coleção de símbolos.
Para trabalhar com uma coleção de símbolos do programa, podem-se usar quatro modos:

  1. Trabalhar com apenas um símbolo,
  2. Trabalhar com uma lista de símbolos,
  3. Trabalhar com a janela "Observação do Mercado",
  4. Trabalhar com uma lista completa de símbolos no servidor.

Para que a classe de coleção de símbolos“saiba” com o que trabalhar, usaremos o seguinte esquema:
No programa — em suas configurações, é especificada a enumeração de métodos para trabalhar com símbolos. Esse pode ser um dos quatro modos de operação mencionados.
O programa também deve ter um array de string, que será preenchido com uma função da biblioteca assim:

  • se for usado apenas um símbolo, o array conterá apenas o símbolo atual,
  • se for usada apenas uma lista personalizada de símbolos, que também pode estar nas configurações do programa e cujos símbolos estão separados por vírgulas, o array será preenchido com símbolos desta linha; se nesta linha houver apenas um símbolo ou a lista estiver vazia, o trabalho será com o símbolo atual
  • se for usada a Observação do Mercado, no array, em sua única célula, em vez do nome do símbolo, aparecerá "MARKET_WATCH"
  • se for usada uma lista completa de símbolos no servidor, no array, em vez do nome do símbolo, aparecerá "ALL"

Tudo isso será feito automaticamente, enquanto o usuário precisará apenas selecionar o modo para trabalhar com a coleção de símbolos, bem como criar pelo menos um array de string e no máximo uma array de string e uma string predefinidos nas configurações.


Classe para a coleção de símbolos

Na pasta da biblioteca \MQL5\Include\DoEasy\Collections\ criamos a nova classe CSymbolsCollection no arquivo SymbolsCollection.mqh.
A classe base para ela deve ser uma classe de objeto da biblioteca padrão CObject.

Imediatamente inserimos todos os arquivos de classe necessários no arquivo recém-criado:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Arquivos incluídos                                               |
//+------------------------------------------------------------------+
#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"
//+------------------------------------------------------------------+
//| Coleção de ordens e transações históricas                        |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   
public:
//--- Construtor
                     CSymbolsCollection();

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

E para as classes-coleções de biblioteca que já se tornaram padrão adicionamos os membros-variáveis da classe e do método:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Arquivos incluídos                                               |
//+------------------------------------------------------------------+
#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"
//+------------------------------------------------------------------+
//| Coleção de ordens e transações históricas                        |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   CListObj          m_list_all_symbols;     // Lista de todos os objetos-símbolo
   
public:
//--- Retorna a lista completa de coleções "como está"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_all_symbols;                                      }
//--- Retorna a lista da propriedade (1) inteira, (2) real e de (3) string selecionada que satisfaz o critério comparado
   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); }

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

Nós já consideramos todas essas variáveis e métodos ao criar as coleções passadas. Eu acho que não devemos nos debruçar sobre eles repetidamente.

Vamos adicionar todas as outras variáveis e métodos necessários para o funcionamento da classe-coleção de símbolos:

//+------------------------------------------------------------------+
//|                                            SymbolsCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Arquivos incluídos                                               |
//+------------------------------------------------------------------+
#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"
//+------------------------------------------------------------------+
//| Coleção de ordens e transações históricas                        |
//+------------------------------------------------------------------+
class CSymbolsCollection : public CObject
  {
private:
   CListObj          m_list_all_symbols;     // Lista de todos os objetos-símbolo
   ENUM_SYMBOLS_MODE m_mode_list;            // Modo de trabalho com listas de símbolos
   int               m_delta_symbol;         // Diferença no número de símbolos em comparação com a verificação anterior
   int               m_last_num_symbol;      // Número de símbolos na janela Observação do mercado na verificação anterior
   int               m_global_error;         // Código de erro global
//--- Retorna o sinalizador indicando que o objeto-símbolo existe na lista contendo todos os símbolos
   bool              IsPresentSymbolInList(const string symbol_name);
//--- Cria um objeto-símbolo e o coloca na lista
   bool              CreateNewSymbol(const ENUM_SYMBOL_STATUS symbol_status,const string name);
//--- Retorna o tipo de lista usado para os símbolos (Observação do mercado/Servidor)
   ENUM_SYMBOLS_MODE TypeSymbolsList(const string &symbol_used_array[]);

//--- Determina de acordo com o nome e retorna se o símbolo pertence ao grupo
   ENUM_SYMBOL_STATUS SymbolStatus(const string symbol_name)      const;
//--- Retorna se o símbolo pertence à categoria de acordo com os critérios do usuário
   ENUM_SYMBOL_STATUS StatusByCustomPredefined(const string symbol_name)  const;
//--- Retorna se o símbolo pertence à categoria de acordo com o cálculo de garantia
   ENUM_SYMBOL_STATUS StatusByCalcMode(const string symbol_name)  const;
//--- Retorna se o símbolo pertence aos (1) majos, (2) minors, (3) extics, (4) rublo predefinidos
//--- (5) indicativos, (6) metais, (7) commodities, (8) índice, (9) criptomoedas, (10) opções
   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;

//--- Procura o símbolo e retorna um sinalizador indicando que existe no servidor
   bool              Exist(const string name)                     const;
   
public:
//--- Retorna a lista completa de coleções "como está"
   CArrayObj        *GetList(void)                                                                          { return &this.m_list_all_symbols;                                      }
//--- Retorna a lista da propriedade (1) inteira, (2) real e de (3) string selecionada que satisfaz o critério comparado
   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); }
//--- Retorna o número de símbolos na Observação do mercado
   int               NewSymbols(void)    const                                                              { return this.m_delta_symbol;                                           }
//--- Retorna o modo de trabalho com a lista de símbolos
   ENUM_SYMBOLS_MODE ModeSymbolsList(void)                        const                                     { return this.m_mode_list;                                              }
//--- Construtor
                     CSymbolsCollection();
   
//--- Define a lista de símbolos usados
   bool              SetUsedSymbol(const string &symbol_used_array[]);
//--- Atualiza (1) todos, (2) dados de cotação para os símbolos da coleção
   void              Refresh(void);
   void              RefreshRates(void);
  };
//+------------------------------------------------------------------+

A classe deve saber com qual lista de símbolos deve trabalhar: símbolo atual, conjunto de símbolos especificado pelo usuário, lista de símbolos na janela "Observação do Mercado" ou lista completa de todos os símbolos no servidor. Variável-membro da classe m_mode_list armazena um dos modos mencionados para trabalhar com símbolos.
Ao trabalhar com a lista de símbolos da janela 'Observação do Mercado', é necessário monitorar essa lista constantemente (isso será implementado no próximo artigo) e conhecer o número de símbolos na verificação anterior e, consequentemente, qual a mudança nesse número ao adicionar/remover símbolo(s) na lista da Observação do Mercado, para reconstruir a lista da coleção de símbolos m_list_all_symbols atempadamente e continuar a trabalhar com eles corretamente.
Como nas coleções anteriores, introduzimos uma nova variável-membro da classe que armazena código de erro, que se pode "ver" na classe base da biblioteca CEngine e processar correta e atempadamente.
Ao criar um novo objeto-símbolo e adicioná-lo à coleção, é necessário verificar se o mesmo símbolo não está na lista. Para fazer isso, é usado o método IsPresentSymbolInList().
Para criar um novo símbolo e adicioná-lo à coleção, é usado o método CreateNewSymbol ().
Como a coleção de símbolos funciona em quatro modos de listas de símbolos, é declarado o método TypeSymbolsList () para determinar qual desses modos usar.
Antes de criar um novo símbolo, primeiro é preciso identificar e definir o grupo ao qual ele pertence ou ao qual o usuário o associa. Para determinar o grupo de símbolos (seu status), usamos o método SymbolStatus().
Ao determinar o status de um símbolo, seu nome é pesquisado primeiro nos arrays personalizados especificados usando o método StatusByCustomPredefined().
Depois, se o método retornar o status "geral", o status do símbolo será determinado pelo tipo de cálculo de garantia com o método StatusByCalcMode().
Para pesquisar segundo arrays personalizados e para determinar se um símbolo pertence a determinada categoria atribuída pelo usuário, são utilizados métodos auxiliares que retornam o sinalizador que indica se o símbolo pertencente a maiores, menores, índices e outros grupos.
O método Exist() retorna o sinalizador que indica se o símbolo existe no servidor.
O método NewSymbols() será necessário para retornar o número de novos símbolos, adicionados ou removidos da janela Observação do Mercado, já o método ModeSymbolsList() retorna para o programa chamado o modo trabalhando com uma das quatro listas (símbolo atual, conjunto de símbolos predefinidos, lista da Observação do Mercado e lista completa de símbolos no servidor).
O método SetUsedSymbol() aceita um array de símbolos ou uma descrição do modo de lista de símbolos dentro do array transferido e cria uma lista de símbolos.
O método Refresh() atualiza todos os dados de todos os símbolos da coleção que podem ser alterados e o método RefreshRates() atualiza apenas os dados de cotação de todos os símbolos da coleção. Ambos os métodos devem ser chamados a partir do temporizador do objeto principal da biblioteca CEngine.

Desse modo, definimos todos os métodos necessários para a operação da classe de coleção de símbolos neste estágio. Agora consideremos sua construção.

Implementação do construtor de classe:

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

Na lista de inicialização do construtor, inicializamos o número de símbolos na última verificação, a diferença entre o número de símbolos atual e o anterior e definimos o modo "trabalhar com o símbolo atual".
No corpo da classe, classificamos a lista da coleção de símbolo por nome, limpamos a lista e damos a ela o identificador "lista de coleção de símbolos".

Método para atualizar todos os dados de todos os símbolo da coleção:

//+------------------------------------------------------------------+
//| Atualiza todos os dados de símbolos da coleção                   |
//+------------------------------------------------------------------+
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();
     }
  }
//+------------------------------------------------------------------+

No ciclo, segundo o número de símbolos na coleção, obtemos o próximo símbolo da lista da coleção e atualizamos seus dados usando o método Refresh() da classe CSymbol, que estudamos no último artigo.

Método para atualizar os dados de cotação de todos os símbolos da coleção:

//+------------------------------------------------------------------+
//| Atualiza os dados de cotação dos símbolos da coleção             |
//+------------------------------------------------------------------+
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();
     }
  }
//+------------------------------------------------------------------+

No ciclo, pelo número de símbolo da coleção, obtemos o próximo símbolo da lista da coleção e atualizamos seus dados usando o método RefreshRates() da classe CSymbol.

Método para criar um novo objeto-símbolo e colocá-lo na lista de coleção de símbolos:

//+------------------------------------------------------------------+
//| Cria um objeto-símbolo e o coloca numa lista                     |
//+------------------------------------------------------------------+
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("Erro de entrada: não há símbolos ","Input error: no ");
      string t2=TextByLanguage(" no servidor"," 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;   // Símbolo forex
      case SYMBOL_STATUS_FX_MAJOR   :  symbol=new CSymbolFXMajor(name);    break;   // Símbolo forex major
      case SYMBOL_STATUS_FX_MINOR   :  symbol=new CSymbolFXMinor(name);    break;   // Símbolo forex minor
      case SYMBOL_STATUS_FX_EXOTIC  :  symbol=new CSymbolFXExotic(name);   break;   // Símbolo exótico forex
      case SYMBOL_STATUS_FX_RUB     :  symbol=new CSymbolFXRub(name);      break;   // Símbolo forex/rublo
      case SYMBOL_STATUS_METAL      :  symbol=new CSymbolMetall(name);     break;   // Metal
      case SYMBOL_STATUS_INDEX      :  symbol=new CSymbolIndex(name);      break;   // Índice
      case SYMBOL_STATUS_INDICATIVE :  symbol=new CSymbolIndicative(name); break;   // Indicativo
      case SYMBOL_STATUS_CRYPTO     :  symbol=new CSymbolCrypto(name);     break;   // Símbolo de criptomoeda
      case SYMBOL_STATUS_COMMODITY  :  symbol=new CSymbolCommodity(name);  break;   // Símbolo de commoditie
      case SYMBOL_STATUS_EXCHANGE   :  symbol=new CSymbolExchange(name);   break;   // Símbolo de bolsa
      case SYMBOL_STATUS_FUTURES    :  symbol=new CSymbolFutures(name);    break;   // Futuro
      case SYMBOL_STATUS_CFD        :  symbol=new CSymbolCFD(name);        break;   // Contrato por diferença
      case SYMBOL_STATUS_STOCKS     :  symbol=new CSymbolStocks(name);     break;   // Título
      case SYMBOL_STATUS_BONDS      :  symbol=new CSymbolBonds(name);      break;   // Obrigação
      case SYMBOL_STATUS_OPTION     :  symbol=new CSymbolOption(name);     break;   // Opção
      case SYMBOL_STATUS_COLLATERAL :  symbol=new CSymbolCollateral(name); break;   // Ativo não negociável
      case SYMBOL_STATUS_CUSTOM     :  symbol=new CSymbolCustom(name);     break;   // Símbolo personalizado
      default                       :  symbol=new CSymbolCommon(name);     break;   // Resto
     }
   if(symbol==NULL)
     {
      ::Print(DFUN,TextByLanguage("Falha ao criar o objeto-símbolo ","Failed to create symbol object "),name);
      return false;
     }
   if(!this.m_list_all_symbols.Add(symbol))
     {
      string t1=TextByLanguage("Falha ao adicionar símbolo ","Failed to add ");
      string t2=TextByLanguage(" na lista"," symbol to the list");
      ::Print(DFUN,t1,name,t2);
      delete symbol;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Para o método são transferidos o status e o nome do símbolo, se esse símbolo já estiver na lista da coleção de símbolos, depois não fazendo nada "silenciosamente" retornamos true, pois não há erro e o símbolo não precisar ser adicionado, pois já existe.
Depois, verificamos se o símbolo existe no servidor por seu nome e, se ele não estiver, exibimos uma mensagem indicando sua ausência, definimos o código de erro como "símbolo desconhecido" e retornamos false.
Se o símbolo existir, então dependendo do statuspassado para o método, criamos um novo objeto-símbolo. Para criar um objeto-símbolo, usamos as classes herdadas do símbolo abstrato correspondentes ao status transferido. Em caso de falha ao criar o objeto, exibimos uma mensagem sobre isso e retornamos false.
Se o novo objeto-símbolo for criado com sucesso, adicionamo-lo à lista de coleção de símbolos e retornamos true após a adição bem-sucedida, ou exibimos uma mensagem de erro e retornamos falseem caso de falha.

Implementação do método que retorna o sinalizador que indica se o símbolo existe na lista da coleção:

//+------------------------------------------------------------------+
//| Retorna o sinalizador da existência do símbolo                   |
//| de acordo com o seu nome na lista de todos os símbolos           |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Para o método é passado o nome do símbolo desejado, depois, a lista é classificada pelo nome do símbolo e filtrada pelo nome transferido. Se a lista não estiver vazia, na lista será encontrado um símbolo com tal nome, e retornamos true, caso contrário, retornamos false .

Implementação do método que define a lista de símbolos da coleção:

//+------------------------------------------------------------------+
//| Define a lista de símbolos usados                                |
//+------------------------------------------------------------------+
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);
   //--- É usado penas o símbolo atual
   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;
      //--- É usada uma lista predefinida de símbolos
      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;
        }
      //--- É usada a lista completa de símbolos do servidor
      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;
        }
      //--- É usada a lista de símbolos da janela "Observação do mercado"
      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;
  }
//+------------------------------------------------------------------+

O método cria uma lista-coleção de símbolos, dependendo do conteúdo do array de símbolos passados para ela. Primeiro, é definido um método de lista de símbolos (símbolo único/conjunto de símbolos personalizado/Observação do Mercado/lista completa), a seguir, a lista é limpa e ordenada por nome.
Se for usado apenas o símbolo atual, então

  • definimos o status do símbolo de acordo com o seu nome e
  • retornamos o resultado da criação de um novo objeto-símbolo e adicionamo-lo à lista de coleção de símbolos.

Se for usada uma lista predefinida de símbolos, então

  • num ciclo, usando o array de nomes de símbolos passados para o método, obtemos o próximo nome do símbolo da matriz
  • determinamos o status do símbolo por seu nome
  • adicionamos à variável retornada o resultado da criação e adição do objeto-símbolo à lista-coleção de símbolos
  • se o símbolo não puder ser criado ou adicionado à lista , vamos para o próximo
  • no final do ciclo, retornamos o estado da variável na qual foram registrados os resultados da criação dos símbolos da coleção

Os outros dois modos são processados da mesma maneira que o modo de lista predefinida. Somente, em vez do array passado para o método, pegamos os símbolo a partir da janela Observação do Mercado ou da lista completa de símbolo no servidor.

Implementação do método que retorna o modo para trabalhar com listas de símbolos:

//+------------------------------------------------------------------+
//| Retorna o tipo de lista de símbolos usada (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)
     );
  }
//+------------------------------------------------------------------+

Para o método é passado um array com nomes de símbolos ou com uma descrição dos modos para trabalhar com listas.
Se for passado um array vazio, retornamos o modo para trabalhar apenas com o símbolo atual
.
Em seguida, obtemos seu conteúdo da célula zero do array e

  • se, numa substring começando no índice 13, estiver a entrada "MARKET_WATCH", retornamos o modo para trabalhar com a janela "Observação do Mercado"
  • Se aparecer a entrada "ALL", retornamos o modo para trabalhar com uma lista completa de símbolos.
  • Caso contrário, 
    • se no array houver apenas uma entrada e tiver o nome do símbolo atual, retornamos o modo para trabalhar somente com o símbolo atual.
    • Bem, na última das opções possíveis, retornamos o modo para trabalhar com uma lista predefinida.

Implementação do método que retorna se o símbolo pertence ao grupo de acordo com seu nome:

//+------------------------------------------------------------------+
//| Define de acordo com o nome e retorna se o símbolo pertence ao grupo 
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

O nome do símbolo é transferido para o método. Em seguida, é verificado se ele pertence aos grupos personalizados especificados. Se, como resultado, for retornado o status "grupo geral", procuramos o grupo de acordo com a propriedade do símbolo "modo de cálculo de garantia". No final, retornamos o status recebido. A propósito, pode ser equiparado a um dos grupos de acordo com o modo 'calcular lucro e margem' ou pode permanecer no grupo geral de símbolos.
Como resultado, pode acontecer que quer o símbolo terá o status de grupo personalizado; quer poderá ser atribuído a ele um grupo de acordo com o método de cálculo de garantia se não estava nos grupos personalizados; quer o símbolo permanecerá no grupo geral se não for possível determiná-lo no grupo em questão.

Implementação de métodos que retornam o sinalizador que indica se um símbolo pertence a grupos personalizados específicos:

//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence aos 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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence aos 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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence aos exotics                        |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence aos de rublos                      |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence aos indicadores                    |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence aos metais                         |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence aos 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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence aos índices                        |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence às criptomoedas                    |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+
//| Retorna se o símbolo pertence às opções                          |
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

Dependendo do nome do método (no grupo personalizado que está sendo verificado), os métodos simplesmente pesquisam se existe um símbolo cujo nome é passado para o método no array personalizado correspondente ao seu grupo. Se esse símbolo for encontrado no array, será retornadotrue. Caso contrário, false.

Método que retorna o status do símbolo de acordo com o fato de ele existir em grupos personalizados:

//+------------------------------------------------------------------+
//| Retorna a categoria segundo os critérios do usuário              |
//+------------------------------------------------------------------+
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
     );
  }  
//+------------------------------------------------------------------+

Para o método é passado o nome do símbolo e em cada grupo personalizado é alternadamente verificado se ele existe usando os métodos acima. Assim que um símbolo é encontrado em qualquer um dos grupos, é retornado o status correspondente ao grupo onde o símbolo é encontrado. Se nenhum símbolo for encontrado nos grupos, será retornado o status "grupo geral de símbolos".

Método que retorna se o símbolo pertence a um grupo pelo método de cálculo de garantia:

//+------------------------------------------------------------------+
//| Retorna se pertence à categorias de cálculo de fundos de garantia|
//+------------------------------------------------------------------+
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
     );
  }
//+------------------------------------------------------------------+

Para o método é passado o nome do símbolo, depois, para este símbolo obtemos o método de cálculo de garantia e, em seguida, dependendo do valor recebido, retornamos o status do símbolo. Se nenhum dos métodos de cálculo tiver sido identificado, retornamos o status "grupo geral de símbolos".

Método que procura o símbolo no servidor e retorna um sinalizador indicando sua presença:

//+------------------------------------------------------------------+
//| Procura o símbolo e retorna um sinalizador indicando que existe no servidor            
//+------------------------------------------------------------------+
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;
  }
//+------------------------------------------------------------------+

A classe de coleção de símbolos está pronta. Agora precisamos pô-la a funcionar. Tudo começa com a classe CEngine, ela é iniciada e gerenciada a partir dela. Faremos as alterações necessárias.

Inserimos o arquivo da classe de coleção de símbolos ao arquivo da classe CEngine e declaramos o objeto-coleção de símbolos e os métodos necessários:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Arquivos incluídos                                               |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
//+------------------------------------------------------------------+
//| Classe base da biblioteca                                        |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Coleção de ordens e transações históricas
   CMarketCollection    m_market;                        // Coleção de ordens e transações de mercado
   CEventsCollection    m_events;                        // Coleção de eventos
   CAccountsCollection  m_accounts;                      // Coleção de contas
   CSymbolsCollection   m_symbols;                       // Coleção de símbolos
   CArrayObj            m_list_counters;                 // Lista dos contadores de temporizador
   int                  m_global_error;                  // Código de erro globla
   bool                 m_first_start;                   // Sinalizador da primeira inicialização
   bool                 m_is_hedge;                      // Sinalizador de conta de cobertura
   bool                 m_is_tester;                     // Sinalizador de trabalho no testador
   bool                 m_is_market_trade_event;         // Sinalizador de evento de negociação na conta
   bool                 m_is_history_trade_event;        // Sinalizador de evento de negociação no histórico da conta
   bool                 m_is_account_event;              // Sinalizador do evento de alteração de conta
   ENUM_TRADE_EVENT     m_last_trade_event;              // Último evento de negociação na conta
   ENUM_ACCOUNT_EVENT   m_last_account_event;            // Último evento nas propriedades da conta
//--- Retorna o índice do contador por ID
   int                  CounterIndex(const int id) const;
//--- Retorna (1) o sinalizador da primeira inicialização, (2) a presença do sinalizador no evento de negociação
   bool                 IsFirstStart(void);
//--- Trabalho com eventos (1) de ordens, de transações e posições, (2) de contas
   void                 TradeEventsControl(void);
   void                 AccountEventsControl(void);
//--- (1) Trabalho com coleções de símbolos e (2) de eventos da lista de símbolos na janela de observação do mercado
   void                 SymbolEventsControl(void);
   void                 MarketWatchEventsControl(void);
//--- Retorna a última (1) ordem pendente a mercado, (2) ordem a mercado, (3) última posição, (4) posição de acordo com o ticket
   COrder              *GetLastMarketPending(void);
   COrder              *GetLastMarketOrder(void);
   COrder              *GetLastPosition(void);
   COrder              *GetPosition(const ulong ticket);
//--- Retorna a última (1) ordem pendente excluída, (2) ordem a mercado histórica, (3) última posição, (4) posição histórica (a mercado ou pendente) de acordo com o ticket
   COrder              *GetLastHistoryPending(void);
   COrder              *GetLastHistoryOrder(void);
   COrder              *GetHistoryOrder(const ulong ticket);
//--- Retorna (1) a primeira e a (2) última ordem a mercado histórica da lista de ordens e posições, (3) a última transação
   COrder              *GetFirstOrderPosition(const ulong position_id);
   COrder              *GetLastOrderPosition(const ulong position_id);
   COrder              *GetLastDeal(void);
public:
   //--- Retorna a lista de (1) posições a mercado, (2) ordens pendentes e (3) ordens a mercado
   CArrayObj           *GetListMarketPosition(void);
   CArrayObj           *GetListMarketPendings(void);
   CArrayObj           *GetListMarketOrders(void);
   //--- Retorna a lista de (1) ordens históricas, (2) ordens pendentes excluídas e (3) transações (4) e todas as ordens a mercado de acordo com o seu ID
   CArrayObj           *GetListHistoryOrders(void);
   CArrayObj           *GetListHistoryPendings(void);
   CArrayObj           *GetListDeals(void);
   CArrayObj           *GetListAllOrdersByPosID(const ulong position_id);
//--- Retorna a lista (1) de contas, (2) de eventos de contas, (3) o evento de alteração da conta de acordo com o seu índice de lista
//--- (4) conta atual, (5) descrição do evento
   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);
//--- Retorna a lista de eventos usados
   CArrayObj           *GetListAllUsedSymbols(void)                     { return this.m_symbols.GetList();                    }
   
//--- Retorna a lista de ordens, de transações e deposições
   CArrayObj           *GetListAllOrdersEvents(void)                    { return this.m_events.GetList();                     }
//--- Redefine o último evento de negociação
   void                 ResetLastTradeEvent(void)                       { this.m_events.ResetLastTradeEvent(); }
//--- Retorna (1) o último evento de negociação, (2) o último evento nas propriedades da conta, (3) o sinalizador de conta de cobertura, (4) o sinalizador de trabalho no testador
   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();            }
//--- Retorna o código de evento da conta
   int                  GetAccountEventsCode(void)                const { return this.m_accounts.GetEventCode();              }
//--- Retorna o código de erro global do CEngine
   int                  GetError(void)                            const { return this.m_global_error;                         }
//--- Cria um contador de temporizador
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Temporizador
   void                 OnTimer(void);
//--- Define a lista de símbolos usados
   bool                 SetUsedSymbols(const string &array_symbols[])   { return this.m_symbols.SetUsedSymbol(array_symbols); }
//--- Construtor/Destrutor
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

No método SymbolEventsControl() atualizaremos os dados de cotação de todos os símbolos da coleção, já no método MarketWatchEventsControl() atualizaremos os outros dados de todos os símbolos da coleção e monitoraremos os eventos na janela 'Observação do Mercado' (na classe de eventos da coleção de símbolos no próximo artigo)
O método GetListAllUsedSymbols() retorna para o programa chamado a lista completa da coleção de símbolos usando o método GetList() da classe CSymbolsCollection.
O método SetUsedSymbols() chama o método com o mesmo nome SetUsedSymbol() da classe CSymbolsCollection, que, por sua vez, preenche a lista de coleções com objetos-símbolo de todos os símbolos usados no programa.

Consideremos a construção desses métodos.

No construtor da classe, criamos contadores, o primeiro temporizador e o segundo da coleção de símbolos. No primeiro, atualizaremos os dados de cotação de todos os símbolos da coleção e, no segundo, os dados restantes do símbolo e controlaremos o rastreamento de eventos na janela 'Observação do Mercado'.

//+------------------------------------------------------------------+
//| Construtor CEngine                                               |
//+------------------------------------------------------------------+
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,"Falha ao criar o temporizador. Erro: ","Could not create timer. Error: ",(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   //---__MQL4__
   #else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Falha ao criar o temporizador. Erro: ","Could not create timer. Error: ",(string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   #endif 
  }
//+------------------------------------------------------------------+

No manipulador OnTimer() da classe escrevemos strings para trabalhar com temporizadores de coleção de símbolos:

//+------------------------------------------------------------------+
//| Temporizador CEngine                                             |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
//--- Temporizador de coleções para ordens e transações históricas, para ordens e posições a mercado
   int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Se não for um testador
         if(!this.IsTester())
           {
            //--- Se a pausa terminar, trabalhamos com os eventos das coleções de ordens, de transações e de posições
            if(counter.IsTimeDone())
               this.TradeEventsControl();
           }
         //--- Se for o testador, trabalhamos com os eventos das coleções de acordo com o tick
         else
            this.TradeEventsControl();
        }
     }
//--- Temporizador de coleções para contas
   index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Se não for um testador
         if(!this.IsTester())
           {
            //--- Se a pausa terminar, trabalhamos com os eventos das coleções de contas
            if(counter.IsTimeDone())
               this.AccountEventsControl();
           }
         //--- Se for o testador, trabalhamos com os eventos das coleções de acordo com o tick
         else
            this.AccountEventsControl();
        }
     }
     
//--- Temporizador1 da coleção de símbolos (atualização de dados de cotação dos símbolos na coleção)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Se não for um testador
         if(!this.IsTester())
           {
            //--- Se a pausa terminar, atualizamos os dados de cotações para todos os símbolos na coleção
            if(counter.IsTimeDone())
               this.SymbolEventsControl();
           }
         //--- Se for o testador, atualizamos todos os dados de cotação para todos os símbolos na coleção de acordo com o tick
         else
            this.SymbolEventsControl();
        }
     }
//--- Temporizador2 da coleção de símbolos (atualização de todos os dados para os símbolos na coleção e acompanhamento de evento da lista de símbolos na janela de observação do mercado)
   index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL)
        {
         //--- Se não for um testador
         if(!this.IsTester())
           {
            //--- Se a pausa terminar
            if(counter.IsTimeDone())
              {
               //--- atualizamos os dados de todos os símbolos na coleção
               this.m_symbols.Refresh();
               //--- Se trabalhamos co uma lista da Observação do mercado, verificamos os eventos da janela Observação do mercado
               if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH)
                  this.MarketWatchEventsControl();
              }
           }
         //--- Se for o testador, atualizamos os dados de todos os símbolos na coleção de acordo com o tick
         else
            this.m_symbols.Refresh();
        }
     }
  }
//+------------------------------------------------------------------+

Aqui, toda a lógica está escrita nos comentários do código, e acho que não faz sentido insistir em sua re-análise.

O método que trabalha com a coleção de símbolos nesta implementação simplesmente atualiza os dados de cotação dos símbolo da coleção por meio do método RefreshRates da classe CSymbolsCollection:

//+------------------------------------------------------------------+
//| Trabalhando com uma coleção de símbolos                          |
//+------------------------------------------------------------------+
void CEngine::SymbolEventsControl(void)
  {
   this.m_symbols.RefreshRates();
  }
//+------------------------------------------------------------------+

O método para trabalhar com eventos da janela 'Observação do mercado' nesta implementação não faz nada, apenas verifica se é chamado a partir do testador e sai se funcionar no testador (no testador, não faz sentido rastrear os eventos da janela Observação do Mercado):

//+------------------------------------------------------------------+
//| Trabalhando com eventos da lista de símbolos na janela de observação do mercado           
//+------------------------------------------------------------------+
void CEngine::MarketWatchEventsControl(void)
  {
   if(this.IsTester())
      return;
//--- Acompanhamento de eventos da janela "Observação do mercado"

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

Faremos todo o trabalho que tem a ver com eventos de coleção de símbolos no próximo artigo.

Estas são todas as alterações necessárias na classe CEngine.

Como precisamos passar para a classe de coleção de símbolos o modo para trabalhar com símbolos necessário, precisamos de uma função que determine o modo operacional e preencha o array correspondente de símbolos que serão transferidos para o objeto principal da biblioteca CEngine.

Abrimos o arquivo de função de serviço DELib.mqh da pasta \MQL5\Include\DoEasy\Services\ e escrevemos nele a função necessária:

//+------------------------------------------------------------------+
//| Prepara um array de símbolos para uma coleção de símbolos        |
//+------------------------------------------------------------------+
bool CreateUsedSymbolsArray(const ENUM_SYMBOLS_MODE mode_used_symbols,string defined_used_symbols,string &used_symbols_array[])
  {
   //--- Se o trabalho com o símbolo atual
   if(mode_used_symbols==SYMBOLS_MODE_CURRENT)
     {
      //--- Escrevemos o nome do símbolo atual numa única célula do array
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=Symbol();
      return true;
     }
   //--- Se estiver trabalhando com um conjunto de símbolos predefinido (da string defined_used_symbols)
   else if(mode_used_symbols==SYMBOLS_MODE_DEFINES)
     {
      //--- Definimos o separador vírgula
      string separator=",";
      //--- Substituímos os separadores errados pelos corretos
      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);
      //--- Contanto que haja espaços, deletamos
      while(StringFind(defined_used_symbols," ")>WRONG_VALUE && !IsStopped()) 
         StringReplace(defined_used_symbols," ","");
      //--- Contanto que haja delimitadores duplos (após remover espaços entre eles), mudamos para um delimitador
      while(StringFind(defined_used_symbols,separator+separator)>WRONG_VALUE && !IsStopped())
         StringReplace(defined_used_symbols,separator+separator,separator);
      //--- Se ficar um separador antes do primeiro símbolo na string, substituímos por um separador
      if(StringFind(defined_used_symbols,separator)==0) 
         StringSetCharacter(defined_used_symbols,0,32);
      //--- Se ficar um separador após o último símbolo na string, mudamos por um separador
      if(StringFind(defined_used_symbols,separator)==StringLen(defined_used_symbols)-1)
         StringSetCharacter(defined_used_symbols,StringLen(defined_used_symbols)-1,32);
      //--- Removemos todos os itens desnecessários à esquerda e à direita
      #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 
      //--- Preparamos o array 
      ArrayResize(used_symbols_array,0);
      ResetLastError();
      //--- dividimos a string em separadores (vírgula) e inserimos todas as substrings encontradas no array
      int n=StringSplit(defined_used_symbols,StringGetCharacter(separator,0),used_symbols_array);
      //--- se não encontrarmos nada, exibimos uma mensagem sobre isso (neste caso, será selecionado automaticamente o trabalho com o símbolo atual)
      if(n<1)
        {
         string err=
           (n==0  ?  
            DFUN_ERR_LINE+TextByLanguage("Erro. A string de símbolos predefinidos está vazia, será usado ","Error. String of predefined symbols is empty, the symbol will be used: ")+Symbol() :
            DFUN_ERR_LINE+TextByLanguage("Falha ao preparar array de símbolos usados. Erro","Failed to create an array of used characters. Error ")+(string)GetLastError()
           );
         Print(err);
         return false;
        }
     }
   //--- Se o trabalho com a janela "Observação do mercado" ou com uma lista inteira
   else
     {
      //--- inserimos o nome do modo de trabalho (mode_used_symbols) numa única célula do array
      ArrayResize(used_symbols_array,1);
      used_symbols_array[0]=EnumToString(mode_used_symbols);
     }
   return true;
  }
//+------------------------------------------------------------------+

Para o método são passados o modo de coleção de símbolos, que deve ser definido nas configurações do programa ou de maneira "hard-coded" (se a seleção do modo não for necessária), a lista de símbolos separados por vírgula necessária para trabalhar (ou uma string vazia) e a matriz na qual será inserida a lista de símbolos ou o modo de operação (para modos de operação com Observação do Mercado e com uma lista completa de símbolos no servidor) e que serão enviados à classe CEngine para definir o modo de operação da biblioteca com símbolos.
Todas as ações executadas pela função com a lista e com o array são descritas em detalhes diretamente na listagem e não faz sentido considerá-las separadamente.

Isso completa o desenvolvimento da classe de coleção de símbolos e estamos prontos para testá-la.

Teste para a coleção de símbolos

Para testar a coleção, pegamos no EA do último artigo e o salvamos com o novo nome \MQL5\Experts\TestDoEasy\ Part15\TestDoEasyPart15_1.mq5.

Imediatamente nos parâmetros de entrada, adicionamos a seleção do modo de trabalho com os símbolos da biblioteca e a variável de string armazenando a lista de símbolos personalizados necessários para trabalhar se selecionado esse modo nas configurações:

//--- 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

À lista de variáveis globais adicionamos uma variável para armazenar uma lista de símbolos personalizados e um array de string para passar uma lista de símbolos para a biblioteca:

//--- 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[];
//+------------------------------------------------------------------+

No manipulador OnInit() do EA atribuímos uma lista personalizada a uma variável para armazená-la, preenchemos o array de símbolos personalizados e o enviamos para a biblioteca:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Ao chamar esta função, no logo é exibida uma lista de constantes de enumeração, 
//--- definida no arquivo DELib.mqh nas strings 22 e 25, para verificar se as constantes são corretas
   //EnumNumbersTest();

//--- Configurando variáveis globais do EA
   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;
   
//--- Preenchendo o array de símbolos usados
   used_symbols=InpUsedSymbols;
   CreateUsedSymbolsArray(InpModeUsedSymbols,used_symbols,array_used_symbols);

//--- Configurando o tipo de lista de símbolos usada na coleção de símbolos
   engine.SetUsedSymbols(array_used_symbols);

//--- Verificando e removendo objetos gráficos não excluídos do EA

A função SetUsedSymbols(), discutida acima, criará um array de símbolo para enviá-lo para a classe de coleção de símbolos. Dependendo do modo selecionado, o array poderá conter quer o símbolo atual quer uma lista personalizada de símbolos quer uma descrição de string do modo de operação com a janela "Observação do Mercado" ou com uma lista completa de símbolos no servidor.

No final do manipulador OnInit(), inserimos o código para verificar rapidamente as listas de símbolos criados pela classe de coleção de símbolos:

//--- Configurando parâmetros da classe de negociação CTrade
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Verificação rápida da coleção de objetos-símbolos
   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);
  }
//+------------------------------------------------------------------+

Neste caso, obtemos a lista completa dos símbolos da coleção, no ciclo, na lista resultante obtemos outro símbolo, atualizamos todos os dados e imprimimos a descrição do símbolo no log - primeiro breve e depois se não estiver selecionado o modo "Trabalhar com a janela Observação do Mercado" ou "Trabalhar com a lista completa de símbolos no servidor", então imprimimos uma descrição completa das propriedades do símbolo no log.

Executamos o EA na janela do terminal e selecionamos o modo "Trabalhar com símbolos do Observação do Mercado" nas configurações. Como resultado, no log será exibida uma lista com breves descrições sobre todos os símbolos da coleção criada pela classe de coleção de símbolos:

2019.06.27 10:01:52.756 Ação ALNU
2019.06.27 10:01:52.756 Ação SU25075RMFS1
2019.06.27 10:01:52.756 Obrigação SU46022RMFS8
2019.06.27 10:01:52.756 Obrigação SU26214RMFS5
2019.06.27 10:01:52.756 Ação AESL
2019.06.27 10:01:52.756 Ação 123456.bin
2019.06.27 10:01:52.756 Ação  ARMD
2019.06.27 10:01:52.757 Obrigação SU46018RMFS6
2019.06.27 10:01:52.757 Ação  GAZP
2019.06.27 10:01:52.757 Metal XAUUSD
2019.06.27 10:01:52.757 Ação  EURRUB_TOD
2019.06.27 10:01:52.757 Ação  GBPRUB_TOM
2019.06.27 10:01:52.757 Futuro Si-9.19
2019.06.27 10:01:52.757 Futuro RTS-3.20
2019.06.27 10:01:52.758 Símbolo Forex menor USDNOK
2019.06.27 10:01:52.758 Símbolo Forex maior USDJPY
2019.06.27 10:01:52.758 Símbolo Forex maior EURUSD
2019.06.27 10:01:52.758 Símbolo Forex menor USDCZK
2019.06.27 10:01:52.758 Símbolo Forex maior USDCAD
2019.06.27 10:01:52.758 Símbolo Forex menor USDZAR
2019.06.27 10:01:52.758 Símbolo Forex menor USDSEK
2019.06.27 10:01:52.758 Símbolo Forex maior AUDUSD
2019.06.27 10:01:52.758 Símbolo Forex menor USDDKK
2019.06.27 10:01:52.758 Símbolo Forex maior NZDUSD
2019.06.27 10:01:52.759 Símbolo Forex menor USDPLN
2019.06.27 10:01:52.759 Símbolo Forex maior GBPUSD
2019.06.27 10:01:52.759 Símbolo Forex USDRUR
2019.06.27 10:01:52.759 Símbolo Forex exótico USDMXN
2019.06.27 10:01:52.759 Símbolo Forex USDHUF
2019.06.27 10:01:52.759 Símbolo Forex menor USDTRY
2019.06.27 10:01:52.759 Símbolo Forex menor USDHKD
2019.06.27 10:01:52.760 Símbolo Forex maior USDCHF
2019.06.27 10:01:52.760 Símbolo Forex menor USDSGD

Agora verificamos a busca de valores definidos na coleção de símbolos.
Renomeamos o EA e o salvamos com o novo nome \MQL5\Experts\TestDoEasy\ Part15\TestDoEasyPart15_2.mq5.

No manipulador OnInit(), alteramos o código para verificar rapidamente a lista de símbolos da coleção. Excluímos a lista de símbolos no log, deixando apenas a atualização de dados de símbolos de coleção e adicionamos as linhas para obter o swap máximo e mínimo das posições longas e curtas, e a dispersão máxima e mínima dos símbolos na coleção. Exibimos os dados resultantes no log:

//--- Configurando parâmetros da classe de negociação CTrade
#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
#endif 
//--- Verificação rápida da coleção de objetos-símbolos
   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();
        }
     }
//--- Obtendo valores máximo e mínimo
   //--- obtemos as propriedades da conta atual (precisamos do número de casas decimais para a moeda da conta)
   CAccount *account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      int index_min=0, index_max=0, dgc=(int)account.CurrencyDigits();
      //--- Se o trabalho com a janela "Observação do mercado", na lista deixamos apenas os símbolos visíveis
      if(InpModeUsedSymbols==SYMBOLS_MODE_MARKET_WATCH)
         list=CSelect::BySymbolProperty(list,SYMBOL_PROP_VISIBLE,true,EQUAL);
      
      //--- swap mínimo/máximo de posições longas
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SWAP_LONG);  // índice do símbolo na lista de coleções que contém o valor mínimo de swap para as posições longas
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SWAP_LONG);  // índice do símbolo na lista de coleções que contém o valor máximo de swap para as posições longas
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Swap mínimo para as posições longas do símbolo ",symbol.Name()," = ",NormalizeDouble(symbol.SwapLong(),dgc));
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Swap máximo para as posições longas do símbolo ",symbol.Name()," = ",NormalizeDouble(symbol.SwapLong(),dgc));
        }
      
      //--- swap mínimo/máximo de posições curtas
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SWAP_SHORT); // índice do símbolo na lista de coleções que contém o valor mínimo de swap para as posições cortas
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SWAP_SHORT); // índice do símbolo na lista de coleções que contém o valor máximo de swap para as posições cortas
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Swap mínimo para as posições curtas do símbolo  ",symbol.Name()," = ",NormalizeDouble(symbol.SwapShort(),dgc));
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Swap máximo para as posições curtas do símbolo ",symbol.Name()," = ",NormalizeDouble(symbol.SwapShort(),dgc));
        }
      
      // --- spread mínimo/máximo
      index_min=CSelect::FindSymbolMin(list,SYMBOL_PROP_SPREAD);     // índice do símbolo na lista de coleções que contém o valor mínimo de spread
      index_max=CSelect::FindSymbolMax(list,SYMBOL_PROP_SPREAD);     // índice do símbolo na lista de coleções que contém o valor máximo de spread
      if(index_max!=WRONG_VALUE && index_min!=WRONG_VALUE)
        {
         symbol=list.At(index_min);
         if(symbol!=NULL)
            Print("Spread mínimo do símbolo ",symbol.Name()," = ",symbol.Spread());
         symbol=list.At(index_max);
         if(symbol!=NULL)
            Print("Spread máximo do símbolo ",symbol.Name()," = ",symbol.Spread());
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Compilamos e executamos o EA no gráfico do terminal. Nas configurações, optamos por trabalhar com a lista completa de símbolos no servidor. Depois de criar a lista completa de coleção de símbolos no servidor (levará algum tempo), o log exibirá informações sobre o swap máximo e mínimo de posições longas e curtas e sobre a distribuição máxima e mínima de todos os símbolos na lista de coleção de símbolos:

2019.06.27 10:36:28.885 Swap mínimo para as posições longas do símbolo USDZAR = -192.9
2019.06.27 10:36:28.885 Swap máximo para as posições longas do símbolo USDMXN = 432.7
2019.06.27 10:36:28.886 Swap mínimo para as posições curtas do símbolo XAUUSD = -17.8
2019.06.27 10:36:28.886 Swap máximo para as posições curtas do símbolo USDMXN = 200.0
2019.06.27 10:36:28.886 Spread mínimo para o símbolo SU52001RMFS3 = 0
2019.06.27 10:36:28.886 Spread máximo para o símbolo GBPRUB_TOM = 3975


O que vem agora?

No próximo artigo, desenvolveremos uma classe de evento de coleção de símbolos.

Abaixo estão anexados todos os arquivos da versão atual da biblioteca e os arquivos do EA de teste. Você pode baixá-los e testar tudo sozinho.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.

Complementos

Artigos desta série:

Parte 1. Conceito, gerenciamento de dados e primeiros resultados
Parte 2. Coleção do histórico de ordens e negócios
Parte 3. Coleção de ordens e posições de mercado, busca e ordenação
Parte 4. Eventos de Negociação. Conceito
Parte 5. Classes e coleções de eventos de negociação. Envio de eventos para o programa
Parte 6. Eventos da conta netting
Parte 7. Eventos de ativação da ordem stoplimit, preparação da funcionalidade para os eventos de modificação de ordens e posições
Parte 8. Eventos de modificação de ordens e posições
Parte 9. Compatibilidade com a MQL4 - preparação dos dados
Parte 10. Compatibilidade com a MQL4 - eventos de abertura de posição e ativação de ordens pendentes
Parte 11. Compatibilidade com a MQL4 - eventos de encerramento de posição
Parte 12. Implementação da classe de objeto "conta" e da coleção de objetos da conta
Parte 13. Eventos do objeto conta
Parte 14. O objeto símbolo
  


Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/7041

Arquivos anexados |
MQL5.zip (207.05 KB)
MQL4.zip (206.85 KB)
Estudo dos padrões de Merrill Estudo dos padrões de Merrill

Neste artigo, nós examinaremos o modelo de padrões de Merrill e tentaremos avaliar qual a sua relevância atual. Para isso, nós desenvolveremos uma ferramenta para testar tais padrões e aplicar o modelo a vários tipos de dados, como preços de fechamento, máxima e mínima, além dos osciladores.

Biblioteca para desenvolvimento fácil e rápido dos programas MetaTrader (parte XIV): O objeto Símbolo Biblioteca para desenvolvimento fácil e rápido dos programas MetaTrader (parte XIV): O objeto Símbolo

Neste artigo, nós criaremos a classe de objeto símbolo que deve ser o objeto base para a criação da coleção de símbolos. A classe nos permitirá os obter dados sobre os símbolos necessários para futuras análises e comparações.

Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XVI): eventos de coleção de símbolos Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XVI): eventos de coleção de símbolos

No artigo, criaremos uma nova classe base - para todos os objetos da biblioteca - que adicionará funcionalidade de evento a todos os seus herdeiros, bem como uma classe para rastrear eventos de uma coleção de símbolos com base numa classe base nova. Além disso, alteraremos as classes e os eventos de conta para operarem sob a nova funcionalidade do objeto base.

Bova abordagem para interpretar a divergência clássica e oculta. Parte II Bova abordagem para interpretar a divergência clássica e oculta. Parte II

Neste artigo, examinaremos criticamente a divergência clássica e analisaremos a eficácia de vários indicadores. Também oferecemos variantes de filtragem para aumentar a precisão da análise e continuar a considerar soluções não padrão. Como resultado, criaremos uma ferramenta atípica para resolver a tarefa em questão.