Gráficos del índice del dólar y del índice del euro — ejemplo de servicio en MetaTrader 5
Contenido
- Introducción
- Fijamos un objetivo
- Escribimos un archivo de inclusión de las funciones de cálculo de los índices de divisas
- Probamos los programas de servicio
- Conclusión
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.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/15684
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Métodos de William Gann (Parte II): Creación del indicador Cuadrado de Gann
Búsqueda con restricciones — Tabu Search (TS).
Redes neuronales en el trading: Transformador vectorial jerárquico (Final)
Algoritmo de optimización de sociedad anárquica (Anarchic Society Optimization, ASO)
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
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. 😅
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á...))
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 ...
¿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 ...
Ese es el reto con el uso de servicios.