English Русский 中文 Deutsch 日本語 Português
Cómo manejar el Error 146, "Trade context busy"

Cómo manejar el Error 146, "Trade context busy"

MetaTrader 4Ejemplos | 19 enero 2016, 12:50
1 000 0
Andrey Khatimlianskii
Andrey Khatimlianskii


1. Qué es el "Trade Context" del Terminal Cliente MetaTrader 4

Sacado de la referencia de MetaEditor:

    Para operar con expertos y scripts solo se proporciona un hilo que se lanza en el contexto del programa (el contexto de trading automático de los expertos y scripts). Por esta razón, si una operación de trading de un experto ocupa el contexto, entonces hay otro experto o script que no puede hacer llamadas a las funciones de trading debido al error 146 (ERR_TRADE_CONTEXT_BUSY).

O mejor dicho, solo un experto (script) puede operar. El Error 146 detendrá a cualquier experto que intente operar en ese momento. En este artículo encontrará soluciones a este problema.


2. Función IsTradeAllowed()

La forma más sencilla de averiguar si el contexto de trading está ocupado es por medio de la función IsTradeAllowed().

Sacado de la referencia de MetaEditor:

    "bool IsTradeAllowed()

    Devuelve true si el experto puede operar y el hilo de trading no está ocupado, en caso contrario devuelve false.

Esto significa que se puede operar solo si la función IsTradeAllowed() devuelve TRUE.

La comprobación se tiene que hacer justo antes de la operación de trading.

Este es un ejemplo de uso incorrecto de la función:

int start()
  {
    // comprobamos si el contexto de trading está libre
    if(!IsTradeAllowed())
      {
        // si la función IsTradeAllowed() devuelve FALSE, informamos al usuario de ello,
        Print("¡El contexto de trading está ocupado! ¡El asesor experto no puede abrir ninguna posición!");
        // y terminamos la operación del experto. Se reiniciará cuando venga el 
        // siguiente tick
        return(-1);
      }
    else
      {
        // si la función IsTradeAllowed() devuelve TRUE, informamos de ello al usuario 
        // y continuamos trabajando
        Print("¡El contexto de trading está libre! Continuamos trabajando...");
      }
    // comprobamos si ahora se puede entrar al mercado
    ...
    // calculamos los niveles Stop Loss y Take Profit, así como el tamaño del lote
    ...
    // abrir una posición
    if(OrderSend(...) < 0) 
        Alert("Error al abrir la posición # ", GetLastError());
    return(0);
  }

En este ejemplo se comprueba el estado del contexto de trading justo al inicio de la función start(). Es una idea equivocada. Otro asesor experto puede ocupar el contexto de trading en el momento en que el nuestro se encuentra haciendo todos los cálculos: comprobación de entrada al mercado, niveles de Stop Loss y Take Profit, tamaño del lote, etc. En cuyo caso el intento de apertura de posición no tendrá éxito.

El siguiente es un ejemplo de uso adecuado de la función:

int start()
  {
    // comprobamos si ahora se puede entrar al mercado
    ...
    // calculamos los niveles de Stop Loss y Take Profit, así como el tamaño del lote
    ...
    // ahora comprobamos si el contexto de trading está libre
    if(!IsTradeAllowed())
      {
        Print("¡El contexto de trading está ocupado! ¡El asesor experto no puede abrir ninguna posición!");
        return(-1);
      }
    else
        Print("¡El contexto de trading está libre! Intentamos abrir una posición...");
    // si la comprobación es correcta, abrimos la posición
    if(OrderSend(...) < 0) 
        Alert("Error al abrir la posición # ", GetLastError());
    return(0);
  }

El estado del contexto de trading se calcula aquí, justo antes de abrir la posición, de este modo la probabilidad de que otro experto se interponga entre estas dos acciones es mucho menor. (Aunque todavía existe. Esto se considera a continuación).

Las dos desventajas fundamentales de este método son:

  • todavía es probable que los expertos comprueben el estado simultáneamente, y, habiendo recibido resultados positivos, intenten operar al mismo tiempo
  • si la comprobación falla, el experto intentará operar de nuevo solo en el siguiente tick, pero ese retraso no es para nada deseable

El segundo problema se soluciona fácilmente: solo hay que esperar hasta que el contexto de trading se libere. Entonces, el asesor experto comenzará a operar inmediatamente después de que el otro experto termine.

Este es el código correspondiente:

int start()
  {
    // comprobamos si ahora se puede entrar al mercado
    ...
    // calculamos los niveles de Take Profit y Stop Loss, así como el tamaño del lote
    ...
    // comprobamos si el contexto de trading está libre
    if(!IsTradeAllowed())
      {
        Print("¡El contexto de trading está ocupado! Esperamos a que se libere...");
        // bucle infinito
        while(true)
          {
            // si el usuario para el experto, paramos la operación
            if(IsStopped()) 
              { 
                Print("¡El usuario paró el asesor experto!"); 
                return(-1); 
              }
            // si el contexto de trading se libera terminamos el bucle y empezamos a operar
            if(IsTradeAllowed())
              {
                Print("¡El contexto de trading se ha liberado!");
                break;
              }
            // si el bucle no satisface ninguna condición de ruptura, esperamos 0.1 segundos
            // y volvemos a hacer la comprobación
            Sleep(100);
          }
      }
    else
        Print("¡El contexto de trading está libre! Intentando abrir una posición...");
    // intentamos abrir una posición
    if(OrderSend(...) < 0) 
        Alert("Error al abrir la posición # ", GetLastError());
    return(0);
  }
Sin embargo, una vez más, esta implementación plantea algunos problemas:
  • dado que la función IsTradeAllowed() no solo se responsabiliza del estado del contexto de trading, sino que también tiene que habilitar y deshabilitar asesores expertos, el experto se puede quedar colgado en un bucle infinito. En este caso solo parará si se elimina manualmente del gráfico
  • si el experto espera que se libere el contexto de trading durante unos segundos, los precios pueden cambiar y será imposible operar con ellos; por lo tanto, los datos se tienen que actualizar. Los niveles de apertura, Take Profit y Stop Loss de la posición a abrir se tienen que recalcular

El código correcto es como sigue:

// tiempo (en segundos) que espera el experto hasta la operación 
// el contexto está libre (si está ocupado)
int MaxWaiting_sec = 30;
int start()
  {
    // comprobamos si ahora se puede entrar al mercado
    ...
    // calculamos los niveles de Stop Loss y Take Profit, así como el tamaño del lote
    ...
    // comprobamos si el contexto de trading está libre
    if(!IsTradeAllowed())
      {
        int StartWaitingTime = GetTickCount();
        Print("¡El contexto de trading está ocupado! Esperamos a que se libere...");
        // bucle infinito
        while(true)
          {
            // si el usuario detuvo el asesor experto, paramos la operación
            if(IsStopped()) 
              { 
                Print("¡El usuario paró el asesor experto!"); 
                return(-1); 
               }
            // si el tiempo de espera supera el especificado en la variable 
            // MaxWaiting_sec, también paramos la operación
            if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) 
              {
                Print("¡El límite de espera (" + MaxWaiting_sec + " sec) se ha sobrepasado!");
                return(-2);
              }
            // si el contexto de trading se ha liberado,
            if(IsTradeAllowed())
              {
                Print("¡El contexto de trading está libre!");
                // refrescamos la información del mercado
                RefreshRates();
                // recalculamos los niveles de Stop Loss y Take Profit
                ...
                // salimos del bucle y comenzamos a operar                
                break;
              }
            // si el bucle no satisface ninguna condición de ruptura, esperamos 0.1 
            // segundos y reiniciamos la comprobación
            Sleep(100);
          }
      }
    else
        Print("¡El contexto de trading está libre! Intentando abrir una posición...");

    // intentamos abrir una posición
    if(OrderSend(...) < 0) 
        Alert("Error al abrir la posición # ", GetLastError());
 
    return(0);
  }
En el ejemplo anterior hemos añadido:
  • refresco de la información del mercado (RefreshRates()) y recálculo correspondiente de Stop Loss y Take Profit
  • tiempo máximo de MaxWaiting_sec, tras exceder ese valor el experto dejará de operar

Ya puede utilizar el código de arriba en sus asesores expertos.

Por cierto, una nota final: hay que poner la lógica de comprobación en una función separada. Así se simplificará la integración en los expertos, así como la utilización.

/////////////////////////////////////////////////////////////////////////////////
// int _IsTradeAllowed( int MaxWaiting_sec = 30 )
//
// la función comprueba el estado del contexto de trading. Códigos de retorno:
//  1 - el contexto de trading está libre, se puede operar
//  0 - el contexto de trading estaba ocupado, pero se liberó. Es posible operar solo después de que 
//      la información del mercado se haya refrescado.
// -1 - el contexto de trading está ocupado, esperando interrumpido por el usuario (el experto se elimina 
//      del gráfico, se apaga el terminal, el periodo del gráfico y/o el símbolo se modificó, etc.)
// -2 - el contexto de trading está ocupado, se alcanza el límite de espera (MaxWaiting_sec). 
//      Posiblemente el experto no puede operar (casilla de verificación "Permitir operaciones en directo" 
//      en la configuración del experto).
//
// MaxWaiting_sec - tiempo (en segundos) que la función esperará 
// hasta que el contexto de trading se libere, si está ocupado. Por defecto, 30.
/////////////////////////////////////////////////////////////////////////////////
int _IsTradeAllowed(int MaxWaiting_sec = 30)
  {
    // comprobamos si el contexto de trading está libre
    if(!IsTradeAllowed())
      {
        int StartWaitingTime = GetTickCount();
        Print("¡El contexto de trading está ocupado! Esperamos a que se libere...");
        // bucle infinito
        while(true)
          {
            // si el usuario detuvo el asesor experto, paramos la operación
            if(IsStopped()) 
              { 
                Print("¡El usuario detuvo el experto!"); 
                return(-1); 
              }
            // si el tiempo de espera excede el tiempo especificado en 
            // la variable MaxWaiting_sec, también se para la operación
            if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
              {
                Print("El límite de espera superó (" + MaxWaiting_sec + " segundos.)!");
                return(-2);
              }
            // si el contexto de trading se ha liberado,
            if(IsTradeAllowed())
              {
                Print("¡El contexto de trading se ha liberado!");
                return(0);
              }
            // si el bucle no satisface ninguna condición de ruptura, esperamos 0.1 
            // segundos y reiniciamos la comprobación      Sleep(100);
          }
      }
    else
      {
        Print("¡El contexto de trading está libre!");
        return(1);
      }
  }

Una plantilla del experto que utiliza la función:

int start()
  {
    // comprobamos si ahora se puede entrar al mercado
    ...
    // calculamos los niveles Stop Loss y Take Profit, así como el tamaño del lote
    ...
    // comprobamos si el contexto de trading está libre
    int TradeAllow = _IsTradeAllowed();
    if(TradeAllow < 0) 
      { 
        return(-1); 
      }
    if(TradeAllow == 0)
      {
        RefreshRates();
        // recalculamos los niveles Take Profit y Stop Loss
        ...
      }
    // abrir una posición
    if(OrderSend(...) < 0) 
        Alert("Error al abrir la posición # ", GetLastError());
    return(0);
  }

A continuación vamos a sacar algunas conclusiones:

Utilizar la función IsTradeAllowed() es sencillo. Idealmente sirve para diferenciar los accesos al contexto de trading por parte de dos o tres expertos que trabajan simultáneamente. Sin embargo, debido a unos inconvenientes, puede producirse un Error 146 cuando hay muchos expertos trabajando a la vez. Si la opción "Permitir operaciones en directo" está desactivada es posible que el experto se cuelgue.

Por todo ello consideramos una solución alternativa a este problema, que consiste en utilizar una variable global como si fuera un semáforo.

 

3. Variables globales del terminal cliente

Esta es la definición:

Las variables globales del terminal cliente son variables que están accesibles a todos los expertos, scripts e indicadores. Esto significa que las variables globales creadas por un experto se pueden utilizar en otros expertos (en nuestro caso, para distribuir los accesos).

MQL4 proporciona varias funciones para trabajar con variables globales:

  • GlobalVariableCheck() - comprueba si una variable global existe
  • GlobalVariableDel() - borra una variable global
  • GlobalVariableGet() - obtiene el valor de la variable global
  • GlobalVariableSet() - crea o modifica una variable global
  • GlobalVariableSetOnCondition() - cambia el valor de la variable global especificada por el usuario. Se diferencia de GlobalVariableSet() porque el nuevo valor se establece solo en un valor previo determinado. Esta función es clave para crear el semáforo.
  • GlobalVariablesDeleteAll() - borra todas las variables globales (es difícil imaginar quién puede necesitar esto)

¿Por qué utilizar GlobalVariableSetOnCondition() y no la combinación de funciones GlobalVariableGet() y GlobalVariableSet()? Por las mismas razones, porque puede transcurrir tiempo entre las dos funciones. Y otro asesor experto se puede interponer en el cambio de semáforo. Pero esto no es lo que necesitamos.


4. El concepto básico de semáforo

El experto que vaya a operar tiene que comprobar el estado del semáforo. Si el semáforo muestra una "luz roja" (variable global = 1) significa que hay otro experto operando, de modo que hay que esperar. Si por el contrario exhibe una "luz verde" (variable global = 0) puede comenzar a operar inmediatamente. No hay que olvidar poner la "luz roja" para otros expertos.

Así que tenemos que crear dos funciones: una para establecer la "luz roja" y otra para poner la "luz verde". La tarea es sencilla. Sin embargo no vamos a establecer conclusiones, sino que vamos a formular la secuencia de acciones a ejecutar por cada una de las funciones, que llamaremos TradeIsBusy() y TradeIsNotBusy(), y terminaremos implementándolas.

 

5. Función TradeIsBusy()

Como hemos dicho antes, la tarea principal de la función consiste en esperar a que aparezca la "luz verde" y entonces cambiar a "luz roja". Además tenemos que comprobar si existe la variable global y, en caso negativo, crearla. Puede parecer más razonable y eficiente hacer esta comprobación desde la función init() del experto. Pero entonces existe la posibilidad de que el usuario la borre, haciendo que ningún otro experto pueda operar. Por este motivo colocamos el código en el cuerpo de la función.

Todo esto se tiene que acompañar con una visualización de la información, y con un procesamiento de los errores que ocurren al trabajar con la variable global. Tampoco tenemos que olvidar que puede producirse un "cuelgue"; el tiempo de operación de la función se tiene que limitar.

Así que finalmente obtenemos lo siguiente:

/////////////////////////////////////////////////////////////////////////////////
// int TradeIsBusy( int MaxWaiting_sec = 30 )
//
// La función sustituye el valor TradeIsBusy 0 por 1.
// Si TradeIsBusy = 1 en el momento del lanzamiento, la función espera hasta que TradeIsBusy valga 0, 
// y entonces hace la sustitución.
// Si no existe la variable global TradeIsBusy, la función la crea.
// Códigos de retorno:
//  1 - completado correctamente. Se asignó el valor 1 a la variable TradeIsBusy
// -1 - TradeIsBusy = 1 en el momento de lanzar la función, el usuario interrumpió la espera
//      (el experto fue eliminado del gráfico, el terminal se cerró, el periodo del gráfico o el símbolo 
//      se cambiaron, etc.)
// -2 - TradeIsBusy = 1 en el momento de lanzar la función, se superó el límite de espera
//      (MaxWaiting_sec)
/////////////////////////////////////////////////////////////////////////////////
int TradeIsBusy( int MaxWaiting_sec = 30 )
  {
    // no tiene sentido dividir el contexto de trading en las pruebas, tan solo terminamos 
    // la función
    if(IsTesting()) 
        return(1);
    int _GetLastError = 0, StartWaitingTime = GetTickCount();
    //+------------------------------------------------------------------+
    //| Comprobamos si existe una variable global; si no, la creamos     |
    //+------------------------------------------------------------------+
    while(true)
      {
        // si el usuario detuvo el asesor experto, paramos la operación
        if(IsStopped()) 
          { 
            Print("¡El usuario detuvo el experto!"); 
            return(-1); 
          }
        // si el tiempo de espera excede el especificado en la variable 
        // MaxWaiting_sec, también paramos la operación
        if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
          {
            Print("¡Tiempo de espera (" + MaxWaiting_sec + " sec) excedido!");
            return(-2);
          }
        // comprobamos si existe la variable global
        // si existe, abandonamos el bucle y vamos al bloque de cambio 
        // del valor TradeIsBusy
        if(GlobalVariableCheck( "TradeIsBusy" )) 
            break;
        else
        // si GlobalVariableCheck devuelve FALSE, significa que no existe o  
        // que ha ocurrido un error durante la comprobación
          {
            _GetLastError = GetLastError();
            // si se trata de un error, mostramos la información, esperamos 0.1 segundos y 
            // reiniciamos la comprobación
            if(_GetLastError != 0)
             {
              Print("TradeIsBusy()-GlobalVariableCheck(\"TradeIsBusy\")-Error #",
                    _GetLastError );
              Sleep(100);
              continue;
             }
          }
        // si no hay ningún error significa que no existe ninguna variable global,
        // intentamos crearla
        // si GlobalVariableSet > 0 significa que la variable global se ha creado correctamente. 
        // Salimos de la función
        if(GlobalVariableSet( "TradeIsBusy", 1.0 ) > 0 ) 
            return(1);
        else
        // si GlobalVariableSet devuelve un valor <= 0 significa que ha ocurrido un error 
        // durante la creación de la variable
         {
          _GetLastError = GetLastError();
          // mostramos la información, esperamos 0.1 segundos y lo intentamos de nuevo
          if(_GetLastError != 0)
            {
              Print("TradeIsBusy()-GlobalVariableSet(\"TradeIsBusy\",0.0 )-Error #",
                    _GetLastError );
              Sleep(100);
              continue;
            }
         }
      }
    //+----------------------------------------------------------------------------------+
    //| Si la ejecución de la función llega hasta aquí, significa que existe             | 
    //| la variable global.                                                              |
    //| Esperamos hasta que TradeIsBusy = 0 y cambiamos el valor de TradeIsBusy por 1    |
    //+----------------------------------------------------------------------------------+
    while(true)
     {
     // si el usuario detuvo el asesor experto, paramos la operación
     if(IsStopped()) 
       { 
         Print("¡El usuario detuvo el experto!"); 
         return(-1); 
       }
     // si el tiempo de espera excede el especificado en la variable 
     // MaxWaiting_sec, también paramos la operación
     if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
       {
         Print("¡Tiempo de espera (" + MaxWaiting_sec + " sec) excedido!");
         return(-2);
       }
     // intentamos cambiar el valor de TradeIsBusy de 0 a 1
     // si todo va bien salimos de la función devolviendo 1 ("completado correctamente")
     if(GlobalVariableSetOnCondition( "TradeIsBusy", 1.0, 0.0 )) 
         return(1);
     else
     // en caso contrario, puede ser por dos razones: TradeIsBusy = 1 (uno tiene que esperar), o 

     // ha ocurrido un error (esto es lo que vamos a comprobar)
      {
      _GetLastError = GetLastError();
      // si todavía es un error, mostramos la información y lo intentamos otra vez
      if(_GetLastError != 0)
      {
   Print("TradeIsBusy()-GlobalVariableSetOnCondition(\"TradeIsBusy\",1.0,0.0 )-Error #",
         _GetLastError );
       continue;
      }
     }
     // si no hay ningún error significa que TradeIsBusy = 1 (hay otro experto operando), entonces mostramos 
     // la información y esperamos...
     Comment("Espere hasta que otro experto termine de operar...");
     Sleep(1000);
     Comment("");
    }
  }

Bien, llegados aquí todo parece claro:

  • se comprueba si la variable global existe y en caso negativo se crea
  • se intenta cambiar el valor de la variable global de 0 a 1; se disparará solo si su valor es = 0.

MaxWaiting_sec es la cantidad máxima de segundos durante los que la función trabaja. La función no se opone a la eliminación del experto del gráfico.

La información de los errores ocurridos se muestra en el log.

 

6. Function TradeIsNotBusy()

La función TradeIsNotBusy soluciona el problema inverso, encendiendo la "luz verde".

No está limitada por el tiempo de la operación y el usuario no puede terminarla. El funcionamiento es sencillo, si la "luz verde" está apagada ningún asesor experto puede operar.

Finalmente, no devuelve ningún código de retorno; el resultado solo puede ser una ejecución correcta.

Este es el aspecto que tiene:

/////////////////////////////////////////////////////////////////////////////////
// void TradeIsNotBusy()
//
// La función establece el valor de la variable global TradeIsBusy = 0.
// Si TradeIsBusy no existe, la función la crea.
/////////////////////////////////////////////////////////////////////////////////
void TradeIsNotBusy()
  {
    int _GetLastError;
    // no tiene sentido dividir el contexto de trading en las pruebas, tan solo terminamos 
    // la función
    if(IsTesting()) 
      { 
        return(0); 
      }
    while(true)
      {
        // si el usuario detiene el asesor experto, dejamos de trabajar
        if(IsStopped()) 
          { 
            Print("¡El usuario detuvo el experto!"); 
            return(-1); 
          }
        // intentamos establecer el valor de la variable global a 0, o 
        // creamos la variable
        // si GlobalVariableSet devuelve un valor > 0 significa que todo 
        // ha ido bien. Salimos de la función
        if(GlobalVariableSet( "TradeIsBusy", 0.0 ) > 0) 
            return(1);
        else
        // si GlobalVariableSet devuelve un valor <= 0 significa que ha ocurrido un error. 
        // Mostramos la información, esperamos, e intentamos de nuevo
         {
         _GetLastError = GetLastError();
         if(_GetLastError != 0 )
           Print("TradeIsNotBusy()-GlobalVariableSet(\"TradeIsBusy\",0.0)-Error #", 
                 _GetLastError );
         }
        Sleep(100);
      }
  }

 

7. Integración y utilización en los Asesores Expertos

Ahora tenemos tres funciones que distribuyen el acceso al flujo de trading. Con el objetivo de simplificar la integración de estas funciones en los expertos, hemos creado el archivo TradeContext.mq4, que activamos por medio de la directiva #include, tal y como se muestra en el archivo que se adjunta en el presente artículo.

Esta es la plantilla del experto que utiliza las funciones TradeIsBusy() y TradeIsNotBusy():

#include <TradeContext.mq4>
 
int start()
  {
    // comprobamos si ahora se puede entrar al mercado
    ...
    // calculamos los niveles StopLoss y TakeProfit, así como el tamaño del lote
    ...
    // esperamos a que se libere el contexto de trading y entonces lo ocupamos (si se produce un error, 
    // salimos)
    if(TradeIsBusy() < 0) 
        return(-1); 
    // refrescamos la información del mercado
    RefreshRates();
    // recalculamos los niveles StopLoss y TakeProfit
    ...
    // abrir una posición
    if(OrderSend(...) < 0) 
      { 
        Alert("Error al abrir la posición # ", GetLastError()); 
      }

    // liberamos el contexto de trading
    TradeIsNotBusy();

    return(0);
  }

Solamente puede ocurrir un problema con las funciones TradeIsBusy() y TradeIsNotBusy(). Si el experto se elimina del gráfico después de que el contexto de trading haya sido ocupado, el valor de la variable TradeIsBusy permanecerá igual a 1. Tras lo cual los otros expertos no pueden operar.

Sin embargo el problema se resuelve fácilmente; basta con no eliminar el experto del gráfico cuando este se encuentra operando.

También es posible que el valor de la variable TradeIsBusy no se establezca a cero cuando el terminal se cierre inesperadamente. En ese caso se puede utilizar la función TradeIsNotBusy() desde la función init() del asesor experto.

Y por supuesto, la variable se puede cambiar en cualquier momento pulsando el botón F3 del terminal. Esta posibilidad no está documentada pero permite desactivar todos los expertos.

komposter (komposterius@mail.ru), 2006.04.11

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

Archivos adjuntos |
TradeContext.mqh (11.54 KB)
Ejemplo de Asesor Experto Ejemplo de Asesor Experto
Este artículo expone los principios de desarrollo de programas MQL4 mediante la creación de un Asesor Experto que implementa un sistema basado en el indicador estándar MACD.
MagicNumber, el identificador "mágico" de la orden MagicNumber, el identificador "mágico" de la orden
Este artículo expone el problema que plantea el uso de varios asesores expertos que trabajan de forma simultánea en un mismo Terminal Cliente MT 4. Aprenderemos a indicar al asesor experto que maneje solamente sus propias órdenes, sin que modifique o cierre otras posiciones, es decir, las abiertas manualmente o las colocadas por otros expertos. Este artículo se dirige a los usuarios que tienen unos conocimientos básicos de programación en MQL 4 y cuentan con algo de experiencia manejando el terminal.
Calidad de modelado de datos de un minuto Calidad de modelado de datos de un minuto
Calidad de modelado de datos de un minuto
Una pausa entre operaciones Una pausa entre operaciones
El presente artículo aborda el problema de la gestión de las pausas entre las operaciones de trading cuando hay varios expertos trabajando en el terminal cliente MT 4. Está pensado para los usuarios que ya cuentan con unas habilidades básicas, tanto en el manejo del terminal como en la programación MQL4.