English Русский 中文 Deutsch 日本語 Português
preview
Características del Wizard MQL5 que debe conocer (Parte 1): Análisis de regresión

Características del Wizard MQL5 que debe conocer (Parte 1): Análisis de regresión

MetaTrader 5Probador | 29 agosto 2022, 15:47
375 0
Stephen Njuki
Stephen Njuki

1. Introducción

El Wizard MQL5 permite crear y ejecutar asesores rápidamente, ya que la mayoría de los aspectos secundarios del trading están precodificados en la biblioteca MQL5. Esto permite a los tráders centrarse en aspectos específicos de su comercio, como las condiciones de entrada y salida. La biblioteca incluye algunas clases de señal de entrada y salida como Accelerator Oscillator, Adaptive Moving Average y muchas otras. No obstante, estas se basan en indicadores con retraso. Además, resulta bastante difícil convertirlas en estrategias exitosas. Por lo tanto, la capacidad de crear nuestras propias señales resulta especialmente importante. En este artículo, veremos cómo podemos hacer esto usando el análisis de regresión.


2. Creando la clase

2.1 Según la Wikipedia, elanálisis de regresión es un conjunto de métodos estadísticos que sirve para investigar el efecto de una o más variables independientes sobre una variable dependiente. Puede ser útil para los asesores personalizados porque los datos de precio son una serie temporal. El análisis comprueba la capacidad que los precios anteriores y las variaciones de precio anteriores tienen de influir en los precios y las variaciones de precio futuros. El análisis de regresión puede representarse usando la siguiente ecuación eqn_1

donde y es la variable dependiente y, por tanto, la variable predicha depende de los valores anteriores x, cada uno con su propio coeficiente β y error ε. Los valores x e y pueden considerarse el nivel de precios anterior y previsto, respectivamente. Además de trabajar con los niveles de precio, las variaciones de precio también pueden estudiarse de forma similar. La incógnita y depende de xs, βs y ε. De estas, solo conocemos xs y β0 (la intersección y). La intersección y se conoce porque es el precio inmediatamente anterior a x i 1. Por lo tanto, tenemos que encontrar el correspondiente βs para cada xy luego para ε Como cada x i 1 era y I en el punto temporal anterior de la serie temporal, podemos encontrar una solución para los valores de βusando ecuaciones simultáneas. Por ejemplo, si el siguiente cambio de precio depende solo de los dos cambios anteriores, nuestra ecuación actual podría ser: eqn_2
Y las ecuaciones anteriores serían: eqn_3

Dado que estamos valorando el error ε por separado, podemos resolver dos ecuaciones simultáneas para los valores de β. La numeración de los valores de x en la fórmula de la Wikipedia no sigue el formato de "fila" de MQL5, es decir, cuando la x con el número más alto es la última. Así, hemos renumerado los valores de x en las dos ecuaciones anteriores para mostrar cómo convertirlas en simultáneas. Empezaremos de nuevo con las intersecciones y de xi1 y xi0 para representar β0 en la ecuación 1. La resolución de ecuaciones simultáneas se procesa mejor usando matrices, así la eficiencia es mayor. Las herramientas necesarias están disponibles en la biblioteca MQL5.

2.2 La biblioteca MQL5 dispone de una amplia colección de clases estadísticas y algoritmos generales que eliminan claramente la necesidad de codificarlos desde cero. El código de la biblioteca está abierto para cualquiera, podrá comprobarlo usted mismo. Para alcanzar nuestros objetivos, usaremos la función RMatrixSolve en la clase CDenseSolver en el archivo solvers.mqh. Esta función se basa en el uso de la descomposición matricial LU para encontrar de forma rápida y eficaz una solución para los valores β. Podrá encontrar artículos sobre este tema en el archivo de MetaQuotes. En la Wikipedia también hay una explicación

Antes de empezar a buscar soluciones a los valores β, en el caso de la clase CExpertSignal, resultaría útil echar un vistazo a su estructura, ya que es la base de nuestra clase. Casi todas las clases de señal de los asesores que se pueden construir en el Wizard tienen las funciones LongCondition y ShortCondition. Estas retornan un valor que establece si debemos abrir una posición larga o corta respectivamente. Este valor debe ser un número entero entre 0 y 100, para que coincida con los parámetros de entrada del Wizard Signal_ThresholdOpen y Signal_ThresholdClose. Por lo general, al comerciar, necesitamos que nuestras condiciones de cierre sean menos conservadoras que las de apertura. Esto significa que el umbral de apertura será mayor que el umbral de cierre. Así, al desarrollar la señal, tendremos parámetros de entrada para calcular el umbral de cierre y parámetros de entrada separados pero similares para el umbral de apertura. A la hora de calcular la condición, la elección de las entradas vendrá determinada por el hecho de si tenemos o no posiciones abiertas. Si tenemos posiciones abiertas, usaremos los parámetros de cierre. Si no hay posiciones, usaremos los parámetros de apertura. A continuación, le mostramos el listado de la interfaz de la clase de señal de nuestro asesor, que posee estos dos conjuntos de parámetros.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CSignalDUAL_RA : public CExpertSignal
  {
protected:
   CiMA              m_h_ma;             // highs MA handle
   CiMA              m_l_ma;             // lows MA handle
   CiATR             m_ATR;
   //--- adjusted parameters
   int               m_size;
   double            m_open_determination,m_close_determination;
   int               m_open_collinearity,m_open_data,m_open_error;
   int               m_close_collinearity,m_close_data,m_close_error;
public:
                     CSignalDUAL_RA();
                    ~CSignalDUAL_RA();
   //--- methods of setting adjustable parameters
   
   //--- PARAMETER FOR SETTING THE NUMBER OF INDEPENDENT VARIABLES
   void              Size(int value)                  { m_size=value;                  }
   
   //--- PARAMETERS FOR SETTING THE OPEN 'THRESHOLD' FOR THE EXPERTSIGNAL CLASS
   void              OpenCollinearity(int value)      { m_open_collinearity=value;     }
   void              OpenDetermination(double value)  { m_open_determination=value;    }
   void              OpenError(int value)             { m_open_error=value;            }
   void              OpenData(int value)              { m_open_data=value;             }
   
   //--- PARAMETERS FOR SETTING THE CLOSE 'THRESHOLD' FOR THE EXPERTSIGNAL CLASS
   void              CloseCollinearity(int value)     { m_close_collinearity=value;    }
   void              CloseDetermination(double value) { m_close_determination=value;   }
   void              CloseError(int value)            { m_close_error=value;           }
   void              CloseData(int value)             { m_close_data=value;            }
   
   //--- method of verification of settings
   virtual bool      ValidationSettings(void);
   //--- method of creating the indicator and timeseries
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- methods for detection of levels of entering the market
   virtual bool      OpenLongParams(double &price,double &sl,double &tp,datetime &expiration);
   virtual bool      OpenShortParams(double &price,double &sl,double &tp,datetime &expiration);
   //--- methods of checking if the market models are formed
   virtual int       LongCondition(void);
   virtual int       ShortCondition(void);
protected:
   //--- method of initialization of the oscillator
   bool              InitRA(CIndicators *indicators);
   //--- methods of getting data
   int               CheckDetermination(int ind,bool close);
   double            CheckCollinearity(int ind,bool close);
   //
   double            GetY(int ind,bool close);
   double            GetE(int ind,bool close);
   
   double            Data(int ind,bool close);
   //
  };

Y más abajo, podrá ver el listado de las funciones LongCondition y ShortCondition. 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSignalDUAL_RA::LongCondition(void)
   {
      int _check=CheckDetermination(0,PositionSelect(m_symbol.Name()));
      if(_check>0){ return(_check); }
      
      return(0);
   }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSignalDUAL_RA::ShortCondition(void)
   {
      int _check=CheckDetermination(0,PositionSelect(m_symbol.Name()));
      if(_check<0){ return((int)fabs(_check)); }
      
      return(0);
   }

 Para encontrar una solución para los valores β, utilizaremos la función GetY. A continuación, le mostramos el listado.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CSignalDUAL_RA::GetY(int ind,bool close)
  {
      double _y=0.0;
      
      CMatrixDouble _a;_a.Resize(m_size,m_size);
      double _b[];ArrayResize(_b,m_size);ArrayInitialize(_b,0.0);
      
      for(int r=0;r<m_size;r++)
      {
         _b[r]=Data(r,close);
         
         for(int c=0;c<m_size;c++)
         {
            _a[r].Set(c,Data(r+c+1, close));
         }
      }
      
      int _info=0;
      CDenseSolver _S;
      CDenseSolverReport _r;
      double _x[];ArrayResize(_x,m_size);ArrayInitialize(_x,0.0);
      
      _S.RMatrixSolve(_a,m_size,_b,_info,_r,_x);
      
      for(int r=0;r<m_size;r++)
      {
         _y+=(Data(r,close)*_x[r]);
      }
      //---
      return(_y);
  }

La función Data alternará entre los cambios en el precio de cierre del símbolo comerciado o los cambios en la media móvil del mismo precio de cierre. La opción usada será determinada por el parámetro de entrada m_open_data o m_close_data, dependiendo de si estamos calculando un umbral de apertura o un umbral de cierre. El listado para seleccionar los datos se muestra en la siguiente enumeración.

enum Edata
  {
      DATA_TREND=0,        // changes in moving average close
      DATA_RANGE=1         // changes in close
  };

A continuación, le mostramos la función Data, que se encarga de seleccionar los datos.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CSignalDUAL_RA::Data(int ind,bool close)
   {
      if(!close)
      {
         if(Edata(m_open_data)==DATA_TREND)
         {
            m_h_ma.Refresh(-1);
            return((m_l_ma.Main(StartIndex()+ind)-m_l_ma.Main(StartIndex()+ind+1))-(m_h_ma.Main(StartIndex()+ind)-m_h_ma.Main(StartIndex()+ind+1)));
         }
         else if(Edata(m_open_data)==DATA_RANGE)
         {
            return((Low(StartIndex()+ind)-Low(StartIndex()+ind+1))-(High(StartIndex()+ind)-High(StartIndex()+ind+1)));
         }
      }
      else if(close)
      {
         if(Edata(m_close_data)==DATA_TREND)
         {
            m_h_ma.Refresh(-1);
            return((m_l_ma.Main(StartIndex()+ind)-m_l_ma.Main(StartIndex()+ind+1))-(m_h_ma.Main(StartIndex()+ind)-m_h_ma.Main(StartIndex()+ind+1)));
         }
         else if(Edata(m_close_data)==DATA_RANGE)
         {
            return((Low(StartIndex()+ind)-Low(StartIndex()+ind+1))-(High(StartIndex()+ind)-High(StartIndex()+ind+1)));
         }
      }
      
      return(0.0);
   }

Una vez que tenemos los valores de β, podemos proceder a calcular el error.

2.3  El error estándar, según la Wikipedia, se calcula según la fórmula siguiente.

eqn_4  

donde s es la desviación estándar y n es el tamaño de la muestra. El error sirve para recordar que no todas las previsiones, por muy minuciosas que sean, resultarán siempre exactas al 100%. Siempre deberemos esperar y considerar los errores de nuestra parte. La desviación estándar indicada en la fórmula se medirá entre nuestros valores previstos y los reales. Para realizar la comparación, también podemos observar la última diferencia entre nuestros valores previstos y los reales. Podemos seleccionar estos dos parámetros de la lista que aparece a continuación. 

enum Eerror
  {
      ERROR_LAST=0,        // use the last error
      ERROR_STANDARD=1     // use standard error
  }

La función GetE retornará entonces nuestra estimación del error dependiendo del parámetro de entrada m_open_error o m_close_error usando la fórmula anterior. A continuación, le mostramos el listado.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CSignalDUAL_RA::GetE(int ind,bool close)
  {
      if(!close)
      {
         if(Eerror(m_open_error)==ERROR_STANDARD)
         {
            double _se=0.0;
            for(int r=0;r<m_size;r++) { _se+=pow(Data(r,close)-GetY(r+1,close),2.0); }
            _se=sqrt(_se/(m_size-1)); _se=_se/sqrt(m_size); return(_se);
         }
         else if(Eerror(m_open_error)==ERROR_LAST){ return(Data(ind,close)-GetY(ind+1,close)); }
      }
      else if(close)
      {
         if(Eerror(m_close_error)==ERROR_STANDARD)
         {
            double _se=0.0;
            for(int r=0;r<m_size;r++){  _se+=pow(Data(r,close)-GetY(r+1,close),2.0); }
            _se=sqrt(_se/(m_size-1)); _se=_se/sqrt(m_size); return(_se);
         }
         else if(Eerror(m_close_error)==ERROR_LAST){ return(Data(ind,close)-GetY(ind+1,close)); }
      }
//---
      return(Data(ind,close)-GetY(ind+1,close));
  }


Como hemos dicho, el hecho de si tenemos alguna posición abierta o no determinará el uso de m_open_error o m_close_error. Una vez tengamos una estimación del error, podremos hacer una predicción aproximada de y. Sin embargo, el análisis de regresión también tiene sus trampas ocultas. Una de ellas es la posibilidad de que las variables independientes resulten demasiado similares y, por tanto, sobreestimen el valor predicho. Este fenómeno se denomina colinealidad y merece la pena estudiarlo con mayor detenimiento.

2.4 La multicolinealidad (definición de la Wikipedia) se puede caracterizar como la presencia de una relación fuerte entre dos o más variables independientes en un modelo de regresión múltiple (Investopedia). La colinealidad no tiene una fórmula como tal, y se detecta usando el factor de inflación de la varianza VIF (variance inflation factor). Este factor se mide según todas las variables independientes (x) para ayudar a entender cómo cada una de estas variables es única para predecir y. La fórmula necesaria se muestra a continuación. En él, R es la regresión de cada variable independiente respecto a las demás.

eqn_5

Teniendo en consideración la colinealidad, para alcanzar nuestro objetivo, tomaremos la correlación inversa de Spearman entre los dos últimos conjuntos de datos de las variables independientes y la normalizaremos. La longitud de nuestros conjuntos de datos será determinada por el parámetro de entrada m_size, con una longitud mínima de 3. Al realizar la normalización, simplemente la restamos de dos e invertimos el resultado. Este será el peso normalizado, que puede entonces multiplicarse por la estimación del error o el valor previsto, o ambos, o no utilizarse en general. Estos parámetros se indicarán en la siguiente enumeración.

enum Echeck
  {
      CHECK_Y=0,           // check for y only
      CHECK_E=1,           // check for the error only
      CHECK_ALL=2,         // check for both the y and the error
      CHECK_NONE=-1        // do not use collinearity checks
  };

La selección del peso a aplicar también se indica usando el parámetro de entrada m_open_collinearity o m_close_collinearity. Una vez más, dependiendo de si hay posiciones abiertas o no. A continuación, le mostramos el listado de CheckCollinearity.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CSignalDUAL_RA::CheckCollinearity(int ind,bool close)
  {
      double _check=0.0;
      double _c=0.0,_array_1[],_array_2[],_r=0.0;
      ArrayResize(_array_1,m_size);ArrayResize(_array_2,m_size);
      ArrayInitialize(_array_1,0.0);ArrayInitialize(_array_2,0.0);
      for(int s=0; s<m_size; s++)
      {
         _array_1[s]=Data(ind+s,close);
         _array_2[s]=Data(m_size+ind+s,close);
      }
      _c=1.0/(2.0+fmin(-1.0,MathCorrelationSpearman(_array_1,_array_2,_r)));
      
      double   _i=Data(m_size+ind,close),    //y intercept
               _y=GetY(ind,close),           //product sum of x and its B coefficients
               _e=GetE(ind,close);           //error
      
      
      
      if(!close)
      {
         if(Echeck(m_open_collinearity)==CHECK_Y){ _check=_i+(_c*_y)+_e;          }
         else if(Echeck(m_open_collinearity)==CHECK_E){ _check=_i+_y+(_c*_e);     }
         else if(Echeck(m_open_collinearity)==CHECK_ALL){ _check=_i+(_c*(_y+_e)); }
         else if(Echeck(m_open_collinearity)==CHECK_NONE){ _check=_i+(_y+_e);     }
      }
      else if(close)
      {
         if(Echeck(m_close_collinearity)==CHECK_Y){ _check=_i+(_c*_y)+_e;          }
         else if(Echeck(m_close_collinearity)==CHECK_E){ _check=_i+_y+(_c*_e);     }
         else if(Echeck(m_close_collinearity)==CHECK_ALL){ _check=_i+(_c*(_y+_e)); }
         else if(Echeck(m_close_collinearity)==CHECK_NONE){ _check=_i+(_y+_e);     }
      }
      
//---
      return(_check);
  }

Además de comprobar la colinealidad, hay casos en los que el análisis de regresión no es tan predecible como podría serlo, debido a los cambios externos en el mercado. Para monitorear dichas eventualidades y medir la capacidad de las variables independientes de nuestra señal para influir en nuestra variable dependiente, usaremos el coeficiente de determinación.

2.5 El coeficiente de determinación es una medida estadística que estudia cómo las diferencias en una variable pueden ser explicadas por las diferencias en una segunda variable al predecir el resultado de un evento (Investopedia). La Wikipedia ofrece una definición más completa, y nuestras fórmulas, mostradas a continuación, están tomadas de allí

  eqn_6

Fórmula de la suma de los cuadrados (donde y es el valor real y f es el valor previsto),  


eqn_7

Fórmula de la suma de los valores finales (donde y es el valor real e ÿ es la media móvil de esos valores),  


eqn_8

El coeficiente propiamente dicho es R al cuadrado. 

El coeficiente mide el grado en el que xs influye en y. Esto es importante porque, como hemos mencionado antes, hay periodos en los que la regresión está en declive, lo cual significa que resulta más seguro mantenerse alejado de los mercados. Monitoreando esto con ayuda de un filtro, resulta más probable que comerciemos cuando el sistema sea fiable. Por regla general, necesitamos que el coeficiente sea superior a 0, siendo 1 el valor ideal. El parámetro de entrada usado para definir nuestro umbral será m_open_determination o m_close_determination, de nuevo en función del número de posiciones abiertas. Si el coeficiente de determinación calculado por la función CheckDetermination a continuación es menor que este parámetro, la condición de compra o venta retornará cero.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSignalDUAL_RA::CheckDetermination(int ind,bool close)
  {
      int _check=0;
      m_h_ma.Refresh(-1);m_l_ma.Refresh(-1);
      double _det=0.0,_ss_res=0.0,_ss_tot=0.0;
      for(int r=0;r<m_size;r++)
      {
         _ss_res+=pow(Data(r,close)-GetY(r+1,close),2.0); 
         _ss_tot+=pow(Data(r,close)-((m_l_ma.Main(r)-m_l_ma.Main(r+1))-(m_h_ma.Main(r)-m_h_ma.Main(r+1))),2.0);
      }
      
      if(_ss_tot!=0.0)
      {
         _det=(1.0-(_ss_res/_ss_tot));
         if(_det>=m_open_determination)
         {
            double _threshold=0.0;
            for(int r=0; r<m_size; r++){ _threshold=fmax(_threshold,fabs(Data(r,close))); }
         
            double _y=CheckCollinearity(ind,close);
            
            _check=int(round(100.0*_y/fmax(fabs(_y),fabs(_threshold))));
         }
      }
//---
      return(_check);
  }

En cuanto podamos comprobar el coeficiente de determinación, tendremos una señal de trabajo. Después, integraremos esta señal en el asesor usando el Wizard MQL5.


3. Montando con el Wizard MQL5

3.1 Al montar un asesor, podemos utilizar una lista de códigos auxiliares personalizados junto con el código del Wizard MQL5. Esto resulta completamente opcional, y dependerá del estilo de trabajo del tráder. Siguiendo el propósito de este artículo, veremos la apertura de una orden pendiente personalizada basada en el símbolo ATR predominante, así como el seguimiento de posiciones abiertas basado en el mismo indicador. No usaremos niveles de take profit.

3.1.1  Las órdenes pendientes basadas en ATR se puede colocar sobrecargando las funciones OpenLongParams y OpenShortParams y configurándolas en nuestra clase de señal, como mostramos a continuación.

//+------------------------------------------------------------------+
//| Detecting the levels for buying                                  |
//+------------------------------------------------------------------+
bool CSignalDUAL_RA::OpenLongParams(double &price,double &sl,double &tp,datetime &expiration)
  {
   CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL;
//---
   if(general==NULL)
     {
      m_ATR.Refresh(-1);
      //--- if a base price is not specified explicitly, take the current market price
      double base_price=(m_base_price==0.0) ? m_symbol.Ask() : m_base_price;
      
      //--- price overload that sets entry price to be based on ATR
      price      =m_symbol.NormalizePrice(base_price-(m_price_level*(m_ATR.Main(0)/m_symbol.Point()))*PriceLevelUnit());
      
      sl         =0.0;
      tp         =0.0;
      expiration+=m_expiration*PeriodSeconds(m_period);
      return(true);
     }
//---
   return(general.OpenLongParams(price,sl,tp,expiration));
  }
//+------------------------------------------------------------------+
//| Detecting the levels for selling                                 |
//+------------------------------------------------------------------+
bool CSignalDUAL_RA::OpenShortParams(double &price,double &sl,double &tp,datetime &expiration)
  {
   CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL;
//---
   if(general==NULL)
     {
      m_ATR.Refresh(-1);
      //--- if a base price is not specified explicitly, take the current market price
      double base_price=(m_base_price==0.0) ? m_symbol.Bid() : m_base_price;
      
      //--- price overload that sets entry price to be based on ATR
      price      =m_symbol.NormalizePrice(base_price+(m_price_level*(m_ATR.Main(0)/m_symbol.Point()))*PriceLevelUnit());
      
      sl         =0.0;
      tp         =0.0;
      expiration+=m_expiration*PeriodSeconds(m_period);
      return(true);
     }
//---
   return(general.OpenShortParams(price,sl,tp,expiration));
  }

El asesor experto generado por el Wizard MQL5 tiene el parámetro de entrada Signal_PriceLevel. Por defecto es cero, pero si le asignamos un valor, ajustará la distancia en pips del precio del símbolo negociado respecto al precio actual al que se colocará la orden de mercado. Cuando este parámetro es negativo, colocamos órdenes stop. Cuando colocamos órdenes límite positivas, el parámetro es del tipo double. Para lo que nosotros pretendemos, este parámetro será una fracción o múltiplo de los puntos de precio actuales en ATR.

3.1.2 La clase de trailing ATR es también una clase configurable CExpertTrailing que usa ATR para establecer y desplazar el stop loss. La implementación de sus funciones clave se muestra en el listado a continuación.

//+------------------------------------------------------------------+
//| Checking trailing stop and/or profit for long position.          |
//+------------------------------------------------------------------+
bool CTrailingATR::CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp)
  {
//--- check
   if(position==NULL)
      return(false);
//---
   m_ATR.Refresh(-1);
   double level =NormalizeDouble(m_symbol.Bid()-m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits());
   
   //--- sl adjustment to be based on ATR
   double new_sl=NormalizeDouble(level-(m_atr_weight*(m_ATR.Main(0)/m_symbol.Point())),m_symbol.Digits());
   
   double pos_sl=position.StopLoss();
   double base  =(pos_sl==0.0) ? position.PriceOpen() : pos_sl;
//---
   sl=EMPTY_VALUE;
   tp=EMPTY_VALUE;
   if(new_sl>base && new_sl<level)
      sl=new_sl;
//---
   return(sl!=EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Checking trailing stop and/or profit for short position.         |
//+------------------------------------------------------------------+
bool CTrailingATR::CheckTrailingStopShort(CPositionInfo *position,double &sl,double &tp)
  {
//--- check
   if(position==NULL)
      return(false);
//---
   m_ATR.Refresh(-1);
   double level =NormalizeDouble(m_symbol.Ask()+m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits());
   
   //--- sl adjustment to be based on ATR
   double new_sl=NormalizeDouble(level+(m_atr_weight*(m_ATR.Main(0)/m_symbol.Point())),m_symbol.Digits());
   
   double pos_sl=position.StopLoss();
   double base  =(pos_sl==0.0) ? position.PriceOpen() : pos_sl;
//---
   sl=EMPTY_VALUE;
   tp=EMPTY_VALUE;
   if(new_sl<base && new_sl>level)
      sl=new_sl;
//---
   return(sl!=EMPTY_VALUE);
  }

m_atr_weight será un parámetro optimizable, al igual que m_price_level, que establece lo cerca que podemos monitorear las posiciones abiertas.

  3.2 El montaje posterior en el Wizard resulta bastante sencillo. El único paso notable será la selección de nuestra señal, como se muestra a continuación.

wizard_1_crop


 

Asimismo, deberemos añadir nuestro método de trailing personalizado.

wizard_2_crop


 

4. Pruebas en el simulador de estrategias

4.1 Después de montar el asesor en el Wizard MQL5, deberemos realizar la compilación para crear el archivo del asesor. Esto también garantizará la ausencia de errores en nuestro código.

4.2 Los parámetros de entrada del asesor experto, por defecto, también deben ser establecidos en la pestaña de parámetros de entrada del Simulador de Estrategias. Asegúrese de que Signal_TakeLevel y Signal_StopLevel sean iguales a cero. Esto se debe a que, para este artículo, la salida solo se determina mediante el trailing stop o el parámetro de entrada Signal_ThresholdClose.

4.3 Lo ideal sería que la optimización se realizara con los ticks reales del bróker con el que se pretende comerciar. En este artículo, optimizaremos la pareja EURUSD en el marco temporal de 4 horas en un periodo en forma de V desde el 2018.01.01 hasta el 2021.01.01. Para realizar una comparación, ejecutaremos dos optimizaciones: la primera solo usará órdenes de mercado, mientras que la segunda permitirá órdenes pendientes. No he escrito "permitirá" porque sí. Seguiremos considerando solo el uso de órdenes de mercado, dado que Signal_PriceLevel puede ser igual a cero, ya que la optimización pasa de un valor negativo a uno positivo. La optimización se puede ajustar de antemano si utilizamos órdenes pendientes. La única diferencia con la variante que no usa órdenes pendientes es que, en este último caso, el parámetro de entrada Signal_PriceLevel seguirá siendo 0 y no formará parte de los datos de entrada optimizados. 


4.4 A continuación, le presentamos los resultados de nuestra optimización. En primer lugar, tenemos el informe y la curva de equidad de los mejores resultados comerciales solo con órdenes de mercado.



Parte 1 del informe

Y luego un informe y una curva de equidad similares al utilizar órdenes pendientes.




Parte 2 del informe

Nuestra señal de análisis de regresión parece beneficiarse del uso de órdenes pendientes, ya que muestra menos reducción con un beneficio ligeramente menor. Podemos intentar mejorar el sistema, por ejemplo, cambiando la clase de trailing stop o el tipo de gestión de dinero. Para nuestras pruebas, hemos usado un porcentaje de margen fijo y hemos optimizado nuestro conjunto de criterios, establecidos en "complex criterion". Le recomendamos hacer pruebas exhaustivas con datos históricos de ticks y realizar un número suficiente de pruebas forward antes de aplicar recomendaciones que queden fuera del alcance de este artículo.

 

5. Conclusión

5.1 El Wizard MQL5 es una poderosa herramienta que debería encontrarse en el arsenal de todo tráder. En este artículo, hemos visto cómo utilizar algunos conceptos estadísticos del análisis de regresión, como la colinealidad y el coeficiente de determinación, como base para un sistema comercial sólido. Como próximo paso, realizaremos pruebas exhaustivas con datos históricos de ticks y estudiaremos si esta señal puede combinarse con otras señales únicas basadas en la experiencia del tráder o las señales incorporadas de la biblioteca MQL5 para crear un sistema comercial más completo. Al igual que el resto de los futuros artículos de esta serie, este artículo no ofrece el Grial, sino que más bien describe un proceso que puede ajustarse en función del enfoque que el tráder tenga sobre los mercados. ¡Gracias por su atención!


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

Archivos adjuntos |
TrailingATR.mqh (6.24 KB)
SignalDUAL_RA.mqh (18.75 KB)
Aprendizaje automático y data science (Parte 05): Árboles de decisión usando como ejemplo las condiciones meteorológicas para jugar al tenis Aprendizaje automático y data science (Parte 05): Árboles de decisión usando como ejemplo las condiciones meteorológicas para jugar al tenis
Los árboles de decisión clasifican los datos imitando la forma de pensar de los seres humanos. En este artículo, veremos cómo construir árboles de decisión y usar estos para clasificar y predecir datos. El objetivo principal del algoritmo del árbol de decisión es dividir la muestra en datos con "impurezas" y en datos "limpios" o próximos a los nodos.
Metamodelos en el aprendizaje automático y el trading: Timing original de las órdenes comerciales Metamodelos en el aprendizaje automático y el trading: Timing original de las órdenes comerciales
Metamodelos en el aprendizaje automático: Creación automática de sistemas comerciales sin apenas intervención humana: el Modelo decide por sí mismo cómo y cuándo comerciar.
Redes neuronales: así de sencillo (Parte 18): Reglas asociativas Redes neuronales: así de sencillo (Parte 18): Reglas asociativas
Como continuación de esta serie, hoy presentamos otro tipo de tarea relacionada con los métodos de aprendizaje no supervisado: la búsqueda de reglas asociativas. Este tipo de tarea se usó por primera vez en el comercio minorista para analizar las cestas de la compra. En este artículo, hablaremos de las posibilidades que ofrece el uso de dichos algoritmos en el trading.
Aprendiendo a diseñar un sistema de trading con Volumes Aprendiendo a diseñar un sistema de trading con Volumes
En este nuevo artículo de la serie sobre la creación de sistemas comerciales basados en indicadores técnicos populares, hablaremos del indicador Volumes. El volumen como concepto es un factor importante en el comercio en los mercados financieros y, por tanto, debe tenerse siempre en cuenta. En este artículo, aprenderemos a desarrollar un sistema comercial basado en el indicador Volumes.