English Русский Español
preview
Gráficos do índice do dólar e do índice do euro — exemplo de serviço no MetaTrader 5

Gráficos do índice do dólar e do índice do euro — exemplo de serviço no MetaTrader 5

MetaTrader 5Exemplos | 11 fevereiro 2025, 09:47
330 0
Artyom Trishkin
Artyom Trishkin

Conteúdo


Introdução

O índice do dólar americano (U.S. Dollar Index) é o mais conhecido e principal índice do mercado de câmbio. Ele permite prever os movimentos das taxas de câmbio e é um indicador essencial do valor relativo da moeda. O USDX existe desde março de 1973, quando seu valor base foi estabelecido em 100 pontos. Ou seja, um índice de 90 pontos hoje indicaria uma desvalorização de 10% do dólar em relação ao valor de 1973, enquanto um índice de 110 pontos representaria uma valorização de 10%.

O índice do dólar foi introduzido em 1973, após a Conferência Internacional da Jamaica, que estabeleceu as taxas de câmbio flutuantes das moedas incluídas no índice. Desde então, o índice é calculado constantemente com base nos dados de negociação cambial fornecidos pelos 500 maiores bancos do mundo. Em 1999, a metodologia de cálculo do índice foi alterada devido à introdução do euro como moeda única europeia, que substituiu diversas moedas nacionais, incluindo o marco alemão.

O USDX é calculado como uma média geométrica ponderada de uma cesta de moedas significativas no mercado global. Cada moeda na cesta pertence ao grupo dos seis principais parceiros comerciais dos EUA, com diferentes níveis de influência econômica. Assim, cada moeda no índice tem um peso específico:

Moeda
Peso
Euro (EUR)
0.576 (57.6%)
Iene japonês (JPY)
0.136 (13.6%)
Libra esterlina (GBP)
0.119 (11.9%)
Dólar canadense (CAD)
0.091 (9.1%)
Coroa sueca (SEK)
0.042 (4.2%)
Franco suíço (CHF)
0.036 (3.6%)

Fórmula de cálculo do índice do dólar americano:

USDX = 50.14348112 * EURUSD^(-0.576) * USDJPY^(0.136) * GBPUSD^(-0.119) * USDCAD^(0.091) * USDSEK^(0.042) * USDCHF^(0.036) 

Os expoentes utilizados no cálculo, aplicados às taxas de câmbio, correspondem ao peso das moedas na cesta utilizada. O coeficiente 50,14348112 ajusta o índice do dólar para 100,0 caso as taxas de câmbio de março de 1973 sejam inseridas na fórmula. Assim, o valor atual do USDX reflete a variação do preço do dólar americano em relação à cesta de moedas, considerando as cotações de 1973. Um índice abaixo de 100 pontos indica uma desvalorização do dólar, enquanto um índice acima de 100 pontos representa um aumento no valor da moeda americana em relação a 1973.

O índice do euro (Euro Currency Index) é um indicador médio da variação das taxas de câmbio de cinco moedas globais (dólar dos EUA, libra esterlina, iene japonês, franco suíço e coroa sueca) em relação ao euro.

Como ativo de negociação, o índice do euro (EURX) foi introduzido em 13 de janeiro de 2006 na bolsa New York Board of Trade (NYBOT), com os códigos ECX, EURX ou E.

O índice do euro tornou-se um "padrão" de referência para o valor atual da moeda única europeia para os participantes dos mercados financeiros internacionais, além de servir como ferramenta para operações de negociação.

O cálculo do índice do euro com base na cesta de cinco moedas utiliza os mesmos dados empregados pelo Banco Central Europeu ao calcular o índice de câmbio ponderado do euro em relação às moedas dos países que compõem o principal comércio exterior da Zona do Euro. A maior parte do comércio internacional desses países é realizada com os Estados Unidos (31,55%), seguidos pelo Reino Unido (30,56%), Japão (18,91%), Suíça (11,13%) e Suécia (7,85%).

Os princípios básicos do cálculo do valor atual do índice do euro são semelhantes aos aplicados no cálculo do índice do dólar americano (USDX). O índice do euro é calculado utilizando o método da média geométrica ponderada:

EURX = 34.38805726 * EURUSD^(0.3155) * EURGBP^(0.3056) * EURJPY^(0.1891) * EURCHF^(0.1113) * EURSEK^(0.0785)

onde os expoentes correspondem aos pesos das moedas na cesta utilizada.


Definição do problema

Então, o que queremos obter no final... Precisamos criar um instrumento sintético, cujo preço seja calculado de acordo com as fórmulas apresentadas acima. Precisamos de um gráfico completo desse instrumento, que seja atualizado a cada novo tick nos símbolos que compõem a cesta de instrumentos e no qual possamos executar qualquer indicador, script ou EA.

Em suma, precisamos criar um gráfico de um instrumento sintético que seja praticamente indistinguível dos gráficos de instrumentos padrão. Para isso, um programa-serviço é adequado, pois executará todas as operações em sua própria thread, independentemente de outros gráficos abertos e programas em execução.

Ao iniciar o serviço, verificaremos a presença do instrumento sintético necessário, criaremos um caso ele não exista e o adicionaremos à janela "Observação do Mercado". Em seguida, será gerado o histórico do instrumento sintético — tanto o de minutos quanto o de ticks — e o gráfico do instrumento criado será aberto. Após essas operações, o serviço começará a receber novos ticks de cada um dos símbolos usados no cálculo do preço do instrumento, que serão adicionados ao histórico do símbolo personalizado criado. Se o terminal for reiniciado, o programa-serviço será iniciado automaticamente, desde que estivesse em execução no momento do fechamento do terminal. Dessa forma, um serviço iniciado uma única vez será sempre reiniciado automaticamente ao abrir o terminal.

Serão criados dois desses símbolos personalizados: o índice do dólar americano e o índice do euro. Para cada um desses instrumentos, será desenvolvido um programa-serviço específico. No entanto, ambos serão baseados em um único arquivo incluído, no qual todas as funções necessárias para a criação dos instrumentos serão reunidas. No programa-serviço, serão especificados apenas o tamanho da cesta para cálculo do índice, o coeficiente base e a estrutura dos símbolos com seus respectivos pesos. Todo o restante será processado dentro do arquivo incluído por meio das funções nele definidas. Isso permitirá que, com base nas funções criadas, seja possível construir índices personalizados com diferentes combinações de moedas e pesos, bastando apenas especificar a lista de símbolos e o peso de cada um.


Escrevendo o arquivo incluído de funções para cálculo dos índices de moedas

Escrevendo o arquivo incluído de funções para cálculo dos índices de moedas No diretório do terminal \MQL5\Services\, criaremos uma nova pasta chamada Indexes e, dentro dela, um novo arquivo incluído com o nome CurrencyIndex.mqh. Esse arquivo conterá todas as funções necessárias para o funcionamento do projeto.

No início do arquivo, adicionaremos as macros, estruturas e enumerações essenciais para a execução do código:

//+------------------------------------------------------------------+
//|                                                CurrencyIndex.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"

#define SECONDS_IN_DAY    (24*60*60)   // number of seconds in a day
#define SECONDS_IN_MINUTE 60           // number of seconds in a minute
#define MSECS_IN_MINIUTE  (60*1000)    // number of milliseconds in a minute

//--- basket symbol structure
struct SymbolWeight
  {
    pair            symbol;           // symbol
   double            weight;           // weight
  };
  
//--- historical data structure
struct str_rates
  {
   int               index;            // data index
   MqlRates          rates[];          // array of historical data
  };
  
//--- tick data structure
struct str_ticks
  {
   int               index;            // data index
   MqlTick           ticks[];          // array of ticks
  };
  
//--- enumeration of price types
enum ENUM_RATES_VALUES
  {
   VALUE_OPEN,                         // Open price
   VALUE_HIGH,                         // High price
   VALUE_LOW,                          // Low price
   VALUE_CLOSE                         // Close price
  };

int ExtDigits=5;                       // symbol price measurement accuracy

Na estrutura do símbolo da cesta, serão definidos dois campos: o nome do símbolo e seu peso na cesta de instrumentos. Para construir a cesta de instrumentos para o cálculo do índice, é conveniente utilizar um array dessas estruturas. Durante a inicialização, os nomes dos símbolos e seus respectivos pesos serão armazenados nesse array. Posteriormente, dentro de um laço, o símbolo e seu peso serão extraídos para o cálculo dos preços do índice. Esse método permite que qualquer conjunto de símbolos e pesos seja armazenado no array, proporcionando flexibilidade para a criação de diversas cestas de instrumentos para o cálculo de índices.

Nas estruturas de dados históricos e de ticks, são utilizados arrays das respectivas estruturas: MqlRates e MqlTick. Esses arrays contêm os dados de cada um dos símbolos da cesta de instrumentos. Além disso, há um índice de dados em cada uma dessas estruturas. Esse índice é necessário para indicar a barra de onde os dados serão extraídos para o cálculo do índice. Por exemplo, para calcular o índice em uma barra específica, é necessário que todos os símbolos da cesta de instrumentos tenham dados nessa barra de um minuto.

No entanto, nem sempre haverá dados disponíveis para cada símbolo, pois podem ocorrer lacunas nas barras (se, por exemplo, não houver ticks para um símbolo específico nesse minuto). Nesse caso, o índice da barra é ajustado, sendo incrementado para obter os dados da barra anterior. Como não temos conhecimento antecipado da quantidade de símbolos na cesta de instrumentos para o cálculo do índice, não podemos declarar previamente no programa a quantidade necessária de índices para cada instrumento. Por isso, a abordagem mais conveniente é armazená-los e utilizá-los dentro dessa estrutura.

Na enumeração dos tipos de preços, apenas são definidas constantes que indicam qual preço deve ser usado para o cálculo dos preços das barras.

Ao iniciar o serviço, o primeiro passo é criar o símbolo personalizado, construir as barras históricas M1 de um mês e armazenar o histórico de ticks. Isso constitui a inicialização do serviço. Escreveremos uma função para essa finalidade.

//+------------------------------------------------------------------+
//| Initializing the service                                         |
//+------------------------------------------------------------------+
bool InitService(const string custom_symbol,const string custom_group)
  {
   MqlRates rates[100];
   MqlTick  ticks[100];
   
//--- initialize the custom symbol
   if(!CustomSymbolInitialize(custom_symbol,custom_group))
      return(false);
   ExtDigits=(int)SymbolInfoInteger(custom_symbol,SYMBOL_DIGITS);
   
//--- we make active all symbols of the instrument basket that participate in the index calculation
   for(int i=0; i<BASKET_SIZE; i++)
     {
      //--- select a symbol in the Market Watch window
      if(!SymbolSelect(ExtWeights[i].symbol,true))
        {
         PrintFormat("cannot select symbol %s",ExtWeights[i].symbol);
         return(false);
        }
      //--- request historical data of bars and ticks for the selected symbol
      CopyRates(ExtWeights[i].symbol,PERIOD_M1,0,100,rates);
      CopyTicks(ExtWeights[i].symbol,ticks,COPY_TICKS_ALL,0,100);
     }
     
//--- build M1 bars for 1 month
   if(!PrepareRates(custom_symbol))
      return(false);
      
//--- get the last ticks after building M1 bars
   PrepareLastTicks(custom_symbol);
   
//--- service initialized
   Print(custom_symbol," datafeed started");
   return(true);
  }

A verificação da existência e a criação do símbolo personalizado são realizadas na função CustomSymbolInitialize():

//+------------------------------------------------------------------+
//| Initialize a custom symbol                                       |
//+------------------------------------------------------------------+
bool CustomSymbolInitialize(string symbol,string group)
  {
   bool is_custom=false;
//--- if a symbol is selected in the Market Watch window, we get a flag that this is a custom symbol
   bool res=SymbolSelect(symbol,true);
   if(res)
      is_custom=(bool)SymbolInfoInteger(symbol,SYMBOL_CUSTOM);

//--- if the selected symbol is not custom, create it
   if(!res)
     {
      if(!CustomSymbolCreate(symbol,group,"EURUSD"))
        {
         Print("cannot create custom symbol ",symbol);
         return(false);
        }
      //--- the symbol was successfully created - set the custom symbol flag
      is_custom=true;
      
      //--- place the created symbol in the Market Watch window
      if(!SymbolSelect(symbol,true))
        {
         Print("cannot select custom symbol ",symbol);
         return(false);
        }
     }
     
//--- open the chart of the created custom symbol
   if(is_custom)
     {
      //--- get the ID of the first window of open charts
      long chart_id=ChartFirst();
      bool found=false;
      //--- in the loop through the list of open charts, find the chart of the created custom symbol
      while(chart_id>=0)
        {
         //--- if the chart is open, report this to the journal, set the flag of the chart found and exit the search loop
         if(ChartSymbol(chart_id)==symbol)
           {
            found=true;
            Print(symbol," chart found");
            break;
           }
         //--- based on the currently selected chart, get the ID of the next one for the next iteration of the search in the loop
         chart_id=ChartNext(chart_id);
        }
      
      //--- if the symbol chart is not found among the open charts
      if(!found)
        {
         //--- report about opening of M1 chart of a custom symbol,
         //--- get the chart ID and move on to it
         Print("open chart ",symbol,",M1");
         chart_id=ChartOpen(symbol,PERIOD_M1);
         ChartSetInteger(chart_id,CHART_BRING_TO_TOP,true);
        }
     }
//--- user symbol initialized
   return(is_custom);
  }

Aqui, verificamos se já existe um símbolo personalizado com o nome especificado. Se não existir, ele será criado. Em seguida, procuramos um gráfico aberto desse símbolo e, caso o gráfico não seja encontrado entre os abertos no terminal, abrimos um novo gráfico para ele.

Após criar o símbolo personalizado e abrir seu gráfico, é necessário construir o histórico de período M1 de um mês. Isso é feito por meio da função PrepareRates():

//+------------------------------------------------------------------+
//| Preparing historical data                                        |
//+------------------------------------------------------------------+
bool PrepareRates(const string custom_symbol)
  {
   str_rates symbols_rates[BASKET_SIZE];
   int       i,reserve=0;
   MqlRates  usdx_rates[];                                              // array timeseries of a synthetic instrument
   MqlRates  rate;                                                      // synthetic instrument single bar data
   datetime  stop=(TimeCurrent()/SECONDS_IN_MINUTE)*SECONDS_IN_MINUTE;  // M1 bar time of the end date
   datetime  start=stop-31*SECONDS_IN_DAY;                              // initial date M1 bar time
   datetime  start_date=0;
   
//--- copy M1 historical data for a month for all symbols of the instrument basket
   start/=SECONDS_IN_DAY;
   start*=SECONDS_IN_DAY;                                               // initial date D1 bar time
   for(i=0; i<BASKET_SIZE; i++)
     {
      if(CopyRates(ExtWeights[i].symbol,PERIOD_M1,start,stop,symbols_rates[i].rates)<=0)
        {
         PrintFormat("cannot copy rates for %s,M1 from %s to %s [%d]",ExtWeights[i].symbol,TimeToString(start),TimeToString(stop),GetLastError());
         return(false);
        }
      PrintFormat("%u %s,M1 rates from %s",ArraySize(symbols_rates[i].rates),ExtWeights[i].symbol,TimeToString(symbols_rates[i].rates[0].time));
      symbols_rates[i].index=0;
      //--- find and set the minimum non-zero start date from the symbol basket
      if(start_date<symbols_rates[i].rates[0].time)
         start_date=symbols_rates[i].rates[0].time;
     }
   Print("start date set to ",start_date);
   
//--- reserve of historical data array to avoid memory reallocation when changing array size
   reserve=int(stop-start)/60;
   
//--- set the start of all historical data of the symbol basket to a single date (start_date)
   for(i=0; i<BASKET_SIZE; i++)
     {
      int j=0;
      //--- as long as j is less than the amount of data in the 'rates' array and
      //--- time at j index in the array is less than start_date time - increase the index
      while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<start_date)
         j++;
      //--- if the index was increased and it is within the 'rates' array, decrease it by 1 to compensate for the last increment
      if(j>0 && j<ArraySize(symbols_rates[i].rates))
         j--;
      //--- write the received index into the structure
      symbols_rates[i].index=j;
     }
      
//--- USD index timeseries
   int    array_size=0;
   
//--- first bar of M1 time series
   rate.time=start_date;
   rate.real_volume=0;
   rate.spread=0;

//--- as long as the bar time is less than the end date time of the M1 timeseries
   while(!IsStopped() && rate.time<stop)
     {
      //--- if the historical data of the instrument bar is calculated
      if(CalculateRate(rate,symbols_rates))
        {
         //--- increase the timeseries array by 1 and add the calculated data to it
         ArrayResize(usdx_rates,array_size+1,reserve);
         usdx_rates[array_size]=rate;
         array_size++;
         //--- reset the size of the array size backup value since it is only applied during the first resize
         reserve=0;
        }
      
      //--- next bar of the M1 timeseries
      rate.time+=PeriodSeconds(PERIOD_M1);
      start_date=rate.time;
      
      //--- in the loop through the list of basket instruments
      for(i=0; i<BASKET_SIZE; i++)
        {
         //--- get the current data index
         int j=symbols_rates[i].index;
         //--- while j is within the timeseries data and if the time of the bar at index j is less than the time set for this bar in rate.time, increase j
         while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<rate.time)
            j++;
         //--- if j is within the timeseries data and the time in start_date is less than the time of the timeseries data by j index
         //--- and the time in the timeseries at index j is less than or equal to the time in rate.time - write the time from the timeseries at index j to start_date
         if(j<ArraySize(symbols_rates[i].rates) && start_date<symbols_rates[i].rates[j].time && symbols_rates[i].rates[j].time<=rate.time)
            start_date=symbols_rates[i].rates[j].time;
        }
      
      //--- in the loop through the list of basket instruments
      for(i=0; i<BASKET_SIZE; i++)
        {
         //--- get the current data index
         int j=symbols_rates[i].index;
         //--- while j is within the timeseries data and if the time of the bar at index j is less than the time set for this bar in start_date, increase j
         while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<=start_date)
            symbols_rates[i].index=j++;
        }
      //--- in rate.time, set the time from start_date for the next bar
      rate.time=start_date;
     }
     
//--- add the created timeseries to the database
   if(array_size>0)
     {
      if(!IsStopped())
        {
         int cnt=CustomRatesReplace(custom_symbol,usdx_rates[0].time,usdx_rates[ArraySize(usdx_rates)-1].time+1,usdx_rates);
         Print(cnt," ",custom_symbol,",M1 rates from ",usdx_rates[0].time," to ",usdx_rates[ArraySize(usdx_rates)-1].time," added");
        }
     }
//--- successful
   return(true);
  }

Nessa função, os dados de cada um dos símbolos da cesta de instrumentos são copiados primeiro, e o tempo inicial da cópia dos dados é definido de forma geral. Em seguida, o índice de dados de cada instrumento é ajustado para que o tempo corresponda ao dos demais símbolos da cesta. Isso garante que a data inicial seja a mesma para todos os símbolos da cesta. Se algum símbolo não possuir uma barra na data inicial, o índice da barra mais próxima anterior, onde haja dados, é utilizado.

Em seguida, dentro de um laço, cada barra da série temporal do instrumento sintético é calculada, e o índice de cada barra subsequente é ajustado para garantir que os dados sejam obtidos da barra atual, se houver, ou da barra anterior, caso não existam dados na barra atual. As barras calculadas são adicionadas ao conjunto de barras da série de barras do instrumento sintético. Após o cálculo de todas as barras da série de barras do instrumento, a série finalizada é adicionada ao histórico de preços do símbolo personalizado.

Os dados históricos de uma única barra do instrumento sintético são calculados na função CalculateRate():

//+------------------------------------------------------------------+
//| Calculation of prices and volumes of synthetic instruments       |
//+------------------------------------------------------------------+
bool CalculateRate(MqlRates& rate,str_rates& symbols_rates[])
  {
   double values[BASKET_SIZE]={0};
   long   tick_volume=0;
   int    i;
//--- get Open prices of all symbols of the instrument basket into the values[] array
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_OPEN);

//--- if the tick volume is zero, then there is no data for this minute - return 'false'
   if(tick_volume==0)
      return(false);
      
//--- write down the total volume of all timeseries
   rate.tick_volume=tick_volume;
   
//--- calculate the Open price based on the prices and weights of all instruments in the basket
   rate.open=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.open*=MathPow(values[i],ExtWeights[i].weight);
      
//--- calculate the High price based on the prices and weights of all instruments in the basket
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_HIGH);
   rate.high=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.high*=MathPow(values[i],ExtWeights[i].weight);
      
//--- calculate the Low price based on the prices and weights of all instruments in the basket
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_LOW);
   rate.low=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.low*=MathPow(values[i],ExtWeights[i].weight);
      
//--- calculate the Close price based on the prices and weights of all instruments in the basket
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_CLOSE);
   rate.close=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.close*=MathPow(values[i],ExtWeights[i].weight);
      
//--- return the result of checking prices for validity
   return(CheckRate(rate));
  }

Para cada um dos preços da barra (Open, High, Low e Close) do instrumento sintético, o cálculo dos preços é realizado com base na fórmula do instrumento sintético:

Open USDX = 50.14348112 * Open EURUSD^(-0.576) * Open USDJPY^(0.136) * Open GBPUSD^(-0.119) * Open USDCAD^(0.091) * Open USDSEK^(0.042) * Open USDCHF^(0.036);
High USDX = 50.14348112 * High EURUSD^(-0.576) * High USDJPY^(0.136) * High GBPUSD^(-0.119) * High USDCAD^(0.091) * High USDSEK^(0.042) * High USDCHF^(0.036);
Low  USDX = 50.14348112 * Low  EURUSD^(-0.576) * Low  USDJPY^(0.136) * Low  GBPUSD^(-0.119) * Low  USDCAD^(0.091) * Low  USDSEK^(0.042) * Low  USDCHF^(0.036);
CloseUSDX = 50.14348112 * CloseEURUSD^(-0.576) * CloseUSDJPY^(0.136) * CloseGBPUSD^(-0.119) * CloseUSDCAD^(0.091) * CloseUSDSEK^(0.042) * CloseUSDCHF^(0.036);

Os preços de cada símbolo da cesta são obtidos na função GetRateValue()

//+------------------------------------------------------------------+
//| Return the specified bar price                                   |
//+------------------------------------------------------------------+
double GetRateValue(long &tick_volume,str_rates &symbol_rates,datetime time,ENUM_RATES_VALUES num_value)
  {
   double value=0;                  // obtained value
   int    index=symbol_rates.index; // data index
   
//--- if the index is within the timeseries
   if(index<ArraySize(symbol_rates.rates))
     {
      //--- depending on the type of requested data, set the corresponding value to the 'value' variable
      switch(num_value)
        {
         //--- Open price
         case VALUE_OPEN:
            if(symbol_rates.rates[index].time<time)
               value=symbol_rates.rates[index].close;
            else
              {
               if(symbol_rates.rates[index].time==time)
                 {
                  value=symbol_rates.rates[index].open;
                  //--- when requesting the Open price, add the tick volume to the tick_volume variable passed by reference,
                  //--- to get the total volume of all symbols in the instrument basket
                  tick_volume+=symbol_rates.rates[index].tick_volume;
                 }
              }
            break;
         //--- High price
         case VALUE_HIGH:
            if(symbol_rates.rates[index].time<time)
               value=symbol_rates.rates[index].close;
            else
              {
               if(symbol_rates.rates[index].time==time)
                  value=symbol_rates.rates[index].high;
              }
            break;
         //--- Low price
         case VALUE_LOW:
            if(symbol_rates.rates[index].time<time)
               value=symbol_rates.rates[index].close;
            else
              {
               if(symbol_rates.rates[index].time==time)
                  value=symbol_rates.rates[index].low;
              }
            break;
         //--- Close price
         case VALUE_CLOSE:
            if(symbol_rates.rates[index].time<=time)
               value=symbol_rates.rates[index].close;
            break;
        }
     }
     
//--- return the received value
   return(value);
  }

A função CalculateRate() retorna o resultado da verificação dos preços calculados para o instrumento sintético:

//--- return the result of checking prices for validity
   return(CheckRate(rate));

Como todos os preços da barra do instrumento sintético são calculados, é necessário verificar a validade dos cálculos e corrigir eventuais erros, caso existam.

Todo esse processo é realizado na função CheckRate(), cujo resultado é retornado:

//+------------------------------------------------------------------+
//| Check prices for validity and return the check result            |
//+------------------------------------------------------------------+
bool CheckRate(MqlRates &rate)
  {
//--- if prices are not valid real numbers, or are less than or equal to zero - return 'false'
   if(!MathIsValidNumber(rate.open) || !MathIsValidNumber(rate.high) || !MathIsValidNumber(rate.low) || !MathIsValidNumber(rate.close))
      return(false);
   if(rate.open<=0.0 || rate.high<=0.0 || rate.low<=0.0 || rate.close<=0.0)
      return(false);
      
//--- normalize prices to the required number of digits
   rate.open=NormalizeDouble(rate.open,ExtDigits);
   rate.high=NormalizeDouble(rate.high,ExtDigits);
   rate.low=NormalizeDouble(rate.low,ExtDigits);
   rate.close=NormalizeDouble(rate.close,ExtDigits);
   
//--- adjust prices if necessary
   if(rate.high<rate.open)
      rate.high=rate.open;
   if(rate.low>rate.open)
      rate.low=rate.open;
   if(rate.high<rate.close)
      rate.high=rate.close;
   if(rate.low>rate.close)
      rate.low=rate.close;
      
//--- all is fine
   return(true);
  }

Se qualquer um dos preços calculados for positivo ou negativo infinito, ou for um valor "não numérico" (NaN — not a number), ou ainda for menor ou igual a zero, a função retorna false.

Em seguida, todos os preços são normalizados com a precisão definida para o símbolo, e a validade dos preços Open, High, Low e Close é verificada em relação uns aos outros. Se necessário, os preços são ajustados e a função retorna true.

Na função InitService() mencionada acima, após a construção das barras de um minuto referentes ao último mês, os últimos dados de ticks do instrumento sintético são obtidos e a função retorna true:

undefined

Os dados de ticks são obtidos na função PrepareLastTicks():

undefined

A lógica da função está detalhada nos comentários do código. Primeiro, as duas últimas barras do instrumento são recuperadas e utilizadas para substituir os dados de seu histórico. Em seguida, os dados de ticks são coletados a partir do início da barra anterior, os valores de tick são calculados e adicionados ao array da série temporal do instrumento. Após o cálculo de todos os ticks, os dados históricos de ticks do instrumento são substituídos pelos valores contidos no array de ticks preenchido.

Os dados do array de ticks usados no cálculo do tick do instrumento sintético são obtidos por meio da função GetTickValue():

undefined

Após a inicialização bem-sucedida do serviço, ele deve, em um laço contínuo, obter os dados de todos os símbolos da cesta de instrumentos e atualizar o histórico de barras e ticks do instrumento, simulando em tempo real o fluxo de dados do símbolo personalizado. Todo esse processo ocorre na função ProcessTick():

undefined

Aqui, dentro de um laço, os ticks de todos os instrumentos da cesta são coletados, enquanto o tempo do tick é calculado simultaneamente. O tempo do tick é definido como o tempo do último tick recebido de todos os símbolos da cesta do instrumento. Se forem recebidos ticks de todos os instrumentos da cesta, o preço Bid do tick do instrumento sintético é calculado de acordo com a fórmula do índice. Após o cálculo do preço Bid, o preço Ask é definido com o mesmo valor do preço Bid calculado para o instrumento sintético. Ao final de todos os cálculos, os dados do array de ticks synth_tick são adicionados ao histórico de preços do símbolo personalizado, simulando a chegada de um novo tick do instrumento sintético.

Todas as funções para a criação dos índices de moedas e para a manipulação de seus dados estão prontas. Agora, com base nessas funções, escreveremos os programas-serviço para criar os índices do dólar americano e do euro.


Testando os programas-serviço

Na pasta onde o arquivo das funções incluídas foi criado (MQL5\Services\Indexes), criaremos um novo arquivo de programa do tipo Serviço com o nome USD_Index.mq5.

Definiremos imediatamente as macros para o nome do símbolo personalizado e seu grupo. Indicaremos a quantidade de símbolos na cesta de instrumentos e o coeficiente base.

Em seguida, incluiremos o arquivo das funções criadas anteriormente para trabalhar com os índices de moedas e definiremos a estrutura da cesta de símbolos, especificando os nomes de todos os símbolos e seus respectivos pesos:

undefined

Agora, no único manipulador de serviço OnStart(), inicializaremos o símbolo personalizado e iniciaremos o processamento dos novos ticks recebidos em um laço infinito:

undefined

Compilamos o arquivo do serviço e o executamos:


Após adicionar o serviço e iniciá-lo, no log do terminal aparecerão as mensagens:

undefined

O gráfico do instrumento sintético criado será aberto e passará a ser atualizado como um gráfico comum de um símbolo padrão. Nele, será possível adicionar indicadores e realizar qualquer tipo de análise técnica:


Agora, criaremos o serviço para a criação e operação do gráfico do índice do euro.

Na pasta \MQL5\Services\Indexes, criaremos um novo arquivo de programa do tipo Serviço com o nome EUR_Index.mq5:

undefined

A única diferença em relação ao arquivo do serviço do índice do dólar americano está no tamanho da cesta de símbolos, no coeficiente base e na estrutura da cesta de símbolos com seus pesos.

Compilamos o programa e iniciamos o serviço. No log do terminal, veremos as seguintes entradas:

undefined

Em seguida, o gráfico do índice do euro será aberto, permitindo uma utilização completa:


Considerações finais

Com os programas-serviço apresentados, aprendemos a criar símbolos personalizados e a manter a atualização contínua de seus gráficos. Quando o terminal for reiniciado, todos os serviços que estavam em execução no momento do fechamento do terminal do cliente também serão reiniciados. Dessa forma, um serviço iniciado uma única vez será automaticamente iniciado novamente ao abrir o terminal. Além disso, esses serviços continuarão enviando novos ticks para o gráfico, garantindo que o histórico do instrumento sintético esteja sempre atualizado.

Com base nesses serviços, é possível criar índices personalizados utilizando dados específicos e visualizar claramente a evolução da cesta de instrumentos usada na criação do índice de moedas. Por exemplo, podemos construir o gráfico do índice comercial ponderado do dólar americano (Trade-Weighted Dollar Index — TWDI). A cesta de moedas do TWDI contém 26 símbolos e é atualmente mais relevante do que o índice convencional do dólar americano.

Todos os arquivos deste projeto estão anexados ao artigo e podem ser utilizados como estão ou servir de base para a criação de algo personalizado, conforme a necessidade de cada um, com suas próprias cestas de instrumentos. O arquivo compactado MQL5.zip, também anexado ao artigo, pode ser simplesmente extraído para o diretório de trabalho do terminal MQL5\, e todos os arquivos do projeto serão automaticamente colocados nas subpastas corretas.


Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/15684

Arquivos anexados |
CurrencyIndex.mqh (51.79 KB)
USD_Index.mq5 (1.41 KB)
EUR_Index.mq5 (1.38 KB)
MQL5.zip (8.66 KB)
Redes neurais em trading: Transformer vetorial hierárquico (Conclusão) Redes neurais em trading: Transformer vetorial hierárquico (Conclusão)
Continuaremos a explorar o método Transformer Vetorial Hierárquico. Neste artigo, concluiremos a construção do modelo, realizando seu treinamento e teste em dados históricos reais.
Do básico ao intermediário: Struct (I) Do básico ao intermediário: Struct (I)
Que tal começarmos a estudar estruturas de uma forma bem mais simples, prática e agradável? Isto por que estruturas é um dos fundamentos, ou pilares da programação. Seja ela estruturada ou não. Sei que muitos acham que estruturas são apenas coleções de dados. Mas posso garantir que elas são muito mais do que isto. E aqui iremos começar a explorar este novo universo, de uma maneira que seja a mais didática possível.
Busca com restrições — Tabu Search (TS) Busca com restrições — Tabu Search (TS)
O artigo analisa o algoritmo de busca tabu, um dos primeiros e mais conhecidos métodos meta-heurísticos. Exploraremos detalhadamente como o algoritmo funciona, desde a escolha da solução inicial até a exploração das soluções vizinhas, com foco no uso da lista tabu. O artigo cobre os aspectos-chave do algoritmo e suas particularidades.
Simulação de mercado (Parte 07): Sockets (I) Simulação de mercado (Parte 07): Sockets (I)
Soquetes. Você sabe para que eles servem, ou como fazer uso deles no MetaTrader 5? Se a resposta for não, vamos começar aprendendo um pouco sobre eles. Este artigo aqui envolve o básico do básico. Mas como existem diversas maneiras de se fazer a mesma coisa, e o que nos interessa realmente é sempre o resultado. Queria mostrar que sim, existe uma forma simples, de passar dados do MetaTrader 5 para dentro de outros programas, como por exemplo o Excel. Porém, a principal ideia, não é transferir dados do MetaTrader 5, para o Excel. E sim fazer o contrário. Ou seja, transferir dados do Excel, ou de qualquer outro programa, para dentro do MetaTrader 5.