Русский
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
300 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)   // количество секунд в сутках
#define SECONDS_IN_MINUTE 60           // количество секунд в минуте
#define MSECS_IN_MINIUTE  (60*1000)    // количество миллисекунд в минуте

//--- структура символа корзины
struct SymbolWeight
  {
   string            symbol;           // символ
   double            weight;           // вес
  };
  
//--- структура исторических данных
struct str_rates
  {
   int               index;            // индекс данных
   MqlRates          rates[];          // массив исторических данных
  };
  
//--- структура тиковых данных
struct str_ticks
  {
   int               index;            // индекс данных
   MqlTick           ticks[];          // массив тиков
  };
  
//--- перечисление типов цен
enum ENUM_RATES_VALUES
  {
   VALUE_OPEN,                         // цена Open
   VALUE_HIGH,                         // цена High
   VALUE_LOW,                          // цена Low
   VALUE_CLOSE                         // цена Close
  };

int ExtDigits=5;                       // точность измерения цены символа

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.

//+------------------------------------------------------------------+
//| Инициализация сервиса                                            |
//+------------------------------------------------------------------+
bool InitService(const string custom_symbol,const string custom_group)
  {
   MqlRates rates[100];
   MqlTick  ticks[100];
   
//--- инициализируем пользовательский символ
   if(!CustomSymbolInitialize(custom_symbol,custom_group))
      return(false);
   ExtDigits=(int)SymbolInfoInteger(custom_symbol,SYMBOL_DIGITS);
   
//--- делаем активными все символы корзины инструментов, участвующие в расчёте индекса
   for(int i=0; i<BASKET_SIZE; i++)
     {
      //--- выбираем символ в окне "Обзор рынка"
      if(!SymbolSelect(ExtWeights[i].symbol,true))
        {
         PrintFormat("cannot select symbol %s",ExtWeights[i].symbol);
         return(false);
        }
      //--- запрашиваем исторические данные баров и тиков по выбранному символу
      CopyRates(ExtWeights[i].symbol,PERIOD_M1,0,100,rates);
      CopyTicks(ExtWeights[i].symbol,ticks,COPY_TICKS_ALL,0,100);
     }
     
//--- строим M1 бары за 1 месяц
   if(!PrepareRates(custom_symbol))
      return(false);
      
//--- получаем последние тики после построения M1 баров
   PrepareLastTicks(custom_symbol);
   
//--- сервис инициализирован
   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():

//+------------------------------------------------------------------+
//| Инициализация пользовательского символа                          |
//+------------------------------------------------------------------+
bool CustomSymbolInitialize(string symbol,string group)
  {
   bool is_custom=false;
//--- если символ выбран в окне "Обзор рынка", получаем флаг, что это пользовательский символ
   bool res=SymbolSelect(symbol,true);
   if(res)
      is_custom=(bool)SymbolInfoInteger(symbol,SYMBOL_CUSTOM);

//--- если выбранный символ не пользовательский - создаём его
   if(!res)
     {
      if(!CustomSymbolCreate(symbol,group,"EURUSD"))
        {
         Print("cannot create custom symbol ",symbol);
         return(false);
        }
      //--- символ успешно создан - устанавливаем флаг, что это пользовательский символ
      is_custom=true;
      
      //--- помещаем созданный символ в окно "Обзор рынка"
      if(!SymbolSelect(symbol,true))
        {
         Print("cannot select custom symbol ",symbol);
         return(false);
        }
     }
     
//--- откроем график созданного пользовательского символа
   if(is_custom)
     {
      //--- получаем идентификатор первого окна открытых графиков
      long chart_id=ChartFirst();
      bool found=false;
      //--- в цикле по списку открытых графиков найдём график созданного пользовательского символа
      while(chart_id>=0)
        {
         //--- если график открыт - сообщаем об этом в журнал, ставим флаг найденного графика и выходим из цикла поиска
         if(ChartSymbol(chart_id)==symbol)
           {
            found=true;
            Print(symbol," chart found");
            break;
           }
         //--- на основании текущего выбранного графика получаем идентификатор следующего для очередной итерации поиска в цикле
         chart_id=ChartNext(chart_id);
        }
      
      //--- если график символа не найден среди открытых графиков
      if(!found)
        {
         //--- сообщаем об открытии графика M1 пользовательского символа,
         //--- получаем идентификатор открываемого графика и переходим на него 
         Print("open chart ",symbol,",M1");
         chart_id=ChartOpen(symbol,PERIOD_M1);
         ChartSetInteger(chart_id,CHART_BRING_TO_TOP,true);
        }
     }
//--- пользовательский символ инициализирован
   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():

//+------------------------------------------------------------------+
//| Подготовка исторических данных                                   |
//+------------------------------------------------------------------+
bool PrepareRates(const string custom_symbol)
  {
   str_rates symbols_rates[BASKET_SIZE];
   int       i,reserve=0;
   MqlRates  usdx_rates[];                                              // массив-таймсерия синтетического инструмента
   MqlRates  rate;                                                      // данные одного бара синтетического инструмента
   datetime  stop=(TimeCurrent()/SECONDS_IN_MINUTE)*SECONDS_IN_MINUTE;  // время бара M1 конечной даты
   datetime  start=stop-31*SECONDS_IN_DAY;                              // время бара M1 начальной даты
   datetime  start_date=0;
   
//--- копируем исторические данные M1 за месяц для всех символов корзины инструментов
   start/=SECONDS_IN_DAY;
   start*=SECONDS_IN_DAY;                                               // время бара D1 начальной даты
   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;
      //--- находим и устанавливаем минимальную ненулевую начальную дату из корзины символов
      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=int(stop-start)/60;
   
//--- установим начало всех исторических данных корзины символов на одну дату (start_date)
   for(i=0; i<BASKET_SIZE; i++)
     {
      int j=0;
      //--- до тех пор, пока j меньше количества данных в массиве rates и
      //--- время по индексу j в массиве меньше времени start_date - увеличиваем индекс
      while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<start_date)
         j++;
      //--- если индекс был увеличен, и он в пределах массива rates - уменьшим его на 1 для компенсации последнего приращения
      if(j>0 && j<ArraySize(symbols_rates[i].rates))
         j--;
      //--- запишем полученный индекс в структуру
      symbols_rates[i].index=j;
     }
      
//--- таймсерии USD index
   int    array_size=0;
   
//--- первый бар таймсерии M1
   rate.time=start_date;
   rate.real_volume=0;
   rate.spread=0;

//--- до тех пор, пока время бара меньше времени конечной даты таймсерии M1
   while(!IsStopped() && rate.time<stop)
     {
      //--- если исторические данные бара инструмента рассчитаны
      if(CalculateRate(rate,symbols_rates))
        {
         //--- увеличиваем массив-таймсерию на 1 и добавляем в него рассчитанные данные
         ArrayResize(usdx_rates,array_size+1,reserve);
         usdx_rates[array_size]=rate;
         array_size++;
         //--- сбросим размер резервного значения размера массива, так как он применяется только при первом изменении размера
         reserve=0;
        }
      
      //--- следующий бар таймсерии M1
      rate.time+=PeriodSeconds(PERIOD_M1);
      start_date=rate.time;
      
      //--- в цикле по списку инструментов корзины
      for(i=0; i<BASKET_SIZE; i++)
        {
         //--- получаем текущий индекс данных
         int j=symbols_rates[i].index;
         //--- пока j в пределах данных таймсерии и, если время бара по индексу j меньше времени, установленного для этого бара в rate.time, увеличиваем индекс j
         while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<rate.time)
            j++;
         //--- если j в пределах данных таймсерии и время в start_date меньше времени данных таймсерии по индексу j
         //--- и время в таймсерии по индексу j меньше, либо равно времени в rate.time - записывавем в start_date время из таймсерии по индексу j
         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;
        }
      
      //--- в цикле по списку инструментов корзины
      for(i=0; i<BASKET_SIZE; i++)
        {
         //--- получаем текущий индекс данных
         int j=symbols_rates[i].index;
         //--- пока j в пределах данных таймсерии и, если время бара по индексу j меньше времени, установленного для этого бара в start_date, увеличиваем индекс j
         while(j<ArraySize(symbols_rates[i].rates) && symbols_rates[i].rates[j].time<=start_date)
            symbols_rates[i].index=j++;
        }
      //--- в rate.time запишем время из start_date для последующего бара
      rate.time=start_date;
     }
     
//--- добавляем в базу созданную таймсерию
   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");
        }
     }
//--- успешно
   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():

//+------------------------------------------------------------------+
//| Расчёт цен и объёмов синтетического инструмента                  |
//+------------------------------------------------------------------+
bool CalculateRate(MqlRates& rate,str_rates& symbols_rates[])
  {
   double values[BASKET_SIZE]={0};
   long   tick_volume=0;
   int    i;
//--- получаем цены Open всех символов корзины инструментов в массив values[]
   for(i=0; i<BASKET_SIZE; i++)
      values[i]=GetRateValue(tick_volume,symbols_rates[i],rate.time,VALUE_OPEN);

//--- если тиковый объём нулевой, значит нет данных на этой минуте - возвращаем false
   if(tick_volume==0)
      return(false);
      
//--- запишем совокупный объем всех таймсерий
   rate.tick_volume=tick_volume;
   
//--- рассчитаем цену Open по ценам и весам всех инструментов корзины
   rate.open=MAIN_COEFF;
   for(i=0; i<BASKET_SIZE; i++)
      rate.open*=MathPow(values[i],ExtWeights[i].weight);
      
//--- рассчитаем цену High по ценам и весам всех инструментов корзины
   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);
      
//--- рассчитаем цену Low по ценам и весам всех инструментов корзины
   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);
      
//--- рассчитаем цену Close по ценам и весам всех инструментов корзины
   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(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()

//+------------------------------------------------------------------+
//| Возвращает указанную цену бара                                   |
//+------------------------------------------------------------------+
double GetRateValue(long &tick_volume,str_rates &symbol_rates,datetime time,ENUM_RATES_VALUES num_value)
  {
   double value=0;                  // получаемое значение
   int    index=symbol_rates.index; // индекс данных
   
//--- если индекс в пределах таймсерии
   if(index<ArraySize(symbol_rates.rates))
     {
      //--- в зависимости от типа запрашиваемых данных записываем соответствующее значение в переменную value
      switch(num_value)
        {
         //--- цена Open
         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;
                  //--- при запросе цены Open добавляем тиковый объём к переменной tick_volume, передаваемой по ссылке,
                  //--- для получения суммарного объёма всех символов корзины инструментов
                  tick_volume+=symbol_rates.rates[index].tick_volume;
                 }
              }
            break;
         //--- цена High
         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
         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
         case VALUE_CLOSE:
            if(symbol_rates.rates[index].time<=time)
               value=symbol_rates.rates[index].close;
            break;
        }
     }
     
//--- возвращаем полученное значение
   return(value);
  }

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

//--- возвращаем результат проверки цен на корректность
   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:

//+------------------------------------------------------------------+
//| Проверка цен на корректность и возврат результата проверки       |
//+------------------------------------------------------------------+
bool CheckRate(MqlRates &rate)
  {
//--- если цены представляют собой не корректные действительные числа, или меньше, либо равны нулю - возвращаем 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);
      
//--- нормализуем цены до требуемого количества знаков
   rate.open=NormalizeDouble(rate.open,ExtDigits);
   rate.high=NormalizeDouble(rate.high,ExtDigits);
   rate.low=NormalizeDouble(rate.low,ExtDigits);
   rate.close=NormalizeDouble(rate.close,ExtDigits);
   
//--- корректируем при необходимости цены
   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;
      
//--- всё успешно
   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:

     
//--- строим M1 бары за 1 месяц
   if(!PrepareRates(custom_symbol))
      return(false);
      
//--- получаем последние тики после построения M1 баров
   PrepareLastTicks(custom_symbol);
   
//--- сервис инициализирован
   Print(custom_symbol," datafeed started");
   return(true);
  }

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

//+------------------------------------------------------------------+
//| Подготовка последних тиков                                       |
//+------------------------------------------------------------------+
void PrepareLastTicks(const string custom_symbol)
  {
   str_ticks symbols_ticks[BASKET_SIZE];
   int       i,j,cnt,reserve=0;
   MqlTick   usdx_ticks[];                                        // массив тиков синтетического инструмента
   MqlTick   tick={0};                                            // данные одного тика синтетического инструмента

   long time_to=TimeCurrent()*1000;                               // время конца тиковых данных в миллисекундах
   long start_date=(time_to/MSECS_IN_MINIUTE)*MSECS_IN_MINIUTE;   // время открытия бара в миллисекундах со временем TimeCurrent()
   long time_from=start_date-MSECS_IN_MINIUTE;                    // время начала копирования тиковых данных в миллисекундах 

//--- если были тики за последнюю минуту
   if(SymbolInfoTick(custom_symbol,tick) && tick.time_msc>=start_date)
     {
      Print(custom_symbol," last tick at ",datetime(tick.time_msc/1000),":",IntegerToString(tick.time_msc%1000,3,'0'));
      str_rates symbols_rates[BASKET_SIZE];
      bool      copy_error=false;
      
      //--- в цикле по количеству символов в корзине инструментов
      for(i=0; i<BASKET_SIZE; i++)
        {
         //--- копируем два последних бара исторических данных инструмента
         if(CopyRates(ExtWeights[i].symbol,PERIOD_M1,0,2,symbols_rates[i].rates)!=2)
           {
            Print("cannot copy ",ExtWeights[i].symbol," rates [",GetLastError(),"]");
            copy_error=true;
            break;
           }
         symbols_rates[i].index=1;
        }
      
      //--- рассчитываем данные последней минуты
      if(!copy_error)
        {
         MqlRates rate;
         double   values[BASKET_SIZE]={0};
         rate.time=datetime(start_date/1000);
         rate.real_volume=0;
         rate.spread=0;
         
         //--- если исторические данные бара инструмента рассчитаны
         if(CalculateRate(rate,symbols_rates))
           {
            MqlRates usdx_rates[1];
            
            //--- заменяем рассчитанными данными бара историю последнего бара M1 пользовательского инструмента
            usdx_rates[0]=rate;
            cnt=CustomRatesUpdate(custom_symbol,usdx_rates);
            if(cnt==1)
              {
               Print(custom_symbol,",M1 last minute rate ",rate.time," added");
               //--- время в миллисекундах последующих добавляемых тииков
               start_date=tick.time_msc+1;
              }
           }
          else
            Print(custom_symbol,",M1 last minute rate ",rate.time," ",rate.open," ",rate.high," ",rate.low," ",rate.close," not updated");
        }
     }
     
//--- получаем все тики с начала предыдущей минуты
   for(i=0; i<BASKET_SIZE; i++)
     {
      if(CopyTicksRange(ExtWeights[i].symbol,symbols_ticks[i].ticks,COPY_TICKS_ALL,time_from,time_to)<=0)
        {
         PrintFormat("cannot copy ticks for %s",ExtWeights[i].symbol);
         return;
        }
      PrintFormat("%u %s ticks from %s",ArraySize(symbols_ticks[i].ticks),ExtWeights[i].symbol,TimeToString(symbols_ticks[i].ticks[0].time,TIME_DATE|TIME_SECONDS));
      symbols_ticks[i].index=0;
     }
     
//--- резерв массива тиков для избегания перераспределения памяти при изменении размера
   reserve=ArraySize(symbols_ticks[0].ticks);
   
//--- установим начало всех тиков на одну дату start_date
   j=0;
   while(j<ArraySize(symbols_ticks[0].ticks) && symbols_ticks[0].ticks[j].time_msc<start_date)
      j++;
   if(j>=ArraySize(symbols_ticks[0].ticks))
     {
      Print("no ticks at ",datetime(start_date/1000),":",IntegerToString(start_date%1000,3,'0')," (",start_date/1000,")" );
      return;
     }
   symbols_ticks[0].index=j;
   long time_msc=symbols_ticks[0].ticks[j].time_msc;
   for(i=1; i<BASKET_SIZE; i++)
     {
      j=0;
      while(j<ArraySize(symbols_ticks[i].ticks) && symbols_ticks[i].ticks[j].time_msc<time_msc)
         j++;
      if(j>0 && j<ArraySize(symbols_ticks[i].ticks))
         j--;
      symbols_ticks[i].index=j;
     }
     
//--- тики USD index
   double values[BASKET_SIZE]={0};
   int    array_size=0;
   
//--- первый тик
   tick.last=0;
   tick.volume=0;
   tick.flags=0;

//--- в цикле от индекса j (от начальной даты всех тиков корзины инструментов)
//--- по количеству полученных тиков первого инструмента корзины
   for(j=symbols_ticks[0].index; j<ArraySize(symbols_ticks[0].ticks); j++)
     {
      //--- записываем данные тика по индексу цикла j
      tick.time=symbols_ticks[0].ticks[j].time;          // время тика
      tick.time_msc=symbols_ticks[0].ticks[j].time_msc;  // время тика в миллисекундах
      
      //--- рассчитаем значение цены Bid по весам всех символов корзины инструментов
      values[0]=symbols_ticks[0].ticks[j].bid;
      symbols_ticks[0].index++;
      for(i=1; i<BASKET_SIZE; i++)
         values[i]=GetTickValue(symbols_ticks[i],symbols_ticks[0].ticks[j].time_msc);
      tick.bid=MAIN_COEFF;
      for(i=0; i<BASKET_SIZE; i++)
         tick.bid*=MathPow(values[i],ExtWeights[i].weight);
      //--- цена Ask равна рассчитанной цене Bid инструмента
      tick.ask=tick.bid;
      
      //--- добавляем рассчитанный тик в массив тиков синтетического инструмента
      ArrayResize(usdx_ticks,array_size+1,reserve);
      usdx_ticks[array_size]=tick;
      array_size++;
      
      //--- обнуляем размер резервированной памяти, так как он нужен только при первом ArrayResize
      reserve=0;
     }
     
//--- Добавляем в ценовую историю пользовательского инструмента данные из собранного массива тиков
   if(array_size>0)
     {
      Print(array_size," ticks from ",usdx_ticks[0].time,":",IntegerToString(usdx_ticks[0].time_msc%1000,3,'0')," prepared");
      cnt=CustomTicksAdd(custom_symbol,usdx_ticks);
      if(cnt>0)
         Print(cnt," ticks applied");
      else
         Print("no ticks applied");
     }
  }

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():

//+------------------------------------------------------------------+
//| Возвращает значение тика                                         |
//+------------------------------------------------------------------+
double GetTickValue(str_ticks &symbol_ticks,long time_msc)
  {
   double value=0;
//--- если индекс данных, записанный в структуре symbol_ticks, находится в пределах массива тиков структуры
   if(symbol_ticks.index<ArraySize(symbol_ticks.ticks))
     {
      //--- получаем значение цены Bid из структуры по индексу данных
      value=symbol_ticks.ticks[symbol_ticks.index].bid;
      //--- если время в структуре в миллисекундах по индексу в структуре меньше переданного в функцию времени
      if(symbol_ticks.ticks[symbol_ticks.index].time_msc<time_msc)
        {
         //--- до тех пор, пока индекс находится в пределах таймсерии в структуре и
         //--- если время в структуре меньше переданного в функцию времени - увеличиваем индекс
         while(symbol_ticks.index<ArraySize(symbol_ticks.ticks) && symbol_ticks.ticks[symbol_ticks.index].time_msc<time_msc)
            symbol_ticks.index++;
        }
     }
//--- возвращаем полученное значение
   return(value);
  }

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():

//+------------------------------------------------------------------+
//| Обработка тиков                                                  |
//+------------------------------------------------------------------+
void ProcessTick(const string custom_symbol)
  {
   static long    last_time_msc=0;     // время в миллисекундах последнего тика
   static MqlTick synth_tick[1];       // структура последнего тика синтетического инструмента
   static MqlTick ticks[BASKET_SIZE];  // массив данных последних тиков корзины символов
   static MqlTick tick;                // вспомогательная переменная для получения данных и поиска времени
   int success_cnt=0;
   int change_cnt=0;
   
//--- инициализируем время тика синтетического символа
   synth_tick[0].time=0;
   
//--- в цикле по количеству символов в корзине инструментов
   for(int i=0; i<BASKET_SIZE; i++)
     {
      //--- получаем данные очередного символа
      if(SymbolInfoTick(ExtWeights[i].symbol,tick))
        {
         //--- увеличиваем количество успешных запросов данных
         success_cnt++;
         //--- получаем самое свежее время из списка символов корзины
         if(synth_tick[0].time==0)
           {
            synth_tick[0].time=tick.time;
            synth_tick[0].time_msc=tick.time_msc;
           }
         else
           {
            if(synth_tick[0].time_msc<tick.time_msc)
              {
               synth_tick[0].time=tick.time;
               synth_tick[0].time_msc=tick.time_msc;
              }
           }
         //--- сохраняем полученные данные по символу в массиве ticks в соответствии с индексом символа корзины
         ticks[i]=tick;
        }
     }
   //--- если получены тики всех инструментов корзины, и это новый тик
   if(success_cnt==BASKET_SIZE && synth_tick[0].time!=0 && last_time_msc<synth_tick[0].time_msc)
     {
      //--- сохраняем время последнего тика
      last_time_msc=synth_tick[0].time_msc;
      
      //--- рассчитываем значение цены Bid синтетического инструмента
      synth_tick[0].bid=MAIN_COEFF;
      for(int i=0; i<BASKET_SIZE; i++)
         synth_tick[0].bid*=MathPow(ticks[i].bid,ExtWeights[i].weight);
         
      //--- цена Ask равна цене Bid
      synth_tick[0].ask=synth_tick[0].bid;
      
      //--- добавляем в ценовую историю пользовательского инструмента новый тик
      CustomTicksAdd(custom_symbol,synth_tick);
     }
  }

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:

//+------------------------------------------------------------------+
//|                                                    USD_Index.mq5 |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property service
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#define CUSTOM_SYMBOL     "USDX.synthetic"
#define CUSTOM_GROUP      "Synthetics"
#define BASKET_SIZE       6
#define MAIN_COEFF        50.14348112

#include "CurrencyIndex.mqh"

SymbolWeight ExtWeights[BASKET_SIZE]=
  {
     { "EURUSD",-0.576 },
     { "USDJPY", 0.136 },
     { "GBPUSD",-0.119 },
     { "USDCAD", 0.091 },
     { "USDSEK", 0.042 },
     { "USDCHF", 0.036 } 
  };

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:

//+------------------------------------------------------------------+
//| Service program start function                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
   if(!InitService(CUSTOM_SYMBOL,CUSTOM_GROUP))
      return;
//---
   while(!IsStopped())
     {
      ProcessTick(CUSTOM_SYMBOL);
      Sleep(10);
     }
//---
   Print(CUSTOM_SYMBOL," datafeed stopped");
  }

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:

USD_Index       open chart USDX.synthetic,M1
USD_Index       31117 EURUSD,M1 rates from 2024.07.29 00:00
USD_Index       31101 USDJPY,M1 rates from 2024.07.29 00:01
USD_Index       31114 GBPUSD,M1 rates from 2024.07.29 00:00
USD_Index       31112 USDCAD,M1 rates from 2024.07.29 00:01
USD_Index       30931 USDSEK,M1 rates from 2024.07.29 00:00
USD_Index       31079 USDCHF,M1 rates from 2024.07.29 00:01
USD_Index       start date set to 2024.07.29 00:01:00
USD_Index       31119 USDX.synthetic,M1 rates from 2024.07.29 00:01:00 to 2024.08.27 14:45:00 added
USD_Index       44 EURUSD ticks from 2024.08.27 14:45:01
USD_Index       45 USDJPY ticks from 2024.08.27 14:45:00
USD_Index       40 GBPUSD ticks from 2024.08.27 14:45:00
USD_Index       45 USDCAD ticks from 2024.08.27 14:45:00
USD_Index       66 USDSEK ticks from 2024.08.27 14:45:00
USD_Index       29 USDCHF ticks from 2024.08.27 14:45:00
USD_Index       12 ticks from 2024.08.27 14:46:02:319 prepared
USD_Index       12 ticks applied
USD_Index       USDX.synthetic datafeed started

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:

//+------------------------------------------------------------------+
//|                                                    EUR_Index.mq5 |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property service
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#define CUSTOM_SYMBOL     "EURX.synthetic"
#define CUSTOM_GROUP      "Synthetics"
#define BASKET_SIZE       5
#define MAIN_COEFF        34.38805726

#include "CurrencyIndex.mqh"

SymbolWeight ExtWeights[BASKET_SIZE]=
  {
   { "EURUSD", 0.3155 },
   { "EURGBP", 0.3056 },
   { "EURJPY", 0.1891 },
   { "EURCHF", 0.1113 }, 
   { "EURSEK", 0.0785 }
  };
//+------------------------------------------------------------------+
//| Service program start function                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
   if(!InitService(CUSTOM_SYMBOL,CUSTOM_GROUP))
      return;
//---
   while(!IsStopped())
     {
      ProcessTick(CUSTOM_SYMBOL);
      Sleep(10);
     }
//---
   Print(CUSTOM_SYMBOL," datafeed stopped");
  }
//+------------------------------------------------------------------+

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:

EUR_Index       open chart EURX.synthetic,M1
EUR_Index       31148 EURUSD,M1 rates from 2024.07.29 00:00
EUR_Index       31137 EURGBP,M1 rates from 2024.07.29 00:01
EUR_Index       31148 EURJPY,M1 rates from 2024.07.29 00:01
EUR_Index       31123 EURCHF,M1 rates from 2024.07.29 00:00
EUR_Index       30898 EURSEK,M1 rates from 2024.07.29 00:00
EUR_Index       start date set to 2024.07.29 00:01:00
EUR_Index       31151 EURX.synthetic,M1 rates from 2024.07.29 00:01:00 to 2024.08.27 15:16:00 added
EUR_Index       53 EURUSD ticks from 2024.08.27 15:16:00
EUR_Index       65 EURGBP ticks from 2024.08.27 15:16:00
EUR_Index       109 EURJPY ticks from 2024.08.27 15:16:00
EUR_Index       68 EURCHF ticks from 2024.08.27 15:16:00
EUR_Index       57 EURSEK ticks from 2024.08.27 15:16:00
EUR_Index       15 ticks from 2024.08.27 15:17:00:877 prepared
EUR_Index       15 ticks applied
EUR_Index       EURX.synthetic datafeed started

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.64 KB)
USD_Index.mq5 (1.41 KB)
EUR_Index.mq5 (1.38 KB)
MQL5.zip (8.76 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.