- 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
Modificar una orden pendiente
MetaTrader 5 permite modificar ciertas propiedades de una orden pendiente, incluyendo el precio de activación, los niveles de protección y la fecha de vencimiento. Las propiedades principales, como el tipo de orden o el volumen, no pueden modificarse. En tales casos, deberá eliminar la orden y sustituirla por otra. El único caso en el que el tipo de orden puede ser modificado por el propio servidor es la activación de una orden stop limitada, que se convierte en la orden Limit correspondiente.
La modificación programática de las órdenes se realiza mediante la operación TRADE_ACTION_MODIFY: es esta constante la que hay que escribir en el campo action de la estructura MqlTradeRequest antes de enviarlo al servidor mediante la función OrderSend o OrderSendAsync. El ticket de la orden modificada se indica en el campo order. Teniendo en cuenta action y order, la lista completa de campos obligatorios para esta operación incluye:
- action
- order
- price
- type_time (el valor por defecto 0 corresponde a ORDER_TIME_GTC)
- expiration (por defecto 0, no importante para ORDER_TIME_GTC)
- type_filling (por defecto 0 corresponde a ORDER_FILLING_FOK)
- stoplimit (sólo para órdenes de los tipos ORDER_TYPE_BUY_STOP_LIMIT y ORDER_TYPE_SELL_STOP_LIMIT)
Campos opcionales:
- sl
- tp
Si ya se han establecido niveles de protección para la orden, deben especificarse para que puedan guardarse. Los valores cero indican la supresión de Stop Loss y/o Take Profit.
En la estructura MqlTradeRequestSync (MqlTradeSync.mqh), la implementación de la modificación de la orden se sitúa en el método modify.
struct MqlTradeRequestSync: public MqlTradeRequest
|
La ejecución real de la solicitud se realiza de nuevo en el método completed, en la rama dedicada del operador if.
bool completed()
|
Para que la estructura MqlTradeResultSync conozca los nuevos valores de las propiedades de la orden editada y pueda compararlos con el resultado, los escribimos en campos libres (no son rellenados por el servidor en este tipo de solicitud). Además, en el método modified, la estructura resultante está a la espera de que se aplique la modificación.
struct MqlTradeResultSync: public MqlTradeResult
|
Aquí vemos cómo se leen las propiedades de orden utilizando la función OrderGetDouble y se compara con los valores especificados. Todo esto sucede según el procedimiento ya conocido, en un bucle dentro de la función wait, dentro de un tiempo de espera determinado de msc (1000 milisegundos por defecto).
Como ejemplo, vamos a utilizar el Asesor Experto PendingOrderModify.mq5, mientras heredamos algunos fragmentos de código de PendingOrderSend.mq5. En concreto, un conjunto de parámetros de entrada y la función PlaceOrder para crear una nueva orden. Se utiliza en el primer lanzamiento si no hay ninguna orden para la combinación dada del símbolo y número Magic, asegurando así que el Asesor Experto tiene algo que modificar.
Se necesitaba una nueva función para encontrar una orden adecuada: GetMyOrder. Es muy similar a la función GetMyPosition, que se utilizó en el ejemplo con seguimiento de posición (TrailingStop.mq5) para encontrar una posición adecuada. La finalidad de las funciones integradas en la API de MQL5 que se utilizan en GetMyOrder debería quedar clara a partir de sus nombres, y la descripción técnica se presentará en secciones independientes.
ulong GetMyOrder(const string name, const ulong magic)
|
Ahora falta el parámetro de entrada Distance2SLTP. En su lugar, el nuevo Asesor Experto calculará automáticamente el rango diario de precios y colocará niveles de protección a una distancia de la mitad de este rango. Al comienzo de cada día, se recalcularán el rango y los nuevos niveles de los campos sl y tp. Las solicitudes de modificación de órdenes se generarán en función de los nuevos valores.
Las órdenes pendientes que se activen y se conviertan en posiciones se cerrarán al alcanzar Stop Loss o Take Profit. El terminal puede informar al programa MQL sobre la activación de órdenes pendientes y el cierre de posiciones si usted describe en él manejadores de evento de trading. Esto permitiría, por ejemplo, evitar la creación de una nueva orden si existe una posición abierta. No obstante, también puede utilizarse la estrategia actual. Así pues, nos ocuparemos de los eventos más adelante.
La lógica principal del Asesor Experto se implementa en el manejador OnTick.
void OnTick()
|
Dos líneas al principio de la función garantizan que el algoritmo se ejecute una vez al principio de cada día. Para ello, calculamos la fecha actual sin hora y la comparamos con el valor de la variable lastDay que contiene la última fecha correcta. Por supuesto, el estado de éxito o error queda claro al final de la función, así que volveremos a ello más adelante.
A continuación, se calcula el rango de precios del día anterior.
const string symbol = StringLen(Symbol) == 0 ? _Symbol : Symbol;
|
Dependiendo de si existe o no una orden en la función GetMyOrder, crearemos una nueva orden a través de PlaceOrder o editaremos la existente utilizando ModifyOrder.
uint retcode = 0;
|
Ambas funciones, PlaceOrder y ModifyOrder, trabajan sobre la base de los parámetros de entrada del Asesor Experto y el rango de precios encontrado. Devuelven el estado de la solicitud, que habrá que analizar de alguna manera para decidir qué acción tomar:
- Actualizar la variable lastDay si la solicitud tiene éxito (la orden se ha actualizado y el Asesor Experto duerme hasta el comienzo del día siguiente).
- Dejar el día anterior en lastDay durante algún tiempo para volver a intentarlo en los próximos ticks si hay problemas temporales (por ejemplo, la sesión de trading aún no ha comenzado).
- Detener el Asesor Experto si se detectan problemas graves (por ejemplo, el tipo de orden seleccionada o la dirección de la operación no están permitidos en el símbolo).
...
|
En la sección Cierre de una posición: total y parcial hemos utilizado un análisis simplificado con la macro IS_TANGIBLE, que daba una respuesta en las categorías «sí» y «no» para indicar si había error o no. Evidentemente, este planteamiento debe mejorarse, y volveremos sobre este tema próximamente. Por ahora, nos centraremos en la funcionalidad principal del Asesor Experto.
El código fuente de la función PlaceOrder prácticamente no ha cambiado con respecto al ejemplo anterior. ModifyOrder se muestra a continuación.
Recordemos que determinamos la localización de las órdenes en función del rango diario, al que se aplicó la tabla de coeficientes. El principio no ha cambiado; sin embargo, dado que ahora tenemos dos funciones que trabajan con órdenes, PlaceOrder y ModifyOrder, la tabla Coefficients se sitúa en un contexto global. No lo repetiremos aquí y pasaremos directamente a la función ModifyOrder.
uint ModifyOrder(const ulong ticket, const double range,
|
Los niveles de precios se calculan en función del tipo de orden y del rango pasado.
const ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
|
Después de calcular todos los valores, creamos un objeto de la estructura MqlTradeRequestSync y ejecutamos la solicitud.
MqlTradeRequestSync request(symbol);
|
Para analizar retcode que tenemos que ejecutar en el bloque de llamada dentro de OnTick, se desarrolló un nuevo mecanismo que complementaba el archivo TradeRetcode.mqh. Todos los códigos de devolución del servidor se dividen en varios grupos de «gravedad», descritos por los elementos de la enumeración TRADE_RETCODE_SEVERITY.
enum TRADE_RETCODE_SEVERITY
|
De forma simplista, la primera mitad corresponde a errores recuperables: suele bastar con esperar un poco y reintentar la solicitud. La segunda mitad requiere que cambie el contenido de la solicitud, compruebe la configuración de la cuenta o del símbolo, los permisos para el programa y, en el peor de los casos, que deje de operar. Aquellos que lo deseen pueden dibujar una línea separadora condicional, no después de SEVERITY_REJECT, como se resalta visualmente ahora, sino antes.
La división de todos los códigos en grupos se realiza mediante la función TradeCodeSeverity (indicada con abreviaturas).
TRADE_RETCODE_SEVERITY TradeCodeSeverity(const uint retcode)
|
Gracias a esta funcionalidad, el manejador OnTick puede complementarse con un tratamiento de errores «inteligente». Una variable estática RetryFrequency almacena la frecuencia con la que el programa intentará repetir la solicitud en caso de errores no críticos. La última vez que se realizó un intento de este tipo se almacenó en la variable RetryRecordTime.
void OnTick()
|
Una vez que la función PlaceOrder o ModifyOrder devuelve el valor de retcode, nos enteramos de su gravedad y, en función de la misma, elegimos una de las tres alternativas: detener el Asesor Experto, esperar un tiempo u operar de forma regular (marcando la modificación exitosa de la orden por el día actual en lastDay).
const TRADE_RETCODE_SEVERITY severity = TradeCodeSeverity(retcode);
|
En caso de problemas repetidos clasificados como solucionables, el tiempo de espera de RetryFrequency aumenta gradualmente con cada error subsiguiente, pero se restablece a 1 segundo cuando la solicitud se procesa correctamente.
Cabe señalar que los métodos de la estructura aplicada MqlTradeRequestSync comprueban la corrección de un gran número de combinaciones de parámetros y, si se encuentran problemas, interrumpen el proceso antes de la llamada a SendRequest. Este comportamiento está activado por defecto, pero puede desactivarse definiendo una macro RETURN(X) vacía antes de la directiva #include con MqlTradeSync.mqh.
#define RETURN(X)
|
Con esta definición de macro, las comprobaciones no sólo imprimirán advertencias en el registro sino que seguirán ejecutando métodos hasta la llamada a SendRequest.
En cualquier caso, tras llamar a uno u otro método de la estructura MqlTradeResultSync, el código de error se añadirá a retcode. Esto lo hará el servidor o los algoritmos de comprobación de la estructura MqlTradeRequestSync (aquí utilizamos el hecho de que la instancia MqlTradeResultSync está incluida dentro de MqlTradeRequestSync). No se proporciona aquí la descripción de la devolución de códigos de error y el uso de la macro RETURN en los métodos MqlTradeRequestSync en aras de la brevedad. Los interesados pueden ver el código fuente completo en el archivo MqlTradeSync.mqh.
Vamos a ejecutar el Asesor Experto PendingOrderModify.mq5 en el probador, con el modo visual activado, utilizando los datos de XAUUSD, H1 (todos los ticks o el modo de ticks reales). Con la configuración por defecto, el Asesor Experto colocará órdenes del tipo ORDER_TYPE_BUY_STOP con un lote mínimo. Comprobemos en el registro y en el historial de operaciones que el programa coloca órdenes pendientes y las modifica al principio de cada día.
2022.01.03 01:05:00 Autodetected daily range: 14.37 2022.01.03 01:05:00 buy stop 0.01 XAUUSD at 1845.73 sl: 1838.55 tp: 1852.91 (1830.63 / 1831.36) 2022.01.03 01:05:00 OK order placed: #=2 2022.01.03 01:05:00 TRADE_ACTION_PENDING, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, » » @ 1845.73, SL=1838.55, TP=1852.91, ORDER_TIME_GTC, M=1234567890 2022.01.03 01:05:00 DONE, #=2, V=0.01, Bid=1830.63, Ask=1831.36, Request executed 2022.01.04 01:05:00 Autodetected daily range: 33.5 2022.01.04 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1836.56] 2022.01.04 01:05:00 OK order modified: #=2 2022.01.04 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, » » @ 1836.56, SL=1819.81, TP=1853.31, ORDER_TIME_GTC, #=2 2022.01.04 01:05:00 DONE, #=2, @ 1836.56, Bid=1819.81, Ask=1853.31, Request executed, Req=1 2022.01.05 01:05:00 Autodetected daily range: 18.23 2022.01.05 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1832.56] 2022.01.05 01:05:00 OK order modified: #=2 2022.01.05 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, » » @ 1832.56, SL=1823.45, TP=1841.67, ORDER_TIME_GTC, #=2 2022.01.05 01:05:00 DONE, #=2, @ 1832.56, Bid=1823.45, Ask=1841.67, Request executed, Req=2 ... 2022.01.11 01:05:00 Autodetected daily range: 11.96 2022.01.11 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1812.91] 2022.01.11 01:05:00 OK order modified: #=2 2022.01.11 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, » » @ 1812.91, SL=1806.93, TP=1818.89, ORDER_TIME_GTC, #=2 2022.01.11 01:05:00 DONE, #=2, @ 1812.91, Bid=1806.93, Ask=1818.89, Request executed, Req=6 2022.01.11 18:10:58 order [#2 buy stop 0.01 XAUUSD at 1812.91] triggered 2022.01.11 18:10:58 deal #2 buy 0.01 XAUUSD at 1812.91 done (based on order #2) 2022.01.11 18:10:58 deal performed [#2 buy 0.01 XAUUSD at 1812.91] 2022.01.11 18:10:58 order performed buy 0.01 at 1812.91 [#2 buy stop 0.01 XAUUSD at 1812.91] 2022.01.11 20:28:59 take profit triggered #2 buy 0.01 XAUUSD 1812.91 sl: 1806.93 tp: 1818.89 » » [#3 sell 0.01 XAUUSD at 1818.89] 2022.01.11 20:28:59 deal #3 sell 0.01 XAUUSD at 1818.91 done (based on order #3) 2022.01.11 20:28:59 deal performed [#3 sell 0.01 XAUUSD at 1818.91] 2022.01.11 20:28:59 order performed sell 0.01 at 1818.91 [#3 sell 0.01 XAUUSD at 1818.89] 2022.01.12 01:05:00 Autodetected daily range: 23.28 2022.01.12 01:05:00 buy stop 0.01 XAUUSD at 1843.77 sl: 1832.14 tp: 1855.40 (1820.14 / 1820.49) 2022.01.12 01:05:00 OK order placed: #=4 2022.01.12 01:05:00 TRADE_ACTION_PENDING, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, » » @ 1843.77, SL=1832.14, TP=1855.40, ORDER_TIME_GTC, M=1234567890 2022.01.12 01:05:00 DONE, #=4, V=0.01, Bid=1820.14, Ask=1820.49, Request executed, Req=7 |
La orden puede activarse en cualquier momento, tras lo cual la posición se cierra al cabo de un tiempo mediante stop loss o take profit (como en el código anterior).
En algunos casos, puede darse la situación de que la posición siga existiendo al comienzo del día siguiente, y entonces se creará una nueva orden además de ésta, como en la captura de pantalla siguiente:
El Asesor Experto con una estrategia de trading basada en órdenes pendientes en el probador
Tenga en cuenta que, debido al hecho de que solicitamos cotizaciones del marco temporal PERIOD_D1 para calcular el rango diario, el probador visual abre el gráfico correspondiente, además del gráfico de trabajo actual. Este servicio funciona no sólo para marcos temporales distintos del de trabajo, sino también para otros símbolos. Esto será útil, en particular, a la hora de desarrollar Asesores expertos multidivisa.
Para comprobar cómo funciona el tratamiento de errores, pruebe a desactivar el trading para el Asesor Experto. El registro contendrá lo siguiente:
Autodetected daily range: 34.48
|
Este error es crítico, y el Asesor Experto deja de funcionar.
Para demostrar uno de los errores más sencillos, podríamos utilizar el manejador OnTimer en lugar de OnTick. Entonces, lanzar el mismo Asesor Experto en símbolos en los que las sesiones de trading duran sólo una parte del día generaría periódicamente una secuencia de errores no críticos sobre un mercado cerrado («Market closed»). En este caso, el Asesor Experto seguiría intentando comenzar a operar, aumentando constantemente el tiempo de espera.
Esto, en particular, es fácil de comprobar en el probador, que permite establecer sesiones de trading arbitrarias para cualquier símbolo. En la pestaña Settings, a la derecha de la lista desplegable Delays, hay un botón que abre el cuadro de diálogo Trade setup. Allí deberá incluir la opción Use your settings y, en la pestaña Trade, añadir al menos un registro a la tabla Non-trading periods.
Establecer periodos no de trading en el probador
Tenga en cuenta que lo que se configura aquí son los periodos no de trading, no las sesiones de trading; es decir, este ajuste actúa exactamente al revés en comparación con la especificación del símbolo.
Muchos errores potenciales relacionados con las restricciones de trading pueden eliminarse mediante un análisis preliminar del entorno utilizando una clase como Permissions presentada en la sección Restricciones y permisos para las transacciones de la cuenta.