English Русский 中文 Deutsch 日本語 Português
La contribución de Thomas DeMark al análisis técnico

La contribución de Thomas DeMark al análisis técnico

MetaTrader 4Trading | 28 marzo 2016, 14:46
1 785 0
Ivan Morozov
Ivan Morozov

Introducción

Existe la opinión de que el análisis técnico es al mismo tiempo una ciencia y un arte. El motivo de esta dualidad es el carácter individual de los puntos de vista que sostienen los diferentes tráders y analistas. Por ejemplo, una misma línea de tendencia se puede trazar de formas completamentes distintas. Esta indeterminación no es muy bien recibida en la práctica, puesto que el comercio bursátil exige ante todo precisión. Con este problema ha topado cada tráder que haya intentado aunque sea una vez construir las líneas de tendencia y haya observado que esto se puede hacer con varios métodos. Este desorden dificulta la creación de sistemas comerciales precisos que puedan analizar los mercados según las líneas de tendencia. Existe además una serie de problemas causados por esta pluralidad: la discrepancia a la hora de buscar los extremos locales,  la divergencia y convergencia debidas a una línea de tendencia construida incorrectamente y otras contrariedades.

No todo el mundo se ha resignado a sufrir los efectos de un enfoque tan libre en el análisis técnico. Por ejemplo, Thomas DeMark ha podido realizar una aproximación de forma analítica a este problema y proponer métodos para solucionarlo. En su libro "El análisis técnico: una nueva ciencia", describió sus métodos para un análisis más preciso de la posición actual de los precios. En este artículo hablaré sobre dos inventos, precisamente sobre los puntos TD y las líneas TD. Por supuesto, en el libro se describen no sólo estos métodos: en él, DeMark escribe también sobre la periodicidad en los mercados, las ondas de Elliott y mucho más.

Asimismo, en el artículo se presenta y explica el progreso en la escritura de tres indicadores y dos expertos creados sobre las ideas de Thomas DeMark. Estoy seguro de que será muy interesante para un amplio círculo de tráders, en especial para los principiantes en el comercio en fórex.


1. Los puntos TD

El primer invento de Thomas DeMark simplifica el hallazgo de los extremos de precio para construir las líneas de tendencia. Él decidió encontrar ese tipo de velas en un gráfico diario, cuyos precios máximos se encontraran por encima de los máximos del día anterior y posterior a uno definido (a continuación voy a usar esta palabra para designar las velas sobre las que voy a determinar la presencia de puntos TD). Si se respeta esta condición, entonces en el gráfico se puede construir un punto TD según el precio máximo de una vela definida. Por consiguiente, si el mínimo de un día determinado es menor al mínimo del día anterior y el posterior, entonces se puede construir un punto TD conforme al precio mínimo en una vela definida.

Punto TD alcistaPunto TD bajista

Fig. 1. Puntos TD alcista y bajista

En el primer dibujo de arriba se representa un punto TD del primer nivel (está marcado con un círculo rojo). Como podemos ver, el máximo de la vela definida es mayor a los máximos de la anterior y la posterior. En el dibujo los valores de los máximos se muestran con líneas horizontales grises. En la segunda figura se muestra un caso análogo, pero con un punto TD bajista. Las reglas se respetan de la misma forma: el mínimo de la vela definida está por debajo de los mínimos de la anterior y la posterior.

Más arriba se han analizado solo los puntos TD de nivel 1. Esto significa que se compara el precio de la vela definida solo con una anterior a ella y con otra posterior. Si hay que construir un punto TD de nivel 2, hay que comparar el precio máximo de la vela definida con los dos  anteriores y los dos posteriores. Lo mismo sucede con los precios mínimos.

Punto TD de nivel 2

Fig. 2. Ejemplo de punto TD de nivel 2

En la imagen de más arriba se muestra un punto TD de nivel 2. Se puede percibir que el precio máximo de la vela definida está por encima de los dos precios máximos de las velas anteriores a ella y de las dos posteriores. 

Punto TD de nivel 40

Fig. 3. Punto TD de nivel 40.

El nivel de los puntos TD puede ser también superior al 2, dependiendo del número de valores de los máximos y los mínimos con los que haya que comparar la vela definida. Además, es lógico que un punto TD, por ejemplo, del nivel tres, sea simultáneamente punto de los niveles  menores: el segundo y el primero. Thomas DeMark en su libro describe los puntos hasta el nivel 3.

Merece la pena notar que ya existe un indicador que funciona con un principio semejante. Realmente, los fractales de Bill Williams no son otra cosa que puntos TD de nivel 2. Recordemos que se construyen si un mínimo de 2 velas hasta la definida y 2 tras la misma tienen máximos más bajos o mínimos más altos, lo que repite completamente la definición de los puntos TD de nivel 2.


2. Líneas TD

En sí mismos, los puntos TD son solo extremos. Para construir una línea de tendencia necesitaremos dos puntos (dos máximos o dos mínimos). Al mismo tiempo, Thomas DeMark ha utilizado solo los dos últimos  puntos como los más importantes.

Líneas TD de nivel 1Líneas TD de nivel 2

Fig. 4. Líneas TD de primer y segundo nivel.

En la imagen de la izquierda se han construido dos líneas TD de nivel 3 (la línea azul es la de los mínimos, y la verde, la de los máximos). El nivel de la línea indica el nivel de los puntos conforme a los que se ha construido esta línea. En la imagen de la derecha se han construido las mismas líneas, pero de segundo  nivel.

Asimismo, Thomas DeMark ha desarrollado 3 proyectores de precio, que están directamente relacionados con las líneas TD, y de los que hablaré brevemente más adelante en el apartado "Información adicional".


3. Creación de indicadores

3.1. iTDDots

Construir manualmente nuevos puntos y líneas para cada nueva vela sería algo tedioso. Creo que si algo se puede automatizar sin ninguna pérdida de calidad, es necesario hacerlo. Más abajo hablaré del proceso de creación de un indicador que construye puntos TD. En este ejemplo, se trabaja con el lenguaje MQL4.

Inicialmente, se ha elaborado un plan del indicador. En su funcionamiento no se tiene en cuenta la vela actual, dado que sus máximos y mínimos pueden cambiar, y esto puede acarrear una construcción incorrecta de los puntos. De esta forma, se tienen en cuenta solo las velas anteriores.

Plan de trabajo con el indicador:

  • Al instalarlo en el gráfico, se deben construir todos los puntos TD en la historia disponible. El usuario establece su nivel.
  • Con cada nueva vela hay que comprobar si existe la posibilidad de construir un nuevo punto y, en caso de que surja dicha posibilidad, habrá que construirlo.
La primera tarea del indicador es determinar la vela cuyo extremo es mayor que los extremos correspondientes de n velas colindantes. Con este fin, propongo escribir una función que va a determinar los precios máximos y mínimos de las velas en el diapasón que necesito. Para determinar el número de velas que necesito comprobar cada vez, es necesario multiplicar el nivel de los puntos por 2 y añadir 1.

Velas utilizadas en el indicador

Fig. 5. Velas utilizadas en el indicador

 En la imagen de arriba se muestra un ejemplo de cómo se enumeran las velas, calculadas por el indicador. Gracias a la imagen, también el método para calcular su cantidad se hace más claro.

Ahora voy a mostrar cómo se ha desarrollado el código.

El indicador debe tener dos búferes para la visualización de los puntos, porque una vela puede ser simultáneamente un punto TD construido conforme un máximo, y un punto TD construido conforme a un mínimo. Por lo tanto, el principio del programa tiene el aspecto siguiente:

#property indicator_chart_window        //Para representar el indicador en el gráfico
#property indicator_buffers 2           //Usamos 2 búferes
#property indicator_plots   2           //Se representarán dos búferes
#property indicator_color1 clrGreen     //Color estándar del primer búfer
#property indicator_type1   DRAW_ARROW  //Tipo de dibujado del primer búfer
#property indicator_width1 2            //Grosor estándar de la línea de representación del primer búfer
#property indicator_color2 clrBlue      //Color estándar del segundo búfer
#property indicator_type2   DRAW_ARROW  //Tipo de dibujado del segundo búfer
#property indicator_width2 2            //Grosor estándar de la línea de representación del segundo búfer

A continuación, al instalarlo en el gráfico, el usuario debe determinar para el indicador el nivel de construcción de los puntos TD:

input int Level = 1;

Para el funcionamiento del indicador, se usan las siguientes variables, que son declaradas como globales:

bool new_candle = true;         
double  pdH,                    //Para el precio máximo de la vela definida
        pdL,                    //Para el precio mínimo de la vela definida
        pricesH[],              //Matriz para el guardado de los precios máximos
        pricesL[];              //Matriz para el guardado de los precios mínimos
bool    DOTH,                   //Para representar un punto(según el máximo)
        DOTL;                   //Para representar un punto(según el mínimo)
double  UpDot[],                //Matriz del búfer para los valores de los puntos construidos conforme a los máximos
        DownDot[];              //Matriz del búfer para los valores de los puntos construidos conforme a los mínimos

Más abajo se muestra la función Init():

int OnInit()
  {
   ChartRedraw(0);                              //Esto es necesario para que al pasar de un marco temporal a otro no haya problemas con la representación
   SetIndexBuffer(0, UpDot);
   SetIndexBuffer(1, DownDot);
   SetIndexEmptyValue(0,0.0);
   SetIndexEmptyValue(1,0.0);
   SetIndexArrow(0,159);                        //Indicamos el número del símbolo de la fuente Wingdings
   SetIndexArrow(1,159);
   SetIndexLabel(0, "TD " + Level + " High");   //Estos nombres se representarán en la ventana de datos
   SetIndexLabel(1, "TD " + Level + " Low");
   return(INIT_SUCCEEDED);
  }

Para obtener los precios de las velas que necesito, he creado una función con la que podemos familiarizarnos más abajo:

void GetVariables(int start_candle, int level)
  {
   /*En esta función el indicador reúne información de las velas para construir los puntos. Todas las variables usadas aquí ya han sido declaradas como globales*/
   pdH = iHigh(NULL, 0, start_candle + 1 + level);      //High de la vela definida
   pdL = iLow(NULL, 0, start_candle + 1 + level);       //Low de la vela definida
   
   ArrayResize(pricesH, level * 2 + 1);                 //Establecemos el tamaño de las matrices
   ArrayResize(pricesL, level * 2 + 1);                 //
   
   for (int i = level * 2; i >= 0; i--){                //Reunimos en las matrices todos los precios (máximos y mínimos) de las velas que necesitamos
      pricesH[i] = iHigh(NULL, 0, start_candle + i + 1);
      pricesL[i] = iLow(NULL, 0, start_candle + i + 1);
  }

Y, al fin, lo más interesante: el código de la función start():

int start()
  {
   int i = Bars - IndicatorCounted();                   //Esto es necesario para no calcular lo mismo cada vez que aparezca una nueva vela
   for (; i >= 0; i--)
     {                                                  //Aquí sucede todo para la construcción de los puntos TD
      DOTH = true;
      DOTL = true;
      GetVariables(i, Level);                           //Obtenemos los valores actuales de los precios
      
      for(int ii = 0; ii < ArraySize(pricesH); ii++)
        {                                              //Determinamos si hay un punto TD en este intervalo
         if (pdH < pricesH[ii]) DOTH = false;
         if (pdL > pricesL[ii]) DOTL = false;
        }   
         
      if(DOTH) UpDot[i + Level + 1] = pdH;              //Si es así, su construcción 
      if(DOTL) DownDot[i + Level + 1] = pdL;
      
      if(UpDot[i + Level + 1] ==  UpDot[i + Level + 2]) UpDot[i + Level + 2] = 0;       //Aquí preveo el caso en el que dos velas son consecutivas
      if(DownDot[i + Level + 1] ==  DownDot[i + Level + 2]) DownDot[i + Level + 2] = 0; //tienen precios máximos o mínimos y estructura del punto TD idénticos
     }                                                                                  //на  de la última vela de la pareja
   return(0);
  }

Bueno, ya se ha familiarizado con el proceso de escritura de un indicador para construir puntos TD. Más abajo se muestran dos gráficos con el indicador instalado TDDots. En el primero, vemos el valor de la variable Level = 1, y en el segundo, el de la variable Level = 10. Esto significa que en el primer gráfico, todos los puntos TD están rodeados por un mínimo de una vela con unos máximos más bajos o unos mínimos más altos, y en el segundo, por 10 velas así. Estos gráficos se presentan para demostrar cómo funciona el indicador.

Todos los puntos TD de nivel 1Todos los puntos TD de nivel 10

Fig. 6. Modelo de funcionamiento del indicador: construcción de los puntos TD de los niveles 1 y 10.


3.2. iTDLines

Como ya he escrito, Thomas DeMark usaba solo los dos últimos puntos para construir las líneas TD. En mi indicador, automatizo esta táctica. La tarea del indicador consiste en construir dos líneas rectas a través de los puntos indicados. ¿Cómo construirlas? Sin lugar a dudas, esta tarea se puede resolver usando una función lineal con el siguiente aspecto: y = kx + b. Para ello, hay que encontrar los coeficientes k y b de tal forma que la línea recta pase rigurosamente por los puntos establecidos.

Para construir una línea recta a través de dos puntos, hay que conocer sus coordenadas. Usando las fórmulas mostradas más abajo, encontraremos k y b para la función lineal. Además, tomaremos como x los números de las velas partiendo del límite derecho del gráfico, y como y, los precios.

k = (y2 - y1) / (x2 - x1),
b = (x2 * y1 - x1 * y2) / (x2 - x1),
donde x1 - es el número de la vela con el primer punto,
      x2 - es el número de la vela con el segundo punto,
      y1 - es el precio del primer punto,
      y2 - es el precio del segundo punto.

Conociendo k y b, solo queda resolver una sencilla ecuación lineal en cada vela, y obtendremos el precio del punto en la línea TD. 

Más abajo se muestra el código del indicador:

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2

#property indicator_color1 clrGreen
#property indicator_color2 clrBlue

input int Level = 1;

double LU[], LD[];

//Esta variable se usará para reducir los cálculos.
datetime LastCount;

En el indicador se usa una variable: el valor del nivel de los puntos conforme a los que se van a construir las líneas. Lo establece el usuario, como en el caso de TDDots. A continuación, vienen dos matrices que contendrán los valores de los precios de todos los puntos de las líneas. La variable LastCount se usará para que el indicador realice los cálculos solo una vez en una vela.

Después se muestra la función que encuentra de inmediato los valores k y b para cada una de las líneas. Se describirá por partes:

void GetBK(double &Ub, double &Uk, double &Db, double &Dk, int &EndUN, int &EndDN)
  {
   double TDU[];
   double TDD[];
   int TDU_n[];
   int TDD_n[];
   ArrayResize(TDU, 2, 2);
   ArrayResize(TDD, 2, 2);
   ArrayResize(TDU_n, 2, 2);
   ArrayResize(TDD_n, 2, 2);

La función retorna 6 valores. Si el valor de las cuatro primeras variables es algo comprensible, en lo que respecta a las dos últimas el asunto se complica. Las necesitaremos para representar las líneas. Cada una de ellas indica la longitud de la línea en velas, partiendo de la actual.

//Obtenemos los valores de los precios de los puntos y los números de las velas desde el principio
   int Ui = 0;
   int Di = 0;
   for(int i = 0;; i++)
     {
      double current_bar_U = iCustom(NULL, 0, "TDDots", Level, 0, i);
      double current_bar_D = iCustom(NULL, 0, "TDDots", Level, 1, i);
      
      if(current_bar_U > 0 && Ui < 2)
        {
         TDU[Ui] = current_bar_U;   //Precio
         TDU_n[Ui] = i;             //Número
         Ui++;
        }
      if(current_bar_D > 0 && Di < 2)
        {
         TDD[Di] = current_bar_D;
         TDD_n[Di] = i;
         Di++;
        }
      if(Ui == 2 && Di == 2) break;
     }

Esta parte del código obtiene los valores de los precios de los dos últimos puntos TD. Los precios se guardan en matrices, para trabajar con ellos posteriormente.

   Ub = ( (TDU_n[0] * TDU[1]) - (TDU[0] * TDU_n[1]) ) / ( TDU_n[0] - TDU_n[1] );
   Uk = (TDU[0] - TDU[1]) / (TDU_n[0] - TDU_n[1]);
   
   Db = ( (TDD_n[0] * TDD[1]) - (TDD_n[1] * TDD[0]) ) / ( TDD_n[0] - TDD_n[1] );
   Dk = (TDD[0] - TDD[1]) / (TDD_n[0] - TDD_n[1]);   
   
   EndUN = TDU_n[1];
   EndDN = TDD_n[1];
  }

Puesto que los números de las velas tienen una dirección de numeración como en las series temporales (la cuenta se lleva de derecha a izquierda), entonces conviene usar los valores opuestos de los puntos. En otras palabras, en las fórmulas mostradas más arriba, en lugar de x2 hay que poner x1, y en lugar de x1, hay que poner x2, y así sucesivamente. De esta forma, tendrán el aspecto siguiente:

b = (x1 * y2 - x2 * y1) / (x1 - x2),
k = (y1 - y2) / (x1 - x2),
donde x1 - es el número de la vela con el primer punto,
      x2 - es el número de la vela con el segundo punto,
      y1 - es el precio del primer punto,
      y2 - es el precio del segundo punto.

Luego sigue la función (Init):

int OnInit()
  {
   SetIndexBuffer(0, LU);
   SetIndexLabel(0, "TDLU");
   SetIndexBuffer(1, LD);
   SetIndexLabel(1, "TDLD");
      
   SetIndexEmptyValue(0, 0);
   SetIndexEmptyValue(1, 0);
   
   LastCount = iTime(NULL, 0, 1);
   
   return(INIT_SUCCEEDED);
  }

En ella se inicializan los búferes y a estos se les adjudica un nombre. Asimismo, para que con el primer tick de nuevos datos el indicador calcule la ubicación de las líneas, en la variable LastCount se guarda la fecha de la vela anterior. En principio, en ella se puede guardar cualquier fecha, excepto la actual.

Después escribimos la función start():

int start()
  {
   //Si es una vela nueva o el primer inicio
   if(iTime(NULL, 0, 0) != LastCount)
     {
      double Ub, Uk, Db, Dk;
      int eUp, eDp;
      
      GetBK(Ub, Uk, Db, Dk, eUp, eDp);
      
      //Eliminamos los valores antiguos
      for(int i = 0; i < IndicatorCounted(); i++)
        {
         LU[i] = 0;      
         LD[i] = 0;
        }
         
      //Construimos valores nuevos
      for(i = 0; i <= eUp; i++)
        {
         LU[i] = Uk * i + Ub;
        }
         
      for(i = 0; i <= eDp; i++)
        {
         LD[i] = Dk * i + Db;
        }
         
      LastCount = iTime(NULL, 0, 0);
     }
      
   return 0;
  }

Antes de dibujar los nuevos valores, es necesario limpiar el gráfico de los valores antiguos. Para ello, al principio de la función se ha implementado un ciclo aparte. Ahora se construyen dos líneas rectas según la ecuación mostrada más arriba, y a la variable LastCount se le adjudica la fecha actual, para no realizar de nuevo estas acciones en la vela actual.

Al final el indicador funciona así:

Muestra del funcionamiento del indicador

Fig. 7. Muestra del funcionamiento del indicador: construcción de las líneas TD según los puntos TD de nivel 5.

Como no resulta difícil adivinar, en la fig. 7 se muestra el funcionamiento del indicador con valor de variable Level = 5.


3.3 Indicador de niveles horizontales

Es indudable que hay multitud de caminos para determinar el precio de los niveles horizontales en el gráfico. Propongo un método sencillo de construcción de dos niveles en relación con el precio actual. En el funcionamiento del indicador se usará el método iTDDots, que ya hemos descrito (ver p. 3.1).

La esencia es muy simple:

  1. El indicador obtiene los valores de un número n de puntos TD de un nivel determinado, establecido por el usuario
  2. Se calcula el valor medio para estos puntos
  3. Conforme a este valor, se representa una línea recta horizontal en el gráfico

Sin embargo, se dan casos cuando los puntos TD se encuentran lejos con respecto al último punto, lo que aleja significativamente del precio actual el nivel horizontal. Para resolver este problema se ha introducido una variable incluida por el usuario, que limita la distancia en puntos entre esos puntos. Es decir, el indicador encuentra el valor medio entre los últimos n extremos, que están alejados el uno del otro en una cantidad de puntos superior a la establecida.

Veamos el código del indicador.

Se usan 2 búferes y 3 variables, cuyos valores debe introducir el usuario:

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
#property indicator_color1 clrGreen
#property indicator_type1   DRAW_LINE
#property indicator_width1 2
#property indicator_color2 clrBlue
#property indicator_type2   DRAW_LINE
#property indicator_width2 2

input int TDLevel = 1;       //Nivel de los puntos
input int NumberOfDots = 3;  //Cantidad de puntos
input double Delta = 0.001;  //Distancia máxima entre dos puntos

double TDLU[], TDLD[];

En este indicador se crean dos niveles horizontales. Puesto que pueden tener valor en un análisis más a largo plazo que la líneas de tendencia en el indicador TDLines, he decidido usar para su construcción los objetos gráficos: rectas horizontales. Sin embargo, este indicador en lo sucesivo será mucho más sencillo si los valores de los niveles horizontales se guardan en los búferes de indicador. He decidido guardar estos valores en la vela actual con el índice 0, para que siempre se pueda obtener el valor del precio del nivel de una forma sencilla, recurriendo a iCustom desde otro programa.

int OnInit()
  {
   SetIndexBuffer(0,TDLU);
   SetIndexBuffer(1,TDLD);
   SetIndexEmptyValue(0,0.0);
   SetIndexEmptyValue(1,0.0);
   SetIndexLabel(0, "ГУ U");
   SetIndexLabel(1, "ГУ D");
   ObjectCreate(0, "Nivel horizontal U", OBJ_HLINE, 0, iTime(NULL, 0, 0), 0);
   ObjectCreate(0, "Nivel horizontal D", OBJ_HLINE, 0, iTime(NULL, 0, 0), 0);
   return(INIT_SUCCEEDED);
  }

Más abajo presentamos la función que calcula el precio de un nivel horizontal concreto.

double GetLevelPrice(int ud,int n,double delta,int level)
  {
   /* ud - índice del tipo de línea. 0 - U, otro valor - D.
   n - cantidad de puntos.
   delta - distancia máxima entre ellos
   level - nivel de los puntos.*/

   //Preparando las matrices para guardar los precios de los puntos
   double TDU[];
   double TDD[];
   ArrayResize(TDU,n,n);
   ArrayResize(TDD,n,n);
   ArrayInitialize(TDU,0);
   ArrayInitialize(TDD,0);
 
   //El ciclo funciona dos veces, puesto que existen solo 2 búferes de datos
   for(int Buffer=0; Buffer<2; Buffer++)
     {
      int N=0;
      int Fails=0;
      bool r=false;
      for(int i=0; r==false; i++)
        {
         double d=iCustom(NULL,0,"TDDots",level,Buffer,i);
         if(d>0)
           {
            if(N>0)
              {
               if(Buffer==0) double cp=TDU[N-1];
               else cp=TDD[N-1];
               if(MathAbs(d-cp)<=delta)
                 {
                  if(Buffer == 0)
                     TDU[N] = d;
                  else TDD[N]=d;
                  N++;
                 }
               //Si la distancia es demasiado grande, se añade 1 al error
               else
                 {
                  Fails++;
                 }
              }
            else
              {
               if(Buffer == 0)
                  TDU[N] = d;
               else TDD[N]=d;

               N++;
              }
           }
         //Si se acumulan muchos errores, el ciclo se detiene
         if(Fails>2 || N>n) r=true;
        }
     }
   
   //Obtener el valor medio
   double ATDU = 0;
   double ATDD = 0;
   N=0;
   for(i=0; i<ArraySize(TDU); i++)
     {
      ATDU=ATDU+TDU[i];
      if(TDU[i]==0)
        {
         i=ArraySize(TDU);
        }
      else
        {
         N++;
        }
     }
   ATDU=ATDU/N;
   N=0;
   for(i=0; i<ArraySize(TDD); i++)
     {
      ATDD=ATDD+TDD[i];
      if(TDD[i]==0)
        {
         i=ArraySize(TDD);
        }
      else
        {
         N++;
        }
     }
   ATDD=ATDD/N;

   //La función retorna el valor
   if(ud == 0) return ATDU;
   else return ATDD;
  }

 En la función llamada con cada nuevo tick, solo queda obtener los valores de los precios y adjudicarlos a los objetos ya creados:

void start()
  { 
   //Elimino los valores de los búferes de indicador en la vela anterior
   TDLD[1] = 0;
   TDLU[1] = 0;

   TDLD[0] = GetLevelPrice(1, TDLevel, Delta, NumberOfDots);
   TDLU[0] = GetLevelPrice(0, TDLevel, Delta, NumberOfDots);
   
   //Si de alguna forma los objetos han desaparecido
   if(ObjectFind("Nivel horizontal U") < 0)
     {
      ObjectCreate(0, "Nivel horizontal U", OBJ_HLINE, 0, iTime(NULL, 0, 0), 0);
     }
   if(ObjectFind("Nivel horizontal D") < 0)
     {
      ObjectCreate(0, "Nivel horizontal D", OBJ_HLINE, 0, iTime(NULL, 0, 0), 0);
     }
   
   ObjectSetDouble(0, "Nivel horizontal U", OBJPROP_PRICE, TDLU[0]);
   ObjectSetDouble(0, "Nivel horizontal D", OBJPROP_PRICE, TDLD[0]);
  }

Para que sea más fácil de usar, después de eliminar el indicador se deben eliminar también los objetos:

void OnDeinit(const int reason)
  {
   if(!ObjectDelete("Nivel horizontal U")) Print(GetLastError());
   if(!ObjectDelete("Nivel horizontal D")) Print(GetLastError());
  }

Al final, el indicador creado permite construir de forma sencilla los niveles horizontales según los parámetros establecidos por el usuario.

Muestra del funcionamiento del indicador

Fig. 8. Muestra del funcionamiento del indicador. 


4. Experto para el comercio según un indicador de niveles horizontales

Cualquier indicador se debe usar para obtener beneficios, y si existe la posibilidad,  también para la obtener beneficios de forma automática. Sin lugar a dudas, el experto descrito en este artículo no puede dar beneficios siempre y en cualquier mercado, pero se ha creado con un objetivo distinto. Ha sido escrito para demostrar en la práctica las posibilidades de los indicadores, y mostrar de forma general la estructura de funcionamiento del experto.

Ahora, hablando de forma más concreta. Usando el indicador de niveles horizontales se puede escribir un experto que comerciará según las señales descritas más abajo.

Condiciones para la compra:

  • El precio ha roto el nivel horizontal superior
  • El precio ha crecido n puntos a partir del valor del precio del nivel horizontal superior

Las condiciones de venta son inversas:

  • El precio ha roto el nivel horizontal inferior
  • El precio ha disminuido n puntos a partir del valor del precio del nivel horizontal inferior

De otra forma, se puede representar así:

Señal de compraSeñal de venta

Fig. 9. Condiciones de compra y venta

El experto abrirá solo una orden y la acompañará con un Trailing Stop, es decir, trasladará el Stop Loss en la cantidad de puntos que el usuario haya establecido al iniciarlo.

La salida del mercado se realizará exclusivamente conforme al stop.

Veamos el código del experto desarrollado en el lenguaje MQL4.
Primero describiremos las variables cuyos valores establece el usuario antes de iniciar el programa.

input int MagicNumber = 88341;      //Con este número mágico el experto abrirá las órdenes
input int GL_TDLevel = 1;           //Nivel de los puntos TD que usará el indicador de niveles horizontales
input int GL_NumberOfDots = 3;      //Cantidad de puntos que usará el indicador de niveles horizontales
input double S_ExtraPoints = 0.0001;//Número de puntos adicionales que precisamente es el complemento L de la ilustración de más arriba
input double GL_Delta = 0.001;      //Distancia a la que los valores de los puntos TD son tenidos en cuenta por parte del indicador de niveles horizontales
input int StopLoss = 50;            //Nivel del stop
input double Lot = 0.01;            //Tamaño del lote

La siguiente función comprueba si se ha cruzado la línea TD. Para no recibir una señal de apertura de posición cada vez que el precio actual cruce la línea construida por el indicador iglevels, se comprueba la condición que exige que el precio mínimo de la vela anterior sea inferior al precio de esta línea para la ruptura de abajo hacia arriba, o que el precio máximo sea superior a la línea para la ruptura de arriba hacia abajo. De esta forma se logra la posibilidad de obtener la señal exclusivamente en una vela. A continuación se muestra el código de esta función:

int GetSignal(string symbol,int TF,int TDLevel,int NumberOfDots,int Delta,double ExtraPoints)
  {
//Valor del precio del nivel construido según los puntos TD superiores
   double UL=iCustom(symbol,TF,"iglevels",GL_TDLevel,GL_NumberOfDots,GL_Delta,0,0)+ExtraPoints;
//...según los inferiores
   double DL=iCustom(symbol,TF,"iglevels",GL_TDLevel,GL_NumberOfDots,GL_Delta,1,0)-ExtraPoints;

   if(Bid<DL && iLow(symbol,TF,1)>DL)
     {
      return 1;
     }
   else
     {
      if(Ask>UL && iHigh(symbol,TF,1)<UL)
        {
         return 0;
        }
      else
        {
         return -1;
        }
     }
  }

Después, para que el experto funcione es necesario declarar las siguientes variables a nivel global:

int Signal = -1;          //Señal actual
datetime LastOrder;       //Fecha de la última operación ejecutada. Es imprescindible para evitar los casos de apertura repetida de órdenes en una vela.

La función Init() debe adjudicar a la variable LastOrder alguna fecha pasada, para que se pueda abrir una nueva orden.

int OnInit()
  {
   LastOrder = iTime(NULL, 0, 1);
   return(INIT_SUCCEEDED);
  }

A continuación se muestra la función OnTick, en la que sucede lo más importante:

void OnTick()
  {
   bool order_is_open=false;
//Tiene lugar la búsqueda de una orden abierta
   for(int i=0; i<OrdersTotal(); i++)
     {
      if(!OrderSelect(i,SELECT_BY_POS)) Print(GetLastError());

      if(OrderMagicNumber()==MagicNumber)
        {
         order_is_open=true;
         break;
        }
     }

//Obtener la señal actual
   Signal=GetSignal(Symbol(),0,GL_TDLevel,GL_NumberOfDots,GL_Delta,S_ExtraPoints);

//Cálculo del tamaño del stop
   double tsl=NormalizeDouble(StopLoss*MathPow(10,-Digits),Digits);

   if(order_is_open==true)
     {
      //Cálculo del precio del stop
      double p=NormalizeDouble(Ask-tsl,Digits);
      if(OrderType()==1) p=NormalizeDouble(Ask+tsl,Digits);

      if(OrderType()==0 && OrderStopLoss()<p)
        {
         if(!OrderModify(OrderTicket(),OrderOpenPrice(),p,0,0)) Print(GetLastError());
        }
      if(OrderType()==1 && OrderStopLoss()>p)
        {
         if(!OrderModify(OrderTicket(),OrderOpenPrice(),p,0,0)) Print(GetLastError());
        }
     }
//Si no hay órdenes
   if(order_is_open==false)
     {
      //Si en la vela actual aún no se han abierto órdenes
      if(iTime(NULL,0,0)!=LastOrder)
        {
         //Compra
         if(Signal==0)
           {
            if(!OrderSend(NULL,0,Lot,Ask,5,Ask-tsl,0,NULL,MagicNumber)) Print(GetLastError());
            LastOrder=iTime(NULL,0,0);
           }
         //Venta
         if(Signal==1)
           {
            if(!OrderSend(NULL,1,Lot,Bid,5,Ask+tsl,0,NULL,MagicNumber)) Print(GetLastError());
            LastOrder=iTime(NULL,0,0);
           }
        }
     }
  }

En ella se comprueba la presencia de una orden abierta, y si la hay, el experto comprueba la posibilidad de desplazar el stop-loss. Si todavía no hay una orden, entonces actúa de acuerdo con la señal obtenida por la función GetSignal. Momento importante: si tiene la intención de comparar en su programa dos números reales, entonces utilice indispensablemente la función NormalizeDouble antes de ello, de lo contrario, el resultado de la comparación puede resultar completamente ilógico.

El experto funciona de la forma siguiente:

Ejemplo de funcionamiento del experto:

Fig.10. Muestra del funcionamiento del experto en el simulador de estrategias

En la figura 10 se puede ver que da tanto posiciones con pérdidas, como con beneficios. Además, la magnitud de los beneficios supera la magnitud de las pérdidas, puesto que en las condiciones establecidas las pérdidas se limitan, y las operaciones rentables van acompañadas de TrailingStop. El experto puede mostrar tanto buenos resultados, como desoladores. Sin embargo, recordemos que no ha sido creado para el comercio real, sino para demostrar en la práctica las posibilidades de la aplicación de los puntos TD y de los indicadores creados sobre su base. Y nuestro asesor ha cumplido esta tarea con creces.


Resultado de la simulación

Fig.11. Resultado de la simulación

Este experto se puede mejorar si se le añade un cierre parcial de posición al alcanzar cierto beneficio, para que no se sufran pérdidas con la corrección de la tendencia, como sucede en ocasiones. En este caso, la posición se cerraría sin pérdidas y sin beneficio, lo que permite la conservación del capital. También es lógico notar que el tamaño del stop debe cambiar dependiendo de la actividad del movimiento de precio.


5. Experto para comerciar con líneas TD

Este experto es un ejemplo del uso de las líneas TD al crear sistemas comerciales. En general, las líneas TD son líneas de tendencia. Esto significa que si el precio ha cruzado una línea TD y ha continuado el movimiento en la misma dirección, entonces la tendencia puede cambiar.

El experto comercia solo con dos señales:

Señal de venta      Señal de compra

Fig.12. Señales según las cuales comercia etdlines

La posición de compra se abre si la línea TD alcista ha sido cruzada de abajo hacia arriba, y el precio ha pasado además una cierta cantidad de puntos (establecida por el usuario) en la misma dirección. Además la línea TD deberá estar dirigida hacia abajo, es decir, a la vela de fecha más tardía le debe corresponder el menor valor de precio de la línea.

La posición de venta tiene el aspecto inverso: si la línea bajista ha sido cruzada de arriba hacia abajo y el precio se ha reducido de forma adicional una cierta cantidad de puntos. La línea TD está dirigida hacia arriba.

Las posiciones se acompañan con un Trailing Stop de tamaño fijo. El cierre de posición se realiza solo por stop.

Más abajo se muestra el código que describe las variables introducidas por el usuario:

input int Level=1;                 //Nivel de las líneas TD
input double Lot=0.01;             //Tamaño del lote
input int AddPips=3;               //Cantidad añadida de puntos
input int Magic=88342;             //Número mágico de las órdenes
input int Stop=50;                 //Tamaño del stop en puntos
input int Bars_To_Open_New_Order=2;//Intervalo en barras hasta la apertura de una nueva orden

datetime LastBar;
datetime Trade_is_allowed;

En este experto se usa el retardo entre el cierre de una orden antigua y la apertura de una nueva. Esto se hace para que las posiciones no se abran con demasiada frecuencia.

En la función OnInit el experto adjudica los valores a las variables LastBar y Trade_is_allowed:

int OnInit()
  {
   LastBar=iTime(NULL,0,1);
   Trade_is_allowed=iTime(NULL,0,0);

   return(INIT_SUCCEEDED);
  }

La siguiente función es GetSignal, que retorna la señal comercial:

int GetSignal()
  {
//Si hay una vela nueva
   if(LastBar!=iTime(NULL,0,0))
     {
      double DU = iCustom(NULL, 0, "itdlines", Level, 0, 0);
      double DD = iCustom(NULL, 0, "itdlines", Level, 1, 0);
      double DU1 = iCustom(NULL, 0, "itdlines", Level, 0, 1);
      double DD1 = iCustom(NULL, 0, "itdlines", Level, 1, 1);
     }

   double add_pips=NormalizeDouble(AddPips*MathPow(10,-Digits),Digits);

//Ruptura de la línea U --> compra
   if(Ask>DU+add_pips && iLow(NULL,0,0)<Ask && DU<DU1)
     {
      return 0;
     }
   else
     {
      //Ruptura de la línea D --> venta
      if(Bid<DD-add_pips && iHigh(NULL,0,0)>Bid && DD>DD1)
        {
         return 1;
        }
      //No hay ruptura --> no hay señal
      else
        {
         return -1;
        }
     }

   return -1;
  }

La variable LastBar se usa para determinar una nueva vela. Esto es necesario para calcular los valores de los dos últimos puntos de las líneas TD con la llegada de cada nuevo tick, ya que se calculan solo una vez, cuando aparece una nueva vela. La función retorna: 0 — compra, 1 — venta, -1 — no hay señal.

Y al fin, la función OnTick, en la que tienen lugar todas las operaciones con órdenes:

void OnTick()
  {
   int signal=GetSignal();
   bool order_is_open=false;

//Buscando una orden abierta
   for(int i=0; i<OrdersTotal(); i++)
     {
      if(!OrderSelect(i,SELECT_BY_POS)) Print(GetLastError());

      //Según el valor del número mágico
      if(OrderMagicNumber()==Magic)
        {
         order_is_open=true;
         i=OrdersTotal();
        }
     }

//Tamaño del stop
   double stop=Stop*MathPow(10,-Digits);
//Si la orden ya está abierta
   if(order_is_open==true)
     {
      //Comprobación de las posibilidades de trasladar el stop

      //Valor del precio de la orden stop
      double order_stop=NormalizeDouble(OrderStopLoss(),Digits);

      //Si la orden es de compra
      if(OrderType()==0)
        {
         if(order_stop<NormalizeDouble(Ask-stop,Digits))
           {
            if(!OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(Ask-stop,Digits),0,0))Print(GetLastError());
           }
        }
      //Si la orden es de venta
      if(OrderType()==1)
        {
         if(order_stop>NormalizeDouble(Bid+stop,Digits))
           {
            if(!OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(Bid+stop,Digits),0,0))Print(GetLastError());
           }
        }
      Trade_is_allowed=iTime(NULL,0,0)+ChartPeriod(0)*60*Bars_To_Open_New_Order;
     }
//Si todavía no hay una orden abierta
   else
     {
      if(signal>=0 && iTime(NULL,0,0)>Trade_is_allowed)
        {
         if(signal==0)
           {
            if(!OrderSend(NULL,signal,Lot,Ask,5,Ask-stop,0,NULL,Magic)) Print(GetLastError());
           }
         if(signal==1)
           {
            if(!OrderSend(NULL,signal,Lot,Bid,5,Bid+stop,0,NULL,Magic)) Print(GetLastError());
           }

        }
     }
  }

Al principio se calcula la señal, con el ciclo for se realiza la búsqueda de la orden con el mismo número mágico establecido por el usuario (se considera que no habrá coincidencias con otros sistemas). A continuación, si ya hay una orden, se comprueba la posibilidad de trasladar el stop, y si es posible, se traslada. Si no hay órdenes, entonces, de acuerdo con la señal puede abrirse una nueva orden, pero solo con la condición de que la fecha de la vela actual sea superior a la fecha de la variable Trade_is_allowed (ella, a su vez, cuando haya una orden abierta, cambia su valor con la llegada de cada tick).

Este experto comercia de la forma siguiente:

Ejemplo de funcionamiento en el simulador

Fig.13. Ejemplo de funcionamiento del experto etdlines:

Se ve perfectamente que el experto comercia bien con los movimientos continuados de precio, sin embargo, los saltos bruscos de precio crean una cierta cantidad de señales falsas, que provocan operaciones con pérdidas. Más abajo se muestra el resultado de la simulación del experto:

Resultado de la simulación

Fig.14. Resultado de la simulación de etdlines


Conclusión

Mi objetivo era describir los puntos TD y las líneas TD de Thomas DeMark y mostrar la implementación de sus desarrollos en el lenguaje MQL4. En el artículo se describe un ejemplo de creación de 3 indicadores y 2 expertos. Como se puede ver, las ideas de DeMark encajan de forma lógica en los sistemas comerciales y confieren muy buenas perspectivas de uso.  


Para utilizar los indicadores y expertos adjuntos más abajo, es necesario instalar en primer lugar itddots.mq4, puesto que este indicador se usa en todos los demás programas. Para que etdlines.mq4 funcione, también es necesario instalar itdlines.mq4, y de forma análoga sucede con eglevels.mq4: es imprescindible instalar primero iglevels.mq4. Esto es extremadamente importante, dado que si no se instalan los indicadores necesarios, el programa que depende de ellos no funcionará, y puede provocar que el funcionamiento del terminal se detenga.

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

Archivos adjuntos |
eglevels.mq4 (7.22 KB)
etdlines.mq4 (7.92 KB)
iglevels.mq4 (9.15 KB)
itdlines.mq4 (6.57 KB)
itddots.mq4 (5.83 KB)
Lenguaje MQL4 para principiantes. Problemas complicados en frases simples. Lenguaje MQL4 para principiantes. Problemas complicados en frases simples.
Este es el segundo artículo de la serie "Lenguaje MQL4 para principiantes". Esta vez examinaremos construcciones del lenguaje más avanzadas y complicadas, aprenderemos nuevas opciones, y veremos cómo pueden aplicarse en la práctica diaria. Se familiarizará con un nuevo tipo de ciclo, "while"; con un nuevo tipo de condición, "switch"; con los operadores "break" y "continue". Además, aprenderá a escribir sus propias funciones y a trabajar con gamas multidimensionales. Y de postre, una explicación sobre un pre procesador.
Se ha añadido a MetaTrader 5 el sistema de cobertura de registro de posiciones Se ha añadido a MetaTrader 5 el sistema de cobertura de registro de posiciones
Para ampliar las posibilidades de los tráders de divisas fórex, se ha añadido a la plataforma un segundo sistema de registro: la cobertura. Ahora puede haber multitud de posiciones de un instrumento, incluidas las posiciones opuestas. Esto hace que sea posible poner en práctica estrategias de negociación con el llamado bloqueo, si el precio va en contra del tráder, este tiene la posibilidad de abrir una posición en la dirección opuesta.
Trading automatizado atípico Trading automatizado atípico
Trading eficaz y cómodo con la plataforma MT4 sin análisis detallado del mercado: ¿es posible? ¿Se puede llevar a cabo un trading así? Supongo que sí. ¡Especialmente en términos de trading automatizado!
Interfaces gráficas II: Control "Menú principal" (Capítulo 4) Interfaces gráficas II: Control "Menú principal" (Capítulo 4)
Es el artículo final de la segunda parte de la serie sobre las interfaces gráficas. Aquí vamos a considerar la creación del control “Menú principal”. Se demostrará el proceso de su desarrollo y la configuración de los manejadores de las clases de la librería para una correcta reacción a las acciones del usuario. Además, hablaremos de los modos de conexión de los menús contextuales a los elementos del menú principal. Aparte de eso, trataremos la cuestión del bloqueo de los controles inactivos en el momento actual.