Cómo manejar el Error 146, "Trade context busy"
1. Qué es el "Trade Context" del Terminal Cliente MetaTrader 4
Sacado de la referencia de MetaEditor:
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
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso