English Русский 中文 Deutsch 日本語
preview
Operar con noticias de manera sencilla (Parte 5): Ejecución de operaciones (II)

Operar con noticias de manera sencilla (Parte 5): Ejecución de operaciones (II)

MetaTrader 5Sistemas comerciales |
170 6
Kabelo Frans Mampa
Kabelo Frans Mampa

Introducción

En este artículo, nuestro objetivo principal es escribir el código para implementar órdenes stop en el experto en trading de noticias. Estas órdenes stop se utilizarán en un artículo posterior para operar con eventos de noticias. Además, crearemos funciones para gestionar el deslizamiento de órdenes stop, cerrar operaciones y realizar comprobaciones de validación para indicar si se puede abrir una operación o una orden. La gestión de operaciones es crucial en cualquier sistema de trading algorítmico, ya que implica tareas como abrir y cerrar operaciones, ajustar los stop-loss y gestionar las tomas de beneficios. Una gestión comercial eficiente puede ayudar a un comerciante a obtener más ganancias y minimizar la exposición a movimientos adversos del mercado.


¿Por qué utilizar órdenes de stop?


Orden limitada vs orden stop

El uso de órdenes de stop al negociar eventos noticiosos es una estrategia común porque ayuda a los operadores a capitalizar los fuertes movimientos de precios que suelen seguir a las publicaciones económicas importantes, al tiempo que minimiza ciertos riesgos.

Capturando movimientos de ruptura

Los acontecimientos noticiosos, como los informes económicos o los anuncios de los bancos centrales, a menudo provocan movimientos de precios repentinos y significativos. Estas son conocidas como rupturas. Una orden de stop permite a un operador ingresar al mercado a un nivel de precio predefinido cuando el precio supera un cierto umbral, lo que es ideal para capturar estos movimientos de ruptura sin tener que monitorear manualmente el mercado.

  • Orden de compra stop: se coloca por encima del precio actual del mercado. Activa una orden de compra cuando el precio sube, capturando el impulso ascendente después de noticias positivas.
  • Orden de stop de venta: se coloca por debajo del precio de mercado actual. Activa una orden de venta cuando el precio baja, aprovechando las noticias negativas.

Puede ayudar a evitar fluctuaciones del mercado

La volatilidad relacionada con las noticias puede provocar fluctuaciones de precios antes de que se establezca una dirección clara. Los traders que entran en posiciones demasiado pronto pueden quedar atrapados en movimientos rápidos en ambas direcciones que pueden alcanzar los stop-loss. Las órdenes de stop ayudan a evitar estos casos falsos al garantizar que la operación solo se active cuando el mercado se comprometa con una dirección más allá de un punto de precio específico.

Cómo limitar el sobreanálisis en el trading

Operar en torno a eventos noticiosos puede ser estresante debido a los rápidos movimientos del mercado. Establecer órdenes de stop con antelación permite a los operadores entrar y salir de posiciones sin interferencia emocional o la necesidad de la dirección del evento. Una vez colocadas las órdenes, la decisión del comerciante ya está tomada, eliminando la tentación de reaccionar exageradamente a las fluctuaciones de precios a corto plazo. En el contexto de este artículo, esto significa que los discursos y otros eventos noticiosos que no tienen un impacto en el evento pueden negociarse con el uso de órdenes de stop, ya que la dirección es difícil o imposible de predecir de antemano.

Minimizar el deslizamiento

Al utilizar órdenes de stop, los operadores pueden evitar el deslizamiento asociado con las órdenes de mercado colocadas durante períodos de alta volatilidad. Si bien aún pueden producirse deslizamientos, una orden de stop tiene más posibilidades de ejecutarse cerca del nivel previsto que una orden de mercado colocada después de que la noticia ya haya provocado un cambio de precio significativo.

Gestión de riesgos

Las órdenes de stop ayudan a automatizar la gestión de riesgos. Por ejemplo, si un comerciante anticipa una reacción volátil a un evento noticioso, puede colocar una orden de compra stop y una orden de venta stop (estrategia straddle). Este enfoque ayuda al comerciante a ingresar al mercado en cualquier dirección, dependiendo de cómo se rompa el precio. Una vez activada, la orden de stop opuesta se puede cancelar para gestionar el riesgo.

Escenario:

Estás operando en EE. UU. dólar en torno al anuncio de las nóminas no agrícolas (Non-Farm Payrolls, NFP), uno de los eventos noticiosos que más mueven el mercado. Esperando que los datos provoquen un movimiento importante en el par EURUSD, usted coloca una orden de compra stop por encima del precio actual y una orden de venta stop por debajo del precio actual (estrategia straddle).

Instantánea antes del NFP

  • Datos positivos del NFP: el dólar se fortalece, lo que provoca la caída del EURUSD y activa su orden de venta stop.
  • Datos negativos del NFP: el dólar se debilita, lo que provoca que el EURUSD suba y active su orden de compra stop.

Instantánea después del NFP

Al utilizar órdenes de stop, usted está preparado para el movimiento en cualquier dirección e ingresa al mercado automáticamente una vez que la acción del precio confirma la dirección de ruptura.


Clase de propiedades de cuenta

La clase CAccountProperties que hereda de la clase CAccountInfo en MQL5. El propósito de esta clase es ampliar CAccountInfo con un método personalizado para recuperar el número total de tipos específicos de órdenes en la cuenta comercial, como órdenes de compra o venta limitadas/detenidas. Esta función adicional se utilizará en la clase de gestión comercial para ayudar a garantizar que el experto se mantenga dentro de las órdenes limitadas de la cuenta.

Por ejemplo: si el límite de órdenes de la cuenta es de 200, esto significa que la cuenta del usuario no puede exceder las 200 órdenes, esto incluye órdenes abiertas y pendientes. Entonces, si queremos abrir una orden de compra-stop y una orden de venta-stop, pero la cuenta del usuario tiene 199 órdenes, el experto identificará que no quedan suficientes órdenes para abrir ambas, una orden de compra-stop y una orden de venta-stop, por lo que en este escenario no se agregarán órdenes adicionales a la cuenta del usuario.

Cabecera e inclusiones:

El código siguiente incluye el archivo AccountInfo.mqh de la biblioteca estándar MQL5. Este archivo contiene funciones y estructuras predefinidas para acceder a la información de la cuenta comercial.

#include <Trade/AccountInfo.mqh>

Definición de clase CAccountProperties:

Herencia: La clase CAccountProperties hereda públicamente de CAccountInfo. Al heredar, CAccountProperties obtiene acceso a las funciones y miembros de datos de CAccountInfo.

CAccountInfo proporciona datos esenciales de la cuenta, como saldo, capital, margen libre, etc.

Propósito de la clase: La clase CAccountProperties agrega un método (numOrders) que cuenta tipos específicos de pedidos.

class CAccountProperties: public CAccountInfo

Función numOrders:

La función numOrders() es donde se lleva a cabo la mayor parte de la acción. Esta función cuenta la cantidad de tipos de órdenes específicas (órdenes limitadas y de stop) que están abiertas actualmente en la cuenta comercial.

  • Tipo de retorno: La función devuelve un entero.
  • Inicialización: La variable num se inicializa a 0. Esto almacenará el recuento de pedidos que coinciden con los tipos deseados.

int CAccountProperties::numOrders(void)
{
   int num=0;

Iteración de orden:

  • OrdersTotal() es una función MQL5 que devuelve el número total de pedidos.
  • Se utiliza un bucle para iterar a través de todos los pedidos, desde el índice 0 hasta OrdersTotal() - 1.

for(int i=0; i<OrdersTotal(); i++)

Validación del pedido:

  • OrderGetTicket(i) es una función MQL5 que recupera el número de ticket del pedido en el índice 'i'. Cada pedido tiene un número de ticket único.
  • Si el número de ticket es mayor a 0, significa que el pedido es válido y el código procede a verificar su tipo.

if(OrderGetTicket(i)>0)

Comprobación del tipo de pedido:

  • OrderGetInteger(ORDER_TYPE) recupera el tipo del pedido en el índice 'i'. Este valor corresponde a un int que representa varios tipos de órdenes (buy limit, sell stop, etc.).
  • La declaración switch verifica el tipo de cada orden y lo compara con las constantes de tipo de orden predefinidas.
switch(int(OrderGetInteger(ORDER_TYPE)))

Manejo de tipos de pedidos específicos:

  • Tipos de pedidos:
    • ORDER_TYPE_BUY_LIMIT: Una orden de compra limitada.
    • ORDER_TYPE_BUY_STOP: Una orden de compra stop.
    • ORDER_TYPE_BUY_STOP_LIMIT: Una orden de límite de compra.
    • ORDER_TYPE_SELL_LIMIT: Una orden de venta limitada.
    • ORDER_TYPE_SELL_STOP: Una orden de venta stop.
    • ORDER_TYPE_SELL_STOP_LIMIT: Una orden de límite de venta.

Para cada tipo de orden reconocido, el contador numérico se incrementa. Si la orden es de cualquier otro tipo, se activa el caso predeterminado y no se realiza ninguna acción.

case ORDER_TYPE_BUY_LIMIT:
    num++;
    break;
case ORDER_TYPE_BUY_STOP:
    num++;
    break;
case ORDER_TYPE_BUY_STOP_LIMIT:
    num++;
    break;
case ORDER_TYPE_SELL_LIMIT:
    num++;
    break;
case ORDER_TYPE_SELL_STOP:
    num++;
    break;
case ORDER_TYPE_SELL_STOP_LIMIT:
    num++;
    break;
default:
    break;

Devolviendo el recuento:

  • Después de iterar a través de todos los pedidos y contar los válidos, la función devuelve el recuento total almacenado en num.

   return num;
}



Clase de Gestión de Riesgos

Esta clase garantiza que la exposición del operador al riesgo de mercado esté controlada, limitando las posibles pérdidas y permitiendo ganancias. Sin embargo, se han realizado pequeñas actualizaciones a esta clase. El código a continuación define una enumeración OrderTypeSelection que se utiliza para representar diferentes tipos de clasificaciones de órdenes. El usuario podrá elegir con cuál de estos tipos de órdenes preferiría operar.
//-- Enumeration for Order type
enum OrderTypeSelection
  {
   MarketPositionType,//MARKET POSITION
   StopOrdersType,//STOP ORDERS
   StopOrderType,//SINGLE STOP ORDER
  } myOrderSelection;

Enumeración: OrderTypeSelection

La enumeración OrderTypeSelection consta de tres valores diferentes:

Tipo de posición de mercado:

  • Representa una orden de posición de mercado.
  • Este tipo se abre en función del precio actual del mercado y se ejecuta inmediatamente al precio actual.
  • Requiere impacto de evento.

StopOrdersType:

  • Representa órdenes de stop.
  • Las órdenes de stop son órdenes condicionales que se ejecutan solo cuando el precio alcanza un determinado nivel. Se abren órdenes tanto de compra como de venta.
  • No requiere impacto de evento.

StopOrderType:

  • Representa una orden de stop única.
  • Se abre una orden individual de compra-stop o de venta-stop.
  • Requiere impacto de evento.

Declaración de variable: myOrderSelection

  • myOrderSelection es una variable que almacenará el tipo de orden actual seleccionado por el comerciante.

Normalización del tamaño del lote

En la función a continuación, el límite de volumen cambiará dependiendo de OrderTypeSelection, cuando myOrderSelection es StopOrdersType(STOP ORDERS) el límite de volumen se reduce a la mitad para acomodar tanto la orden de compra-stop como la orden de venta-stop ya que ambas deben abrirse al mismo tiempo, mientras que MarketPositionType(MARKET POSITION) y StopOrderType(SINGLE STOP ORDER) solo abren órdenes individuales.

void CRiskManagement::NormalizeLotsize(double &Lotsize)
{
    // Adjust lot size to match symbol's step size or minimum size
    if (Lotsize <= 0.0) return;
    double VolumeLimit = (myOrderSelection != StopOrdersType) ? CSymbol.LotsLimit() : CSymbol.LotsLimit() / 2;
    // Check and adjust if volume exceeds limits
    // ...
}



Clase de sesiones

El código a continuación define una clase CSessions, que es responsable de administrar y rastrear los tiempos de sesión comercial para un símbolo comercial en particular. Utiliza métodos de una clase base CTimeManagement (incluida a través de Timemanagement.mqh) para determinar si una sesión comercial ha comenzado, finalizado y para obtener la hora de finalización de la sesión. Para ponerlo en contexto, necesitamos saber cuándo terminará la sesión de negociación para cerrar las operaciones de antemano y establecer las fechas de vencimiento de las órdenes, esto es para evitar la negociación nocturna.

¿Por qué evitar las operaciones nocturnas?

Comercio nocturno

Mayor riesgo de mercado debido a la volatilidad

Los mercados pueden ser muy volátiles durante la noche, especialmente en respuesta a eventos económicos, desarrollos geopolíticos o noticias de otros mercados globales. Dado que pueden ocurrir eventos importantes fuera del horario comercial normal, las posiciones mantenidas durante la noche están expuestas a movimientos de precios impredecibles. Estos movimientos pueden causar:

  • Brechas significativas en el precio cuando el mercado abre al día siguiente, lo que podría resultar en ganancias o pérdidas sustanciales.
  • Capacidad limitada para responder a cambios rápidos, ya que la liquidez puede ser baja y su capacidad para modificar o salir de posiciones rápidamente puede verse limitada.

Liquidez limitada

La liquidez tiende a ser menor durante las sesiones de negociación nocturna porque hay menos participantes activos en el mercado. Esto puede conducir a:

  • Spreads más amplios entre los precios de oferta y demanda, lo que aumenta los costos de transacción.
  • Deslizamiento, donde las operaciones se ejecutan a un precio diferente al esperado debido a la falta de profundidad del mercado.
  • Dificultad para cerrar posiciones grandes sin mover el mercado, lo que podría causar una ejecución de precios desfavorable.

Mayor riesgo de brechas (gaps)

Las operaciones nocturnas pueden exponer a los operadores a brechas de precios, donde el precio al abrir el mercado es significativamente diferente del precio de cierre del día anterior. Pueden producirse lagunas debido a comunicados de prensa u otros eventos, y pueden dar como resultado:

  • Pérdidas mayores a las esperadas, especialmente si el precio se mueve en contra de su posición.
  • Las órdenes de stop-loss son ineficaces, ya que los gaps pueden saltarse los niveles de stop predeterminados, ejecutando su orden a un precio mucho peor del esperado.

Cargos por intereses (tarifas de swap)

En el caso de ciertos instrumentos como Forex, mantener posiciones durante la noche puede generar comisiones por swap o cargos por intereses, dependiendo de la diferencia de tasas de interés entre las monedas involucradas. Estos costos pueden acumularse con el tiempo y reducir la rentabilidad. Las tarifas de swap pueden:

  • Convierta operaciones rentables en perdedoras, especialmente si las mantiene durante períodos prolongados.
  • Varían según las condiciones del mercado, lo que hace que sea más difícil predecirlas y planificarlas.

Control reducido sobre la ejecución de operaciones

Durante la noche, especialmente en mercados con horarios limitados o irregulares, la ejecución de operaciones puede ser más lenta o menos precisa debido a:

  • Horario de atención del bróker: Es posible que algunos brókers no admitan determinadas acciones o modificaciones comerciales fuera de su horario de atención habitual, lo que reduce su capacidad para responder a las condiciones del mercado.

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include "Timemanagement.mqh"
//+------------------------------------------------------------------+
//|Sessions Class                                                    |
//+------------------------------------------------------------------+
class CSessions:CTimeManagement
  {
public:
                     CSessions(void) {}
                    ~CSessions(void) {}
   //--- Check if trading Session has began
   bool              isSessionStart(int offsethour=0,int offsetmin=0);
   //--- Check if trading Session has ended
   bool              isSessionEnd(int offsethour=0,int offsetmin=45);
   //--- Get Session End datetime
   datetime          SessionEnd(int offsethour=0,int offsetmin=45);
  };
//+------------------------------------------------------------------+
//|Check if trading Session has started                              |
//+------------------------------------------------------------------+
bool CSessions::isSessionStart(int offsethour=0,int offsetmin=0)
  {
//--- Declarations
   datetime datefrom,dateto,DateFrom[],DateTo[];

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateFrom Array size
         ArrayResize(DateFrom,int(ArraySize(DateFrom))+1,int(ArraySize(DateFrom))+2);
         //--- Assign the last array index datefrom value
         DateFrom[int(ArraySize(DateFrom))-1] = datefrom;
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateFrom.Size()>0)
     {
      /* Adjust DateFrom index zero date as the first index date will be the earliest date
       from the whole array, we add the offset to this date only*/
      DateFrom[0] = TimePlusOffset(DateFrom[0],MinutesS(offsetmin));
      DateFrom[0] = TimePlusOffset(DateFrom[0],HoursS(offsethour));
      //--- Iterate through the whole array
      for(uint i=0; i<DateFrom.Size(); i++)
        {
         //--- Check if the current time is within the trading session
         if(TimeIsInRange(Time(Today(ReturnHour(DateFrom[i]),ReturnMinute(DateFrom[i])))
                          ,Time(Today(ReturnHour(DateTo[i]),ReturnMinute(DateTo[i])))))
           {
            return true;
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return true;
     }
   return false;
  }

//+------------------------------------------------------------------+
//|Check if trading Session has ended                                |
//+------------------------------------------------------------------+
bool CSessions::isSessionEnd(int offsethour=0,int offsetmin=45)
  {
//--- Declarations
   datetime datefrom,dateto,DateTo[],lastdate=0,sessionend;

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateTo.Size()>0)
     {
      //--- Assign lastdate a default value
      lastdate = DateTo[0];
      //--- Iterate through the whole array
      for(uint i=0; i<DateTo.Size(); i++)
        {
         //--- Check for the latest date in the array
         if(DateTo[i]>lastdate)
           {
            lastdate = DateTo[i];
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return false;
     }
   /* get the current time and modify the hour and minute time to the lastdate variable
   and assign the new datetime to sessionend variable*/
   sessionend =  Time(Today(ReturnHour(lastdate),ReturnMinute(lastdate)));
//--- Re-adjust the sessionend dates with the minute and hour offsets
   sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin));
   sessionend = TimeMinusOffset(sessionend,HoursS(offsethour));

//--- Check if sessionend date is more than the current time
   if(TimeTradeServer()<sessionend)
     {
      return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//|Get Session End datetime                                          |
//+------------------------------------------------------------------+
datetime CSessions::SessionEnd(int offsethour=0,int offsetmin=45)
  {
//--- Declarations
   datetime datefrom,dateto,DateTo[],lastdate=0,sessionend;

//--- Find all session times
   for(int i=0; i<10; i++)
     {
      //--- Get the session dates for the current symbol and Day of week
      if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto))
        {
         //--- Check if the end date's hour is at midnight
         if(ReturnHour(dateto)==00||ReturnHour(dateto)==24)
           {
            //--- Adjust the date to one minute before midnight
            dateto = Time(TimeTradeServer(),23,59);
           }
         //--- Re-adjust DateTo Array size
         ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2);
         //--- Assign the last array index dateto value
         DateTo[int(ArraySize(DateTo))-1] = dateto;
        }
     }

//--- Check if there are session times
   if(DateTo.Size()>0)
     {
      //--- Assign lastdate a default value
      lastdate = DateTo[0];
      //--- Iterate through the whole array
      for(uint i=0; i<DateTo.Size(); i++)
        {
         //--- Check for the latest date in the array
         if(DateTo[i]>lastdate)
           {
            lastdate = DateTo[i];
           }
        }
     }
   else
     {
      //--- If there are no trading session times
      return 0;
     }
   /* get the current time and modify the hour and minute time to the lastdate variable
   and assign the new datetime to sessionend variable*/
   sessionend = Time(Today(ReturnHour(lastdate),ReturnMinute(lastdate)));
//--- Re-adjust the sessionend dates with the minute and hour offsets
   sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin));
   sessionend = TimeMinusOffset(sessionend,HoursS(offsethour));
//--- return sessionend date
   return sessionend;
  }
//+------------------------------------------------------------------+

Explicación de los componentes clave

Definición de clase: CSessions

  • Herencia:
    • La clase CSessions hereda de CTimeManagement, por lo que tiene acceso a cualquier método y atributo relacionado con la gestión del tiempo definido en esa clase base.
  • Métodos:
    • isSessionStart(int offsethour=0, int offsetmin=0):
      • Determina si una sesión comercial ha comenzado comparando la hora actual del servidor con las horas de inicio de la sesión. También permite desplazamientos horarios opcionales.
    • isSessionEnd(int offsethour=0, int offsetmin=45):
      • Determina si una sesión comercial ha finalizado, comparando la hora actual del servidor con las horas de finalización de la sesión. Al igual que isSessionStart, toma desplazamientos opcionales (45 minutos predeterminados).
    • SessionEnd(int offsethour=0, int offsetmin=45):
      • Devuelve el valor de fecha y hora del final de la sesión comercial actual, ajustándose a las compensaciones proporcionadas (valor predeterminado: 45 minutos).

Detalles del método

isSessionStart(int offsethour=0, int offsetmin=0)

Este método verifica si la sesión comercial ha comenzado según la hora del servidor y el cronograma de la sesión del símbolo comercial.

Variables:

  • datefrom, dateto: Se utilizan para almacenar las horas de inicio y finalización de la sesión para cada día de negociación.
  • DateFrom[], DateTo[]: Matrices para almacenar las horas de inicio y finalización de la sesión después de los ajustes.

Lógica:

  • Recuperación de sesión:
    • La función SymbolInfoSessionTrade recupera las horas de inicio (datefrom) y finalización (dateto) de la sesión comercial para el símbolo actual y el día de la semana (recuperado por DayOfWeek(TimeTradeServer())).
    • Si la hora de finalización de la sesión (dateto) es a medianoche (00:00 o 24:00), la hora se ajusta a un minuto antes de la medianoche (23:59).
  • Manipulación de matrices:
    • Las horas de inicio de la sesión (datefrom) se almacenan en la matriz DateFrom[] y las horas de finalización (dateto) se almacenan en la matriz DateTo[].
  • Solicitud de compensación:
    • El método ajusta la hora de inicio de la primera sesión según los valores offsethour y offsetmin dados utilizando las funciones auxiliares MinutesS() y HoursS().
  • Comprobar la hora actual:
    • Luego, el método itera a través de los tiempos de sesión en DateFrom[] y DateTo[] y verifica si la hora actual del servidor (TimeTradeServer()) cae dentro de alguno de los intervalos de sesión.
    • Si se encuentra una sesión válida, devuelve verdadero, lo que indica que la sesión ha comenzado. De lo contrario, devuelve falso.

isSessionEnd(int offsethour=0, int offsetmin=45)

Este método verifica si la sesión comercial ha finalizado.

Variables:

  • Similar a isSessionStart(), pero con foco en las horas de finalización (dateto).
  • lastdate: almacena la última hora de finalización de la sesión para comparar.
  • sessionend: contiene la hora de finalización de la sesión calculada final con las compensaciones aplicadas.

Lógica:

  • Recuperación de sesión:
    • Recupera los tiempos de sesión (datefrom, dateto) como en isSessionStart().
    • Comprueba si hay horas de sesión válidas en DateTo[] e identifica la última hora de finalización de la sesión (lastdate).
  • Solicitud de compensación:
    • Ajusta la hora de finalización de la sesión aplicando los desplazamientos (offsethour y offsetmin).
  • Comparar con la hora del servidor:
    • Si la hora actual es anterior a la hora de finalización de la sesión ajustada, devuelve falso, lo que indica que la sesión aún está en curso. Si la sesión ha finalizado (la hora actual es posterior al final de la sesión), devuelve verdadero.

SessionEnd(int offsethour=0, int offsetmin=45)

Este método devuelve el valor de fecha y hora del final de la sesión actual, aplicando las compensaciones de tiempo proporcionadas.

Lógica:

  • Similar a isSessionEnd(), recupera los tiempos de sesión, aplica compensaciones y devuelve el tiempo final de finalización de la sesión ajustado (sessionend).
Resumen de las desventajas del trading nocturno:
  • Mayor volatilidad y movimientos impredecibles del mercado.
  • Baja liquidez, lo que genera diferenciales más amplios y costos más elevados.
  • Riesgo de brechas de precios que podrían dar lugar a grandes pérdidas.
  • Exposición a eventos globales y sus impactos en el mercado.
  • Cargos por intereses o tarifas de swap en los mercados Forex.
  • Control reducido sobre la ejecución comercial y la toma de decisiones.


Clase de Gestión Comercial

La clase CTradeManagement extiende CRiskManagement y contiene funcionalidad para gestionar operaciones, como colocar órdenes de compra/venta, manejar niveles de stop-loss y take-profit, y gestionar órdenes de stop.

Diagrama de flujo para abrir una orden de compra o venta:

+---------------------------+
| Receive Trade Signal      |
+---------------------------+
              |
              V
+---------------------------+
| Check for Available Margin|
+---------------------------+
              |
              V
+---------------------------+
| Place Buy/Sell Order      |
+---------------------------+
              |
              V
+----------------------------+
| Set Stop Loss & Take Profit|
+----------------------------+
              |
              V
+----------------------------+
| Monitor Open Position      |
+----------------------------+
              |
        If conditions met:
              |
              V
+----------------------------+
| Adjust SL/TP or Close Trade|
+----------------------------+
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/en/users/kaaiblo |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <Trade\OrderInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include "RiskManagement.mqh"
#include "TimeManagement.mqh"
#include "Sessions.mqh"

//+------------------------------------------------------------------+
//|TradeManagement class                                             |
//+------------------------------------------------------------------+
class CTradeManagement:CRiskManagement
  {
private:
   CTrade            Trade;//Trade class object
   CSymbolProperties CSymbol;//SymbolProperties class object
   CTimeManagement   CTime;//TimeManagement class object
   CSessions         CTS;//Sessions class object
   bool              TradeResult;//boolean to store trade result
   double            mySL;//double variable to store Stoploss
   double            myTP;//double variable to store Takeprofit
   uint              myDeviation;//store price deviation for stop orders
   double            myOpenPrice;//store open price for stop orders
   //--- Will retrieve if there are any open trades
   bool              OpenTrade(ENUM_POSITION_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--- Will retrieve if there are any deals
   bool              OpenedDeal(ENUM_DEAL_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--- Will retrieve if there are any open orders
   bool              OpenOrder(ENUM_ORDER_TYPE Type,ulong Magic,string COMMENT=NULL);
   //--  Check if trade is valid
   bool              Valid_Trade(ENUM_POSITION_TYPE Type,double Price,double SL,double TP);
   //-- Check if stop order is valid
   bool              Valid_Order(ENUM_ORDER_TYPE Type,double Price,double SL,double TP);
   //--- Will attempt to open buy trade
   bool              Buy(double SL,double TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open sell trade
   bool              Sell(double SL,double TP,ulong Magic,string COMMENT=NULL);

   //--- class to set and retrieve an order's properties
   class OrderSettings
     {
   private:
      struct TradeProperties
        {
         //store open-price,take-profit,stop-loss for stop orders
         double         Open,Take,Stop;
        } myTradeProp;
   public:
                     OrderSettings() {}
      //--- Set order properties
      void           Set(double myOpen,double myTake,double myStop)
        {
         //--- Set open-price
         myTradeProp.Open=myOpen;
         //--- Set take-profit
         myTradeProp.Take=myTake;
         //--- Set stop-loss
         myTradeProp.Stop=myStop;
        }
      TradeProperties Get()
        {
         //--- retrieve order properties
         return myTradeProp;
        }
     };

   //--- Declare variables for different order types
   OrderSettings     myBuyStop,mySellStop,myBuyTrade,mySellTrade;

   //--- Will set buy-stop order properties
   void              SetBuyStop(int SL,int TP);
   //--- Will set buy position properties
   void              SetBuyTrade(int SL,int TP,double OP);
   //--- Will set sell-stop order properties
   void              SetSellStop(int SL,int TP);
   //--- Will set sell position properties
   void              SetSellTrade(int SL,int TP,double OP);

public:
   //--- Class constructor
                     CTradeManagement(uint deviation,string SYMBOL=NULL)
                     :myDeviation(deviation)//Assign deviation value
     {
      //--- Set symbol name
      CSymbol.SetSymbolName(SYMBOL);
     }
   //--- Class destructor
                    ~CTradeManagement(void)
     {
     }
   //--- Will attempt to open buy trade
   bool              Buy(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open sell trade
   bool              Sell(int SL,int TP,ulong Magic,string COMMENT=NULL);
   /*This function will delete a pending order if the previous opposing pending order is
   opened into a position, this function is used when trading with StopOrdersType(STOP ORDERS)*/
   void              FundamentalMode(string COMMENT_COMMON);
   /* Function will attempt to re-adjust stop-losses or take-profit values that have
   been changed due to slippage on an order when opening.
   */
   void              SlippageReduction(int SL,int TP,string COMMENT_COMMON);
   //--- This function will open both buy-stop and sell-stop orders for StopOrdersType(STOP ORDERS)
   bool              OpenStops(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open a sell-stop order
   bool              OpenSellStop(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Will attempt to open a buy-stop order
   bool              OpenBuyStop(int SL,int TP,ulong Magic,string COMMENT=NULL);
   //--- Function will attempt to close all trades depending on the position comment
   void              CloseTrades(string COMMENT_COMMON);
  };
// ...

Miembros privados:

  • Objetos y variables:
    • CTrade Trade: gestiona la ejecución de operaciones (órdenes de compra/venta), etc.
    • CSymbolProperties CSymbol: administra las propiedades del símbolo.
    • CTimeManagement CTime: maneja la lógica relacionada con el tiempo.
    • CSessions CTS: gestiona sesiones relacionadas con el horario comercial.
    • bool TradeResult: un indicador booleano para el resultado de una operación (éxito/fracaso).
    • double mySL: valor de stop-loss.
    • double myTP: valor take-profit.
    • uint myDeviation: desviación de precio para órdenes stop.
    • double myOpenPrice: almacena el precio de apertura para órdenes stop.

Clase interna OrderSettings:

class OrderSettings
{
private:
   struct TradeProperties
   {
      double Open, Take, Stop;
   } myTradeProp;
public:
   OrderSettings() {}
   void Set(double myOpen, double myTake, double myStop)
   {
      myTradeProp.Open = myOpen;
      myTradeProp.Take = myTake;
      myTradeProp.Stop = myStop;
   }
   TradeProperties Get()
   {
      return myTradeProp;
   }
};

  • Propósito: Esta clase interna encapsula las propiedades comerciales (precio de apertura, take-profit, stop-loss). Permite configurar y obtener estas propiedades fácilmente.
    • myTradeProp: una estructura para almacenar propiedades comerciales.
    • Set(): método para establecer los valores de apertura, take-profit y stop-loss.
    • Get(): recupera las propiedades comerciales almacenadas.
  • Variables que utilizan OrderSettings:

OrderSettings myBuyStop, mySellStop, myBuyTrade, mySellTrade;

    • Estos objetos almacenan las propiedades de diferentes tipos de órdenes (compra-stop, venta-stop, compra-operación, venta-operación).

La función FundamentalMode en el código a continuación está diseñada para eliminar una orden de stop pendiente cuando su orden opuesta se ha ejecutado y abierto como una posición. Esta funcionalidad es relevante para esta estrategia que utiliza órdenes de compra-stop y de venta-stop (como las estrategias straddle) para ingresar posiciones en mercados volátiles. Una vez que se activa una de las órdenes de stop y se abre una posición, la orden opuesta restante (que ahora es redundante) se elimina para evitar operaciones innecesarias.

Ejemplo:

La orden de compra stop se abre a 1,13118 y la orden de venta stop se abre a 1,12911. La orden de compra stop se ejecuta primero con el mensaje del diario: 'deal performed [#2 buy 0.01 EURUSD at 1.13134]'. En este caso, la función eliminará/cancelará la orden de venta stop restante con el mensaje de diario «order canceled [#3 sell stop 0.01 EURUSD at 1.12911]», tal y como se indica en la imagen siguiente. 

Pedido cancelado debido a FundamentalMode

//+------------------------------------------------------------------+
//|This function will delete a pending order if the previous opposing|
//|pending order is opened into a position, this function is used    |
//|when trading with StopOrdersType(STOP ORDERS)                     |
//+------------------------------------------------------------------+
void CTradeManagement::FundamentalMode(string COMMENT_COMMON)
  {
//--- Iterate through all open positions if Orders are more than zero
   for(int P=0; P<PositionsTotal()&&OrdersTotal()>0; P++)
     {
      //--- Check if Position ticket is above zero
      if(PositionGetTicket(P)>0)
        {
         //--- Check if the Position's Symbol,Magic,Type,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()&&
            StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- Iterate through all open orders
            for(int O=0; O<OrdersTotal(); O++)
              {
               //--- Check if Order ticket is above zero
               if(OrderGetTicket(O)>0)
                 {
                  //--- Check if the Order's Symbol,Magic,Comment is correct
                  if(OrderGetString(ORDER_SYMBOL)==CSymbol.GetSymbolName()
                     &&OrderGetInteger(ORDER_MAGIC)==PositionGetInteger(POSITION_MAGIC)
                     &&StringFind(OrderGetString(ORDER_COMMENT),COMMENT_COMMON)>=0
                     &&OrderGetString(ORDER_COMMENT)==PositionGetString(POSITION_COMMENT))
                    {
                     //--- Identify Position type
                     switch(int(PositionGetInteger(POSITION_TYPE)))
                       {
                        /* In the case that the Position type is a buy and if the corresponding order type is
                        a sell-stop then delete this order*/
                        case  POSITION_TYPE_BUY:
                           if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP)
                             {
                              //--- Delete the sell-stop order
                              Trade.OrderDelete(OrderGetTicket(O));
                             }
                           break;
                        /* In the case that the Position type is a sell and if the corresponding order type is
                        a buy-stop then delete this order*/
                        case POSITION_TYPE_SELL:
                           if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP)
                             {
                              //--- Delete the sell-stop order
                              Trade.OrderDelete(OrderGetTicket(O));
                             }
                           break;
                        default:
                           break;
                       }
                    }
                 }
              }
           }
        }
     }
  }

Propósito de la función:

La función garantiza que cuando una orden pendiente (por ejemplo, una orden de compra-stop o de venta-stop) se abre como una operación, la orden opuesta (por ejemplo, de venta-stop o de compra-stop) se elimina. Esto evita ejecutar ambas órdenes, lo que podría provocar que se abran posiciones no deseadas.

void CTradeManagement::FundamentalMode(string COMMENT_COMMON)

  • FundamentalMode: esta función intenta eliminar una orden de stop pendiente si se ha abierto la posición opuesta.
  • COMMENT_COMMON: Un parámetro de cadena utilizado para identificar transacciones/órdenes en función del comentario asociado a ellas.

Bucle sobre posiciones abiertas:

for(int P = 0; P < PositionsTotal() && OrdersTotal() > 0; P++)

  • PositionsTotal(): Devuelve el número total de posiciones abiertas.
  • OrdersTotal(): Devuelve el número total de pedidos pendientes.
  • Este bucle for itera a través de todas las posiciones abiertas y garantiza que haya órdenes pendientes para procesar.

Validación de ticket de posición:

if(PositionGetTicket(P) > 0)

  • PositionGetTicket(P): recupera el número de ticket de la posición en el índice 'P'.
  • Asegura que el ticket de posición sea válido (es decir, superior a cero).

Comprobar propiedades de posición:

if(PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() &&
   StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL): recupera el símbolo de la posición abierta.
  • CSymbol.GetSymbolName(): recupera el nombre del símbolo para el conjunto de símbolos en el constructor.
  • PositionGetString(POSITION_COMMENT): recupera el comentario asociado con la posición.
  • StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON): Comprueba si la subcadena COMMENT_COMMON está presente en el comentario de la posición. Asegura que el puesto pertenece a la estrategia/experto.

Bucle sobre órdenes abiertas:

for(int O = 0; O < OrdersTotal(); O++)

  • Itera a través de todas las órdenes pendientes abiertas.

Validación del ticket de pedido:

if(OrderGetTicket(O) > 0)

  • OrderGetTicket(O): recupera el número de ticket del pedido en el índice 'O'.
  • Asegura que el ticket del pedido sea válido.

Comprobar propiedades del pedido:

if(OrderGetString(ORDER_SYMBOL) == CSymbol.GetSymbolName() &&
   OrderGetInteger(ORDER_MAGIC) == PositionGetInteger(POSITION_MAGIC) &&
   StringFind(OrderGetString(ORDER_COMMENT), COMMENT_COMMON) >= 0 &&
   OrderGetString(ORDER_COMMENT) == PositionGetString(POSITION_COMMENT))

  • OrderGetString(ORDER_SYMBOL): recupera el símbolo asociado con el pedido.
  • OrderGetInteger(ORDER_MAGIC): recupera el número mágico del pedido (que ayuda a identificar los pedidos para cada evento).
  • PositionGetInteger(POSITION_MAGIC): recupera el número mágico de la posición para hacerlo coincidir con el número mágico de la orden.
  • OrderGetString(ORDER_COMMENT): recupera el comentario del pedido.
  • Asegura que el símbolo de la orden, el número mágico y el comentario coincidan con las propiedades de la posición. Esto garantiza que la orden pendiente esté relacionada con la posición abierta.

Identificar y eliminar órdenes opuestas:

switch(int(PositionGetInteger(POSITION_TYPE)))
{
   case POSITION_TYPE_BUY:
      if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP)
      {
         Trade.OrderDelete(OrderGetTicket(O));
      }
      break;
   case POSITION_TYPE_SELL:
      if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP)
      {
         Trade.OrderDelete(OrderGetTicket(O));
      }
      break;
   default:
      break;
}

  • PositionGetInteger(POSITION_TYPE): recupera el tipo de posición abierta (POSITION_TYPE_BUY o POSITION_TYPE_SELL).
  • OrderGetInteger(ORDER_TYPE): recupera el tipo de orden pendiente (ORDER_TYPE_BUY_STOP o ORDER_TYPE_SELL_STOP).

Cambio de caso:

  • Si la posición es una posición de compra (POSITION_TYPE_BUY), la función verifica si hay una orden de venta pendiente (ORDER_TYPE_SELL_STOP). Si se encuentra, esta orden de venta stop se elimina porque ya no es necesaria.
  • De manera similar, si la posición es una posición de venta (POSITION_TYPE_SELL), la función verifica si hay una orden de compra-stop pendiente (ORDER_TYPE_BUY_STOP). Si se encuentra esta orden de compra-stop se elimina.

Trade.OrderDelete(OrderGetTicket(O)): Este método elimina la orden pendiente usando su número de ticket, eliminando efectivamente la orden opuesta innecesaria después de que se haya abierto la posición correspondiente.

La función SlippageReduction está diseñada para garantizar que los niveles de stop-loss (SL) y take-profit (TP) de una posición abierta se establezcan correctamente en sus valores esperados. Si el deslizamiento (la diferencia entre el precio esperado y el precio real de la ejecución de una orden) ha provocado que el SL y el TP se desvíen de sus valores previstos cuando se ejecutó la orden, la función ajusta la posición para reflejar el SL y el TP esperados.

Ejemplo: Un comerciante desea abrir una orden de compra-stop y una orden de venta-stop para operar con NFP. El comerciante desea una desviación de precio para ambas órdenes de 100 pips respecto del precio actual, también desea una relación riesgo-recompensa de 1:6 por lo que requiere un stop-loss de 100 pips y un take-profit de 600 pips. En este caso, con referencia a la imagen de abajo, el buy-stop se sitúa en 1,13118, el SL se fija en 1,13018 y el TP se fija en 1,13718 manteniendo el ROI 1:6. Debido a la volatilidad del evento NFP, la operación de compra se ejecuta a un precio desfavorable de 1,13134 indicado por el mensaje del Diario 'order performed buy 0.01 at 1.13134 [#2 buy stop 0.01 EURUSD at 1.13118]'.

Deslizamiento experimentado por la orden de compra-stop:

El deslizamiento para el precio de apertura se calculará como - [Precio real - Precio esperado]/Punto

Precio esperado: 1,13118 | Precio real: 1,13134 | Deslizamiento: (1,13134 - 1,13118)/0,00001 -> 16 pips (diferencia de precio)

Por lo tanto, para mantener el ROI y el stop-loss, los valores de SL y TP se ajustarán en 16 pips respectivamente.

Reducción de deslizamiento en acción

//+------------------------------------------------------------------+
//|Function will attempt to re-adjust stop-losses or take-profit     |
//|values that have been changed due to slippage on an order when    |
//|opening.                                                          |
//+------------------------------------------------------------------+
void CTradeManagement::SlippageReduction(int SL,int TP,string COMMENT_COMMON)
  {
//--- Iterate through all open positions
   for(int i=0; i<PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      //--- Check if Position ticket is above zero
      if(ticket>0)
        {
         //--- Check if the Position's Symbol,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()
            &&StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- Identify Position type
            switch(int(PositionGetInteger(POSITION_TYPE)))
              {
               case  POSITION_TYPE_BUY:
                  //--- set expect buy trade properties
                  SetBuyTrade(SL,TP,PositionGetDouble(POSITION_PRICE_OPEN));
                  //--- assign sl price
                  mySL = PositionGetDouble(POSITION_SL);
                  //--- assign tp price
                  myTP = PositionGetDouble(POSITION_TP);
                  //--- Normalize sl price
                  CSymbol.NormalizePrice(mySL);
                  mySL = double(DoubleToString(mySL,CSymbol.Digits()));
                  //--- Normalize tp price
                  CSymbol.NormalizePrice(myTP);
                  myTP = double(DoubleToString(myTP,CSymbol.Digits()));
                  //--- check if expected properties match actual trade properties
                  if((myBuyTrade.Get().Stop!=mySL||
                      myBuyTrade.Get().Take!=myTP)
                     &&Valid_Trade(POSITION_TYPE_BUY,myBuyTrade.Get().Open,
                                   myBuyTrade.Get().Stop,myBuyTrade.Get().Take))
                    {
                     //--- Modify position to respect expected properties
                     Trade.PositionModify(ticket,myBuyTrade.Get().Stop,myBuyTrade.Get().Take);
                    }
                  break;
               case POSITION_TYPE_SELL:
                  //--- set expect sell trade properties
                  SetSellTrade(SL,TP,PositionGetDouble(POSITION_PRICE_OPEN));
                  //--- assign sl price
                  mySL = PositionGetDouble(POSITION_SL);
                  //--- assign tp price
                  myTP = PositionGetDouble(POSITION_TP);
                  //--- Normalize sl price
                  CSymbol.NormalizePrice(mySL);
                  mySL = double(DoubleToString(mySL,CSymbol.Digits()));
                  //--- Normalize tp price
                  CSymbol.NormalizePrice(myTP);
                  myTP = double(DoubleToString(myTP,CSymbol.Digits()));
                  //--- check if expected properties match actual trade properties
                  if((mySellTrade.Get().Stop!=mySL||
                      mySellTrade.Get().Take!=myTP)
                     &&Valid_Trade(POSITION_TYPE_SELL,mySellTrade.Get().Open,
                                   mySellTrade.Get().Stop,mySellTrade.Get().Take))
                    {
                     //--- Modify position to respect expected properties
                     Trade.PositionModify(ticket,mySellTrade.Get().Stop,mySellTrade.Get().Take);
                    }
                  break;
               default:
                  break;
              }
           }
        }
     }
  }

Propósito de la función:

El objetivo de esta función es:

  1. Verifique cada posición abierta.
  2. Verifique si los valores de stop-loss y take-profit de la posición coinciden con los valores esperados.
  3. Si no coinciden debido al deslizamiento, la posición se modifica para reflejar los valores SL y TP correctos.

void CTradeManagement::SlippageReduction(int SL, int TP, string COMMENT_COMMON)

  • SlippageReduction: el nombre de la función refleja su propósito: ajustar el SL y el TP si se han visto afectados por el deslizamiento durante la ejecución de la orden.

Parámetros:

  • SL: Valor de stop-loss esperado.
  • TP: Valor de toma de ganancias esperado.
  • COMMENT_COMMON: Una cadena utilizada para filtrar e identificar las operaciones que pertenecen a este experto.

Recorrer puestos vacantes:

for (int i = 0; i < PositionsTotal(); i++)
{
    ulong ticket = PositionGetTicket(i);

  • PositionsTotal(): Esta función devuelve el número de posiciones abiertas.
  • PositionGetTicket(i): recupera el número de ticket para la posición en el índice 'i'. Un ticket identifica de forma única una posición abierta.

Validación de ticket de posición:

if (ticket > 0)

  • Se asegura de que el puesto tenga un número de ticket válido antes de continuar con los siguientes pasos.

Comprobar propiedades de posición:

if (PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() &&
    StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL): recupera el símbolo asociado con la posición abierta.
  • CSymbol.GetSymbolName(): obtiene el nombre del símbolo para la estrategia comercial (por ejemplo, "EURUSD").
  • PositionGetString(POSITION_COMMENT): recupera el comentario adjunto a la posición abierta.
  • StringFind(): verifica si COMMENT_COMMON es parte del comentario de la posición, asegurando que la función solo procese posiciones relacionadas con la estrategia actual.

Identificar y gestionar los tipos de posiciones (compra o venta):

Caso 1: Posición de compra

case POSITION_TYPE_BUY:

  • La función maneja posiciones de tipo BUY.

Establecer propiedades comerciales de compra esperadas:

SetBuyTrade(SL, TP, PositionGetDouble(POSITION_PRICE_OPEN));

  • SetBuyTrade(SL, TP, PositionGetDouble(POSITION_PRICE_OPEN)): Establece el stop-loss, el take-profit y el precio de apertura esperados para la operación de compra. La función SetBuyTrade asigna estos valores esperados al objeto myBuyTrade.

Asignar valores reales de SL y TP:

mySL = PositionGetDouble(POSITION_SL);
myTP = PositionGetDouble(POSITION_TP);

  • PositionGetDouble(POSITION_SL): recupera el valor de stop-loss real de la posición abierta.
  • PositionGetDouble(POSITION_TP): recupera el valor de toma de ganancias real de la posición abierta.

Normalizar valores SL y TP:

CSymbol.NormalizePrice(mySL);
mySL = double(DoubleToString(mySL, CSymbol.Digits()));

CSymbol.NormalizePrice(myTP);
myTP = double(DoubleToString(myTP, CSymbol.Digits()));

  • NormalizePrice(mySL): normaliza el valor del stop-loss al número correcto de decimales (según las propiedades del símbolo, por ejemplo, los pares de divisas pueden tener 4 o 5 puntos decimales).
  • DoubleToString(mySL, CSymbol.Digits()): convierte el valor SL normalizado en una cadena y luego nuevamente en un doble para garantizar la precisión.
  • El mismo proceso se aplica al valor de take-profit (myTP).

Comparar SL/TP esperado y real:

if ((myBuyTrade.Get().Stop != mySL || myBuyTrade.Get().Take != myTP) &&
    Valid_Trade(POSITION_TYPE_BUY, myBuyTrade.Get().Open, myBuyTrade.Get().Stop, myBuyTrade.Get().Take))
{
    Trade.PositionModify(ticket, myBuyTrade.Get().Stop, myBuyTrade.Get().Take);
}

  • myBuyTrade.Get().Stop: recupera el valor de stop-loss esperado.
  • mySL: El valor de stop-loss real recuperado de la posición.
  • Si los valores reales de SL y TP no coinciden con los esperados, la función llama a Trade.PositionModify para actualizar la posición y establecer el stop-loss y el take-profit correctos.
  • Valid_Trade(): verifica si los parámetros comerciales (precio, SL, TP) son válidos para su modificación antes de intentar modificar la posición.

Modificar posición:

Trade.PositionModify(ticket, myBuyTrade.Get().Stop, myBuyTrade.Get().Take);

  • Esta función modifica la posición asociada con el ticket para reflejar los valores de stop-loss y take-profit correctos en función de los valores esperados.

Caso 2: Posición de venta

  • Esto es similar al proceso para las posiciones de compra.

La función Valid_Trade verifica si los parámetros de una operación (como el precio, los niveles de stop-loss y take-profit) son válidos en función del tipo de operación (compra o venta) y las reglas que rigen los niveles de stop-loss y take-profit en relación con las propiedades del símbolo actual. La función devuelve verdadero si el comercio es válido y falso si no lo es.

//+------------------------------------------------------------------+
//|Check if a trade is valid                                         |
//+------------------------------------------------------------------+
bool CTradeManagement::Valid_Trade(ENUM_POSITION_TYPE Type,double Price,double SL,double TP)
  {
//--- Identify Position type
   switch(Type)
     {
      case  POSITION_TYPE_BUY:
         if((Price<TP||TP==0)&&(Price>SL||SL==0)&&
            ((int((Price-SL)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            ((int((TP-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            Price>0
           )
           {
            //--- Trade properties are valid.
            return true;
           }
         break;
      case POSITION_TYPE_SELL:
         if((Price>TP||TP==0)&&(Price<SL||SL==0)&&
            ((int((Price-TP)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            ((int((SL-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            Price>0
           )
           {
            //--- Trade properties are valid.
            return true;
           }
         break;
      default://Unknown
         return false;
         break;
     }
//--- Trade properties are not valid.
   Print("Something went wrong, SL/TP/Open-Price is incorrect!");
   return false;
  }

Propósito de la función:

El propósito de esta función es:

  1. Validar los parámetros comerciales (precio, SL y TP) para posiciones de compra o venta.
  2. Asegúrese de que los niveles de stop-loss y take-profit estén configurados correctamente en relación con el precio de apertura y que cumplan con los requisitos de nivel de stop del símbolo.

Parámetros:

bool CTradeManagement::Valid_Trade(ENUM_POSITION_TYPE Type, double Price, double SL, double TP)

  • Type: El tipo de posición, ya sea POSITION_TYPE_BUY (posición de compra) o POSITION_TYPE_SELL (posición de venta).
  • Price: El precio de apertura de la operación.
  • SL: El nivel de stop-loss para la operación.
  • TP: El nivel de toma de ganancias.

Declaración de cambio:

La función utiliza una declaración switch para manejar diferentes tipos de posiciones (compra o venta) y aplica una lógica de validación diferente para cada caso.

Caso 1: Posición de compra

case POSITION_TYPE_BUY:

La lógica para una posición de compra verifica las siguientes condiciones:

Validación de Take-Profit (TP):

if ((Price < TP || TP == 0)

  • El nivel de toma de ganancias debe ser mayor que el precio de apertura (Precio < TP), o puede establecerse en cero (sin toma de ganancias).

Validación de Stop-Loss (SL):

(Price > SL || SL == 0)

  • El nivel de stop-loss debe ser menor que el precio de apertura (Precio > SL), o puede establecerse en cero (sin stop-loss).

Validación del nivel de parada del símbolo:

  • El nivel de stop del símbolo define la distancia mínima entre el precio y el stop-loss/take-profit.
  • Los niveles de stop-loss y take-profit deben estar a una distancia mínima (igual o mayor que el nivel de stop) del precio de apertura.

Validación de la distancia de stop-loss:

((int((Price - SL) / CSymbol.Point()) >= CSymbol.StopLevel()) || SL == 0)

  • La diferencia entre el precio y el stop-loss debe ser al menos el nivel de stop del símbolo (en puntos). Si el stop-loss se establece en cero, esta condición se ignora.

Validación de la distancia de toma de ganancias:

((int((TP - Price) / CSymbol.Point()) >= CSymbol.StopLevel()) || TP == 0)

  • La diferencia entre el take-profit y el precio también debe cumplir el requisito del nivel de stop.

Validación final:

&& Price > 0

  • El precio debe ser mayor que cero.

Si se cumplen todas estas condiciones, el comercio se considera válido y la función devuelve verdadero.

Caso 2: Posición de venta

Para una posición de venta, la lógica es similar pero invertida.

Caso predeterminado:

default:
    return false;

Si el tipo de posición no es POSITION_TYPE_BUY ni POSITION_TYPE_SELL, la función devuelve falso, lo que indica que la operación no es válida.

Propiedades comerciales no válidas:

Si alguna de las comprobaciones de validación falla, la función registra un mensaje de error y devuelve falso.

Print("Something went wrong, SL/TP/Open-Price is incorrect!");
return false;

Este mensaje indica que el precio de stop-loss, take-profit o apertura es incorrecto.

El código a continuación define un método, Valid_Order, que verifica si una orden de stop (ya sea una orden BUY STOP o SELL STOP) es válida verificando las condiciones de stop-loss (SL), take-profit (TP), precio y desviación de la orden. La función devuelve verdadero si el pedido es válido y falso en caso contrario.

//+------------------------------------------------------------------+
//|Check if stop order is valid                                      |
//+------------------------------------------------------------------+
bool CTradeManagement::Valid_Order(ENUM_ORDER_TYPE Type,double Price,double SL,double TP)
  {
//--- Identify Order type
   switch(Type)
     {
      case  ORDER_TYPE_BUY_STOP:
         if((Price<TP||TP==0)&&(Price>SL||SL==0)&&(Price>CSymbol.Ask())&&
            ((int((Price-SL)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            ((int((TP-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            myDeviation>=uint(CSymbol.StopLevel())
           )
           {
            //--- Order properties are valid.
            return true;
           }
         break;
      case ORDER_TYPE_SELL_STOP:
         if((Price>TP||TP==0)&&(Price<SL||SL==0)&&(Price<CSymbol.Bid())&&
            ((int((Price-TP)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(TP==0))&&
            ((int((SL-Price)/CSymbol.Point())>=CSymbol.StopLevel())
             ||(SL==0))&&
            myDeviation>=uint(CSymbol.StopLevel())
           )
           {
            //--- Order properties are valid.
            return true;
           }
         break;
      default://Other
         return false;
         break;
     }
//--- Order properties are not valid.
   Print("Something went wrong, SL/TP/Deviation/Open-Price is incorrect!");
   return false;
  }

Descripción general:

La función realiza la validación en función del tipo de orden de stop:

  1. BUY STOP: Una orden de compra-stop se coloca por encima del precio de mercado actual (el precio de venta) y se activa cuando el precio de mercado alcanza o supera este nivel.
  2. SELL STOP: una orden de venta-stop se coloca por debajo del precio de mercado actual (el precio de oferta) y se activa cuando el precio de mercado cae a este nivel o por debajo de él.

Parámetros:

bool CTradeManagement::Valid_Order(ENUM_ORDER_TYPE Type, double Price, double SL, double TP)

  • Type: El tipo de orden de stop (ORDER_TYPE_BUY_STOP o ORDER_TYPE_SELL_STOP).
  • Price: El precio al que se establece la orden de stop.
  • SL: El precio de stop-loss asociado con la orden de stop.
  • TP: El precio de toma de ganancias asociado con la orden de stop.

Declaración de cambio:

La declaración switch maneja diferentes tipos de órdenes y aplica reglas de validación distintas para cada uno.

Caso 1: Orden BUY STOP

case ORDER_TYPE_BUY_STOP:

Para una orden BUY STOP, se deben cumplir las siguientes condiciones:

Validación de Take-Profit (TP):

(Price < TP || TP == 0)

El precio de toma de ganancias debe ser mayor que el precio de la orden (Precio < TP), o puede establecerse en cero (sin toma de ganancias).

Validación de Stop-Loss (SL):

(Price > SL || SL == 0)

El precio de stop-loss debe ser menor que el precio de la orden (Precio > SL), o puede establecerse en cero (sin stop-loss).

Precio superior al precio de venta:

(Price > CSymbol.Ask())

El precio de la orden debe ser mayor que el precio de venta actual, lo cual es una condición clave para una orden BUY STOP.

Validación del nivel de stop del símbolo: Validación de la distancia de stop-loss:

((int((Price - SL) / CSymbol.Point()) >= CSymbol.StopLevel()) || SL == 0)

La diferencia entre el precio de la orden y el stop-loss debe ser igual o superior al nivel de stop del símbolo, que es la distancia mínima permitida en puntos entre el precio de la orden y el nivel de stop-loss. Si el stop-loss se establece en cero, se omite esta condición.

Validación de la distancia de toma de ganancias:

((int((TP - Price) / CSymbol.Point()) >= CSymbol.StopLevel()) || TP == 0)

La diferencia entre el precio de toma de ganancias y el precio de la orden también debe cumplir con el requisito del nivel de stop, o el precio de toma de ganancias puede ser cero.

Validación de desviación de precios:

myDeviation >= uint(CSymbol.StopLevel())

La desviación del precio (tolerancia al deslizamiento) debe ser mayor o igual al nivel de stop del símbolo. La desviación permite que la orden se ejecute incluso si el precio del mercado se mueve ligeramente más allá del nivel de stop establecido.

Si se cumplen todas estas condiciones, la orden BUY STOP se considera válida y la función devuelve verdadero.

Caso 2: Orden SELL STOP

Para una orden SELL STOP, la lógica de validación es la inversa de una orden BUY STOP.

Caso predeterminado:

default:
    return false;

Si el tipo de orden no es ORDER_TYPE_BUY_STOP ni ORDER_TYPE_SELL_STOP, la función devuelve falso, lo que indica que la orden no es válida.

Manejo de errores:

Si las condiciones fallan y la función no puede validar el pedido, registra un mensaje de error:

Print("Something went wrong, SL/TP/Deviation/Open-Price is incorrect!");

Este mensaje informa al usuario que algo anda mal con el stop-loss, el take-profit, la desviación del precio o el precio de la orden, lo que hace que la orden no sea válida.

El código siguiente define una función SetBuyStop, responsable de establecer las propiedades de una orden de compra-stop. Calcula los precios de stop-loss (SL) y take-profit (TP) basándose en los datos de entrada y los asigna a la orden. La función realiza los siguientes pasos:

  1. Calcula el precio de apertura para la orden de compra-stop.
  2. Calcula y establece los precios de stop-loss y take-profit en relación con el precio de apertura.
  3. Normaliza los precios para garantizar que estén alineados con la precisión del símbolo (número de dígitos decimales).

//+------------------------------------------------------------------+
//|Will set buy-stop order properties                                |
//+------------------------------------------------------------------+
void CTradeManagement::SetBuyStop(int SL,int TP)
  {
//-- Get Open-price
   myOpenPrice=CSymbol.Ask()+myDeviation*CSymbol.Point();
   CSymbol.NormalizePrice(myOpenPrice);
   NormalizeDouble(myOpenPrice,CSymbol.Digits());
//--- Get SL value
   mySL=SL*CSymbol.Point();
   mySL=myOpenPrice-mySL;
//--- Normalize the SL Price
   CSymbol.NormalizePrice(mySL);
   NormalizeDouble(mySL,CSymbol.Digits());
//--- Get TP value
   myTP=TP*CSymbol.Point();
   myTP+=myOpenPrice;
//--- Normalize the TP Price
   CSymbol.NormalizePrice(myTP);
   NormalizeDouble(myTP,CSymbol.Digits());
//--- Set BuyStop properties
   myBuyStop.Set(myOpenPrice,myTP,mySL);
  }

Parámetros:

  • SL: El valor del stop-loss en términos de puntos.
  • TP: El valor del take-profit en términos de puntos.

void CTradeManagement::SetBuyStop(int SL, int TP)

Tanto SL como TP son valores enteros que representan la cantidad de puntos (incrementos de precio) desde el precio de apertura hasta el nivel de stop-loss o take-profit.

Paso 1: Cálculo del precio de apertura para la orden de compra-stop

myOpenPrice = CSymbol.Ask() + myDeviation * CSymbol.Point();

  • CSymbol.Ask() recupera el precio de venta actual para el símbolo (par de divisas o activo).
  • myDeviation * CSymbol.Point() agrega una desviación (buffer de precio) en términos de puntos para garantizar que el precio de apertura se coloque por encima del precio de venta actual. CSymbol.Point() devuelve el tamaño de un punto (el cambio de precio más pequeño) para el símbolo.

Normalización:

CSymbol.NormalizePrice(myOpenPrice);
NormalizeDouble(myOpenPrice, CSymbol.Digits());

  • CSymbol.NormalizePrice(myOpenPrice) ajusta el precio para alinearse con la precisión del símbolo (es decir, a un formato de precio válido).
  • NormalizeDouble(myOpenPrice, CSymbol.Digits()) garantiza que el precio se redondee a la cantidad correcta de decimales definida por la precisión del símbolo (CSymbol.Digits()).

Paso 2: Cálculo y normalización del precio del stop-loss (SL)

mySL = SL * CSymbol.Point();
mySL = myOpenPrice - mySL;

  • SL * CSymbol.Point() convierte el valor entero del stop-loss (en puntos) en una diferencia de precio.
  • myOpenPrice - mySL calcula el precio de stop-loss restando el valor de stop-loss (en puntos) del precio de apertura. Esto se debe a que el stop-loss de una orden de compra-stop se coloca por debajo del precio de apertura.

Normalización:

CSymbol.NormalizePrice(mySL);
NormalizeDouble(mySL, CSymbol.Digits());

  • El precio del stop-loss se normaliza y se redondea a la precisión correcta, como se explicó anteriormente.

Paso 3: Cálculo y normalización del precio de toma de ganancias (TP)

myTP = TP * CSymbol.Point();
myTP += myOpenPrice;

  • TP * CSymbol.Point() convierte el valor entero de toma de ganancias (en puntos) en una diferencia de precio.
  • myTP += myOpenPrice agrega este valor al precio de apertura porque el take-profit para una orden de compra-stop se coloca por encima del precio de apertura.

Normalización:

CSymbol.NormalizePrice(myTP);
NormalizeDouble(myTP, CSymbol.Digits());

  • El precio de toma de ganancias está normalizado y redondeado, similar a los precios de stop-loss y de apertura.

Paso 4: Asignación de los valores calculados a la orden de compra-stop

myBuyStop.Set(myOpenPrice, myTP, mySL);

  • myBuyStop.Set(myOpenPrice, myTP, mySL) asigna los valores calculados de precio de apertura, take-profit y stop-loss al objeto de orden myBuyStop.
  • El objeto myBuyStop ahora contendrá las propiedades necesarias para colocar la orden de compra-stop en el mercado.

La función SetBuyTrade establece las propiedades para una posición de compra. Calcula y asigna el precio de apertura, el stop-loss (SL) y el take-profit (TP) correctos para la operación. Esta función se utiliza para configurar los parámetros de una posición de compra (no una orden pendiente como una orden de compra con stop). Se abre una posición de compra al precio de mercado actual, con niveles de stop-loss y take-profit especificados en relación con el precio de apertura.

Parámetros:

  • SL: El valor del stop-loss en puntos (incrementos de precio).
  • TP: El valor de toma de ganancias en puntos.
  • OP: El precio de apertura de la operación (el precio al que se abre la posición).

Paso 1: Establecer el precio de apertura

myOpenPrice = OP;
CSymbol.NormalizePrice(myOpenPrice);
myOpenPrice = double(DoubleToString(myOpenPrice, CSymbol.Digits()));

  • myOpenPrice = OP: La función toma el precio de apertura proporcionado (Open Price, OP) y lo asigna a myOpenPrice.
  • CSymbol.NormalizePrice(myOpenPrice): Esto normaliza el precio de apertura de acuerdo con la precisión del símbolo, garantizando que coincida con el número correcto de decimales.
  • myOpenPrice = double(DoubleToString(myOpenPrice, CSymbol.Digits())): El precio se convierte luego en una cadena (string) y nuevamente en un doble (double) con la cantidad correcta de dígitos decimales (precisión) para el símbolo. Esto garantiza la consistencia en el manejo de los valores de precios, evitando problemas con la precisión de punto flotante.

Paso 2: Cálculo y normalización del precio del stop-loss (SL)

mySL = SL * CSymbol.Point();
mySL = myOpenPrice - mySL;

  • SL * CSymbol.Point(): Convierte el valor de stop-loss de puntos a una diferencia de precio real. CSymbol.Point() devuelve el tamaño de un punto (el incremento de precio más pequeño posible para el símbolo).
  • myOpenPrice - mySL: El precio de stop-loss se establece restando el valor de stop-loss calculado del precio de apertura. En una posición de compra, el stop-loss se coloca por debajo del precio de apertura para proteger la operación de pérdidas excesivas.

Normalización:

CSymbol.NormalizePrice(mySL);
mySL = double(DoubleToString(mySL, CSymbol.Digits()));

  • El precio del stop-loss se normaliza para garantizar que tenga la precisión correcta y luego se redondea al número apropiado de decimales.

Paso 3: Cálculo y normalización del precio de toma de ganancias (TP)

myTP = TP * CSymbol.Point();
myTP += myOpenPrice;

  • TP * CSymbol.Point(): Convierte el valor de toma de ganancias de puntos a una diferencia de precio.
  • myTP += myOpenPrice: El precio de take-profit se suma al precio de apertura ya que, para una posición de compra, el take-profit se coloca por encima del precio de apertura.

Normalización:

CSymbol.NormalizePrice(myTP);
myTP = double(DoubleToString(myTP, CSymbol.Digits()));

  • El precio de toma de ganancias se normaliza y se redondea para que coincida con la precisión del símbolo.

Paso 4: Asignación de los valores calculados a la posición de compra

myBuyTrade.Set(myOpenPrice, myTP, mySL);

  • myBuyTrade.Set(myOpenPrice, myTP, mySL): los valores calculados de precio de apertura, stop-loss y take-profit se asignan al objeto myBuyTrade. Este objeto ahora contiene todas las propiedades relevantes de la posición de compra que se abrirá o modificará.

La función OpenBuyStop intenta abrir una orden de compra-stop con un stop-loss (SL), un take-profit (TP), un número mágico y un comentario opcional especificados.

Parámetros:

  • SL: Valor del stop-loss en puntos.
  • TP: Valor de take-profit en puntos.
  • Magic: Número mágico único para identificar la orden, normalmente utilizado por asesores expertos.
  • COMMENT: (Opcional) Un comentario string para adjuntar al pedido para su identificación.

//+------------------------------------------------------------------+
//|Will attempt to open a buy-stop order                             |
//+------------------------------------------------------------------+
bool CTradeManagement::OpenBuyStop(int SL,int TP,ulong Magic,string COMMENT=NULL)
  {
   SetBuyStop(SL,TP);
//--- Set the order type for Risk management calculation
   SetOrderType(ORDER_TYPE_BUY);
//--- Set open price for Risk management calculation
   OpenPrice = myBuyStop.Get().Open;
//--- Set close price for Risk management calculation
   ClosePrice = myBuyStop.Get().Stop;
//--- Set Trade magic number
   Trade.SetExpertMagicNumber(Magic);
//--- Check if there are any open trades or opened deals or canceled deals already
   if(!OpenOrder(ORDER_TYPE_BUY_STOP,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT)
//--- Check if the buy-stop properties are valid
      &&Valid_Order(ORDER_TYPE_BUY_STOP,myBuyStop.Get().Open,myBuyStop.Get().Stop,myBuyStop.Get().Take))
     {
      //--- Iterate through the Lot-sizes if they're more than max-lot
      for(double i=Volume();i>=CSymbol.LotsMin()&&
          /* Check if current number of orders +1 more orders is less than
                 account orders limit.*/
          (PositionsTotal()+Account.numOrders()+1)<Account.LimitOrders()
          ;i-=CSymbol.LotsMax())
        {
         //--- normalize Lot-size
         NormalizeLotsize(i);
         /* Open buy-stop order with a Lot-size not more than max-lot and set order expiration
         to the Symbol's session end time for the current day.
         */
         if(!Trade.BuyStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,myBuyStop.Get().Open,
                           CSymbol.GetSymbolName(),myBuyStop.Get().Stop,myBuyStop.Get().Take,
                           ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT))
           {
            //--- Order failed to open
            return false;
           }
        }
     }
   else
     {
      //--- Order failed
      return false;
     }
//--- Return trade result.
   return true;
  }

Paso 1: Establecer propiedades de compra y stop

SetBuyStop(SL, TP);

  • Esta línea llama al método SetBuyStop (explicado anteriormente) para calcular y establecer el precio de apertura, el stop-loss y el take-profit para la orden de compra-stop.
  • El resultado se almacena en el objeto myBuyStop, que contiene las propiedades clave para la orden de compra-stop (precio de apertura, SL y TP).

Paso 2: Establecer el tipo de orden para la gestión de riesgos

SetOrderType(ORDER_TYPE_BUY);

  • Esto establece el tipo de orden interna en ORDER_TYPE_BUY. Si bien se trata de una orden de compra con stop, en esencia sigue siendo una orden de compra, y se utiliza para calcular métricas de gestión de riesgos como stop-loss, take-profit y dimensionamiento de posiciones.

Paso 3: Establecer precios de apertura y cierre para la gestión de riesgos

OpenPrice = myBuyStop.Get().Open;
ClosePrice = myBuyStop.Get().Stop;

  • OpenPrice: se establece en el precio de apertura calculado de la orden de compra-stop (myBuyStop.Get().Open).
  • ClosePrice: se establece en el precio de stop-loss calculado de la orden de compra-stop (myBuyStop.Get().Stop).

Paso 4: Establecer el número mágico

Trade.SetExpertMagicNumber(Magic);

  • Esto establece el número mágico para la orden usando Trade.SetExpertMagicNumber. El número mágico identifica de forma única la orden, lo que resulta útil al gestionar operaciones abiertas por un asesor experto.

Paso 5: Verifique si hay operaciones o acuerdos abiertos

if (!OpenOrder(ORDER_TYPE_BUY_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY, Magic, COMMENT)

  • OpenOrder: verifica si hay órdenes de compra-stop pendientes ya abiertas con el mismo número mágico y comentario. Si existe, la función omite la apertura de un nuevo pedido.
  • OpenedDeal: verifica si ya hay una posición de compra abierta usando el mismo número mágico y comentario. Si existe tal acuerdo, la función evita abrir uno nuevo.

Paso 6: Validar las propiedades de la orden de compra-stop

&& Valid_Order(ORDER_TYPE_BUY_STOP, myBuyStop.Get().Open, myBuyStop.Get().Stop, myBuyStop.Get().Take))

  • Esto verifica si las propiedades de la orden de compra-stop calculadas son válidas utilizando el método Valid_Order (explicado anteriormente). El método verifica que el precio de apertura, el stop-loss y el take-profit sean correctos y que la orden se adhiera a las reglas del símbolo (por ejemplo, nivel mínimo de stop, valor del punto, etc.).

Paso 7: Iterar a través de los tamaños de lote

for (double i = Volume(); i >= CSymbol.LotsMin() && (PositionsTotal() + Account.numOrders() + 1) < Account.LimitOrders(); i -= CSymbol.LotsMax())

  • Volume(): recupera el volumen comercial actual (tamaño del lote) para la orden.
  • El bucle comienza desde el volumen actual (i = Volume()) y siempre que:

    1. El tamaño del lote es mayor o igual al tamaño de lote mínimo (CSymbol.LotsMin()).
    2. El número total de posiciones más las órdenes existentes de la cuenta es menor que el límite de órdenes de la cuenta (Account.LimitOrders()).

Reduce el tamaño del lote en incrementos del tamaño de lote máximo (CSymbol.LotsMax()) para cada iteración para garantizar que la orden cumpla con los límites de volumen.

Paso 8: Normalizar el tamaño del lote

NormalizeLotsize(i);

  • El tamaño del lote (i) está normalizado, lo que garantiza que se ajuste para cumplir con las reglas de precisión del símbolo.

Paso 9: Intente abrir una orden de compra-stop

if (!Trade.BuyStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, myBuyStop.Get().Open, CSymbol.GetSymbolName(), 
myBuyStop.Get().Stop, myBuyStop.Get().Take, ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT))

  • Trade.BuyStop(): intenta colocar la orden de compra-stop con los siguientes parámetros:

    1. Tamaño del lote: el tamaño del lote está limitado a CSymbol.LotsMax() (el tamaño de lote máximo permitido del símbolo). Si (i) es mayor que el tamaño de lote máximo, utiliza el máximo; de lo contrario, utiliza el tamaño de lote actual (i).
    2. Precio de apertura: El precio al que debe ejecutarse la orden de compra-stop.
    3. Nombre del símbolo: el nombre del símbolo comercial (par de divisas, etc.).
    4. Stop-loss: el precio de stop-loss calculado para la orden.
    5. Take-profit: el precio de take-profit calculado para la orden.
    6. Hora de vencimiento de la orden: la orden está configurada para expirar al final de la sesión de negociación del símbolo en el día actual (CTS.SessionEnd()).
    7. Comentario: El comentario opcional proporcionado anteriormente.

Si la orden no se abre, la función devuelve falso, lo que indica que la orden de compra-stop no se colocó correctamente.

Paso 10: Devolver resultado

return true;

  • Si la orden se abre correctamente, la función devuelve verdadero, lo que indica que la orden de compra-stop se colocó correctamente.

Función OpenSellStop

Para la función OpenSellStop, la lógica de la función es similar a la de la función OpenBuyStop explicada anteriormente.

La función OpenStops intenta abrir órdenes tanto de compra como de venta. 

//+------------------------------------------------------------------+
//|This function will open both buy-stop and sell-stop orders for    |
//|StopOrdersType(STOP ORDERS)                                       |
//+------------------------------------------------------------------+
bool CTradeManagement::OpenStops(int SL,int TP,ulong Magic,string COMMENT=NULL)
  {
//--- Set buy-stop properties
   SetBuyStop(SL,TP);
//--- Set sell-stop properties
   SetSellStop(SL,TP);
//--- Set the order type for Risk management calculation
   SetOrderType(ORDER_TYPE_BUY);
//--- Set open price for Risk management calculation
   OpenPrice = myBuyStop.Get().Open;
//--- Set close price for Risk management calculation
   ClosePrice = myBuyStop.Get().Stop;
//--- Set Trade magic number
   Trade.SetExpertMagicNumber(Magic);
//--- Check if there are any open trades or opened deals or canceled deals already
   if(!OpenOrder(ORDER_TYPE_BUY_STOP,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT)
      &&!OpenedDeal(DEAL_TYPE_BUY_CANCELED,Magic,COMMENT)
//--- Check if the buy-stop properties are valid
      &&Valid_Order(ORDER_TYPE_BUY_STOP,myBuyStop.Get().Open,myBuyStop.Get().Stop,myBuyStop.Get().Take)
      &&!OpenOrder(ORDER_TYPE_SELL_STOP,Magic,COMMENT)
      &&!OpenedDeal(DEAL_TYPE_SELL,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_SELL_CANCELED,Magic,COMMENT)
//--- Check if the sell-stop properties are valid
      &&Valid_Order(ORDER_TYPE_SELL_STOP,mySellStop.Get().Open,mySellStop.Get().Stop,mySellStop.Get().Take))
     {
      //--- Iterate through the Lot-sizes if they're more than max-lot
      for(double i=Volume();i>=CSymbol.LotsMin()&&
          /* Check if current number of orders +2 more orders is less than
             account orders limit.*/
          (PositionsTotal()+Account.numOrders()+2)<Account.LimitOrders()
          ;i-=CSymbol.LotsMax())
        {
         //--- normalize Lot-size
         NormalizeLotsize(i);
         /* Open orders with a Lot-size not more than max-lot and set order expiration
         to the Symbol's session end time for the current day.
         */
         if(!Trade.BuyStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,myBuyStop.Get().Open,
                           CSymbol.GetSymbolName(),myBuyStop.Get().Stop,myBuyStop.Get().Take,
                           ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT)
            ||!Trade.SellStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,mySellStop.Get().Open,
                              CSymbol.GetSymbolName(),mySellStop.Get().Stop,mySellStop.Get().Take,
                              ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT))
           {
            //--- one or more orders failed to open.
            return false;
           }
        }
     }
   else
     {
      //--- Orders failed
      return false;
     }
//--- Return trade result.
   return true;
  }

Paso 1: Establecer las propiedades Buy-Stop y Sell-Stop

SetBuyStop(SL, TP);
SetSellStop(SL, TP);

  • SetBuyStop: este método calcula y establece el precio de apertura, el stop-loss y el take-profit para la orden de compra-stop. Estos valores se almacenan en el objeto myBuyStop.
  • SetSellStop: de manera similar, este método calcula y establece el precio de apertura, el stop-loss y el take-profit para la orden de venta-stop, almacenados en el objeto mySellStop.

Paso 2: Establecer el tipo de orden para la gestión de riesgos

SetOrderType(ORDER_TYPE_BUY);

  • Esto establece el tipo de orden interna para el cálculo de gestión de riesgos en una orden de compra, incluso si se colocan órdenes de compra-stop y de venta-stop. Esta configuración se utiliza más adelante para evaluar el riesgo en función del stop-loss y el take-profit de la orden de compra.

Paso 3: Establecer precios de apertura y cierre para la gestión de riesgos

OpenPrice = myBuyStop.Get().Open;
ClosePrice = myBuyStop.Get().Stop;

  • OpenPrice: el precio de apertura calculado para la orden de compra-stop.
  • ClosePrice: el precio de stop-loss calculado para la orden de compra-stop. Estos precios se utilizan para los cálculos de gestión de riesgos internos.

Paso 4: Establecer el número mágico

Trade.SetExpertMagicNumber(Magic);

  • Esto asigna el número mágico a la operación, que identifica de forma única las órdenes gestionadas por el asesor experto.

Paso 5: Verifique si hay pedidos o ofertas existentes

if(!OpenOrder(ORDER_TYPE_BUY_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY, Magic, COMMENT)
   && !OpenedDeal(DEAL_TYPE_BUY_CANCELED, Magic, COMMENT)

  • OpenOrder: Comprueba si ya existe una orden de compra-stop abierta con el mismo número mágico y comentario. Si existe, no se abrirá ninguna nueva orden de compra-stop.
  • OpenedDeal: verifica si ya existe una posición de compra activa o cancelada con el mismo número mágico y comentario. Si existe, no se colocará ninguna nueva orden de compra-stop.

Paso 6: Validar las propiedades de la orden de compra-stop

&& Valid_Order(ORDER_TYPE_BUY_STOP, myBuyStop.Get().Open, myBuyStop.Get().Stop, myBuyStop.Get().Take)

  • Valid_Order: Esto valida las propiedades de la orden de compra-stop (precio de apertura, stop-loss, take-profit) para garantizar que se ajusten a las reglas del símbolo (por ejemplo, nivel de stop mínimo). Si la validación pasa, se procede a verificar la orden de venta stop.

Paso 7: Verifique si existen órdenes de venta o acuerdos

&& !OpenOrder(ORDER_TYPE_SELL_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_SELL, Magic, COMMENT)
&& !OpenedDeal(DEAL_TYPE_SELL_CANCELED, Magic, COMMENT)

  • De manera similar a las verificaciones de compra-stop, estas condiciones verifican si ya existe una orden de venta-stop, una operación de venta activa o una operación de venta cancelada con el mismo número mágico y comentario. Si existe, la función evita colocar una nueva orden de venta stop.

Paso 8: Validar las propiedades de la orden Sell-Stop

&& Valid_Order(ORDER_TYPE_SELL_STOP, mySellStop.Get().Open, mySellStop.Get().Stop, mySellStop.Get().Take))

  • Valid_Order: Esto valida las propiedades de la orden sell-stop (precio de apertura, stop-loss, take-profit). Si la validación pasa, se procede a abrir órdenes tanto de compra-stop como de venta-stop.

Paso 9: Iterar a través de los tamaños de lote

for (double i = Volume(); i >= CSymbol.LotsMin() && (PositionsTotal() + Account.numOrders() + 2) < Account.LimitOrders(); i -= CSymbol.LotsMax())

  • Volume(): recupera el volumen comercial actual (tamaño del lote).
  • El bucle comienza con el volumen completo (i = Volume()) y disminuye el tamaño del lote (i -= CSymbol.LotsMax()) si excede el tamaño de lote máximo permitido (CSymbol.LotsMax()).
  • Asegura que el número total de posiciones abiertas y órdenes pendientes esté dentro del límite de la cuenta (Account.LimitOrders()).

El bucle garantiza que si el tamaño del lote excede el tamaño de lote máximo permitido del símbolo, dividirá las órdenes en varias más pequeñas.

Paso 10: Normalizar el tamaño del lote

NormalizeLotsize(i);

  • El tamaño del lote (i) se normaliza para cumplir con la precisión permitida del símbolo.

Paso 11: Intente abrir órdenes Buy-Stop y Sell-Stop

if(!Trade.BuyStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, myBuyStop.Get().Open,
                  CSymbol.GetSymbolName(), myBuyStop.Get().Stop, myBuyStop.Get().Take,
                  ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT)
   || !Trade.SellStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, mySellStop.Get().Open,
                     CSymbol.GetSymbolName(), mySellStop.Get().Stop, mySellStop.Get().Take,
                     ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT))

  • Trade.BuyStop: intenta colocar una orden de compra-stop con los siguientes parámetros:
    • Tamaño del lote: si el tamaño del lote actual (i) excede el máximo permitido (CSymbol.LotsMax()), coloca la orden con el tamaño de lote máximo permitido.
    • Precio de apertura: el precio de apertura calculado para la orden de compra-stop.
    • Nombre del símbolo: El nombre del instrumento comercial.
    • Stop-loss: El precio de stop-loss para la orden de compra-stop.
    • Take-profit: El precio de toma de ganancias para la orden de compra-stop.
    • Hora de expiración: se establece al final de la sesión comercial del símbolo para el día actual (CTS.SessionEnd()).
    • Comentario: Un comentario opcional para describir el pedido.
  • Trade.SellStop: De manera similar, intenta colocar una orden de venta stop con la misma lógica que la orden de compra stop.

Si alguna de las órdenes no se abre, la función devuelve falso.

Paso 12: Devolver resultado

return true;

  • Si tanto la orden de compra como la de venta se abren correctamente, la función devuelve verdadero. Si alguna parte del proceso falla, la función devuelve falso.

La función CloseTrades está diseñada para cerrar todas las posiciones abiertas que coincidan con un comentario específico (COMMENT_COMMON). 

//+------------------------------------------------------------------+
//|Function will attempt to close all trades depending on the        |
//|position comment                                                  |
//+------------------------------------------------------------------+
void CTradeManagement::CloseTrades(string COMMENT_COMMON)
  {
//--- Iterate through all open positions
   for(int i=0; i<PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      //--- Check if Position ticket is above zero
      if(ticket>0)
        {
         //--- Check if the Position's Symbol,Comment is correct
         if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()
            &&StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0)
           {
            //--- close trade.
            Trade.PositionClose(ticket);
           }
        }
     }
  }

Paso 1: Iterar a través de todas las posiciones abiertas

for (int i = 0; i < PositionsTotal(); i++)

  • PositionsTotal(): Devuelve el número total de posiciones abiertas.
  • Este bucle itera sobre todas las posiciones abiertas actualmente. La variable 'i' es el índice de la posición, que va desde 0 a PositionsTotal() - 1.

Paso 2: Obtener el número de ticket del puesto

ulong ticket = PositionGetTicket(i);

  • PositionGetTicket(i): recupera el número de ticket de la posición abierta en el índice 'i'. El número de ticket es un identificador único para cada posición.
  • El número de ticket se almacena en la variable ticket.

Paso 3: Verificar si el ticket es válido

if (ticket > 0)

  • Esto verifica si el número de ticket es mayor que cero, lo que garantiza que el ticket recuperado sea válido antes de continuar. Un número de ticket de 0 indicaría que no existe ninguna posición en el índice dado, lo que no sería un estado válido.

Paso 4: Validar el símbolo de posición y el comentario

if (PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() 
    && StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)

  • PositionGetString(POSITION_SYMBOL): recupera el nombre del símbolo de la posición en el índice 'i' (por ejemplo, par de divisas).
  • CSymbol.GetSymbolName(): recupera el nombre del símbolo asociado con el objeto CSymbol.
  • La primera condición verifica si el símbolo de la posición coincide con el símbolo gestionado por CSymbol.
  • PositionGetString(POSITION_COMMENT): recupera la cadena de comentarios adjunta a la posición abierta.
  • StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON): Comprueba si la cadena COMMENT_COMMON especificada está presente en el comentario de la posición.
    • Si el comentario contiene COMMENT_COMMON, la función devuelve un índice donde comienza la coincidencia.
    • La condición >= 0 garantiza que solo pasa si el comentario contiene la subcadena.

Esto garantiza que solo se seleccionen para el cierre las posiciones con el símbolo y comentario coincidentes.

Paso 5: Cerrar la operación

Trade.PositionClose(ticket);

  • Trade.PositionClose(ticket): intenta cerrar la posición abierta identificada por el número de ticket.
  • Si la posición coincide con las condiciones (símbolo correcto y contiene el comentario especificado), se cierra utilizando este método.


Conclusión

En este artículo, implementamos el código para abrir órdenes de stop y verificar la validez de las operaciones y órdenes antes de abrirlas. Creamos una función llamada FundamentalMode que maneja un modo de trading especial mediante la gestión de órdenes de stop opuestas. Además, se implementó una reducción de deslizamiento para las órdenes stop, mitigando el riesgo de deslizamiento de precios durante condiciones de mercado volátiles causadas por los comunicados de prensa.

Conclusiones clave:

  • Precisión en la ejecución: la clase de gestión comercial maneja todos los aspectos de la colocación, modificación y cierre de operaciones, garantizando que la ejecución de las operaciones sea precisa, incluso en mercados volátiles.
  • Ajustes en tiempo real: la capacidad de ajustar dinámicamente los stop-loss y los take-profits garantizan que el EA responda a los cambios del mercado en tiempo real, lo que permite una mejor gestión de riesgos.
  • Gestión de deslizamientos: al tener en cuenta los deslizamientos e implementar la lógica para ajustar los parámetros comerciales de forma dinámica, la clase Gestión de operaciones garantiza que las operaciones se ejecuten lo más cerca posible de las condiciones previstas, lo que reduce las pérdidas potenciales.

Gracias por tu tiempo, espero poder aportar más valor en el próximo artículo. :) 

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16169

Archivos adjuntos |
NewsTrading_Part5.zip (598.37 KB)
Kabelo Frans Mampa
Kabelo Frans Mampa | 7 nov 2024 en 13:30
Hamid Rabia #:

Hola Kabelo,

¡¡muy interesante!!

Por desgracia, el archivo zip(NewsTrading_Part5.zip )es el mismo que el del artículo 4(NewsTrading_Part4.zip )??

Hola Hamid, Desafortunadamente, no puedo poner todo el código en un artículo. La parte 5 de News tiene más código que la parte 4, pero no está completa. En la parte 6 se implementará el código restante para que todo funcione en conjunto.

Gracias por tu tiempo y comprensión.

Hamid Rabia
Hamid Rabia | 26 nov 2024 en 19:02

Hola,

¿Hay alguna forma sencilla de mostrar antes de las noticias el tipo de orden, compra, sel o NAN?

Gracias

Kabelo Frans Mampa
Kabelo Frans Mampa | 26 nov 2024 en 21:38
Hamid Rabia tipo de orden, comprar, sel o NAN?

Gracias

Hola Hamid Rabia, gracias por la sugerencia. Me aseguraré de implementar una solución y te enviaré un mensaje privado una vez que esté terminada.

Veeral10
Veeral10 | 3 dic 2024 en 22:27

Es un artículo fascinante.


Espero con impaciencia la sexta parte y ver el código completo.

Kabelo Frans Mampa
Kabelo Frans Mampa | 4 dic 2024 en 13:59
Veeral10 #:

Es un artículo fascinante el que has creado.


Espero con impaciencia la sexta parte y ver el código completo.

Hola Veeral10, ¡gracias por tus amables palabras!

Redes neuronales en el trading: Modelos con transformada de wavelet y atención multitarea Redes neuronales en el trading: Modelos con transformada de wavelet y atención multitarea
Le proponemos familiarizarse con un framework que combina la transformada de wavelet y el modelo multitarea Self-Attention con el objetivo de mejorar la capacidad de respuesta y la precisión de las previsiones en condiciones de mercado volátiles. La transformada de wavelet descompone los rendimientos de los activos en frecuencias altas y bajas, captando cuidadosamente las tendencias del mercado a largo plazo y las fluctuaciones a corto plazo.
Redes neuronales en el trading: Framework comercial híbrido con codificación predictiva (Final) Redes neuronales en el trading: Framework comercial híbrido con codificación predictiva (Final)
Continuamos nuestro análisis del sistema comercial híbrido StockFormer, que combina codificación predictiva y algoritmos de aprendizaje por refuerzo para el análisis de series temporales financieras. El sistema se basa en tres ramas del Transformer con un mecanismo Diversified Multi-Head Attention (DMH-Attn) que permite identificar patrones complejos y relaciones entre activos. Ya nos hemos familiarizado con los aspectos teóricos del framework e implementado los mecanismos de DMH-Attn, así que hoy hablaremos sobre la arquitectura de los modelos y su entrenamiento.
Ingeniería de características con Python y MQL5 (Parte II): El ángulo del precio Ingeniería de características con Python y MQL5 (Parte II): El ángulo del precio
Hay muchas publicaciones en el foro MQL5 pidiendo ayuda para calcular la pendiente de los cambios de precios. Este artículo demostrará una forma posible de calcular el ángulo formado por los cambios de precio en cualquier mercado en el que desee operar. Además, responderemos si vale la pena invertir el esfuerzo y el tiempo extra para diseñar esta nueva característica. Exploraremos si la pendiente del precio puede mejorar la precisión de nuestro modelo de IA al pronosticar el par USDZAR en M1.
Análisis de múltiples símbolos con Python y MQL5 (Parte II): Análisis de componentes principales para la optimización de carteras Análisis de múltiples símbolos con Python y MQL5 (Parte II): Análisis de componentes principales para la optimización de carteras
La gestión del riesgo de las cuentas de trading es un reto para todos los operadores. ¿Cómo podemos desarrollar aplicaciones de trading que aprendan dinámicamente los modos de riesgo alto, medio y bajo para diversos símbolos en MetaTrader 5? Al utilizar el Análisis de Componentes Principales (Principal Components Analysis, PCA), obtenemos un mejor control sobre la variación de la cartera. Demostraré cómo crear aplicaciones que aprendan estos tres modos de riesgo a partir de datos de mercado obtenidos de MetaTrader 5.