Descargar MetaTrader 5

Fundamentos de programación en MQL5 - Tiempo

7 mayo 2014, 15:40
Dmitry Fedoseev
0
936

Contenido


Introducción

El lenguaje MQL5 dispone de una serie de simples funciones para trabajar con el tiempo, el aprendizaje de las cuales no debe suponer dificultad alguna. El círculo de tareas cuya solución requiere el uso de fechas y horas es bastante reducido. Las tareas principales son las siguientes:

  • Realización de algunas acciones en un momento dado (Fig. 1). Pueden ser las tareas que se realizan cada día a la misma hora, el día especificado y a la hora especificada semanalmente, o simplemente el día con la fecha y la hora especificadas.

    Fig. 1. Momento de tiempo.
    Fig. 1. Momento de tiempo.

  • Permiso o prohibición para ejecutar algunas acciones durante el intervalo de tiempo determinado (sesión de tiempo). Puede tratarse de una sesión de tiempo durante 24 horas (cada 24 horas desde un momento de tiempo hasta el otro), permiso/prohibición para realizar algunas acciones durante determinados días de la semana, sesiones de tiempo desde una determinada hora de un día de la semana hasta otra hora de otro día de la semana, o simplemente durante un intervalo de fechas y horas establecidas.

    Fig. 2 Intervalo de tiempo.
    Fig. 2. Intervalo de tiempo.

En la práctica, el trabajo con el tiempo es una tarea bastante complicada. Las dificultades se deben a las particularidades del ambiente de funcionamiento de los EAs e indicadores, así como a las peculiaridades de medición de tiempo según el calendario:

  • Barras perdidas en el gráfico debido a la falta del cambio de precios. Sobre todo las omisiones se notan en los períodos de tiempo cortos: M1, M5, incluso en M15. Tampoco se descarta la posibilidad de omisiones en los períodos más largos.

  • Las cotizaciones de algunos Centros de Operaciones (DC por sus siglas en inglés, Dealing Center) incluyen las barras de domingo que prácticamente tienen que corresponder al lunes.

  • Los fines de semana. Para el lunes el día de ayer es el viernes, y no el domingo. Para el viernes mañana será el lunes, y no el sábado.

  • Aparte de la presencia de las barras de domingo, algunos DC siguen cotizando continuamente, es decir, durante todo el fin de semana. La actividad de los precios es bastante baja en comparación con los días laborales, pero está presente durante todo el fin de semana.

  • Diferencia entre los husos horarios del servidor comercial y el ordenador local (ordenador del trader con el terminal de trading). La hora de servidor de diferentes DC puede variar.

  • Cambio de horario de verano/invierno

Vamos a empezar este artículo con la teoría general sobre el tiempo. Luego analizaremos el uso de las funciones estándar MQL5 para trabajar con el tiempo, y a la vez veremos algunas técnicas de programación. Al final del artículo nos dedicaremos a la solución de problemas prácticos.

El artículo ha salido muy largo. Por eso es poco probable que los principiantes que han empezado a estudiar el lenguaje de programación MQL5 hace poco puedan con todo el material a la primera. Sería mejor dedicar tres días como mínimo para leerlo.


Peculiaridades de medición del tiempo según el calendario

Vamos a apartarnos un poco del tema y hablaremos de astronomía. Se sabe que la Tierra gravita alrededor del Sol, y a la vez gira sobre su propio eje. El eje de giro sobre sí misma está un poco inclinado respecto al eje de giro alrededor del Sol. El tiempo que necesita la Tierra para hacer un giro sobre su eje en coordenadas astronómicas (celestes, globales) se llama el día sideral o astronómico.

Para un habitante común de la Tierra (a diferencia de los astrónomos) el día sideral no representa ningún interés. Lo que importa más es el cambio entre el día y la noche. El tiempo requerido para un ciclo del cambio entre el día y la noche se llama el día solar. Si nos fijamos en el sistema solar desde el lado del Polo Norte de la Tierra (fig. 3), veremos que la Tierra se gira alrededor de su eje y también alrededor del Sol en sentido inverso de las agujas del reloj. Por esa razón, para hacer una vuelta alrededor de su eje respecto al Sol, la Tierra tiene que girar un poco más de 360 grados. Consecuentemente, el día solar es un poquito más largo que el día sideral.

Fig. 3. Dirección de rotación de la Tierra alrededor de su eje y alrededor del Sol (vista desde el lado del Polo Norte de la Tierra).
Fig. 3. Dirección de rotación de la Tierra alrededor de su eje y alrededor del Sol (vista desde el lado del Polo Norte de la Tierra).

Por razones de conveniencia y precisión de medición de tiempo, el día solar será utilizado como base de la medición de tiempo. El día se divide en 24 partes de una hora cada una, que a su vez se divide en 60 minutos, etc. Un día sideral dura 23 horas 56 minutos 4 segundos. Debido a que el eje de rotación de la Tierra está un poco inclinado respecto al eje de la perpendicular a su plano orbital, tiene lugar el ciclo de las estaciones lo que resulta ser muy observable para los habitantes de la Tierra.

El número de días solares en un año no es entero. En realidad, es un poco más: 365 días y 6 horas. Por eso el calendario se ajusta periódicamente, 4 veces al año (el año múltiple de 4). Se añade un día más: 29 de febrero (año bisiesto). No obstante, este ajuste tampoco es absolutamente exacto (se añade un poco de tiempo que sobra). Por esa razón, algunos años múltiples a 4 no son bisiestos. No se hace ningún ajuste del calendario en los años que terminan en "00" (múltiples de 100). Pero eso no es todo.

Si el año es múltiple de 100 y aparte es múltiple de 400, este año es bisiesto, y se realiza el ajuste del calendario. El año 1900 es múltiple de 4, es múltiple de 100 pero no es múltiple de 400, por eso no es bisiesto. El año 2000 es múltiple de 4, es múltiple de 100 y es múltiple de 400, por eso es bisiesto. El siguiente año múltiple de 4 y de 100 es 2100. También es múltiple de 400, por eso es bisiesto. Resulta que los lectores de este artículo viven en los tiempos cuando cada año múltiple de 4 será bisiesto. El próximo año múltiple de 100 y que va a ser bisiesto es 2400.


Husos horarios

La Tierra gira alrededor de su eje, por eso tenemos el cambio del día y de noche. Puede ser de día o de noche, o mejor dicho cualquier hora del día/noche, a la misma hora en diferentes lugares de la Tierra. Puesto que el día está dividido en 24 horas, la circunferencia de la Tierra está dividida en 24 secciones de 15 grados cada una que se denominan husos horarios. Por razones de conveniencia, los límites de husos horarios no siempre siguen las líneas de los meridianos. En vez de eso se trazan de acuerdo con los límites de división territorial administrativa: fronteras de los países, regiones, etc.

El meridiano con la longitud 0, también conocido como meridiano de Greenwich, ha sido adaptado como punto de referencia. Este meridiano recibe su nombre por pasar por la ciudad de Lóndres, barrio de Greenwich. El huso horario de Greenwich se extiende por ambos lados del meridiano de Greenwich a 7,5 grados. Al Este del huso horario de Greenwich (en el mapa a la derecha) se encuentran 12 husos horarios positivos (de +1 a +12), al Oeste (en el mapa a la izquierda), 12 negativos (de -1 a -12). Entonces, resulta que los husos horarios -12 y +12 tienen sólo 7,5 grados de ancho en vez de 15 grados que tienen los demás husos. Los husos horarios -12 y +12 se encuentran a ambos lados del meridiano con la longitud de 180 grados que lleva el nombre de la Línea internacional de cambio de fecha.

Supongamos que en Greenwich es mediodía (12:00), en el huso horario -12 serán las 00:00 (el comienzo del día), y en el huso horario +12 serán las 24:00, es decir, 00:00 del día siguiente. Lo mismo pasa con cualquier otra hora: el reloj va a marcar la misma hora pero las fechas en el calendario van a ser diferentes. El huso horario -12 no tiene el uso práctico, en vez de él se utiliza +12. El huso horario -11 tampoco tiene el uso práctico, en vez de él se utiliza +13. Probablemente eso está relacionado con las particularidades de relaciones económicas. Por ejemplo, el Estado de Samoa ubicado en el huso horario +13 tiene fuertes relaciones económicas con Japón por eso resulta más conveniente utilizar ahí el horario más próximo a Japón.

Además, en el mundo hay husos horarios excepcionales: -04:30, +05:45, etc. Los interesados pueden encontrar la lista de todos los husos horarios en los ajustes del tiempo de Windows.


Cambios de horario de verano y de invierno

Muchos países en el mundo adelantan una hora en sus relojes para la temporada de verano con el fin de usar más la luz diurna y ahorrar en consumo de energía eléctrica. Acerca de 80 países en el mundo realizan el cambio del horario de verano, los demás no lo hacen. Entre los países que realizan este cambio, algunos no lo hacen en todas sus regiones (inclusive EE.UU). Los países y regiones más grandes y desarrollados económicamente lo hacen: casi todos los países de Europa (inclusive Alemania, Inglaterra, Suiza), EE.UU (Nueva York, Chicago), Australia (Sydney), Nueva Zelanda (Wellington). Japón no practica el horario de verano. El 27 de marzo de 2011 Rusia adelantó sus relojes por ultima vez. El cambio inverso en octubre no lo hizo. Desde aquel entonces Rusia no practica más el cambio de horario de verano/invierno.

El procedimiento del cambios de horario de verano/invierno es diferente en distintos países. En los Estados Unidos el cambio de horario de verano se realiza el segundo domingo del marzo a las 02:00 hora local, el cambio de horario de invierno se hace el primer domingo de noviembre a las 02:00. En Europa el cambio de horario de verano se realiza el último domingo del marzo a las 2:00, el cambio de horario de invierno se hace el último domingo de noviembre a las 3:00. Además, en vez de hacerlo a la hora local, el cambio se realiza en todos los países simultáneamente: Londres a las 02:00, Berlín a las 03:00, etc., según el huso horario. El cambio inverso se realiza cuando en Londres son las 03:00, en Berlín son las 04:00, etc.

Australia y Nueva Zelanda se encuentran en el hemisferio austral. Ahí el verano empieza cuando en el hemisferio boreal empieza el invierno. Consecuentemente, en Australia el cambio de horario de verano se realiza el primer domingo del octubre, la vuelta al horario de invierno se hace el primer domingo de abril. Es difícil ser más exacto hablando de Australia. Es que este país es muy descentralizado, en diferentes regiones se rigen por diferentes normas en cuanto al cambio de horario de verano/invierno. En nueva Zelanda el cambio de horario de verano se realiza el último domingo del septiembre a las 02:00, y la vuelta del horario se hace el primer domingo de abril a las 03:00.


Patrón de tiempo

Como hemos mencionado antes, el día solar será utilizado como base de la medición de tiempo. Consecuentemente, el patrón de tiempo es el tiempo medio de Greenwich. A su base se calcula el tiempo en los demás husos horarios. Para el tiempo medio de Greenwich suelen utilizar la abreviatura GMT (Greenwich Mean Time, por sus siglas en inglés -Tiempo medio de Greenwich).

Sin embargo, como se ha descubierto, la rotación de la Tierra es un poco irregular. Por eso, desde hace un tiempo hemos empezado a utilizar el reloj atómico para medir el tiempo. Ahora el nuevo estándar de tiempo es el UTC (Coordinated Universal Time, Tiempo universal coordinado). Actualmente, el UTC es el principal estándar de tiempo para todo el mundo según el cual se realiza la medición de tiempo en todos los husos horarios, con ajustes necesarios hechos para el horario de verano. El UTC no está sujeto al cambio de estaciones (horario de verano/invierno).

Debido a que el GMT (medido por el Sol) no corresponde un poco al UTC (medido por el reloj atómico), cada 500 días entre el UTC y el GMT surge una diferencia aproximadamente de un segundo. Por eso, periódicamente se realiza el ajuste de un segundo el 30 de junio o el 31 de diciembre.


Formatos de fecha y hora

En diferentes países se utilizan diferentes formatos de fecha. Por ejemplo, en Rusia habitualmente primero escriben el día, luego va el mes, y al final se pone el año. Los números de la fecha van separados con puntos. Por ejemplo, 01.12.2012 (el primero de diciembre de 2012). En EE.UU primero ponen el mes, luego el día y al final el año. Los números de la fecha separan con barra "/". Además del punto y la barra inclinada "/" algunos estándares admiten también el uso del guión "-" para separar los números de la fecha. Cuando apuntamos el tiempo, las horas, minutos y segundos se separan con el signo de dos puntos ":", por ejemplo, 12:15:30 (12 horas, 15 minutos, 30 segundos).

Existe un simple modo para indicar el formato de fecha y hora. Por ejemplo, "dd.mm.yyyy" significa que primero va el día (fecha del mes que debe ocupar dos dígitos; si el día es un número de 1 a 9, se añade cero al principio), luego el mes (también tiene que tener dos dígitos), y luego se pone el año expresado con cuatro dígitos. "d-m-yy" significa que primero se pone el día (puede contener sólo un dígito), como separador se utiliza el guión "-", luego el mes (se acepta un dígito) y al final se pone el año de dos dígitos, por ejemplo: 1-12-12 (el 1 de enero de 2012).

La hora de la fecha se separa con espacio. En el formato se utilizan las letras "h" (horas), "m" (minutos), "s" (segundos), y también se indica el número de dígitos necesario. Por ejemplo, "hh:mi:ss" significa que primero van las horas (para los valores de 1 a 9 añadimos cero al principio), luego los minutos (tienen que ser dos dígitos), y al final los segundos (dos dígitos). Las horas, minutos y segundos van separados con dos puntos.

Desde el punto de vista del programador, el formato de fecha y hora más correcto será el formato "yyyy.mm.dd hh:mi:ss". Después de organizar las cadenas con fechas utilizando este formato, ellas se ordenan en orden cronológico. Supongamos que Usted cada día almacena los datos en los archivos y los guarda en la misma carpeta: si para los nombres de estos archivos se han utilizado las fechas en este formato, los archivos en la carpeta estarán cómodamente organizados según el orden correcto.

Bueno, hemos terminado con la parte teórica, ahora pasamos a la parte práctica.


Tiempo en MQL5

En MQL5 el tiempo se mide en segundos transcurridos desde el inicio de así llamada la época UNIX que empezó el 1 de enero de 1970. Para almacenar el tiempo, se utiliza la variable datetime. El valor mínimo de una variable del tipo datetime es 0 (corresponde a la fecha del comienzo de la época), el valor máximo es 32 535 244 799 (corresponde al 31 de diciembre de 3000, la hora 23:59:59).


Determinar la hora actual del servidor

Para determinar la hora actual, se utiliza la función TimeCurrent(). Esta función devuelve la última hora conocida del servidor:

datetime tm=TimeCurrent();
//--- mostrar resultado
Alert(tm);

La misma hora del servidor se utiliza para indicar la hora de las barras en el gráfico. La última hora conocida del servidor es la hora del último cambio del precio de cualquiera de los símbolos abiertos en la ventana "Observación del Mercado". Si en la ventana "Observación del Mercado" hay un símbolo EURUSD, la función TimeCurrent() va a devolver la hora del último cambio del precio de EURUSD. Puesto que en la ventana "Observación del Mercado" generalmente figura una gran cantidad de símbolos, la función prácticamente devuelve la hora actual del servidor. Sin embargo, los precios no se cambian los fines de semana, por eso la función devuelve un valor que se diferencia sustancialmente de la hora real del servidor.

Si necesita averiguar la hora del servidor para un determinado símbolo (la hora del último cambio del precio), puede utilizar la función SymbolInfoInteger() con el identificador SYMBOL_TIME:

datetime tm=(datetime)SymbolInfoInteger(_Symbol,SYMBOL_TIME);
//--- mostrar resultado
Alert(tm);


Determinar la hora actual local

La hora local (la hora que marca el reloj de su ordenador) se determina por la función TimeLocal():

datetime tm=TimeLocal();
//--- mostrar resultado
Alert(tm);

En la práctica, cuando va a programar los EAs e indicadores, tendrá que utilizar principalmente la hora del servidor. La hora local conviene utilizarla para los mensajes en las alertas, en el diario: para el usuario resulta más cómodo comparar la hora del mensaje con la hora marcada en el reloj de su ordenador determinando así cuánto tiempo ha pasado desde la llegada del mensaje. No obstante, el terminal MetaTrader 5 añade la hora a los mensajes visualizados por las funciones Alert() y Print() de forma automática, por eso la necesidad de usar la función TimeLocal() puede surgir muy raramente.


Visualizar la fecha/hora

Por favor, tenga presente que en el ejemplo de arriba el valor de la variable tm se imprime directamente a través de la función Alert(). Pero el valor se visualiza en un formato cómodo para leer, por ejemplo, "2012.12.05 22:31:57". Eso se debe a que la función Alert() convierte los argumentos pasados a ella al tipo string (lo mismo ocurre cuando se utiliza la función Print(), Comment(), durante la visualización en los archivos de texto y csv). Cuando generamos un mensaje de texto que incluye un valor de la variable del tipo datetime, nosotros mismos tenemos que encargarnos de la conversión de los tipos. Realizamos la conversión al tipo string si necesitamos obtener el tiempo formateado, o al tipo long y luego al tipo string si lo que necesitamos es un valor numérico:

datetime tm=TimeCurrent();
//--- mostrar resultado
Alert("Formateado: "+(string)tm+", en segundos: "+(string)(long)tm);

Puesto que los rangos de las variables long y ulong cubren el rango de valores de la variable del tipo datetime, también pueden ser utilizadas para almacenar el tiempo. Pero en este caso, para visualizar el tiempo formateado, tenemos que convertir el tipo long al tipo datetime, luego al string, o sólo al tipo string si queremos visualizar un valor numérico:

long tm=TimeCurrent();
//--- mostrar resultado
Alert("Formateado: "+(string)(datetime)tm+", en segundos: "+(string)tm);


Formatear la fecha/hora

Cómo formatear la fecha/hora se explica en el artículo "Fundamentos de programación en MQL5 - Cadenas de caracteres" en la sección "Conversión de diversas variables a una cadena". Vamos a recordar brevemente los momentos principales. Aparte de la posibilidad de convertir los tipos, el lenguaje MQL5 ofrece la función TimeToString() que permite especificar el formato de visualización de la fecha y hora al convertirlas a una cadena:

datetime tm=TimeCurrent();
string str1="Fecha y hora con minutos: "+TimeToString(tm);
string str2="Sólo fecha: "+TimeToString(tm,TIME_DATE);
string str3="Sólo hora con minutos: "+TimeToString(tm,TIME_MINUTES);
string str4="Sólo hora con segundos: "+TimeToString(tm,TIME_SECONDS);
string str5="Fecha y hora con segundos: "+TimeToString(tm,TIME_DATE|TIME_SECONDS);
//--- mostrar resultados
Alert(str1);
Alert(str2);
Alert(str3);
Alert(str4);
Alert(str5);

La función TimeToString() puede aplicarse a las variables del tipo datetime y a las variables del tipo long, ulong y a algunos otros tipos de variables enteras, pero no hay que usarla para almacenar el tiempo.

Al formatear un mensaje de texto utilizando la función StringFormat(), tenemos que encargarnos de convertir el tipo.

  • Al almacenar el tiempo en una variable del tipo datetime:

    datetime tm=TimeCurrent();
    //--- formatear cadena
    string str=StringFormat("Formateado: %s, en segundos: %I64i",(string)tm,tm);
    //--- mostrar resultado
    Alert(str);
  • Al almacenar el tiempo en una variable del tipo long:

    long tm=TimeCurrent();
    //--- formatear cadena
    string str=StringFormat("Formateado: %s, en segundos: %I64i",(string)(datetime)tm,tm);
    //--- mostrar resultado
    Alert(str);
  • O utilizar la función TimeToString():

    datetime tm=TimeCurrent();
    //--- formatear cadena
    string str=StringFormat("Fecha: %s",TimeToString(tm,TIME_DATE));
    //--- mostrar resultado
    Alert(str);


Convertir el tiempo al número, suma y resta del tiempo

Para convertir una fecha formateada (cadena) al número (cantidad de segundos desde el inicio de la época), se utiliza la función StringToTime():

datetime tm=StringToTime("2012.12.05 22:31:57");
//--- mostrar resultado
Alert((string)(long)tm);

Como resultado de trabajo de este ejemplo, se visualizará "1354746717": tiempo en segundos que corresponde a la fecha "2012.12.05 22:31:57".

Cuando el tiempo está representado como un número, es muy cómodo realizar diferentes operaciones con él. Por ejemplo, se puede averiguar la fecha y la hora en el pasado o en el futuro. Ya que el tiempo se mide en segundos, tenemos que sumar el intervalo de tiempo expresado en segundos. Sabiendo que un minuto es igual a 60 segundos, una hora es igual a 60 minutos o 3600 segundos, no supone ningún problema calcular cualquier intervalo de tiempo.

Restamos o sumamos 1 hora (3600 segundos) y obtenemos el tiempo que era hace una hora o que va a ser dentro de una hora.

datetime tm=TimeCurrent();
datetime ltm=tm-3600;
datetime ftm=tm+3600;
//--- mostrar resultado
Alert("Ahora: "+(string)tm+", hace una hora: "+(string)ltm+", dentro de una hora: "+(string)ftm);

La fecha pasada a la función StringToTime() puede ser incompleta. Podemos pasar la fecha sin la hora o la hora sin la fecha. Si pasamos la fecha sin la hora, la función devuelve el valor de la hora 00:00:00 para la fecha especificada:

datetime tm=StringToTime("2012.12.05");
//--- mostrar resultado
Alert(tm);

Si pasamos sólo la hora, la función devuelve el valor que corresponde a la hora de la fecha de hoy:

datetime tm=StringToTime("22:31:57");
//--- mostrar resultado
Alert((string)tm);

Además, se puede pasar la hora sin segundos. Si se pasa sólo la fecha, de la hora se pasan solo las horas. Es poco probable que necesitemos aplicarlo en la practica. Si alguien tiene curiosidad, puede probarlo por sí mismo.


Componentes de fecha y hora

Para averiguar los valores de los componentes de fecha y hora (año, mes, día, etc.), se utiliza la función TimeToStruct() y la estructura MqlDateTime. La estructura se pasa a la función por referencia. Después de la ejecución de la función, la estructura se llenará con valores de los componentes de la fecha pasada a ella:

datetime    tm=TimeCurrent();
MqlDateTime stm;
TimeToStruct(tm,stm);
//--- mostrar componentes de la fecha
Alert("Año: "        +(string)stm.year);
Alert("Mes: "      +(string)stm.mon);
Alert("Día: "      +(string)stm.day);
Alert("Hora: "        +(string)stm.hour);
Alert("Minuto: "     +(string)stm.min);
Alert("Segundo: "    +(string)stm.sec);
Alert("Día de la semana: "+(string)stm.day_of_week);
Alert("Día del año: "  +(string)stm.day_of_year);

Fíjense en que la estructura contiene no sólo los campos con los componentes de la fecha, sino también tiene dos campos adicionales: día de la semana (campo day_of_week) y día del año (day_of_year). El día de la semana se cuenta a partir de 0 (0 es domingo, 1 es lunes, etc.). Los días del año también empiezan a contar a partir de 0. Los demás valores siguen el orden general de conteo (el mes a partir del 1, el día a partir del 1).

Existe otro modo de llamar a la función TimeCurrent(). La estructura del tipo MqlDateTime se pasa a la función por referencia. Después de la llamada a la función, la estructura se llenará con los componentes de la fecha actual:

MqlDateTime stm;
datetime tm=TimeCurrent(stm);
//--- mostrar componentes de la fecha
Alert("Año: "        +(string)stm.year);
Alert("Mes: "      +(string)stm.mon);
Alert("Día: "      +(string)stm.day);
Alert("Hora: "        +(string)stm.hour);
Alert("Minuto: "     +(string)stm.min);
Alert("Segundo: "    +(string)stm.sec);
Alert("Día de la semana: "+(string)stm.day_of_week);
Alert("Día del año: "  +(string)stm.day_of_year);

La misma llamada también es posible para la función TimeLocal().


Generar fecha a base de sus componentes

Usted puede realizar también la conversión inversa de la estructura MqlDateTime al tipo datetime. Para eso se utiliza la función StructToTime().

Vamos a determinar la hora que era exactamente hace un mes. La duración del mes no es un valor constante. Algunos meses tienen 30 días, otros tienen 31 días, y el febrero tiene 20 o 29 días. Por eso el método de suma y resta del tiempo que hemos visto más arriba no nos vale. Descomponemos las fechas, reducimos el valor del mes en 1, y si el valor del mes es 1, le establecemos el valor de 12 y reducimos el valor del año en 1:

datetime tm=TimeCurrent();
MqlDateTime stm;
TimeToStruct(tm,stm);
if(stm.mon==1)
  {
   stm.mon=12;
   stm.year--;
  }
else
  {
   stm.mon--;
  }
datetime ltm=StructToTime(stm);
//--- mostrar resultado
Alert("Ahora: "+(string)tm+", hace un mes: "+(string)ltm);


Determinar la hora de las barras

Durante la creación de un indicador, el editor MetaEditor crea automáticamente una de dos versiones de la función OnCalculate().

Versión 1:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   return(rates_total);
  }

Versión 2:

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   return(rates_total);
  }

En los parámetros de la primera versión de la función se encuentra el array time[] cuyos elementos incluyen la hora de todas las barras.

Si se utiliza la segunda versión, así como en todos los casos de programación de los Asesores Expertos y acceso de los indicadores a la hora de las barras de otros períodos de tiempo, se utiliza la función CopyTime(). Hay tres versiones de esta función. En todas las versiones de la función los dos primeros parámetros de la función definen el símbolo y el período del gráfico para el que se determina la hora de las barras. El último parámetro define el array que almacena los valores devueltos. Otros dos parámetros intermedios dependen de la versión de la función utilizada.

CopyTime - Versión 1 Se indica el índice de la barra y el número de elementos copiados:

//--- variables para indicar los parámetros de la función
int start = 0; // índice de la barra
int count = 1; // número de barras
datetime tm[]; // array en el que se almacena la hora devuelta de las barras
//--- copiado de la hora 
CopyTime(_Symbol,PERIOD_D1,start,count,tm);
//--- mostrar resultado
Alert(tm[0]);

En este ejemplo se copia la hora de la última barra con el período de tiempo D1. La versión de la función que utilizamos depende del tipo de parámetros que se le pasan. En este ejemplo se utilizan las variables del tipo int. Eso significa que necesitamos obtener la hora por el número de la barra para la cantidad de las barras especificada.

Cuando se utiliza la función CopyTime(), las barras se cuentan de derecha a izquierda empezando desde cero. Para los valores obtenidos (el último parámetro) se utiliza un array dinámico. La misma función CopyTime() lo acota hasta conseguir el tamaño necesario. También se puede utilizar un array estático. Pero en este caso el tamaño del array tiene que corresponder exactamente al número de los elementos solicitados (el valor del cuarto parámetro).

Es importante comprender el orden de los elementos en el array que se devuelve, cuando la hora se obtiene de diferentes barras al mismo tiempo. Las barras del gráfico se cuentan de derecha a izquierda empezando el copiado desde la primera barra especificada, mientras que en el array los elementos se encuentran de izquierda a derecha:

//--- variables para indicar los parámetros de la función
int start = 0; // índice de la barra
int count = 2; // número de barras
datetime tm[]; // array en el que se almacena la hora devuelta de las barras
//--- copiado de la hora 
CopyTime(_Symbol,PERIOD_D1,start,count,tm);
//--- mostrar resultado
Alert("Hoy: "+(string)tm[1]+", ayer: "+(string)tm[0]);

Como resultado de ejecución de este código, el elemento número 0 del array tm va a contener la hora de la barra de ayer, el elemento 1 va a contener la hora de la barra de hoy.

En algunas ocasiones, podría ser más conveniente si la hora en el array estuviera organizada en el mismo orden que el cálculo de las barras en el gráfico. Para eso podemos utilizar la función ArraySetAsSeries():

//--- variables para indicar los parámetros de la función
int start = 0; // índice de la barra
int count = 2; // número de barras
datetime tm[]; // array en el que se almacena la hora devuelta de las barras
ArraySetAsSeries(tm,true); // indicamos que el array estará organizado en orden inverso de los elementos
//--- copiado de la hora 
CopyTime(_Symbol,PERIOD_D1,start,count,tm);
//--- mostrar resultado
Alert("Hoy: "+(string)tm[0]+", ayer: "+(string)tm[1]);

Ahora la hora de la barra de hoy se encuentra en el elemento con el índice 0, y la hora de la barra de ayer, en el elemento con el índice 1.

CopyTime - Versión 2. En este caso cuando se llama a la función CopyTime(), se especifica la hora de la barra a partir de la cual se empieza el copiado y el número de las barras a copiar. Esta opción conviene para determinar la hora del período de tiempo (timeframe) mayor que contiene la barra del período menor:

//--- obtenemos la hora de la última barra del período M5
int m5_start=0; 
int m5_count=1;
datetime m5_tm[];
CopyTime(_Symbol,PERIOD_M5,m5_start,m5_count,m5_tm);
//--- determinamos la hora de la barra en el período H1 que contiene la barra M5
int h1_count=1;
datetime h1_tm[];
CopyTime(_Symbol,PERIOD_H1,m5_tm[0],h1_count,h1_tm);
//--- mostrar resultado
Alert("Barra en М5 con la hora "+(string)m5_tm[0]+" forma parte de la barra H1 con la hora "+(string)h1_tm[0]);

La tarea se complica si necesitamos determinar la hora de la barra con el período menor a partir del cual se empieza la barra del período mayor. En el período menor puede faltar la barra con la misma hora que tiene la barra del período mayor. En este caso, obtenemos la hora de la última barra en el período menor que forma parte de la barra anterior del período mayor. Entonces, hay que determinar el número de la barra del período menor y obtener la hora de la siguiente barra.

A continuación, ponemos el ejemplo en forma de la función lista para usar en la práctica:

bool LowerTFFirstBarTime(string aSymbol,
                         ENUM_TIMEFRAMES aLowerTF,
                         datetime aUpperTFBarTime,
                         datetime& aLowerTFFirstBarTime)
  {
   datetime tm[];
//--- determinamos la hora de la barra del período menor que corresponde a la hora de la barra del período mayor 
   if(CopyTime(aSymbol,aLowerTF,aUpperTFBarTime,1,tm)==-1)
     {
      return(false);
     }
   if(tm[0]<aUpperTFBarTime)
     {
      //--- hemos obtenido la hora de la barra anterior
      datetime tm2[];
      //--- determinamos la hora de la última barra del período menor 
      if(CopyTime(aSymbol,aLowerTF,0,1,tm2)==-1)
        {
         return(false);
        }
      if(tm[0]<tm2[0])
        {
         //--- hay barra que va después de la barra del período menor que precede la apertura de la barra del período mayor
         int start=Bars(aSymbol,aLowerTF,tm[0],tm2[0])-2;
         //--- la función Bars() devuelve el número de barras a partir de la barra con la hora tm[0] hasta
         //--- la barra con la hora tm2[0], y necesitamos determinar el índice de la barra que va después de 
         //--- la barra con la hora tm2[2], por eso restamos 2
         if(CopyTime(aSymbol,aLowerTF,start,1,tm)==-1)
           {
            return(false);
           }
        }
      else
        {
         //--- no hay barra del período menor que forma parte de la barra del período mayor
         aLowerTFFirstBarTime=0;
         return(true);
        }
     }
//--- asignamos el valor obtenido a la variable 
   aLowerTFFirstBarTime=tm[0];
   return(true);
  }

Parámetros de la función:

  • aSymbol - símbolo;
  • aLowerTF - período menor;
  • aUpperTFBarTime - hora de la barra del período mayor;
  • aLowerTFFirstBarTime - valor del período menor que se devuelve.

En el código siempre se comprueba si la llamada a la función CopyTime() ha sido realizada con éxito. En caso del error la función devuelve false. La hora de la barra se devuelve por referencia a través del parámetro aLowerTFFirstBarTime.

Ejemplo del uso de la función:

//--- hora de la barra del período mayor H1
   datetime utftm=StringToTime("2012.12.10 15:00");
//--- variable para el valor devuelto
   datetime val;
//--- llamada a la función
   if(LowerTFFirstBarTime(_Symbol,PERIOD_M5,utftm,val))
     {
      //--- mostrar resultado en caso del trabajo exitoso de la función
      Alert("val = "+(string)val);
     }
   else
     {
      //--- en caso del error paramos el trabajo de la función de la que se invoca la función LowerTFFirstBarTime()
      Alert("Error del copiado de la hora");
      return;
     }

Si la barra del período menor que forma parte de la barra del período mayor no existe, esto puede ocurrir si a la función le ha sido pasada la hora de la barra inexistente del período mayor. En este caso, la función devuelve true y en la variable aLowerTFFirstBarTime se escribe la hora 0. Si la barra del período mayor existe, siempre habrá por lo menos una barra en cada período menor.

Es un poco más fácil encontrar la hora de la última barra del período menor que forma parte de la barra del período mayor. Calculamos la hora de la siguiente barra del período mayor y por el valor obtenido determinamos la hora de la barra correspondiente del período menor. Si la hora obtenida es igual a la hora calculada del período mayor, entonces tenemos que encontrar la hora de la barra anterior en el período menor, si la hora es menor, entonces la hora obtenida es correcta.

A continuación, ponemos el ejemplo en forma de la función lista para usar en la práctica:

bool LowerTFLastBarTime(string aSymbol,
                        ENUM_TIMEFRAMES aUpperTF,
                        ENUM_TIMEFRAMES aLowerTF,
                        datetime aUpperTFBarTime,
                        datetime& aLowerTFFirstBarTime)
  {
//--- hora de la siguiente barra del período mayor
   datetime NextBarTime=aUpperTFBarTime+PeriodSeconds(aUpperTF);
   datetime tm[];
   if(CopyTime(aSymbol,aLowerTF,NextBarTime,1,tm)==-1)
     {
      return(false);
     }
   if(tm[0]==NextBarTime)
     {
      //--- Hay barra del período menor que corresponde a la hora de la siguiente barra del período mayor.
      //--- Determinamos la hora de la última barra en el período menor
      datetime tm2[];
      if(CopyTime(aSymbol,aLowerTF,0,1,tm2)==-1)
        {
         return(false);
        }
      //--- determinamos el índice de la barra anterior en el período menor
      int start=Bars(aSymbol,aLowerTF,tm[0],tm2[0]);
      //--- determinamos la hora de esta barra
      if(CopyTime(aSymbol,aLowerTF,start,1,tm)==-1)
        {
         return(false);
        }
     }
//--- asignamos el valor obtenido a la variable 
   aLowerTFFirstBarTime=tm[0];
   return(true);
  }

Parámetros de la función:

  • aSymbol - símbolo;
  • aUpperTF - período mayor;
  • aLowerTF - período menor;
  • aUpperTFBarTime - hora de la barra del período mayor;
  • aLowerTFFirstBarTime - valor del período menor que se devuelve.

En el código siempre se comprueba si la llamada a la función CopyTime() ha sido realizada con éxito. En caso del error la función devuelve false. La hora de la barra se devuelve por referencia a través del parámetro aLowerTFFirstBarTime.

Ejemplo del uso de la función:

//--- hora de la barra del período mayor H1
datetime utftm=StringToTime("2012.12.10 15:00");
//--- variable para el valor devuelto
datetime val;
//--- llamada a la función
if(LowerTFLastBarTime(_Symbol,PERIOD_H1,PERIOD_M5,utftm,val))
  {
//--- mostrar resultado en caso del trabajo exitoso de la función
   Alert("val = "+(string)val);
  }
else
  {
//--- en caso del error paramos el trabajo de la función de la que se invoca la función LowerTFFirstBarTime()
   Alert("Error del copiado de la hora");
   return;
  }

¡Atención! Se supone que a la función se pasa la hora exacta del período mayor. Si no sabemos la hora exacta de la barra y sabemos sólo la hora de un determinado tick dentro de esa barra o de la barra del período menor que forma parte de la barra del período mayor, necesitaremos realizar la normalización de la hora. En el terminal MetaTrader 5 se utilizan los períodos de tiempo que dividen el día en un número entero de barras. Determinamos el número de la barra desde el inicio de la época y luego multiplicamos por la duración de la barra en segundos:

datetime BarTimeNormalize(datetime aTime,ENUM_TIMEFRAMES aTimeFrame)
  {
   int BarLength=PeriodSeconds(aTimeFrame);
   return(BarLength*(aTime/BarLength));
  }

Parámetros de la función:

  • aTime - hora;
  • aTimeFrame - período de tiempo.

Ejemplo del uso de la función:

//--- hora a normalizar
datetime tm=StringToTime("2012.12.10 15:25");
//--- llamada a la función
datetime tm1=BarTimeNormalize(tm,PERIOD_H1);
//--- mostrar resultado
Alert(tm1);

Por otra parte, no es sólo la normalización de la hora, sino también un modo más para determinar la hora del período mayor a base de la hora del período menor.

CopyTime - Versión 3. En este caso, después de la llamada a la función CopyTime(), especificamos el intervalo de las horas de las barras de las cuales hay que realizar el copiado de tiempo. De esta manera, resulta fácil obtener todas las barras del período menor que forman parte de la barra del período mayor:

//--- hora del inicio de la barra del período H1
datetime TimeStart=StringToTime("2012.12.10 15:00");
//--- hora estimada de la última barra en
//--- el período M5
datetime TimeStop=TimeStart+PeriodSeconds(PERIOD_H1)-PeriodSeconds(PERIOD_M5);
//--- copiamos la hora
datetime tm[];
CopyTime(_Symbol,PERIOD_M5,TimeStart,TimeStop,tm);
//--- mostrar resultado
Alert("Copiado barras: "+(string)ArraySize(tm)+", la primera barra: "+(string)tm[0]+", la última: "+(string)tm[ArraySize(tm)-1]);


Determinar la hora de inicio del día y tiempo transcurrido desde el inicio del día

El modo más evidente para determinar la hora de inicio del día según la hora especificada es descomponer el tiempo en sus componentes, poner a cero las horas, minutos, segundos y volver a componerlos. No obstante, hay un manera más sencilla de hacerlo. Un día se compone de 86 400 segundos. Necesitamos obtener un número entero de la división de la hora en la cantidad de segundos de un día y volver a multiplicarlo por la cantidad de segundos de un día:

datetime tm=TimeCurrent();
tm=(tm/86400)*86400;
//--- mostrar resultado
Alert("Hora de inicio del día: "+(string)tm);

¡Atención! Este método es válido sólo para las variables de números enteros. Si en nuestros cálculos aparecen las variables del tipo double, float, tenemos que truncar la parte fraccionaria utilizando la función MathFloor():

MathFloor(tm/86400)*86400

Aparte de eso, después de la multiplicación, los valores tienen que ser normalizados a través de la función NormalizeDouble(). Puesto que necesitamos obtener un valor entero, podemos utilizar también la función de redondeo MathRound():

MathRound(MathFloor(tm/86400)*86400)

Al utilizar las variables enteras, la resta se trunca automáticamente. A la hora de trabajar con el tiempo, muy raras veces nos puede surgir la necesidad de utilizar las variables del tipo double, float. Seguramente su uso va a indicar en un enfoque totalmente equivocado.

Para determinar la cantidad de segundos transcurridos desde el inicio del día, sólo tenemos que coger el resultado de la división de la hora por 86 400:

datetime tm=TimeCurrent();
long seconds=tm%86400;
//--- mostrar resultado
Alert("Desde el inicio del día han pasado: "+(string)seconds+" seg.");

Podemos utilizar los métodos similares para convertir el tiempo obtenido en segundos a las horas, minutos, segundos. Lo implementamos como una función:

int TimeFromDayStart(datetime aTime,int &aH,int &aM,int &aS)
  {
//--- Cantidad de segundos transcurridos desde el inicio del día (aTime%86400),
//--- dividimos por la cantidad de segundos en una hora, obtenemos la cantidad de horas
   aH=(int)((aTime%86400)/3600);
//--- Cantidad de segundos transcurridos desde el inicio de la última hora (aTime%3600),
//--- dividimos por la cantidad de segundos en un minuto, obtenemos la cantidad de minutos
   aM=(int)((aTime%3600)/60);
//--- Cantidad de segundos transcurridos desde el inicio del último minuto 
   aS=(int)(aTime%60);
//--- Cantidad de segundos transcurridos desde el inicio del día
   return(int(aTime%86400));
  }

El primer parámetro transferido es la hora. Los demás parámetros se utilizan para los valores devueltos: aH - horas, aM - minutos, aS - segundos. La función devuelve el número total de segundos desde el inicio del día. Comprobamos la función:

datetime tm=TimeCurrent();
int t,h,m,s;
t=TimeFromDayStart(tm,h,m,s);
//--- mostrar resultados
Alert(Desde el inicio del día han pasado ",t," s, lo que representa ",h," h, ",m," m, ",s," s ");

El cálculo del número del día se puede utilizar en los indicadores para determinar la barra del inicio del día:

bool NewDay=(time[i]/86400)!=(time[i-1]/86400);

Se supone que se utiliza la numeración de izquierda a derecha, donde time[i] es la hora de la barra actual, time[i-1] es la hora de la barra anterior.


Determinar la hora de inicio de la semana y tiempo transcurrido desde el inicio de la semana

Resulta un poco más complicado determinar la hora del inicio de la semana que el inicio del día. Aunque el número de días que tiene una semana es constante y se puede calcular su duración en segundos (serán 604 800 segundos), no es suficiente calcular el número entero de las semanas transcurridas desde el inicio de la época y multiplicarlas por la duración de la semana.

Es que, en la mayoría de los países la semana empieza el lunes, pero en algunos otros (como EE.UU., Canadá, Israel, etc.) la semana comienza por el domingo. Pero, como recordamos, la época de medición de tiempo empieza el jueves. Si el jueves fuera el primer día de la semana, sería suficiente hacer estos simples cálculos.

Por razones de conveniencia, vamos a considerar las particularidades del cálculo del inicio de la semana en el ejemplo del primer día de la época que corresponde al valor 0. Necesitamos añadir al tiempo un valor que haga cambiar el primer día de la época (1970.01.01 00:00), que se cuenta desde cero, por el cuarto día, es decir tenemos que sumar la duración de cuatro días. Si la semana empieza el lunes, entonces el jueves tiene que ser el cuarto día. O sea, hay que añadir la duración de tres días. Si la semana empieza el domingo, entonces el jueves tiene que ser el quinto día. O sea, hay que añadir la duración de cuatro días.

Vamos a escribir la función para calcular el número de orden de la semana:

long WeekNum(datetime aTime,bool aStartsOnMonday=false)
  {
//--- si la semana empieza el domingo, añadimos la duración de 4 días (miércoles+martes+lunes+domingo),
//    si empieza el lunes, añadimos 3 días (miércoles+martes+lunes
   if(aStartsOnMonday)
     {
      aTime+=259200; // duración de tres días (86400*3)
     }
   else
     {
      aTime+=345600; // duración de cuatro días (86400*4)  
     }
   return(aTime/604800);
  }

Esta función puede ser útil en los indicadores para determinar la primera barra de la nueva semana:

bool NewWeek=WeekNum(time[i])!=WeekNum(time[i-1]);

Se supone que se utiliza la numeración de izquierda a derecha, donde time[i] es la hora de la barra actual, time[i-1] es la hora de la barra anterior.

Ahora podemos calcular la hora del inicio de la semana. Puesto que para el cálculo del número de la semana hemos asumido que la época empieza tres (o cuatro) días antes, entonces tenemos que hacer la corrección inversa:

long WeekStartTime(datetime aTime,bool aStartsOnMonday=false)
  {
   long tmp=aTime;
   long Corrector;
   if(aStartsOnMonday)
     {
      Corrector=259200; // duración de tres días (86400*3)
     }
   else
     {
      Corrector=345600; // duración de cuatro días (86400*4)
     }
   tmp+=Corrector;
   tmp=(tmp/604800)*604800;
   tmp-=Corrector;
   return(tmp);
  }  

La función devuelve el valor del tipo long porque la primera semana puede tener el valor de tiempo negativo (por tres o cuatro semanas antes del inicio de la época). El segundo parámetro de la función puede determinar si la semana empieza el domingo o el lunes.

Teniendo la hora del inicio de la semana, podemos calcular la cantidad de segundos transcurridos desde el inicio de la semana:

long SecondsFromWeekStart(datetime aTime,bool aStartsOnMonday=false)
  {
   return(aTime-WeekStartTime(aTime,aStartsOnMonday));
  }

Podemos convertir los segundos en días, horas, minutos y segundos. Si a la hora de calcular las horas, minutos y segundos desde el inicio del día no era difícil realizar los cálculos, pues en este caso será más fácil utilizar la función TimeToStruct():

long sfws=SecondsFromWeekStart(TimeCurrent());
MqlDateTime stm;
TimeToStruct(sfws,stm);
stm.day--;
Alert("Desde el inicio de la semana han pasado "+(string)stm.day+" d, "+(string)stm.hour+" h, "+(string)stm.min+" m, "+(string)stm.sec+" s");

Fíjense en que el valor stm.day se reduce en 1. Los días del mes se cuentan a partir del primero, y nosotros tenemos que determinar el número de días enteros. Alguien puede considerar las funciones de esta sección inútiles desde el punto de vista práctico, pero sea como sea su comprensión supone una buena experiencia durante el trabajo con el tiempo.


Determinar el número de la semana desde una fecha especificada, inicio del año, inicio del mes

Observando los campos de la estructura MqlDateTime (en particular el campo day_of_year) entran ganas de crear las funciones para determinar el número de la semana desde el inicio del año, número de la semana desde el inicio del mes. Es mejor crear una función universal para determinar el número de la semana desde la fecha especificada. El principio de trabajo de la función es similar al que se utilizaba para determinar el número de la semana desde el inicio de la época:

long WeekNumFromDate(datetime aTime,datetime aStartTime,bool aStartsOnMonday=false)
  {
   long Time,StartTime,Corrector;
   MqlDateTime stm;
   Time=aTime;
   StartTime=aStartTime;
//--- determinar el inicio de la época de referencia
   StartTime=(StartTime/86400)*86400;
//--- determinar el tiempo transcurrido  desde el inicio de la época de referencia
   Time-=StartTime;
//--- determinar el día de la semana del inicio de la época de referencia
   TimeToStruct(StartTime,stm);
//--- si la semana empieza el lunes, los números de los días de la semana se reducen en 1,
//    y el día con el número 0 se convierte en el día con el número 6
   if(aStartsOnMonday)
     {
      if(stm.day_of_week==0)
        {
         stm.day_of_week=6;
        }
      else
        {
         stm.day_of_week--;
        }
     }
//--- calcular el valor del corrector de tiempo 
   Corrector=86400*stm.day_of_week;
//--- corrección del tiempo
   Time+=Corrector;
//--- cálculo y devolución del número de la semana
   return(Time/604800);
  }

Basándonos en esta función, vamos a escribir dos funciones para determinar el número de la semana desde el inicio del año y el número de la semana desde el inicio del mes. Para eso primero necesitamos determinar el tiempo del inicio del año y el tiempo del inicio del mes. Descomponemos el tiempo en sus componentes, corregimos los valores de algunos campos y volvemos a convertir los componentes en el tiempo.

  • La función para determinar la hora del inicio del año:

    datetime YearStartTime(datetime aTime)
          {
           MqlDateTime stm;
           TimeToStruct(aTime,stm);
           stm.day=1;
           stm.mon=1;
           stm.hour=0;
           stm.min=0;
           stm.sec=0;
           return(StructToTime(stm));
          }
  • La función para determinar la hora del inicio del mes:

    datetime MonthStartTime(datetime aTime)
          {
           MqlDateTime stm;
           TimeToStruct(aTime,stm);
           stm.day=1;
           stm.hour=0;
           stm.min=0;
           stm.sec=0;
           return(StructToTime(stm));
          }

Ahora seguimos con la función que se utiliza para determinar el número de la semana desde el inicio del año e inicio del mes.

  • Desde el inicio del año:

    long WeekNumYear(datetime aTime,bool aStartsOnMonday=false)
          {
           return(WeekNumFromDate(aTime,YearStartTime(aTime),aStartsOnMonday));
          }
  • Desde el inicio del mes:

    long WeekNumMonth(datetime aTime,bool aStartsOnMonday=false)
          {
           return(WeekNumFromDate(aTime,MonthStartTime(aTime),aStartsOnMonday));
          }

Pues bien, por fin pasamos a las tareas puramente prácticas.


Crear un conjunto de herramientas experimental

Como ya hemos mencionado antes, hay Centros de Operaciones (DC por sus siglas en inglés, Dealing Center) con las barras de domingo, es decir cotizando durante el fin de semana entero. Nos gustaría comprobar el correcto funcionamiento de algunas funciones en todas las ocasiones. Claro que podemos encontrar en Internet los DC adecuados e ir comprobando el trabajo de las funciones basándonos en las cotizaciones de sus cuentas demos. Pero además de buscar los DC, tendremos que localizar también en el gráfico las zonas apropiadas para realizar las pruebas necesarias.

Vamos a crear nuestro propio "polígono de pruebas" para probar las funciones. El viernes, el fin de semana y el lunes son los días que más nos interesan. Vamos a crear un array con la hora de las barras del viernes, el lunes, y con las barras durante el fin de semana cuando es necesario. En total tenemos cuatro opciones:

  1. Sin barras del fin de semana.
  2. Algunas barras al final del domingo. Que sean, por ejemplo, 4 barras.
  3. Cotizaciones continuas durante el fin de semana.
  4. El sábado hay barras y el domingo, no.

Para que el array no nos salga muy grande, vamos a utilizar el período de tiempo H1. En este caso el tamaño máximo del array va a ser de 96 elementos (24 barras por día y 4 días), y el propio array va a caber en el gráfico cuando lo vamos a representar a través de los objetos gráficos. Entonces, obtendremos algo como un búfer de indicadores con la hora, y el repaso del array en el ciclo va a ser similar a la primera ejecución de la función OnCalculate() al iniciarse el indicador. Así podremos visualizar el trabajo de la función.

Esta herramienta está implementada en forma del script que se adjunta al presente artículo (el archivo "sTestArea.mq5"). En la función OnStart() del script se realiza la preparación. En la parte de arriba del código de la función podemos encontrar la variable Variant que nos permite seleccionar una de las cuatro opciones arriba mencionadas. Abajo de la función OnStart() se encuentra la función LikeOnCalculate() que parece a la función OnCalculate() del indicador. Esta función tiene dos parámetros: rates_total - número de barras y time[] - array con la hora de las barras.

Luego continuamos trabajando en esta función como si estuviéramos escribiendo un indicador. Podemos llamar de ella a la función SetMarker() para colocar el marcador. A la función SetMarker() se le pasan tres parámetros: índice de la barra, índice del búfer (línea en la que se visualiza el marcador) y el color del marcador.

En la figura 4 se muestra el resultado del trabajo del script, con la variable Variant = 2 en dos líneas de marcadores debajo de cada barra (las barras se marcan con las inscripciones que contienen las horas de las barras). Todos los elementos del gráfico tienen configurado el color invisible.

Fig. 4. Trabajo del script sTestArea.mq5.
Fig. 4. Trabajo del script sTestArea.mq5.

Las inscripciones con la hora de las barras se colorean en función del día de la semana: el viernes es rojo, el sábado es violeta, el domingo es verde y el lunes es azul. Ahora podemos escribir diferentes funciones que requieren un enfoque especial para las barras del fin de semana, y mantener la observación visual de su trabajo.


Indicador Pivot - Opción 1

Primero vamos a intentar crear un indicador Pivot simple. Para calcular la línea Pivot, necesitamos saber el precio de cierre de la sesión de ayer, así como el precio máximo y mínimo de la sesión de ayer. El valor del indicador se calcula como la media de estos tres valores. En el transcurso del día vamos a registrar la aparición del nuevo mínimo y el máximo del precio, calcular el valor Pivot al inicio del nuevo día, mostrar y seguir trazando el nivel durante todo el día.

Vamos a prever dos versiones de trabajo del indicador:

  1. El Pivot se calcula cada nuevo día (se supone que el fin de semana no hay barras). Si el fin de semana hay barras, entonces estos días con barras van a considerarse como días separados.
  2. Las barras del sábado van a pertenecer al viernes, y las del domingo pertenecerán al lunes (eso se aplica al caso de cotización durante el fin de semana entero y al caso sólo de las barras del domingo). En este caso hay que tener en cuenta que puede no haber barras el fin de semana.

En la primera versión será suficiente determinar sólo el inicio del nuevo día. Pasamos a la función la hora actual (aTimeCur) y la hora anterior (aTimePre), calculamos los números de días desde el inicio de la época. Si no coinciden, entonces ha empezado nuevo día:

bool NewDay1(datetime aTimeCur,datetime aTimePre)
  {
   return((aTimeCur/86400)!=(aTimePre/86400));
  }

La segunda versión. Si ha empezado el sábado, el comienzo del día tiene que ser ignorado. Si ha empezado el domingo, definimos el comienzo del día (es obvio, simplemente ha empezado un nuevo día). Si ha empezado el lunes después del domingo, saltamos el comienzo del día. Si el lunes ha sido precedido por cualquier otro día de la semana, pudiendo ser sábado o viernes, definimos el comienzo del día. Obtenemos la siguiente función:

bool NewDay2(datetime aTimeCur,datetime aTimePre)
  {
   MqlDateTime stm;
//--- nuevo día
   if(NewDay1(aTimeCur,aTimePre))
     {
      TimeToStruct(aTimeCur,stm);
      switch(stm.day_of_week)
        {
         case 6: // sábado
            return(false);
            break;
         case 0: // domingo
            return(true);
            break;
         case 1: // lunes
            TimeToStruct(aTimePre,stm);
            if(stm.day_of_week!=0)
              { // día anterior que no sea el domingo
               return(true);
              }
            else
              {
               return(false);
              }
            break;
         default: // cualquier otro día de la semana
            return(true);
        }
     }
   return(false);
  }

Ahora mostramos la función general dependiendo de la versión:

bool NewDay(datetime aTimeCur,datetime aTimePre,int aVariant=1)
  {
   switch(aVariant)
     {
      case 1:
         return(NewDay1(aTimeCur,aTimePre));
         break;
      case 2:
         return(NewDay2(aTimeCur,aTimePre));
         break;
     }
   return(false);
  }

Vamos a probar el funcionamiento de la función utilizando la herramienta "sTestArea" (el archivo sTestArea_Pivot1.mq5 del anexo; el inicio del día está marcado con marrón). En total hay que hacer ocho pruebas: dos versiones de la función para cuatro opciones de formación de las barras. Una vez comprobado el correcto funcionamiento de la función, podemos empezar a escribir el indicador. La programación de los indicadores no es el tema del presente artículo por eso se adjunta un indicador ya hecho (el archivo Pivot1.mq5), con la parte más difícil de su creación descrita al detalle.


Determinar la sesión de tiempo

Necesitamos permitir al Asesor Experto tradear en determinados períodos de tiempo durante el día, todos los días en el mismo período de tiempo. Especificamos la hora y el minuto del inicio de la sesión comercial, la hora y el minuto del fin de la sesión comercial. La especificación separada de las horas y minutos (en vez de las variables de cadenas especificando la hora del tipo "14:00") permitirá realizar la optimización en el Probador de Estrategias si la función se utiliza en el Asesor Experto.

Para determinar la sesión de tiempo, vamos a hacer los siguientes pasos:

  1. Calculamos el tiempo en segundos desde el inicio del día para el momento inicial del tiempo, y hacemos lo mismo para el momento final del tiempo.
  2. Calculamos la hora actual en segundos desde el inicio del día.
  3. Comparamos la hora actual con la hora inicial y final.

Es posible que una sesión comercial empiece un día y finalice el otro. Es decir, cuando la sesión comercial sobrepasa la medianoche, la hora final calculada desde el inicio del día acaba siendo menor que la hora del inicio. Por eso hay que realizar dos comprobaciones. Tenemos la siguiente función:

bool TimeSession(int aStartHour,int aStartMinute,int aStopHour,int aStopMinute,datetime aTimeCur)
  {
//--- hora del inicio de sesión
   int StartTime=3600*aStartHour+60*aStartMinute;
//--- hora del fin de sesión
   int StopTime=3600*aStopHour+60*aStopMinute;
//--- hora actual en segundos desde el inicio del día
   aTimeCur=aTimeCur%86400;
   if(StopTime<StartTime)
     {
      //--- paso de la medianoche
      if(aTimeCur>=StartTime || aTimeCur<StopTime)
        {
         return(true);
        }
     }
   else
     {
      //--- en los límites de un día
      if(aTimeCur>=StartTime && aTimeCur<StopTime)
        {
         return(true);
        }
     }
   return(false);
  }

En caso de que la sesión pase la medianoche, la hora actual tiene que ser mayor o igual a la hora del inicio de la sesión O menor que la hora del fin de la sesión. Si la sesión se encuentra dentro del día, la hora actual tiene que ser mayor o igual a la hora inicial O menor que la hora final.

Para probar el funcionamiento de la función, ha sido creado el indicado (archivo 'Session.mq5' del anexo). Podemos utilizarlo no sólo para la prueba, sino también para otras finalidades prácticas como cualquier otro indicador de la aplicación.


Determinar el momento de tiempo durante el día

Una simple verificación de la igualdad del tiempo especificado no va a funcionar correctamente porque los ticks no llegan en intervalos regulares, pueden haber retrasos de varios segundos a varios minutos. Es muy probable que en el mercado simplemente no haya tick con la hora especificada. Es necesario realizar la verificación de la intersección de la marca de tiempo especificada.

La hora actual tiene que ser igual a o mayor que la hora especificada, y la hora anterior tiene que ser menor. Puesto que necesitamos determinar el momento de tiempo dentro del día, hace falta convertir la hora actual (y la anterior) en segundos desde el inicio del día. De la misma manera, hay que convertir los parámetros establecidos de la hora (hora y minutos) en segundos. Puede pasar que la hora anterior caiga al día anterior, es decir, en segundos antes del inicio del día ella será mayor que la hora actual. En este caso procedemos del mismo modo que a la hora de determinar la sesión de tiempo: hacemos dos verificaciones.

Tenemos la siguiente función:

bool TimeCross(int aHour,int aMinute,datetime aTimeCur,datetime aTimePre)
  {
//--- hora especificada desde el inicio del día
   datetime PointTime=aHour*3600+aMinute*60;
//--- hora actual desde el inicio del día
   aTimeCur=aTimeCur%86400;
//--- hora anterior desde el inicio del día
   aTimePre=aTimePre%86400;
   if(aTimeCur<aTimePre)
     {
      //--- paso de la medianoche
      if(aTimeCur>=PointTime || aTimePre<PointTime)
        {
         return(true);
        }
     }
   else
     {
      if(aTimeCur>=PointTime && aTimePre<PointTime)
        {
         return(true);
        }
     }
   return(false);
  }

A base de esta función ha sido hecho el indicador (archivo 'TimePoint.mq5' en los adjuntos al artículo).


Indicador Pivot - Opción 2

Ahora que ya hemos aprendido a determinar el momento de tiempo, vamos a hacer el indicar Pivot más sofisticado. Ahora el día va a empezar no como habitualmente a las 00:00, sino a cualquier hora especificada. Vamos a llamarlo el día personalizado (por el usuario). Para determinar el inicio del día personalizado, utilizaremos la función TimeCross() descrita anteriormente. Debido a diferentes versiones de formación de las barras durante el fin de semana, algunos días habrá que omitir. Es difícil plantear todas las reglas de verificación a la primera, por eso vamos a hacerlo todo paso a paso. Lo importante es tener algo con que empezar y tener opciones para continuar. Tenemos el script de verificación 'sTestArea.mq5', por eso la solución correcta puede ser encontrada de forma experimental.

El caso "Sin barras del fin de semana" es el más sencillo: el nuevo día empieza cuando la hora cruza la marca de tiempo establecida.

En el caso de sólo algunas barras al final del domingo, la función TimeCross() define la primera barra del domingo como el inicio del día considerando cualquier parámetro de la función. Se considera que los fines de semana no hay cotizaciones (las barras del domingo pertenecen al lunes), entonces el domingo tiene que ignorarse. Si la hora establecida cae aproximadamente en el medio de la serie de las barras del domingo, también ignoramos porque el inicio del día ya ha sido registrado el viernes.

El caso de cotizaciones continuas durante el fin de semana: Si el inicio del día personalizado cae en la mitad del día natural (Fig. 5),

Fig. 5. El inicio del día personalizado cae en la mitad del día natural. El viernes - rojo, el sábado - violeta, el domingo - verde, el lunes - azul.
Fig. 5. El inicio del día personalizado cae en la mitad del día natural.
El viernes - rojo, el sábado - violeta, el domingo - verde, el lunes - azul.

entonces una parte del sábado se puede atribuir al viernes, una parte del domingo se puede atribuir al lunes. Pero nos queda una serie de barras desde la mitad del sábado hasta la mitad del domingo que no puede ser atribuida a nada. Claro que podemos dividir el intervalo desde el sábado hasta el domingo en partes iguales asignando una mitad al viernes y la otra al lunes. Eso complicará mucho un indicador muy simple y eso que las cotizaciones el fin de semana no son importantes.

La más solución razonable sería atribuir todas las barras del sábado y del domingo al día personalizado que dura desde el viernes hasta el lunes. Entonces los inicios de días que caen en el sábado y el domingo se ignoran.

Tenemos la siguiente función:

bool NewCustomDay(int aHour,int aMinute,datetime aTimeCur,datetime aTimePre)
  {
   MqlDateTime stm;
   if(TimeCross(aHour,aMinute,aTimeCur,aTimePre))
     {
      TimeToStruct(aTimeCur,stm);
      if(stm.day_of_week==0 || stm.day_of_week==6)
        {
         return(false);
        }
      else
        {
         return(true);
        }
     }
   return(false);
  }

A base de esta función ha sido hecho el indicador (el archivo adjunto 'Pivot2.mq5').


Determinar los días de trading durante la semana

Es bastante fácil permitir al Asesor Experto tradear sólo durante determinados días de la semana. Dividimos la hora en sus componentes usando la función TimeToStruct() y declaramos las variables del tipo bool para cada día de la semana en los parámetros del Asesor Experto. Dependiendo del día de la semana, la función devuelve el valor de la variable correspondiente.

Esta verificación puede ser realizada de manera más óptima. Al inicializar un Asesor Experto o indicador, llenamos el array con los valores de las variables que permiten o prohíben tradear durante determinados días de la semana. Luego comprobamos el valor del elemento del array correspondiente al día de la semana. Obtenemos dos funciones: una se invoca durante la inicialización, la otra, en caso de la necesidad.

Variables:

input bool Sunday   =true; // Domingo
input bool Monday   =true; // Lunes
input bool Tuesday  =true; // Martes 
input bool Wednesday=true; // Miércoles
input bool Thursday =true; // Jueves
input bool Friday   =true; // Viernes
input bool Saturday =true; // Sábado

bool WeekDays[7];

La función de inicialización:

void WeekDays_Init()
  {
   WeekDays[0]=Sunday;
   WeekDays[1]=Monday;
   WeekDays[2]=Tuesday;
   WeekDays[3]=Wednesday;
   WeekDays[4]=Thursday;
   WeekDays[5]=Friday;
   WeekDays[6]=Saturday;
  }

La función principal:

bool WeekDays_Check(datetime aTime)
  {
   MqlDateTime stm;
   TimeToStruct(aTime,stm);
   return(WeekDays[stm.day_of_week]);
  }

A base de esta función ha sido hecho el indicador 'TradeWeekDays.mq5” que se adjunta al artículo.


Determinar el tiempo de trading durante la semana

Hay que determinar las sesiones comerciales desde una hora especificada de un día de la semana hasta una hora de otro día de la semana. Esta función es parecida a la función TimeSession(), sólo que los cálculos se realizan a base del tiempo transcurrido desde el principio de la semana. Tenemos la siguiente función:

bool WeekSession(int aStartDay,int aStartHour,int aStartMinute,int aStopDay,int aStopHour,int aStopMinute,datetime aTimeCur)
  {
//--- hora del inicio de la sesión desde el inicio de la semana
   int StartTime=aStartDay*86400+3600*aStartHour+60*aStartMinute;
//--- hora del fin de la sesión desde el inicio de la semana
   int StopTime=aStopDay*86400+3600*aStopHour+60*aStopMinute;
//--- hora actual en segundos desde el inicio de la semana
   long TimeCur=SecondsFromWeekStart(aTimeCur,false);
   if(StopTime<StartTime)
     {
      //--- paso del cambio de la semana
      if(TimeCur>=StartTime || TimeCur<StopTime)
        {
         return(true);
        }
     }
   else
     {
      //--- en los límites de una semana
      if(TimeCur>=StartTime && TimeCur<StopTime)
        {
         return(true);
        }
     }
   return(false);
  }

A base de esta función ha sido hecho el indicador 'SessionWeek.mq5' que se adjunta al artículo.

Bueno, hemos considerado prácticamente todas las tareas más comunes relacionadas con el tiempo, hemos analizado las técnicas de programación y las funciones estándar MQL5 necesarias para eso.


Funciones adicionales de MQL5

Nos quedan por ver algunas funciones más que se usan para trabajar con el tiempo: TimeTradeServer(), TimeGMT(), TimeDaylightSavings(), TimeGMTOffset(). La principal particularidad de estas funciones consiste en que ellas utilizan el reloj y configuraciones de tiempo del ordenador del usuario.

Función TimeTradeServer(). Hemos mencionado antes que la función TimeCurrent() va a mostrar la hora incorrecta los fines de semana (la hora del último cambio del precio el viernes). La función TimeTradeServer) calculará la hora correcta del servidor:

datetime tm=TimeTradeServer();
//--- mostrar resultado
Alert(tm);

Función TimeGMT(). Esta función calcula la hora GTM basándose en los valores del reloj del ordenador del usuario y siguientes configuraciones: huso horario y el horario de verano/invierno:

datetime tm=TimeGMT();
//--- mostrar resultado
Alert(tm);

Para ser más exacto, la función devuelve la hora UTC.

Función TimeDaylightSavings(). Esta función devuelve el valor de corrección de la hora por el horario de verano a partir de las configuraciones del ordenador.

int val=TimeDaylightSavings();
//--- mostrar resultado
Alert(val);

Para obtener la hora sin tomar en consideración la corrección por el horario de verano, hay que sumar el valor de la corrección a la hora local.

Función TimeGMTOffset(). Esta función permite obtener el huso horario del ordenador del usuario. El valor se devuelve en segundos que hay que sumar a la hora local para obtener la hora GTM.

int val=TimeGMTOffset();
//--- mostrar resultado
Alert(val);

La hora en el ordenador del usuario será igual a TimeGMT()-TimeGMTOffset()-TimeDaylightSavings():

datetime tm1=TimeLocal();
datetime tm2=TimeGMT()-TimeGMTOffset()-TimeDaylightSavings();
//--- mostrar resultado
Alert(tm1==tm2);


Otras funciones útiles para trabajar con el tiempo

Función para determinar el año bisiesto

bool LeapYear(datetime aTime)
  {
   MqlDateTime stm;
   TimeToStruct(aTime,stm);
//--- múltiple de 4 
   if(stm.year%4==0)
     {
      //--- múltiple de 100
      if(stm.year%100==0)
        {
         //--- múltiple de 400
         if(stm.year%400==0)
           {
            return(true);
           }
        }
      //--- no múltiple de 100 
      else
        {
         return(true);
        }
     }
   return(false);
  }

El principio para determinar el año bisiesto está descrito en la sección "Peculiaridades de medición del tiempo según el calendario".

Función para determinar el número de días en el mes

int DaysInMonth(datetime aTime)
  {
   MqlDateTime stm;
   TimeToStruct(aTime,stm);
   if(stm.mon==2)
     {
      //--- febrero
      if(LeapYear(aTime))
        {
         //--- febrero del año bisiesto 
         return(29);
        }
      else
        {
         //--- febrero del año normal 
         return(28);
        }
     }
   else
     {
      //--- el resto de meses
      return(31-((stm.mon-1)%7)%2);
     }
  }

La función comprueba si el año es bisiesto y devuelve el valor 28 o 29, para los demás meses se hace el cálculo. El número de días de los primeros 7 meses van alternando de la siguiente manera: 31, 30 31, 30, etc., también se alternan 5 meses restantes, por eso la función calcula la resta de la división por 7. Después de eso, se realiza la verificación par-impar, la corrección obtenida se resta de 31.


Peculiaridades de trabajo de las funciones de tiempo en el Probador de Estrategias

El Probador genera su flujo de cotizaciones, los valores de la función TimeCurrent() corresponde al flujo de cotizaciones en el Prbador. Los valores de la función TimeTradeServer() corresponden a los valores de TimeCurrent(). De forma similar, los valores de la función TimeLocal() corresponden al valor de TimeCurrent(). La función TimeCurrent() en el Probador no toma en cuenta el huso horario y la corrección del horario de verano. El trabajo de los Asesores Expertos se basa en el cambio del precio. Por eso si el Asesor necesita trabajar con el tiempo, hay que utilizar la función TimeCurrent(). Eso permite probar el Asesor en el Probador de Estrategias con total seguridad.

El trabajo de las funciones TimeGMT(), TimeDaylightSavings(), TimeGMTOffset() se basa exclusivamente en las configuraciones actuales del ordenador del usuario (los cambios del horario de verano/invierno no son simulados en el Probador). Si durante la prueba del Asesor Experto alguien necesita simular los cambios entre el horario de verano e invierno (si alguien realmente lo necesita), tiene que encargarse de ello por sí mismo. Eso requerirá la información sobre las fechas y horas exactas para cambiar el reloj, además de un detallado análisis.

La solución de este problema sale de los límites de un solo artículo y no se considera aquí. Si el Asesor trabaja dentro de las horas de la sesión de Europa o los EE.UU. y el Centro de operaciones hace el cambio del horario de verano/invierno, entonces no habrá discrepancia entre la hora del servidor y la hora del evento, a diferencia de la sesión asiática (Japón no cambia el horario de verano, Australia cambia el horario en noviembre).


Conclusión

Pues, hemos visto todas las funciones estándar del lenguaje MQL5 que sirven para trabajar con el tiempo. Hemos visto las técnicas de programación utilizadas a la hora de trabajar con el tiempo. Han sido creadas varias funciones útiles para la aplicación práctica con la descripción detallada de su funcionamiento, también han sido creados unos indicadores.

Todas las funciones estándar para trabajar con el tiempo pueden ser divididas en las siguientes categorías:

  1. TimeCurrent(), TimeLocal() son funciones básicas que se utilizan para determinar la hora actual.
  2. TimeToString(), StringToTime(), TimeToStruct(), StructToTime() son funciones para procesar la hora.
  3. CopyTime() es la función para trabajar con las horas de las barras.
  4. TimeTradeServer(), TimeGMT(), TimeDaylightSavings(), TimeGMTOffset() son funciones que dependen de las configuraciones del ordenador del usuario.


Lista de archivos adjuntos

  • sTestArea.mq5 - script para probar las funciones complejas.
  • sTestArea_Pivot1.mq5 - uso del script sTestArea.mq5 para probar las funciones de hora del indicador Pivot1.mq5.
  • Pivot1.mq5 - indicador Pivot que utiliza los días estándar (la función NewDay).
  • Session.mql5 - indicador de la sesión comercial del día (la función TimeSession).
  • TimePoint.mq5 - indicador de un determinado momento de tiempo (la función TimeCross).
  • Pivot2.mq5 - indicador Pivot que utiliza los días personalizados por el usuario (la función NewCustomDay).
  • TradeWeekDays.mq5 - indicador de días comerciales de la semana (la función WeekDays_Check).
  • SessionWeek.mq5 - indicador de la sesión comercial de la semana (la función WeekSession).
  • TimeFunctions.mqh - todas las funciones mencionadas en este artículo recogidas en un archivo único.

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

Archivos adjuntos |
stestarea.mq5 (4.96 KB)
pivot1.mq5 (4.5 KB)
session.mq5 (3.51 KB)
timepoint.mq5 (3.32 KB)
pivot2.mq5 (4.14 KB)
tradeweekdays.mq5 (3.59 KB)
sessionweek.mq5 (5.08 KB)
timefuncions.mqh (20.52 KB)
Vídeo tutorial: Servicio de señales de MetaTrader Vídeo tutorial: Servicio de señales de MetaTrader

Este vídeo tutorial explica en solo 15 minutos lo que es el servicio de señales de MetaTrader (MetaTrader Signals Service) y demuestra con detalle cómo suscribirse a señales de trading y cómo ser un proveedor de señales en nuestro servicio. Gracias a este tutorial, será capaz de suscribirse a cualquier señal de trading, o publicar y promocionar sus propias señales en nuestro servicio.

Indicador para la representación del gráfico Kagi Indicador para la representación del gráfico Kagi

En este artículo se propone un indicador del gráfico Kagi con varias opciones de trazado y funciones adicionales. Además, se tiene en cuenta el principio de representación gráfica del indicador y las características de su implementación en MQL5. Los casos más conocidos de su implementación en el trading se muestran en la estrategia de intercambio Yin/Yang, alejándose de la línea de tendencia y aumentando los "hombros" o disminuyendo las "cinturas" de manera coherente.

Documentación generada automáticamente para el código de MQL5 Documentación generada automáticamente para el código de MQL5

La mayoría de desarrolladores de código Java estarán familiarizados con la documentación generada automáticamente que puede obtenerse con JavaDocs. La idea es añadir comentarios al código de forma semiestructurada para que pueda ser extraída en forma de una archivo de ayuda por el que sea fácil navegar. El mundo de C++ tiene también una serie de autogeneradores de documentación, siendo líderes SandCastle y Doxygen, ambos de Microsoft. El artículo describe el uso de Doxygen para crear archivos de ayuda de html a partir de comentarios estructurados en código MQL5. El experimento funcionó muy bien y creo que la documentación de ayuda que ofrece Doxygen a partir del código MQL5 añadirá una gran cantidad de valor a este.

Usar WinInet en MQL5. Parte 2: solicitudes y archivos POST Usar WinInet en MQL5. Parte 2: solicitudes y archivos POST

En este artículo seguimos estudiando los principios del trabajo con internet usando solicitudes HTTP e intercambiando información con el servidor. El artículo describe nuevas funciones de la clase CMqlNet, métodos para enviar información desde formularios y envío de archivos usando solicitudes POST así como autorización en sitios web bajo un registro de usuario usando cookies.