![Gráficos sin agujeros](https://c.mql5.com/i/articles/overlay.png)
Gráficos sin agujeros
1. Motivación
En MT 4 se dibujan barras, dentro de las cuales suceden cambios en el precio. Si no ocurre ningún cambio de precio en un minuto aparece un hueco (gap) en el gráfico.
Los desarrolladores han elegido deliberadamente esta forma de dibujar los gráficos porque la mayoría de traders prefiere los gráficos que contienen precios reales. Sin embargo, también hay usuarios que prefieren los gráficos continuos. Prefieren que las barras se dibujen incluso si el precio de apertura es igual al precio de cierre de la barra anterior. En consecuencia no hay gaps en la escala de tiempo del gráfico, y, por ejemplo, 100 barras siempre se corresponden con los 100 minutos del gráfico de un minuto. Estos datos pueden cambiar en la presente implementación. Por ejemplo, 100 minutos pueden "caber" en 98 barras si hubiera dos minutos entre las mismas con cotizaciones sin ingresos.
Afortunadamente, MQL4 proporciona todas las herramientas necesarias que le ayudan a dibujar tales gráficos de forma independiente.
2. Implementación
En primer lugar vamos a dividir la tarea en dos etapas distintas:
-
procesamiento del historial de los datos
-
actualización de la última barra
En la primera etapa, creamos un nuevo archivo de historial con prefijo "ALL" antes del nombre del símbolo ("ALL" significa "todas las barras") y en el historial escribimos las barras añadidas.
En el script "period_converter", que viene con el terminal cliente MT 4, se soluciona un problema parecido. El script genera un gráfico con un periodo no estándar. Vamos a utilizar este ejemplo para aprender a trabajar con el archivo del historial.
Antes de crear una aplicación MQL4 tenemos que hacer un análisis previo y decidir qué tipo de programa será: script, indicador o experto. Los indicadores se utilizan para visualizar el contenido del array. Sin embargo no los necesitamos aquí. La diferencia entre los scripts y los expertos es que los primeros se borran del gráfico inmediatamente después de realizar sus operaciones. Esto encaja en esta fase, así que este es el script que vamos a codificar ahora.
Como resultado obtenemos el siguiente código (AllMinutes_Step1.mq4):
#property show_inputs //---- Activar/desactivar el dibujo de las barras en los días festivos //---- Si == true, los días festivos no se rellenan //---- Si == false, los días festivos se rellenan con barras O=H=L=C extern bool SkipWeekEnd=true; int start() { int HistoryHandle=-1,pre_time,now_time,_PeriodSec; double now_close,now_open,now_low,now_high,now_volume,pre_close; int _GetLastError=0,cnt_copy=0,cnt_add=0; int temp[13]; //---- recordamos el símbolo del gráfico y el periodo string _Symbol=Symbol(); int _Period= Period(); _PeriodSec = _Period * 60; //---- abrimos el archivo donde escribimos el historial string file_name=StringConcatenate("ALL",_Symbol,_Period,".hst"); HistoryHandle=FileOpenHistory(file_name,FILE_BIN|FILE_WRITE); if(HistoryHandle<0) { _GetLastError=GetLastError(); Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - Error #",_GetLastError); return(-1); } //---- Escribimos la cabecera del archivo FileWriteInteger(HistoryHandle,400,LONG_VALUE); FileWriteString(HistoryHandle,"Copyright © 2006, komposter",64); FileWriteString(HistoryHandle,"ALL"+_Symbol,12); FileWriteInteger(HistoryHandle,_Period,LONG_VALUE); FileWriteInteger(HistoryHandle,Digits,LONG_VALUE); FileWriteInteger(HistoryHandle,0,LONG_VALUE); //timesign FileWriteInteger(HistoryHandle,0,LONG_VALUE); //last_sync FileWriteArray(HistoryHandle,temp,0,13); //+-----------------------------------------------------------------+ //| Procesamiento del historial //+-----------------------------------------------------------------+ int bars=Bars; pre_time=Time[bars-1]; for(int i=bars-1; i>=0; i--) { //---- Recordamos los parámetros de la barra now_open=Open[i]; now_high=High[i]; now_low=Low[i]; now_close=Close[i]; now_volume=Volume[i]; now_time=Time[i]/_PeriodSec; now_time*=_PeriodSec; //---- si hay barras omitidas, while(now_time>pre_time+_PeriodSec) { pre_time+=_PeriodSec; pre_time /= _PeriodSec; pre_time *= _PeriodSec; //---- si no es el fin de semana, if(SkipWeekEnd) { if(TimeDayOfWeek(pre_time)<=0 || TimeDayOfWeek(pre_time)>5) { continue; } if(TimeDayOfWeek(pre_time)==5) { if( TimeHour(pre_time) == 23 || TimeHour(pre_time + _PeriodSec) == 23 ) { continue; } } } //---- escribimos la barra omitida en el archivo FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE); FileFlush(HistoryHandle); cnt_add++; } //---- escribimos la barra nueva en el archivo FileWriteInteger(HistoryHandle,now_time,LONG_VALUE); FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE); FileFlush(HistoryHandle); cnt_copy++; //---- recordamos la hora y el precio de cierre //---- de la barra registrada pre_close=now_close; pre_time=now_time/_PeriodSec; pre_time*=_PeriodSec; } //---- cerramos el archivo FileClose(HistoryHandle); //---- mostramos las estadísticas Print("< - - - ",_Symbol,_Period,": hubo ",cnt_copy, " barras añadidas ",cnt_add," barras - - - >"); Print("< - - - Abra el gráfico para ver los resultados \"ALL", _Symbol,_Period,"\" - - - >"); return(0); }
Se recomienda prestar atención a la variable SkipWeekEnd . Si vale false, los fines de semana se llenarán con barras O=H=L=C.
Adjuntemos nuestro script al gráfico GBPUSD de un minuto y comprobemos cómo funciona:
Abramos el gráfico ALLGBPUSD1 en modo sin conexión y comparémoslo con el gráfico inicial:
Como se observa, se han añadido al gráfico algunos minutos omitidos. Están marcados con un círculo de color rojo. Esto es lo que queríamos conseguir.
Ahora que tenemos un gráfico con los huecos rellenados, podemos actualizarlo. Las nuevas cotizaciones se mostrarán en el mismo, pero los gaps permanecerán de nuevo vacíos.
El script "period_converter" se puede utilizar a modo de ejemplo. También resuelve el problema de la actualización de los gráficos. Tan solo haremos este cambio: añadir el bloque que rellena las barras omitidas. Vamos a transferir nuestro código al asesor experto porque el gráfico se tiene que actualizar en cada tick. Se lanzará cada vez que entre una nueva cotización. Vamos a poner el código de la primera parte en la función init() porque esta parte se tiene que ejecutar solo una vez. El nuevo código se pondrá en la función start() porque se ejecutará en cada tick. Además, el lugar más adecuado para cerrar el archivo es la función deinit().
Por lo tanto tenemos el siguiente código (AllMinutes_Step2.mq4):
#include <WinUser32.mqh> //---- Activamos/desactivamos el dibujo de las barras en los días festivos //---- Si es == true, los días festivos permanecerán sin rellenar //---- Si es == false, los días festivos se rellenarán con las barras O=H=L=C extern bool SkipWeekEnd=true; int HistoryHandle=-1,hwnd=0,last_fpos=0,pre_time,now_time; int _Period,_PeriodSec; double now_close,now_open,now_low,now_high,now_volume; double pre_close,pre_open,pre_low,pre_high,pre_volume; string _Symbol; int init() { int _GetLastError=0,cnt_copy=0,cnt_add=0; int temp[13]; //---- recordamos el símbolo del gráfico y el periodo _Symbol=Symbol(); _Period=Period(); _PeriodSec=_Period*60; hwnd=0; //---- abrimos el archivo para escribir en el historial string file_name=StringConcatenate("ALL",_Symbol,_Period,".hst"); HistoryHandle=FileOpenHistory(file_name,FILE_BIN|FILE_WRITE); if(HistoryHandle<0) { _GetLastError=GetLastError(); Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - Error #",_GetLastError); return(-1); } //---- Escribimos la cabecera del archivo FileWriteInteger(HistoryHandle,400,LONG_VALUE); FileWriteString(HistoryHandle,"Copyright © 2006, komposter",64); FileWriteString(HistoryHandle,StringConcatenate("ALL",_Symbol), 12); FileWriteInteger(HistoryHandle,_Period,LONG_VALUE); FileWriteInteger(HistoryHandle,Digits,LONG_VALUE); FileWriteInteger(HistoryHandle,0,LONG_VALUE); //timesign FileWriteInteger(HistoryHandle,0,LONG_VALUE); //last_sync FileWriteArray(HistoryHandle,temp,0,13); //+-----------------------------------------------------------------+ //| Procesamiento del historial //+-----------------------------------------------------------------+ int bars=Bars; pre_time=Time[bars-1]; for(int i=bars-1; i>=1; i--) { //---- Recordamos los parámetros de la barra now_open=Open[i]; now_high=High[i]; now_low=Low[i]; now_close=Close[i]; now_volume=Volume[i]; now_time=Time[i]/_PeriodSec; now_time*=_PeriodSec; //---- si hay barras omitidas, while(now_time>pre_time+_PeriodSec) { pre_time+=_PeriodSec; pre_time /= _PeriodSec; pre_time *= _PeriodSec; //---- si no es un fin de semana, if(SkipWeekEnd) { if(TimeDayOfWeek(pre_time)<=0 || TimeDayOfWeek(pre_time)>5) { continue; } if(TimeDayOfWeek(pre_time)==5) { if( TimeHour(pre_time) == 23 || TimeHour(pre_time + _PeriodSec) == 23 ) { continue; } } } //---- escribimos la barra obtenida en el archivo FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE); FileFlush(HistoryHandle); cnt_add++; } //---- escribimos la barra nueva en el archivo FileWriteInteger(HistoryHandle,now_time,LONG_VALUE); FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE); FileFlush(HistoryHandle); cnt_copy++; //---- recordamos el valor de la hora y el precio de cierre de la //---- barra registrada pre_close=now_close; pre_time=now_time/_PeriodSec; pre_time*=_PeriodSec; } last_fpos=FileTell(HistoryHandle); //---- mostramos las estadísticas Print("< - - - ",_Symbol,_Period,": hubo ",cnt_copy," barras, añadidas ",cnt_add," bars - - - >"); Print("< - - - Abra el gráfico para ver los resultados \"ALL", _Symbol,_Period,"\" - - - >"); //---- llamamos a la función start para que la barra 0 se dibuje inmediatamente start(); return(0); } //---- int start() { //+---------------------------------------------------------------+ //| Procesamos los ticks entrantes //+---------------------------------------------------------------+ //---- ponemos el "cursor" antes de la última barra //---- (esto se tiene que hacer en todos los lanzamientos a excepción del primero) FileSeek(HistoryHandle,last_fpos,SEEK_SET); //---- Recordamos los parámetros de la barra now_open=Open[0]; now_high=High[0]; now_low=Low[0]; now_close=Close[0]; now_volume=Volume[0]; now_time=Time[0]/_PeriodSec; now_time*=_PeriodSec; //---- si la barra se ha formado, if(now_time>=pre_time+_PeriodSec) { //---- escribimos la barra recién formada FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE); FileWriteDouble(HistoryHandle,pre_open,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_low,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_high,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_volume,DOUBLE_VALUE); FileFlush(HistoryHandle); //---- antes de escribir la barra 0 recordamos la posición en el archivo last_fpos=FileTell(HistoryHandle); } //---- si las barras omitidas han aparecido, while(now_time>pre_time+_PeriodSec) { pre_time+=_PeriodSec; pre_time /= _PeriodSec; pre_time *= _PeriodSec; //---- si no es fin de semana, if(SkipWeekEnd) { if(TimeDayOfWeek(pre_time)<=0 || TimeDayOfWeek(pre_time)>5) { continue; } if(TimeDayOfWeek(pre_time)==5) { if(TimeHour(pre_time)==23 || TimeHour(pre_time+_PeriodSec)==23) { continue; } } } //---- escribimos la barra omitida en el archivo FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE); FileFlush(HistoryHandle); //---- antes de escribir la barra 0 recordamos la posición en el archivo last_fpos=FileTell(HistoryHandle); } //---- escribimos la barra actual FileWriteInteger(HistoryHandle,now_time,LONG_VALUE); FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE); FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE); FileFlush(HistoryHandle); //---- recordamos los parámetros de la barra registrada pre_open=now_open; pre_high=now_high; pre_low=now_low; pre_close=now_close; pre_volume=now_volume; pre_time=now_time/_PeriodSec; pre_time*=_PeriodSec; //---- encontramos las nuevas cotizaciones a enviar if(hwnd==0) { hwnd = WindowHandle( StringConcatenate( "ALL", _Symbol ), _Period ); if( hwnd != 0 ) { Print("< - - - Gráfico ", "ALL"+_Symbol,_Period," se ha encontrado! - - - >"); } } //---- si la búsqueda es satisfactoria, actualizamos if(hwnd!=0) { PostMessageA(hwnd,WM_COMMAND,33324,0); } } //---- int deinit() { if(HistoryHandle>=0) { //---- cerramos el archivo FileClose(HistoryHandle); HistoryHandle=-1; } return(0); }
Llegados a este punto es importante hacer un apunte. La actualización del gráfico consume bastantes recursos del procesador porque el terminal tiene que cargar todas las barras del archivo. Si hay muchas barras en el archivo el terminal se puede ralentizar. Depende en gran medida del rendimiento del PC donde está instalado el terminal cliente de MT 4. En cualquier caso los recursos no son inagotables. El problema se puede resolver de una forma sencilla, basta con reducir la cantidad de barras que se visualizan en el gráfico a 10,000 ("Herramientas" – "Opciones" – "Gráficos", el parámetro "Max. barras en gráfico"). Ahora hay que reiniciar el terminal y adjuntar el experto:
El experto arregla el historial "inmediatamente" y se mantiene a la espera hasta que aparecen nuevos ticks. Después de 2 minutos, los mismos gráficos aparecen como ilustra esta imagen:
Como se ve, se ha añadido una barra de un minuto en el gráfico superior, mientras que la barra omitida se añade al gráfico inferior.
¡Esto significa que hemos conseguido el resultado deseado!
3. Escala
De momento tenemos un gráfico correcto. ¿Pero cómo tenemos que proceder cuando necesitamos abrir, por ejemplo, 10 gráficos sin barras omitidas? Abrir un gráfico adicional "temporal" para cada uno de los gráficos no es la mejor solución. Esto malgastaría recursos y, por otro lado, el trabajo se volvería poco agradable.
Vamos a crear un experto que pueda procesar cualquier número de gráficos. Esta es una solución más conveniente que nos ayudará a ahorrar recursos.
En consecuencia tenemos que modificar nuestro código para que pueda trabajar con varios gráficos simultáneamente:
-
añadir una variable externa que ayude a cambiar la lista de gráficos,
-
cambiar todas las variables con arrays que tengan una cantidad de elementos igual a la cantidad de gráficos que se tienen que procesar,
-
colocar el código entero en un bucle donde estos gráficos serán buscados, y
-
poner el bloque actualizado en el bucle infinito, independientemente de las cotizaciones. Si se listan varios símbolos, la hora de actualización también puede ser diferente.
Como resultado obtenemos este código (AllMinutes.mq4):
#include <WinUser32.mqh> //---- Lista de gráficos a procesar separados por comas (",") extern string ChartList="EURUSD1,GBPUSD1"; //---- Activar/desactivar el dibujo de las barras durante los fines de semana //---- Si es == true, los fines de semana permanecerán vacíos //---- Si es == false, los fines de semana se rellenan con las barras O=H=L=C extern bool SkipWeekEnd=true; //---- Frecuencia de actualización de los gráficos, en milisegundos //---- cuanto más alto es el valor, tanto menos recursos utilizará //---- el experto. extern int RefreshLuft=1000; int init() { start(); return(0); } int start() { int _GetLastError=0,cnt_copy=0,cnt_add=0,temp[13]; int Charts=0,pos=0,curchar=0,len=StringLen(ChartList); string cur_symbol="",cur_period="",file_name=""; string _Symbol[100]; int _Period[100],_PeriodSec[],_Bars[]; int HistoryHandle[],hwnd[],last_fpos[],pre_time[],now_time[]; double now_close[],now_open[],now_low[],now_high[],now_volume[]; double pre_close[],pre_open[],pre_low[],pre_high[],pre_volume[]; //---- contamos la cantidad de gráficos a procesar while(pos<=len) { curchar=StringGetChar(ChartList,pos); if(curchar>47 && curchar<58) { cur_period=cur_period+CharToStr(curchar); } else { if(curchar==',' || pos==len) { MarketInfo(cur_symbol,MODE_BID); if(GetLastError()==4106) { Alert("Símbolo desconocido ",cur_symbol,"!!!"); return(-1); } if(iClose(cur_symbol,StrToInteger(cur_period),0)<=0) { Alert("Periodo desconocido ",cur_period,"!!!"); return(-1); } _Symbol[Charts]=cur_symbol; _Period[Charts]=StrToInteger(cur_period); cur_symbol=""; cur_period=""; Charts++; } else { cur_symbol=cur_symbol+CharToStr(curchar); } } pos++; } Print("< - - - Encontrados ",Charts," gráficos correctos. - - - >"); ArrayResize(_Symbol,Charts); ArrayResize(_Period,Charts); ArrayResize(HistoryHandle,Charts); ArrayResize(hwnd,Charts); ArrayResize(last_fpos,Charts); ArrayResize(pre_time,Charts); ArrayResize(now_time,Charts); ArrayResize(now_close,Charts); ArrayResize(now_open,Charts); ArrayResize(now_low,Charts); ArrayResize(now_high,Charts); ArrayResize(now_volume,Charts); ArrayResize(pre_close,Charts); ArrayResize(pre_open,Charts); ArrayResize(pre_low,Charts); ArrayResize(pre_high,Charts); ArrayResize(pre_volume,Charts); ArrayResize(_PeriodSec,Charts); ArrayResize(_Bars,Charts); for(int curChart=0; curChart<Charts; curChart++) { _PeriodSec[curChart]=_Period[curChart] *60; //---- abrimos el archivo donde escribimos el historial file_name=StringConcatenate("ALL",_Symbol[curChart], _Period[curChart],".hst"); HistoryHandle[curChart]=FileOpenHistory(file_name, FILE_BIN|FILE_WRITE); if(HistoryHandle[curChart]<0) { _GetLastError=GetLastError(); Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - Error #",_GetLastError); continue; } //---- Escribimos la cabecera del archivo FileWriteInteger(HistoryHandle[curChart],400,LONG_VALUE); FileWriteString(HistoryHandle[curChart], "Copyright © 2006, komposter",64); FileWriteString(HistoryHandle[curChart], StringConcatenate("ALL", _Symbol[curChart]),12); FileWriteInteger(HistoryHandle[curChart],_Period[curChart], LONG_VALUE); FileWriteInteger(HistoryHandle[curChart], MarketInfo(_Symbol[curChart], MODE_DIGITS),LONG_VALUE); FileWriteInteger(HistoryHandle[curChart],0, LONG_VALUE); //timesign FileWriteInteger(HistoryHandle[curChart],0, LONG_VALUE); //last_sync FileWriteArray(HistoryHandle[curChart],temp,0,13); //+-----------------------------------------------------------+ //| Procesamiento del historial //+-----------------------------------------------------------+ _Bars[curChart]=iBars(_Symbol[curChart],_Period[curChart]); pre_time[curChart]=iTime(_Symbol[curChart], _Period[curChart],_Bars[curChart]-1); for(int i=_Bars[curChart]-1; i>=1; i--) { //---- Recordamos los parámetros de la barra now_open[curChart]=iOpen(_Symbol[curChart], _Period[curChart],i); now_high[curChart]=iHigh(_Symbol[curChart], _Period[curChart],i); now_low[curChart]=iLow(_Symbol[curChart], _Period[curChart],i); now_close[curChart]=iClose(_Symbol[curChart], _Period[curChart],i); now_volume[curChart]=iVolume(_Symbol[curChart], _Period[curChart],i); now_time[curChart]=iTime(_Symbol[curChart], _Period[curChart],i) /_PeriodSec[curChart]; now_time[curChart]*=_PeriodSec[curChart]; //---- si hay barras omitidas, while(now_time[curChart]>pre_time[curChart]+ _PeriodSec[curChart]) { pre_time[curChart]+=_PeriodSec[curChart]; pre_time[curChart] /= _PeriodSec[curChart]; pre_time[curChart] *= _PeriodSec[curChart]; //---- si no es un fin de semana, if(SkipWeekEnd) { if(TimeDayOfWeek(pre_time[curChart])<=0 || TimeDayOfWeek(pre_time[curChart])>5) { continue; } if(TimeDayOfWeek(pre_time[curChart])==5) { if(TimeHour(pre_time[curChart])==23 || TimeHour(pre_time[curChart]+_PeriodSec[curChart])==23) { continue; } } } //---- escribimos la barra omitida en el archivo FileWriteInteger(HistoryHandle[curChart],pre_time[curChart], LONG_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart],0, DOUBLE_VALUE); FileFlush(HistoryHandle[curChart]); cnt_add++; } //---- escribimos la barra nueva en el archivo FileWriteInteger(HistoryHandle[curChart],now_time[curChart], LONG_VALUE); FileWriteDouble(HistoryHandle[curChart], now_open[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], now_low[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], now_high[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], now_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], now_volume[curChart],DOUBLE_VALUE); FileFlush(HistoryHandle[curChart]); cnt_copy++; //---- recordamos la hora y el precio de cierre //---- de la barra registrada pre_close[curChart]=now_close[curChart]; pre_time[curChart]=now_time[curChart]/ _PeriodSec[curChart]; pre_time[curChart]*=_PeriodSec[curChart]; } last_fpos[curChart]=FileTell(HistoryHandle[curChart]); //---- mostramos las estadísticas Print("< - - - ",_Symbol[curChart],_Period[curChart],": hubo ", cnt_copy," barras, añadidas ",cnt_add," barras - - - >"); Print("< - - - Para ver los resultados, abra el gráfico \"ALL", _Symbol[curChart],_Period[curChart],"\" - - - >"); } //+---------------------------------------------------------------+ //| Procesamos los ticks entrantes //+---------------------------------------------------------------+ while(!IsStopped()) { RefreshRates(); for(curChart=0; curChart<Charts; curChart++) { //---- ponemos el "cursor" antes de la última barra //---- (esto se tiene que hacer en todos los lanzamientos a excepción del primero) FileSeek(HistoryHandle[curChart],last_fpos[curChart], SEEK_SET); //---- Recordamos los parámetros de la barra now_open[curChart]=iOpen(_Symbol[curChart], _Period[curChart],0); now_high[curChart]=iHigh(_Symbol[curChart], _Period[curChart],0); now_low[curChart]=iLow(_Symbol[curChart], _Period[curChart],0); now_close[curChart]=iClose(_Symbol[curChart], _Period[curChart],0); now_volume[curChart]=iVolume(_Symbol[curChart], _Period[curChart],0); now_time[curChart]=iTime(_Symbol[curChart], _Period[curChart],0) /_PeriodSec[curChart]; now_time[curChart]*=_PeriodSec[curChart]; //---- si se forma una barra, if(now_time[curChart]>=pre_time[curChart]+ _PeriodSec[curChart]) { //---- escribimos la barra formada FileWriteInteger(HistoryHandle[curChart],pre_time[curChart], LONG_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_open[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_low[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_high[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_volume[curChart],DOUBLE_VALUE); FileFlush(HistoryHandle[curChart]); //---- recordamos la posición en el archivo antes de registrar la barra cero last_fpos[curChart]=FileTell(HistoryHandle[curChart]); } //---- si aparecen barras omitidas, while(now_time[curChart]>pre_time[curChart]+_PeriodSec[curChart]) { pre_time[curChart] += _PeriodSec[curChart]; pre_time[curChart] /= _PeriodSec[curChart]; pre_time[curChart] *= _PeriodSec[curChart]; //---- si no es un fin de semana, if(SkipWeekEnd) { if(TimeDayOfWeek(pre_time[curChart])<=0 || TimeDayOfWeek(pre_time[curChart])>5) { continue; } if(TimeDayOfWeek(pre_time[curChart])==5) { if(TimeHour(pre_time[curChart])==23 || TimeHour(pre_time[curChart]+_PeriodSec[curChart])==23) { continue; } } } //---- escribimos la barra omitida en el archivo FileWriteInteger(HistoryHandle[curChart],pre_time[curChart], LONG_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], pre_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart],0, DOUBLE_VALUE); FileFlush(HistoryHandle[curChart]); //---- recordamos la posición en el archivo antes de registrar la barra cero last_fpos[curChart]=FileTell(HistoryHandle[curChart]); } //---- escribimos la barra actual FileWriteInteger(HistoryHandle[curChart],now_time[curChart], LONG_VALUE); FileWriteDouble(HistoryHandle[curChart], now_open[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], now_low[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], now_high[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], now_close[curChart],DOUBLE_VALUE); FileWriteDouble(HistoryHandle[curChart], now_volume[curChart],DOUBLE_VALUE); FileFlush(HistoryHandle[curChart]); //---- recordamos los parámetros de la barra escrita pre_open[curChart] = now_open[curChart]; pre_high[curChart] = now_high[curChart]; pre_low[curChart]=now_low[curChart]; pre_close[curChart]=now_close[curChart]; pre_volume[curChart]=now_volume[curChart]; pre_time[curChart]=now_time[curChart]/ _PeriodSec[curChart]; pre_time[curChart]*=_PeriodSec[curChart]; //---- encontramos la ventana donde enviar las nuevas cotizaciones if(hwnd[curChart]==0) { hwnd[curChart]=WindowHandle(StringConcatenate("ALL", _Symbol[curChart]), _Period[curChart]); if(hwnd[curChart]!=0) { Print("< - - - Gráfico ","ALL"+_Symbol[curChart], _Period[curChart]," encontrado! - - - >"); } } //---- y si la encontramos la actualizamos if(hwnd[curChart]!=0) { PostMessageA(hwnd[curChart],WM_COMMAND,33324,0); } } Sleep(RefreshLuft); } for(curChart=0; curChart<Charts; curChart++) { if(HistoryHandle[curChart]>=0) { //---- cerramos el archivo FileClose(HistoryHandle[curChart]); HistoryHandle[curChart]=-1; } } return(0); }
Ahora vamos a lanzar el experto en el gráfico de 5 minutos del EURUSD con el parámetro ChartList igual a "EURUSD1,GBPUSD1,EURGBP1" y abriremos los tres gráficos en modo sin conexión:
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/1407
![Las matemáticas de los algoritmos genéticos](https://c.mql5.com/2/13/133_1.png)
![El sistema experto 'Comentador'. Aplicación práctica de indicadores embebidos en programas MQL4](https://c.mql5.com/2/13/129_2.gif)
![Experto comercial universal: Los modos comerciales de las estrategias (Parte 1)](https://c.mql5.com/2/21/gu84ttj7g7r_klt2.png)
![Almacenamiento y visualización de la información](https://c.mql5.com/2/13/128_4.gif)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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