Distintas maneras para averiguar la tendencia en MQL5

Dmitriy Skub | 19 diciembre, 2013

Introducción

Cualquier trader se sabe la regla "La tendencia es tu amiga, sigue la tendencia", pero casi todo el mundo tiene su propia idea sobre lo que es una tendencia. Casi todos los traders han oído o leído historias horribles sobre la quiebra de los traders por ir en contra de la tendencia.

Cualquier trader daría lo que fuera por la posibilidad de determinar con precisión la tendencia en un momento dado. Es quizá el Santo Grial que busca todo el mundo. En este artículo abordaremos distintas maneras de detección de una tendencia. Para ser más preciso, cómo programar distintas maneras clásicas para la detección de tendencias mediante MQL5.


1. ¿Qué es una tendencia y porqué conocerla?

En primer lugar, vamos a formular el concepto general de una tendencia.

La tendencia es la dirección del cambio de precio a largo plazo en el mercado. A partir de esta definición general de la tendencia obtenemos lo siguiente:

Vamos a ilustrar este concepto:

Figura 1. Análisis de la tendencia

Figura 1. Análisis de la tendencia

Viendo la figura, puedes observar el crecimiento de la tendencia general desde finales del 2005 hasta mayo del 2006 (flecha verde del gráfico). Pero si tenemos en cuenta segmentos más pequeños del gráfico del precio, verás que en febrero del 2006 había una clara tendencia a la baja (flecha rojo del gráfico), y casi todo el precio del mes de enero se encuentra en un rango lateral (flecha amarilla)

Por lo tanto, antes de identificar la tendencia tienes que determinar el período de tiempo que te interesa. En el trading, el período de tiempo determina en primer lugar el tiempo en el que se mantiene la posición en el mercado, desde su apertura hasta su cierre. Además de esto, depende de los niveles de la parada de protección y los cierres previstos, así como la frecuencia de las operaciones de trading.

El propósito de este artículo es ayudar a los nuevos traders a utilizar de manera competente las herramientas de detección de tendencia, proporcionadas por la plataforma MetaTrader 5. Este artículo pretende también los conocimientos básicos para escribir indicadores sencillos, que automatizan este proceso. El último objetivo es escribir expertos sencillos, que utilizan estos indicadores para el trading automatizado.  

  Para concretar, consideraremos el gráfico del precio diario (período de tiempo D1 en el terminal) del instrumento con mayor liquidez en el mercado de divisas - EURUSD. el tiempo durante el cual se mantiene un posición en dicho período puede variar de días a meses. Por consiguiente, lo que se pretende es tomar cientos y hasta miles de puntos, y poner los Stop Loss de protección a una distancia de cientos de puntos.

En general, se puede utilizar todo lo que se describe a continuación con cualquier período de tiempo. Sin embargo, hay que tener en cuenta que a menor período de tiempo del gráfico, mayor impacto será el impacto de la componente del ruido en el trading, debido a las noticias, especulaciones de los principales actores del mercado y otros factores, afectando la volatilidad del mercado.

Si tenemos en cuenta que cuanto más larga es la tendencia, menos posibilidad hay para que cambie, así que, al hacer trading siguiendo la tendencia, es más probable ganar que perder dinero. Ahora tienes que comprender cómo identificar la tendencia en el gráfico del precio. Lo vamos a comentar en este artículo.


2. Cómo se detecta una tendencia

Estos son algunos métodos conocidos para detectar la tendencia:

  1. Por Promedios móviles
  2. Por los picos del zigzag
  3. Por las indicaciones de ADX
  4. Por NRTR
  5. Por el color de las velas de Heiken Ashi

Vamos a abordar de manera sistemática todos estos métodos, sus ventajas e inconvenientes. Y después los compararemos en el mismo período del historial.

2.1. Detección de tendencia mediante Promedios móviles

Tal vez, la manera más fácil de detectar una tendencia y su dirección es mediante los promedios móviles. Una de las primeras herramientas de análisis técnico -el promedio móvil -todavía se utiliza en distintas variaciones y es la base de la mayoría de los indicadores. Los traders utilizan un promedio móvil, así como todo su conjunto, que a veces se le llama "abanico" (lo llamaremos "Fan" de aquí en adelante). 

Vamos a formular una regla sencilla para un promedio móvil:

En este caso usaremos el precio de cierre de la barra para reducir el número de "falsos" cambios d tendencia, cuando el precio fluctúa por encima y por debajo del promedio móvil (se le conoce como "rebote" o "bounce").  

Vamos a ilustrar este método:

Figura 2. Identificación de la tendencia mediante el promedio móvil

Figura 2. Identificación de la tendencia mediante el promedio móvil

En este caso utilizamos el gráfico EURUSD D1 y un promedio móvil simple con un período de 200, basado en los precios de cierre (línea roja del gráfico). en la parte inferior de la figura puedes ver el indicador de tendencia especialmente desarrollado - MATrendDetector. La posición del indicador del histograma indica la dirección de la tendencia, respecto al eje cero. +1 corresponde a una tendencia alcista. -1 corresponde a una bajista. Más adelante comentaremos este y otros indicadores, utilizados en este artículo.

Puedes observar que cuando la barra cierra por encima/debajo del promedio móvil, el precio cambia a menudo hacia la dirección opuesta. Es decir que este método proporciona muchas señales falsas. Es por lo que se utilización en los expertos e indicadores es muy limitada, sólo se usa como un filtro de tendencia muy "bruto".

2.2. Detección de tendencia mediante tres promedios móviles

¿Qué se puede hacer para mejorar la calidad de detección de tendencia mediante promedios móviles? Por ejemplo, puedes utilizar dos o más promedios móviles con distintos períodos. Después, la regla de detección de tendencia para cualquier número (más de uno) de promedios móviles con períodos distintos será la siguiente:

En este caso utilizamos los siguientes términos:

Este "orden correcto de los promedios" se llama también apertura hacia arriba y abajo del abanico del los promedios (up/down opening of averages fan), debido a su parecido visual.

Vamos a ilustrar este método:

Figura 3. Detección de tendencia mediante varios promedios móviles

Figura 3. Detección de tendencia mediante varios promedios móviles

En este caso utilizamos el gráfico EURUSD D1 y promedios móviles con los períodos de 200 (línea gruesa roja), 50 (línea de grosor medio amarilla medio) y 21 (línea fina morada), basados en los precios de cierre.

En la parte inferior de la figura puedes ver el indicador de tendencia especialmente desarrollado - FanTrendDetector. La posición del indicador del histograma indica la dirección de la tendencia, respecto al eje cero. +1 corresponde a una tendencia alcista. -1 corresponde a una bajista. Si el valor del histograma es cero, esto significa que no se puede determinar la tendencia. También está el indicador MATrendDetector para la comparación.

Es evidente que se ha reducido el número de falsas señales de cambio de tendencia. Pero la detección de la tendencia dura más. Esto tiene sentido, hasta que estén los promedios móviles alineados en el orden "correcto", hace falta un poco de tiempo. ¿Qué es mejor o peor?, pues depende del sistema de trading que utiliza estos métodos.

En este caso, los valores de los períodos de los promedios no se eligen de cualquier manera, sino que son los más utilizados por los traders y por el autor de este artículo. Eligiendo el conjunto de promedios móviles y su número, puedes intentar mejorar las características de este método de detección de tendencia para un par de divisas concreto.

2.3. Detección de tendencia mediante los máximos y mínimos del indicador ZigZag

Ahora vamos a aproximarnos a la detección de tendencia desde la perspectiva de los clásicos del análisis técnico. En concreto, vamos a utilizar la siguiente regla de Charles Dow:

Obtendremos los mínimos y máximos mediante los picos del indicador Zigzag.

Vamos a ilustrar este método:

Figura 4. Detección de tendencia mediante el indicador ZigZag

Figura 4. Detección de tendencia mediante el indicador ZigZag

En este caso utilizamos el gráfico EURUSD D1 y el indicador Zigzag con los siguientes parámetros: ExtDepth = 5, ExtDeviation = 5, ExtBackstep = 3.

en la parte inferior de la figura puedes ver el indicador de tendencia especialmente desarrollado - ZigZagTrendDetector.

El principal inconveniente de este método de detección de tendencia en tiempo real es la imposibilidad de saber si ya se ha formado el valor de pico o no. En el historial se puede ver el valor de pico muy bien, y puedes saber donde se ha formado. Sin embrago, con los cambios de precio en tiempo real, el valor de pico puede aparecer y desaparecer de repente. Para ver esto, basta con observar las lineas de representación del indicador ZigZag en el modo de prueba visual de cualquier experto.

Este inconveniente hace que el uso de este método en tiempo real sea inviable. Pero es muy útil para los análisis técnicos de los historiales de datos para encontrar patrones y evaluar la calidad de distintos sistemas de trading.

2.4. Detección de tendencia mediante el indicador ADX

El siguiente método que hemos considerado es la detección de tendencia mediante el indicador ADX (Average Directional Movement Index). Además de utilizarse para detectar la tendencia, este indicador también sirve para evaluar su fuerza. Es una característica muy valiosa del indicador ADX. Se determina la fuerza de la tendencia mediante la línea ADX, si el valor es superior a 20 (el nivel generalmente aceptado, pero no necesariamente el mejor en este momento), entonces la tendencia tiene la fuerza suficiente.

Se determina la dirección de la tendencia mediante la posición de las lineas +DI y -DI, la una respecto a la otra. Este indicador utiliza el suavizado de las tres lineas mediante promediado exponencial, por lo tanto su respuesta al cambio de tendencia e retrasa.

Vamos a formular la regla de detección de tendencia:

En este caso, la línea de tendencia ADX no se utiliza para detectar la tendencia. Es necesaria para reducir el número de señales falsas de este indicador. Si la tendencia es débil (ADX inferior a 20), es mejor esperar hasta que se haga más fuerte, y sólo entonces empezar a hacer trading con la tendencia.

Vamos a ilustrar este método:

Figura 5. Identificación de la tendencia mediante el indicador ADX

Figura 5. Identificación de la tendencia mediante el indicador ADX

En este caso utilizamos el gráfico EURUSD D1 y el indicador ADX con los siguientes parámetros: PeriodADX = 21 (línea gruesa azul -valor de la fuerza de tendencia de ADX, línea fina verde -valor de +DI, línea fina roja -valor de -DI).

en la parte inferior de la figura puedes ver el indicador de tendencia especialmente desarrollado -ADXTrendDetector. Para comparar, en el gráfico superior (carmesí) del indicador ADXTrendDetector se ha desactivado el filtro de fuerza de tendencia (ADXTrendLevel = 0), y en el gráfico inferior (azul) -se ha activado (ADXTrendLevel = 20).

Se puede ver que se ha descartado la parte del denominado "rebote" en la detección de tendencia, cuando activamos el filtro de fuerza de tendencia. Es preferible utilizar este filtro en el trabajo real. Se pude mejorar la calidad del indicador mediante una selección acertada de los parámetros externos de acuerdo con la situación actual del mercado (lateral/rango/tendencia) y según el tipo de movimiento del par de divisas.

En general, este indicador proporciona una buena oportunidad para construir sistemas de trading con trazado de tendencia como filtros de entrada.

2.5. Detección de tendencia mediante el indicador NRTR

El siguiente método de detección de tendencia utiliza el indicador NRTR (Nick Rypock Trailing Reverse). Este indicador se encuentra siempre a una distancia constante de los valores extremos del precio alcanzado, los precios más bajos en las tendencias alcistas y precios más altos en las tendencias bajistas. El concepto principal de este indicador -se deben ignorar los pequeños movimientos correctivos opuestos a la tendencia principal, y el movimiento opuesto a la tendencia principal, que exceda cierto nivel, indica un cambio en la dirección de la tendencia.

De ahí la regla para determinar la dirección de la tendencia:

Para reducir el impacto de la falsa tendencia de conmutación debida a las fluctuaciones de los precios, usaremos los precios de cierre para comprobar la posición de la línea de NRTR.

Vamos a ilustrar este método:

Figura 6. Identificación de la tendencia mediante el indicador NRTR

Figura 6. Identificación de la tendencia mediante el indicador NRTR

Estos grandes puntos azules corresponden a una tendencia alcista, mientras los puntos rojos corresponden a una tendencia bajista. En la parte inferior del gráfico se muestra nuestro indicador NRTRTrendDetector, descrito más abajo.

2.6. Detección de tendencia mediante las tres velas de Heiken Ashi

Otro método muy popular para determinar la tendencia -se hace mediante las velas de Heiken Ashi. Los gráficos de Heiken Ashi son la versión modificada de los gráficos de velas japonesas. Sus valores son parcialmente promediado con la vela anterior.

Vamos a ilustrar este método:

Figura 7. Detección de tendencia mediante el color de las velas de Heiken Ashi

Figura 7. Detección de tendencia mediante el color de las velas de Heiken Ashi

Como puedes ver, este método tampoco está exento de señales "falsas" cuando hay fluctuaciones de precio en un rango lateral. Pero lo peor es que este indicador puede no sólo volver a dibujar la última barra, sino también la penúltima. Es decir, la señal con la que hemos entrado, se puede invertir en la siguiente barra. Esto se debe al hecho de que cuando se determina el color de las velas, se analizan dos barras, por lo que se recomienda utilizar este método en conjuntamente con otras señales de apoyo.


3. Indicadores de tendencia

Ahora vamos a crear los indicadores de tendencia.

3.1. Indicador de tendencia basado en el promedio móvil

El indicador más fácil, así que el método más fácil para determinar la tendencia, basado en el promedio móvil. Vamos a ver las partes que lo componen. El código fuente entero del indicador está en el archivo MATrendDetector.MQ5, adjunto a este artículo.

Al principio del código del indicador está la línea que conecta la librería para calcular los distintos promedios móviles. Esta librería está incluida en el terminal de cliente y está lista para su utilización inmediatamente después de la instalación. Esta es la línea:

#include <MovingAverages.mqh>

Usaremos una función, la que calcula el promedio móvil simple:

double SimpleMA(const int position, const int period, const double &price[])

Aquí defines los parámetros de entrada:

La función devuelve el valor calculado del promedio móvil.

La siguiente parte del código contiene los ajustes iniciales para mostrar el indicador en la pantalla:

//---------------------------------------------------------------------
#property indicator_separate_window
//---------------------------------------------------------------------
#property indicator_applied_price       PRICE_CLOSE
#property indicator_minimum             -1.4
#property indicator_maximum             +1.4
//---------------------------------------------------------------------
#property indicator_buffers             1
#property indicator_plots               1
//---------------------------------------------------------------------
#property indicator_type1               DRAW_HISTOGRAM
#property indicator_color1              Black
#property indicator_width1              2
//---------------------------------------------------------------------

Se establecen los siguientes parámetros:

Los dos últimos parámetros te permiten establecer una escala fija para mostrar el gráfico del indicador. Esto es posible porque conocemos los valores máximos y mínimos de nuestro indicador desde -1 a +1, ambos incluidos. Esto se hace par conseguir un aspecto más bonito del gráfico, y no solapar los bordes de la ventana y el título del indicador en la ventana.

La siguiente parte es la introducción de los parámetros del indicador, que se pueden cambiar durante la colocación del indicador en el gráfico o más tarde, cuando está funcionando:

input int   MAPeriod = 200;

Hay sólo un parámetro -el valor del período del promedio móvil.

La siguiente parte fundamental del indicador -las funciones que procesan distintos eventos, que suceden durante el funcionamiento del indicador en el gráfico.

La primera es la función de inicialización -OnInit(). Se le hace una llamada inmediatamente después de cargar el indicador. En nuestro indicador, se ve del siguiente modo:

void OnInit()
{
  SetIndexBuffer( 0, TrendBuffer, INDICATOR_DATA );
  PlotIndexSetInteger( 0, PLOT_DRAW_BEGIN, MAPeriod );
}

La función SetIndexBuffer() enlaza el array previamente declarado, en el cual almacenaremos los valores de la tendencia TrendBuffer[], con uno de los buffers del indicador. Sólo tenemos un buffer de indicador y su índice es cero.

La función PlotIndexSetInteger() establece el número de barras iniciales, sin dibujarlas en la ventana del indicador.

Puesto que es matemáticamente imposible calcular el promedio móvil simple en un número de barras inferior al período del indicador, vamos a especificar un número de barras igual al período del promedio móvil.

A continuación está la función que procesa los eventos necesarios para volver a calcular un indicador -OnCalculate():

int OnCalculate(const int _rates_total, 
                const int _prev_calculated,
                const int _begin, 
                const double& _price[ ] )
{
  int  start, i;

//   Si el número de barras en la pantalla es inferior al período del promedio móvil, no se pueden realizar los cálculos:
  if( _rates_total < MAPeriod )
  {
    return( 0 );
  }

//  Determinar la barra inicial para los cálculos del buffer del indicador:
  if( _prev_calculated == 0 )
  {
    start = MAPeriod;
  }
  else
  {
    start = _prev_calculated - 1;
  }

//      Bucle de cálculo de los valores del buffer del indicador:
  for( i = start; i < _rates_total; i++ )
  {
    TrendBuffer[ i ] = TrendDetector( i, _price );
  }

  return( _rates_total );
}

Se llama a esta función primero después de la inicialización del indicador y más adelante a cada cambio en los datos del precio. Por ejemplo, cuando llega un nuevo tick al símbolo sobre el cual se calcula el indicador. Vamos a verlo en detalle.

Primero, comprueba si hay bastantes números de barras en el gráfico -si es inferior al período del promedio móvil, entonces no hay nada que calcular y la función finaliza con el operador return. Si hay suficientes barras, se determina la barra inicial, a partir de la cual se calcula el indicador. Se hace esto para no volver a calcular todos los valores del indicador a cada tick del precio.

En este caso utilizamos un mecanismo proporcionado por el terminal. Cada vez que llamas a una función de control, se comprueba el valor del argumento de la función _prev_calculated -se trata del número de barras procesadas en la llamada anterior de la función OnCalculate(). Si es cero, se vuelven a calcular todos los valores del indicador. De lo contrario, se vuelve a calcular únicamente la última barra con el índice _prev_calculated - 1.

El bucle para calcular los valores del buffer del indicador se hace con un operador for -desde el cual podemos llamar a la función de detección de tendencia TrendDetector para cada valor calculado de nuevo del buffer del indicador. Por lo tanto, anulando sólo esta función, podemos implementar distintos algoritmos para calcular la dirección de la tendencia. En este caso, las demás partes del indicador quedan prácticamente sin cambios (es posible que cambien los parámetros externos).

Veamos ahora la propia función de detección de tendencia - TrendDetector.

int TrendDetector(int _shift, const double& _price[])
{
  double  current_ma;
  int     trend_direction = 0;

  current_ma = SimpleMA(_shift, MAPeriod, _price);

  if(_price[_shift] > current_ma)
  {
    trend_direction = 1;
  }
  else if(_price[_shift] < current_ma)
  {
    trend_direction = -1;
  }

  return(trend_direction);
}

La función lleva acabo las siguientes tareas:

Cuando la función devuelve cero, no se puede detectar la tendencia.

Se puede ver el resultado del trabajo del indicador en la Figura 2 y la Figura 3.

3.2. Indicador de tendencia basado en el "Fan" de los promedios móviles

Vamos a ver ahora, cómo se puede crear un indicador un poco más complejo en base al anterior, que utiliza el "Fan" de promedios móviles para detectar una tendencia.

El código fuente entero del indicador está en el archivo FanTrendDetector.MQ5, adjunto a este artículo.

Las diferencias entre este indicador y el anterior son las siguientes:

input int MA1Period = 200; // valor del período del promedio móvil superior
input int MA2Period = 50;  // valor del período del promedio móvil intermedio
input int MA3Period = 21;  // valor del período del promedio móvil inferior
int TrendDetector(int _shift, const double& _price[])
{
  double  current_ma1, current_ma2, current_ma3;
  int     trend_direction = 0;

  current_ma1 = SimpleMA(_shift, MA1Period, _price);
  current_ma2 = SimpleMA(_shift, MA2Period, _price);
  current_ma3 = SimpleMA(_shift, MA3Period, _price);

  if(current_ma3 > current_ma2 && current_ma2 > current_ma1)
  {
    trend_direction = 1;
  }
  else if(current_ma3 < current_ma2 && current_ma2 < current_ma1)
  {
    trend_direction = -1;
  }

  return(trend_direction);
}

La función comprueba si los promedios móviles se encuentran en el orden correcto, comparándolos entre sí mediante los operadores if...else y su orden. Si los promedios están dispuestos en orden creciente, la función devuelve 1 -tendencia alcista. Si los promedios están dispuestos en orden decreciente, la función devuelve -1 -tendencia bajista. Si ambas condiciones, comprobadas en la parte if, son falsas, se devuelve cero (no se puede determinar la tendencia). La función tiene dos argumentos de entrada -el cambio en el buffer de la barra analizada y propio buffer con las series de precios.

Las de las partes restantes del indicador son las mismas que en el indicador anterior.

3.3. Indicador de tendencia basado en el indicador ZigZag

Vamos a ver ahora el indicador que utiliza las rupturas del indicador ZigZag para determinar los valores extremos y detectar la dirección de la tendencia según Charles Dow. El código fuente entero del indicador está en el archivo ZigZagTrendDetector.MQ5, adjunto a este artículo.

Se asignan las variables externas con los valores de los parámetros del indicador externo ZigZag:

//---------------------------------------------------------------------
//  Parámetros externos:
//---------------------------------------------------------------------
input int   ExtDepth = 5;
input int   ExtDeviation = 5;
input int   ExtBackstep = 3;
//---------------------------------------------------------------------

Una diferencia significativa caracteriza este indicador -el numero de buffers del indicador. Además del buffer para la visualización, utilizamos aquí dos buffers más para los cálculos. Por lo tanto, hacemos los cambios de ajustes apropiados en el código del indicador:

#property indicator_buffers  3

Añade dos buffers adicionales. Almacenarán el valor extremo, obtenido a partir del indicador externo ZigZag:

double ZigZagHighs[];  // puntos de giro superiores del zigzag
double ZigZagLows[];   // puntos de giro inferiores del zigzag

Hay que hacer también cambios en el controlador del evento de inicialización del indicador -se definen estos dos buffers adicionales como buffers de cálculo:

//  Buffers para almacenar los puntos de giro del zigzag
SetIndexBuffer(1, ZigZagHighs, INDICATOR_CALCULATIONS);
SetIndexBuffer(2, ZigZagLows, INDICATOR_CALCULATIONS);

En el código de cálculo de la función OnCalculate tenemos que proporcionar la lectura de las rupturas del zigzag en nuestros buffers. Esto se hace de la siguiente manera:

//  Copiar los puntos de giro superiores e inferiores del zigzag a los buffers:
  CopyBuffer(indicator_handle, 1, 0, _rates_total - _prev_calculated, ZigZagHighs);
  CopyBuffer(indicator_handle, 2, 0, _rates_total - _prev_calculated, ZigZagLows);

//  Bucle para calcular los valores del buffer del indicador:
  for(i = start; i < _rates_total; i++)
  {
    TrendBuffer[i] = TrendDetector(i);
  }

Este es el código de la función TrendDetector:

//---------------------------------------------------------------------
//  Determinar la dirección actual de la tendencia:
//---------------------------------------------------------------------
//  Devuelve:
//    -1 -tendencia al alza -Down
//    +1 -tendencia al alza -Up
//     0 -tendencia sin definir
//---------------------------------------------------------------------
double    ZigZagExtHigh[2];
double    ZigZagExtLow[2];
//---------------------------------------------------------------------
int TrendDetector(int _shift)
{
  int    trend_direction = 0;

//  Encontrar los últimos puntos de giro del zigzag:
  int    ext_high_count = 0;
  int    ext_low_count = 0;
  for(int i = _shift; i >= 0; i--)
  {
    if(ZigZagHighs[i] > 0.1)
    {
      if(ext_high_count < 2)
      {
        ZigZagExtHigh[ext_high_count] = ZigZagHighs[i];
        ext_high_count++;
      }
    }
    else if(ZigZagLows[i] > 0.1)
    {
      if(ext_low_count < 2)
      {
        ZigZagExtLow[ext_low_count] = ZigZagLows[i];
        ext_low_count++;
      }
    }

//  Si se encuentran dos pares de valores extremos, interrumpir el bucle
    if(ext_low_count == 2 && ext_high_count == 2)
    {
      break;
    }
  }

//  si no se encuentra el número de valores extremos, no se puede determinar la tendencia:
  if(ext_low_count != 2 || ext_high_count != 2)
  {
    return(trend_direction);
  }

//  Comprobar el cumplimiento de la condición de Dow:
  if(ZigZagExtHigh[0] > ZigZagExtHigh[1] && ZigZagExtLow[0] > ZigZagExtLow[1])
  {
    trend_direction = 1;
  }
  else if(ZigZagExtHigh[0] < ZigZagExtHigh[1] && ZigZagExtLow[0] < ZigZagExtLow[1])
  {
    trend_direction = -1;
  }

  return(trend_direction);
}

Buscamos aquí los cuatro últimos valores extremos del zigzag. Ten en cuenta que la búsqueda se vuelve atrás en el tiempo. Es por eso que el índice en el bucle for decrece a cada iteración de búsqueda hasta llegar a cero. Si se encuentran los valores extremos, se comparan entre sí, para mantener la coherencia de la definición de la tendencia según Dow. Los valores extremos pueden estar en dos posiciones -de tendencia alcista y de tendencia bajista. Se comprueban estas variantes con los operadores if...else.

3.4. Indicador de tendencia basado en el indicador ADX

Considera el indicador de tendencia ADXTrendDetector, que utiliza el indicador ADX. El código fuente completo del indicador se encuentra en el archivo ADXTrendDetector.MQ5, adjunto a este artículo. Se asignan las variables externas con los valores de los parámetros del indicador externo ADX:

//---------------------------------------------------------------------
//      Parámetros externos
//---------------------------------------------------------------------
input int  PeriodADX     = 14;
input int  ADXTrendLevel = 20;

Este es el código de la función TrendDetector:

//---------------------------------------------------------------------
//  Determinar la dirección actual de la tendencia:
//---------------------------------------------------------------------
//  Devolver:
//    -1 -tendencia al alza -Down
//    +1 -tendencia al alza -Up
//     0 -tendencia sin definir
//---------------------------------------------------------------------
int TrendDetector(int _shift)
{
  int     trend_direction = 0;
  double  ADXBuffer[ 1 ];
  double  PlusDIBuffer[ 1 ];
  double  MinusDIBuffer[ 1 ];

//  Copiar los valores del indicador ADX a los buffers:
  CopyBuffer(indicator_handle, 0, _shift, 1, ADXBuffer);
  CopyBuffer(indicator_handle, 1, _shift, 1, PlusDIBuffer);
  CopyBuffer(indicator_handle, 2, _shift, 1, MinusDIBuffer);

//  Si se tiene en cuenta el valor de ADX (fuerza de la tendencia):
  if(ADXTrendLevel > 0)
  {
    if(ADXBuffer[0] < ADXTrendLevel)
    {
      return(trend_direction);
    }
  }

//  Comprobar las posiciones de +DI y -DI, la una respecto a la otra:
  if(PlusDIBuffer[0] > MinusDIBuffer[0])
  {
    trend_direction = 1;
  }
  else if(PlusDIBuffer[0] < MinusDIBuffer[0])
  {
    trend_direction = -1;
  }

  return( trend_direction );
}

Mediante CopyBuffer() obtienes los valores necesarios de los buffers del indicador a partir del indicador externo ADX para un número de barras, indicado por el argumento _shift. A continuación, analiza las posiciones de +DI y -DI, la una respecto a la otra. Si es necesario, ten en cuenta la fuerza de la tendencia -si es inferior al valor establecido, no se puede detectar la tendencia.

3.5. Indicador de tendencia basado en el indicador NRTR

La estructura del indicador de tendencia NRTRTrendDetector, basado en NRTR, es parecida a la anterior. El código fuente completo del indicador se encuentra en el archivo NRTRTrendDetector.MQ5, adjunto a este artículo.

La primera diferencia -en el bloque de los parámetros externos:

//---------------------------------------------------------------------
//      Parámetros externos:
//---------------------------------------------------------------------
input int     ATRPeriod =  40;    // Período ATR, en barras
input double  Koeff     = 2.0;    // Coeficiente de cambio de valor de ATR   
//---------------------------------------------------------------------

La segunda diferencia -en la función de detección de la dirección de tendencia TrendDetector:

//---------------------------------------------------------------------
//      Determinar la dirección actual de la tendencia:
//---------------------------------------------------------------------
//  Devolver:
//    -1 -tendencia al alza -Down
//   +1 -tendencia al alza -Up
//     0 -tendencia sin definir
//---------------------------------------------------------------------
int TrendDetector(int _shift)
{
  int     trend_direction = 0;
  double  Support[1];
  double  Resistance[1];

//      Copiar los valores del indicador NRTR a los buffers:
  CopyBuffer(indicator_handle, 0, _shift, 1, Support);
  CopyBuffer(indicator_handle, 1, _shift, 1, Resistance);

//  Comprobar los valores de las lineas del indicador:
  if(Support[0] > 0.0 && Resistance[0] == 0.0)
  {
    trend_direction = 1;
  }
  else if(Resistance[0] > 0.0 && Support[0] == 0.0)
  {
    trend_direction = -1;
  }

  return( trend_direction );
}

Leemos aquí los valores desde los dos buffers con índices 0 y 1 del indicador externo NRTR. Los valores del buffer Support son distintos de cero cuando hay una tendencia alcista, y los valores del buffer Resistance son distintos de cero cuando hay una tendencia bajista.

3.6. Detección de tendencia basada en las velas de Heiken Ashi

Vamos a ver ahora el indicador de tendencia que utiliza las velas de Heiken Ashi.

En este caso, no vamos a llamar al indicador externo, sino que vamos a calcular las velas nosotros mismos. Esto mejorará el rendimiento del indicador y liberará la CPU para tareas más importantes. El código fuente completo del indicador se encuentra en el archivo HeikenAshiTrendDetector.MQ5, adjunto a este artículo.

Puesto que el indicador Heiken Ashi no realiza el ajuste de los parámetros externos, podemos quitar el bloque con los operadores input. Nos esperan cambios relevantes en la parte del controlador de eventos para volver a calcular el indicador. Utilizaremos aquí una variante alternativa del controlador, que proporciona un acceso a todos los arrays de precio del gráfico actual.

Este es el código de la función OnCalculate():

int OnCalculate(const int _rates_total, 
                const int _prev_calculated,
              const datetime& Time[],
              const double& Open[],
              const double& High[],
              const double& Low[],
              const double& Close[],
              const long& TickVolume[],
              const long& Volume[], 
              const int& Spread[])
{
  int     start, i;
  double  open, close, ha_open, ha_close;

//  Determinar la barra inicial para el cálculo del buffer del indicador:
  if(_prev_calculated == 0)
  {
    open = Open[0];
    close = Close[0];
    start = 1;
  }
  else
  {
    start = _prev_calculated - 1;
  }

//  Bucle para el cálculo de los valores del buffer del indicador:
  for(i = start; i < _rates_total; i++)
  {
//  Precio de apertura de las velas de Heiken Ashi:
    ha_open = (open + close) / 2.0;

//  Precio de cierre de las velas de Heiken Ashi:
    ha_close = (Open[i] + High[i] + Low[i] + Close[i]) / 4.0;

    TrendBuffer[i] = TrendDetector(ha_open, ha_close);

    open = ha_open;
    close = ha_close;
  }

  return(_rates_total);
}

Para determinar el color de las velas de Heiken Ashi, necesitamos sólo dos precios, la apertura y el cierre, así que contamos sólo con ellos. 

Después de detectar la dirección de la tendencia mediante la llamada de la función TrendDetector, almacena los valores del precio actual de las velas de Heiken Ashi en las variables intermedias open y close. La función TrendDetector parece muy sencilla. La puedes insertar en OnCalculate, pero para mayor versatilidad en el caso de algoritmos más avanzados y complejos dejamos esta función. Esta es la función:

int TrendDetector(double _open, double _close)
{
  int    trend_direction = 0;

  if(_close > _open)         // si la vela está crece, se trata de una tendencia alcista
  {
    trend_direction = 1;
  }
  else if(_close < _open)     // si la vela decrece, se trata de una tendencia bajista
  {
    trend_direction = -1;
  }

  return(trend_direction);
}

Los argumentos de la función son los dos precios de las velas de Heiken Ashi, apertura y cierre, y son los que determinan la dirección.


4. Ejemplo de utilización de un indicador de detección de tendencia en un experto

Vamos a crear un Expert Advisor que utiliza distintos indicadores. Sería interesante comparar los resultados de los expertos que utilizan distintos métodos de detección de tendencia. Primero, comprueba los resultados con los parámetros por defecto, y luego trata de encontrar los óptimos.

En este caso, el propósito de crear los Expert Advisors es la comparación de los métodos de detección de tendencia por precisión y velocidad. Por lo tanto, vamos a formular los principios generales de creación de todos los Expert Advisors:

Todos los indicadores de tendencia que hemos creado contienen un buffer de indicador con índice cero, que almacena los datos necesarios relativos a la dirección de la tendencia. Lo utilizaremos en el Expert Advisor para obtener una señal o abrir/cerrar una posición.

Puesto que necesitamos funciones de trading, hemos incluido la librería correspondiente, que está instalada con MetaTrader 5. Esta librería contiene la clase CTrade y varios métodos para trabajar con posiciones y órdenes. Esto simplifica la rutina de funcionamiento de las funciones de trading. La librería está incluida en la siguiente línea:

#include <Trade\Trade.mqh>

Usaremos dos métodos de la librería: las de abrir y cerrar una posición. El primer método te permite abrir la posición de una determinada dirección y volumen:

PositionOpen(const string symbol, 
             ENUM_ORDER_TYPE order_type,
             double volume, double price,
             double sl, double tp, const string comment )

Los argumentos de entrada son los siguientes:

El segundo m´todo te permite cerrar una posición:

PositionClose( const string symbol, ulong deviation )

Los argumentos de entrada son los siguientes:

Vamos a ver en detalle la estructura del Expert Advisor que utiliza el indicador MATrendDetector. El código fuente completo del Expert Advisor se encuentra en el archivo MATrendExpert.MQ5, adjunto a este artículo. El primer bloque importante del experto es el bloques de ajustes de los parámetros externos.

input double Lots = 0.1;
input int    MAPeriod = 200;

El parámetro Lots del Expert Advisor es el volumen del lote utilizado cuando se abre la posición. Para obtener una comparativa de los resultados de los distintos métodos de detección de tendencia, utilizamos el lote permanente sin gestión de fondos. Todos los indicadores de tendencia, comentados antes, utilizan todos los demás parámetros externos. La lista y el propósito son exactamente iguales a los del indicador correspondiente.

El segundo bloque importante del Expert Advisor es la inicialización de su controlador de eventos.

//---------------------------------------------------------------------
//      Inicialización del controlador de eventos:
//---------------------------------------------------------------------
int OnInit()
{
//  Crear un controlador externo del indicador para futuras referencias a el:
  ResetLastError();
  indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Examples\\MATrendDetector", MAPeriod);

// Si la inicialización falla, devolver un código distinto de cero:
  if(indicator_handle == INVALID_HANDLE)
  {
    Print("Error al inicializar MATrendDetector, código = ", GetLastError());
    return(-1);
  }
  return(0);
}

Aquí, creamos el identificado para hacer referencia al indicador de tendencia, y, si hay éxito en dicha creación, se devuelve el código cero. Si hay un fallo en la creación del identificador del indicador (por ejemplo, si el compilador no se ha compilado en el formato EX5), mostramos un mensaje informando de ello y se devuelve un código distinto de cero. En este caso, se detiene Expert Advisor y se descarga del terminal, con el correspondiente mensaje en el diario.

El siguiente bloque importante del Expert Advisor es la desinicialización de su controlador de eventos.

//---------------------------------------------------------------------
//      Desinicialización del controlador de eventos:
//---------------------------------------------------------------------
void OnDeinit(const int _reason)
{
//  Eliminar el controlador del indicador:
  if(indicator_handle != INVALID_HANDLE)
  {
    IndicatorRelease(indicator_handle);
  }
}


Aquí se elimina el identificador del indicador y se libera la memoria que tenía asignada.

No tienes que hacer nada más para desinicializar el Expert Advisor.

A continuación está el bloque principal del identificador de eventos del Expert Advisor, relativo a un nuevo tick del símbolo actual.

//---------------------------------------------------------------------
//  Identificador de eventos relativo a un nuevo tick del símbolo actual:
//---------------------------------------------------------------------
int    current_signal = 0;
int    prev_signal = 0;
bool   is_first_signal = true;
//---------------------------------------------------------------------
void OnTick()
{
//  Esperar al comienzo de una nueva barra:
  if(CheckNewBar() != 1)
  {
    return;
  }

//  Obtener la señal para abrir/cerrar una posición:
  current_signal = GetSignal();
  if(is_first_signal == true)
  {
    prev_signal = current_signal;
    is_first_signal = false;
  }

//  Seleccionar la posición mediante el símbolo actual:
  if(PositionSelect(Symbol()) == true)
  {
//  Comprobar si tenemos que cerrar una posición inversa:
    if(CheckPositionClose(current_signal) == 1)
    {
      return;
    }
  }

//  Comprobar si hay una señal BUY:
  if(CheckBuySignal(current_signal, prev_signal) == 1)
  {
    CTrade  trade;
    trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, Lots, SymbolInfoDouble(Symbol(), SYMBOL_ASK ), 0, 0);
  }

//  Comprobar si hay una señal SELL:
  if(CheckSellSignal(current_signal, prev_signal) == 1)
  {
    CTrade  trade;
    trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, Lots, SymbolInfoDouble(Symbol(), SYMBOL_BID ), 0, 0);
  }

//  Almacenar la señal actual:
  prev_signal = current_signal;
}

Vamos a ver las funciones adicionales utilizadas por el Expert Advisor.

En primer lugar, nuestro Expert Advisor tiene que buscar la señal para abrir otra barra nueva en el gráfico. Se utiliza la función CheckNewBar para hacerlo:

//---------------------------------------------------------------------
//  Devolver el flag de la nueva barra:
//---------------------------------------------------------------------
//  -si devuelve 1, hay una nueva barra
//---------------------------------------------------------------------
int CheckNewBar()
{
  MqlRates  current_rates[1];

  ResetLastError();
  if(CopyRates(Symbol(), Period(), 0, 1, current_rates)!= 1)
  {
    Print("Error copiando CopyRates, código = ", GetLastError());
    return(0);
  }

  if(current_rates[0].tick_volume>1)
  {
    return(0);
  }

  return(1);
}

Se determina la presencia de una nueva barra por el valor del volumen del tick. Al abrir una barra nueva, el valor inicial de la misma es cero (ya que no habían cotizaciones). Con la llegada del nuevo tick el valor pasa a ser igual a 1.

Crearemos en esta función el array current_rates[] de estructuras MqlRates, constituido por un elemento, copiamos en el array los precios actuales y la información de los volúmenes, y luego comprobamos el valor del volumen el tick. 

Usaremos esta función en nuestro controlador de eventos relativo al nuevo tick del símbolo actual, de la siguiente manera:

//  Esperar al comienzo de una nueva barra:
if(CheckNewBar()!= 1)
{
  return;
}

Por lo tanto, se abre una barra nueva, y puedes obtener una señal acerca de la dirección actual de la tendencia. Esto se hace de la siguiente manera:

//  Obtener la señal para abrir/cerrar una posición:
  current_signal = GetSignal();
  if(is_first_signal == true)
  {
    prev_signal = current_signal;
    is_first_signal = false;
  }

Puesto que tenemos que seguir los cambios de tendencia, es necesario recordar el valor de la tendencia de la barra anterior. Para ello, hemos utilizado la variable prev_signal en la parte del código anterior. Además, debes utilizar un flag, indicando que es la primera señal (aún no hay ninguna anterior). Se trata de la variable is_first_signal. Si el valor del flag es true, asignamos a la variable prev_signal el valor inicial.

En este caso utilizamos la función GetSignal, que devuelve la dirección actual de la tendencia, obtenida a partir de nuestro indicador. Este es su código:  

//---------------------------------------------------------------------
//      Obtener la señal para abrir/cerrar una posición:
//---------------------------------------------------------------------
int GetSignal()
{
  double    trend_direction[1];

//  Obtener la señal a partir del indicador de tendencia
  ResetLastError();
  if(CopyBuffer(indicator_handle, 0, 0, 1, trend_direction) != 1)
  {
    Print("Error copiando CopyBuffer, código = ", GetLastError());
    return(0);
  }

  return((int)trend_direction[0]);
}

Se copian los datos de la tendencia del indicador a partir del buffer cero de nuestro array de un elemento, trend_direction. Y la función devuelve el valor del elemento del array. Además se convierte el tipo double al tipo int para evitar advertencias del compilador.

Antes de abrir una nueva posición, debes comprobar si hace falta cerra la posición opuesta, abierta antes. También debes comprobar si existe ya una posición abierta en la misma dirección. Todo esto se hace mediante la siguiente parte del código:

//  Seleccionar la posición del símbolo actual:
  if(PositionSelect(Symbol()) == true)
  {
//  Comprobar si tenemos que cerrar una posición inversa:
    if(CheckPositionClose(current_signal) == 1)
    {
      return;
    }
  }

Para poder acceder a la posición, primero hay que seleccionarla, esto se hace mediante la función PositionSelect() del símbolo actual. Si la función devuelve true, entonces la posición existe y se ha seleccionado con éxito, así que la puedes utilizar.

Se utiliza la función CheckPositionClose para cerrar la posición opuesta:

//---------------------------------------------------------------------
//  Comprobar si tenemos que cerrar una posición
//---------------------------------------------------------------------
//  Devuelve:
//    0 -ninguna posición abierta
//    1 -posición ya abierta en la dirección de la señal
//---------------------------------------------------------------------
int CheckPositionClose(int _signal)
{
  long    position_type = PositionGetInteger(POSITION_TYPE);

  if(_signal == 1)
  {
//  Si hay una posición BUY ya abierta, devolver:
    if(position_type == (long)POSITION_TYPE_BUY)
    {
      return(1);
    }
  }

  if(_signal==-1)
  {
//  Si hay una posición SELL ya abierta, devolver:
    if( position_type == ( long )POSITION_TYPE_SELL )
    {
      return(1);
    }
  }

//  Cerrar posición:
  CTrade  trade;
  trade.PositionClose(Symbol(), 10);

  return(0);
}

En primer lugar, comprueba si la posición está abierta en la dirección de la tendencia. De ser así, la función devuelve 1 y no se cierra la posición actual. Si la posición se abre en la dirección opuesta, entonces tienes que cerrarla. Esto se hace mediante el método PositionClose descrito anteriormente. Ya que nos es una posición abierta, devuelve cero.

Una vez realizadas todas las comprobaciones y acciones necesarias para las posiciones existentes, tienes que comprobar si hay una nueva señal. Esto se hace mediante la siguiente parte del código:

//  Comprobar si hay una señal BUY:
if(CheckBuySignal(current_signal, prev_signal)==1)
{
  CTrade  trade;
  trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, Lots, SymbolInfoDouble(Symbol(), SYMBOL_ASK), 0, 0);
}

Si hay una señal BUY, entonces abre una posición larga con el volumen dado por el precio actual SYMBOL_ASK. Puesto que todas las posiciones están cerradas mediante la señal opuesta, entonces no se utilizan Take Profit y Stop Loss. El Expert Advisor está "siempre en el mercado". 

En el trading real se recomienda utilizar una protección Stop Loss en caso de circunstancias fortuitas, como la pérdida de la conexión con el servidor DC y otros casos de fuerza mayor.

Para las señales Sell todo es parecido:

//  Comprobar si hay una señal SELL:
if(CheckSellSignal(current_signal, prev_signal) == 1)
{
  CTrade  trade;
  trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, Lots, SymbolInfoDouble(Symbol(), SYMBOL_BID), 0, 0);
}


La única diferencia está en el precio de venta - SYMBOL_BID.

Se comprueba la presencia de la señal mediante la función CheckBuySignal para comprar y mediante la función CheckSellSignal para vender. Estas funciones son muy sencillas y claras:

//---------------------------------------------------------------------
//  Comprobar si la señal ha cambiado a BUY:
//---------------------------------------------------------------------
//  Devolver:
//    0 -ninguna señal
//    1 -hay una señal BUY
//---------------------------------------------------------------------
int CheckBuySignal(int _curr_signal, int _prev_signal)
{
//  Comprobar si la señal ha cambiado a BUY:
  if((_curr_signal==1 && _prev_signal==0) || (_curr_signal==1 && _prev_signal==-1))
  {
    return(1);
  }

  return(0);
}

//---------------------------------------------------------------------
//  Comprobar si hay una señal SELL:
//---------------------------------------------------------------------
//  Devuelve:
//    0 -ninguna señal
//    1 -hay una señal SELL
//---------------------------------------------------------------------
int CheckSellSignal(int _curr_signal, int _prev_signal)
{
//  Comprobar si la señal ha cambiado a SELL:
  if((_curr_signal==-1 && _prev_signal==0) || (_curr_signal==-1 && _prev_signal==1))
  {
    return(1);
  }

  return(0);
}

Comprobamos aquí si la tendencia ha cambiado a la dirección opuesta o si ha aparecido una dirección de la tendencia. Si se cumple cualquiera de estas condiciones, la función devuelve la presencia de la señal.

En general, dicho esquema del Expert Advisor ofrece una estructura bastante global, que se puede mejorar y ampliar fácilmente para adaptarse a algoritmos más complejos.

Se construyen los demás Expert Advisors exactamente de la misma manera. Existen diferencias importantes en el bloque de los parámetros externos, que deben corresponder al indicador de tendencia utilizado y deben recibir los argumentos al crear el controlador del indicador.

Vamos a ver los resultados de nuestro primer Expert Advisor en el historial de los datos. Usaremos el historial EURUSD, en el intervalo del 01.04.2004 hasta el 06.08.2010 con barras diarias. Después de ejecutar el Expert Advisor en el Simulador de estrategias con los parámetros por defecto, obtenemos los siguientes resultados:

Figura 8. Resultados de la prueba del Expert Advisor en el Simulador de estrategias mediante el indicador MATrendDetector

Figura 8. Resultados de la prueba del Expert Advisor en el Simulador de estrategias mediante el indicador MATrendDetector

Informe del simulador de estrategia
MetaQuotes-Demo (Build 302)

Ajustes
Experto: MATrendExpert
Símbolo: EURUSD
Período: Diario (2004.04.01 - 2010.08.06)
Entradas: Lots=0.100000

MAPeriod=200
Broker: MetaQuotes Software Corp.
Divisa: USD
Depósito inicial: 10 000,00

Resultados
Barras: 1649 Ticks: 8462551
Beneficio total neto: 3 624.59 Beneficio bruto: 7 029.16 Pérdida bruta: -3 404.57
Coeficiente de beneficio: 2.06 Pago previsto: 92,94
Factor de recuperación: 1,21 Ratio de Sharpe: 0,14

Disposición de saldo:
Disposición absoluta de saldo: 2 822.83 Disposición máxima de saldo: 2 822.83 (28.23%) Disposición relativa de saldo: 28.23% (2 822.83)
Disposición del valor negociable:
Disposición absoluta del valor negociable: 2 903.68 Disposición máxima del valor negociable: 2 989.93 (29.64%) Disposición relativa del valor negociable: 29.64% (2 989.93)

Total de operaciones: 39 Operaciones cortas (ganadas %): 20 (20.00%) Operaciones largas (ganadas %): 19 (15.79%)
Total de transacciones: 78 Operaciones con beneficio (% del total): 7 (17.95%) Operaciones con pérdida (% del total): 32 (82.05%)

Operación con mayor beneficio: 3 184.14 Operación con mayor pérdida (% del total): -226,65

Promedio de beneficio por operación: 1 004.17 Promedio de pérdida por operación: (% del total): -106.39

Ganancias máximas consecutivas ($): 4 (5 892.18) Pérdidas máximas consecutivas ($): 27 (-2 822.83)

Beneficio máximo consecutivo (recuento): 5 892.18 (4) Pérdida máxima consecutiva (recuento): -2 822.83 (27)

Promedio de ganancias consecutivas: 2 Promedio de pérdidas consecutivas: 8


En general queda muy bien, excepto la parte desde el inicia de la prueba hasta el 22.09.2004. No hay ninguna garantía de que esta sección no se vuelva a repetir en el futuro. Si observas el gráfico de este período, puedes observar un movimiento lateral predominante en un intervalo limitado. Bajo estas condiciones, nuestro sencillo experto de tendencia no estaba a la altura. Esta imagen con las transacciones colocadas en la misma, corresponde a dicho período.

Figura 9. Sección con movimiento lateral

Figura 9. Sección con movimiento lateral

Hay también un promedio móvil SMA200 en el gráfico.

Vamos a ver ahora lo que mostrará un Expert Advisor más "avanzado" mediante indicador de varios promedios móviles, en el mismo período y con los parámetros por defecto:

Figura 10. Resultados de la prueba del Expert Advisor en el Simulador de estrategias mediante el indicador FanTrendDetector

Figura 10. Resultados de la prueba del Expert Advisor en el Simulador de estrategias mediante el indicador FanTrendDetector

Informe del Simulador de estrategia
MetaQuotes-Demo (Build 302)

Ajustes
Experto: FanTrendExpert
Símbolo: EURUSD
Período: Diario (2004.04.01 - 2010.08.06)
Entradas: Lots=0.100000

MA1Period=200

MA2Period=50

MA3Period=21
Broker: MetaQuotes Software Corp.
Divisa: USD
Depósito inicial: 10 000.00

Resultados
Barras: 1649 Ticks: 8462551
Beneficio Total Neto 2 839.63 Beneficio bruto: 5 242.93 Pérdida bruta: -2 403.30
Coeficiente de beneficio 2.18 Pago previsto: 149.45
Factor de recuperación: 1.06 Ratio de Sharpe: 0.32

Disposición de saldo:
Disposición absoluta de saldo: 105.20 Disposición máxima de saldo: 1 473.65 (11.73%) Disposición relativa de saldo: 11.73% (1 473.65)
Disposición del valor negociable:
Disposición absoluta del valor negociable: 207.05 Disposición máxima del valor negociable: 2 671.98 (19.78%) Disposición relativa del valor negociable: 19.78% (2 671.98)

Total de operaciones: 19 Operaciones cortas (ganadas %): 8 (50.00%) Operaciones largas (ganadas %): 11 (63.64%)
Total de transacciones: 38 Operaciones con beneficio (% del total): 11 (57.89%) Operaciones con pérdida (% del total): 8 (42.11%)

Operación con mayor beneficio: 1 128.30 Operación con mayor pérdida (% del total): -830.20

Promedio de beneficio por operación: 476.63 Promedio de pérdida por operación: (% del total): -300,41

Ganancias máximas consecutivas ($): 2 (1 747.78) Pérdidas máximas consecutivas ($): 2 (-105,20)

Beneficio máximo consecutivo (recuento): 1 747.78 (2) Pérdida máxima consecutiva (recuento): -830.20 (1)

Promedio de ganancias consecutivas: 2 Promedio de pérdidas consecutivas: 1

¡Mucho mejor! si te fijas en la sección "problemática" que se apreciaba con el experto anterior, estás sería la imagen:

Figura 11. Resultados del FanTrendExpert en la sección con movimiento lateral

Figura 11. Resultados del FanTrendExpert en la sección con movimiento lateral

Al comprarla con la Figura 9, es obvio que el número de falsos avisos de cambio de tendencia ha disminuido. Pero el número de transacciones se ha reducido a la mitad, algo bastante lógico. Al analizar la curva de saldo/valor negociable de ambos Expert Advisors, puedes ver que el cierre de muchas transacciones no ha sido el óptimo en términos del máximo beneficio. Por lo tanto, la próxima puesta a nivel del Expert Advisor es perfeccionar el algoritmo de cierre de transacciones. Pero esto va más allá del objetivo de este artículo. Los lectores pueden hacerlo por sí mismos.


5. Resultados de las pruebas de los Expert Advisors

Vamos a probar todos nuestros expertos. A continuación se presenta todo el historial disponible del intervalo que va desde 1993 hasta 2010 en el par EURUSD y la periodicidad D1.

Figura 12. Prueba de MATrendExpert

Figura 12. Prueba de MATrendExpert

Figura 13. Prueba de FanTendExpert

Figura 13. Prueba de FanTendExpert

Figura 14. Prueba de ADXTrendExpert (ADXTrendLevel = 0)

Figura 14. Prueba de ADXTrendExpert (ADXTrendLevel = 0)

Figura 15. Prueba de ADXTrendExpert (ADXTrendLevel = 20)

Figura 15. Prueba de ADXTrendExpert (ADXTrendLevel = 20)

Figura 16. Prueba de NRTRTrendExpert

Figura 16. Prueba de NRTRTrendExpert

Figura 17. Prueba de Heiken Ashi

Figura 17. Prueba de Heiken Ashi

Vamos a analizar los resultados de las pruebas.

Entre los más comunes Expert Advisors hay dos fundamentales, el que está basado en un promedio móvil y el que está basado en el "Fan" de promedios móviles. De hecho, estos expertos son los que más se aproximan a la regla de seguimiento de la tendencia (y, por lo tanto, el precio), simplemente usando unas series suavizados de los precios para el último período de tiempo. Debido a la utilización de un promedio móvil "pesado" con un período de 200, parece que el impacto de la volatilidad del mercado está disminuyendo.

El bajo número de transacciones de estos Expert Advisors no es una desventaja, ya que el tiempo de retención de la posición puede tardar varios meses, siguiendo la tendencia de 200 días. Es interesante ver cómo alterna MATrendExpert las áreas de tendencia donde crece el saldo, y es plano (en el contexto del experto) donde hay pérdidas.

El método de detección de tendencia mediante el indicador ADX también proporcionó buenos resultados. Allí se cambió un poco el valor de PeriodADX a 17, lo que proporcionó resultados más uniformes a lo largo del historial. El efecto del filtro de fuerza de tendencia no es muy relevante. Puede que tengas que ajustar el parámetro ADXTrendLevel, o incluso ajustarlo de manera dinámica en función de la volatilidad del mercado. Hay varios períodos de disposición, y por lo tanto se requieren medidas adicionales para igualar la curva de saldo.

El indicador NRTR mostró una rentabilidad prácticamente nula con los parámetros por defecto, tanto en el intervalo entero de la prueba o a lo largo de intervalos elegidos al azar. En cierta medida esto es un signo de estabilidad de este método de detección de tendencia. A lo mejor, se puede mejorar la rentabilidad de este Expert Advisor ajustando los parámetros, es decir, se requiere una optimización.

Obviamente, el Expert Advisor basado en Heiken Ashi no era rentable. A pesar de verse bien en el historial, seguramente debido a que vuelve a dibujar en tiempo real, los resultados de la prueba están lejos de ser ideales. A lo mejor se pueden obtener mejores resultados con una versión suavizada de este indicador -Heiken Ashi Suavizado, que no tiende tanto a redibujar.

En definitiva, todos los Expert Advisors se van a beneficiar del sistema de apertura de posiciones con un nivel de Trailing Stop dinámico y con la creación de un nivel meta. Disponer de un sistema de gestión de capital estría muy bien, permitiendo reducir la disposición y tal vez incrementar el beneficio en intervalos largos.


Conclusión

De esta manera, no es tan difícil escribir un código que permite determinar la tendencia. Lo importante no es sólo hacerlo, sino también tener una idea razonable, aprovechando las reglas generales del mercado. Y lo más importante de estas leyes es la seguridad que te da el sistema de trading basado en las mismas, este sistema seguirá vigente durante mucho tiempo.