- 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
Cierre de posiciones opuestas: total y parcial (cobertura)
En las cuentas de cobertura se permite abrir varias posiciones al mismo tiempo y, en la mayoría de los casos, estas posiciones pueden ser en sentido opuesto. En algunas jurisdicciones, las cuentas de cobertura están restringidas: sólo se pueden tener posiciones en una dirección al mismo tiempo. En este caso, recibirá el código de error TRADE_RETCODE_HEDGE_PROHIBITED cuando intente ejecutar una operación de trading opuesta. Además, esta restricción a menudo se correlaciona con el ajuste de la propiedad de cuenta ACCOUNT_FIFO_CLOSE a true.
Cuando se abren dos posiciones opuestas al mismo tiempo, la plataforma admite el mecanismo de su cierre mutuo simultáneo mediante la operación TRADE_ACTION_CLOSE_BY. Para realizar esta acción, debe rellenar dos campos más en la estructura MqlTradeTransaction además del campo action: position y position_by deben contener los tickets de las posiciones que se van a cerrar.
La disponibilidad de esta característica depende de la propiedad SYMBOL_ORDER_MODE del instrumento financiero: SYMBOL_ORDER_CLOSEBY (64) debe estar presente en la máscara de bits de las banderas permitidas.
Esta operación no sólo simplifica el cierre (una operación en lugar de dos), sino que también ahorra un diferencial.
Como sabe, cualquier posición nueva comienza a operar con una pérdida igual al diferencial. Por ejemplo, cuando se compra un instrumento financiero, la transacción se concluye al precio Ask, pero para una transacción de salida, es decir, una venta, el precio real es Bid. Para una posición corta, la situación es inversa: inmediatamente después de entrar al precio Bid, empezamos a realizar un seguimiento del precio Ask para una posible salida.
Si cierra posiciones al mismo tiempo de forma regular, sus precios de salida estarán a una distancia del diferencial actual entre sí. Sin embargo, si utiliza la operación TRADE_ACTION_CLOSE_BY, ambas posiciones se cerrarán sin tener en cuenta los precios actuales. El precio al que se compensan las posiciones es igual al precio de apertura de la posición position_by (en la estructura de solicitudes). Se especifica en la orden ORDER_TYPE_CLOSE_BY generada por la solicitud TRADE_ACTION_CLOSE_BY.
Lamentablemente, en los informes en el contexto de transacciones y posiciones, los precios de cierre y apertura de posiciones/transacciones opuestas se muestran en pares de valores idénticos, en sentido espejo, lo que da la impresión de una doble ganancia o pérdida. De hecho, el resultado financiero de la operación (la diferencia entre los precios ajustados por el lote) sólo se registra para la operación de salida de la primera posición (el campo position de la estructura de la solicitud). El resultado de la segunda operación de salida es siempre 0, independientemente de la diferencia de precios.
Otra consecuencia de esta asimetría es que al cambiar los lugares de los tickets en los campos position y position_by, las estadísticas de pérdidas y ganancias en el contexto de operaciones largas y cortas cambian en el informe de trading; por ejemplo, las operaciones largas rentables pueden aumentar exactamente tanto como disminuye el número de operaciones cortas rentables. Pero esto, en teoría, no debería afectar al resultado global, si suponemos que el retraso en la ejecución de la orden no depende del orden de transferencia de los tickets.
En el siguiente diagrama se muestra una explicación gráfica del proceso (los diferenciales están intencionadamente exagerados).

Contabilidad de diferenciales al cerrar posiciones rentables
He aquí un caso de un par de posiciones rentables. Si las posiciones tenían direcciones opuestas y eran deficitarias, al cerrarlas por separado, el diferencial se tendría en cuenta dos veces (en cada una). El contracierre permite reducir la pérdida en un diferencial.

Contabilización del diferencial al cerrar posiciones no rentables
Las posiciones invertidas no tienen por qué ser del mismo tamaño. La operación de cierre opuesto funcionará en el mínimo de los dos volúmenes.
En el archivo MqlTradeSync.mqh, la operación del cierre opuesto se implementa utilizando el método closeby con dos parámetros para los tickets de posición.
struct MqlTradeRequestSync: public MqlTradeRequest
|
Para controlar el resultado del cierre, almacenamos el ticket de una posición menor en la variable result.position. Todo en el método completed y en la estructura MqlTradeResultSync está listo para el seguimiento síncrono del cierre de posición: el mismo algoritmo funcionó para el cierre normal de una posición.
struct MqlTradeRequestSync: public MqlTradeRequest
|
Las posiciones opuestas suelen utilizarse como sustituto de una orden de stop o un intento de obtener beneficios en una corrección a corto plazo mientras se permanece en el mercado y se sigue la tendencia principal. La opción de utilizar una orden de pseudo-stop le permite posponer la decisión de cerrar realmente las posiciones durante algún tiempo, continuando el análisis de los movimientos del mercado a la espera de que el precio se invierta en la dirección correcta. Sin embargo, hay que tener en cuenta que las posiciones «bloqueadas» requieren mayores depósitos y están sujetas a swaps. Por eso es difícil imaginar una estrategia de trading basada en posiciones opuestas en su forma pura, que pueda servir de ejemplo para esta sección.
Vamos a desarrollar la idea de la estrategia basada en barras precio-acción esbozada en el ejemplo anterior. El nuevo Asesor Experto es TradeCloseBy.mq5.
Utilizaremos la señal anterior para entrar en el mercado al detectar dos velas consecutivas que cerraron en la misma dirección. Una función responsable de su formación es de nuevo GetTradeDirection. No obstante, vamos a permitir reingresos si la tendencia continúa. El número máximo total de posiciones permitidas se fijará en la variable de entrada PositionLimit; el valor por defecto es 5.
La función GetMyPositions sufrirá algunos cambios: tendrá dos parámetros, que serán referencias a arrays que aceptan tickets de posición: compra y venta por separado.
#define PUSH(A,V) (A[ArrayResize(A, ArraySize(A) + 1, ArraySize(A) * 2) - 1] = V)
|
La función devuelve el tamaño del array más pequeño de los dos. Cuando es mayor que cero, tenemos la oportunidad de cerrar posiciones opuestas.
Si el array mínimo es de tamaño cero, la función devolverá el tamaño de otro array, pero con un signo menos, para que el código que llama sepa que todas las posiciones están en la misma dirección.
Si no hay posiciones en ninguna dirección, la función devolverá 0.
Las posiciones de apertura seguirán bajo el control de la función OpenPosition; aquí no hay cambios.
El cierre sólo se realizará en el modo de dos posiciones opuestas en la nueva función CloseByPosition. En otras palabras: este Asesor Experto no es capaz de cerrar posiciones de una en una, de la forma habitual. Por supuesto, en un robot real es improbable que se produzca un principio así, pero como ejemplo de un cierre que se aproxima, encaja muy bien. Si necesitamos cerrar una sola posición, basta con abrir una posición opuesta para ella (en este momento la ganancia o pérdida flotante es fija) y llamar a CloseByPosition para dos.
bool CloseByPosition(const ulong ticket1, const ulong ticket2)
|
El código utiliza el método request.closeby descrito anteriormente. Se rellenan los campos position y position_by y se llama a OrderSend.
La lógica de trading se describe en el manejador OnTick que analiza la configuración de precios sólo en el momento de la formación de una nueva barra y recibe una señal de la función GetTradeDirection.
void OnTick()
|
A continuación, rellenamos los arrays ticketsLong y ticketsShort con los tickets de posición del símbolo de trabajo y con el número Magic dado. Si la función GetMyPositions devuelve un valor mayor que cero, da el número de pares formados de posiciones opuestas. Pueden cerrarse en bucle mediante la función CloseByPosition. La combinación de pares en este caso se elige aleatoriamente (por orden de posiciones en el entorno del terminal); sin embargo, en la práctica, puede ser importante seleccionar los pares por volumen o de una forma tal que se cierren primero los más rentables.
ulong ticketsLong[], ticketsShort[];
|
Para cualquier otro valor de n, debe comprobar si existe una señal (posiblemente repetida) para entrar en el mercado y ejecutarla llamando a OpenPosition.
else if(type == ORDER_TYPE_BUY || type == ORDER_TYPE_SELL)
|
Por último, si todavía hay posiciones abiertas, pero están en la misma dirección, comprobamos si su número ha alcanzado el límite, en cuyo caso formamos una posición opuesta para «colapsar» dos de ellas en la siguiente barra (cerrando así una de cualquier posición de las antiguas).
else if(n < 0)
|
Vamos a ejecutar el Asesor Experto en el probador en XAUUSD, H1 desde principios de 2022, con la configuración predeterminada. A continuación se muestra el gráfico con las posiciones en el proceso del programa, así como la curva de balance.

Resultados de la prueba TradeCloseBy en XAUUSD, H1
Es fácil encontrar en el registro los momentos en los que finaliza una tendencia (compra con tickets del nº 2 al nº 4), y comienzan a generarse transacciones en sentido opuesto (venta nº 5), tras lo cual se activa un cierre de contador.
2022.01.03 01:05:00 instant buy 0.01 XAUUSD at 1831.13 (1830.63 / 1831.13 / 1830.63) 2022.01.03 01:05:00 deal #2 buy 0.01 XAUUSD at 1831.13 done (based on order #2) 2022.01.03 01:05:00 deal performed [#2 buy 0.01 XAUUSD at 1831.13] 2022.01.03 01:05:00 order performed buy 0.01 at 1831.13 [#2 buy 0.01 XAUUSD at 1831.13] 2022.01.03 01:05:00 Waiting for position for deal D=2 2022.01.03 01:05:00 OK New Order/Deal/Position 2022.01.03 02:00:00 instant buy 0.01 XAUUSD at 1828.77 (1828.47 / 1828.77 / 1828.47) 2022.01.03 02:00:00 deal #3 buy 0.01 XAUUSD at 1828.77 done (based on order #3) 2022.01.03 02:00:00 deal performed [#3 buy 0.01 XAUUSD at 1828.77] 2022.01.03 02:00:00 order performed buy 0.01 at 1828.77 [#3 buy 0.01 XAUUSD at 1828.77] 2022.01.03 02:00:00 Waiting for position for deal D=3 2022.01.03 02:00:00 OK New Order/Deal/Position 2022.01.03 03:00:00 instant buy 0.01 XAUUSD at 1830.40 (1830.16 / 1830.40 / 1830.16) 2022.01.03 03:00:00 deal #4 buy 0.01 XAUUSD at 1830.40 done (based on order #4) 2022.01.03 03:00:00 deal performed [#4 buy 0.01 XAUUSD at 1830.40] 2022.01.03 03:00:00 order performed buy 0.01 at 1830.40 [#4 buy 0.01 XAUUSD at 1830.40] 2022.01.03 03:00:00 Waiting for position for deal D=4 2022.01.03 03:00:00 OK New Order/Deal/Position 2022.01.03 05:00:00 instant sell 0.01 XAUUSD at 1826.22 (1826.22 / 1826.45 / 1826.22) 2022.01.03 05:00:00 deal #5 sell 0.01 XAUUSD at 1826.22 done (based on order #5) 2022.01.03 05:00:00 deal performed [#5 sell 0.01 XAUUSD at 1826.22] 2022.01.03 05:00:00 order performed sell 0.01 at 1826.22 [#5 sell 0.01 XAUUSD at 1826.22] 2022.01.03 05:00:00 Waiting for position for deal D=5 2022.01.03 05:00:00 OK New Order/Deal/Position 2022.01.03 06:00:00 close position #5 sell 0.01 XAUUSD by position #2 buy 0.01 XAUUSD (1825.64 / 1825.86 / 1825.64) 2022.01.03 06:00:00 deal #6 buy 0.01 XAUUSD at 1831.13 done (based on order #6) 2022.01.03 06:00:00 deal #7 sell 0.01 XAUUSD at 1826.22 done (based on order #6) 2022.01.03 06:00:00 Positions collapse initiated 2022.01.03 06:00:00 OK CloseBy Order/Deal/Position |
La transacción nº 3 es un artefacto interesante. Un lector atento observará que abrió por debajo de la anterior, violando aparentemente nuestra estrategia. De hecho, aquí no hay ningún error, y esto es consecuencia de que las condiciones de las señales están escritas de la forma más sencilla posible: sólo en función de los precios de cierre de las barras. Por lo tanto, una vela de reversión bajista (D), que abrió con un precio más alto y cerró por encima del final de la vela alcista anterior (C), generó una señal de compra. Esta situación se ilustra en la siguiente captura de pantalla:

Transacciones con tendencia al alza a precios de cierre
Todas las velas de la secuencia A, B, C, D y E cierran por encima de la anterior y animan a seguir comprando. Para excluir tales artefactos, habría que analizar además la dirección de las propias barras.
Lo último a lo que hay que prestar atención en este ejemplo es la función OnInit. Dado que el Asesor Experto utiliza la operación TRADE_ACTION_CLOSE_BY, aquí se comprueba la configuración de la cuenta y del símbolo de trabajo correspondientes.
int OnInit()
|
Si una de las propiedades no admite cierre cruzado, el Asesor Experto no podrá seguir funcionando. Al crear robots de trabajo, estas comprobaciones, por regla general, se llevan a cabo dentro del algoritmo de trading y cambian el programa a modos alternativos; en concreto, a un único cierre de posiciones y al mantenimiento de una posición agregada en caso de compensación.