English Русский 中文 Deutsch 日本語 Português
preview
Gestionando el Horario (Parte 2): Funciones

Gestionando el Horario (Parte 2): Funciones

MetaTrader 5Ejemplos |
1 294 37
Carl Schreiber
Carl Schreiber

Las variables globales

En lugar de pedir ayuda a su bróker, de quien probablemente recibirá una respuesta insuficiente (quién estaría dispuesto a explicar dónde se ha metido la hora faltante), simplemente nos fijaremos en cómo estos calculan sus precios en las semanas de cambio horario, pero evitando engorrosos cálculos manuales: un programa se encargará de ello, después de todo, ¿para qué tenemos un PC?

Antes de las funciones en el archivo de inclusión DealingWithTime.mqh (y después de la sustitución de la macros), declararemos las variables necesarias como variables globales:

//--- global variables for time switches
int      DST_USD=0,                             // act time shift USD
         DST_EUR=0,                             // act time shift EU
         DST_AUD=0,                             // act time shift Australia
         DST_RUS=0;                             // D'2014.10.26 02:00', -10800,

Estas variables DST_USD, DST_EUR... contendrán el cambio horario real de los EE.UU., la UE, etcétera, y serán actualizados y configurados por nuestras funciones. En el horario de invierno, que es el horario normal, serán iguales a cero: la hora no se cambia en ese periodo.

Después de ello, tendremos las variables para la próxima vez que se produzca el cambio de hora. Se necesitarán principalmente para saber cuándo requerimos un nuevo cálculo para ahorrar los recursos de la CPU:

datetime nxtSwitch_USD,                         // date of next switch
         nxtSwitch_EUR,                         // date of next switch
         nxtSwitch_AUD,                         // date of next switch
         nxtSwitch_RUB = D'2014.10.26 02:00';   // Russia s different :(

Analizaremos la situación rusa más adelante en este mismo artículo.

Esta estructura y su variable global son el corazón de todo. :)

struct _OffsetBroker
  {
   int   USwinEUwin,                            // US=Winter & EU=Winter
         USsumEUsum,                            // US=Summer & EU=Summer
         USsumEUwin,                            // US=Summer & EU=Winter
         actOffset,                             // actual time offset of the broker
         secFxWiWi,                             // duration of FX in sec
         secFxSuSu,                             // duration of FX in sec
         secFxSuWi,                             // duration of FX in sec
         actSecFX;                              // actual duration of FX in sec
   bool  set;                                   // are all set?
  };
_OffsetBroker OffsetBroker;

Asignaremos las compensaciones del bróker para los tres periodos relevantes y la duración del mercado fórex estará abierta en estos periodos, tanto para el valor real como para un conjunto de sencilla verificación, si los valores han sido asignados. La variable global se llama OffsetBroker, y la encontraremos varias veces.


Función central para determinar los cambios horarios del bróker

Llamando esta función:

setBokerOffset();

un asesor, indicador o script podrá determinar por sí mismo cuándo y cómo maneja el bróker los cambios horarios. Se coloca al comienzo del código en el script adjunto después de Start(). La función determinará los valores relevantes del bróker para los periodos relevantes (horario de verano, horario de invierno y horario intermedio), que luego se podrán utilizar para determinar los demás horarios necesarios a través de GMT. Como todo lo demás, se encuentra en el archivo de inclusión adjunto DealingWithTime.mqh, que también contiene las partes necesarias del primer artículo. Después, se declara la variable, se realizan las inicializaciones y se ponen a cero las variables globales relevantes:

   datetime dateEUR,dateUSD,dateAUD,dateNxt,                   // switch date for EU, AU, & US
            arrTme[];                                          // array to copy time
   int b;
   OffsetBroker.USsumEUwin =
      OffsetBroker.USsumEUsum =
         OffsetBroker.USwinEUwin = INT_MIN;
   nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0;          // reset variables

encontramos los fines de semana de cambio:

//--- AU, EU & US switches to winter time in 2020
   if(IS_DEBUG_MODE)
      Print("\n2nd half-year 2020 for ",AccountInfoString(ACCOUNT_COMPANY), "DebugMode: ",IS_DEBUG_MODE);

   nextDST("EUR", D'2020.06.21 14:00');                        // EUR: get DST and set next change
   b = CopyTime("EURUSD",PERIOD_H1,nxtSwitch_EUR,1,arrTme);    // get time last 1h bar before switch in EU
   dateEUR = arrTme[0];                                        // last hour on Friday before the weekend

   nextDST("USD", D'2020.06.21 14:00');                        // USD: get DST and set next change
   b = CopyTime("EURUSD",PERIOD_H1,nxtSwitch_USD,1,arrTme);    // get time last 1h bar before switch in USA
   dateUSD = arrTme[0];                                        // last hour on Friday before the weekend

   nextDST("AUD", D'2020.06.21 14:00');                        // AUD: get DST and set next change
   b = CopyTime("EURUSD",PERIOD_H1,nxtSwitch_AUD,1,arrTme);    // get time last 1h bar before switch in AU
   dateAUD = arrTme[0];                                        // last hour on Friday before the weekend

   dateNxt = fmax(nxtSwitch_EUR,nxtSwitch_USD)+WeekInSec;      // get the next weekend
   b = CopyTime("EURUSD",PERIOD_H1,dateNxt,1,arrTme);          // get time last 1h bar before the weekend
   dateNxt = arrTme[0];                                        // last hour on Friday before the weekend

Para mayor comodidad, la mayoría de las impresiones se realizan automáticamente en el modo de depuración: if(IS_DEBUG_MODE). Entonces, al iniciar, por ejemplo, el script adjunto en el depurador (F5), podremos ver todos los detalles; pero si iniciamos el mismo script directamente en un gráfico, obtendremos solo las cosas importantes.

Para las tres zonas horarias, la llamada de la función (por ejemplo, nextDST ("EUR", D'2020.06.21 14:00 ') se usa en primer lugar para calcular las diferencias horarias aplicables para la UE y, en segundo lugar, para el siguiente cambio. En junio, se trata del horario de verano, y el próximo fin de semana de cambio será al horario de invierno. En la línea inmediatamente posterior, obtenemos la hora de apertura de la última barra h1 el viernes anterior a ese fin de semana, ya que será el punto de referencia para nuestro cálculo. Eche un vistazo al punto 4 de los supuestos del final del primer artículo:

          4. Si faltan horas entre el viernes a las 17:00 y el domingo a las 17:00, luego faltarán cotizaciones el domingo hasta la primera cotización, y no el viernes después de la última cotización recibida.

He decidido usar las horas de h1 y "EURUSD". Probablemente, este símbolo es el que tiene la historia más larga, no solo en MQ. Pero esto también significa que si el mercado de divisas cierra a las 17:00 hora de Nueva York, la última hora o la última barra de 1 hora comenzará a las 16:00, y esta será la hora en la que estaremos particularmente interesados más adelante. La primera hora después de un fin de semana es las 17:00 del domingo en Nueva York. En aras de una mayor exhaustividad, el cambio de Australia también se determina, pero no se usará en el futuro (ver más abajo). Después de eso, aún se determina el primer fin de semana posterior al cambio de ambas zonas horarias, para calcular así la diferencia horaria del bróker del próximo periodo de diferencia.

Luego, para las tres horas del viernes, se calculan las diferencias horarias respectivas relacionadas con el periodo del bróker con la función chckFriday(...), usando como base la hora 16:00 de Nueva York. Esta función forma parte del archivo de inclusión y la analizaremos a continuación.

   chckFriday(dateEUR,"EUR");                                  // function to determine broker offset for the Friday given
   chckFriday(dateUSD,"USD");                                  // function to determine broker offset for the Friday given
   chckFriday(dateNxt,"NXT");                                  // function to determine broker offset for the Friday given

Después de eso, se utiliza el mismo principio para calcular los demás cambios de hora en la otra mitad del año, que (mire arriba) realmente no necesitamos y que podrían comentarse así:

   if(IS_DEBUG_MODE)
      Print("\n1st half-year 2021 for ",AccountInfoString(ACCOUNT_COMPANY), "DebugMode: ",IS_DEBUG_MODE);
   nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0;

   nextDST("AUD", D'2021.01.21 14:00');                        // AUD: get DST and set next change
   b = CopyTime("EURUSD",PERIOD_H1,nxtSwitch_AUD,1,arrTme);    // get time last 1h bar before switch in EU
   dateAUD = arrTme[0];                                        // last hour on Friday before the weekend

...
   chckFriday(dateUSD,"USD");                                  // function to determin broker offset for the Friday given
   chckFriday(dateEUR,"EUR");                                  // function to determin broker offset for the Friday given
   chckFriday(dateNxt,"NXT");                                  // function to determin broker offset for the Friday given

Finalmente, una vez se han detectado las compensaciones del bróker y se han asignado estas a los campos correspondientes, las horas de cambio decisivas (nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0) se ponen a cero para su uso posterior. Como el recálculo debe realizarse solo si se "ha pasado" un fin de semana con cambio de hora durante el curso histórico, la hora posterior puede impedir que se efectúen los cálculos correctos, por consiguiente, será mejor reiniciar una vez de más, que una de menos. Luego se verifica si se han asignado todos los valores; el resultado se imprime en el diario del experto y se retorna la comprobación:

   nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0;          // reset variables for use by a user
   if(OffsetBroker.USsumEUwin != INT_MIN
      && OffsetBroker.USsumEUsum != INT_MIN
      && OffsetBroker.USwinEUwin != INT_MIN
     )
      OffsetBroker.set = true;
   else
      OffsetBroker.set = false;
   if(OffsetBroker.set)
      Print("\nTime Offset of ",AccountInfoString(ACCOUNT_COMPANY),": ",
            "\nUS=Winter & EU=Winter (USwinEUwin) = ",OffsetBroker.USwinEUwin,
            "\nUS=Summer & EU=Summer (USsumEUsum) = ",OffsetBroker.USsumEUsum,
            "\nUS=Summer & EU=Winter (USsumEUwin) = ",OffsetBroker.USsumEUwin,
            "\n");
   else
      Print(__FILE__,"[",__LINE__,"] Assigning the broker offset went wrong - somehow.");
   return(OffsetBroker.set);

Si todo está bien, veremos unas líneas, por ejemplo, así:

Time Offset of MetaQuotes Software Corp.:
US=Winter & EU=Winter (USwinEUwin)       =   -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin)    =   -7200

Esto permite que cualquier usuario use estos valores para las variables de entrada en lugar de la función que se ejecutará antes de que el asesor comience a funcionar, ya sea en el simulador de estrategias o en vivo en un gráfico. Le ofrecemos un ejemplo al final del artículo.


Determinando y estableciendo las diferencias horarias del bróker

Vamos a analizar ahora la función chckFriday(...). Determina la compensación horaria del bróker respectivo para los diferentes periodos y la asigna al campo correspondiente de la variable global OffsetBroker del Tpy de la estructura _OffsetBroker. La estructura conoce estos tres campos:

   int   USwinEUwin,                            // US=Winter & EU=Winter
         USsumEUsum,                            // US=Summer & EU=Summer
         USsumEUwin,                            // US=Summer & EU=Winter

Son asignados a la diferencia horaria correspondiente del bróker del periodo respectivo. Los periodos son:

  • el mismo en ambas regiones, ya sea en invierno (o en la hora estándar) o en verano, o
  • EE.UU. ya (todavía) en verano y la UE todavía (ya) en invierno.

La situación inversa, cuando los Estados Unidos ya (todavía) están en invierno, pero la UE todavía (ya) está en verano, no existe. Aquí nos surgen varias preguntas: ¿por qué no existe una cuarta categoría? ¿Y qué pasa con Australia o el AUD, es que faltan aquí?

Estas son las reglas para cambiar:

  • UE: último domingo de octubre y último domingo de marzo
  • EE.UU.: primer domingo de noviembre y segundo domingo de marzo y
  • AU: primer domingo de noviembre y último domingo de marzo


Respecto a la primera pregunta: la UE cambia al horario de invierno una o dos semanas antes que los EE.UU., pues allí todavía es verano, mientras que en la UE ya es invierno. Por ello, el valor se asigna al campo USsumEUwin de la variable OffsetBroker. Luego, en primavera, los EE.UU. cambian al horario de verano antes que la UE; después es nuevamente verano en los EE.UU. durante una semana o dos, mientras que aún es invierno en la UE. Nuevamente, el valor se asigna al campo USsumEUwin de la variable OffsetBroker. Esto deja claro que el caso inverso (en la UE ya (todavía) es verano, pero en los EE. UU. todavía (ya) es invierno) no ocurre en absoluto. En realidad, esto elimina la necesidad de calcular las compensaciones horarias del bróker para ambos periodos de cambio en otoño y primavera. Sin embargo, se ejecuta, simplemente para mayor integridad y control.

Sobre la segunda pregunta: Australia cambia en noviembre como los EE.UU. y en primavera como la UE. Por ello, no hay fines de semana diferentes adicionales para el cambio. No obstante, el reloj en Australia se adelanta 1 hora cuando es invierno en la UE y los EE. UU., porque allí la Navidad y el Año Nuevo caen en verano.

Ahora, si ya calculamos el cambio de tiempo para un periodo especial, también podremos calcular la duración actual de la apertura del mercado de divisas en estas semanas. Estos valores se guardan en los campos secFxWiWi, secFxSuSu, secFxSuWi y el valor válido actual en actSecFX. Al final del artículo, en el capítulo Aplicación, mostraremos cómo gestionar esto.

Pero antes de poder asignar los valores, deberemos determinarlos. Después de la declaración de la variable y el restablecimiento de las variables globales, las diferencias horarias para EU y USD se calcularán para la hora tB (time Broker) dada:

//+------------------------------------------------------------------+
//| auxiliary function to determine time offset of the broker        |
//+------------------------------------------------------------------+
int chckFriday(
   datetime tB,                                                // time Broker: the last hour on Friday
   string cmt=""                                               // text to start the line
)
  {

   int hNY, hGMT, hTC, hDiff;
   nxtSwitch_AUD = nxtSwitch_USD = nxtSwitch_EUR = 0;          // reset to be save
   nextDST("EUR",tB);                                          // get the offset for EUR of the time tB given
   nextDST("USD",tB);                                          // get the offset for USD of the time tB given

Aquí, tB será el comienzo de la última hora del viernes, es decir, cuando son las 16:00 en Nueva York. Esta suposición será la base del cálculo adicional, porque podremos calcular GMT para este tiempo:

tGMT = tNY + (NYShift + DST_USD)

y por lo tanto la compensación del bróker a GMT. Determinamos la compensación de esta manera: restamos de la última hora del viernes del bróker tB los últimos segundos de este día, SoB (tB). Obtenemos la hora 00:00 para el día y luego añadimos los segundos hasta las 16:00 (16*3600). Ahora conocemos la hora de Nueva York, de la cual obtenemos GMT añadiendo NYShift + DST_USD. Así, podemos determinar fácilmente la compensación horaria del bróker a partir de GMT y luego asignarla al campo correspondiente de OffsetBroker.
En la función, todo esto se realiza en horas (en lugar de en segundos) con la macrosustitución HoD() (Hora del Día), para ceñirse mejor a la documentación y que la verificación de la impresión resulte más sencilla:

   hNY   = HoD(tB - SoD(tB) + 16*3600);                        // get the hour of New York time
   hGMT  = HoD(tB - SoD(tB) + 16*3600 + NYShift + DST_USD);    // get the hour of GMT
   hTC   = HoD(tB);                                            // get the hour of the time given
   hDiff = hGMT - HoD(tB);                                     // get the difference between GMT and the broker

Después de todo, no resulta tan complicado. ;)

Por motivos de seguridad, insertaremos lo siguiente: Se encarga de comprobar si la situación no esperada (EE.UU. en verano y UE en invierno), no tiene lugar:

   if(DST_USD==0 && DST_EUR!=0)                                // this should not occur
      Alert(__LINE__," ",TOSTR(DST_USD),TOSTR(DST_EUR),"  USwin && EUsum");

Ahora podemos asignar la diferencia hallada y la duración de la apertura del mercado FX:

//--- set the broker offset for the various time situations:
   if(DST_USD+DST_EUR==0)                                      // both in winter (normal) time
     {
      OffsetBroker.actOffset = OffsetBroker.USwinEUwin = hDiff*3600;
      OffsetBroker.actSecFX  = OffsetBroker.secFxWiWi = SoW(tB);
     }
   else
      if(DST_USD == DST_EUR)                                   // else both in summer time
        {
         OffsetBroker.actOffset = OffsetBroker.USsumEUsum = hDiff*3600;
         OffsetBroker.actSecFX  = OffsetBroker.secFxSuSu = SoW(tB);
        }
      else
         if(DST_USD!=0 && DST_EUR==0)                          // US:summer EU:winter
           {
            OffsetBroker.actOffset = OffsetBroker.USsumEUwin = hDiff*3600;
            OffsetBroker.actSecFX  = OffsetBroker.secFxSuWi = SoW(tB);
           }

Finalmente, imprimimos todos los valores encontrados y retornamos la última compensación real:

//--- calc the ring of times NY->GMT->Broker->GMT->NY <= the last NY must always be 16!!
   Print(cmt,": ",DoWs(tB),TimeToString(tB),": ",TOSTR(hNY),TOSTR(hGMT),TOSTR(hTC),TOSTR(hDiff),
         " BrokerTime => GMT: ",TimeToString(tB+OffsetBroker.actOffset),
         " => tNY: ",TimeToString((tB + OffsetBroker.actOffset)-(NYShift + DST_USD)),
         "  End-FX after: ",OffsetBroker.actSecFX/3600,"h"
        );
   return(OffsetBroker.actOffset);

Esto tiene el aspecto siguiente:

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End FX in: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End FX in: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End FX in: 143h

Aquí se muestran algunas cosas interesantes que vale la pena discutir. La UE cambia primero el 25/10. Una semana después, los Estados Unidos, el 1/11. En este periodo intermedio, la última hora del viernes en MQ comienza a las 22:00, en lugar de a las 23:00, y la semana termina con esta barra después de 142horas, en lugar de las 143 horas habituales. ¿143 o 142 horas? La semana de FX tiene solo 120 horas: 5*24=120 Los segundos de la semana (SoW()) y las otras funciones comparables se refieren a la semana calendárica, que comienza el domingo a las 00:00. Pero del domingo a las 00:00 al viernes a las 23:00, ahora hay 6* 24-1 = 143. Este valor se usa a continuación para calcular en cualquier momento de una semana el tiempo restante que permanece abierto el mercado Fórex.

Estas tres líneas también se usan para verificar la lógica y el cálculo, y también como ejemplo de cómo un usuario puede determinar una hora local requerida a partir de GMT. Comenzando desde la izquierda, a la marca temporal del bróker le sigue la hora supuesta en Nueva York, hNY: 16, luego la hora GMT basada en la hora de Nueva York, la hora del bróker y su diferencia horaria: - 2 o 3. En la segunda parte, más a la derecha, GMT se calcula a partir de la hora del bróker (tB+OffsetBroker.actOffset) y después de GMT vemos nuevamente la hora en Nueva York ((tB+OffsetBroker.actOffset)-(NYShift + DST_USD)). Aquí siempre debemos tener 16:00 para tNY, y así es. A continuación, se realiza una segunda verificación de horas arbitrarias en la historia, y, además, de otros brókeres.


Cálculo de los fines de semana del cambio de horario

Pero antes de llegar a la verificación, donde revisaremos la historia de horas para "EURUSD", tendremos que discutir la esencia del cálculo, la función nextDST(...).
La función se llama con el parámetro zone para la zona horaria "USD", "EUR" o "AUD" (donde "AUD" no es realmente necesario) y t, el parámetro para la hora actual, generalmente la hora actual del bróker TimeCurrent(). Primero se verifica si es necesario un nuevo cálculo (aquí para "EUR"):

void nextDST(string zone, datetime t)
  {
   if((zone == "EUR") && t < nxtSwitch_EUR)
     {
      if(IS_DEBUG_MODE)
         Print("no change as time < nxtSwitch_EUR");
      return;
     }
...

Esto también muestra por qué resulta importante restablecer los valores de nxtSwitch_EUR a cero al inicio de la prueba, porque, de lo contrario, podría no realizarse un nuevo cálculo durante toda la prueba.

Luego, después de declararse e inicializarse la variable, llegamos al corazón de la función, que no es mío. Hace bastante tiempo, encontré en algún lugar de la red un algoritmo que determina un día determinado del mes. Se usa para determinar el horario de verano o invierno en un momento concreto. El algoritmo no es tan complicado:

  1. Determine el día del mes, un domingo, en el que se realiza el cambio.
  2. Cree una fecha a partir de ella.
  3. Encuentre el domingo de cambio futuro más cercano.
  4. Establezca el cambio horario, 0h o -1h, y el próximo domingo de cambio.

La magia de este algoritmo está en la línea de código que determina el día en el mes del cambio de hora. Para la UE, es el último domingo de marzo, y se calcula así (como ya he dicho, la idea de la fórmula no es mía):

d = (int)(31 - MathMod((4 + MathFloor(5*y/4)), 7));         // determing the last Sunday in March for the EU switch
Para los resultados del año 2021, para el último domingo de marzo, aquí tenemos la fórmula EXCEL, como d=25:

31 - MOD(ROUNDDOWN(5*2021/4);7) = 25.


A partir de ahí, se forma la marca temporal en la que la UE cambia al horario de verano: el 25 de marzo de 2021, último domingo de marzo:
spr = StringToTime(""+(string)y+".03."+(string)d+" 03:00"); // convert to datetime format

El procedimiento para todas las demás fechas es similar, y no necesita una explicación aparte.
Aquí, vemos ahora una sección más grande del código, que determina el horario actual de verano o invierno y la próxima fecha de cambio en la UE para una fecha determinada. Necesitamos tres secciones en un año: antes del primer cambio horario, entre el primer cambio y el segundo, y después del segundo. El siguiente cambio horario ya se encuentra en el próximo año, y esto debe tenerse en cuenta:

   if(zone == "EUR")
     {
      d = (int)(31 - MathMod((4 + MathFloor(5*y/4)), 7));         // determing the last Sunday in March for the EU switch
      spr = StringToTime(""+(string)y+".03."+(string)d+" 03:00"); // convert to datetime format
      if(t < spr)
        {
         DST_EUR = 0;                                             // no time offset
         nxtSwitch_EUR = spr;                                     // set the next time switch
         if(IS_DEBUG_MODE)
            Print(zone,"-DST for ",TimeToString(t)," DST: ",StringFormat("% 5i",DST_EUR),"  nxtSwitch: ",DoWs(nxtSwitch_EUR)," ",TimeToString(nxtSwitch_EUR));
         return;
        }
      d = (int)(31 - MathMod((1 + MathFloor(5*y/4)), 7));         // determing the last Sunday in October for the EU switch
      aut = StringToTime(""+(string)y+".10."+(string)d+" 03:00"); // convert to datetime format
      if(t < aut)
        {
         DST_EUR =-3600;                           // = +1h => 09:00 London time = GMT+05h+DST_EU = GMT+0+1 = GMT+1;
         nxtSwitch_EUR = aut;                                     // set the next time switch
         if(IS_DEBUG_MODE)
            Print(zone,"-DST for ",TimeToString(t)," DST: ",StringFormat("% 5i",DST_EUR),"  nxtSwitch: ",DoWs(nxtSwitch_EUR)," ",TimeToString(nxtSwitch_EUR));
         return;
        }
      y++;                                                        // re-calc the spring switch for the next year
      d = (int)(31 - MathMod((4 + MathFloor(5*y/4)), 7));         // determing the last Sunday in March for the EU switch
      spr = StringToTime(""+(string)y+".03."+(string)d+" 03:00"); // convert to datetime format
      if(t < spr)
        {
         DST_EUR = 0;                                             // no time offset
         nxtSwitch_EUR = spr;                                     // set the next time switch
         if(IS_DEBUG_MODE)
            Print(zone,"-DST for ",TimeToString(t)," DST: ",StringFormat("% 5i",DST_EUR),"  nxtSwitch: ",DoWs(nxtSwitch_EUR)," ",TimeToString(nxtSwitch_EUR));
         return;
        }
      Print("ERROR for ",zone," @ ",TimeToString(t)," DST: ",StringFormat("% 5i",DST_EUR),"  nxtSwitch: ",DoWs(nxtSwitch_EUR)," ",TimeToString(nxtSwitch_EUR),"  winter: ",TimeToString(aut),"  spring: ",TimeToString(spr));
      return;
     }

Se pueden ver tres secciones en un año:

  1. Antes del cambio al horario de verano en marzo.
  2. Antes del cambio al horario de invierno en octubre/noviembre.
  3. En el horario de invierno, el cambio al horario de verano del próximo año.

Esto se repite para EUR, USD y AUD.

Llamadas únicas de la función nextDST(..) como

nextDST("EUR", D'2019.02.05 20:00');
nextDST("EUR", D'2019.06.05 20:00');
nextDST("EUR", D'2019.11.20 20:00');
        
nextDST("USD", D'2019.02.05 20:00');
nextDST("USD", D'2019.06.05 20:00');
nextDST("USD", D'2019.11.20 20:00');
        
nextDST("AUD", D'2019.02.05 20:00');
nextDST("AUD", D'2019.06.05 20:00');
nextDST("AUD", D'2019.11.20 20:00');

pondrán a prueba los tres momentos relevantes del año para las tres regiones. Este es el resultado:

EU: último domingo de marzo y último domingo de octubre:
    EUR-DST for 2019.02.05 20:00 DST:        0   nxtSwitch: Su. 2019.03.31 03:00
    EUR-DST for 2019.06.05 20:00 DST: -3600   nxtSwitch: Su. 2019.10.27 03:00
    EUR-DST for 2019.11.20 20:00 DST:        0   nxtSwitch: Su. 2020.03.29 03:00

US: segundo domingo de marzo y primer domingo de noviembre:
    USD-DST for 2019.02.05 20:00 DST:        0   nxtSwitch: Su. 2019.03.10 03:00
    USD-DST for 2019.06.05 20:00 DST: -3600   nxtSwitch: Su. 2019.11.03 03:00
    USD-DST for 2019.11.20 20:00 DST:        0   nxtSwitch: Su. 2020.03.08 03:00

AU: primer domingo de noviembre y último domingo de marzo:
    AUD-DST for 2019.02.05 20:00 DST: -3600   nxtSwitch: Su. 2019.03.31 03:00
    AUD-DST for 2019.06.05 20:00 DST:        0   nxtSwitch: Su. 2019.11.03 03:00
    AUD-DST for 2019.11.20 20:00 DST: -3600   nxtSwitch: Su. 2020.03.29 03:00

Quizás el cambio de hora en Australia sea confuso, pero Australia, a diferencia de los EE.UU. y Europa, se encuentra en el hemisferio sur, donde el cambio de año se da en mitad del verano y, por consiguiente, es de esperar que su horario de verano sea en el invierno europeo.


El cambio en Rusia

Una pequeña nota: dado que MQ tiene raíces rusas y hay muchos usuarios rusos, también se incluyen los cambios de horario rusos. No obstante, debido a la gran cantidad de cambios, y cuándo y cómo se cambian los relojes en Rusia, he decidido usar una matriz bidimensional en la que se han introducido las horas y las diferencias correspondientes, y que el lector podrá consultar con esta función:

long RussiaTimeSwitch[][2] =
  {
   D'1970.01.00 00:00', -10800,
   D'1980.01.00 00:00', -10800,
   D'1981.04.01 00:00', -14400,
...
   D'2012.01.00 00:00', -14400,
   D'2014.10.26 02:00', -10800,
   D'3000.12.31 23:59', -10800
  };
int SzRussiaTimeSwitch = 67;                    // ArraySize of RussiaTimeSwitch

//+------------------------------------------------------------------+
//| Russian Time Switches                                            |
//+------------------------------------------------------------------+
void offsetRubGMT(const datetime t)
  {
   int i = SzRussiaTimeSwitch; //ArrayRange(RussiaTimeSwitch,0); 66
   while(i-->0 && t < RussiaTimeSwitch[i][0])
      continue;
// t >= RussiaTimeSwitch[i][0]
   nxtSwitch_RUB  = (datetime)RussiaTimeSwitch[fmin(SzRussiaTimeSwitch-1,i+1)][0];
   DST_RUS        = (int)RussiaTimeSwitch[fmin(SzRussiaTimeSwitch-1,i+1)][1];
   return;
  }
//+------------------------------------------------------------------+


La función que mantiene los tiempos actualizados

Ahora llegamos a la última función de este proyecto, encargada de mantener actualizados los valores cruciales:

//+------------------------------------------------------------------+
//| function to determin broker offset for the time tB given         |
//+------------------------------------------------------------------+
void checkTimeOffset(datetime tB)
  {
   if(tB < nxtSwitch_USD && tB < nxtSwitch_EUR && tB < nxtSwitch_AUD)
      return;                                                  // nothing has changed, return

También pregunta primero desde el principio si se debe establecer la compensación horaria (y la próxima fecha de cambio). De lo contrario, saldremos inmediatamente de la función.

Si no, los valores de "EUR", "USD", "AUD" y "RUB" se calcularán con la función nextDST() descrita anteriormente:

   if(tB>nxtSwitch_USD)
      nextDST("USD", tB);                                      // US has switched
   if(tB>nxtSwitch_EUR)
      nextDST("EUR", tB);                                      // EU has switched
   if(tB>nxtSwitch_AUD)
      nextDST("AUD", tB);                                      // AU has switched
   if(tB>nxtSwitch_RUB)
      nextDST("RUB", tB);                                      // RU has switched

"USD" y "EUR" se necesitan para determinar la compensación del bróker. "AUD" y "RUB" solo son necesarios si el usuario quiere conocerlos, de lo contrario, se podrán desactivar simplemente con //.

Luego, dependiendo del periodo, le asignaremos al campo OffsetBroker.actOffset la compensación horaria del bróker válida desde ese momento y OffsetBroker.actSecFX, el periodo actual de apertura del mercado de divisas:

   if(DST_USD+DST_EUR==0)                                      // both in winter (normal) time
     {
      OffsetBroker.actOffset = OffsetBroker.USwinEUwin;
      OffsetBroker.actSecFX  = OffsetBroker.secFxWiWi;
     }
   else
      if(DST_USD == DST_EUR)                                   // else both in summer time
        {
         OffsetBroker.actOffset = OffsetBroker.USsumEUsum;
         OffsetBroker.actSecFX  = OffsetBroker.secFxSuSu;
        }
      else
         if(DST_USD != DST_EUR)                                // US:summer EU:winter
           {
            OffsetBroker.actOffset = OffsetBroker.USsumEUwin;
            OffsetBroker.actSecFX  = OffsetBroker.secFxSuWi;
           }

Eso es todo. Estas son todas las funciones que debemos usar: una para determinar las compensaciones horarias del bróker partiendo de una cotización y otra que siempre determina la compensación horaria actual, a partir de la cual podemos averiguar fácilmente la hora GMT y, por consiguiente, cualquier otra hora local, incluso en el simulador de estrategias.

A continuación, vamos a mostrar dos formas de usar todo.


Script que muestra la configuración y el uso

Al principio, lo pusimos en el script (adjunto) DealingWithTimeScript.mq5:

#include <DealingWithTime.mqh>
//+------------------------------------------------------------------+
//| Finding the broker offsets                                       |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- step 1: set the broker time offsets in winter, summer and in between
   bool isTimeSet = setBokerOffset();
   if(!isTimeSet)
     {
      Alert("setBokerOffset failed");
      return;
     }

Esto se imprimirá en el diario de expertos:

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Compensación horaria de MetaQuotes Software Corp.: 
US=Winter & EU=Winter (USwinEUwin)      =  -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin)   =   -7200

Esto ya permite asignar las compensaciones encontradas a las variables de entrada. Este es el ejemplo del próximo capítulo. Lo que vemos aquí ya está explicado, así que avanzaremos al siguiente paso de este script. Simulamos una ruta desde el pasado hasta la hora real del bróker y calculamos e imprimimos los valores de GMT Nueva York y el tiempo restante que el mercado de divisas permanece abierto para marcas temporales seleccionadas al azar.

Obtenemos la historia completa de las marcas temporales de 1h de "EURUSD" con la función CopyTime("EURUSD",PERIOD_H1,datetime(0),TimeCurrent(),arr1h).

No obstante, para no ahogarnos en toneladas de datos y en una expresión larga para este gran periodo, solo mostraremos los datos de barras determinadas al azar. Para ello, seleccionaremos cuántos datos debe haber. Con el 5 dado serán aproximadamente 10, porque en el promedio aleatorio, la distancia de salto es aproximadamente la mitad de sz/5:

//--- step 2: get the quotes (here only 1h time stamps)
   datetime arr1h[], tGMT, tNY, tLeft;
   CopyTime("EURUSD",PERIOD_H1,datetime(0),TimeCurrent(),arr1h);
   int b       = 0,
       sz      = ArraySize(arr1h)-1,                  // oldest time stamp
       nChecks = sz/5,                                // ~2*5+1 randomly chosen bars
       chckBar = MathRand()%nChecks;                  // index of the first bar to check

Ahora, vamos a pasar por todas las barras, desde la más antigua hasta la actual, como en una prueba u optimización en el simulador de estrategias: while(++b<=sz). Lo primero que haremos es comprobar la situación de la hora para cada nueva barra: checkTimeOffset(arr1h[b]). Recordemos que en esta función lo primero que se comprueba es la necesidad de realizar un recálculo, por lo que, a pesar de su frecuencia, esta llamada no consume muchos recursos:

//---  step 3: simulate an EA or an indicator go through the time from the past to now
   while(++b<=sz)
     {
      //--- check the time situation, normally do it at the first bar after the weekend
      checkTimeOffset(arr1h[b]);

Ahora, realizaremos los cálculos (solo) para la barra determinada por el valor aleatorio GMT (tGMT), la hora de Nueva York (tNY) y tLeft el resto tiempo hasta que se cierre el mercado de divisas. Entonces, esto es imprimido, y luego se calcula el índice de la siguiente barra:

      //--- for a randomly selected bar calc. the times of GMT, NY & tLeft and print them
      if(b>=chckBar || b==sz)
        {
         tGMT  = arr1h[b] + OffsetBroker.actOffset;         // GMT
         tNY   = tGMT - (NYShift+DST_USD);                  // time in New York
         tLeft = OffsetBroker.actSecFX - SoW(arr1h[b]);     // time till FX closes
         PrintFormat("DST_EUR:%+ 6i  DST_EUR:%+ 6i  t[%6i]  tBrk: %s%s  "+
                     "GMT: %s%s  NY: %s%s  End-FX: %2ih => left: %2ih ",
                     DST_EUR,DST_USD,b,
                     DoWs(arr1h[b]),TimeToString(arr1h[b],TIME_DATE|TIME_MINUTES),
                     DoWs(tGMT),TimeToString(tGMT,TIME_DATE|TIME_MINUTES),
                     DoWs(tNY),TimeToString(tNY,TIME_DATE|TIME_MINUTES),
                     OffsetBroker.actSecFX/3600,tLeft/3600
                    );
         chckBar += MathRand()%nChecks;               // calc. the index of the next bar to check
        }

Aquí tenemos ahora la impresión completa de este script para una cuenta demo de Metaquotes:

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Compensación horaria de MetaQuotes Software Corp.: 
US=Winter & EU=Winter (USwinEUwin)      =  -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin)   =   -7200

DST_EUR: -3600  DST_EUR: -3600  t[ 28194]  tBrk: Mo.2002.05.20 22:00  GMT: Mo.2002.05.20 19:00  NY: Mo.2002.05.20 15:00  End-FX: 143h => left: 97h 
DST_EUR: -3600  DST_EUR: -3600  t[ 40805]  tBrk: We.2004.05.26 06:00  GMT: We.2004.05.26 03:00  NY: Tu.2004.05.25 23:00  End-FX: 143h => left: 65h 
DST_EUR: -3600  DST_EUR: -3600  t[ 42882]  tBrk: Th.2004.09.23 19:00  GMT: Th.2004.09.23 16:00  NY: Th.2004.09.23 12:00  End-FX: 143h => left: 28h 
DST_EUR:      +0  DST_EUR:     +0  t[ 44752]  tBrk: Tu.2005.01.11 17:00  GMT: Tu.2005.01.11 15:00  NY: Tu.2005.01.11 10:00  End-FX: 143h => left: 78h 
DST_EUR:      +0  DST_EUR: -3600  t[ 64593]  tBrk: We.2008.03.26 03:00  GMT: We.2008.03.26 01:00  NY: Tu.2008.03.25 21:00  End-FX: 142h => left: 67h 
DST_EUR:      +0  DST_EUR:     +0  t[ 88533]  tBrk: Tu.2012.02.07 13:00  GMT: Tu.2012.02.07 11:00  NY: Tu.2012.02.07 06:00  End-FX: 143h => left: 82h 
DST_EUR:      +0  DST_EUR:     +0  t[118058]  tBrk: We.2016.11.16 06:00  GMT: We.2016.11.16 04:00  NY: Tu.2016.11.15 23:00  End-FX: 143h => left: 65h 
DST_EUR: -3600  DST_EUR: -3600  t[121841]  tBrk: Mo.2017.06.26 05:00  GMT: Mo.2017.06.26 02:00  NY: Su.2017.06.25 22:00  End-FX: 143h => left: 114h 
DST_EUR:      +0  DST_EUR: -3600  t[144995]  tBrk: Mo.2021.03.22 06:00  GMT: Mo.2021.03.22 04:00  NY: Mo.2021.03.22 00:00  End-FX: 142h => left: 112h 
DST_EUR: -3600  DST_EUR: -3600  t[148265]  tBrk: Tu.2021.09.28 15:00  GMT: Tu.2021.09.28 12:00  NY: Tu.2021.09.28 08:00  End-FX: 143h => left: 80h 

Ya hemos analizado los dos primeros bloques. La tercera y última parte muestra para los puntos temporales elegidos aleatoriamente las respectivas diferencias de tiempo de la UE y los EE.UU., el índice del punto temporal seguido de las horas del bróker, GMT y Nueva York, seguidas de la hora de apertura del mercado de divisas en este momento y en el tiempo restante; para mayor claridad, se convierten en horas, en lugar de en segundos. Esto se puede verificar rápidamente ahora mismo: el 20/5/2002, la hora del bróker (MQ) es 22:00; se aplica el horario de verano, GMT = bróker-3h = 19:00 y NY = GMT - (5h-1h) = 15:00, y el mercado de divisas cierra en 97 horas. 97 = 4*24 (Mon.22:00-Fri.22:00 = 96h) +1h (Fri.22:00-23:00) - qed.

Entonces, un asesor o indicador que solo requiera las diversas compensaciones, necesitará apenas dos llamadas de función:

   bool isTimeSet = setBokerOffset();
   if(!isTimeSet)
     {
      Alert("setBokerOffset failed");
      return;
     }
..
   checkTimeOffset(TimeCurrent());


Alternativa usando las Variables de Entrada

Finalmente, vamos a ver un ejemplo en el que un asesor puede usar esto en sus variables de entrada. Con el script de arriba hemos obtenido la expresión:

Time Offset of MetaQuotes Software Corp.:
US=Winter & EU=Winter (USwinEUwin)      =  -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin)   =   -7200

Sabiendo esto, el asesor (no está adjunto, podrá copiarlo de aquí) tendría el aspecto siguiente:

#include <DealingWithTime.mqh>
// offsets of MetaQuotes demo account: DO NOT USE THEM FOR DIFFERENT BROKERS!!
input int   USwinEUwin=  -7200;    // US=Winter & EU=Winter
input int   USsumEUsum= -10800;    // US=Summer & EU=Summer
input int   USsumEUwin=  -7200;    // US=Summer & EU=Winter

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   OffsetBroker.USwinEUwin = USwinEUwin;
   OffsetBroker.USsumEUsum = USsumEUsum;
   OffsetBroker.USsumEUwin = USsumEUwin;
   OffsetBroker.actOffset  = WRONG_VALUE; 
   
   nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0;
   //--- Just a simple test if not ste or changed
   if(OffsetBroker.USwinEUwin+OffsetBroker.USsumEUsum+OffsetBroker.USsumEUwin==0)
      OffsetBroker.set     = false;
   else
      OffsetBroker.set     = true;
   //...
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   checkTimeOffset(TimeCurrent());
   tGMT  = TimeCurrent() + OffsetBroker.actOffset;    // GMT
   tNY   = tGMT - (NYShift+DST_USD);                  // time in New York
   tLon  = tGMT - (LondonShift+DST_EUR);              // time in London
   tSyd  = tGMT - (SidneyShift+DST_AUD);              // time in Sidney
   tMosc = tGMT - (MoskwaShift+DST_RUS);              // time in Moscow
   tTok  = tGMT - (TokyoShift);                       // time in Tokyo - no DST

   //...
   
  }

Aquí hemos utilizado las compensaciones de Metaquotes. ¡Asegúrese de usar las compensaciones de su bróker!

En OnTick(), en primer lugar, se calculan las compensaciones horarias y, a continuación, la hora GMT y las horas locales de Nueva York, Londres, Sydney, Moscú y Tokio para mostrar lo simple que resulta ahora. No se olvide: fíjese en los paréntesis.


Conclusión

A modo de conclusión, solo expondremos los resultados de la función setBokerOffset() aplicada a las cuentas demo de varios brókeres:

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of MetaQuotes Software Corp.: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -7200



EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of RoboForex Ltd: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -7200


EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of Alpari International: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -7200


EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 143h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 143h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of Pepperstone Group Limited: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -10800


EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 143h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 143h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of Eightcap Pty Ltd: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -10800

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of InstaForex Companies Group: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -7200


EUR: Fr.2020.10.23 21:00: hNY:16  hGMT:20  hTC:21  hDiff:-1   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 141h
USD: Fr.2020.10.30 21:00: hNY:16  hGMT:20  hTC:21  hDiff:-1   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 141h
NXT: Fr.2020.11.06 21:00: hNY:16  hGMT:21  hTC:21  hDiff: 0   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 141h
USD: Fr.2021.03.12 21:00: hNY:16  hGMT:21  hTC:21  hDiff: 0   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 141h
EUR: Fr.2021.03.26 21:00: hNY:16  hGMT:20  hTC:21  hDiff:-1   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 141h
NXT: Fr.2021.04.02 21:00: hNY:16  hGMT:20  hTC:21  hDiff:-1   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 141h

Time Offset of JFD Group Ltd: 
US=Winter & EU=Winter (USwinEUwin) = 0
US=Summer & EU=Summer (USsumEUsum) = -3600
US=Summer & EU=Winter (USsumEUwin) = -3600

Que el comercio le enriquezca. :)



Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/9929

Archivos adjuntos |
DealingWithTime.mqh (52.89 KB)
Carl Schreiber
Carl Schreiber | 5 jul 2023 en 19:08

La versión de DealingWithTime.mqh v. 1.01 del artículo Dealing with Time (Part 2): Funciones ( https://www.mql5.com/es/articles/9929 ) dejó de funcionar porque MQ cambió el comportamiento de CopyTime( ) algún tiempo después de la publicación de este artículo. Ahora esta función ya no devuelve valores de tiempo futuros si son mayores que el TimeCurrent( ) especificado para los parámetros start_time y/o stop_time. En su lugar, devuelve el valor de tiempo de apertura máximo posible de la última barra actual.

Dado que el final de la sesión de divisas se definió de forma que determinara el desfase horario del corredor, ¡esto da lugar ahora a valores incorrectos!

Este cálculo se ha modificado en la versión 2.03. Esta versión ya está disponible en CodeBase aquí: https://www.mql5.com/es/code/45287.

Pero también el cálculo de la traducción de la hora ha sido completamente cambiado, de modo que los complejos tiempos de traducción de la hora desde Sydney, Australia hasta los años 70 están ahora cubiertos.

También se adjunta la tabla DST 1975 - 2030.xlsx como un archivo zip con todos los cambios de tiempo desde los años 70 para que todos puedan comprobar las fórmulas funcionan correctamente, aquí está un ejemplo de la serie de la tabla:

1 de enero de 1982. - Hora estándar de EEUU (DST == 0), y el siguiente cambio es el 25 de abril de 1982, el último (25 del mes) domingo de abril (4). La tabla ya está ordenada por zona horaria geográfica (columna A), luego por zona horaria del año (columna L, spr=primavera, aut=otoño), y finalmente por la fecha de la consulta (columna C). La hoja de cálculo puede crearse automáticamente mediante un EA habilitado (el script no puede ejecutarse en modo de depuración). Test_DST 2.mq5 si lo ejecuta en modo de depuración y copia las líneas de registro en el depurador y las pega en la hoja de cálculo; El separador de celdas será un espacio.

Además, ahora hay una nueva función simple SecTillClose(), que le da el tiempo restante en segundos (moneda de tiempo MQ) hasta el cierre del mercado de divisas - sin CopyTime(). Esto es interesante para aquellos que quieren cerrar sus posiciones antes del fin de semana o no quieren abrir una nueva posición en un determinado período antes del fin de semana.

El indicador incluido DealingWithTime_TestIndi.mq5, como comentario al gráfico, muestra no sólo el horario de verano en Europa, EE.UU. y Australia (Sydney), sino también la hora actual y la diferencia horaria de diferentes ciudades. Aquí puedes encontrar una tabla con las diferentes horas locales de las principales ciudades para comparar: https://www.timeanddate.com/worldclock/. De esta forma puede comprobar los valores en cualquier momento. Este indicador también muestra cómo se definen y utilizan estos valores (qué se resta o se suma de qué), lo que facilita su uso por su cuenta: copiar y pegar, la forma más rápida de programar.

Las dos últimas líneas también muestran el último segundo de la sesión FX actual y el tiempo restante en horas (que es más fácil de juzgar) y segundos. En Nueva York, cuando la sesión FX se cierra a las 17:00 hora local del viernes, no hay ninguna barra válida abierta a las 17:00 hora de Nueva York. Por lo tanto, esta función resta 1 segundo para obtener la última hora de apertura válida de la última barra en el horario del broker. Sin embargo, algunos corredores terminan su sesión de divisas unos minutos antes, dejando de proporcionar precios o de aceptar órdenes de negociación.

Daniel K
Daniel K | 18 feb 2024 en 16:37

Hola @Anil Varma

He leído el último post del autor @Carl Schreiber sobre la función CopyTime() pero como me resulta más comprensible la 1ª versión sigo usando DealingWithTime.mqh v . 1.01.

En mi indicador quiero:

Asigne a cada barra el tiempo bruto NY (segundos), la hora NY y el minuto NY utilizando los siguientes buffers para poder visualizarlos en la ventana de datos:
double NyRawTimeBuffer[];
double NyHourBuffer[];
double NyMinuteBuffer[];
 void AssignNyTime (const datetime& time[],int rates_total)
   {
      
      MqlDateTime dT_struc;
      
      //--- Asignar a cada vela NY tiempo bruto (en segundos), NY hora, NY min
      ArraySetAsSeries(time,true);
      for(int z=0;z<rates_total;z++)
         { 
            checkTimeOffset(time[z]);                   // comprobar cambios de DST
            datetime tC, tGMT, tNY;
            tC    = time[z];
            tGMT  = time[z] + OffsetBroker.actOffset;   // GMT
            tNY   = tGMT - (NYShift+DST_USD);           // tiempo en Nueva York
            int j = int (tNY);                          // conversión de datetime a int 
            NyRawTimeBuffer[z]=j;
            
            TimeToStruct(tNY,dT_struc);
            NyHourBuffer[z]=dT_struc.hour;
            NyMinuteBuffer[z]=dT_struc.min;
 
         }        
       
   return;
   }

La función funcione solo cuando el gráfico del timeframe donde se inicia la terminal esta en H1.

Si cierro el terminal y el timeframe se establece en digamos M5, y luego reinicio el terminal me da el siguiente error:

2024.02.18 15:33:38.048 MyFractals_V4 (EURUSD,M5) 240: CopyTime() FAILED for EURUSD H1: need times from 2024.02.12 02:00:00, but there are only from 1970.01.01 00:00:00 error: 4401

Usted ya me sugirió a través de mensajes a utilizar CheckLoadHistory() de este artículo ( https://www.mql5.com/es/code/1251 ) y colocarlo antes de CopyXXXX() función en su biblioteca:

//--- encuentra el offset del broker
    OffsetBroker.set = false;
    
    CheckLoadHistory("EURUSD",PERIOD_H1,TERMINAL_MAXBARS,true);
      
    b = CopyTime("EURUSD",PERIOD_H1,BegWk+26*3600,5,arrTme);      // obtener la hora de la última barra de 1h antes del cambio en EU

Pero el problema sigue ahí.

En el checkhistory.mqh (fila 19) anoté el siguiente comentario pero no entiendo si puede ser un problema. Intenté comentarlo y probar el programa de nuevo pero no funcionó.

//--- no pida la carga de sus propios datos si es un indicador
   if(MQL5InfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR && Period()==period && Symbol()==symbol) return(true);

¿Hay alguna forma de corregir el error sin cambiar a la librería actualizada DealingWithTimeV2.03.mqh sin reescribir todo el código?




Anil Varma
Anil Varma | 19 feb 2024 en 05:53
Nauris Zukas probador de estrategias, pero no funciona.


¿Tiene la "La alternativa utilizando a través de variables de entrada" la única manera de obtener tiempos correctos en el probador de estrategia?

Hola

He intentado modificar el código como a continuación y hasta ahora está trabajando para mí. Tenga en cuenta que he convertido la clase con constructores y todos los métodos son parte de la clase. La clase necesita ser llamada e inicializada en su EA/Clase de Estrategia.

CDealWithTime.OnTick() debe colocarse en EA/Strategy OnTick()

//+-----------------------------------------------------------------------------------------------------------------------------+
//| función para determinar el offset del broker para el tiempo dado (tB)
//+-----------------------------------------------------------------------------------------------------------------------------+
void CDealWithTime::OnTick(void) {

                string vMethod = __FUNCTION__ + " Line[" + (string)__LINE__ + "] ";

                // Realizar la actualización horaria de TimeH01[]
                if(IsNewBarH01()) {
      // ... BegWk = BoW(tC), Código original
                        datetime weekBegin = BoW(TimeCurrent());
                        // El código original añadía 5 compases al tiempo de WeakBegin.
                        // Sumar 5 días * 3600 segundosEnHora a la hora de inicio débil, no es igual a cinco compases, ya que puede que no haya compases en
                        // fin de semana/festivo. Por lo tanto WeekBegin+5*3600 puede resultar un tiempo mientras que no hay barra en él.
                        int                      barShift  = iBarShift(mSymbol,mTimeFrame,weekBegin+(5*3600));
                        // Devolverá la primera barra disponible en Time(weekBegin+(5*3600)
                        datetime timeStop        = iTime(mSymbol,mTimeFrame,barShift);          // Última barra de una hora del viernes, antes del cambio en la UE.
                        // Resultado al sumar segundos (weekBegin)+(5*3600): CDealWithTime::OnTick Line[229] : GetLastError[0] copiedTime EURUSD-PERIOD_H1 for [0] bars weekBegin[2024.01.01 02:00] to timeStop[2023.12.31 21:00] time5th[2023.12.29 23:00]
                        int bars = Bars(mSymbol,mTimeFrame,weekBegin,timeStop);

                        // Necesitamos el bucle while..ya que IsNewBarH01() se comprobará sólo una vez en el Tick, si es true entonces hasta la siguiente nueva barra no se comprobará más
                        ResetLastError();
                        int attempt = 0;
                        while(CopyTime(mSymbol,mTimeFrame,weekBegin,timeStop,TimeH01) != bars && attempt <= 10) {
                                Sleep(100);
                                attempt++;
                        }
                        if(attempt > 0) {
                                PrintFormat("%s: GetLastError[%i] copiedTime %s-%s for [%i] bars weekBegin[%s] to timeStop[%i][%s]",vMethod,GetLastError(),mSymbol,EnumToString(mTimeFrame),bars,TimeToString(weekBegin),barShift,TimeToString(timeStop));
                        }
                }

                // Realice una comprobación semanal, si hay cambios en el horario de ahorro de luz diurna (DST).
                if(IsNewBarW01()) {
                        checkTimeOffset(TimeCurrent());
                        int  attempt  = 0;
                        bool isOffset = false;
                        do{
                                isOffset = setBokerOffset();
                                attempt++;
                        } while(!isOffset && attempt <= 10);
                }

} // Fin de OnTick()
Anil Varma
Anil Varma | 19 feb 2024 en 05:58
amrali #:

Este código calcula automáticamente el horario de verano para los corredores europeos y estadounidenses:

https://www.mql5.com/es/code/27860

El código anterior se utilizó en Forex Market Hours https://www.mql5.com/es/code/27771 para calcular los cambios de horario de verano.

Se pueden construir funciones similares para distintas zonas del mundo.

Hola Amrali

Bonito y sencillo código como alternativa al artículo DealingWithTime v2.03. Lo estudiaré con más detalle.

Anil Varma
Anil Varma | 19 feb 2024 en 06:08
Daniel K #:
DealingWithTime.mqh v . 1.01.

Hola Daniel

DealingWithTime.mqh v . 1.01. Este artículo y su código ya no funciona debido a los cambios en los métodos de cálculo MQL, como se explica por Carl en DealingWithTime.mqh v 2.03 artículo https://www.mql5.com/es/code/45287

Usted no debe usarlo en absoluto.

Cómo ser un mejor programador (parte 06): 9 hábitos que conducen a una codificación eficaz Cómo ser un mejor programador (parte 06): 9 hábitos que conducen a una codificación eficaz
La escritura de código no siempre redunda en el dominio de una codificación efectiva. Hay ciertos hábitos que he desarrollado gracias a la experiencia, y que nos ayudan a codificar con mayor eficacia. En el presente artículo, analizaremos con detalle algunos de ellos. Este es un artículo de lectura obligada para aquellos programadores que quieran lograr escribir algoritmos complejos con menos molestias.
Gráficos en la biblioteca DoEasy (Parte 84): Clases herederas del objeto gráfico abstracto estándar Gráficos en la biblioteca DoEasy (Parte 84): Clases herederas del objeto gráfico abstracto estándar
En este artículo, analizaremos la creación de las clases herederas del objeto gráfico abstracto estándar del terminal. El objeto de esta clase describirá las propiedades comunes para todos los objetos gráficos, es decir, se tratará simplemente de un cierto objeto gráfico. Para aclarar su pertenencia a un objeto gráfico real, necesitaremos heredar de él, y en la clase del objeto heredado, escribir las propiedades inherentes a ese objeto gráfico en particular.
Gráficos en la biblioteca DoEasy (Parte 85): Colección de objetos gráficos - añadiendo los objetos nuevamente creados Gráficos en la biblioteca DoEasy (Parte 85): Colección de objetos gráficos - añadiendo los objetos nuevamente creados
En este artículo, finalizaremos la creación de las clases herederas de la clase de objeto gráfico abstracto y comenzaremos a implementar el almacenamiento de estos objetos en la clase de colección. En concreto, crearemos la funcionalidad necesaria para añadir los objetos gráficos estándar recién creados a la clase de colección.
Gestionando el Horario (Parte 1): Fundamentos Gestionando el Horario (Parte 1): Fundamentos
Funciones y fragmentos de código que simplifican y aclaran el manejo del tiempo, la diferencia con el bróker y los cambios en el horario de verano o invierno. La sincronización precisa puede ser un elemento crucial en el trading. A la hora actual, ¿la bolsa de valores de Londres o Nueva York está ya abierta o sigue cerrada? ¿Cuándo comienza y finaliza el horario comercial para el trading en Fórex? Para un tráder que comercia de forma manual y en vivo, esto no supone un gran problema.