
Gráficos do índice do dólar e do índice do euro — exemplo de serviço no MetaTrader 5
Conteúdo
- Introdução
- Definição do problema
- Escrevendo o arquivo incluído de funções para cálculo dos índices de moedas
- Testando os programas-serviço
- Considerações finais
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.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/15684





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso