Análisis de la negociación a posteriori: ajustando el TrailingStop y los nuevos stops en el simulador de estrategias
Contenido
- Introducción
- Selección y perfeccionamiento de la clase de trailing de posiciones
- Probamos distintos tipos de TrailingStop
- Conclusión
Introducción
En el último artículo creamos un asesor experto que comercia en el simulador de estrategias del terminal de cliente basándose en los resultados del trading en una cuenta real. Asimismo, añadimos la posibilidad de establecer nuevos tamaños de StopLoss y TakeProfit para probar su comercio en el simulador con diferentes tamaños de órdenes stop. El resultado fue inesperado: en lugar de pérdidas, obtuvimos un beneficio proporcional a la pérdida real. Por lo tanto, si usáramos en nuestra cuenta los niveles de StopLoss y TakeProfit con los que logramos beneficios en el simulador, el comercio en la cuenta real sería rentable. Y estamos hablando de un simple cambio en las dimensiones de los stops. Me pregunto qué pasaría si añadiéramos un StopLoss trailing? ¿Cómo cambiaría esto el comercio?
Hoy conectaremos varias señales de trailing diferentes al asesor experto y probaremos nuestras transacciones en el simulador de estrategias. Y veremos cuál será el resultado.
Selección y perfeccionamiento de la clase de trailing de posiciones
Intentemos conectar el trailing usando el indicador Parabolic SAR y una media móvil. El indicador Parabolic SAR, a juzgar por su descripción y nombre (SAR = Stop And Reverse), debería ser ideal para desplazar la línea de stop según sus valores:
El indicador técnico Parabolic SAR se desarrolló para analizar los mercados de tendencia. El indicador se representa en un gráfico de precios. Por su significado este indicador es similar a la media móvil, con la única diferencia de que Parabolic SAR se mueve con gran aceleración y puede cambiar su posición respecto al precio. En una tendencia alcista (Up Trend) el indicador estará por debajo de los precios, mientras que en una tendencia bajista (Down Trend) estará por encima de los precios.
Si el precio cruza la línea de Parabolic SAR, el indicador se invertirá y sus próximos valores se situarán al otro lado del precio. En este "viraje" del indicador, el precio máximo o mínimo del periodo anterior servirá como punto de referencia. El viraje del indicador supondrá una señal del final (transición a la corrección o el mercado plano) de la tendencia, o de su inversión.
Parabolic SAR destaca en la identificación de puntos de salida del mercado. Las posiciones largas deberán cerrarse cuando el precio caiga por debajo de la línea del indicador técnico, mientras que las posiciones cortas deberán cerrarse cuando el precio suba por encima de la línea de Parabolic SAR. Es decir, deberemos monitorear la dirección del movimiento de Parabolic SAR y mantener las posiciones abiertas en el mercado solo en la dirección de este movimiento. Este indicador se usa con frecuencia como una línea de trailing stop.
Si hay abierta una posición larga (es decir, el precio está por encima de la línea de Parabolic SAR), la línea del indicador se moverá hacia arriba, independientemente de la dirección en la que se muevan los precios. La magnitud de movimiento de la línea de Parabolic SAR dependerá de la magnitud del movimiento del precio.
Y la media móvil, debido a su desfase natural, también resulta adecuada para situar la línea de stop por detrás del precio. Para una posición larga, si la línea del indicador está por debajo del precio, podemos «tirar» del stop tras el precio según el valor de la media móvil. El precio cruzará la media móvil de forma natural y el stop se activará. Para una posición corta, el stop deberá moverse tras la línea de media móvil si el precio está por debajo de ella.
Ya escribí un artículo con anterioridad sobre la conexión de cualquier trailing a asesores expertos: "Cómo crear cualquier tipo de Trailing Stop y conectarlo a un asesor experto". En este, sugerimos crear clases de trailing y simplemente introducirlas en el EA.
Vamos a aprovechar esta oportunidad. Lo único que no nos conviene mucho es que cada símbolo o número mágico ejecute su propio ciclo de posiciones abiertas. Por lo tanto, deberemos refinar la clase del artículo. Afortunadamente, no es algo difícil.
Vamos a cargar el único archivo que necesitamos de los adjuntos al artículo, Trailings.mqh. En el último artículo, creamos una carpeta donde se encontraban los archivos de clases y los asesores expertos para el trading según la historia de transacciones: \MQL5\Experts\TradingByHistoryDeals. Guardaremos el archivo Trailings.mqh descargado en esta carpeta y lo abriremos en el MetaEditor.
El método principal y primario llamado desde el programa será el método Run(). Si examinamos su código, veremos que aquí se organiza el desbordamiento de las posiciones abiertas:
//+------------------------------------------------------------------+ //| Launch simple trailing with StopLoss offset from the price | //+------------------------------------------------------------------+ bool CSimpleTrailing::Run(void) { //--- if disabled, leave if(!this.m_active) return false; //--- trailing variables MqlTick tick = {}; // price structure bool res = true; // result of modification of all positions //--- check the correctness of the data by symbol if(this.m_point==0) { //--- let's try to get the data again ::ResetLastError(); this.SetSymbol(this.m_symbol); if(this.m_point==0) { ::PrintFormat("%s: Correct data was not received for the %s symbol. Error %d",__FUNCTION__, this.m_symbol, ::GetLastError()); return false; } } //--- in a loop by the total number of open positions int total =::PositionsTotal(); for(int i = total - 1; i >= 0; i--) { //--- get the ticket of the next position ulong pos_ticket =::PositionGetTicket(i); if(pos_ticket == 0) continue; //--- get the symbol and position magic string pos_symbol = ::PositionGetString(POSITION_SYMBOL); long pos_magic = ::PositionGetInteger(POSITION_MAGIC); //--- if the position does not match the filter by symbol and magic number, leave if((this.m_magic != -1 && pos_magic != this.m_magic) || (pos_symbol != this.m_symbol)) continue; //--- if failed to get the prices, move on if(!::SymbolInfoTick(this.m_symbol, tick)) continue; //--- get the position type, its opening price and StopLoss level ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); double pos_open =::PositionGetDouble(POSITION_PRICE_OPEN); double pos_sl =::PositionGetDouble(POSITION_SL); //--- get the calculated StopLoss level double value_sl = this.GetStopLossValue(pos_type, tick); //--- if StopLoss modification conditions are suitable, modify the position stop level and add the result to the res variable if(this.CheckCriterion(pos_type, pos_open, pos_sl, value_sl, tick)) res &=this.ModifySL(pos_ticket, value_sl); } //--- at the end of the loop, return the result of modifying each position that matches the "symbol/magic" filter return res; }
Si llamamos a este método para cada objeto comercial de un símbolo (véase el primer artículo), obtendremos tantos ciclos de enumeración de posiciones abiertas, como símbolos se han usado en la negociación. Pero esto no es algo óptimo. Haremos que el propio programa contenga un ciclo con todas las posiciones abiertas, y que estos métodos sean llamados desde este ciclo. Pero en el método también hay un ciclo incorporado.... Por lo tanto, necesitaremos crear otro método Run() en la clase de trailing, pero especificando la entrada de la posición cuyo stop debe moverse, y llamarlo desde el ciclo principal.
En la clase de la red de trailing simple declararemos un nuevo método al que se le transmitirá el ticket de la posición requerida:
//+------------------------------------------------------------------+ //| Class of the position StopLoss simple trailing | //+------------------------------------------------------------------+ class CSimpleTrailing : public CObject { private: //--- check the criteria for modifying the StopLoss position and return the flag bool CheckCriterion(ENUM_POSITION_TYPE pos_type, double pos_open, double pos_sl, double value_sl, MqlTick &tick); //--- modify StopLoss of a position by its ticket bool ModifySL(const ulong ticket, const double stop_loss); //--- return StopLevel in points int StopLevel(void); protected: string m_symbol; // trading symbol long m_magic; // EA ID double m_point; // Symbol Point int m_digits; // Symbol digits int m_offset; // stop distance from price int m_trail_start; // profit in points for launching trailing uint m_trail_step; // trailing step uint m_spread_mlt; // spread multiplier for returning StopLevel value bool m_active; // trailing activity flag //--- calculate and return the StopLoss level of the selected position virtual double GetStopLossValue(ENUM_POSITION_TYPE pos_type, MqlTick &tick); public: //--- set trailing parameters void SetSymbol(const string symbol) { this.m_symbol = (symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_point =::SymbolInfoDouble(this.m_symbol, SYMBOL_POINT); this.m_digits = (int)::SymbolInfoInteger(this.m_symbol, SYMBOL_DIGITS); } void SetMagicNumber(const long magic) { this.m_magic = magic; } void SetStopLossOffset(const int offset) { this.m_offset = offset; } void SetTrailingStart(const int start) { this.m_trail_start = start; } void SetTrailingStep(const uint step) { this.m_trail_step = step; } void SetSpreadMultiplier(const uint value) { this.m_spread_mlt = value; } void SetActive(const bool flag) { this.m_active = flag; } //--- return trailing parameters string Symbol(void) const { return this.m_symbol; } long MagicNumber(void) const { return this.m_magic; } int StopLossOffset(void) const { return this.m_offset; } int TrailingStart(void) const { return this.m_trail_start; } uint TrailingStep(void) const { return this.m_trail_step; } uint SpreadMultiplier(void) const { return this.m_spread_mlt; } bool IsActive(void) const { return this.m_active; } //--- launch trailing with StopLoss offset from the price bool Run(void); bool Run(const ulong pos_ticket); //--- constructors CSimpleTrailing() : m_symbol(::Symbol()), m_point(::Point()), m_digits(::Digits()), m_magic(-1), m_trail_start(0), m_trail_step(0), m_offset(0), m_spread_mlt(2) {} CSimpleTrailing(const string symbol, const long magic, const int trailing_start, const uint trailing_step, const int offset); //--- destructor ~CSimpleTrailing() {} };
Luego escribiremos su implementación fuera del cuerpo de la misma:
//+------------------------------------------------------------------+ //| Launch simple trailing with StopLoss offset from the price | //+------------------------------------------------------------------+ bool CSimpleTrailing::Run(const ulong pos_ticket) { //--- if trailing is disabled, or the ticket is invalid, we leave if(!this.m_active || pos_ticket==0) return false; //--- trailing variables MqlTick tick = {}; // price structure //--- check the correctness of the data by symbol ::ResetLastError(); if(this.m_point==0) { //--- let's try to get the data again this.SetSymbol(this.m_symbol); if(this.m_point==0) { ::PrintFormat("%s: Correct data was not received for the %s symbol. Error %d",__FUNCTION__, this.m_symbol, ::GetLastError()); return false; } } //--- select a position by ticket if(!::PositionSelectByTicket(pos_ticket)) { ::PrintFormat("%s: PositionSelectByTicket(%I64u) failed. Error %d",__FUNCTION__, pos_ticket, ::GetLastError()); return false; } //--- if prices could not be obtained, return 'false' if(!::SymbolInfoTick(this.m_symbol, tick)) return false; //--- get the position type, its opening price and StopLoss level ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); double pos_open =::PositionGetDouble(POSITION_PRICE_OPEN); double pos_sl =::PositionGetDouble(POSITION_SL); //--- get the calculated StopLoss level double value_sl = this.GetStopLossValue(pos_type, tick); //--- if the conditions for modifying StopLoss are suitable, return the result of modifying the position stop if(this.CheckCriterion(pos_type, pos_open, pos_sl, value_sl, tick)) return(this.ModifySL(pos_ticket, value_sl)); //--- conditions for modification are not suitable return false; }
Ahora este método se llamará o bien desde el método Run(), que no tiene parámetros formales, pero en el que se organiza un ciclo a través de todas las posiciones abiertas, o bien directamente desde el programa transmitiéndole el ticket requerido.
Vamos a mejorar el método Run() sin parámetros formales. Primero eliminaremos un bloque de código del ciclo:
for(int i = total - 1; i >= 0; i--) { //--- get the ticket of the next position ulong pos_ticket =::PositionGetTicket(i); if(pos_ticket == 0) continue; //--- get the symbol and position magic string pos_symbol = ::PositionGetString(POSITION_SYMBOL); long pos_magic = ::PositionGetInteger(POSITION_MAGIC); //--- if the position does not match the filter by symbol and magic number, leave if((this.m_magic != -1 && pos_magic != this.m_magic) || (pos_symbol != this.m_symbol)) continue; //--- if failed to get the prices, move on if(!::SymbolInfoTick(this.m_symbol, tick)) continue; //--- get the position type, its opening price and StopLoss level ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); double pos_open =::PositionGetDouble(POSITION_PRICE_OPEN); double pos_sl =::PositionGetDouble(POSITION_SL); //--- get the calculated StopLoss level double value_sl = this.GetStopLossValue(pos_type, tick); //--- if StopLoss modification conditions are suitable, modify the position stop level and add the result to the res variable if(this.CheckCriterion(pos_type, pos_open, pos_sl, value_sl, tick)) res &=this.ModifySL(pos_ticket, value_sl); }
En su lugar, escribiremos la llamada a un nuevo método:
//+------------------------------------------------------------------+ //| Launch simple trailing with StopLoss offset from the price | //+------------------------------------------------------------------+ bool CSimpleTrailing::Run(void) { //--- if disabled, leave if(!this.m_active) return false; //--- trailing variables bool res = true; // result of modification of all positions //--- check the correctness of the data by symbol if(this.m_point==0) { //--- let's try to get the data again ::ResetLastError(); this.SetSymbol(this.m_symbol); if(this.m_point==0) { ::PrintFormat("%s: Correct data was not received for the %s symbol. Error %d",__FUNCTION__, this.m_symbol, ::GetLastError()); return false; } } //--- in a loop by the total number of open positions int total =::PositionsTotal(); for(int i = total - 1; i >= 0; i--) { //--- get the ticket of the next position ulong pos_ticket =::PositionGetTicket(i); if(pos_ticket == 0) continue; //--- get the symbol and position magic string pos_symbol = ::PositionGetString(POSITION_SYMBOL); long pos_magic = ::PositionGetInteger(POSITION_MAGIC); //--- if the position does not match the filter by symbol and magic number, leave if((this.m_magic != -1 && pos_magic != this.m_magic) || (pos_symbol != this.m_symbol)) continue; res &=this.Run(pos_ticket); } //--- at the end of the loop, return the result of modifying each position that matches the "symbol/magic" filter return res; }
En la clase de trailing, en el valor especificado, declararemos de la misma manera un nuevo método Run() con la indicación del ticket de la posición:
//+------------------------------------------------------------------+ //| Trailing class based on a specified value | //+------------------------------------------------------------------+ class CTrailingByValue : public CSimpleTrailing { protected: double m_value_sl_long; // StopLoss level for long positions double m_value_sl_short; // StopLoss level for short positions //--- calculate and return the StopLoss level of the selected position virtual double GetStopLossValue(ENUM_POSITION_TYPE pos_type, MqlTick &tick); public: //--- return StopLoss level for (2) long and (2) short positions double StopLossValueLong(void) const { return this.m_value_sl_long; } double StopLossValueShort(void) const { return this.m_value_sl_short; } //--- launch trailing with the specified StopLoss offset from the price bool Run(const double value_sl_long, double value_sl_short); bool Run(const ulong pos_ticket, const double value_sl_long, double value_sl_short); //--- constructors CTrailingByValue(void) : CSimpleTrailing(::Symbol(), -1, 0, 0, 0), m_value_sl_long(0), m_value_sl_short(0) {} CTrailingByValue(const string symbol, const long magic, const int trail_start, const uint trail_step, const int trail_offset) : CSimpleTrailing(symbol, magic, trail_start, trail_step, trail_offset), m_value_sl_long(0), m_value_sl_short(0) {} //--- destructor ~CTrailingByValue(void){} };
Y fuera del cuerpo de la clase escribiremos su implementación:
//+------------------------------------------------------------------+ //| Launch trailing with the specified StopLoss offset from the price| //+------------------------------------------------------------------+ bool CTrailingByValue::Run(const ulong pos_ticket,const double value_sl_long,double value_sl_short) { this.m_value_sl_long =value_sl_long; this.m_value_sl_short=value_sl_short; return CSimpleTrailing::Run(pos_ticket); }
Aquí llamaremos al método Run() añadido a la clase de trailing simple, especificando el ticket de la posición.
Le recomiendo familiarizarse con las clases de trailing en el artículo del que hemos tomado el archivo de trailing.
La clase comercial de símbolos creada en el artículo pasado almacena las listas de transacciones y la clase comercial CTrade de la Biblioteca Estándar. Para no mejorarla para la conexión de las clases con los trails, crearemos una nueva clase basada en ella, a la que añadiremos el trabajo con trails.
En el catálogo del terminal \MQL5\Experts\TradingByHistoryDeals\ crearemos el nuevo archivo SymbolTradeExt.mqh de la clase CSymbolTradeExt. Al archivo deberemos conectar el archivo de las clases de los trailings y el archivo de la clase CSymbolTrade, del que deberá heredarse la clase creada:
//+------------------------------------------------------------------+ //| SymbolTradeExt.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "Trailings.mqh" #include "SymbolTrade.mqh" class CSymbolTradeExt : public CSymbolTrade { }
Al utilizar la clase de trailing tomada del artículo, se nos proporcionarán trailings para Parabolic y todos los tipos de medias móviles estándar. La clase también tendrá un trailing de los valores especificados, pero no la usaremos aquí: requiere su propio cálculo de los niveles de stop en su propio programa y transmitir estos en los parámetros del método llamado Run() de la clase de trailing. Puede ser, por ejemplo, el cálculo de los niveles del indicador ATR y la transmisión de los valores calculados a la clase de trailing.
Vamos a escribir la enumeración de los modos de trailing:
//+------------------------------------------------------------------+ //| SymbolTradeExt.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "Trailings.mqh" #include "SymbolTrade.mqh" enum ENUM_TRAILING_MODE // Enumeration of trailing modes { TRAILING_MODE_SIMPLE=2, // Simple trailing TRAILING_MODE_SAR, // Trailing by Parabolic SAR TRAILING_MODE_AMA, // Trailing by adjustable moving average TRAILING_MODE_DEMA, // Trailing by double exponential moving average TRAILING_MODE_FRAMA, // Trailing by fractal adaptive moving average TRAILING_MODE_MA, // Trailing by simple moving average TRAILING_MODE_TEMA, // Trailing by triple exponential moving average TRAILING_MODE_VIDYA, // Trailing by moving average with dynamic averaging period }; class CSymbolTradeExt : public CSymbolTrade { }
¿Por qué los valores de las constantes de enumeración empiezan con el valor 2 en lugar de cero? El asesor experto que usaremos de base para crear uno nuevo también tiene una lista de modos de prueba:
enum ENUM_TESTING_MODE { TESTING_MODE_ORIGIN, /* Original trading */ TESTING_MODE_SLTP, /* Specified StopLoss and TakeProfit values */ };
Aquí hay dos constantes: el comercio original (0) y el comercio con los valores de órdenes stop indicados (1). Aquí añadiremos más adelante nuevas constantes correspondientes a las constantes de enumeración finales. Por este motivo, los valores de las constantes de enumeración finales comienzan por el valor 2.
En la sección privada de la clase, declararemos el puntero al objeto de la clase trailing y una variable para almacenar el periodo del gráfico para calcular los indicadores utilizados en los trailings. En la sección pública declararemos el método para establecer los parámetros de trailing, el método que inicia la posición de trailing, los constructores y el destructor:
class CSymbolTradeExt : public CSymbolTrade { private: CSimpleTrailing *m_trailing; // Trailing class object ENUM_TIMEFRAMES m_timeframe; // Timeframe for calculating the indicator for trailing public: //--- Set trailing and its parameters bool SetTrailing(const ENUM_TRAILING_MODE trailing_mode, const int data_index, const long magic, const int start, const int step, const int offset, const MqlParam ¶m[]); //--- Start a trail of the position specified by the ticket void Trailing(const ulong pos_ticket); //--- Constructor/destructor CSymbolTradeExt() : m_trailing(NULL), m_timeframe(::Period()) { this.SetSymbol(::Symbol()); } CSymbolTradeExt(const string symbol, const ENUM_TIMEFRAMES timeframe); ~CSymbolTradeExt(); };
En el constructor de la clase, en la línea de inicialización, el símbolo para el que se construye el objeto se transmitirá al constructor de la clase padre, el valor del marco temporal de cálculo del indicador se establecerá en el valor transmitido en los parámetros formales, y el puntero al objeto de la clase de trailing se inicializará con el valor NULL:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSymbolTradeExt::CSymbolTradeExt(const string symbol, const ENUM_TIMEFRAMES timeframe) : CSymbolTrade(symbol) { this.m_trailing=NULL; this.m_timeframe=timeframe; }
En el destructor de la clase, si se ha creado un objeto de trailing, se eliminará:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSymbolTradeExt::~CSymbolTradeExt() { //--- delete the created trailing object if(this.m_trailing!=NULL) delete this.m_trailing; }
Como los diferentes tipos de trailing que operan en base a diferentes tipos de indicadores tienen diferentes parámetros de ajuste, pasaremos todo este conjunto de parámetros al método de ajuste de parámetros de trailing a través de la estructura MqlParam, solo que para cada trailing el conjunto de parámetros será diferente, y cada campo de la estructura llevará el valor de su parámetro de indicador inherente solo a él.
//+------------------------------------------------------------------+ //| Set trailing parameters | //+------------------------------------------------------------------+ bool CSymbolTradeExt::SetTrailing(const ENUM_TRAILING_MODE trailing_mode, const int data_index, const long magic, const int start, const int step, const int offset, const MqlParam ¶m[]) { //--- Set trailing parameters (only necessary structure fields are used for each indicator type) int ma_period = (int)param[0].integer_value; int ma_shift = (int)param[1].integer_value; ENUM_APPLIED_PRICE ma_price = (ENUM_APPLIED_PRICE)param[2].integer_value; ENUM_MA_METHOD ma_method = (ENUM_MA_METHOD)param[3].integer_value; int fast_ema = (int)param[4].integer_value; int slow_ema = (int)param[5].integer_value; int period_cmo = (int)param[6].integer_value; double sar_step = param[0].double_value; double sar_max = param[1].double_value; //--- depending on the trailing type, we create a trailing object //--- if the value passed as the calculation period is less than the allowed value, then each indicator is assigned its own default value switch(trailing_mode) { case TRAILING_MODE_SIMPLE : this.m_trailing=new CSimpleTrailing(this.Symbol(), magic, start, step, offset); break; case TRAILING_MODE_AMA : this.m_trailing=new CTrailingByAMA(this.Symbol(), this.m_timeframe, magic, (ma_period<1 ? 9 : ma_period), (fast_ema<1 ? 2 : fast_ema), (slow_ema<1 ? 30 : slow_ema), ma_shift, ma_price, start, step, offset); break; case TRAILING_MODE_DEMA : this.m_trailing=new CTrailingByDEMA(this.Symbol(), this.m_timeframe, magic, (ma_period==0 ? 14 : ma_period), ma_shift, ma_price, start, step, offset); break; case TRAILING_MODE_FRAMA : this.m_trailing=new CTrailingByFRAMA(this.Symbol(), this.m_timeframe, magic, (ma_period==0 ? 14 : ma_period), ma_shift, ma_price, start, step, offset); break; case TRAILING_MODE_MA : this.m_trailing=new CTrailingByMA(this.Symbol(), this.m_timeframe, magic, ma_period, (ma_period==0 ? 10 : ma_period), ma_method, ma_price, start, step, offset); break; case TRAILING_MODE_TEMA : this.m_trailing=new CTrailingByTEMA(this.Symbol(), this.m_timeframe, magic, (ma_period==0 ? 14 : ma_period), ma_shift, ma_price, start, step, offset); break; case TRAILING_MODE_VIDYA : this.m_trailing=new CTrailingByVIDYA(this.Symbol(), this.m_timeframe, magic, (period_cmo<1 ? 9 : period_cmo), (ma_period==0 ? 12 : ma_period), ma_shift, ma_price, start, step, offset); break; case TRAILING_MODE_SAR : this.m_trailing=new CTrailingBySAR(this.Symbol(), this.m_timeframe, magic, (sar_step<0.0001 ? 0.02 : sar_step), (sar_max<0.02 ? 0.2 : sar_max), start, step, offset); break; default : break; } //--- something went wrong - return 'false' if(this.m_trailing==NULL) return false; //--- all is well - make the trail active and return 'true' this.m_trailing.SetActive(true); return true; }
Al llamar a este método para diferentes tipos de trailing, deberemos rellenar correctamente la estructura MqlParam. Comprobaremos esto cuando finalicemos el asesor experto para utilizar trailings.
Método que inicia un trailing de la posición especificada por el ticket:
//+------------------------------------------------------------------+ //| Start trailing of the position specified by the ticket | //+------------------------------------------------------------------+ void CSymbolTradeExt::Trailing(const ulong pos_ticket) { if(this.m_trailing!=NULL) this.m_trailing.Run(pos_ticket); }
Desde el ciclo sobre posiciones abiertas del asesor experto, según el símbolo de la posición, obtendremos de la lista el objeto comercial del símbolo, y desde él llamaremos a este método para arrastrar el stop de la posición cuyo ticket pasaremos al método. Si el objeto de trailing existe, llamaremos a su nuevo método Run() especificando el ticket de posición.
Probamos distintos tipos de TrailingStop
Para la prueba, vamos a tomar el archivo del asesor experto TradingByHistoryDeals_SLTP.mq5del artículo anterior y lo guardaremos en la misma carpeta \MQL5\Experts\TradingByHistoryDeals\ con el nuevo nombre TradingByHistoryDeals_Ext.mq5.
La conexión del archivo de clase CSymbolTrade la sustituiremos por la conexión del archivo de clase CSymbolTradeExt, luego conectaremos el archivo de clases de trailings y ampliaremos la lista de constantes de enumeración de modos de prueba, incluyendo la selección del trailing necesario:
//+------------------------------------------------------------------+ //| TradingByHistoryDeals_Ext.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "SymbolTradeExt.mqh" #include "Trailings.mqh" enum ENUM_TESTING_MODE { TESTING_MODE_ORIGIN, /* Original trading */ TESTING_MODE_SLTP, /* Specified StopLoss and TakeProfit values */ TESTING_MODE_TRAIL_SIMPLE, /* Simple Trailing */ TESTING_MODE_TRAIL_SAR, /* Trailing by Parabolic SAR indicator */ TESTING_MODE_TRAIL_AMA, /* Trailing by AMA indicator */ TESTING_MODE_TRAIL_DEMA, /* Trailing by DEMA indicator */ TESTING_MODE_TRAIL_FRAMA, /* Trailing by FRAMA indicator */ TESTING_MODE_TRAIL_MA, /* Trailing by MA indicator */ TESTING_MODE_TRAIL_TEMA, /* Trailing by TEMA indicator */ TESTING_MODE_TRAIL_VIDYA, /* Trailing by VIDYA indicator */ }; //+------------------------------------------------------------------+ //| Expert | //+------------------------------------------------------------------+
En los parámetros de entrada del asesor experto, añadiremos las variables para la selección de parámetros de trailing:
//+------------------------------------------------------------------+ //| Expert | //+------------------------------------------------------------------+ //--- input parameters input group " - Strategy parameters - " input string InpTestedSymbol = ""; /* The symbol being tested in the tester */ input long InpTestedMagic = -1; /* The magic number being tested in the tester */ sinput bool InpShowDataInLog = false; /* Show collected data in the log */ input group " - Stops parameters - " input ENUM_TESTING_MODE InpTestingMode = TESTING_MODE_ORIGIN; /* Testing Mode */ input int InpStopLoss = 300; /* StopLoss in points */ input int InpTakeProfit = 500; /* TakeProfit in points */ input group " - Trailing Parameters -" input bool InpSetStopLoss = true; /* Set Initial StopLoss */ input bool InpSetTakeProfit = true; /* Set Initial TakeProfit */ input int InpTrailingStart = 150; /* Trailing start */ // Profit in points to start trailing input int InpTrailingStep = 50; /* Trailing step in points */ input int InpTrailingOffset = 0; /* Trailing offset in points */ input group " - Indicator Parameters -" input ENUM_TIMEFRAMES InpIndTimeframe = PERIOD_CURRENT; /* Indicator's timeframe */ // Timeframe of the indicator used in trailing calculation input int InpMAPeriod = 0; /* MA Period */ input int InpMAShift = 0; /* MA Shift */ input int InpFastEMAPeriod = 2; /* AMA Fast EMA Period */ input int InpSlowEMAPeriod = 30; /* AMA Slow EMA Period */ input int InpCMOPeriod = 9; /* VIDYA CMO Period */ input double InpSARStep = 0.02; /* Parabolic SAR Step */ input double InpSARMax = 0.2; /* Parabolic SAR Max */ input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; /* MA Applied Price */ input ENUM_MA_METHOD InpMAMethod = MODE_SMA; /* MA Smoothing Method */ input int InpDataIndex = 1; /* Indicator data index */ // Bar of data received frrom the indicator
Por defecto, aquí InpMAPeriod se establecerá en cero. Esto se debe a que cada tipo de media móvil tiene un valor de periodo por defecto diferente. Y, como al darse un valor cero de este parámetro, a la clase de trailing se le transmite el valor predeterminado correspondiente al indicador, todos los datos serán válidos si queremos utilizar los valores predeterminados simples para el indicador utilizado en el final.
En todo el código, sustituiremos todas las apariciones de la cadena "CSymbolTrade" por "CSymbolTradeExt". Ahora tendremos una nueva clase de objeto comercial de símbolos CSymbolTradeExt heredada de la anterior clase CSymbolTrade escrita en el último artículo. Y en esta clase, se declarará un objeto de la clase de trailing. Por lo tanto, sustituiremos el tipo de la clase anterior por el nuevo. De hecho, no será necesario hacerlo en todas partes, sino solo donde se requiera el trailing. Pero no vamos a entrar aquí en los detalles sobre la herencia de clases, sino que sustituiremos la clase antigua por una nueva.
Los trailings se llamarán para cada posición según su ticket. Y para acceder al trailing de las posiciones, deberemos obtener de la lista el objeto comercial del símbolo para el que está abierta la posición. Para ello, escribamos una función que retornará el puntero a un objeto comercial de la lista según el nombre de símbolo:
//+------------------------------------------------------------------+ //| Return the pointer to the symbol trading object by name | //+------------------------------------------------------------------+ CSymbolTrade *GetSymbolTrade(const string symbol, CArrayObj *list) { SymbTradeTmp.SetSymbol(symbol); list.Sort(); int index=list.Search(&SymbTradeTmp); return list.At(index); }
A la función se le transmitirá el nombre del símbolo para el que se desea devolver el objeto comercial, así como el puntero a una lista que almacenará los punteros a los objetos comerciales de los símbolos. El nombre del símbolo transmitido a la función se fijará en el objeto comercial temporal, y en la lista se buscará el índice del objeto con este nombre de símbolo. Como resultado, se retornará el puntero al objeto buscado de la lista según el índice. Si tal objeto no está en la lista, el índice será -1 y se devolverá un valor NULL de la lista.
Al crear un array de símbolos utilizados, se crearán los objetos comerciales de los símbolos, y los símbolos de trailing especificados en los parámetros de entrada deberán inicializarse en ellos. Vamos a mejorar la función que crea un array de símbolos utilizados:
//+------------------------------------------------------------------+ //| Creates an array of used symbols | //+------------------------------------------------------------------+ bool CreateListSymbolTrades(SDeal &array_deals[], CArrayObj *list_symbols) { bool res=true; // result MqlParam param[7]={}; // trailing parameters int total=(int)array_deals.Size(); // total number of deals in the array //--- if the deal array is empty, return 'false' if(total==0) { PrintFormat("%s: Error! Empty deals array passed",__FUNCTION__); return false; } //--- in a loop through the deal array CSymbolTradeExt *SymbolTrade=NULL; for(int i=0; i<total; i++) { //--- get the next deal and, if it is neither buy nor sell, move on to the next one SDeal deal_str=array_deals[i]; if(deal_str.type!=DEAL_TYPE_BUY && deal_str.type!=DEAL_TYPE_SELL) continue; //--- find a trading object in the list whose symbol is equal to the deal symbol string symbol=deal_str.Symbol(); SymbTradeTmp.SetSymbol(symbol); list_symbols.Sort(); int index=list_symbols.Search(&SymbTradeTmp); //--- if the index of the desired object in the list is -1, there is no such object in the list if(index==WRONG_VALUE) { //--- we create a new trading symbol object and, if creation fails, //--- add 'false' to the result and move on to the next deal SymbolTrade=new CSymbolTradeExt(symbol, InpIndTimeframe); if(SymbolTrade==NULL) { res &=false; continue; } //--- if failed to add a symbol trading object to the list, //--- delete the newly created object, add 'false' to the result //--- and we move on to the next deal if(!list_symbols.Add(SymbolTrade)) { delete SymbolTrade; res &=false; continue; } //--- initialize trailing specified in the settings in the trading object ENUM_TRAILING_MODE mode=(ENUM_TRAILING_MODE)InpTestingMode; SetTrailingParams(mode, param); SymbolTrade.SetTrailing(mode, InpDataIndex, InpTestedMagic, InpTrailingStart, InpTrailingStep, InpTrailingOffset, param); } //--- otherwise, if the trading object already exists in the list, we get it by index else { SymbolTrade=list_symbols.At(index); if(SymbolTrade==NULL) continue; } //--- if the current deal is not yet in the list of deals of the symbol trading object if(SymbolTrade.GetDealByTime(deal_str.time)==NULL) { //--- create a deal object according to its sample structure CDeal *deal=CreateDeal(deal_str); if(deal==NULL) { res &=false; continue; } //--- add the result of adding the deal object to the list of deals of a symbol trading object to the result value res &=SymbolTrade.AddDeal(deal); } } //--- return the final result of creating trading objects and adding deals to their lists return res; }
Aquí hemos añadido una declaración de la estructura de parámetros de entrada del indicador y un pequeño bloque de código donde se inicializará el trailing en el objeto comercial.
Para establecer los parámetros del trailing seleccionado en los ajustes, escribiremos una función especial:
//+------------------------------------------------------------------+ //| Set the trailing parameters according to its selected type | //+------------------------------------------------------------------+ void SetTrailingParams(const ENUM_TRAILING_MODE mode, MqlParam ¶m[]) { //--- reset all parameters ZeroMemory(param); //--- depending on the selected trailing type, we set the indicator parameters switch(mode) { case TRAILING_MODE_SAR : param[0].type=TYPE_DOUBLE; param[0].double_value=InpSARStep; param[1].type=TYPE_DOUBLE; param[1].double_value=InpSARMax; break; case TRAILING_MODE_AMA : param[0].type=TYPE_INT; param[0].integer_value=InpMAPeriod; param[1].type=TYPE_INT; param[1].integer_value=InpMAShift; param[2].type=TYPE_INT; param[2].integer_value=InpAppliedPrice; param[4].type=TYPE_INT; param[4].integer_value=InpFastEMAPeriod; param[5].type=TYPE_INT; param[5].integer_value=InpSlowEMAPeriod; break; case TRAILING_MODE_DEMA : param[0].type=TYPE_INT; param[0].integer_value=InpMAPeriod; param[1].type=TYPE_INT; param[1].integer_value=InpMAShift; param[2].type=TYPE_INT; param[2].integer_value=InpAppliedPrice; break; case TRAILING_MODE_FRAMA : param[0].type=TYPE_INT; param[0].integer_value=InpMAPeriod; param[1].type=TYPE_INT; param[1].integer_value=InpMAShift; param[2].type=TYPE_INT; param[2].integer_value=InpAppliedPrice; break; case TRAILING_MODE_MA : param[0].type=TYPE_INT; param[0].integer_value=InpMAPeriod; param[1].type=TYPE_INT; param[1].integer_value=InpMAShift; param[2].type=TYPE_INT; param[2].integer_value=InpAppliedPrice; param[3].type=TYPE_INT; param[3].integer_value=InpMAMethod; break; case TRAILING_MODE_TEMA : param[0].type=TYPE_INT; param[0].integer_value=InpMAPeriod; param[1].type=TYPE_INT; param[1].integer_value=InpMAShift; param[2].type=TYPE_INT; param[2].integer_value=InpAppliedPrice; break; case TRAILING_MODE_VIDYA : param[0].type=TYPE_INT; param[0].integer_value=InpMAPeriod; param[1].type=TYPE_INT; param[1].integer_value=InpMAShift; param[2].type=TYPE_INT; param[2].integer_value=InpAppliedPrice; param[6].type=TYPE_INT; param[6].integer_value=InpCMOPeriod; break; case TRAILING_MODE_SIMPLE : break; default: break; } }
Aquí, para cada uno de los tipos de trailing, para los indicadores usados en el trailing, se establecerán en la estructura los valores indicados en los parámetros de entrada del asesor experto. Los valores asignados a los campos de la estructura se comprobarán y corregirán en la nueva clase CSymbolTradeExt del objeto comercial de símbolos que hemos comentado anteriormente.
Vamos a terminar la función para comerciar según la historia de transacciones. Ahora deberemos distinguir claramente qué tipo de prueba se utiliza.
Para probar el comercio original no es necesario colocar órdenes stop, todas las posiciones se cerrarán según el cierre de las transacciones históricas.
Si se está probando el comercio con órdenes stop de otros tamaños, deberemos obtener sus tamaños correctos y establecerlos para la posición abierta. En el caso de las pruebas de trailing, no será necesario fijar stops, pero para evitar reducciones y "esperas" excesivamente grandes, recomendamos fijar stops iniciales, y luego desplazarlos usando trailings según la lógica establecida. En la configuración del asesor experto hay parámetros que determinan la necesidad de establecer órdenes stop iniciales:
input bool InpSetStopLoss = true; /* Set Initial StopLoss */ input bool InpSetTakeProfit = true; /* Set Initial TakeProfit */
Al utilizarlos, también necesitaremos obtener los tamaños de stop correctos y ajustarlos a la posición que estamos abriendo:
//+------------------------------------------------------------------+ //| Trading by history | //+------------------------------------------------------------------+ void TradeByHistory(const string symbol="", const long magic=-1) { datetime time=0; int total=ExtListSymbols.Total(); // number of trading objects in the list //--- in a loop by all symbol trading objects for(int i=0; i<total; i++) { //--- get another trading object CSymbolTradeExt *obj=ExtListSymbols.At(i); if(obj==NULL) continue; //--- get the current deal pointed to by the deal list index CDeal *deal=obj.GetDealCurrent(); if(deal==NULL) continue; //--- sort the deal by magic number and symbol if((magic>-1 && deal.Magic()!=magic) || (symbol!="" && deal.Symbol()!=symbol)) continue; //--- sort the deal by type (only buy/sell deals) ENUM_DEAL_TYPE type=deal.TypeDeal(); if(type!=DEAL_TYPE_BUY && type!=DEAL_TYPE_SELL) continue; //--- if this is a deal already handled in the tester, move on to the next one if(deal.TicketTester()>0) continue; //--- if the deal time has not yet arrived, move to the next trading object of the next symbol if(!obj.CheckTime(deal.Time())) continue; //--- in case of a market entry deal ENUM_DEAL_ENTRY entry=deal.Entry(); if(entry==DEAL_ENTRY_IN) { //--- set the sizes of stop orders depending on the stop setting method //--- stop orders are not used initially (for original trading) ENUM_ORDER_TYPE order_type=(deal.TypeDeal()==DEAL_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL); double sl=0; double tp=0; //--- in case of the mode for setting the specified stop order values if(InpTestingMode==TESTING_MODE_SLTP) { //--- get correct values for StopLoss and TakeProfit sl=CorrectStopLoss(deal.Symbol(), order_type, ExtStopLoss); tp=CorrectTakeProfit(deal.Symbol(), order_type, ExtTakeProfit); } //--- otherwise, if testing with trailing stops else { if(InpTestingMode!=TESTING_MODE_ORIGIN) { //--- if allowed in the settings, we set correct stop orders for the positions being opened if(InpSetStopLoss) sl=CorrectStopLoss(deal.Symbol(), order_type, ExtStopLoss); if(InpSetTakeProfit) tp=CorrectTakeProfit(deal.Symbol(), order_type, ExtTakeProfit); } } //--- open a position by deal type ulong ticket=(type==DEAL_TYPE_BUY ? obj.Buy(deal.Volume(), deal.Magic(), sl, tp, deal.Comment()) : type==DEAL_TYPE_SELL ? obj.Sell(deal.Volume(),deal.Magic(), sl, tp, deal.Comment()) : 0); //--- if a position is opened (we received its ticket) if(ticket>0) { //--- increase the number of deals handled by the tester and write the deal ticket in the tester to the properties of the deal object obj.SetNumProcessedDeals(obj.NumProcessedDeals()+1); deal.SetTicketTester(ticket); //--- get the position ID in the tester and write it to the properties of the deal object long pos_id_tester=0; if(HistoryDealSelect(ticket)) { pos_id_tester=HistoryDealGetInteger(ticket, DEAL_POSITION_ID); deal.SetPosIDTester(pos_id_tester); } } } //--- in case of a market exit deal if(entry==DEAL_ENTRY_OUT || entry==DEAL_ENTRY_INOUT || entry==DEAL_ENTRY_OUT_BY) { //--- get a deal a newly opened position is based on CDeal *deal_in=obj.GetDealInByPosID(deal.PositionID()); if(deal_in==NULL) continue; //--- get the position ticket in the tester from the properties of the opening deal //--- if the ticket is zero, then most likely the position in the tester is already closed ulong ticket_tester=deal_in.TicketTester(); if(ticket_tester==0) { PrintFormat("Could not get position ticket, apparently position #%I64d (#%I64d) is already closed \n", deal.PositionID(), deal_in.PosIDTester()); obj.SetNextDealIndex(); continue; } //--- if we reproduce the original trading history in the tester, if(InpTestingMode==TESTING_MODE_ORIGIN) { //--- if the position is closed by ticket if(obj.ClosePos(ticket_tester)) { //--- increase the number of deals handled by the tester and write the deal ticket in the tester to the properties of the deal object obj.SetNumProcessedDeals(obj.NumProcessedDeals()+1); deal.SetTicketTester(ticket_tester); } } //--- otherwise, in the tester we work with stop orders placed according to different algorithms, and closing deals are skipped; //--- accordingly, for the closing deal, we simply increase the number of deals handled by the tester and //--- write the deal ticket in the tester to the properties of the deal object else { obj.SetNumProcessedDeals(obj.NumProcessedDeals()+1); deal.SetTicketTester(ticket_tester); } } //--- if a ticket is now set in the deal object, then the deal has been successfully handled - //--- set the deal index in the list to the next deal if(deal.TicketTester()>0) { obj.SetNextDealIndex(); } } }
Ahora escribiremos una función que realizará el trailing de la posición:
//+------------------------------------------------------------------+ //| Trail positions | //+------------------------------------------------------------------+ void Trailing(void) { //--- variables for getting position properties long magic=-1; string symbol=""; //--- in a loop through all positions int total=PositionsTotal(); for(int i=total-1; i>=0; i--) { //--- get the ticket of the next position ulong ticket=PositionGetTicket(i); if(ticket==0) continue; //--- get the magic number and position symbol ResetLastError(); if(!PositionGetInteger(POSITION_MAGIC, magic)) { Print("PositionGetInteger() failed. Error ", GetLastError()); continue; } if(!PositionGetString(POSITION_SYMBOL, symbol)) { Print("PositionGetString() failed. Error ", GetLastError()); continue; } //--- if the position does not meet the specified conditions of the magic number and symbol, we move to the next one if((InpTestedMagic>-1 && magic!=InpTestedMagic) || (InpTestedSymbol!="" && symbol!=InpTestedSymbol)) continue; //--- get a trading object by a symbol name and call its method for trailing a position by ticket CSymbolTradeExt *obj=GetSymbolTrade(symbol, &ExtListSymbols); if(obj!=NULL) obj.Trailing(ticket); } }
Bueno, aquí todo es sencillo: seleccionaremos cada posición en el ciclo, comprobaremos que su número mágico y su símbolo se correspondan con los introducidos en los ajustes del asesor experto y, si es la posición deseada^, obtendremos el objeto comercial del símbolo de esta posición y llamaremos desde ella el trailing con la indicación del ticket de la posición.
Escribiremos la llamada a esta función en el manejador OnTick() del asesor experto:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- work only in the strategy tester if(!MQLInfoInteger(MQL_TESTER)) return; //--- Trail open positions Trailing(); //--- Handle the list of deals from the file TradeByHistory(InpTestedSymbol, InpTestedMagic); }
Y eso es todo, el asesor experto está listo. El código completo del asesor experto se puede encontrar en los archivos adjuntos al artículo.
Probaremos diferentes tipos de trailings y compararemos los resultados del comercio original y del comercio con trailings de posición utilizando diferentes algoritmos.
Al ejecutar el asesor experto en el gráfico de símbolos, este recopilará la historia de transacciones, la escribirá en un archivo y mostrará una alerta indicando la configuración deseada del simulador: fecha inicial de la prueba, depósito inicial y apalancamiento:

El registro mostrará los símbolos utilizados en el comercio y el número de transacciones realizadas con cada símbolo:
Alert: Now you can run testing Interval: 2024.09.13 - current date Initial deposit: 3000.00, leverage 1:500 Symbols used in trading: 1. AUDUSD trade object. Total deals: 222 2. EURJPY trade object. Total deals: 120 3. EURUSD trade object. Total deals: 526 4. GBPUSD trade object. Total deals: 352 5. NZDUSD trade object. Total deals: 182 6. USDCAD trade object. Total deals: 22 7. USDCHF trade object. Total deals: 250 8. USDJPY trade object. Total deals: 150 9. XAUUSD trade object. Total deals: 118
Ahora ejecutaremos el asesor experto en el simulador con los parámetros recomendados para el simulador especificados en la alerta y los parámetros duplicados en el registro.
Comercio original:

Vemos que el comercio original ha traído unas pérdidas de 658 dólares.
Vamos a probar con un trailing diferente. Permitiremos un tamaño de stop inicial de 100 pips, y ejecutaremos cada trailing en el simulador.
Solo uno cada vez, sin cambiar ningún otro parámetro, y veremos cómo cambia el comercio.
Trailing simple:

Hemos tenido unas pérdidas de 746,1 dólares. Incluso más que en el comercio original.
Vamos a comprobar los trailings de los indicadores.
Trailing de Parabolic SAR:

El beneficio es de 541,8 dólares.
Veamos ahora los trailings basados en diferentes tipos de medias móviles.
Trailing de AMA:

El beneficio es de 806,5 dólares.
Trailing de DEMA:

El beneficio es de 1.397,1 dólares.
Trailing de FRAMA:

El beneficio es de 1.291,6 dólares.
Trailing de MA:

El beneficio es de 563,1 dólares.
Trailing de TEMA:

El beneficio es de 1.355,1 dólares.
Trailing de VIDYA:

El beneficio es de 283,3 dólares.
Así, la tabla final muestra los resultados del uso de diferentes tipos de trailings de StopLoss en relación con el comercio original:
| # | Tipo de trailing | Tamaño de StopLoss | Tamaño de TakeProfit | Beneficio total |
|---|---|---|---|---|
| 1 | Comercio original | 100 | 500 | - 658.0 |
| 2 | TrailingStop simple | Inicialmente 100, luego stop a 100 pips del precio | 0 | - 746.1 |
| 3 | TrailingStop de Parabolic SAR | Inicialmente 100, luego stop según el valor del indicador | 0 | + 541.8 |
| 4 | TrailingStop de VIDYA | Inicialmente 100, luego stop según el valor del indicador | 0 | -283.3 |
| 5 | TrailingStop de MA | Inicialmente 100, luego stop según el valor del indicador | 0 | + 563.1 |
| 6 | TrailingStop de AMA | Inicialmente 100, luego stop según el valor del indicador | 0 | + 806.5 |
| 7 | TrailingStop de FRAMA | Inicialmente 100, luego stop según el valor del indicador | 0 | + 1291.6 |
| 8 | TrailingStop de TEMA | Inicialmente 100, luego stop según el valor del indicador | 0 | + 1355.1 |
| 9 | TrailingStop de DEMA | Inicialmente 100, luego stop según el valor del indicador | 0 | + 1397.1 |
Como resultado, cuando el comercio original no resulta rentable, el trailing simple habitual, un análogo aproximado del trailing en el terminal cliente, arroja unas pérdidas aún mayores. Bien, veamos un trailing basado en Parabolic SAR, posicionado como un indicador que muestra retrocesos y niveles de stop (Stop And Reverse). Sí, siguiendo el nivel de StopLoss detrás de la línea indicadora en la primera barra, hemos obtenido un beneficio de 540 $.
Veamos ahora los resultados del desplazamiento de los stops de posición según los valores de los diferentes tipos de medias móviles en la primera barra. El trailing del indicador VIDYA ha mostrado unas pérdidas de 280 dólares, pero todos los demás han mostrado beneficios. Además, el seguimiento del stop detrás de la media móvil simple ha dado un beneficio mayor que el trailing sobre Parabolic SAR. Y el "campeón" absoluto en la mejora de los resultados comerciales ha sido la media móvil exponencial doble, +1397 dólares de beneficio.
Conclusión
Hoy hemos creado un asesor experto que nos permite probar nuestras transacciones en el simulador de estrategias y elegir el stop trailing de posición más adecuado para nuestro estilo comercial. Debemos considerar que todos los parámetros de indicadores utilizados en las pruebas de trailing tenían valores estándar y que los ajustes de los trailings se eligieron al azar, "a ojo".
La solución más correcta sería realizar pruebas para cada símbolo individual, y seleccionar una configuración de trailings aceptable considerando el movimiento y la volatilidad del par de divisas. Pero este asesor experto no tiene ajustes separados para establecer sus "propios" parámetros para cada símbolo individual negociado. Sin embargo, lo más importante es que hemos demostrado que es posible probar, analizar y mejorar las transacciones con la ayuda del simulador de estrategias. Y esto significa que usted podrá mejorar aún más el asesor experto para establecer sus propios parámetros para cada símbolo: no es algo difícil, lo más importante es querer.
Todos los archivos de la clase y el asesor experto analizados se adjuntan al artículo. También se adjunta un archivo del cual, tras descomprimirlo, podremos obtener de inmediato los archivos instalados para realizar la prueba en las carpetas necesarias del terminal.
Programas utilizados en el artículo:
| # | Nombre | Tipo | Descripción |
|---|---|---|---|
| 1 | Trailings.mqh | Biblioteca de clases | Biblioteca de clases de trailing |
| 2 | SymbolTrade.mqh | Biblioteca de clases | Biblioteca de clases de estructuras y transacciones, clase comercial del símbolo |
| 3 | SymbolTradeExt.mqh | Biblioteca de clases | Símbolo y clase comercial |
| 4 | TradingByHistoryDeals_Ext.mq5 | Asesor | Asesor experto para ver y cambiar StopLoss, TakeProfit y trailings en el simulador de estrategias de transacciones y operaciones negociadas en la cuenta. |
| 5 | MQL5.zip | Archivo | Archivo con los ficheros presentados anteriormente para desempaquetar en el directorio MQL5 del terminal cliente. |
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/16991
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.
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 4): Analytics Forecaster EA
Perspectivas bursátiles a través del volumen: Confirmación de tendencias
Algoritmo de trading evolutivo con aprendizaje por refuerzo y extinción de individuos no rentables (ETARE)
Redes neuronales en el trading: Aprendizaje contextual aumentado por memoria (Final)
- 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
Трал по VIDYA:
Beneficio - 283,3 dólares.Error: Pérdida - 283,3 dólares.
Error: Pérdida de 283,3 dólares.
En el artículo está escrito el valor negativo del beneficio.
Sin embargo, el espacio después del signo menos se insertó accidentalmente.