English Русский 中文 Deutsch 日本語 Português
preview
Gráficos del índice del dólar y del índice del euro — ejemplo de servicio en MetaTrader 5

Gráficos del índice del dólar y del índice del euro — ejemplo de servicio en MetaTrader 5

MetaTrader 5Ejemplos |
400 41
Artyom Trishkin
Artyom Trishkin

Contenido


Introducción

El índice del dólar estadounidense (U.S. Dollar Index) es el más conocido e importante en el mercado de divisas. Gracias a él, podemos predecir el movimiento de los tipos de cambio. Se trata de un indicador importante del valor relativo del dólar estadounidense. El índice del dólar estadounidense (USDX) existe desde marzo de 1973, cuando se tomó como valor base un nivel de 100 puntos. Es decir, un índice igual a 90 puntos en la actualidad significaría una caída del dólar del 10% con respecto a la cifra de 1973, mientras que un índice igual a 110 puntos significaría una aumento del 10%.

El índice del dólar se introdujo en 1973, cuando entraron en vigor los tipos de cambio flotantes de las divisas incluidas en el índice tras la Conferencia Internacional de Jamaica. Desde entonces, el índice del dólar se calcula continuamente a partir de los datos comerciales sobre divisas proporcionados por los 500 mayores bancos del mundo. En 1999 se produjo un cambio en el método de cálculo del índice del dólar estadounidense, al ponerse en circulación la moneda única europea, el euro, que sustituyó a las monedas nacionales de varios países europeos y al marco alemán.

El USDX se calcula como la media geométrica ponderada de una cesta de divisas de importantes monedas mundiales. Cada divisa de esta cesta pertenece a un grupo de seis grandes socios comerciales de EE.UU. que no son iguales en poder económico, por lo que a cada divisa del índice se le asigna una cuota específica de influencia (peso):

Divisa
Peso
Euro (EUR)
0.576 (57.6%)
Yen japonés (JPY)
0.136 (13.6%)
Libra esterlina (GBP)
0.119 (11.9%)
Dólar canadiense (CAD)
0.091 (9.1%)
Corona sueca (SEK)
0.042 (4.2%)
Franco suizo (CHF)
0.036 (3.6%)

Fórmula para calcular el índice del dólar estadounidense:

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

Los grados del cálculo a los que se convierten los tipos corresponden al peso de las monedas en la cesta usada. Un factor de 50,14348112 llevaría el índice del dólar a un valor de 100,0 si en la fórmula se sustituyeran los tipos de cambio de marzo de 1973. Así, el USDX actual muestra la variación del valor del dólar estadounidense frente a una cesta de divisas en comparación con las cotizaciones de 1973. Un valor del índice inferior a 100 puntos indica una depreciación del dólar, mientras que un valor superior a 100 puntos indica un aumento del valor de la moneda estadounidense en comparación con 1973.

El Índice del Euro (Euro Currency Index) es la medida media de la variación de los tipos de cambio de cinco monedas mundiales (el dólar estadounidense, la libra esterlina, el yen japonés, el franco suizo y la corona sueca) frente al euro.

Como instrumento comercial, el Euro Index (EURX) se introdujo el 13 de enero de 2006 en la bolsa New York Board of Trade (NYBOT), tickers ECX, EURX o E.

El índice del euro se ha convertido en una "referencia" del valor actual de la divisa única europea para los participantes en los mercados financieros internacionales, así como en un instrumento para las operaciones comerciales.

El cálculo del índice del euro para una cesta de cinco divisas coincide con los datos utilizados por el Banco Central Europeo para calcular el índice del euro ponderado según el comercio para las monedas de los países que constituyen el principal volumen de comercio exterior de los países de la zona del euro. El comercio con los Estados Unidos representa la mayor parte del comercio internacional de los países de la eurozona (31,55%), seguido del Reino Unido (30,56%), Japón (18,91%), Suiza (11,13%) y Suecia (7,85%).

Los principios básicos para calcular el valor actual del Índice del Euro son similares a los usados para calcular el Índice del Dólar estadounidense (USDX). El Índice del Euro se calcula utilizando el método de cálculo de la media ponderada geométrica:

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

donde las ponderaciones de las monedas de la cesta utilizada se usan como grado.


Fijamos un objetivo

Entonces, ¿qué queremos conseguir al final? ...  Necesitamos crear un instrumento sintético cuyo precio se calcule usando las fórmulas anteriores. Necesitamos un gráfico completo del instrumento que se actualizará con la llegada de cada nuevo tick en los símbolos utilizados en la cesta de instrumentos, y en este gráfico podremos ejecutar cualquier indicador, script y asesor experto.

En general, necesitamos crear un gráfico de instrumento sintético que casi no se distinga de los gráficos de los instrumentos estándar. Y para este propósito nos vendrá bien un programa de servicio que realice todo en su propio flujo, independientemente de otros gráficos abiertos y de los programas que se ejecuten en ellos.

Al lanzar el servicio, comprobaremos la disponibilidad del instrumento sintético requerido, lo crearemos en caso de que no exista y lo colocaremos en la ventana de la Observación del Mercado. A continuación, se creará la historia del instrumento sintético, de minutos y ticks, y se abrirá el gráfico del instrumento creado. Tras estas manipulaciones, el servicio recibirá para cada símbolo nuevos ticks a partir de los cuales se calculará el precio del instrumento, y se añadirán nuevos ticks a la historia del símbolo personalizado creado. Tras reiniciar el terminal, el programa de servicio se iniciará automáticamente si se había iniciado al cerrar el terminal. Así, una vez iniciado, el servicio requerido se reiniciará siempre cuando se inicie el terminal.

Hoy crearemos dos de estos símbolos personalizados: el índice del dólar estadounidense y el índice del euro. Para cada uno de estos instrumentos se creará un programa de servicio diferente. Pero se crearán sobre la base de un archivo de inclusión en el que colocaremos todas las funciones para crear el instrumento deseado. En el programa de servicio solo se especificará el tamaño de la cesta, el coeficiente básico y la estructura de símbolos con sus ponderaciones necesarias para el cálculo del instrumento. Todo lo demás ocurrirá dentro del archivo de inclusión al llamar a las funciones definidas en él. Esto nos permitirá, usando las funciones creadas, crear nuestros propios índices con un conjunto diferente de divisas, así como los pesos de estas, simplemente especificando una lista de símbolos y los pesos de cada uno de ellas.


Escribimos un archivo de inclusión de las funciones de cálculo de los índices de divisas

En el directorio del terminal \MQL5\Services\, crearemos la nueva carpeta Indexes\, y dentro de ella, un nuevo archivo de inclusión con el nombre CurrencyIndex.mqh. En él escribiremos todas las funciones necesarias para el funcionamiento del proyecto.

Al inicio del archivo escribiremos las macrosustituciones de estructura y enumeración necesarias para el trabajo:

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

En la estructura de símbolos de la cesta escribiremos dos campos: el nombre del símbolo y su peso en la cesta de instrumentos. Al compilar una cesta de instrumentos para calcular el índice, será conveniente utilizar un array de estructuras de este tipo: en él escribiremos los nombres de los símbolos y sus pesos directamente en la inicialización, y después de este array obtendremos un símbolo y su peso para calcular los precios del índice en un ciclo. En este caso, cualquier símbolo con su peso puede escribirse en el array, lo cual ofrece flexibilidad para crear cualquier cesta de instrumentos para el cálculo de índices.

Las estructuras de datos históricos y de ticks utilizan los arrays de estructuras correspondientes: MqlRates y MqlTick contendrán los datos para cada uno de los símbolos de la cesta de instrumentos. Y también habrá un índice de datos en cada una de estas estructuras. El índice deberá especificar el número de la barra existente de la que se toman los datos para el cálculo del índice.

Por ejemplo, para calcular el índice en una barra cualquiera, será necesario que cada uno de los símbolos de la cesta de instrumentos que participan en el cálculo del índice tenga datos en esa barra de minutos, si bien puede que estos no se den necesariamente en todos y cada uno de los símbolos: en algunos sitios se dan saltos de barras (si no ha habido ticks en ninguno de los símbolos en ese minuto). En este caso deberemos especificar el índice de la barra de la que se toman los datos para el cálculo (cuando no hay datos sobre el símbolo, el índice se incrementará) para tomar los datos de la barra anterior. Y como no sabemos de antemano el número de símbolos de la cesta de instrumentos para calcular el índice, no podemos declarar por anticipado en el programa el número necesario de índices para cada instrumento. Por ello, resulta cómodo almacenarlos y utilizarlos en una estructura de este tipo.

La enumeración del tipo de precio simplemente establece las constantes que especifican el precio a obtener para el cálculo del precio de la barra.

Al iniciar el servicio, lo primero que debemos hacer es crear un símbolo personalizado, construir las barras M1 históricas para el mes y la historia de ticks. Esta será la inicialización del servicio. Vamos a escribir una función de este tipo:

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

La comprobación de la existencia de un símbolo personalizado y la creación del mismo tiene lugar en la función 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);
  }

Aquí comprobaremos si existe un símbolo personalizado con el nombre especificado. Si no, crearemos uno. A continuación, buscaremos un gráfico abierto de este símbolo y, si el gráfico no se encuentra entre los gráficos abiertos en el terminal, lo abriremos.

Tras crear un símbolo personalizado y abrir su gráfico, deberemos crear la historia del periodo M1 durante un mes. Para ello usaremos la función 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);
  }

La función primero copiará los datos de cada uno de los símbolos de la cesta de instrumentos y establecerá el tiempo total de inicio del copiado de los datos. A continuación, para cada uno de los instrumentos se fijará un índice de datos cuya hora coincidirá con la de los demás símbolos de la cesta, de modo que la fecha inicial sea la misma para todos los símbolos de la cesta, y si para algún símbolo no existe barra en la fecha inicial, se fijará para este el índice de la barra anterior más próxima en la que existan datos.

Más adelante en un ciclo, cada barra de la serie temporal del instrumento sintético se calculará barra a barra con la corrección pertinente del índice de cada barra posterior, de forma que los datos de la misma procedan de la barra actual, si hay datos en ella, o de la anterior, si no hay datos en la barra actual. Las barras calculadas se añadirán al array de series temporales del instrumento sintético. Tras calcularse todas las barras de la serie temporal del instrumento, la serie temporal calculada se añadirá a la historia de precios del símbolo personalizado.

Los datos históricos de una barra de un instrumento sintético se calcularán en la función 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 uno de los precios de barra (Open, High, Low y Close) del instrumento sintético, los precios se calcularán utilizando la fórmula del 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);

Los precios de cada símbolo de la cesta se obtendrán en la función 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);
  }

Desde la función CalculateRate() se retornará el resultado de la comprobación de los precios calculados del instrumento sintético:

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

Como se calculan todos los precios de las barras del instrumento sintético, deberemos comprobar la exactitud de su cálculo y corregir los errores (si los hay).

Todo esto se efectuará en la función CheckRate(), cuyo resultado se retornará:

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

Si alguno de los precios calculados es más o menos infinito, o "no es un número" (NaN - not a number), o bien es menor o igual que cero, la función retornará false.

A continuación, todos los precios se normalizarán con la precisión especificada para el símbolo y se comprobará que los precios Open, High, Low y Close sean correctos entre sí. De ser necesario, los precios se ajustarán y se retornará true.

En la función InitService() comentada anteriormente , después de construir las barras de minutos para el mes, obtendremos los últimos datos de ticks del instrumento sintético y retornaremos true:

undefined

Los datos de ticks se obtendrán en PrepareLastTicks():

undefined

La lógica de la función se describirá en los comentarios del código. En primer lugar, obtendremos las dos últimas barras del instrumento y sustituiremos con ellas los datos de la historia del instrumento. A continuación, obtendremos los datos de ticks desde el principio de la barra anterior, calcularemos el valor de tick según esta y lo añadiremos al array de la serie temporal del instrumento. Una vez finalizado el cálculo de todos los ticks, sustituiremos los datos históricos de los ticks del instrumento por los datos del array de ticks rellenado.

Los datos del array de ticks para calcular el tick de un instrumento sintético se obtendrán usando la función GetTickValue():

undefined

Una vez el servicio se haya inicializado correctamente, debería, en un ciclo infinito, recuperar los datos de todos los símbolos de la cesta de instrumentos y actualizar la historia de barras y ticks del instrumento, emulando así el flujo de datos en tiempo real de un símbolo personalizado. Todo esto sucederá en la función ProcessTick():

undefined

Aquí obtendremos los ticks de todos los instrumentos de la cesta en un ciclo, mientras calculamos el tiempo del tick. El tiempo de tick se tomará como la hora del último tick llegado de todos los símbolos de la cesta de instrumentos. Si se reciben ticks de cada instrumento de la cesta, calcularemos el precio Bid de un tick de un instrumento sintético utilizando la fórmula de cálculo del índice. Una vez calculado el precio Bid, igualaremos el valor del precio Ask al valor del precio Bid calculado del instrumento sintético. Al final de todos los cálculos añadiremos los datos del array de ticks synth_tick a la historia de precios del símbolo personalizado, emulando así la llegada de un nuevo tick del instrumento sintético.

Todas las funciones para crear índices de divisas y trabajar con sus datos están listas. Ahora, basándonos en las funciones creadas, escribiremos programas de servicio para crear el índice del dólar americano y el índice del euro.


Probamos los programas de servicio

En la carpeta donde hemos escrito el archivo de funciones de inclusión \MQL5\Services\Indexes\, crearemos un nuevo archivo de programa con el tipo Servicio y el nombre USD_Index.mq5.

Luego definiremos de inmediato las macrosustituciones para el nombre del símbolo personalizado y su grupo. Y especificaremos el número de símbolos de la cesta de instrumentos y el coeficiente básico.

Después conectaremos el archivo de las funciones escritas anteriormente para trabajar con índices de divisas y definiremos la estructura del símbolo de la cesta, especificando los nombres de todos los símbolos y sus pesos:

undefined

Ahora en un único manejador de servicio OnStart() inicializaremos el símbolo personalizado e iniciaremos el procesamiento de los nuevos ticks entrantes en un ciclo infinito:

undefined

Compilaremos el archivo de servicio y lo ejecutaremos:


Tras añadir el servicio y ejecutarlo, veremos mensajes en el diario de registro:

undefined

Se abrirá el gráfico del instrumento sintético creado, que se actualizará como un gráfico normal de un símbolo estándar. Ahora podremos colocar indicadores en el gráfico y realizar cualquier análisis técnico:


Bien, vamos a crear un servicio que nos permita crear un gráfico del índice del euro y trabajar con él.

En la carpeta \MQL5\Services\Indexes\, crearemos un nuevo archivo de programa con el tipo Servicio y el nombre EUR_Index.mq5:

undefined

La única diferencia con respecto al archivo del programa de servicio para crear el gráfico del índice del dólar estadounidense y trabajar con él será el tamaño de la cesta de símbolos, el coeficiente básico y la estructura de los símbolos de la cesta con sus pesos.

Vamos a compilar el programa y a ejecutar el servicio. Veremos las siguientes entradas en el registro:

undefined

A continuación, se abrirá un gráfico del índice del euro, con el que podremos trabajar a fondo:


Conclusión

Usando como ejemplo los programas de servicio, hoy hemos aprendido a crear símbolos personalizados y a mantener actualizados todos nuestros gráficos. Al reiniciar el terminal, también se reiniciarán todos los servicios que estaban en ejecución cuando se cerró el terminal de cliente. Así, iniciado una vez el servicio, se iniciará automáticamente cuando se ponga en marcha el terminal. Y estos servicios "lanzarán" constantemente los nuevos ticks al gráfico, manteniendo actualizada la historia del instrumento sintético.

Basándose en los servicios antes mencionados, podremos crear nuestros propios índices con sus propios datos y ver de forma clara y visual un gráfico con los cambios en la cesta de instrumentos utilizados en el índice de divisas creado. Por ejemplo, podremos representar gráficamente el índice del dólar estadounidense ponderado según el comercio (TWDI). La cesta de divisas TWDI consta de 26 símbolos y resulta más relevante hoy en día que el índice regular del dólar estadounidense.

Todos los archivos de este proyecto se adjuntan al artículo: podrá utilizarlos tal cual, o usarlos para crear algo según sus propias necesidades usando como base sus propias cestas de instrumentos. El archivo MQL5.zip, también adjunto a este artículo, puede descomprimirse de forma sencilla en su directorio de trabajo del terminal MQL5\: todos los archivos del proyecto se colocarán inmediatamente en las subcarpetas necesarias.


Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/15684

Archivos adjuntos |
MQL5.zip (8.66 KB)
CurrencyIndex.mqh (51.79 KB)
USD_Index.mq5 (1.41 KB)
EUR_Index.mq5 (1.38 KB)
MQL5.zip (8.66 KB)
Silk Road Trading LLC
Ryan L Johnson | 14 abr 2025 en 21:13
Artyom Trishkin #:

Quería guiar a una persona paso a paso hasta esta sencilla conclusión. Lo tomaron y arruinaron todo :)

A veces hay que hacer pensar a la gente en lugar de copiar cosas ya hechas.

Ah bueno, me hizo pensar. 😅

Roman Shiredchenko
Roman Shiredchenko | 25 abr 2025 en 02:48

Artem podrías mirar - antes los servicios dibujaban bien - ahora paran....

ejemplo en el archivo adjunto

restart - reiniciado.

solía dibujar - se puede ver desde arriba donde el indicador es rojo-azul, ahora se ha convertido en una tira...)



y todavía no puedo hacer nada con él ...


Lo he ejecutado y borrado y recompilado y reiniciado.


Dibuja palitos en el triple y ya está...))

Artyom Trishkin
Artyom Trishkin | 25 abr 2025 en 03:33
Roman Shiredchenko #:
los servicios antes dibujaban bien - ahora se detienen....

¿El servicio adjunto al artículo dibuja normalmente? Sí, lo hace.

Por lo tanto, es necesario buscar errores en ti mismo (si todos los datos, por ejemplo, se reciben para el cálculo), supongo ...

Roman Shiredchenko
Roman Shiredchenko | 25 abr 2025 en 04:14
Artyom Trishkin #:

¿El servicio adjunto al artículo dibuja con normalidad? Sí.

Por lo tanto, es necesario buscar errores en ti mismo (si todos los datos, por ejemplo, se reciben para el cálculo), supongo ...

Ok. Voy a comprobar todo de nuevo. Lo he ejecutado antes y todo funcionaba.....
Etienne Bah Mumbah
Etienne Bah Mumbah | 28 oct 2025 en 11:22
En realidad, parece que los servicios no se pueden migrar al MetaQuotes VPS.
Ese es el reto con el uso de servicios.
Métodos de William Gann (Parte II): Creación del indicador Cuadrado de Gann Métodos de William Gann (Parte II): Creación del indicador Cuadrado de Gann
Crearemos un indicador basado en el Cuadrado de Gann de 9, construido elevando al cuadrado el tiempo y el precio. Prepararemos el código y probaremos el indicador en la plataforma en diferentes intervalos de tiempo.
Búsqueda con restricciones — Tabu Search (TS). Búsqueda con restricciones — Tabu Search (TS).
En este artículo se analiza el algoritmo de búsqueda tabú, uno de los primeros y más conocidos métodos de la metaheurística. Hoy mostraremos con detalle cómo funciona el algoritmo, empezando por la selección de una solución inicial y la exploración de las opciones vecinas, centrándonos en el uso de la lista tabú. El artículo abarcará los aspectos clave del algoritmo y sus características.
Redes neuronales en el trading: Transformador vectorial jerárquico (Final) Redes neuronales en el trading: Transformador vectorial jerárquico (Final)
Continuamos nuestro análisis del método del Transformador Vectorial Jerárquico. En este artículo finalizaremos la construcción del modelo. También lo entrenaremos y probaremos con datos históricos reales.
Algoritmo de optimización de sociedad anárquica (Anarchic Society Optimization, ASO) Algoritmo de optimización de sociedad anárquica (Anarchic Society Optimization, ASO)
En este artículo, nos familiarizaremos con el algoritmo de optimización de sociedad anárquica (Anarchic Society Optimization, ASO) y discutiremos cómo un algoritmo basado en el comportamiento irracional y aventurero de los participantes en una sociedad anárquica (un sistema anómalo de interacción social libre de poder centralizado y varios tipos de jerarquías) es capaz de explorar el espacio de soluciones y evitar las trampas del óptimo local. El artículo presenta una estructura ASO unificada aplicable tanto a problemas continuos como discretos.