Asesor Experto Multiplataforma: Stops Personalizados, Ausencia de Pérdidas y Trailing
Índice
- Introducción
- Niveles stop personalizados
- Cambio de los niveles stop
- Ausencia de pérdidas
- Trailing-stop
- Niveles de trailing del take-profit
- Implementación
- CTrails (Contenedor)
- Expandiendo CTrail
- Ejemplos
Introducción
En el artículo anterior se mostró cómo usar la clase personalizada CStop para colocar stop-loss y take-profit para una transacción en el experto comercial multiplataforma. Los niveles indicados pueden ser expresados en pips o en puntos, pero no para todos los asesores, ni mucho menos. En muchos expertos, el stop-loss y take-profit deben expresarse, no en la distancia con respecto al precio de entrada, sino en la distancia con respecto al precio en el gráfico (normalmente como resultado de una serie de cálculos).
En este artículo vamos a analizar cómo usar la clase CStop para definir los niveles de stop-loss y take-profit expresados en el precio en el gráfico. De esta forma, mostraremos cómo se pueden cambiar estos niveles usando una biblioteca especial. Además, se introduce la nueva clase CTrail, que se parece mucho a la implementación de los niveles stop personalizados. Sin embargo, a diferencia de los stops, CTrail se usa para cambiar consecutivamente los niveles stop a lo largo del tiempo.
Niveles stop personalizados
Bien, ya sabemos cómo implementar en un asesor multiplataforma los stop-loss/take-profit expresados en puntos o pips. Pero hay estrategias que exigen el cálculo dinámico de los niveles stop, normalmente basándose en los datos de las series temporales (OHLC) y según los datos de los indicadores técnicos.
El cálculo dinámico del stop-loss y el take-profit se puede ajustar en la clase CStop a través de sus dos métodos, StopLossCustom y TakeProfitCustom. Son sobrecargas de los métodos ya existentes en esta clase. Aquí tenemos su código:
bool CStopBase::StopLossCustom(void) { return m_stoploss==0; } bool CStopBase::TakeProfitCustom(void) { return m_takeprofit==0; }
El programador puede ampliarlos para conseguir el stop-loss o take-profit necesario para cualquier ejemplar de CStop.
Más abajo se enumeran los métodos alternativos que retornan el valor del tipo bool:
bool CStopBase::StopLossCustom(void) { return m_stoploss==0; } bool CStopBase::TakeProfitCustom(void) { return m_takeprofit==0; }
Ambas funciones retornan true, si un miembro aparte de la clase (o m_stoploss, o m_takeprofit) tiene un valor cero. Hablaremos de su valor más abajo.
Vamos a analizar el escenario en el que CStop calcula el nivel stop. Para describir los pasos siguientes, supondremos que estamos trabajando con un stop-loss (m_stoploss).
- Si m_stoploss es igual 0, usaremos para el cálculo StopLossCustom.
- Si m_stoploss no es igual a 0, lo usaremos precisamente a él para calcular el stop-loss actual de la transacción con respecto al precio de entrada.
La misma secuencia de acciones se usa para calcular el take-profit con la ayuda del método TakeProfitCustom y el miembro de la clase m_takeprofit.
Estos cuatro métodos pueden ser localizados en dos lugares, dependiendo de para qué se usen. Para los niveles stop principales, el método se llama desde el gestor de órdenes (COrderManager). Para el resto de los stops, desde el ejemplar de cada orden.
En los casos en los que los principales niveles stop son enviados al bróker junto con la solicitud comercial, los asesores necesitan esta información en el momento de enviar la solicitud comercial, y no después de que la transacción inicial haya sido enviada. Esto es cierto para los stops de bróker en MetaTrader 4 y del modo cobertura de MetaTrader 5. En estos modos, la información del stop-loss y el take-profit deberá incluirse en la solicitud comercial para la función OrderSend (MQL4, MQL5), y estos niveles de stop se aplicarán solo a la principal transacción comercial.
Dentro del método TradeOpen del gestor de órdenes se encuentra la llamada de los dos métodos CStop. Más abajo se muestra el código para la versión de MQL4:
bool COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type,double price,bool in_points=true) { int trades_total = TradesTotal(); int orders_total = OrdersTotal(); m_symbol=m_symbol_man.Get(symbol); if(!CheckPointer(m_symbol)) return false; if(!IsPositionAllowed(type)) return true; if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0)) { ENUM_ORDER_TYPE ordertype=type; if(in_points) price=PriceCalculate(type); double sl=0,tp=0; if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { sl = m_main_stop.StopLossCustom()?m_main_stop.StopLossCustom(symbol,type,price):m_main_stop.StopLossCalculate(symbol,type,price); tp = m_main_stop.TakeProfitCustom()?m_main_stop.TakeProfitCustom(symbol,type,price):m_main_stop.TakeProfitCalculate(symbol,type,price); } double lotsize=LotSizeCalculate(price,type,sl); if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { if (!m_main_stop.Broker()) { sl = 0; tp = 0; } } int ticket=(int)SendOrder(type,lotsize,price,sl,tp); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET)) { COrder *order = m_orders.NewOrder(OrderTicket(),OrderSymbol(),OrderMagicNumber(), (ENUM_ORDER_TYPE)::OrderType(),::OrderLots(),::OrderOpenPrice()); if (CheckPointer(order)) { LatestOrder(GetPointer(order)); return true; } } } } return false; }
Más abajo se muestra el código para la versión de MQL5:
bool COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type,double price,bool in_points=true) { bool ret=false; double lotsize=0.0; int trades_total =TradesTotal(); int orders_total = OrdersTotal(); m_symbol=m_symbol_man.Get(symbol); if(!IsPositionAllowed(type)) return true; if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0)) { if(in_points) price=PriceCalculate(type); double sl=0.0,tp=0.0; if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { sl = m_main_stop.StopLossCustom()?m_main_stop.StopLossCustom(symbol,type,price):m_main_stop.StopLossCalculate(symbol,type,price); tp = m_main_stop.TakeProfitCustom()?m_main_stop.TakeProfitCustom(symbol,type,price):m_main_stop.TakeProfitCalculate(symbol,type,price); } lotsize=LotSizeCalculate(price,type,m_main_stop==NULL?0:m_main_stop.StopLossCalculate(symbol,type,price)); if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { if (!m_main_stop.Broker() || !IsHedging()) { sl = 0; tp = 0; } } ret=SendOrder(type,lotsize,price,sl,tp); if(ret) { COrder *order = m_orders.NewOrder((int)m_trade.ResultOrder(),m_trade.RequestSymbol(),(int)m_trade.RequestMagic(), m_trade.RequestType(),m_trade.ResultVolume(),m_trade.ResultPrice()); if (CheckPointer(order)) { LatestOrder(GetPointer(order)); return true; } } } return ret; }
A pesar de que la implementación de TradeOpen se da aparte, podemos encontrar un denominador común en el modo en que las dos versiones del método calculan los principales niveles stop. Primero se calculan el stop-loss y el take-profit (sl y tp, respectivamente) teniendo en cuenta el nivel stop principal. Esto se hace independientemente de si ha sido necesario o no un stop-loss y un take-profit en la actual solicitud comercial inicial, puesto que la información también puede ser necesaria para calcular el volumen del lote (en el caso de la gestión del dinero).
Después de calcular el volumen del lote, en ciertos casos hay que resetear el nivel stop a 0. Así, por ejemplo, en MQL4 las variables sl y tp se igualan a 0, si el stop principal no es el del bróker.
if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { if (!m_main_stop.Broker()) { sl = 0; tp = 0; } }
En MQL5 intentaremos evitar el uso de stop-loss y take-profit en el modo de compensación, puesto que se aplicarán a todo el volumen de la posición, y no solo al volumen de la transacción introducida. Por eso las variables se establecen como 0, si el stop no es el del bróker o la plataforma se encuentra en el modo de compensación.
if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { if (!m_main_stop.Broker() || !IsHedging()) { sl = 0; tp = 0; } }
Como discutimos en el artículo anterior, en el modo de compensación el asesor transforma los stops del bróker en stops basados en órdenes pendientes, así que desaparece la necesidad de enviar stop-loss y take-profit en la solicitud comercial a la transacción inicial.
Para las transacciones con varios niveles de stop-loss y take-profit, estos niveles se procesan en cuanto se procese con éxito la transacción principal. En los niveles stop no tiene sentido si la transacción principal aún no se ha introducido en el mercado, por eso deberá ser procesada en primer lugar. Estos niveles stop adicionales se generan durante la inicialización del ejemplar COrder, disponible desde el método CreateStops (a través de COrderBase, puesto que la implementación en MQL4 y MQL5 es la misma):
void COrderBase::CreateStops(CStops *stops) { if(!CheckPointer(stops)) return; if(stops.Total()>0) { for(int i=0;i<stops.Total();i++) { CStop *stop=stops.At(i); if(CheckPointer(stop)==POINTER_INVALID) continue; m_order_stops.NewOrderStop(GetPointer(this),stop); } } }
Como se muestra en este código, el ejemplar de COrderStop se crea para cada ejemplar de CStop encontrado por el experto. Se trata de niveles stop adicionales, sin embargo, se crean solo después de que la transacción principal se haya abierto con éxito en el mercado.
Cambio de los niveles stop
CStop tiene su propio ejemplar por separado de CTradeManager, y eso significa que CExpertTradeX, también (ampliación de la clase CExpertTrade de la Biblioteca Estándar MQL5). Este ejemplar es independiente del ejemplar que se encuentra en el gestor de órdenes (COrderManager), usado exclusivamente para procesar las entradas en las transacciones principales.
Es decir, el cambio de los niveles stop se gestiona no con СOrderManager, sino con un ejemplar propio de CStop. Sin embargo, sabemos que los niveles stop para cada orden deberán cambiar por separado. Eso significa que el cambio deberá llamarse desde la transacción, que a su vez deberá cambiar por sí misma, es decir, desde el ejemplar COrder, que presenta esta transacción.
El monitoreo de los niveles stop comienza desde el método CheckStops de la clase COrder, cuyo código se muestra más abajo:
void COrderBase::CheckStops(void) { m_order_stops.Check(m_volume); }
Aquí sencillamente se llama al método Check de uno de los miembros de la clase, que constituye un ejemplar COrderStops. Como sabemos por el anterior artículo, СOrderStops es un contenedor de punteros a los ejemplares COrderStop.
Vamos a analizar el método Check de la clase COrderStops. Se muestra en el siguiente fragmento de código:
COrderStopsBase::Check(double &volume) { if(!Active()) return; for(int i=0;i<Total();i++) { COrderStop *order_stop=(COrderStop *)At(i); if(CheckPointer(order_stop)) { if(order_stop.IsClosed()) continue; order_stop.CheckTrailing(); order_stop.Update(); order_stop.Check(volume); if(!CheckNewTicket(order_stop)) return; } } }
Como podemos ver por el código, el método Check ejecuta como mínimo cinco acciones sobre cada ejemplar de COrderStop.
- Comprueba si está ya cerrado el ejemplar COrderStop aparte (método IsClosed). Esto es necesario para definir las acciones posteriores sobre el nivel stop: si está cerrado, en lo sucesivo el asesor no debería comprobarlo.
- Acutualiza los niveles stop basándose en los ejemplares de trailing stop designados para él, si los hay (método CheckTrailing). Esta acción se refiere solo al trailing del stop-loss o del take-profit.
- Actualiza el nivel stop (método Update). El método se usa cuando el nivel stop se ha cambiado, no dentro del asesor, sino desde el exterior (por ejemplo, al arrastrar la línea del stop virtual).
- Comprueba si está disponible el precio de un nivel stop determinado (método Check).
- Comprueba si el nuevo ticket supone una transacción. La función se usa solo en MetaTrader 4, porque solo en esta plataforma el experto cambia la ID de la posición al darse su cierre parcial. La implementación para MetaTrader 5 en este caso es más sencilla, porque esta plataforma guarda toda la historia de desarrollo de la transacción.
El ejemplar COrderStop presenta el nivel stop de la transacción como definido en CStop. Esto significa que arrastrar cualquier nivel stop provocará finalmente el cambio del ejemplar de este objeto de clase.
El método CheckTrailing se muestra más abajo:
муbool COrderStopBase::CheckTrailing(void) { if(!CheckPointer(m_stop) || m_order.IsClosed() || m_order.IsSuspended() || (m_stoploss_closed && m_takeprofit_closed)) return false; double stoploss=0,takeprofit=0; string symbol=m_order.Symbol(); ENUM_ORDER_TYPE type=m_order.OrderType(); double price=m_order.Price(); double sl = StopLoss(); double tp = TakeProfit(); if(!m_stoploss_closed) stoploss=m_stop.CheckTrailing(symbol,type,price,sl,TRAIL_TARGET_STOPLOSS); if(!m_takeprofit_closed) takeprofit=m_stop.CheckTrailing(symbol,type,price,tp,TRAIL_TARGET_TAKEPROFIT); if(!IsStopLossValid(stoploss)) stoploss=0; if(!IsTakeProfitValid(takeprofit)) takeprofit=0; return Modify(stoploss,takeprofit); }
En este código vemos que se sigue la idea principal: no se puede cambiar un nivel stop si ya está cerrado. Si la orden para el nivel stop todavía está activa, se llama al método CheckTrailing del ejemplar CStop correspondiente.
Veamos el método CheckTrailing de la clase CStop:
double CStopBase::CheckTrailing(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!CheckPointer(m_trails)) return 0; return m_trails.Check(symbol,type,entry_price,price,mode); }
Aquí CStop llama al método Check de uno de sus miembros de clase, m_trails. m_trails es el contenedor para los punteros a los objetos de trailing. El código del método se muestra más abajo:
double CTrailsBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!Active()) return 0; double val=0.0,ret=0.0; for(int i=0;i<Total();i++) { CTrail *trail=At(i); if(!CheckPointer(trail)) continue; if(!trail.Active()) continue; int trail_target=trail.TrailTarget(); if(mode!=trail_target) continue; val=trail.Check(symbol,type,entry_price,price,mode); if((type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_TAKEPROFIT)) { if(val>ret || ret==0.0) ret=val; } else if((type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_TAKEPROFIT)) { if(val<ret || ret==0.0) ret=val; } } return ret; }
En este momento, basta con comprender que el contenedor CTrails itera en sus ejemplares CTrail y retorna el valor final. Este valor se expresa en precio en el gráfico del símbolo elegido, y por eso tiene el tipo double. Precisamente este será el nuevo valor del stop-loss o el take-profit después de su desplazamiento exitoso.
Ahora vamos a volver al método CheckTrailing de la clase COrderStop, porque precisamente desde él se ejecuta realmente la llamada para el cambio del stop-loss:
муbool COrderStopBase::CheckTrailing(void) { if(!CheckPointer(m_stop) || m_order.IsClosed() || m_order.IsSuspended() || (m_stoploss_closed && m_takeprofit_closed)) return false; double stoploss=0,takeprofit=0; string symbol=m_order.Symbol(); ENUM_ORDER_TYPE type=m_order.OrderType(); double price=m_order.Price(); double sl = StopLoss(); double tp = TakeProfit(); if(!m_stoploss_closed) stoploss=m_stop.CheckTrailing(symbol,type,price,sl,TRAIL_TARGET_STOPLOSS); if(!m_takeprofit_closed) takeprofit=m_stop.CheckTrailing(symbol,type,price,tp,TRAIL_TARGET_TAKEPROFIT); if(!IsStopLossValid(stoploss)) stoploss=0; if(!IsTakeProfitValid(takeprofit)) takeprofit=0; return Modify(stoploss,takeprofit); }
El método retorna un valor del tipo bool. Este supone el resultado del cambio del nivel stop con el método Modify de la clase COrderStop (retorna true en el caso de éxito). Pero antes de enviar la solicitud de cambio, el método comprueba si son válidos aún el stop-loss y take-profit. Para ello, se usan los métodos IsStopLossValid y IsTakeProfitValid. Si el valor propuesto no es válido, se resetea.
bool COrderStopBase::IsStopLossValid(const double stoploss) const { return stoploss!=StopLoss(); } bool COrderStopBase::IsTakeProfitValid(const double takeprofit) const { return takeprofit!=TakeProfit(); }
En el código mostrado más arriba, tanto para el stop-loss, como para el take-profit, los valores propuestos no deberán ser iguales al actual.
Después de valorar el stop-loss y el take-profit, se llama al método Modify de la clase COrderStop. Se muestra más abajo:
bool COrderStopBase::Modify(const double stoploss,const double takeprofit) { bool stoploss_modified=false,takeprofit_modified=false; if(stoploss>0 && takeprofit>0) { if(ModifyStops(stoploss,takeprofit)) { stoploss_modified=true; takeprofit_modified=true; } } else if(stoploss>0 && takeprofit==0) stoploss_modified=ModifyStopLoss(stoploss); else if(takeprofit>0 && stoploss==0) takeprofit_modified=ModifyTakeProfit(takeprofit); return stoploss_modified || takeprofit_modified; }
Hasta el momento actual, se ejecutan tres tipos de operaciones, dependiendo de los valores de stop-loss y take-profit. Normalmente, el cambio puede realizarse con una acción, como en el caso de los stops del bróker.
Pero puede ser de otras forma. Cada nivel stop virtual y nivel stop basado en órdenes pendientes debe procesarse individualmente: por una parte, el stop-loss, y por otra, el take-profit. El código del cambio de ambos valores se muestra en los métodos siguientes:
bool COrderStopBase::ModifyStops(const double stoploss,const double takeprofit) { return ModifyStopLoss(stoploss) && ModifyTakeProfit(takeprofit); }
La tarea del método ModifyStops es llamar a los otros dos métodos. Desde ese momento, comienza la implementación por separado. Se basa en dos importantes factores: el tipo de compilador (MQL4 o MQL5) y el tipo de stop (del bróker, pendiente o virtual). Si estamos tratando con un stop del bróker, el resultado será una solicitud comercial de cambio de la posición principal. En el caso de los stops basados en las órdenes pendientes, el experto tendrá que mover el precio de apertura de la orden pendiente. Si el stop es virtual, el asesor simplemente actualizará sus datos internos referentes al nivel stop.
COrderStop no tiene objetos comerciales (o punteros a ellos) como miembros de clase, y por eso, según su naturaleza, no es capaz de cambiar los propios niveles stop. Para hacerlo, sigue necesitando el ejemplar CStop. Por consiguiente, el resultado de cualquier cambio del nivel stop será la llamada del método en CStop.
Ausencia de pérdidas
Ausencia de pérdidas — es el punto en el que los ingresos y los gastos son iguales. En el trading, los ingresos son los beneficios actuales de una transacción, y los gastos, son el spread y/o la comisión. En otras palabras, en el punto de ausencia de pérdidas la transacción se cierra con un beneficio/pérdida igual a cero. En el caso de los creadores de mercado, que cobran comisión, el punto de ausencia de pérdidas se corresponde normalmente con el precio de entrada en la transacción.
En los asesores, la transacción alcanza la ausencia de pérdidas cuando el experto coloca un stop-loss en el punto del precio de entrada. Cuando la transacción alcanza este nivel, su riesgo máximo se hace igual a cero. Sin embargo, conviene recordar que aún puede ser cerrada por otros motivos: por ejemplo, por una señal de salida, normalmente en zona de beneficios.
Al trabajar con ausencia de pérdidas, el asesor tiene como mínimo dos limitaciones:
- La dinámica del stop-loss. El stop-loss de la transacción siempre debe estar por debajo del actual precio de mercado para las posiciones largas, y por encima para las cortas. Teniendo en cuenta esta limitación, el asesor puede desplazar el stop-loss al punto de ausencia de pérdidas solo si la transacción se encuentra en el estado de beneficio no registrado.
Distancia mínima con respecto al precio de apertura actual para colocar el stop-loss, designado por el bróker. La exigencia de una distancia mínima entre niveles stop se aplica también al cambiar el stop-loss de la transacción, no solo al abrirla. De esta forma, en ocasiones se puede conseguir colocar el stop-loss de la transacción al nivel de la ausencia de pérdidas en cuanto tiene lugar la entrada en el mercado. El precio deberá desplazarse a una distancia definida para que el asesor pueda desplazar el stop-loss en la dirección del punto de ausencia de pérdidas.
Teniendo en cuenta estas limitaciones, en el desarrollo de la función para la ausencia de pérdidas deberemos tener en cuenta como mínimo dos factores:
- El precio de activación.
- El nuevo stop-loss al darse la activación.
El precio de activación o precio disparador, es el precio que deberá ser alcanzado por el mercado para que el asesor pueda desplazar el stop-loss de la transacción al punto de ausencia de pérdidas. El precio se define dependiendo del desarrollo de la posición, puesto que las transacciones tienen diferentes precios de entrada.
Después de la activación, el asesor debe desplazar el stop-loss al punto de ausencia de pérdidas. Normalmente (¡pero no siempre!), coincidirá con el precio de apertura de la posición. Para los brókeres que cobran comisión, el punto de ausencia de pérdidas estará por encima del precio de entrada para las posiciones largas y por debajo para las posiciones cortas.
Estos valores normalmente se calculan teniendo en cuenta los valores, por ejemplo, del precio de mercado actual y el precio de entrada en la transacción.
En la figura se muestra el esquema de cálculo del punto de ausencia de pérdidas descrito más arriba. Se calculan tres valores: la activación, la desactivación y el nuevo nivel stop. Si el nivel del precio actual está por encima o es igual al precio mínimo necesario para la etapa inicial (colocación del nivel de stop loss en el nivel de ausencia de pérdidas), entonces el nivel stop calculado anteriormente se usará como nuevo nivel stop preliminar. En el caso contrario, el resultado será 0. Como siguiente paso, hay que comprobar si el nuevo nivel stop se encuentra dentro de los límites del nivel stop del bróker. La comprobación siempre retorna true si la condición anterior se ha cumplido. De esta forma, como información final, se retornará el nivel stop calculado.
Trailing-stop
El trailing stop se puede analizar como un caso particular de paso a ausencia de pérdidas. Mientras que solo suele haber un punto de ausencia de pérdidas, el trailing stop se puede alcanzar varias veces. Después de activar el trailing, normalmente se usa durante toda la vida de la posición. Al desarrollar el trailing-stop, hay que tener en cuenta como mínimo tres factores:
- El precio de activación.
- El nuevo stop-loss al darse la activación.
- La frecuencia del trailing.
La función de trailing-stop tiene dos variables comunes con la ausencia de pérdidas. Sin embargo, a diferencia de la ausencia de pérdidas, el precio al activarse el trailing-stop puede encontrarse en el punto donde la transacción se halla en el estado de pérdida no registrada o “out of the money”. Mientras la transacción permanece con pérdidas, es posible cambiar su stop-loss, y también seguir su dinámica. Cuando esto sucede, el hecho de alcanzar el stop-loss actual va a provocar pérdidas de todas formas, puesto que sigue sin satisfacer las condiciones para lograr la ausencia de pérdidas o incluso obtener ganancias.
El tercer factor es la frecuencia del trailing-stop, o "salto". Este define la distancia (en puntos o pips) desde el stop-loss actual al siguiente, tras el primer desplazamiento del stop-loss. Es casi análogo a la primera variable: el precio de activación. Sin embargo, a diferencia del precio de activación, el precio calculado según esta tercera variable se cambia con cada salto del trailing.
En algunos trailing-stop existe un cuarto factor: el precio de desactivación. Al alcanzar este punto, el asesor deja de mover el stop-loss. Esto se calcula también por separado para cada transacción.
En la figura de abajo se muestra el esquema de desplazamiento del stop-loss de la transacción. Las diferencias con respecto a la ilustración son pocas.
Si el precio está por encima del salto inicial de trailing, entonces es muy probable que el nivel stop salga de los marcos de este salto incial. En el caso contrario, usará este punto inicial de trailing como valor para el nuevo nivel stop para la pasada actual. Si el nivel stop aún no ha salido de los límites del precio de activación, sencillamente seguirá a la parte restante del cálculo de la ausencia de pérdidas (el precio de desactivación no se aplica a la ausencia de pérdidas).
Niveles de trailing del take-profit
El trailing, o trailing stop, normalmente presupone el arrastre de un stop-loss, pero es posible también el trailing del take-profit de una transacción. En este caso, si el precio se acerca al nivel del take-profit, se irá desplazando más allá con suavidad (lo opuesto al trailing stop). Teóricamente, el precio nunca debe alcanzar al take-profit sometido a trailing. Sin embargo, esto es a veces posible: por ejemplo, cuando el precio da saltos imprevistos y cuando se dan interrupciones de precio. En pocas palabras: cualquier condición que cause que el mercado se mueva más deprisa de lo que pueda reaccionar el asesor.
Para que el trailing del take-profit sea significativo, se deberán sasisfacer varias condiciones:
- El precio de activación deberá encontrarse dentro del nivel actual de take-profit. Si el precio de activación se sale de los límites del nivel actual de take-profit, el precio del mercado deberá romper este nivel antes que el precio de activación. Y, en cuanto el precio del take-profit sea alcanzado, la transacción dejará el mercado mucho antes de que pueda comenzar el trailing de esta posición. Para las posiciones largas, el precio de activación deberá estar por debajo del nivel de take-profit. Para las posiciones cortas, el precio de activación deberá estar por encima del take-profit.
- El precio con el que tendrá lugar la siguiente activación del trailing deberá encontrarse dentro de los límites del nivel actual del take-profit. Aquí la esencia es la misma que en la primera condición, pero con la diferencia de que la segunda condición se aplicará a las activaciones segunda y siguientes.
- En un escenario ideal, el trailing debe ser lo menos frecuente posible. La distancia entre niveles (salto) deberá ser lo suficientemente amplia. Una alta frecuencia en el trailing del take-profit reduce las posibilidades de que el mercado alcance el nivel de take-profit, porque este es desplazado del precio actual en cada etapa del trailing.
Implementación
CTrailBase, que sirve como clase básica para CTrail, se muestra en el código inferior:
class CTrailBase : public CObject { protected: bool m_active; ENUM_TRAIL_TARGET m_target; double m_trail; double m_start; double m_end; double m_step; CSymbolManager *m_symbol_man; CSymbolInfo *m_symbol; CEventAggregator *m_event_man; CObject *m_container; public: CTrailBase(void); ~CTrailBase(void); virtual int Type(void) const {return CLASS_TYPE_TRAIL;} //--- inicialización virtual bool Init(CSymbolManager*,CEventAggregator*); virtual CObject *GetContainer(void); virtual void SetContainer(CObject*); virtual bool Validate(void) const; //--- métodos de establecimiento y obtención bool Active(void) const; void Active(const bool); double End(void) const; void End(const double); void Set(const double,const double,const double,const double); double Start(void) const; void Start(const double); double Step(void) const; void Step(const double); double Trail(void) const; void Trail(const double); int TrailTarget(void) const; void TrailTarget(const ENUM_TRAIL_TARGET); //--- comprobación virtual double Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET); protected: //--- price calculation virtual double ActivationPrice(const ENUM_ORDER_TYPE,const double); virtual double DeactivationPrice(const ENUM_ORDER_TYPE,const double); virtual double Price(const ENUM_ORDER_TYPE); virtual bool Refresh(const string); };El método Set nos permite cambiar los ajustes del ejemplar CTrails. Funciona como un constructor de clase normal. Si es necesario, también podemos declarar un constructor personalizado, que llame este método:
void CTrailBase::Set(const double trail,const double st,const double step=1,const double end=0) { m_trail=trail; m_start=st; m_end=end; m_step=step; }
CTrail en sus cálculos se apoya en los datos del mercado. De esta forma, tiene un ejemplar del gestor de símbolos (CSymbolManager) como uno de los miembros de la clase. Antes de realizar los cálculos posteriores, es necesario actualizar el símbolo.
bool CTrailBase::Refresh(const string symbol) { if(!CheckPointer(m_symbol) || StringCompare(m_symbol.Name(),symbol)!=0) m_symbol=m_symbol_man.Get(symbol); return CheckPointer(m_symbol); }
El precio de activación es el precio que provocará el movimiento inicial del stop-loss o el take-profit mediante un ejemplar de CTrail. Los parámetros iniciales y finales, así como el salto, se expresan en puntos, por eso la clase debe calcular el precio de activación en forma de precio en el gráfico. Exactamente igual se calculan los otros precios:
double CTrailBase::ActivationPrice(const ENUM_ORDER_TYPE type,const double entry_price) { if(type==ORDER_TYPE_BUY) return entry_price+m_start*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) return entry_price-m_start*m_symbol.Point(); return 0; }
El precio de desactivación se calcula según el mismo plan, pero esta vez se usa el miembro de la clase m_end class.
double CTrailBase::DeactivationPrice(const ENUM_ORDER_TYPE type,const double entry_price) { if(type==ORDER_TYPE_BUY) return m_end==0?0:entry_price+m_end*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) return m_end==0?0:entry_price-m_end*m_symbol.Point(); return 0; }
Si el precio de desactivación designado es 0, significa que esta función está desactivada. El trailing se aplicará hasta que la transacción principal sea cerrada.
El método Price calcula el nuevo valor del stop-loss o el take-profit si las condiciones en el momento actual permiten el trailing del stop-loss:
double CTrailBase::Price(const ENUM_ORDER_TYPE type) { if(type==ORDER_TYPE_BUY) { if(m_target==TRAIL_TARGET_STOPLOSS) return m_symbol.Bid()-m_trail*m_symbol.Point(); else if(m_target==TRAIL_TARGET_TAKEPROFIT) return m_symbol.Ask()+m_trail*m_symbol.Point(); } else if(type==ORDER_TYPE_SELL) { if(m_target==TRAIL_TARGET_STOPLOSS) return m_symbol.Ask()+m_trail*m_symbol.Point(); else if(m_target==TRAIL_TARGET_TAKEPROFIT) return m_symbol.Bid()-m_trail*m_symbol.Point(); } return 0; }
Ahora vamos a hablar del método Check. Un ejemplar concreto de СTrail puede seguir un stop-loss, o bien un take-profit. Por eso, en CTrail necesitamos la posibilidad de indicar cuál precisamente de los stops de la transacción se está siguiendo. Para ello nos hará falta la enumeración ENUM_TRAIL_TARGET. Su declaración se encuentra en MQLx\Common\Enum\ENUM_TRAIL_TARGET.mqh, y el código se muestra más abajo:
enum ENUM_TRAIL_TARGET
{
TRAIL_TARGET_STOPLOSS,
TRAIL_TARGET_TAKEPROFIT
};
Vamos a analizar ahora el método Check. A diferencia de los otros métodos de esta clase que hemos discutido hasta ahora, este es público. El método se llama cuando hay que comprobar si se ha actualizado el nivel de trailing-stop.
double CTrailBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!Active()) return 0; if(!Refresh(symbol)) return 0; if(m_start==0 || m_trail==0) return 0; double next_stop=0.0,activation=0.0,deactivation=0.0,new_price=0.0,point=m_symbol.Point(); activation=ActivationPrice(type,entry_price); deactivation=DeactivationPrice(type,entry_price); new_price=Price(type); if(type==ORDER_TYPE_BUY) { if (m_target==TRAIL_TARGET_STOPLOSS) { if(m_step>0 && (activation==0.0 || price>=activation-m_trail*point) && (new_price>price+m_step*point)) next_stop=new_price; else next_stop=activation-m_trail*point; if(next_stop<price) next_stop=price; if((deactivation==0) || (deactivation>0 && next_stop>=deactivation && next_stop>0.0)) if(next_stop<=new_price) return next_stop; } else if (m_target==TRAIL_TARGET_TAKEPROFIT) { if(m_step>0 && ( activation==0.0 || price>=activation) && (new_price>price+m_step*point)) next_stop=new_price; else next_stop=activation+m_trail*point; if(next_stop<price) next_stop=price; if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0)) if(next_stop>=new_price) return next_stop; } } if(type==ORDER_TYPE_SELL) { if (m_target==TRAIL_TARGET_STOPLOSS) { if(m_step>0 && (activation==0.0 || price<=activation+m_trail*point) && (new_price<price-m_step*point)) next_stop=new_price; else next_stop=activation+m_trail*point; if(next_stop>price) next_stop=price; if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0)) if(next_stop>=new_price) return next_stop; } else if (m_target==TRAIL_TARGET_TAKEPROFIT) { if(m_step>0 && (activation==0.0 || price<=activation) && (new_price<price-m_step*point)) next_stop=new_price; else next_stop=activation-m_trail*point; if(next_stop>price) next_stop=price; if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0)) if(next_stop<=new_price) return next_stop; } } return 0; }
Aquí podemos ver que el cálculo del trailing del stop-loss y el take-profit son diferentes. Para el cálculo del stop-loss querríamos que este se acercase más y más al precio del mercado con cada etapa el trailing. Con respecto al valor del take-profit, querríamos lo contrario, alejarlo del precio actual a una distancia determinada (en puntos o pips) con cada salto del trailing.
CTrails (Contenedor)
CTrail también tiene el contenedor CTrails. Su definición se muestra en el código de más abajo:
class CTrailsBase : public CArrayObj { protected: bool m_active; CEventAggregator *m_event_man; CStop *m_stop; public: CTrailsBase(void); ~CTrailsBase(void); virtual int Type(void) const {return CLASS_TYPE_TRAILS;} //--- inicialización virtual bool Init(CSymbolManager*,CEventAggregator*); virtual CStop *GetContainer(void); virtual void SetContainer(CStop*stop); virtual bool Validate(void) const; //--- métodos de establecimiento y obtención bool Active(void) const; void Active(const bool activate); //--- comprobación virtual double Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET); };
El contenedor debe interactuar con CStop y los objetos de CTrail, a los que hace referencia. También tendrá su propio método Check:
double CTrailsBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!Active()) return 0; double val=0.0,ret=0.0; for(int i=0;i<Total();i++) { CTrail *trail=At(i); if(!CheckPointer(trail)) continue; if(!trail.Active()) continue; int trail_target=trail.TrailTarget(); if(mode!=trail_target) continue; val=trail.Check(symbol,type,entry_price,price,mode); if((type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_TAKEPROFIT)) { if(val>ret || ret==0.0) ret=val; } else if((type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_TAKEPROFIT)) { if(val<ret || ret==0.0) ret=val; } } return ret; }
CTrails, como los otros contenedores usados en los artículos de esta serie, es heredero de CArrayObj, que ha sido desarrollado para guardar numerosos punteros a los ejemplares СObject y sus herederos. Así, CTrails puede guardar solo un ejemplar de CTrail. Si CTrails contiene multitud de punteros a los ejemplares CTrail, al llamar al método Сheck, él llamará a los métodos Check de todos estos ejemplares. No solo el valor del precio más cercano al mercado actual será retornado como resultado final. Este comportamiento se puede corregir, ampliando СTrails.
Las clases CTrail y CTrails han sido reducidos a un cálculo puro. Esto significa que todos los métodos están codificados en la clase básica (CTrailBase), y no en algua implementación específica de su lenguaje. Cuando se llama la comprobación del estatus del trailing-stop (CStop), este recibe un nuevo valor para el nivel stop, y CStop, por consiguiente, cambiará este nivel.
Expandiendo CTrail
La clase CTrail se aplica no solo al trailing-stop y a la ausencia de pérdidas. Puede ser utilizada para seguir toda la evolución del nivel stop de la transacción a lo largo del tiempo. El proceso es muy similar al método de implementación de los stops personalizados, que se describe en este artículo. Pero este cambio tiene lugar mediante la ampliación de los métodos de CTrail, para aplicar los cambios a un nivel stop después de que la transacción haya entrado en el mercado.
Ejemplos
Ejemplo №1: Stops personalizados
Usamos stop-loss y take-profit para la transacción que el asesor va a abrir. Ampliamos el tercer ejemplo del asesor del anterior artículo, para crear un nuevo experto. Los niveles stop personalizados se calcularán usando como base el máximo y el mínimo del precio de la vela anterior. Como protección adicional, añadiremos la condición del mínimo requerido: si el nivel stop calculado se acerca al precio de entrada en menos de 200 puntos, entonces en su lugar se establecerá un nivel stop en esta marca de 200 puntos. Gracias a ello, estaremos seguros de que nuestra solicitud comercial será aceptada por el bróker.
Primero declaramos el descendiente de la clase CStop. En este ejemplar, llamamos al descendiente CCustomStop. Su definición se muestra más abajo:
class CCustomStop : public CStop { public: CCustomStop(const string); ~CCustomStop(void); virtual double StopLossCustom(const string,const ENUM_ORDER_TYPE,const double); virtual double TakeProfitCustom(const string,const ENUM_ORDER_TYPE,const double); };
Debemos ampliar los métodos StopLossCustom y TakeProfitCustom. El método StopLossCustom tiene el siguiente aspecto:
double CCustomStop::StopLossCustom(const string symbol,const ENUM_ORDER_TYPE type,const double price) { double array[1]; double val=0; if(type==ORDER_TYPE_BUY) { if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } else if(type==ORDER_TYPE_SELL) { if(CopyLow(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } if(val>0) { double distance=MathAbs(price-val)/m_symbol.Point(); if(distance<200) { if(type==ORDER_TYPE_BUY) val = price+200*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) val=price-200*m_symbol.Point(); } } return val; }
Primero usamos las funciones CopyLow y CopyHigh para calcular el stop-loss dependiendo del tipo de transacción (compra o venta). Obtenemos el valor absoluto de su distancia desde el precio de entrada en puntos. Si se encuentra por debajo del mínimo imprescindible, colocamos el stop-loss, no en el nivel calculado, sino en este mínimo imprescindible.
El método TakeProfitCustom repite un proceso semejante:
double CCustomStop::TakeProfitCustom(const string symbol,const ENUM_ORDER_TYPE type,const double price) { double array[1]; double val=0; m_symbol=m_symbol_man.Get(symbol); if(!CheckPointer(m_symbol)) return 0; if(type==ORDER_TYPE_BUY) { if(CopyLow(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } else if(type==ORDER_TYPE_SELL) { if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } if(val>0) { double distance=MathAbs(price-val)/m_symbol.Point(); if(distance<200) { if(type==ORDER_TYPE_BUY) val = price-200*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) val=price+200*m_symbol.Point(); } } return val; }
Los miembros de la clase CStop responsables de la asignación del valor del stop-loss y el take-profit en puntos (m_stoploss y m_takeprofit), se inicializan por defecto como 0. De esta forma, solo necesitaremos comentar las siguientes líneas dentro de la función OnInit:
//main.StopLoss(stop_loss); //main.TakeProfit(take_profit);
El ejemplar CStop usará el cálculo personalizado solo en el caso de que se haya asignado el valor 0 al stop-loss y el take-profit.
Ejmplo № 2: Ausencia de pérdidas
Para la función de ausencia de pérdidas vamos a modificar igualmente el archivo de encabezamiento principal del tercer ejemplo del artículo anterior. Pero esta vez no vamos a ampliar los objetos de las clases. En lugar de ello, simplemente declararemos los ejemplares CTrail y CTrails:
CTrails *trails = new CTrails(); CTrail *trail = new CTrail(); trail.Set(breakeven_value,breakeven_start,0); trails.Add(trail); main.Add(trails);
La última línea es muy importante. Tenemos que añadir el ejemplar CTrails al ejemplar CStop ya creado. De esta forma, el comportamiento se aplicará a este ejemplar aparte.
El tecer argumento del método Set de la clase CTrail es el salto. Su valor por defecto es 1 (1 punto). Puesto que solo necesitamos usar la función de ausencia de pérdidas, le asignaremos el valor 0.
El código mostrado más arriba, requiere la variable breakeven_start, que es el precio de activación (en puntos) desde el precio de entrada, y breakeven_value, que es la nueva distancia del stop-loss (en puntos) desde el precio de activación. Declaramos los parámetros de entrada para estas dos variables:
input int breakeven_value = 200; input int breakeven_start = 200;
Con este ajuste, en cuanto el precio se desplace un mínimo de 200 puntos en la dirección de la transacción, el stop-loss también se desplazará 200 puntos desde el precio de activación. 200 - 200 = 0, y de esta forma, el nuevo-stop loss calculado será el precio de entrada en la transacción.
Ejemplo № #3: Trailing-stop
Vamos a ver la implementación del trailing-stop. Este ejemplo es muy semejante al anterior. Recordemos que en el anterior ejemplo, introdujimos en la función OnInit el código siguiente:
CTrails *trails = new CTrails(); CTrail *trail = new CTrail(); trail.Set(breakeven_value,breakeven_start,0); trails.Add(trail); main.Add(trails);
Al usar el trailing-stop, el proceso no cambia:
CTrails *trails = new CTrails(); CTrail *trail = new CTrail(); trail.Set(trail_value,trail_start,trail_step); trails.Add(trail); main.Add(trails);
Esta vez, el método Set de la clase CTrail usa tres parámetros. Los dos primeros de ellos son idénticos a los mostrados en el ejemplo anterior. El tercer parámetro es el salto, que supone la frecuencia del trailing después de la activación. En el artículo anterior, este parámetro del método tiene un valor por defecto de 0, y esto significa que después de la activación inicial, no habrá ningún trailing añadido. A continuación, añadimos los parámetros de entrada trail_value, trail_start y trail_step.
Ejemplo №4: Trailing personalizado
En este ejemplo vamos a implementar un trailing personalizado. Ampliaremos el asesor mostrado en el primer ejemplo en este artículo. Recordemos que en él se declara el stop personalizado basado en el máximo/mínimo de la vela anterior. En este ejemplo, ampliaremos esta función: no solo colocaremos el stop-loss y el take-profit con entrada. También necesitamos que el stop-loss de la transacción se desplace más allá del precio de mercado usando el máximo o el mínimo de la vela anterior. Como medida de precaución, también implementaremos un stop mínimo para cada salto del trailing.
Para crear este asesor, ampliaremos primero CTrail. Nombraremos esta clase-descendiente como CCustomTrail. Su definición se muestra más abajo:
class CCustomTrail : public CTrail { public: CCustomTrail(void); ~CCustomTrail(void); virtual double Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET); };
Ahora, ampliamos el método Check. Destacaremos que, en este caso, el método no comprueba a qué se aplica precisamente el trailing: a un stop-loss o a un take-profit. Por defecto, para CTrail se ha designado el trailing del stop-loss:
double CCustomTrail::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!Active()) return 0; if(!Refresh(symbol)) return 0; double array[1]; double val=0; if(type==ORDER_TYPE_BUY) { if(CopyLow(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } else if(type==ORDER_TYPE_SELL) { if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } if(val>0) { double distance=MathAbs(price-val)/m_symbol.Point(); if(distance<200) { if(type==ORDER_TYPE_BUY) val = m_symbol.Ask()-200*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) val=m_symbol.Bid()+200*m_symbol.Point(); } } if((type==ORDER_TYPE_BUY && val<=price+10*m_symbol.Point()) || (type==ORDER_TYPE_SELL && val>=price-10*m_symbol.Point())) val = 0; return val; }Como podemos ver, el cálculo es casi el mismo que vimos entre los métodos CCustomStop. Además, hemos añadido a la parte final del código la comprobación del valor retornado: el nuevo stop-loss debe estar a 100 puntos (10 pips) más del anterior. Esto es necesario para prevenir que el stop-loss de la transacción "flote" dependiendo del valor del máximo o mínimo reciente. En lugar de que el stop-loss oscile arriba y abajo detrás del valor de los máximos/mínimos locales, hemos establecido un ajuste, de acuerdo al cual el nuevo nivel de stop-loss debe ser superior al sustituido (superior para las posiciones largas, inferior para las cortas).
Conclusión
En el artículo hemos demostrado cómo se pueden colocar niveles stop personalizados en el asesor comercial multiplataforma. En lugar de colocar el stop-loss y el take-profit en pips y puntos, los métodos presentados usan un modo en el que estos niveles pueden ser expresados en valores de precio en el gráfico. El artículo, asimismo, ha expuesto un método con ayuda del cual se puede cambiar (realizar el trailing) el stop-loss y el take-profit durante la vida de la transacción.
Programas utilizados en el artículo
# |
Nombre |
Tipo |
Descripción de los parámetros |
---|---|---|---|
1 |
breakeven_ha_ma.mqh |
Archivo de encabezamiento |
Archivo de encabezamiento principal utilizado en el primer ejemplo |
2. |
breakeven_ha_ma.mq4 | Asesor Experto |
Archivo fuente principal utilizado para la versión MQL4 del primer ejemplo |
3. | breakeven_ha_ma.mq5 | Asesor Experto | Archivo fuente principal utilizado para la versión MQL5 del primer ejemplo |
4. | trail_ha_ma.mqh | Archivo de encabezamiento |
Archivo de encabezamiento principal utilizado en el segundo ejemplo |
5. | trail_ha_ma.mq4 | Asesor Experto |
Archivo fuente principal utilizado para la versión MQL4 del segundo ejemplo |
6. | trail_ha_ma.mq5 | Asesor Experto |
Archivo fuente principal utilizado para la versión MQL5 del segundo ejemplo |
7. | custom_stop_ha_ma.mqh | Archivo de encabezamiento | Archivo de encabezamiento principal utilizado en el tercer ejemplo |
8. | custom_stop_ha_ma.mq4 | Asesor Experto | Archivo fuente principal utilizado para la versión MQL4 del tercer ejemplo |
9. | custom_stop_ha_ma.mq5 | Asesor Experto | Archivo fuente principal utilizado para la versión MQL5 del tercer ejemplo |
10. | custom_trail_ha_ma.mqh |
Archivo de encabezamiento | Archivo de encabezamiento principal utilizado en el cuarto ejemplo |
11. | custom_trail_ha_ma.mq4 | Asesor Experto | Archivo fuente principal utilizado para la versión MQL4 del cuarto ejemplo |
12. |
custom_trail_ha_ma.mq5 | Asesor Experto | Archivo fuente principal utilizado para la versión MQL5 del cuarto ejemplo |
Archivos de las clases creadas en el artículo
# | Nombre |
Tipo |
Descripción de los parámetros |
---|---|---|---|
1 |
MQLx\Base\Stop\StopBase.mqh | Archivo de encabezamiento | CStop (clase básica) |
2. |
MQLx\MQL4\Stop\Stop.mqh | Archivo de encabezamiento | CStop (versión MQL4) |
3. |
MQLx\MQL5\Stop\Stop.mqh | Archivo de encabezamiento | CStop (versión MQL5) |
4. | MQLx\Base\Trail\TrailBase.mqh | Archivo de encabezamiento | CTrail (base class) |
5. | MQLx\MQL4\Trail\Trail.mqh | Archivo de encabezamiento | CTrail (MQL4 version) |
6. | MQLx\MQL5\Trail\Trail.mqh | Archivo de encabezamiento | CTrail (MQL5 version) |
7. | MQLx\Base\Trail\TrailsBase.mqh | Archivo de encabezamiento | CTrails (base class) |
8. | MQLx\MQL4\Trail\Trails.mqh | Archivo de encabezamiento | CTrails (MQL4 version) |
9. | MQLx\MQL5\Trail\Trails.mqh | Archivo de encabezamiento | CTrails (MQL5 version) |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/3621
- 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