English Русский 中文 Deutsch 日本語 Português
Como escribir Zig Zags rápido sin redibujado

Como escribir Zig Zags rápido sin redibujado

MetaTrader 4Ejemplos | 20 abril 2016, 15:19
879 0
Candid
Candid

Introducción

Entre todos los algoritmos posibles para el mapeado de zig zag, podemos distinguir una clase que el autor llama "Zig zag con cambio al pasar el nivel de reducción de la velocidad". Esta clase, en su totalidad o en parte, incluye la mayoría de zig zag existentes. El propio nombre de la clase, de hecho, representa una plantilla de algoritmo. Para elaborar un indicador a partir de esta, es suficiente con solo añadir la función que detectará el nivel de ralentización. La diversidad de algoritmos de dicha función solo está limitada por la imaginación del autor del futuro zig zag.

Método general

En primer lugar, vamos a intentar formular el método general de escritura de un indicador. De este modo:

- La función start() de cualquier indicador (así como cualquier asesor experto) representa una función de retrollamada, es decir, una función que será llamada para que procese un evento concreto. Es decir, para procesar un tick.

- La finalidad de escribir un indicador es, por lo general, el cálculo de una o varias características del mercado. Junto con las cantidades accesorias necesarias para los cálculos, estas forman un conjunto de variables del indicador dado. Vamos a definir el estado del indicador como un conjunto de valores de dichas variables clave en un momento específico. En base a esta definición, podemos decir lo siguiente:

  • Al calcular los nuevos valores de las variables en un nuevo tick, la función start() calcula el nuevo estado del indicador.
  • De este modo, de hecho, la función start() es un operador que transfiere el indicador desde un estado a otro.

- En estos términos, el proceso de escritura de un indicador se reduce a determinar un conjunto de cantidades que describen su estado (variables de estado) y a escribir un operador que transfiera el indicador a un nuevo estado a la llegada de un nuevo tick. La inicialización de las variables de estado se convierte en parte esencial del algoritmo del indicador. Mostraremos cómo puede hacerse todo esto en el ejemplo del Zig Zag de un cierto tipo.

Qué Zig Zag nos interesan

Como se ha dicho anteriormente, nos interesan los Zig Zag que cambian al pasar el nivel de reducción de la velocidad. ¿Qué es un "nivel de reducción de la velocidad"? Asumimos que queremos escribir un Zig Zag para el que se fija el pico cuando el precio se desplaza de este pico en H puntos. Fijar un pico significa cambiar la dirección del segmento de un zig zag a una opuesta. Vamos solo a fijar el mínimo y ahora estaremos en un segmento más arriba. Vamos a introducir una variable para el tiempo del precio máximo de un segmento de arriba incompleto, TempMax. Fijaremos este máximo (y cambiaremos la dirección) si el precio atraviesa el nivel de:

SwitchLevel = TempMax - H *Point .

Si el máximo de tiempo se actualiza antes del cambio, tendremos que calcular el nuevo valor de SwitchLevel. De este modo, SwitchLevel seguirá el máximo del tiempo, siendo H los puntos detrás de él.

La situación será absolutamente simétrica para un segmento de abajo: SwitchLevel ahora seguirá el mínimo de tiempo (TempMin ), siendo los mismos puntos H detrás de este. Pero ahora tendremos:

SwitchLevel = TempMin + H *Point .

De hecho, hemos descrito solo el algoritmo de cálculo del nivel de ralentización para el Zig Zag que vamos a crear. Obviamente, no es el único algoritmo posible. Por ejemplo, si tenemos en cuenta el límite inferior o superior de un canal como nivel de ralentización, obtendremos exactamente tantos Zig Zag como métodos para el cálculo del canal. Además, en un análisis más detallado, la mayoría de zig zag conocidos por el autor resultan estar total o parcialmente incluidos en la clase considerada. Pero no todos ellos. Por ejemplo el zig zag calculado en los fractales de Williams no puede incluir en esta clase.

Modelo de Zig Zag

Vamos a determinar las variables del estado del Zig Zag.

En primer lugar, estará la dirección del segmento actual. Llamaremos a la variable correspondiente UpZ y le asignaremos el valor true para los segmentos de arriba y false para los segmentos de abajo.

Obviamente, debemos añadir a la lis TempMax y TempMin presentados anteriormente. También añadiremos sus coordenadas temporales. Aquí, no obstante, tenemos algún grado de libertad a la hora de definir las unidades de medida. Como coordenada temporal usaremos el número de barra comenzando desde el principio del gráfico, es decir, usaremos el sistema de numeración inverso al aceptado en MT4. Esto simplificará el código y mejorará su velocidad de ejecución. De este modo, la lista será completada con las variables TempMaxBar y TempMinBar.

Tenemos pensado dibujar el zig zag en un gráfico y usarlo de alguna forma. Por tanto, lo añadiremos a la lista de coordenadas de los últimos picos Zig Zag fijados: CurMax, CurMaxBar, CurMin, CurMinBar.

Y eso es todo respecto a la lista. Un autor de un zig zag concreto puede libremente completar la lista con aquello que vaya a hacer con este zig zag. Por ejemplo, puede ser razonable añadir las coordenadas de los picos anteriores: PreMax, PreMaxBar, PreMin, PreMinBar. O puede que necesitemos añadir las coordenadas de un número predefinido de picos precedentes, usando matrices en tal caso.

Operador de transición

En el método propuesto, escribir un operador de transición para un zig zag es una tarea bastante simple. Tan solo tenemos que traducir la definición de la clase del zig zag que nos interesa a MQL4. Así es como aparece:

// First, process the case of an up-segment
    if (UpZ) {
// Check whether the current maximum has changed
      if (High[pos]>TempMax) {
// If yes, then correct the corresponding variables
        TempMax = High[pos];
        TempMaxBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (Low[pos]<SwitchLevel()) {
// If yes, then fix the maximum
          CurMax = TempMax;
          CurMaxBar = TempMaxBar;
// And draw a peak
          ZZ[Bars-CurMaxBar]=CurMax;  // Here switching to reverse numbering
// Correct the corresponding variables
          UpZ = false;
          TempMin = Low[pos];
          TempMinBar = Bars-pos;  // Here switching to direct numbering
        }
      }
    }  else {
// Now processing the case of down-segment
// Check whether the current minimum has changed
      if (Low[pos]<TempMin) {
// If yes, then correct the corresponding variables
        TempMin = Low[pos];
        TempMinBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (High[pos]>SwitchLevel()) {
// If yes, then fix the minimum
          CurMin = TempMin;
          CurMinBar = TempMinBar;
// And draw a peak
          ZZ[Bars-CurMinBar]=CurMin;  // Here switching to reverse numbering
// Correct the corresponding variables
          UpZ = true;
          TempMax = High[pos];
          TempMaxBar = Bars-pos;  // Here switching to direct numbering
       }
      }
    }

El operador de transición está listo. Ahora podemos referirnos a las variables de estado del indicador en cualquier momento.

No obstante, dicho operador tiene una característica especial que puede percibirse como un error al escribir el zig zag. Vamos a considerar el fragmento siguiente con mayor detalle:

      if (High[pos]>TempMax) {
// If yes, then correct the corresponding variables
        TempMax = High[pos];
        TempMaxBar = Bars-pos;  // Here switching to direct numbering
      } else {
// If not, then check whether the slowing level has been broken through
        if (Low[pos]<SwitchLevel()) {
// If yes, then fix the maximum
          CurMax = TempMax;
          CurMaxBar = TempMaxBar;

El uso del par if - else significa que el Low de la barra que contiene TempMax no sea considerado. Las situaciones en que este precio es inferior al siguiente mínimo fijado pueden percibirse como errores al dibujar el zig zag. ¿Se trata de un error?

Teniendo en cuenta que el trabajo sobre el historial y en tiempo real deben ser idénticos, el autor es de la opinión de que esto no es un error. De hecho, dentro de un periodo de tiempo nunca sabremos qué ocurrió antes en el historial, el máximo o el mínimo de la barra. El uso aquí de la construcción if-else significa tomar una decisión consciente: Preferimos el momento. Significa que sacrificamos el mínimo para un segmento de arriba y el máximo para un segmento de abajo. Es razonable pensar que cuanto más pequeño es el periodo de tiempo con menor frecuencia aparecerá este dilema.

Otro fragmento necesita algunos comentarios:

// Correct the corresponding variables
          UpZ = false;
          TempMin = Low[pos];
          TempMinBar = Bars-pos;  // Here switching to direct numbering
        }
      }

Aquí, de hecho, se establece el punto de inicio de la comprobación del mínimo de tiempo para el momento de cambiar entre segmentos. Es justificable para el ejemplo considerado, pero, en general, no debemos hacerlo. Sería más razonable establecer el mínimo temporal para el intervalo mínimo desde el máximo fijo hasta la posición actual (es decir, el momento del cambio). El código puede ser el siguiente:

   // Correct the corresponding variables
          UpZ = false;
          TempMinBar = CurMaxBar+1;
          TempExtPos = Bars - TempMinBar;  // Here switching to reverse numbering
          TempMin = Low[TempExtPos];
          for (i=TempExtPos-1;i>=pos;i--) {
            if (Low[i]<TempMin) {
              TempMin = Low[i];
              TempMinBar = Bars-i;  // Here switching to direct numbering
            }
          }

Aquí, el Low de la barre donde se ha fijado el mínimo se excluye de nuevo de la consideración.

Estas dos notas también tienen que ver con el procesamiento de los segmentos de abajo.

Indicador

Solo queda completar el indicador para que funcione. No es necesario comentar en init() y deinit(), todo está bastante claro y estándar aquí. No obstante, tomaremos una importante decisión sobre la función start(). Trabajaremos solo con barras completas. La razón principal de ello es que nos permite lograr una estructura del código sencilla y compacta.

Hay otra importante consideración sobre ello. Un trabajo serio en un sistema de trading implica recopilar las estadísticas del historial. Estas estadísticas serán válidas (correctas) solo si las características obtenidas en tiempo real se corresponden por completo con las obtenidas en el historial. No tenemos un historial de los ticks reales, por lo que solo podemos lograr esa correspondencia completa trabajando en tiempo real solo con barras completas. Lo máximo que podemos hacer para reducir el retraso es usar periodos de tiempo más pequeños, hasta M1.

Otra característica esencial es no usar la función IndicatorCounted(). La razón principal para ello es que el código usado necesita otra acción importante: la inicialización de las variables de estado del indicador. Esto no puede hacerse en la función init() ya que usar una numeración directa requiere recalcular el indicador en el llenado del historial, por tanto, reinicializar las variables de estado. La función init() no se ejecuta en el llenado del historial.

De este modo, tenemos que añadir una función "estándar" más, Reset(). Finalmente, el deseo de no usar IndicatorCounted() demasiado ayuda e impide organizar la comprobación del recálculo necesaria para un indicador de este tipo. Esta comprobación se realiza de la forma siguiente:

int start() {
//  Work with completed bars only
  if (Bars == PreBars) return(0);  
//  Check whether there are enough bars on the chart
  if (Bars < MinBars) {
    Alert(": Not enough bars on the chart");
    return(0);
  }  
//  If the history was not pumped, make calculations for the bar just completed
  if (Bars-PreBars == 1 && BarTime==Time[1]) StartPos = 1;
//  Otherwise, count the number of bars specified in function Reset() 
  else StartPos = Reset();
// Modify check variables
  PreBars = Bars;  
  BarTime=Time[0];
// Cycle on history
  for (pos=StartPos;pos>0;pos--) {

La función Reset() es la siguiente:

int Reset() {
  if (MinBars == 0) MinBars = Bars-1;
  StartPos = MinBars;
  PreBars = 0;
  BarTime = 0;
  dH = H*Point;
  UpZ = true;
  TempMaxBar = Bars-StartPos;
  TempMinBar = Bars-StartPos;
  TempMax = High[StartPos];
  TempMin = Low[StartPos];
  StartPos++;
  return(StartPos);
}

Aquí podríamos prestar especial atención a la variable adicional, dH, a la que de una vez por todas asignamos el valor umbral de cambio del ZigZag (H ) transformado en la escala del precio. Puede surgir una pregunta: ¿Por qué es UpZ = truey no false ? La respuesta es simple: Después de un pequeño número de segmentos, el indicador dará como resultado el mismo gráfico, con independencia del valor inicial de UpZ.

Bueno, por último, los cálculos del nivel de ralentización:

double SwitchLevel() {
  double SwLvl;
  if (UpZ) SwLvl = TempMax - dH;
  else SwLvl = TempMin + dH;
  return(SwLvl);
}

Aquí debe quedar todo claro.

Conclusión

Se adjunta al artículo una plantilla para escribir ZigZags, ZZTemplate. Todo lo que tenemos que hacer es añadir el código necesario a la función SwitchLevel(). Para convertir la plantilla en el zig zag usado aquí como ejemplo, solo tenemos que encontrar las líneas siguientes y comentar en ellas:

//extern int H = 33;
//double dH;
//  dH = H*Point;

//  if (UpZ) SwLvl = TempMax - dH;
//  else SwLvl = TempMin + dH;


El código final se refiere a la velocidad del zig zag. La plantilla implica generalización. Además, queremos tener una estructura lo más transparente posible. Creo que pueden optimizarse construcciones más específicas para mejorar su funcionamiento.

La recomendación general es la siguiente: Cuando sea posible, colocamos las operaciones en operadores if. Como ejemplo (pero no como modelo ideal) de optimización, adjunto encontrará el indicador HZZ, una alternativa al zig zag usado en este artículo. La simplicidad del problema nos permite abandonar la función SwitchLevel() y algunas variables de estado. Como pequeño regalo, he añadido a HZZ picos de ZigZag al archivo y una comprobación "sobre la marcha" de algunas características estadísticas del ZigZag.

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

Archivos adjuntos |
HZZ.mq4 (4.22 KB)
ZZTemplate.mq4 (5.67 KB)
La inacción es el estímulo para el progreso o cómo trabajar con gráficos de forma interactiva La inacción es el estímulo para el progreso o cómo trabajar con gráficos de forma interactiva
Un indicador para el trabajo interactivo con líneas de tendencia, niveles Fibo e iconos impuestos manualmente en un gráfico. Nos permite dibujar las zonas coloreadas de niveles Fibo, muestra los momentos de cruce del precio sobre la línea de tendencia y gestiona el objeto "Price label" (etiqueta del precio).
Repaso de HTML usando MQL4 Repaso de HTML usando MQL4
Hoy d&iacute;a, HTML es uno de los tipos de documentos m&aacute;s utilizados. El terminal de cliente de MetaTrader 4 nos permite guardar las declaraciones, informes de pruebas y optimizaciones como archivos .html. A veces es necesario para obtener informaci&oacute;n de dichos archivos en un programa MQL4. El art&iacute;culo describe una de las variantes de c&oacute;mo obtener la estructura y contenidos de HTML.
Método para encontrar los errores en el código mediante comentarios Método para encontrar los errores en el código mediante comentarios
El artículo describe un método de búsqueda de errores en el código de MQL4 basado en los comentarios. Este método ha mostrado ser útil en caso de que problemas originados durante la compilación y provocados por los errores en un código razonablemente grande.
Operaciones con grupos de archivos Operaciones con grupos de archivos
Algunas veces es necesario realizar las mismas operaciones con un grupo de archivos. Si tenemos una lista de archivos en un grupo, esto no es un problema. Sin embargo, si necesitamos hacer esta lista nosotros mismos, surge la pregunta: &quot;¿C&oacute;mo podemos hacerlo?&quot; El art&iacute;culo propone hacerlo mediante las funciones FindFirstFile() y FindNextFile() incluidas en kernel32.dll.