
Eventos de Trading en MetaTrader 5
Introducción
Todos los comandos para ejecutar operaciones de trading se pasan al servidor de trading desde el terminal de cliente MetaTrader 5 a través de solicitudes de envío. Cada solicitud se debe llenar correctamente de acuerdo con la operación solicitada; de lo contrario, no pasará la validación primaria y no se aceptará por el servidor para su procesamiento.
Las solicitudes aceptadas por el servidor de trading se almacenan en forma de órdenes que pueden ser pendientes o ejecutarse inmediatamente por el precio de mercado. Las órdenes se almacenan en el servidor hasta que se llenan o cancelan. El resultado de la ejecución de una orden es una transacción.
Una transacción cambia la posición de trading por un símbolo determinado; puede abrir, cerrar, incrementar, reducir o invertir la posición. Por tanto, una posición abierta siempre es el resultado de ejecutar una o más transacciones. Puede encontrar más información detallada en el artículo Órdenes, Posiciones, y Transacciones en MetaTrader 5.
Este artículo describe el concepto, términos y procesos que fluyen en el período desde el envío de una solicitud hasta su traslado al historial de trading tras su procesamiento.
Paso de Solicitud del Terminal de Cliente al Servidor de Trading
Para ejecutar una operación de trading, debe enviar una orden al sistema de trading. Siempre se envía una solicitud al servidor de trading cuando se presenta una orden del terminal de cliente. La estructura de una solicitud debe llenarse correctamente, independientemente de cómo lleve a cabo las operaciones de trading - manualmente o usando un programa MQL5.
Para ejecutar una operación de trading manualmente, debe abrir la ventana de diálogo para llenar una solicitud de trading pulsando la tecla F9. Cuando haga trading automáticamente a través de MQL5, las solicitudes se envían usando la función OrderSend(). Puesto que un gran número de solicitudes incorrectas pueden causar una sobrecarga del servidor de trading poco deseable, cada solicitud se debe revisar antes de enviarla usando la función OrderCheck(). El resultado de la revisión de una solicitud se coloca en una variable descrita por la estructura MqlTradeCheckresult.
Una vez que la solicitud llega al servidor de trading, pasa la revisión primaria, comprobado...
- ... si tiene liquidez suficiente como para ejecutar la operación de trading;
- ... si los precios especificados son correctos: precios abiertos, Stop Loss, Take Profit, etc.;
- ... si el precio específico está presente en el flujo de precios para la ejecución instantánea;
- ... si los niveles de Stop Loss y el Take Profit están ausentes en el modo de Ejecución de Mercado;
- ... si el volumen es correcto: volumen mínimo y máximo, paso, volumen máximo de la posición (SYMBOL_VOLUME_MIN, SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_STEP y SYMBOL_VOLUME_LIMIT);
- ... el estado del símbolo: sesión de cotización o trading, posiblemente de trading por el símbolo, un modo específico de trading (por ejemplo, solo cierre de posiciones), etc.
- ... el estado de la cuenta de trading: límites diferentes para tipos de cuenta específicos;
- ... otras revisiones, dependiendo de la operación de trading solicitada.
Si una solicitud pasa la revisión primaria de corrección, se colocará en las solicitudes que aguardan para ser procesadas. Como resultado de procesar una solicitud, se crea una orden (comando para ejecutar una operación de trading) en la base del servidor de trading. No obstante, hay dos tipos de solicitudes que no resultan en la creación de una orden:
- una solicitud para cambiar una posición (cambiar su Stop Loss y/o Take Profit);
- una solicitud para modificar una orden pendiente (sus niveles de precio y fecha de caducidad).
El terminal de cliente recibe el mensaje de que la solicitud ha sido aceptada y colocada en el subsistema de trading de la plataforma MetaTrader 5. El servidor coloca la solicitud aceptada en la cola de solicitudes para su futuro procesamiento, que puede resultar en...
- ... la realización de una orden pendiente;
- ... la ejecución de una orden instantánea por el precio de mercado;
- ... la modificación de una orden o posición.
La duración de estancia de una solicitud en la cola del servidor tiene un límite de tres minutos. Una vez que el este período se ha excedido, la solicitud se quita de la cola de solicitudes.
Enviar Eventos de Trading del Servidor al Terminal de Cliente
El modelo de evento y las funciones de gestión de eventos se implementan en el lenguaje MQL5. Esto significa que en respuesta a cualquier evento predefinido, la ejecución del entorno MQL5 llama a la función apropiada: el controlador de eventos. Para el procesamiento de eventos de trading está la función predefinida OnTrade(). Dentro de ella se debe colocar el código para trabajar con órdenes, posiciones y transacciones. Esta función se llama solo para Asesores Expertos, no se usará en indicadores y scripts aún cuando usted añada en ellos una función con el mismo nombre y tipo.
Los eventos de trading se generan por el servidor en caso de...
- ... cambio de órdenes activas,
- ... cambio de posiciones,
- ... cambio de transacciones,
- ... cambio de historial de trading.
Note que una operación puede provocar varios eventos. Por ejemplo, la activación de una orden pendiente lleva al desarrollo de dos eventos:
- la aparición de una transacción que se escribe en el historial de trading;
- el traslado de la orden pendiente de la lista de órdenes activas a la lista de órdenes del historial (la orden se ha trasladado al historial).
Otro ejemplo de eventos múltiples es la ejecución de varias transacciones en la base de una sola orden, en caso de que el volumen requerido no se pueda obtener de una sola oferta opuesta. El servidor de trading crea y envía los mensajes sobre cada evento al terminal de cliente. Por eso, la función OnTrade() se puede llamar varias veces para un evento aparentemente individual. Este es un ejemplo sencillo del procedimiento de una orden en el subsistema de trading de la plataforma MetaTrader 5.
Ejemplo: mientras una orden pendiente para la compra de 10 lotes de EURUSD aguarda su ejecución, aparecen ofertas contrarias para la venta de 1, 4 y 5 lotes. Esas tres solicitudes juntas suponen el volumen requerido de 10 lotes, de modo que se ejecutan una a una, si la política de ejecución permite la realización de operaciones de trading por partes.
Como resultado de la ejecución de 4 órdenes, el servidor realizará 3 transacciones de 1, 4 y 5 lotes en la base de ofertas contrarias existentes. ¿Cuántos eventos de trading se generarán en este caso? La primera solicitud opuesta para la venta de un lote llevará a la ejecución de la transacción de 1 lote. Este sería el primer evento de trading (transacción de 1 lote). Pero la orden pendiente para comprar 10 lotes también ha cambiado; ahora es la orden para comprar 9 lotes de EURUSD. El cambio de volumen de la orden pendiente sería el segundo evento de trading (cambio de volumen de una orden pendiente).
Para la segunda transacción de 4 lotes, se generarán los otros dos evento de trading, y se enviará un mensaje sobre cada uno de ellos al terminal de cliente que inició la orden pendiente inicial para la compra de 10 lotes EURUSD.
La última transacción de 5 lotes generará tres eventos de trading:
- la transacción de 5 lotes;
- el cambio de volumen;
- el traslado de la orden al historial de órdenes.
Como resultado de la ejecución de la transacción, el terminal de cliente recibirá 7 eventos de trading uno tras otro (suponiendo que la conexión entre el terminal de cliente y el servidor de trading sea estable y no se pierdan mensajes). Esos mensajes se deben procesar en un Asesor Experto usando la función OnTrade().
Procesamiento de Órdenes con el Servidor de Trading
Todas las órdenes que aguardan su ejecución se trasladarán al historial al final, ya sea porque la condición para su ejecución se cumplió, o porque fueron canceladas. Hay muchas variantes de una cancelación de orden:
- ejecución de una transacción en la base de la orden;
- rechazo de la orden por un comprador;
- cancelación de la orden a petición del trader (solicitud manual o automática de un programa MQL5);
- caducidad de la orden, que se determina bien por el trader al enviar la solicitud, o bien por las condiciones de trading del sistema determinado;
- falta de liquidez en la cuenta de trading para ejecutar la transacción en el momento en el que se cumplieron las condiciones de su ejecución;
- la orden se canceló a causa de la política de ejecución (una orden parcialmente ejecutada se cancela).
Independientemente de la razón por la que una orden activa pasa al historial, el mensaje sobre el cambio se envía al terminal de cliente. Los mensajes sobre el evento de trading no van a todos los terminales de cliente, sino a los que están conectados con la cuenta correspondiente.
Por eso, la documentación de la función OrderSend() dice:
Valor de devolución
Si la revisión de una solicitud con la función OrderSend() devuelva "true", esto no es un símbolo de ejecución exitosa de una operación de trading. Para una descripción más detallada del resultado de ejecución de funciones, analice los campos de la estructura MqlTradeResult.
Actualización del Historial de Trading en el Terminal de Cliente
Los mensajes sobre los eventos de trading y cambios en el historial de trading vienen a través de canales separados. Al enviar una solicitud de compra usando la función OrderSend(), puede obtener el ticket de la orden, que se crea como resultado de la validación exitosa de la solicitud. Al mismo tiempo, puede que la orden no aparezca en el terminal de cliente, y que fallen los intentos de seleccionarla usando la función OrderSelect().
En la figura de arriba puede ver cómo el servidor de trading le comunica el ticket de la orden al programa MQL5, pero el mensaje sobre el evento de trading (la aparición de una nueva orden) no ha llegado todavía. El mensaje sobre el cambio de la lista de órdenes activas no ha llegado todavía.
Puede darse una situación en la que el mensaje sobre la aparición de una nueva orden llegue al programa cuando se realiza una transacción en su base, por tanto la orden ya no está en la lista de órdenes activas y ha pasado al historial. Esta es una situación real, puesto que la velocidad de procesamiento de solicitudes es mucho más rápida que la velocidad actual de entrega de mensajes a través de una red.
Gestión de Eventos de Trading
Todas las operaciones en el servidor y el envío de mensajes sobre eventos de trading se realizan asimétricamente. Solo hay un método seguro para descubrir qué es lo que ha cambiado exactamente en la cuenta de trading. Este método es la memorización del estado e historial de trading, y después la comparación con el nuevo estado.
El algoritmo para rastrear los eventos de trading en los Asesores Expertos es como se muestra a continuación:
- declarar los contadores de órdenes, posiciones y transacciones en el panorama global;
- determinar la profundidad del historial de trading que se solicitará para la caché del programa MQL5. Cuanto más historial carguemos en la caché, más recursos del terminal y del ordenador consumiremos;
- inicializar los contadores de órdenes, posiciones y transacciones en la función OnInit();
- determinar el controlador de funciones en las que se pedirá el historial de trading de la caché;
- Tras cargar el historial de trading, veremos también que le pasó a la cuenta de trading, comparando el estado memorizado con el actual.
Este es el algoritmo más sencillo, y permite descubrir si el número de posiciones abiertas (órdenes, transacciones) ha cambiado, y cuál fue la dirección del cambio. Si hay cambios, entonces podemos obtener más información detallada. Si el número de órdenes no ha cambiado pero las órdenes mismas han sido modificadas, necesita un enfoque diferente - una variante que no se trata en este artículo.
Los cambios del contador se pueden ver en las funciones OnTrade() y OnTick() de un Asesor Experto.
Escribamos un programa de ejemplo paso a paso.
1. El contador de órdenes, transacciones y posiciones en el panorama global.
int orders; // number of active orders int positions; // number of open positions int deals; // number of deals in the trade history cache int history_orders; // number of orders in the trade history cache bool started=false; // flag of initialization of the counters
2. La profundidad del historial de trading a cargar en la caché se configura en la variable de entrada days ("días", que carga el historial de trading por el número de días especificado en esta variable).
input int days=7; // depth of the trade history in days //--- set the limit of the trade history on the global scope datetime start; // start date of the trade history in cache datetime end; // end date of the trade history in cache
3. Inicialización de los contadores y los límites del historial de trading. Exponga la inicialización de los contadores con la función InitCounters() para una mejor legibilidad del código:
int OnInit() { //--- end=TimeCurrent(); start=end-days*PeriodSeconds(PERIOD_D1); PrintFormat("Limits of the trade history to be loaded: start - %s, end - %s", TimeToString(start),TimeToString(end)); InitCounters(); //--- return(0); }
La función InitCounters() trata de cargar el historial de trading en la caché, y en caso de éxito, inicializa todos los contadores. Asimismo, si el historial se carga con éxito, el valor de la variable global "started" será "true", lo que indica que los contadores se han inicializado correctamente.
//+------------------------------------------------------------------+ //| initialization of the counters of positions, orders and deals | //+------------------------------------------------------------------+ void InitCounters() { ResetLastError(); //--- load history bool selected=HistorySelect(start,end); if(!selected) { PrintFormat("%s. Failed to load the history from %s to %s to the cache. Error code: %d", __FUNCTION__,TimeToString(start),TimeToString(end),GetLastError()); return; } //--- get current values orders=OrdersTotal(); positions=PositionsTotal(); deals=HistoryDealsTotal(); history_orders=HistoryOrdersTotal(); started=true; Print("The counters of orders, positions and deals are successfully initialized"); }
4. La revisión de los cambios en la cuenta de trading se ejecuta en los controladores OnTick() y OnTrade(). La variable "started" se revisa primero: si su valor es "true", se llama a la función SimpleTradeProcessor(). De lo contrario, se llama a la función de inicialización de los contadores InitCounters().
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(started) SimpleTradeProcessor(); else InitCounters(); } //+------------------------------------------------------------------+ //| called when the Trade event occurs | //+------------------------------------------------------------------+ void OnTrade() { if(started) SimpleTradeProcessor(); else InitCounters(); }
5. La función SimpleTradeProcessor() comprueba si el número de órdenes, transacciones y posiciones ha cambiado. Tras realizar todas las revisiones, llamamos la función CheckStartDateInTradeHistory(), que mueve el valor "start" más cerca al momento presente si es necesario.
//+------------------------------------------------------------------+ //| simple example of processing changes in trade and history | //+------------------------------------------------------------------+ void SimpleTradeProcessor() { end=TimeCurrent(); ResetLastError(); //--- load history bool selected=HistorySelect(start,end); if(!selected) { PrintFormat("%s. Failed to load the history from %s to %s to the cache. Error code: %d", __FUNCTION__,TimeToString(start),TimeToString(end),GetLastError()); return; } //--- get current values int curr_orders=OrdersTotal(); int curr_positions=PositionsTotal(); int curr_deals=HistoryDealsTotal(); int curr_history_orders=HistoryOrdersTotal(); //--- check whether the number of active orders has been changed if(curr_orders!=orders) { //--- number of active orders is changed PrintFormat("Number of orders has been changed. Previous number is %d, current number is %d", orders,curr_orders); /* other actions connected with changes of orders */ //--- update value orders=curr_orders; } //--- change in the number of open positions if(curr_positions!=positions) { //--- number of open positions has been changed PrintFormat("Number of positions has been changed. Previous number is %d, current number is %d", positions,curr_positions); /* other actions connected with changes of positions */ //--- update value positions=curr_positions; } //--- change in the number of deals in the trade history cache if(curr_deals!=deals) { //--- number of deals in the trade history cache has been changed PrintFormat("Number of deals has been changed. Previous number is %d, current number is %d", deals,curr_deals); /* other actions connected with change of the number of deals */ //--- update value deals=curr_deals; } //--- change in the number of history orders in the trade history cache if(curr_history_orders!=history_orders) { //--- the number of history orders in the trade history cache has been changed PrintFormat("Number of orders in the history has been changed. Previous number is %d, current number is %d", history_orders,curr_history_orders); /* other actions connected with change of the number of order in the trade history cache */ //--- update value history_orders=curr_history_orders; } //--- check whether it is necessary to change the limits of trade history to be requested in cache CheckStartDateInTradeHistory(); }
La función CheckStartDateInTradeHistory() calcula la fecha de inicio de la solicitud del historial de trading para el momento actual (curr_start) y la compara con la variable "start". Si la diferencia entre ellas es mayor de 1 día, entonces "start" se corrige y los contadores de órdenes del historial y transacciones se actualizan.
//+------------------------------------------------------------------+ //| Changing start date for the request of trade history | //+------------------------------------------------------------------+ void CheckStartDateInTradeHistory() { //--- initial interval, as if we started working right now datetime curr_start=TimeCurrent()-days*PeriodSeconds(PERIOD_D1); //--- make sure that the start limit of the trade history period has not gone //--- more than 1 day over intended date if(curr_start-start>PeriodSeconds(PERIOD_D1)) { //--- we need to correct the date of start of history loaded in the cache start=curr_start; PrintFormat("New start limit of the trade history to be loaded: start => %s", TimeToString(start)); //--- now load the trade history for the corrected interval again HistorySelect(start,end); //--- correct the counters of deals and orders in the history for further comparison history_orders=HistoryOrdersTotal(); deals=HistoryDealsTotal(); } }
El código completo del Asesor Experto DemoTradeEventProcessing.mq5 está adjunto con este artículo.
Conclusión
Todas las operaciones en la plataforma de trading online Meta Trader 5 se ejecutan de forma asíncrona, y los mensajes sobre todos los cambios en una cuenta de trading se envían de forma independiente unos de otros. Por tanto, no tiene sentido tratar de rastrear eventos independientes basándonos en la regla "una solicitud - un evento de trading". Si necesita determinar de forma precisa qué ha cambiado exactamente cuando ocurre un evento de trading, debe analizar todas sus transacciones, posiciones y órdenes en cada llamada del controlador OnTrade(), comparando su estado actual con el anterior.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/232





- 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