- Evento principal de Asesores Expertos: OnTick
- Principios y conceptos básicos: orden, transacción y posición
- Tipos de operaciones de trading
- Tipos de órdenes
- Modos de ejecución de órdenes por precio y volumen
- Fechas de vencimiento de órdenes pendientes
- Cálculo del margen para una orden futura: OrderCalcMargin
- Estimación del beneficio de una operación de trading: OrderCalcProfit
- Estructura MqlTradeRequest
- Estructura MqlTradeCheckResult
- Solicitar validación: OrderCheck
- Solicitar resultado del envío: estructura MqlTradeResult
- Enviar una solicitud de trading: OrderSend y OrderSendAsync
- Operaciones de compraventa
- Modificar los niveles de Stop Loss y/o Take Profit de una posición
- Trailing stop
- Cierre de una posición: total y parcial
- Cierre de posiciones opuestas: total y parcial
- Colocar una orden pendiente
- Modificar una orden pendiente
- Borrar una orden pendiente
- Obtener una lista de órdenes activas
- Propiedades de una orden (activas e históricas)
- Funciones para leer las propiedades de órdenes activas
- Seleccionar órdenes por propiedades
- Obtener la lista de posiciones
- Propiedades de posiciones
- Funciones de lectura de propiedades de posición
- Propiedades de transacción
- Seleccionar órdenes y transacciones del historial
- Funciones para leer propiedades de órdenes del historial
- Funciones para leer propiedades de transacciones del historial
- Tipos de transacciones de trading
- Evento OnTradeTransaction
- Peticiones síncronas y asíncronas
- Evento OnTrade
- Seguimiento de los cambios en el entorno de trading
- Crear Asesores Expertos multisímbolo
- Limitaciones y ventajas de los Asesores Expertos
- Crear Asesores Expertos en el Asistente MQL
Enviar una solicitud de trading: OrderSend y OrderSendAsync
Para realizar operaciones de trading, la API de MQL5 proporciona dos funciones: OrderSend y OrderSendAsync. Al igual que OrderCheck, realizan una comprobación formal de los parámetros de la solicitud pasados en forma de la estructura MqlTradeRequest y, a continuación, si tiene éxito, envía una solicitud al servidor.
La diferencia entre ambas funciones es la siguiente. OrderSend espera que la orden se ponga en cola para ser procesado en el servidor y recibe de éste datos significativos en los campos de la estructura MqlTradeResult que se pasa como segundo parámetro de la función. OrderSendAsync devuelve inmediatamente el control al código de llamada independientemente de cómo responda el servidor. Al mismo tiempo, de todos los campos de la estructura MqlTradeResult excepto retcode, la información importante se rellena sólo en request_id. Utilizando este identificador de solicitud, un programa MQL puede recibir más información sobre el progreso del procesamiento de esta solicitud en el evento OnTradeTransaction. Un enfoque alternativo consiste en analizar periódicamente las listas de órdenes, transacciones y posiciones. Esto también puede hacerse en bucle, estableciendo un tiempo de espera en caso de problemas de comunicación.
Es importante señalar que, a pesar del sufijo «Async» en el nombre de la segunda función, la primera función sin este sufijo tampoco es totalmente síncrona. El hecho es que el resultado del procesamiento de órdenes por el servidor, en concreto, la ejecución de una transacción (o, probablemente, varias transacciones basadas en una orden) y la apertura de una posición, generalmente se produce de forma asíncrona en un sistema de trading externo. Por lo tanto, la función OrderSend también requiere la recopilación y el análisis en diferido de las consecuencias de la ejecución de la solicitud, que los programas MQL deben, si es necesario, implementar ellos mismos. Más adelante veremos un ejemplo de envío verdaderamente síncrono de una solicitud y recepción de todos sus resultados (véase MqlTradeSync.mqh).
bool OrderSend(const MqlTradeRequest &request, MqlTradeResult &result)
La función devuelve true en caso de una comprobación básica correcta de la estructura request en el terminal y algunas comprobaciones adicionales en el servidor. Sin embargo, esto sólo indica la aceptación de la orden por parte del servidor y no garantiza una ejecución satisfactoria de la operación de trading.
El servidor de trading puede rellenar los valores del campo deal o order en la estructura result devuelta si estos datos son conocidos en el momento en que el servidor formatea una respuesta a la llamada OrderSend. Sin embargo, en el caso general, los eventos de ejecución de transacciones o colocación de órdenes Limit correspondientes a una orden pueden producirse después de que se envíe la respuesta al programa MQL en el terminal. Por lo tanto, para cualquier tipo de solicitud de trading, al recibir el resultado de la ejecución OrderSend, es necesario comprobar el código de retorno del servidor de trading retcode y el código de respuesta del sistema de trading externo retcode_external (si es necesario) que están disponibles en la estructura result devuelta. Basándose en ellos, debe decidir si esperar a las acciones pendientes en el servidor o emprender sus propias acciones.
Cada orden aceptada se almacena en el servidor de trading a la espera de ser procesada hasta que se produzca alguno de los siguientes eventos que afectan a su ciclo de vida:
- ejecución cuando aparece una solicitud de contador
- se activa cuando llega el precio de ejecución
- fecha de vencimiento
- cancelación por el usuario o el programa MQL
- eliminación por el bróker (por ejemplo, en caso de compensación o escasez de fondos, Stop Out)
El prototipo OrderSendAsync repite por completo el de OrderSend.
bool OrderSendAsync(const MqlTradeRequest &request, MqlTradeResult &result)
La función está pensada para trading de alta frecuencia, cuando, según las condiciones del algoritmo, es inaceptable perder tiempo esperando una respuesta del servidor. El uso de OrderSendAsync no acelera el procesamiento de solicitudes por parte del servidor ni el envío de solicitudes al sistema de trading externo.
¡Atención! En el probador, la función OrderSendAsync funciona como OrderSend, lo cual dificulta la depuración del procesamiento pendiente de solicitudes asíncronas.
La función devuelve true al enviar correctamente la solicitud al servidor de MetaTrader 5. Sin embargo, esto no significa que la solicitud haya llegado al servidor y haya sido aceptada para su procesamiento. Al mismo tiempo, el código de respuesta de la estructura result receptora contiene el valor TRADE_RETCODE_PLACED (10008), es decir, «la orden se ha realizado».
Al procesar la solicitud recibida, el servidor enviará un mensaje de respuesta al terminal sobre un cambio en el estado actual de las posiciones, órdenes y transacciones, lo que lleva a la generación del evento OnTrade en un programa MQL. Allí, el programa puede analizar el nuevo entorno de trading y el historial de la cuenta. A continuación veremos ejemplos relevantes.
Además, los detalles de la ejecución de la solicitud del operador en el servidor pueden rastrearse utilizando el manejador OnTradeTransaction. Al mismo tiempo, debe tenerse en cuenta que, como resultado de la ejecución de una solicitud de trading, el manejador OnTradeTransaction será llamado varias veces. Por ejemplo, al enviar una solicitud de compra de mercado, ésta es aceptada para su procesamiento por el servidor, se crea la correspondiente orden de «compra» para la cuenta, se ejecuta la orden y se realiza la operación, como resultado de lo cual se elimina de la lista de órdenes abiertas y se añade al historial de órdenes. A continuación, la operación se añade al historial y se crea una nueva posición. Para cada uno de estos eventos, se llamará a la función OnTradeTransaction.
Empecemos con un ejemplo sencillo de Asesor Experto CustomOrderSend.mq5. Permite establecer todos los campos de la solicitud en los parámetros de entrada, lo que es similar a CustomOrderCheck.mq5, pero además difiere en que envía una solicitud al servidor en lugar de una simple comprobación en el terminal. Ejecute el Asesor Experto en su cuenta demo. Después de completar los experimentos, no olvide eliminar el Asesor Experto del gráfico o cerrar el gráfico para no enviar una solicitud de prueba cada vez que vuelva a iniciar el terminal.
El nuevo ejemplo presenta otras mejoras. En primer lugar, se añade el parámetro de entrada Async.
input bool Async = false; |
Esta opción permite seleccionar la función que enviará la solicitud al servidor. Por defecto, el parámetro es igual a false y se utiliza la función OrderSend. Si se establece en true, se llamará a OrderSendAsync.
Además, con este ejemplo, empezaremos a describir y completar un conjunto especial de funciones en el archivo de encabezado TradeUtils.mqh, que resultará muy útil para simplificar la codificación de los robots. Todas las funciones se colocan en el espacio de nombres TU (de «Trade Utilities»), y en primer lugar, introducimos funciones para la conveniente salida registro de estructura MqlTradeRequest y MqlTradeResult.
namespace TU
|
El objetivo de las funciones es proporcionar todos los campos significativos (no vacíos) de forma concisa pero conveniente: se muestran en una línea con una designación única para cada uno.
Como puede ver, la función utiliza la clase SymbolMetrics para MqlTradeRequest. Facilita la normalización de múltiples precios o volúmenes para el mismo instrumento. No olvide que la normalización de precios y volúmenes es un requisito previo para preparar una solicitud de operación correcta.
class SymbolMetrics
|
La normalización directa de los valores se confía a las funciones auxiliares NormalizePrice y NormalizeLot (el esquema de esta última es idéntico al que vimos en el archivo LotMarginExposure.mqh).
double NormalizePrice(const double price, const string symbol = NULL)
|
Si conectamos TradeUtils.mqh, el ejemplo CustomOrderSend.mq5 tiene la siguiente forma (los fragmentos de código '...' omitidos no se han modificado con respecto a CustomOrderCheck.mq5).
void OnTimer()
|
Dado que ahora los precios y el volumen están normalizados, puede intentar introducir valores desiguales en los parámetros de entrada correspondientes. A menudo se obtienen en los programas durante los cálculos, y nuestro código los convierte de acuerdo con la especificación de símbolos.
Con la configuración por defecto, el Asesor Experto crea una solicitud para comprar el lote mínimo del instrumento actual por mercado y lo hace utilizando la función OrderSend.
OrderSend(request,result)=true / ok TRADE_ACTION_DEAL, EURUSD, ORDER_TYPE_BUY, V=0.01, ORDER_FILLING_FOK, @ 1.12462 DONE, D=1250236209, #=1267684253, V=0.01, @ 1.12462, Bid=1.12456, Ask=1.12462, Request executed, Req=1 |
Por regla general, con la negociación permitida, esta operación debería completarse con éxito (estado HECHO, comentario «Solicitud ejecutada»). En la estructura result recibimos inmediatamente el número de transacción D.
Si abrimos la configuración del Asesor Experto y sustituimos el valor del parámetro Async por true, enviaremos una solicitud similar pero con la función OrderSendAsync.
OrderSendAsync(request,result)=true / ok TRADE_ACTION_DEAL, EURUSD, ORDER_TYPE_BUY, V=0.01, ORDER_FILLING_FOK, @ 1.12449 PLACED, Order placed, Req=2 |
En este caso, el estado es PLACED (colocado), y no se conoce el número de operación en el momento en que se devuelve la función. Sólo conocemos el identificador único de la solicitud Req=2. Para obtener el número de transacción y de posición, debe interceptar el mensaje TRADE_TRANSACTION_REQUEST con el mismo ID de solicitud en el manejador OnTradeTransaction, donde la estructura rellenada se recibirá como parámetro MqlTradeResult.
Desde el punto de vista del usuario, ambas solicitudes deben ser igual de rápidas.
Será posible comparar el rendimiento de estas dos funciones directamente en el código de un programa MQL utilizando otro ejemplo de Asesor Experto (véase la sección sobre solicitudes síncronas y asíncronas), que consideraremos después de estudiar el modelo de eventos de trading.
Cabe señalar que los eventos de trading se envían al manejador OnTradeTransaction (si está presente en el código), independientemente de la función que se utilice para enviar las solicitudes, OrderSend o OrderSendAsync. La situación es la siguiente: en caso de aplicar OrderSend, la totalidad o parte de la información sobre la ejecución de la orden está disponible inmediatamente en la estructura receptora MqlTradeResult. Sin embargo, en el caso general, el resultado se distribuye en el tiempo y el volumen; por ejemplo, cuando una orden se «rellena» en varias transacciones. A continuación, puede obtenerse información completa a partir de los eventos de trading o analizando el historial de transacciones y órdenes.
Si intenta enviar una solicitud deliberadamente incorrecta, por ejemplo, cambiar el tipo de orden a ORDER_TYPE_BUY_STOP pendiente, recibirá un mensaje de error, ya que para este tipo de órdenes debería utilizar la acción TRADE_ACTION_PENDING. Además, deben situarse a una distancia del precio actual (utilizamos el precio de mercado por defecto). Antes de esta prueba es importante no olvidar volver a cambiar el modo de consulta a síncrono (Async=false) para ver inmediatamente el error en la estructura MqlTradeResult tras finalizar la llamada a OrderSend. De lo contrario, OrderSendAsync devolvería true, pero el orden seguiría sin establecerse, y el programa sólo podría recibir información al respecto en OnTradeTransaction, que aún no tenemos.
OrderSend(request,result)=false / TRADE_SEND_FAILED(4756) TRADE_ACTION_DEAL, EURUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, @ 1.12452, ORDER_TIME_GTC REQUOTE, Bid=1.12449, Ask=1.12452, Requote, Req=5 |
En este caso, el error informa de un precio de recotización no válido.
En las secciones siguientes se presentarán ejemplos del uso de funciones para realizar acciones de trading específicas.