
Operar con noticias de manera sencilla (Parte 5): Ejecución de operaciones (II)
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?
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).
- 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.
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?
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).
- 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.
//+------------------------------------------------------------------+ //|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.
//+------------------------------------------------------------------+ //|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:
- Verifique cada posición abierta.
- Verifique si los valores de stop-loss y take-profit de la posición coinciden con los valores esperados.
- 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:
- Validar los parámetros comerciales (precio, SL y TP) para posiciones de compra o venta.
- 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:
- 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.
- 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:
- Calcula el precio de apertura para la orden de compra-stop.
- Calcula y establece los precios de stop-loss y take-profit en relación con el precio de apertura.
- 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:
- El tamaño del lote es mayor o igual al tamaño de lote mínimo (CSymbol.LotsMin()).
- 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:
- 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).
- Precio de apertura: El precio al que debe ejecutarse la orden de compra-stop.
- Nombre del símbolo: el nombre del símbolo comercial (par de divisas, etc.).
- Stop-loss: el precio de stop-loss calculado para la orden.
- Take-profit: el precio de take-profit calculado para la orden.
- 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()).
- 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
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
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.
Hola,
¿Hay alguna forma sencilla de mostrar antes de las noticias el tipo de orden, compra, sel o NAN?
Gracias
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.
Es un artículo fascinante.
Espero con impaciencia la sexta parte y ver el código completo.
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!