English Русский 中文 Deutsch 日本語 Português
Colocando las entradas por los indicadores

Colocando las entradas por los indicadores

MetaTrader 5Probador | 13 diciembre 2017, 08:33
2 232 0
Dmitriy Gizlyk
Dmitriy Gizlyk

Introducción

Por favor, responda a una pregunta, ¿cuando Usted observa una serie de transacciones rentables, le entran ganas de repetir su estrategia? O, tal vez, al repasar su estrategia comercial, ¿se ha puesto a pensar en cómo se puede evitar las transacciones no rentables? Creo que la mayoría de Ustedes dan una respuesta positiva por lo menos a una de estas preguntas. En este artículo, quiero proponer una técnica de distribución del historial de las transacciones por los indicadores, también contaré cómo seleccionar los indicadores que ayudarán a subir la eficacia del trading.

1. Planteamiento del problema

En el artículo anterior ya contaba sobre la construcción del Asesor Experto (EA) a base del filtro de Kalman. Durante la simulación, él mostró beneficios, pero también reveló 2 puntos débiles en la estrategia: una salida retardada y una serie de transacciones con pérdidas en el flat.

Pues bien, nuestra tarea consiste en reducir el número de transacciones con pérdidas de esta estrategia. Para eso, guardaremos los valores de la serie de indicadores en el momento de la apertura de las posiciones. Luego realizaremos el análisis y compararemos los valores de los indicadores con los resultados de las transacciones. Nos quedará sólo elegir los indicadores que ayudarán a mejorar los resultados del trading.

Primero, hagamos un plan de acciones.

  1. Elegimos el período de la simulación. Lo probamos y guardamos el informe.
  2. Analizamos sintácticamente (parsing) el informe de la simulación y creamos el array de transacciones (con resultado de operaciones).
  3. Aclaramos la lista de los indicadores a usar y el formato de almacenamiento de los datos. Preparamos las clases para el uso posterior.
  4. Preparamos los formularios de los informes para mostrar los resultados.
  5. Creamos el Asesor Experto analítico.
  6. Iniciamos el Asesor Experto analítico en el Probador de estrategias y analizamos los informes.
  7. Añadimos los indicadores necesarios al EA.
  8. Probamos el EA actualizado y comparamos los resultados.

2. La primera simulación del EA a probar

En el artículo arriba mencionado, el EA realizó 150 transacciones durante un mes. Esta cantidad no es suficiente para un análisis estadístico. Para la representación de los resultados, vamos a aumentar el período de prueba en ocho veces. Sin ninguna optimización, establecemos el período para la construcción de una función autoregresiva en 3120 barras (cerca de 3 meses) e iniciamos la prueba.

La primera simulación.La primera simulación

Los resultados de la simulación nos indican que hemos obtenido un gráfico del balance evidentemente con pérdidas, donde después de 1 o 2 transacciones rentables tenemos una serie de transacciones con pérdidas. En total, la parte de las transacciones rentables ha llegado a un poco menos de 34%. Aunque el tamaño medio de ganancias supera el tamaño medio de pérdidas en 45%, eso no es suficiente para obtener el benenficio durante el período completo de la simulación.

La primera simulación

En el gráfico de precios se puede observar que cuando no hay una tendencia evidente (estando en flat), el EA abre y cierra las posiciones con pérdidas. Nuestra tarea consiste en disminuir la cantidad de estas transacciones, y si es posible, evitarlas por completo.

Gráfico de simulación

En primer lugar, hay que guardar el informe de la simulación para su procesamiento posterior. Pero aquí hay un momento importante: por razones de seguridad el trabajo con los archivos en el lenguaje MQL5 se encuentra bajo un estricto control. Los archivos con los que se realizan las operaciones utilizando los medios del lenguaje MQL5 deben encontrarse dentro del entorno protegido de archivos (file sandbox). Por eso, tenemos que guardar el informe dentro de este entorno. Pero puesto que vamos a iniciar el programa en el Probador de estrategias, también hay que tomar en cuenta que cada agente trabaja en su propio entorno protegido. Por tanto, para que durante la simulación en cualquier agente, el programa pueda obtener el acceso al informe, lo guardaremos en la carpeta compartida de los terminales.

Para averiguar la ruta hacia la carpeta compartida de los terminales de cliente, en MetaEditor abrimos el menú "File" y seleccionamos el submenú "Open Common Data Folder".

Ruta hacia file sandbox

En la ventana que se abre, vamos a la carpeta "Files".

Ruta hacia file sandbox

Luego, copiamos la línea de la ruta al portapapeles usando la combinación "Ctrl+C".

Ruta hacia file sandbox

Ya sabemos la ruta hacia file sandbox, y ahora podemos guardar nuestro informe de la prueba. Para eso en "Strategy Tester" seleccionamos la pestaña "Result" y hacemos el clic derecho en cualquier lugar. En el menú que se abre, seleccionamos "Report" -> "HTML (Internet Explorer)".

Guardando el informe.

Cuando ejecutemos estas operaciones, se abrirá la ventana de sistema para guardar el archivo. Primero, insertamos la ruta a nuestro file sandbox en el campo del nombre del archivo, y pulsamos «Guardar». Esta operación cambiará la carpeta para guardar el archivo.

Guardando el informe.

En el siguiente paso, indicamos el nombre para guardar el informe de la prueba y guardamos el archivo.

Guardando el informe.

Después de guardar el informe en file sandbox, pasamos a la siguiente fase de nuestro trabajo, a la creación del array de transacciones para el análisis posterior.

3. Creando el arrays de transacciones

3.1. Idea general sobre el análisis sintáctico

En el apartado anterior, hemos guardado el informe de simulación del EA. Ahora, procedemos a formar un array conveniente para el procesamiento a su base. Nosotros vemos la lista de transacciones en el navegador, pero los programas MQL5 no pueden cargar el array de datos desde el archivo html directamente. Por eso, hay que realizar el análisis sintáctico del informe.

Lista de transacciones en el informe.

En realidad, el archivo html representa un texto dividido por las etiquetas que describen su formateo y el dise;o. Al abrir el informe en un editor del texto, Usted puede encontrar fácilmente 2 etiquetas "<table>", lo que significa que todos los datos en el informe están divididos en 2 tablas de datos. La información sobre las transacciones se encuentra en la segunda tabla. La información sobre las órdenes se encuentra en su principio, le sigue la información sobre las transacciones.

Vista del informe HTML.

Las etiquetas "<tr>...</tr>" sirven para marcar las líneas de la tabla. Para dividir la información en celdas dentro de las líneas, se usan las etiquetas "<td>...</td>".

3.2. Clase para almacenar la información sobre la transacción

Ya nos hemos aclarado con el formato de la representación de los datos en el informe. Ahora pasaremos al formato del guardado de los datos dentro de nuestro array. Puesto que el EA analizado trabaja sólo con un instrumento, se puede no guardar el nombre de este instrumento. No obstante, lo vamos a necesitar para inicializar los indicadores. Al final, la estructura de la escritura para la transacción incluirá las siguientes posiciones:

  • hora de la apertura de la posición;
  • volumen de la apertura de la posición;
  • dirección de la transaccción;
  • volumen del cierre de la posición;
  • importe de la comisión;
  • importe del swap;
  • importe del beneficio.

Hemos aclarado los aspectos esenciales de esta fase del trabajo. Vamos a empezar a escribir el código. Priemro, crearemos la clase de la transacción CDeal.

class CDeal       :  public CObject
  {
private:
   datetime          OpenTime;         // Time of open position
   double            OpenedVolume;     // Volume of opened position
   ENUM_POSITION_TYPE Direct;          // Direct of opened position
   double            ClosedVolume;     // Closed volume
   double            Comission;        // Comission to position
   double            Swap;             // Swap of position
   double            Profit;           // Profit of position
   
public:
                     CDeal();
                    ~CDeal();
  };

La inicialización de la clase va a realizarse durante la escritura de una nueva transacción abierta, cuando ya sepamos la hora de la apertura de la posición, el volumen y la dirección de la transacción. Por eso, en los parámetros de la función de la inicialización serán pasados sus valores y la comisión (si existe). Otros parámetros serán reseteados durante la inicialización. Al final, la función de la inicialización de la clase tendrá el siguiente aspecto:

CDeal::CDeal(ENUM_POSITION_TYPE type,datetime time,double volume,double comission=0.0)  : ClosedVolume(0),
                                                                                          Swap(0),
                                                                                          Profit(0)
  {
   OpenTime      = time;
   OpenedVolume  = volume;
   Direct        = type;
   Comission     = comission;
  }

En nuestro futuro trabajo, tendremos que comprobar el estado de las transacciones guardadas. Para eso, crearemos la función IsClosed en la que vamos a comprobar si la transacción ya está cerrada en la base. Ahí vamos a comparar los volúmenes de la apertura y cierre de la transacción. Si son iguales, entonces la transacción ya está cerrada y la función devolverá true. Si la función no está cerrada, la función devolverá false y el volumen restante en el mercado.

bool CDeal::IsClosed(double &opened_volume)
  {
   opened_volume=OpenedVolume-ClosedVolume;
   return (opened_volume<=0);
  }

En el caso si necesitamos comprobar sólo el estado de la transacción y no hay necesidad de averiguar el volumen no cerrado, crearemos otra función con el mismo nombre.

bool CDeal::IsClosed(void)
  {
   double opened_volume;
   return IsClosed(opened_volume);
  }

Para cerrar correctamente una transacción, necesitamos saber su tipo. Creamos la función GetType que va a devolver el valor private de la variable Direct. Esta función es bastante corta, por eso podemos escribirla en el cuerpo de la clase.

ENUM_POSITION_TYPE Type(void) {  return Direct; }

Después de comprobar el estatus, hay que completar las transacciones no cerradas. Para eso, creamos la función Close. En ella se pasan los siguientes parámetros: volumen del cierre, beneficio de la transacción, comisión y el swap acumulado. La función va a devolver false si el objeto pasado supera el volumen no cerrado de la transacción. En el resto de los casos, los parámetros pasados serán guardados en las variables correspondientes de la clase, y la función devolverá true.

bool CDeal::Close(double volume,double profit,double comission=0.0,double swap=0.0)
  {
   if((OpenedVolume-ClosedVolume)<volume)
      return false;
   ClosedVolume   += volume;
   Profit         += profit;
   Comission      += comission;
   Swap           += swap;
   return true;
  }

Luego, durante el análisis de las transacciones, vamos a necesitar una función que devolverá el beneficio solicitado de la transacción. La llamaremos GetProfit.

double CDeal::GetProfit(void)
  {
   return (Comission+Swap+Profit);
  }

Además, para obtener los datos sobre el estado de los indicadores a tiempo, necesitaremos saber la hora de la transacción. Para este propósito, crearemos la función GetTime.

datetime          GetTime(void)  {  return OpenTime;  }

3.3. Clase del análisis sintáctico del informe

Después de crear la clase para almacenar la información sobre cada transacción, pasaremos directamante al análisis sintáctico del informe (parsing). Para eso, creamos la clase CParsing. En esta clase declaramos:

  • objeto de la clase CArrayObj  — para almacenar el array de transacciones;
  • objeto de la clase CFileTxt — para trabajar con el archivo del informe;
  • variable tipo string — para almacenar el nombre del instrumento.

Aparte de las funciones de inicialización y deinicialización, la clase va a contener otras dos funciones:

  • ReadFile — para el parsing
  • GetSymbol — para devolver el nombre del instrumento a petición.

class CParsing
  {
private:
   CArrayObj        *car_Deals;     //Array of deals
   CFileTxt         *c_File;        //File to parsing
   
   string            s_Symbol;      //Symbol of deals
   
public:
                     CParsing(CArrayObj *&array);
                    ~CParsing();
                    
   bool              ReadFile(string file_name);
   string            GetSymbol(void)   {  return s_Symbol;  }
  };

El objetivo principal de las funciones de esta clase consiste en crear unrray de transacciones para el procesamiento posterior. Entonces, el array creado debe estar disponible para el trabajo en el programa principal. A este fin, el objeto de la clase CArrayObj para almacenar el array de transacciones lo vamos a declarar en el programa principal, y la referencia a este objeto lo pasaremos a la clase durante la inicialización. Al final, la función de la inicialización tendrá el siguiente aspecto:

CParsing::CParsing(CArrayObj *&array)  :  s_Symbol(NULL)
  {
   if(CheckPointer(array)==POINTER_INVALID)
     {
      array=new CArrayObj();
     }
   car_Deals=array;
  }

La eliminación del objeto de la clase CFileTxt la escribiremos en la función de la deinicialización. El cierre del archivo está escrito en la función de la deinicialización de la clase base CFile, no vamos a mostrarlo aquí:

CParsing::~CParsing()
  {
   if(CheckPointer(c_File)!=POINTER_INVALID)
      delete c_File;
  }

Pasaremos directamente al parsing. Al llamar a la función de parsing ReadFile, pasamos el nombre del informe en los parámetros. Lo primero que hacemos en la función es comprobar si el parámetro pasado está vacío. También comprobamos la presencia del array para almacenar la información sobre las transacciones. En caso de que si no se cumple por lo menos una de las condiciones, paramos la ejecución de la función y devolvemos false.

bool CParsing::ReadFile(string file_name)
  {
   //---
   if(file_name==NULL || file_name=="" || CheckPointer(car_Deals)==POINTER_INVALID)
      return false;

Luego inicializamos el objeto de la clase CFileTxt e intentamos abrir el archivo pasado en el parámetro de la función. En caso del fallo, salimos fuera de la función con el resultado false.

   if(CheckPointer(c_File)==POINTER_INVALID)
     {
      c_File=new CFileTxt();
      if(CheckPointer(c_File)==POINTER_INVALID)
         return false;
     }
   //---
   if(c_File.Open(file_name,FILE_READ|FILE_COMMON)<=0)
      return false;

Después de abrir el archivo, leemos su contenido en la variable tipo string. Si el archivo está vacío, salimos fuera de la función con el resultado false.

   string html_report=NULL;
   while(!c_File.IsEnding())
      html_report+=c_File.ReadString();
   c_File.Close();
   if(html_report==NULL || html_report=="")
      return false;

El siguiente paso es buscar el carácter que no figura en el texto del informe y puede ser utilizado como el separador. Si este carácter no se encuentra, salimos fuera de la función con el resultado false.

   string delimiter  =  NULL;
   ushort separate   =  0;
   for(uchar tr=1;tr<255;tr++)
     {
      string temp =  CharToString(tr);
      if(StringFind(html_report,temp,0)>0)
         continue;
      delimiter   =  temp;
      separate    =  tr;
      break;
     }
   if(delimiter==NULL)
      return false;

Como ya ha sido mencionado, en la estructura de los archivos html, las tablas se cierran con la etiqueta "</table>".  Reemplazamos esta etiqueta por nuestro separador, dividiendo el informe en líneas. Así, separamos la tabla requerida en una línea separada.

   if(StringReplace(html_report,"</table>",delimiter)<=0)
      return false;
   //---
   s_Symbol=NULL;
   car_Deals.Clear();
   //---
   string html_tables[];
   int size=StringSplit(html_report,separate,html_tables);
   if(size<=1)
      return false;

Repitiendo este procedimiento con la etiqueta "</tr>", dividiremos la tabla en líneas.

   if(StringReplace(html_tables[size-2],"</tr>",delimiter)<=0)
      return false;
   size=StringSplit(html_tables[size-2],separate,html_tables);
   if(size<=1)
      return false;

Ahora, procesaremos el array de las líneas obtenido cíclicamente. Primero, omitimos todas las líneas que contienen la información sobre las órdenes. En este caso, vamos a orientarnos en la línea con el texto  "Deals" que separa las órdenes y las transacciones en el informe.

   bool found_start=false;
   double opened_volume=0;
   for(int i=0;i<size;i++)
     {
      //---
      if(!found_start)
        {
         if(StringFind(html_tables[i],"Deals",0)>=0)
            found_start=true;
         continue;
        }

Después de eso, dividimos cada línea en las celdas y transformamos la información en un formato corrrespondiente.

      string columns[];
      int temp=StringFind(html_tables[i],"<td>",0);
      if(temp<0)
         continue;
      if(temp>0)
         html_tables[i]=StringSubstr(html_tables[i],temp);
      StringReplace(html_tables[i],"<td>","");
      StringReplace(html_tables[i],"</td>",delimiter);
      temp=StringSplit(html_tables[i],separate,columns);
      if(temp<13)
         continue;
      //---
      ENUM_POSITION_TYPE   e_direction =  (ENUM_POSITION_TYPE)(columns[3]=="buy" ? POSITION_TYPE_BUY : columns[3]=="sell" ?
 POSITION_TYPE_SELL : -1);
      if(e_direction==-1)
         continue;
      //---
      datetime             dt_time     =  StringToTime(columns[0]);
      StringReplace(columns[5]," ","");
      double               d_volume    =  StringToDouble(columns[5]);
      StringReplace(columns[8]," ","");
      double               d_comission =  StringToDouble(columns[8]);
      StringReplace(columns[9]," ","");
      double               d_swap      =  StringToDouble(columns[9]);
      StringReplace(columns[10]," ","");
      double               d_profit    =  StringToDouble(columns[10]);
      if(s_Symbol==NULL || s_Symbol=="")
        {
         s_Symbol=columns[2];
         StringTrimLeft(s_Symbol);
         StringTrimRight(s_Symbol);
        }

En el siguiente paso, comprobamos si la transacción es una operación del cierre de la posición. Si el resultado es positivo, cerramos las posiciones en nuestra base según el método FIFO.

      if(opened_volume>0 && StringFind(columns[4],"out",0)>=0)
        {
         int total=car_Deals.Total();
         double total_volume=MathMin(opened_volume,d_volume);
         for(int d=0;(d<total && e_direction!=(-1) && total_volume>0);d++)
           {
            CDeal *deal=car_Deals.At(d);
            if(CheckPointer(deal)==POINTER_INVALID)
               continue;
            //---
            if(deal.Type()==e_direction)
               continue;
            //---
            double deal_unclosed=0;
            if(deal.IsClosed(deal_unclosed))
               continue;
            double close_volume     =  MathMin(deal_unclosed,total_volume);
            double close_comission  =  d_comission/d_volume*close_volume;
            double close_swap       =  d_swap/total_volume*close_volume;
            double close_profit     =  d_profit/total_volume*close_volume;
            if(deal.Close(close_volume,close_profit,close_comission,close_swap))
              {
               opened_volume  -= close_volume;
               d_volume       -= close_volume;
               total_volume   -= close_volume;
               d_comission    -= close_comission;
               d_swap         -= close_swap;
               d_profit       -= close_profit;
              }
           }
        }

Luego, comprobamos si la operación de la apertura de la posición ha sido realizada. Si es necesario, creamos una nueva transacción en nuestra base.

      if(d_volume>0 && StringFind(columns[4],"in",0)>=0)
        {
         CDeal *deal = new CDeal(e_direction,dt_time,d_volume,d_comission);
         if(CheckPointer(deal)==POINTER_INVALID)
            return false;
         if(!car_Deals.Add(deal))
            return false;
         opened_volume  += d_volume;
        }
     }

Si por lo menos una transacción ha sido guardada, la función devuelve true, de lo contrario, es false.

   return (car_Deals.Total()>0);
  }

Pasamos a la siguiente fase del trabajo.

4. Preparación de las clases para trabajar con los indicadores

Como ya hemos dicho antes, una de nuestras tareas consiste en separar las transacciones no rentables cuando no hay una tendencia evidente. La cuestión de la determinación de la tendencia se levanta de una manera regular, inclusive en esta web (por ejemplo, los artículos [3] y [4]). Yo no pretendo descubrir algunos métodos visibles de la identeficación de la tendencia. Solamente deseo proponer una tecnología de comparación de las transacciones realizadas y las indicaciones de los indicadores para el análisis posterior y una optimización asumida de los sistemas comerciales. Por eso, vamos a considerar aquí los indicadores más conocidos que ya entran en la entrega estándar del terminal.

4.1. Clase para incluir el indicador ATR

El primero será el indicador del tipo oscilador Average True Range. Como se sabe, la volatilidad del mercado se aumenta en caso de los movimientos tendenciales. Precisamente sobre ello nos avisará el crecimiento del valor del oscilador. ¿Qué valores tenderemos que guardar? Puesto que el EA analizado coloca las órdenes sólo en la apertura de la vela, yo propongo guardar el valor del indicador en la última vela cerrada, así como la relación de este valor al enterior. El primer valor mostrará la volatilidad actual, y el segundo demostrará la dinámica del cambio de la volatilidad.

Este indicador es un típico indicador de su clase con un búfer. Por eso, tiene sentido crear la única clase para trabajar con semejantes indicadores.

El enfoque de guardar los valores de los indicadores será el mismo que en el caso de las transacciones: primero, crearemos la clase para guardar los valores del indicador para una transacción, y luego crearemos la clase del nivel superior para trabajar directamente con los indicadores según las solicitudes externas y guardar los datos en el array.

La primera clase la llamaremos CValue. Va contener 3 variables private para almacenar la información sobre los valores del indicador (Value), la relación entre dos últimos valores del indicador (Dinamic) y el número del ticket de la orden para la que se guardan los valores (Deal_Ticket). El número del ticket será necesario para la comparación posterior de los valores del indicador con las órdenes durante el análisis. Todos los valores necesarios para el almacenamiento serán pasados a la instancia de la clase durante su inicialización. Para extraer la información necesaria, crearemos las funciones GetTicket, GetValue y GetDinamic, que van a devolver los valores de las variables correspondientes. Crearemos adicionalmente la función GetValues que va a devolver simultáneamente el valor del indicador y su dinámica.

class CValue       : public CObject
  {
private:
   double            Value;            //Indicator's value
   double            Dinamic;          //Dinamics value of indicator
   long              Deal_Ticket;      //Ticket of deal 
   
public:
                     CValue(double value, double dinamic, long ticket);
                    ~CValue(void);
   //---
   long              GetTicket(void)   {  return Deal_Ticket;  }
   double            GetValue(void)    {  return Value;        }
   double            GetDinamic(void)  {  return Dinamic;      }
   void              GetValues(double &value, double &dinamic);
  };

Luego, creamos la clase del nivel superior para almacenar el array de datos COneBufferArray. Va a contener el array de datos almacenados y el manejador (handle) del indicador en el bloque private. Recordaré que hemos decidido crear una clase universal para trabajar con los indicadores de un búfer. Pero la llamada a diferentes indicadores se acompaña con un diverso conjunto de parámetros. Por eso, según mi opinión, la opción más simple será inicializar el indicadoe en el programa principal y sólo después de eso inicializar la clase y pasarle el handle del indicador necesario. Vamos a introducir la variable s_Name para la identificación posterior del indicador en el informe.

class COneBufferArray   :  CObject
  {
private:
   CArrayObj        *IndicatorValues;     //Array of indicator's values
   
   int               i_handle;            //Handle of indicator
   string            s_Name;
   string            GetIndicatorName(int handle);
   
public:
                     COneBufferArray(int handle);
                    ~COneBufferArray();
   //---
   bool              SaveNewValues(long ticket);
   //---
   double            GetValue(long ticket);
   double            GetDinamic(long ticket);
   bool              GetValues(long ticket, double &value, double &dinamic);
   int               GetIndyHandle(void)  {  return i_handle;     }
   string            GetName(void)        {  return (s_Name!= NULL ? s_Name : "...");       }
  };

Para guardar los datos según una solicitud externa, vamos a crear la función SaveNewValues que va a contener sólo un parámetro, el ticket de la orden. Al principio de la función, comprobamos el estado de los arrays para el almacenamiento de los datos y el handle del indicador. En caso del error, la función devolverá el valor false.

bool COneBufferArray::SaveNewValues(long ticket)
  {
   if(CheckPointer(IndicatorValues)==POINTER_INVALID)
      return false;
   if(i_handle==INVALID_HANDLE)
      return false;

Después de eso, obtenemos los datos del indicador. Si no se puede cargar los valores del indicador, la función devolverá false.

   double ind_buffer[];
   if(CopyBuffer(i_handle,0,1,2,ind_buffer)<2)
      return false;

En el siguiente paso, creamos la instancia de la clase CValue y le pasamos los valores necesarios. En caso del error de la creación de la instancia, la función devolverá false.

   CValue *object=new CValue(ind_buffer[1], (ind_buffer[0]!=0 ? ind_buffer[1]/ind_buffer[0] : 1), ticket);
   if(CheckPointer(object)==POINTER_INVALID)
return false;

Si la clase todavía no sabe el nombre del indicador, lo obtenemos desde el gráfico llamando a la función GetIndicatorName (el código de la función se adjunta).

   if(s_Name==NULL)
      s_Name=GetIndicatorName(i_handle);

Para terminar, añadimos la instancia recién creada de la clase de datos al array y salimos de la función, devolviendo el resultado de la operación.

   return IndicatorValues.Add(object);
  }

Para devolver los datos desde el array por la solicitud, creamos las funciones GetValue, GetDinamic y GetValues que van a devolver los valores necesarios por el número del ticket de la orden. 

El código completo de la clase se adjunta al artículo.

He usado esta clase para la recopilación de los datos de los indicadores CCI, Volúmenes, Fuerza, oscilador de Chaikin y la desviación estándar.

4.2. Clase para incluir el indicador MACD

Vamos a añadir a nuestra colección otro indicador— MACD. Como se sabe se usa para determinar la fuerza y la dirección de la tendencia.

A diferencia de los indicadores arriba mencionados, MACD tiene 2 búferes de indicadores (Main y Signal). Por tanto, vamos a guardar la información sobre dos líneas. Usando el algoritmo arriba mencionado para los indicadores, el código de la clase para el almacenamiento de datos tendrá el siguiente aspecto:

class CMACDValue      : public CObject
  {
private:
   double            Main_Value;        //Main line value
   double            Main_Dinamic;      //Dinamics value of main lime
   double            Signal_Value;      //Signal line value
   double            Signal_Dinamic;    //Dinamics value of signal lime
   long              Deal_Ticket;       //Ticket of deal 
   
public:
                     CMACDValue(double main_value, double main_dinamic, double signal_value, double signal_dinamic, long ticket);
                    ~CMACDValue(void);
   //---
   long              GetTicket(void)         {  return Deal_Ticket;     }
   double            GetMainValue(void)      {  return Main_Value;      }
   double            GetMainDinamic(void)    {  return Main_Dinamic;    }
   double            GetSignalValue(void)    {  return Signal_Value;    }
   double            GetSignalDinamic(void)  {  return Signal_Dinamic;  }
   void              GetValues(double &main_value, double &main_dinamic, double &signal_value, double &signal_dinamic);
  };

Los cambios correspondientes también han ocurrido en la clase para el trabajo con el array de datos. A diferencia de la clase universal descrito en el apartado 4.1, esta clase va a trabajar con un indicador determinado, por eso, durante la inicialización de la clase, no va a recibir el handle del indicador, sino los parámetros necesarios para su inicialización. La inicialización del indicador va a realizarse directamente en la clase.

class CMACD
  {
private:
   CArrayObj        *IndicatorValues;     //Array of indicator's values
   
   int               i_handle;            //Handle of indicator
   
public:
                     CMACD(string symbol, ENUM_TIMEFRAMES timeframe, uint fast_ema, uint slow_ema, uint signal, ENUM_APPLIED_PRICE applied_price);
                    ~CMACD();
   //---
   bool              SaveNewValues(long ticket);
   //---
   double            GetMainValue(long ticket);
   double            GetMainDinamic(long ticket);
   double            GetSignalValue(long ticket);
   double            GetSignalDinamic(long ticket);
   bool              GetValues(long ticket, double &main_value, double &main_dinamic, double &signal_value, double &signal_dinamic);                
  };

La lógica de las funciones es la misma, sólo se ha cambiado el número de los búferes de indicadores y las variables guardadas.

bool CMACD::SaveNewValues(long ticket)
  {
   if(CheckPointer(IndicatorValues)==POINTER_INVALID)
      return false;
   if(i_handle==INVALID_HANDLE)
      return false;
   double main[], signal[];
   if(!CopyBuffer(i_handle,0,1,2,main)<2 || !CopyBuffer(i_handle,1,1,2,signal)<2)
      return false;
   CMACDValue *object=new CMACDValue(main[1], (main[0]!=0 ? main[1]/main[0] : 1), signal[1], (signal[0]!=0 ? signal[1]/signal[0] : 1), ticket);
   if(CheckPointer(object)==POINTER_INVALID)
      return false;
   return IndicatorValues.Add(object);
  }

Esta lógica de escalamiento se aplica a cualquier número de los búferes de indicadores. Pero si hay que guardar sólo unos determinados búferes de indicadores, basta con describir eso en la función SaveNewValues de la clase correspondiente. Pero no recomendaría hacer eso en esta fase, porque todavía no sabemos si hay una interdependencia entre las transacciones rentables y los valores de los determinados búferes de indicadores, y si existe, ¿cuál es su grado?

Para el mejor entendimiento del material , si se puede decir de esta manera, voy a poner otro ejemplo de almacenamiento de datos de un indicador con tres búferes de datos.

4.3. Clase para incluir el indicador ADX

El indicador ADX se usa ampliamente para determinar la fuerza y la dirección de la tendencia. Corresponde a nuestra tarea y se añade merecidamente a nuestra «hucha».

Este indicador tiene 3 búferes de indicadores, y de acuerdo con el método de escalamiento propuesto antes, aumentamos el número de las variables guardadas. De esta manera, la clase de almacenamiento de datos tiene el siguiente aspecto.

class CADXValue      : public CObject
  {
private:
   double            ADX_Value;        //ADX value
   double            ADX_Dinamic;      //Dinamics value of ADX
   double            PDI_Value;        //+DI value
   double            PDI_Dinamic;      //Dinamics value of +DI
   double            NDI_Value;        //-DIvalue
   double            NDI_Dinamic;      //Dinamics value of -DI
   long              Deal_Ticket;      //Ticket of deal 
   
public:
                     CADXValue(double adx_value, double adx_dinamic, double pdi_value, double pdi_dinamic, double ndi_value, double ndi_dinamic, long ticket);
                    ~CADXValue(void);
   //---
   long              GetTicket(void)         {  return Deal_Ticket;     }
   double            GetADXValue(void)       {  return ADX_Value;       }
   double            GetADXDinamic(void)     {  return ADX_Dinamic;     }
   double            GetPDIValue(void)       {  return PDI_Value;       }
   double            GetPDIDinamic(void)     {  return PDI_Dinamic;     }
   double            GetNDIValue(void)       {  return NDI_Value;       }
   double            GetNDIDinamic(void)     {  return NDI_Dinamic;     }
   void              GetValues(double &adx_value, double &adx_dinamic, double &pdi_value, double &pdi_dinamic, double &ndi_value, double &ndi_dinamic);
  };

 El aumento de datos almacenados provocará el cambio en la clase del trabajo con el array.

class CADX
  {
private:
   CArrayObj        *IndicatorValues;     //Array of indicator's values
   
   int               i_handle;            //Handle of indicator
   
public:
                     CADX(string symbol, ENUM_TIMEFRAMES timeframe, uint period);
                    ~CADX();
   //---
   bool              SaveNewValues(long ticket);
   //---
   double            GetADXValue(long ticket);
   double            GetADXDinamic(long ticket);
   double            GetPDIValue(long ticket);
   double            GetPDIDinamic(long ticket);
   double            GetNDIValue(long ticket);
   double            GetNDIDinamic(long ticket);
   bool              GetValues(long ticket,double &adx_value,double &adx_dinamic,double &pdi_value,double &pdi_dinamic,double &ndi_value,double &ndi_dinamic);
  };
bool CADX::SaveNewValues(long ticket)
  {
   if(CheckPointer(IndicatorValues)==POINTER_INVALID)
      return false;
   if(i_handle==INVALID_HANDLE)
      return false;
   double adx[], pdi[], ndi[];
   if(!CopyBuffer(i_handle,0,1,2,adx)<2 || !CopyBuffer(i_handle,1,1,2,pdi)<2 || !CopyBuffer(i_handle,1,1,2,ndi)<2)
      return false;
   CADXValue *object=new CADXValue(adx[1], (adx[0]!=0 ? adx[1]/adx[0] : 1), pdi[1], (pdi[0]!=0 ? pdi[1]/pdi[0] : 1), ndi[1], (ndi[0]!=0 ? ndi[1]/ndi[0] : 1), ticket);
   if(CheckPointer(object)==POINTER_INVALID)
      return false;
   return IndicatorValues.Add(object);
  }

Creo que ahora el principio de la construcción de las clases para el trabajo con los indicadores está bien claro. Por eso no vamos a describir el código para los siguientes indicadores para ahorrar el volumen del artículo. De la misma manera he añadido los indicadores BW MFI y Alligator a la «hucha» para el analisis. El que quiere puede encontrar el código completo de las clases en los archivos adjuntos.

5. Preparamos los formularios de los informes para mostrar los resultados

Después de obtener la información de parte de los indicadores necesarios en el momento de la ejecución de las transacciones, ha llegado el momento para pensar en el análisis de los resultados obtenidos. Yo creo que la manera más clara será construir los gráficos de dependencia del beneficio por las transacciones a base de los valores correspondientes de los indicadores. Yo propongo construir los gráficos usando la tecnoloquía propuesta por Victor en el artículo [2].

Cabe mencionar lo siguiente: puesto que realizo la optimización de la estrategia comercial, voy a buscar las dependencias del beneficio de las indicaciones de los indicadores. Si el lector intenta repetir alguna estrategia, tiene que buscar las dependencias entre el número de transacciones y las indicaciones de los indicadores.

Primero, crearemos las clases que van a preparar la información de cada indicador.

5.1. Clase universal de los indicadores con un búfer

La primera será la clase para trabajar con los indicadores de un búfer. ¿Qué información podemos analizar? Recordaremos que hemos guardado el valor del búfer de indicadores y la dinámica de su cambio. Por tanto, podemos analizar lo siguiente:

  • dependencia entre el beneficio de las operaciones realizadas y el valor del indicador en el momento de la apertura de la posición,
  • influencia de la tendencia del movimiento de la línea del indicador en el beneficio,
  • así como, la influencia integral del valor del indicador y su din”amica en el resultado de las operaciones realizadas.

Para dibujar los gráficos, crearemos la clase CStaticOneBuffer. Esta clase va a contener una referencia al array de los datos guardados DataArray, array de los valores del indicador Value con el paso establecido d_Step, así como, dos arrays del beneficio total separadamente para las posiciones largas y cortas. Nótese, los arrays para calcular el beneficio total serán bidimensionales. El tamaño de la primera dimensión va a corresponder al tamaño del array Value. La segunda dimensión va a contener tres elementos: el primero será para la dinámica descendiente del indicador, el segundo se usa para el moviento horizontal del indicador y el tercero servirá para el movimiento ascendiente.

Al inicializar la clase, en los parámetros vamos a pasar la referencia al array de datos y el tamaño del paso para los valores del indicador.

class CStaticOneBuffer  :  CObject
  {
private:
   COneBufferArray  *DataArray;
   
   double            d_Step;                    //Step in values Array
   double            Value[];                   //Array of values
   double            Long_Profit[][3];          //Array of long trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Short_Profit[][3];         //Array of short trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   
   bool              AdValues(double value, double dinamic, double profit, ENUM_POSITION_TYPE type);
   int               GetIndex(double value);
   bool              Sort(void);
   
public:
                     CStaticOneBuffer(COneBufferArray *data, double step);
                    ~CStaticOneBuffer();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

En la función de la inicialización, guardaremos los valores pasados y resetearemos los arrays que usamos.

CStaticOneBuffer::CStaticOneBuffer(COneBufferArray *data,double step)
  {
   DataArray   =  data;
   d_Step      =  step;
   ArrayFree(Value);
   ArrayFree(Long_Profit);
   ArrayFree(Short_Profit);
  }

Para recopilar la información estadística, crearemos la función Ad en la que vamos a pasar la información sobre la transacción. Dentro de la función van a ubicarse los parámetros correspondientes del indicador, y los datos van a guardarse en los elementos necesarios de los arrays.

bool CStaticOneBuffer::Ad(long ticket,double profit,ENUM_POSITION_TYPE type)
  {
   if(CheckPointer(DataArray)==POINTER_INVALID)
      return false;

   double value, dinamic;
   if(!DataArray.GetValues(ticket,value,dinamic))
      return false;
   value = NormalizeDouble(value/d_Step,0)*d_Step;
   return AdValues(value,dinamic,profit,type);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CStaticOneBuffer::AdValues(double value,double dinamic,double profit,ENUM_POSITION_TYPE type)
  {
   int index=GetIndex(value);
   if(index<0)
      return false;
   
   switch(type)
     {
      case POSITION_TYPE_BUY:
        if(dinamic<1)
           Long_Profit[index,0]+=profit;
        else
           if(dinamic==1)
              Long_Profit[index,1]+=profit;
           else
              Long_Profit[index,2]+=profit;
        break;
      case POSITION_TYPE_SELL:
        if(dinamic<1)
           Short_Profit[index,0]+=profit;
        else
           if(dinamic==1)
              Short_Profit[index,1]+=profit;
           else
              Short_Profit[index,2]+=profit;
        break;
     }
   return true;
  }

Para visualizar los gráficos, crearemos las funciones HTML_header y HTML_body en las que van a generarse las partes del código del encabezado y del cuerpo de la página HTML. Los principios de la construcción del código de la página HTML se describen detalladamente en el artículo [2], no vamos a hacer eso aquí. El código completo de la función se adjunta al artículo.

5.2. Clase para visualizar los datos del indicador Bill Williams MFI

El siguiente indicador será Bill Williams MFI. Según la manera de visualizarse en el gráfico recuerda los indicadores con un búfer, pero hay algunas diferencias: BW MFI tiene también el búfer de la paleta de colores que también tiene importancia. Al mismo tiempo, a diferencia de los indicadores con dos búferes, no nos va a interesar la dinámica del cambio del búfer de colores. Por eso, a los gráficos de los indicadores con un búfer propuestos antes se les añadirán los gráficos de la dependencia del beneficio del color del indicador y los gráficos de la influencia integral de valores y la dinámica del indicador tomando en cuenta el color actual del indicador.

Para la recopilación de los datos estadísticos y la creación de los gráficos analíticos crearemos la clase CStaticBWMFI. La estructura de la clase es la misma que hemos considerado antes. Se han cambiado los arrays del cálculo del beneficio, ahora tienen tres dimensiones. La tercera dimensión ha obtenido 4 elementos según el número de los colores utilizados.

class CStaticBWMFI  :  CObject
  {
private:
   CBWMFI           *DataArray;
   
   double            d_Step;                       //Step in values Array
   double            Value[];                      //Array of values
   double            Long_Profit[][3][4];          //Array of long trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Short_Profit[][3][4];         //Array of short trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   
   bool              AdValues(double value, double _color, double dinamic, double profit, ENUM_POSITION_TYPE type);
   int               GetIndex(double value);
   bool              Sort(void);
   
public:
                     CStaticBWMFI(CBWMFI *data, double step);
                    ~CStaticBWMFI();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

Encontrará el código completo de la clase en los archivos adjuntos.

5.3. Clase para visualizar los datos del indicador MACD

Ahora hablaremos del indicador MACD. Como se sabe, tiene dos búferes: un histograma y una línea de señal. Según la interpretación de las señales de este indicador, el valor y la dirección de este indicador son importantes, así como la posición de la línea de señal (por encima o por debajo del histograma). Para un análisis en profundidad, tenemos que crear una serie de gráficos.

  • Dependencia de la rentabilidad de las transacciones de los valores del histograma y de su dirección (de forma paralela y en conjunto).
  • Dependencia de la rentabilidad de las transacciones de los valores de la línea de señal y de su dirección.
  • Dependencia del beneficio de la posición de la línea de señal respecto al histograma.
  • Dependencia del beneficio de la influencia de los valores del histograma, su dirección y la posición de la línea de señal respecto al histograma.
Para el análisis de los datos, crearemos la clase CStaticMACD. En la construcción de la clase, serán aplicados los mismos principios que en la construcción de las clases estadísticas anteriores. Tendrá el array de tres dimensiones para la estadística del beneficio según los valores del histograma, pero a diferencia de la clase anterior, la tercera dimensión va a contener 3 elementos según la posición de la línea de señal respecto al histograma (por debajo, en el nivel, por encima). Además, añadiremos otro array bidimensional para calcular el beneficio según los valores de la línea de señal.

class CStaticMACD  :  CObject
  {
private:
   CMACD            *DataArray;
   
   double            d_Step;                       //Step in values Array
   double            Value[];                      //Array of values
   double            SignalValue[];                //Array of values
   double            Long_Profit[][3][3];          //Array of long trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Short_Profit[][3][3];         //Array of short trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Signal_Long_Profit[][3];      //Array of long trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   double            Signal_Short_Profit[][3];     //Array of short trades profit, direct -> DOWN-0, EQUAL-1, UP-2
   
   bool              AdValues(double main_value, double main_dinamic, double signal_value, double signal_dinamic, double profit, ENUM_POSITION_TYPE type);
   int               GetIndex(double value);
   int               GetSignalIndex(double value);
   bool              Sort(void);
   
public:
                     CStaticMACD(CMACD *data, double step);
                    ~CStaticMACD();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

Como puede ver, la estructura de la clase, el nombre y la destinación de las funciones son los mismos. Se ha cambiado sólo el contenido interno de las funciones, véase los archivos adjuntos.

5.4. Clase para visualizar los datos del indicador ADX

La clase CStaticADX será la siguiente. Va a recopilar la estadística según los valores del indicador ADX. Las reglas de la interpretación de las señales del indicador son las siguientes: +DI muestra la fuerza del movimiento positivo, -DI muestra la fuerza del movimiento negativo, ADX muestra la fuerza del movimiento medio. Vamos a construir los gráficos de las dependencias a base de estas reglas:

  • la dependencia del beneficio del valor +DI, su dirección y la posición respecto a ADX;
  • la dependencia del beneficio del valor -DI, su dirección y la posición respecto a ADX;

Cuando creaba la clase de la recopilación de la estadística, decidí recopilar un poco más de los datos. Como resultado, tenía que guardar los datos sobre:

  • el valor del indicador;
  • la dirección de la línea;
  • la posición respecto a la línea del movimiento opuesto;
  • la dirección de la línea del movimiento opuesto;
  • la posición respecto a la línea ADX;
  • la dirección de la línea ADX.
Gracias a esta división de la información y al enfoque utilizado en las clases anteriores, necesité los arrays de seis dimensiones. Pero los arrays de este tamaño de se soportan en MQL. Para solucionar este problema, fue creada la clase adicional CProfitData en la que iba a guardarse toda la información necesaria.

class CProfitData
  {
   public:
   double         Value;
   double         LongProfit[3]/*UppositePosition*/[3]/*Upposite Direct*/[3]/*ADX position*/[3]/*ADX direct*/;
   double         ShortProfit[3]/*UppositePosition*/[3]/*Upposite Direct*/[3]/*ADX position*/[3]/*ADX direct*/;
   
                  CProfitData(void) 
                  {  ArrayInitialize(LongProfit,0); ArrayInitialize(ShortProfit,0);  }
                 ~CProfitData(void) {};
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CStaticADX  :  CObject
  {
private:
   CADX             *DataArray;
   
   double            d_Step;           //Step in values Array
   CProfitData      *PDI[][3];         //Array of values +DI
   CProfitData      *NDI[][3];         //Array of values -DI
   
   bool              AdValues(double adx_value, double adx_dinamic, double pdi_value, double pdi_dinamic, double ndi_value, double ndi_dinamic, double profit, ENUM_POSITION_TYPE type);
   int               GetPDIIndex(double value);
   int               GetNDIIndex(double value);
   bool              Sort(void);
   
public:
                     CStaticADX(CADX *data, double step);
                    ~CStaticADX();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

Por lo demás, fueron guardados los enfoques y los principios de la construcción de las clases anteriores. Encontrará el código completo de la clase en los archivos adjuntos.

5.5. Clase para visualizar los datos del indicador Alligator

Al final de este bloque, crearemos la clase para recopilar la estadística del indicador Alligator. Las señales de este indicador se basan en tres medias móviles de diferentes períodos. Por tanto, al interpretar las señales de este indicador, no nos importan los valores determinados de las líneas de los indicadores Lo que más nos interesa es la dirección y la posición de las líneas.

Para concretizar las señales del indicador, introduciremos la determinación de la tendencia según la posición de las líneas. Si la línea LIPS está por encima de TEETH, y TEETH es por encima de JAW, consideramos BUY como una tendencia. Si LIPS está por debajo de TEETH, y TEETH está por debajo de JAW, consideramos sell como una tendencia. En caso de la falta de un orden estricto de las líneas, consideramos que la tendencia no está definida o estamos en FLAT.

Por tanto, los gráficos de la dependencia van a construirse a base de las señales de la dirección de la tendencia y la dinámica de las líneas del indicador.

Basándose en lo dicho antes, crearemos la clase CStaticAlligator. Los principios de la construcción de la clase se heredan de las clases anteriores.

class CStaticAlligator  :  CObject
  {
private:
   CAlligator             *DataArray;
   
   double            Long_Profit[3]/*Signal*/[3]/*JAW direct*/[3]/*TEETH direct*/[3]/*LIPS direct*/;  //Array of long deals profit
   double            Short_Profit[3]/*Signal*/[3]/*JAW direct*/[3]/*TEETH direct*/[3]/*LIPS direct*/; //Array of short feals profit
   
   bool              AdValues(double jaw_value, double jaw_dinamic, double teeth_value, double teeth_dinamic, double lips_value, double lips_dinamic, double profit, ENUM_POSITION_TYPE type);
   
public:
                     CStaticAlligator(CAlligator *data);
                    ~CStaticAlligator();
   bool              Ad(long ticket, double profit, ENUM_POSITION_TYPE type);
   string            HTML_header(void);
   string            HTML_body(void);
  };

Encontrará el código completo de la clase en los archivos adjuntos.

6. Construimos el Asesor Experto para recopilar y analizar la información

Ahora, cuando el trabajo preparatorio está hecho, crearemos el Asesor Experto que va a iniciarse directamente en el Probador de estrategias para la recopilación de la información y visualización de los datos analíticos. En primer lugar, en los parámetros de entrada especificamos el nombre del informe de la simulación para el análisis, el timeframe usado y todos los parámetros necesarios de los indicadores utilizados.

input string            FileName          =  "Kalman_test.html"   ;
input ENUM_TIMEFRAMES   Timefarame        =  PERIOD_CURRENT       ;
input string            s1                =  "ADX"                ;  //---
input uint              ADX_Period        =  14                   ;
input string            s2                =  "Alligator"          ;  //---
input uint              JAW_Period        =  13                   ;
input uint              JAW_Shift         =  8                    ;
input uint              TEETH_Period      =  8                    ;
input uint              TEETH_Shift       =  5                    ;
input uint              LIPS_Period       =  5                    ;
input uint              LIPS_Shift        =  3                    ;
input ENUM_MA_METHOD    Alligator_Method  =  MODE_SMMA            ;
input ENUM_APPLIED_PRICE Alligator_Price  =  PRICE_MEDIAN         ;
input string            s3                =  "ATR"                ;  //---
input uint              ATR_Period        =  14                   ;
input string            s4                =  "BW MFI"             ;  //---
input ENUM_APPLIED_VOLUME BWMFI_Volume    =  VOLUME_TICK          ;
input string            s5                =  "CCI"                ;  //---
input uint              CCI_Period        =  14                   ;
input ENUM_APPLIED_PRICE CCI_Price        =  PRICE_TYPICAL        ;
input string            s6                =  "Chaikin"            ;  //---
input uint              Ch_Fast_Period    =  3                    ;
input uint              Ch_Slow_Period    =  14                   ;
input ENUM_MA_METHOD    Ch_Method         =  MODE_EMA             ;
input ENUM_APPLIED_VOLUME Ch_Volume       =  VOLUME_TICK          ;
input string            s7                =  "Force Index"        ;  //---
input uint              Force_Period      =  14                   ;
input ENUM_MA_METHOD    Force_Method      =  MODE_SMA             ;
input ENUM_APPLIED_VOLUME Force_Volume    =  VOLUME_TICK          ;
input string            s8                =  "MACD"               ;  //---
input uint              MACD_Fast         =  12                   ;
input uint              MACD_Slow         =  26                   ;
input uint              MACD_Signal       =  9                    ;
input ENUM_APPLIED_PRICE MACD_Price       =  PRICE_CLOSE          ;
input string            s9                =  "Standart Deviation" ;  //---
input uint              StdDev_Period     =  14                   ;
input uint              StdDev_Shift      =  0                    ;
input ENUM_MA_METHOD    StdDev_Method     =  MODE_SMA             ;
input ENUM_APPLIED_PRICE StdDev_Price     =  PRICE_CLOSE          ;
input string            s10               =  "Volumes"            ;  //---
input ENUM_APPLIED_VOLUME Applied_Volume  =  VOLUME_TICK          ;

Затем объявим экземпляры всех описанных выше классов.

CArrayObj         *Deals;
CADX              *ADX;
CAlligator        *Alligator;
COneBufferArray   *ATR;
CBWMFI            *BWMFI;
COneBufferArray   *CCI;
COneBufferArray   *Chaikin;
COneBufferArray   *Force;
CMACD             *MACD;
COneBufferArray   *StdDev;
COneBufferArray   *Volume;
CStaticOneBuffer  *IndicatorsStatic[];
CStaticBWMFI      *BWMFI_Stat;
CStaticMACD       *MACD_Stat;
CStaticADX        *ADX_Stat;
CStaticAlligator  *Alligator_Stat;

6.1. La función de la inicialización del EA

Puesto que nuestro Asesor Experto está destinado para el análisis de los datos en el Probador de estrategias, primero comprobaremos el entorno en el que va a iniciarse. Si el inicio se empieza fuera del Probador, tenemos que interrumpir su inicialización.

int OnInit()
  {
//---
   if(!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION))
return INIT_FAILED;

Luego, ejecutamos el parsing de los datos desde el archivo del informe de la simulación. Después de leer los datos desde el informe, ya no necesitamos la instancia de la clase del parsing y lo eliminamos de la memoria.

   CParsing *Parsing =  new CParsing(Deals);
   if(CheckPointer(Parsing)==POINTER_INVALID)
      return INIT_FAILED;
   if(!Parsing.ReadFile(FileName) || CheckPointer(Deals)==POINTER_INVALID || Deals.Total()<=0)
     {
      delete Parsing;
      return INIT_FAILED;
     }
   delete Parsing;

Después de eso, realizamos la inicialización de las clases de indicadores.

//---
   ADX =  new CADX(_Symbol,Timefarame,ADX_Period);
   if(CheckPointer(ADX)==POINTER_INVALID)
      return INIT_FAILED;
//---
   Alligator =  new CAlligator(_Symbol,Timefarame,JAW_Period,JAW_Shift,TEETH_Period,TEETH_Shift,LIPS_Period,LIPS_Shift,Alligator_Method,Alligator_Price);
   if(CheckPointer(Alligator)==POINTER_INVALID)
      return INIT_FAILED;
//---
   int handle=iATR(_Symbol,Timefarame,ATR_Period);
   if(handle>0)
     {
      ATR      =  new COneBufferArray(handle);
      if(CheckPointer(ATR)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   BWMFI    =  new CBWMFI(_Symbol,Timefarame,BWMFI_Volume);
   if(CheckPointer(BWMFI)==POINTER_INVALID)
      return INIT_FAILED;
//---
   handle=iCCI(_Symbol,Timefarame,CCI_Period,CCI_Price);
   if(handle>0)
     {
      CCI      =  new COneBufferArray(handle);
      if(CheckPointer(CCI)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   handle=iChaikin(_Symbol,Timefarame,Ch_Fast_Period,Ch_Slow_Period,Ch_Method,Ch_Volume);
   if(handle>0)
     {
      Chaikin  =  new COneBufferArray(handle);
      if(CheckPointer(Chaikin)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   handle=iForce(_Symbol,Timefarame,Force_Period,Force_Method,Force_Volume);
   if(handle>0)
     {
      Force    =  new COneBufferArray(handle);
      if(CheckPointer(Force)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   MACD     =  new CMACD(_Symbol,Timefarame,MACD_Fast,MACD_Slow,MACD_Signal,MACD_Price);
   if(CheckPointer(MACD)==POINTER_INVALID)
      return INIT_FAILED;
//---
   handle=iStdDev(_Symbol,Timefarame,StdDev_Period,StdDev_Shift,StdDev_Method,StdDev_Price);
   if(handle>0)
     {
      StdDev   =  new COneBufferArray(handle);
      if(CheckPointer(StdDev)==POINTER_INVALID)
         return INIT_FAILED;
     }
//---
   handle=iVolumes(_Symbol,Timefarame,Applied_Volume);
   if(handle>0)
     {
      Volume   =  new COneBufferArray(handle);
      if(CheckPointer(Volume)==POINTER_INVALID)
         return INIT_FAILED;
     }

Al final de la función OnInit, establecemos el contador de las órdenes a 0 y salimos de la función..

   cur_ticket   =  0;
//---
   return(INIT_SUCCEEDED);
  }

6.2. Recopilación de datos estadísticos

La recopilación de los datos va a realizarse en la función Funciones OnTick. Al principio de la función, comprobaremos si la información está recopilada por todas las órdenes. Si es así, salimos de la función.

void OnTick()
  {
   if(cur_ticket>=Deals.Total())
      return;

En el siguiente paso, la hora de la ejecución de la transacción analizada se compara con la hora del tick procesado. Si la hora de la transacción todavía no ha llegado, salimos de la función.

   CDeal *object  =  Deals.At(cur_ticket);
   if(object.GetTime()>TimeCurrent())
      return;

Si hemos pasado las comprobaciones anteriores, comprobamos el estado de las instancias de las clases de indicadores y guardamos la información necesaria, llamando a la función SaveNewValues para cada clase de indicadores.

   if(CheckPointer(ADX)!=POINTER_INVALID)
      ADX.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(Alligator)!=POINTER_INVALID)
      Alligator.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(ATR)!=POINTER_INVALID)
      ATR.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(BWMFI)!=POINTER_INVALID)
      BWMFI.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(CCI)!=POINTER_INVALID)
      CCI.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(Chaikin)!=POINTER_INVALID)
      Chaikin.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(Force)!=POINTER_INVALID)
      Force.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(MACD)!=POINTER_INVALID)
      MACD.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(StdDev)!=POINTER_INVALID)
      StdDev.SaveNewValues(cur_ticket);
   //---
   if(CheckPointer(Volume)!=POINTER_INVALID)
      Volume.SaveNewValues(cur_ticket);

Al final de la función, aumentamos el contador de las órdenes procesadas y salimos de la función.

   cur_ticket++;
   return;
  }

6.3. Visualización de los gráficos para el análisis

El análisis de los datos y la visualización del informe va a realizarse en la función OnTester. Al iniciar la función, comprobaremos el número de las transacciones para el análisis.

double OnTester()
  {
   double ret=0.0;
   int total=Deals.Total();

Si es necesario realizar el análisis, realizamos la inicialización de las clases estadísticas.

Para facilitar el procesamiento posterior, reunimos las clases estadísticas de los indicadores con un búfer en el array. Por eso, vamos a contar los indicadores con un búfer en paralelo con la inicialización.

   int total_indy=0;
   if(total>0)
     {
      if(CheckPointer(ADX)!=POINTER_INVALID)
         ADX_Stat=new CStaticADX(ADX,1);
      //---
      if(CheckPointer(Alligator)!=POINTER_INVALID)
         Alligator_Stat=new CStaticAlligator(Alligator);
      //---
      if(CheckPointer(ATR)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(ATR,_Point*10);
         if(CheckPointer(indy)!=POINTER_INVALID)
           {
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
           }
        }
      //---
      if(CheckPointer(BWMFI)!=POINTER_INVALID)
         BWMFI_Stat=new CStaticBWMFI(BWMFI,_Point*100);
      //---
      if(CheckPointer(CCI)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(CCI,10);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
      //---
      if(CheckPointer(Chaikin)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(Chaikin,100);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
      //---
      if(CheckPointer(Force)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(Force,0.1);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
      //---
      if(CheckPointer(MACD)!=POINTER_INVALID)
         MACD_Stat=new CStaticMACD(MACD,_Point*10);
      //---
      if(CheckPointer(StdDev)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(StdDev,_Point*10);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
      //---
      if(CheckPointer(Volume)!=POINTER_INVALID)
        {
         CStaticOneBuffer *indy=new CStaticOneBuffer(Volume,100);
         if(CheckPointer(indy)!=POINTER_INVALID)
            if(ArrayResize(IndicatorsStatic,total_indy+1)>0)
              {
               IndicatorsStatic[total_indy]=indy;
               total_indy++;
              }
        }
     }

A continuación, comparamos los datos de los indicadores con las transacciones correspondientes y agrupamos la información según las direcciones necesarias para la visualización de los informes gráficos. Para eso, en cada clase estadística, llamaremos la función Ad, psando la información sobre la transacción en sus parámetros.

   for(int i=0;i<total;i++)
     {
      CDeal               *deal     =  Deals.At(i);
      ENUM_POSITION_TYPE   type     =  deal.Type();
      double               d_profit =  deal.GetProfit();
      
      for(int ind=0;ind<total_indy;ind++)
         IndicatorsStatic[ind].Ad(i,d_profit,type);
      if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID)
         BWMFI_Stat.Ad(i,d_profit,type);
      if(CheckPointer(MACD_Stat)!=POINTER_INVALID)
         MACD_Stat.Ad(i,d_profit,type);
      if(CheckPointer(ADX_Stat)!=POINTER_INVALID)
         ADX_Stat.Ad(i,d_profit,type);
      if(CheckPointer(Alligator_Stat)!=POINTER_INVALID)
         Alligator_Stat.Ad(i,d_profit,type);
     }

Después de la agrupación de los datos, creamos el archivo del informe Report.html y lo guardamos en la carpeta compartida de los terminales.

   if(total_indy>0 || CheckPointer(BWMFI_Stat)!=POINTER_INVALID || CheckPointer(MACD_Stat)!=POINTER_INVALID
      || CheckPointer(ADX_Stat)!=POINTER_INVALID || CheckPointer(Alligator_Stat)!=POINTER_INVALID )
     {
      int handle=FileOpen("Report.html",FILE_WRITE|FILE_TXT|FILE_COMMON);
      if(handle<0)
         return ret;

Al principio del archivo, escribimos el encabezado de nuestro informe html.

      FileWrite(handle,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">");
      FileWrite(handle,"<html> <head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
      FileWrite(handle,"<title>Deals to Indicators</title> <!-- - -->");
      FileWrite(handle,"<script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js\" type=\"text/javascript\"></script>");
      FileWrite(handle,"<script src=\"https://code.highcharts.com/highcharts.js\" type=\"text/javascript\"></script>");
      FileWrite(handle,"<!-- - --> <script type=\"text/javascript\">$(document).ready(function(){");

Luego, llamando consecutivamente a la función HTML_header de todas las clases estadísticas, introducimos los datos en nuestro archivo para construir los gráficos.

      for(int ind=0;ind<total_indy;ind++)
         FileWrite(handle,IndicatorsStatic[ind].HTML_header());
      if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID)
         FileWrite(handle,BWMFI_Stat.HTML_header());
      if(CheckPointer(MACD_Stat)!=POINTER_INVALID)
         FileWrite(handle,MACD_Stat.HTML_header());
      if(CheckPointer(ADX_Stat)!=POINTER_INVALID)
         FileWrite(handle,ADX_Stat.HTML_header());
      if(CheckPointer(Alligator_Stat)!=POINTER_INVALID)
         FileWrite(handle,Alligator_Stat.HTML_header());

Después de eso, llamando consecutivamente a la función HTML_body de cada clase estadística, creamos la plantilla de la visualización del informe. Obsérvese: con la llamada a esta función nosotros terminamos el trabajo con la clase estadística y la eliminamos para limpiar la memoria.

      FileWrite(handle,"});</script> <!-- - --> </head> <body>");
      for(int ind=0;ind<total_indy;ind++)
        {
         FileWrite(handle,IndicatorsStatic[ind].HTML_body());
         delete IndicatorsStatic[ind];
        }
      if(CheckPointer(BWMFI_Stat)!=POINTER_INVALID)
        {
         FileWrite(handle,BWMFI_Stat.HTML_body());
         delete BWMFI_Stat;
        }
      if(CheckPointer(MACD_Stat)!=POINTER_INVALID)
        {
         FileWrite(handle,MACD_Stat.HTML_body());
         delete MACD_Stat;
        }
      if(CheckPointer(ADX_Stat)!=POINTER_INVALID)
        {
         FileWrite(handle,ADX_Stat.HTML_body());
         delete ADX_Stat;
        }
      if(CheckPointer(Alligator_Stat)!=POINTER_INVALID)
        {
         FileWrite(handle,Alligator_Stat.HTML_body());
         delete Alligator_Stat;
        }

Al final, escribimos las etiquetas de cierre, cerramos el archivo, limpiamos los arrays y salimos de la función.

      FileWrite(handle,"</body> </html>");
      FileFlush(handle);
      FileClose(handle);
     }
//---
   ArrayFree(IndicatorsStatic);
//---
   return(ret);
  }

No olvidamos eliminar las clases restantes en la función OnDeinit.

7. Análisis de la información

Nuestro trabajo ya se acerca a su final lógico. Ha llegado el momento para ver sus resultados. Para eso, volvemos al Probador de estrategias, repetimos ahí todos los ajustes que hemos usado en la simulación del EA analizado en el segundo apartado de este artículo, e iniciamos la prueba de nuestro EA analítico recien creado.

Después de terminar la prueba, abrimos la carpeta compartida de los terminales y buscamos el archivo Report.html. Lo abrimos en el navegador. A continuación, voy a mostrar los ejemplos desde mi informe.

7.1. ATR

Informes ATR

Al analizar los gráficos de las dependencias del beneficio del indicador ATR, yo no veo las zonas potencialmente rentables, y por tanto, no hay posibilidad de filtrar las transacciones.

7.2. CCI.

Informes CCI

El gráfico de la dependencia del beneficio del indicador CCI permiten conseguir un cierto beneficio con las transacciones BUY si el valor del indicador está por encima de 200 y la línea del indicador es ascendiente. Pero en las transacciones SELL no hay zonas rentables.

7.3. Chaikin

Informes Chaikin

El oscilador Chaikin, igual como ATR, no ha demostrado la correlación entre las indicaciones del indicador y el beneficio de las transacciones.

7.4. Indicador de fuerza

Informes del indicador de fuerza.

Los gráficos analíticos del indicador de fuerza tampoco han revelado ningunas dependencias.

7.5. Desviación estándar

Informes SndDev

El análisis de las dependencias de los valores del indicador StdDev permite identificar algunas zonas rentables para las órdenes de compra, pero no hay posibilidad de la filtración de las transacciones de venta.

7.6. Indicador de volúmenes

Dependencia del beneficio de los volúmenes.

Tampoco se puede detectar las dependencias durante el análisis del indicador de volúmenes.

7.7. Bill Williams MFI

BW MFI

El indicador BW MFI permite obtener beneficio durante la filtración de las transacciones de compra si están abiertas con el color 0. Pero no se puede detectar ningunas dependencias para las transacciones de venta.

7.8. MACD.

Informes MACDInformes MACD

Las señales del indicador MACD dan la posibilidad de la filtración de las transacciones rentables de compra. Eso se puede hacer, si se realizan las transacciones de compra cuando la línea de señal está por encima del histograma. Pero el análisis no muestra las zonas rentables para las transacciones de venta. Al mismo tiempo, el indicador permite reducir las operaciones no rentables, excluyendo las transacciones de venta si el histograma crece y la línea de señal está por debajo o igual al histograma.

7.9. ADX

Informes ADX

El análisis de las señales del indicador ADX no da la posibilidad de filtrar las transacciones.

7.10. Alligator

Informes AlligatorInformes Alligator

Yo creo que el uso del indicador Alligator es más fecundo para la filtración de las transacciones. Se puede encontrar los patrones para realizar las transacciones en las combinaciones de la posición y la dirección de la línea. Se puede realizar las transacciones rentables de compra si:

  • la posición de las líneas del indicador muestra la tendencia para la venta y la línea LIPS o JAW da vuelta hacia arriba;
  • la posición de las líneas del indicador muestra la tendencia para la compra y las líneas LIPS y TEETH apuntan hacia arriba;
  • tendencia no definida y las líneas TEETH y JAW apuntan hacia abajo. 

Para las transacciones de venta vamos a usar las señales de espejo.

8. Corregimos el Asesor Experto inicial

Hemos realizado un gran trabajo en cuanto al análisis de las transacciones de nuestro EA. Ahora, veremos cómo eso influirá en los resultados del trabajo de nuestra estrategia. Para eso, al módulo de las señales comerciales del artículo [1] le añadimos los indicadores con las reglas de filtración de acuerdo con el análisis arriba realizado. Yo propongo añadir MACD y Alligator a nuestro módulo.

Yo recomendaría añadir los filtros de indicadores consecutivamente y realizar cíclicamente el procedimiento de la distribución de las transacciones por los indicadores después de añadir cada filtro. Eso dará un entendimiento más claro de la influencia de cada filtro en toda la estrategia y ayudará a estimar su influencia en conjunto. Además, si en la primera etapa, el análisis no permite detectar la dependencia del beneficio de las indicaciones de algún indicador, eso no significa en absoluto que Usted no verá esta dependencia en las siguientes iteraciones. No lo hago ahora simplemente para no inflar el artículo que ya está bastante extenso

Primero, añadimos los parámetros de los indicadores a la descripción del módulo.

//| Parameter=JAW_Period,uint,13,JAW Period                                   |
//| Parameter=JAW_Shift,uint,8,JAW Shift                                      |
//| Parameter=TEETH_Period,uint,8,TEETH Period                                |
//| Parameter=TEETH_Shift,uint,5,TEETH Shift                                  |
//| Parameter=LIPS_Period,uint,5,LIPS Period                                  |
//| Parameter=LIPS_Shift,uint,3,LIPS_Shift                                    |
//| Parameter=Alligator_Method,ENUM_MA_METHOD,MODE_SMMA,Method                |
//| Parameter=Alligator_Price,ENUM_APPLIED_PRICE,PRICE_MEDIAN,Alligator Price |
//| Parameter=MACD_Fast,uint,12,MACD Fast                                     |
//| Parameter=MACD_Slow,uint,26,MACD Slow                                     |
//| Parameter=MACD_Signal,uint,9,MACD Signal                                  |
//| Parameter=MACD_Price,ENUM_APPLIED_PRICE,PRICE_CLOSE,MACD Price            |

Por tanto, al bloque private añadimos las variables para almacenar los parámetros, y al bloque public, la función para su escritura.

   uint              ci_MACD_Fast;
   uint              ci_MACD_Slow;
   uint              ci_MACD_Signal;
   ENUM_APPLIED_PRICE ce_MACD_Price;
   uint              ci_JAW_Period;
   uint              ci_JAW_Shift;
   uint              ci_TEETH_Period;
   uint              ci_TEETH_Shift;
   uint              ci_LIPS_Period;
   uint              ci_LIPS_Shift;
   ENUM_MA_METHOD    ce_Alligator_Method;
   ENUM_APPLIED_PRICE ce_Alligator_Price;
   void              JAW_Period(uint value)                 {  ci_JAW_Period  =  value;   }
   void              JAW_Shift(uint value)                  {  ci_JAW_Shift   =  value;   }
   void              TEETH_Period(uint value)               {  ci_TEETH_Period=  value;   }
   void              TEETH_Shift(uint value)                {  ci_TEETH_Shift =  value;   }
   void              LIPS_Period(uint value)                {  ci_LIPS_Period =  value;   }
   void              LIPS_Shift(uint value)                 {  ci_LIPS_Shift  =  value;   }
   void              Alligator_Method(ENUM_MA_METHOD value) {  ce_Alligator_Method  =  value;   }
   void              Alligator_Price(ENUM_APPLIED_PRICE value) {  ce_Alligator_Price=  value;   }
   void              MACD_Fast(uint value)                  {  ci_MACD_Fast   =  value;   }
   void              MACD_Slow(uint value)                  {  ci_MACD_Slow   =  value;   }
   void              MACD_Signal(uint value)                {  ci_MACD_Signal =  value;   }
   void              MACD_Price(ENUM_APPLIED_PRICE value)   {  ce_MACD_Price  =  value;   }

Además, hay que aregar las clases para trabajar con los indicadores y las funciones de inicialización de la obtención de los datos necesarios. He usado una clase estándar para trabajar con MACD. Puesto que para Alligator no existe una clase estándar, la he sustituido por tres clases de las medias móviles, asignándoles los nombres de acuerdo con los nombres de las líneas del indicador.

protected:
   CiMACD            m_MACD;           // object-oscillator
   CiMA              m_JAW;
   CiMA              m_TEETH;
   CiMA              m_LIPS;
     
   //--- method of initialization of the indicators
   bool              InitMACD(CIndicators *indicators);
   bool              InitAlligator(CIndicators *indicators);
   //--- methods of getting data
   double            Main(int ind)                     { return(m_MACD.Main(ind));      }
   double            Signal(int ind)                   { return(m_MACD.Signal(ind));    }
   double            DiffMain(int ind)                 { return(Main(ind+1)!=0 ? Main(ind)-Main(ind+1) : 0); }
   int               AlligatorTrend(int ind);
   double            DiffJaw(int ind)                  { return(m_JAW.Main(ind+1)!=0 ? m_JAW.Main(ind)/m_JAW.Main(ind+1) : 1); }
   double            DiffTeeth(int ind)                { return(m_TEETH.Main(ind+1)!=0 ? m_TEETH.Main(ind)/m_TEETH.Main(ind+1) : 1); }
   double            DiffLips(int ind)                 { return(m_LIPS.Main(ind+1)!=0 ? m_LIPS.Main(ind)/m_LIPS.Main(ind+1) : 1); }

Durante el siguiente paso, introduciremos los cambios en la función InitIndicators para agregar nuestros indicadores en la librería del EA.

bool CSignalKalman::InitIndicators(CIndicators *indicators)
  {
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);
//--- initialize close serias
   if(CheckPointer(m_close)==POINTER_INVALID)
     {
      if(!InitClose(indicators))
         return false;
     }
//--- create and initialize MACD oscilator
   if(!InitMACD(indicators))
      return(false);
//--- create and initialize Alligator
   if(!InitAlligator(indicators))
      return(false);
//--- create and initialize Kalman Filter
   if(CheckPointer(Kalman)==POINTER_INVALID)
      Kalman=new CKalman(ci_HistoryBars,ci_ShiftPeriod,m_symbol.Name(),ce_Timeframe);
   
//--- ok
   return(true);
  }

Luego introduciremas las adiciones en las funciones de la toma de decisiones. No olvidemos que los indicadores agregados actuan como el filtro. Por eso, vamos a acceder a los indicadores sólo después de obtener la señal principal.

int CSignalKalman::LongCondition(void)
  {
   if(!CalculateIndicators())
      return 0;
   int result=0;
   //--- 
   if(cd_correction>cd_forecast)
     {
      if(Signal(1)>Main(1))
         result=80;
      else
        {
         switch(AlligatorTrend(1))
           {
            case 1:
              if(DiffLips(1)>1 && DiffTeeth(1)>1 && DiffJaw(1)<=1)
                 result=80;
              break;
            case -1:
              if(DiffLips(1)>1 || DiffJaw(1)>1)
                 result=80;
              break;
            case 0:
              if(DiffJaw(1)<1)
                {
                 if(DiffLips(1)>1)
                    result=80;
                 else
                    if(DiffTeeth(1)<1)
                       result=80;
                }
              break;
           }
        }
     }
   return result;
  }

Introducimos los mismos cambios en la función ShortCondition. Encontrará el código completo de las soluciones comerciales en los archivos adjuntos.

9. Simulación del EA después de introducir los cambios

Después de introducir los cambios en el módulo de las soluciones comerciales, creamos el nuevo EA (la descripción detallada de la creación del EA con el uso del módulo de las señales comerciales se encuentra en el artículo [5]). Vamos a probar el EA recién creado con los parámetros similares a la simulación inicial del apartado 2 de este artículo.

Como han demostrado los resultados de la simulación, sin cambiar los parámetros del EA, el uso de los filtros ha permitido aumentar el factor del beneficio de 0,75 a 1,12. Es decir, hemos conseguido obtener el beneficio con los parámetros no rentables del EA inicial. Recordaré que al principio he cogido los parámetros no optimizados del EA inicial a propósito.

Prueba repetidaPrueba repetidaPrueba repetida

Conclusión

En este artículo, ha sido demostrada la tecnología de la distribución del historial de las transacciones por los indicadores, lo que ha permitido crear un sistema de los filtros a base de los indicadores estándar. En los resultados de la simulación, este sistema ha demostrado un resultado importante de la rentabilidad del EA analizado. El sistema propuesto puede aplicarse no sólo durante la optimización de un sistema comercial existente, sino durante los intentos de crear un sistema nuevo.

Referencias

  1. Usando el filtro de Kalman en la predicción del precio.
  2. Gráficos y diagramas en HTML.
  3. ¿Cuánto dura una tendencia?
  4. Distintas maneras para averiguar la tendencia en MQL5
  5. Examinamos en la práctica el método adaptativo del seguimiento del mercado.

Los programas usados en el artículo:

#
 Nombre
Tipo 
Descripción 
1 Kalman.mqh  Librería de la clase  Clase del filtro de Kalman
2 SignalKalman.mqh  Librería de la clase  Módulo de las señales comerciales por el filtro de Kalman
3 SignalKalman+Filters.mqh  Librería de la clase  Módulo de las señales comerciales por el filtro de Kalman después de añadir los filtros de indicadores
4 Kalman_expert.mq5  Asesor Experto  Asesor Experto inicial para la estrategia con el uso del filtro de Kalman
5 Kalman+Filters.mq5  Asesor Experto  Asesor Experto modificado para la estrategia con el uso del filtro de Kalman
6 Deals_to_Indicators.mq5  Asesor Experto  Asesor Experto para distribuir el historial de transaccioens por los indicadores
7 Deal.mqh   Librería de la clase  Clase para almacenar la información sobre la transacción
8 Parsing.mqh  Librería de la clase  Clase para el parsing del historial de las transacciones del informe de simulación
9 Value.mqh   Librería de la clase  Clase para guardar los datos sobre el estado del búfer de indicador
10 OneBufferArray.mqh  Librería de la clase  Clase para guardar el historial de datos del indicador con un búfer
11 StaticOneBuffer.mqh  Librería de la clase  Clase para recopilar y analizar la estadística del indicador con un búfer
12 ADXValue.mqh  Librería de la clase  Clase para guardar los datos sobre el estado del búfer de indicador ADX
13 ADX.mqh  Librería de la clase  Clase para guardar el historial de datos del indicador ADX
14 StaticADX.mqh  Librería de la clase  Clase para recopilar y analizar la estadística del indicador ADX
15 AlligatorValue.mqh  Librería de la clase  Clase para guardar los datos sobre el estado del búfer de indicador Alligator
16 Alligator.mqh  Librería de la clase  Clase para guardar el historial de datos del indicador Alligator
17 StaticAlligator.mqh  Librería de la clase  Clase para recopilar y analizar la estadística del indicador Alligator
18 BWMFIValue.mqh  Librería de la clase  Clase para guardar los datos sobre el estado del búfer de indicador BW MFI
19 BWMFI.mqh  Librería de la clase  Clase para guardar el historial de datos del indicador BW MFI
20 StaticBWMFI.mqh  Librería de la clase  Clase para recopilar y analizar la estadística del indicador BW MFI
21 MACDValue.mqh  Librería de la clase  Clase para guardar los datos sobre el estado del búfer de indicador MACD
22 MACD.mqh  Librería de la clase  Clase para guardar el historial de datos del indicador MACD
23 StaticMACD.mqh  Librería de la clase  Clase para recopilar y analizar la estadística del indicador MACD
24  Reports.zip  Archivo  El archivo comprimido contiene los resultados de la simulación de los EAs en el Probador de estrategias y el informe analítico.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/3968

Archivos adjuntos |
Reports.zip (360.75 KB)
Mini emulador del mercado o Probador de estrategias manual Mini emulador del mercado o Probador de estrategias manual
El mini emulador del mercado es un indicador que sirve para la emulación parcial del trabajo en el terminal. Supuestamente, se puede usarlo para la simulación de las estrategias «manuales» del análisis y el trading en el mercado.
Asesor Experto multiplataforma: las clases CExpertAdvisor y CExpertAdvisor Asesor Experto multiplataforma: las clases CExpertAdvisor y CExpertAdvisor
En el artículo final de la serie sobre el asesor comercial multiplataforma, hablaremos sobre las clases CExpertAdvisor y CExpertAdvisors, que sirven de contendores para los componentes del experto anteriormente descritos. Asimismo, analizaremos la implementación del monitoreo de las nuevas barras y el guardado de datos.
Comparación de diferentes tipos de media móvil en el comercio Comparación de diferentes tipos de media móvil en el comercio
Se han analizado 7 tipos de medias móviles (MA), asimismo, se ha desarrollado una estrategia comercial para trabajar con ellas. Se ha realizado la simulación y la comparación de diferentes MA en una estrategia comercial, dando una característica comparativa de la efectividad del uso de las diferentes MA.
R cuadrado como evaluación de la calidad de la curva del balance de la estrategia R cuadrado como evaluación de la calidad de la curva del balance de la estrategia
En este artículo se describe cómo construir el criterio personalizado de la optimización de R². Usando este criterio se puede evaluar la calidad de la curva del balance de la estrategia y eligir las estrategias más estables y crecientes regularmente. Se describen los principios de su construcción, así como los métodos estadísticos que se usan para evaluar las propiedades y la calidad de esta métrica.