Tendencia universal con interfaz gráfica

Dmitry Fedoseev | 20 abril, 2017


Contenido

Introducción

En dos artículos anteriores de esta serie, fue descrito el desarrollo del oscilador universal con interfaz gráfica, y luego, del canal universal a su base.  

El indicador universal de tendencia también puede ser creado a base del oscilador universal. Para evitar la repetición, usaremos un enfoque un poco diferente. Tal vez, a alguien le guste más.

En el oscilador universal y el canal universal, todos los objetos de los controles se creaban en la clase del formulario a través de los punteros automáticos, mientras que de la gestión de su apariencia y de la posición se encargaba una clase adicional. Ahora vamos a crear un control compuesto universal siguiendo los principios descritos en la serie de artículos «Controles gráficos personalizados» (artículo 1, artículo 2, artículo 3). Sin embargo, serán creados sólo los métodos que son necesarios para la solución de las tareas de este artículo en cuestión. 

Tipos de indicadores

No vamos a limitarnos a los indicadores que pertenecen tradicionalmente al tipo tendencial. Cualquier indicador que depende de la dirección del movimiento del precio (sea una media móvil, sea un oscilador RSI) puede aplicarse tanto para determinar el momento de la entrada, como para determinar la tendencia. Por ejemplo, si el precio está por encima de la media móvil, se puede considerar que hay una tendencia ascendiente, y viceversa (fig. 1).


Fig. 1. Uso de la media móvil y del precio para determinar la tendencia. Las flechas azules  hacia arriba muestran los intervalos cuando
se puede comprar (precio por encima de la media) , hacia abajo, los intervalos cuando se puede vender (precio por debajo de la media).

De la misma manera, se puede usar la inclinación de la media: inclinación hacia arriba indica en una tendencia alcista, inclinación hacia abajo, bajista (fig. 2).

 
Fig. 2. Uso de inclinación de la media móvil para determinar la tendencia. Las flechas azules  hacia arriba muestran los intervalos cuando 
se puede comprar (la media hacia arriba), las flechas rojas 
hacia abajo muestran los intervalos cuando se puede vender (la media hacia abajo).

Si definimos la barra en la que el precio cruza la media (el precio del cierre se encontraba por debajo de la media en una barra, y en la siguiente estaba por encima), en este caso la media se usa para determinar el momento de la entrada. En este caso, la señal comercial existe durante una barra (fig. 3). 

 
Fig. 3. Uso de la media móvil y del precio para determinar los momentos de la entrada. Las flechas azules hacia arriba muestran las barras
de la compra (el precio estaba por debajo de la media en una barra, en la siguiente, estaba por encima de la media), las flechas rojas 
hacia abajo muestran
las barras de venta 
(el precio estaba por encima de la media en una barra, en la siguiente, estaba por debajo de la media)

También se puede usar la inclinación de la media: las barras en las que la media cambia de dirección son las barras de entrada (fig. 4). 

 
Fig. 4. Uso de la inclinación de la media móvil para determinar los momentos de la entrada. Las flechas azules hacia arriba muestran las barras 
de la compra (extremos inferiores de la media), las flechas rojas hacia abajo, las barras de la venta (extremos superiores)

En este artículo, nos van a interesar las variantes de la determinación de la tendencia (como en la fig. 1 y fig. 2). Las variantes para determinar las barras de la entrada han sido mostradas para que podamos comparar y comprender mejor el asunto.  

Los osciladores suelen utilizarse para determinar el momento de la entrada, en mayoría de los casos, según la intersección del nivel, por ejemplo RSI (fig. 5). Para determinar el momento de la entrada, el oscilador estocástico puede utilizarse por lo menos de dos maneras: por la intersección del nivel y por la intersección de la línea principal y la línea de señal. 

 
Fig. 5. Determinación de las barras de entrada usando el indicador RSI. Las flechas azules muestran las barras de compra (cruzamiento del nivel
70 hacia arriba), las flechas rojas, las barras de venta (cruzamiento del nivel 30 hacia abajo)

Vamos a ver cómo se puede utilizar los osciladores para determinar la tendencia. Para el estocástico, la respuesta es obvia: según la posición de la línea principal y la línea de señal, cuando la línea principal está por encima de la línea de señal, se puede comprar, y cuando está por debajo de la línea de señal, se puede vender (fig. 6).


Fig. 6. Definición de la línea principal y de señal del oscilador estocástico para determinar la tendencia. Con flechas azules se marca
la tendencia alcista (línea principal está por encima de la línea de señal), con flechas rojas, la tendencia bajista (línea principal está por debajo de la línea de señal)

De la misma manera, también se puede usar los niveles: si el oscilador está por encima de un determinado nivel, se puede comprar, si está por debajo, se puede vender (fig. 7).

 
Fig. 7. Uso del oscilador RSI y los niveles para determinar la tendencia. Las flechas azules marcan la tendencia alcista 
(RSI superior a 60), las flechas marcan la tendencia bajista (RSI inferior a 40)
 

Visualización de la tendencia

Vamos a centrarnos en la forma más conveniente para visualizar la tendencia. La primera idea que surge es una línea coloreada que cambia su color en función de la tendencia. Vamos a considerar la definición de la tendencia según el oscilador RSI y los niveles 60 y 40.  Si la línea RSI está por encima del nivel 60, se puede comprar, y si está por debajo de 40se puede vender (fig. 7). Resulta que cuando la línea RSI se encuentra entre los niveles 40 y 60, no se puede comprar ni vender. Ahora vamos a alterar los niveles: el nivel de compra es igual a 40 (los valores más de 40 permiten comprar), el nivel de venta es (los valores menos de 60 permiten vender). En este caso, entre los niveles 40 y 60 se puede tanto comprar como vender (fig. 8).

 
Fig. 8. Uso del oscilador RSI y los niveles para determinar la tendencia. Cuando el valor del nivel de compra es igual a 40 y el de venta es igual a 60,
hay áreas donde se puede comprar y vender (hay dos flechas a la vez).

De eso se desprende que la tendencia debe visualizarse a través de dos series de datos independientes. Vamos a usar dos búferes de indicadores: uno desplazado un poco hacia arriba, otro , un poco hacia abajo (para poder mostrar dos búferes a la vez). Puesto que los búferes van a visualizarse intermitentemente, es mejor usar los histogramas o iconos. Usaremos los iconos. Entonces, aparte del color diferente para los búferes diferentes, se puede usar los iconos diferentes. Este indicador se muestra en la fig. 9.

 
Fig. 9. Fig. 9. Forma conveniente para visualizar la tendencia

Esta manera de visualizar la tendencia permite aumentar el número de indicadores utilizados. Por ejemplo, se puede introducir los indicadores que no dependen de la dirección, y que solamente permiten/prohíben el trading en varios intervalos. Por ejemplo, se puede utilizar el indicador ATR o STD. Si su valor es superior a un determinado nivel, se muestran ambas flechas (arriba y abajo). De esta manera, el indicador universal se hace aún más universal.  

Indicadores utilizados

Antes de empezar a crear el indicador universal de tendencia, definiremos la lista completa de indicadores según los cuales vamos a determinar la tendencia. De esta manera, averiguaremos el conjunto necesario de los parámetros externos (número y tipos de variables). Claro que es poco probable que se pueda meter de golpe todas las posibles variantes en el indicador, es que son demasiadas. Una media media móvil puede usarse como mínimo de dos maneras. Y eso que es posible utilizar dos medias móviles. Por tanto, desde el principio es necesario facilitar al máximo la posibilidad de modificar el indicador.

Algunas variantes del uso de los indicadores pueden ser idénticos, aunque eso puede aparecer no tan evidente. Por ejemplo, el uso de dos medias móviles (rápida y lenta) para determinar la tendencia hace que la variante de la determinación de la tendencia según el precio y la media sea innecesaria. Si para la media rápida se establece el período igual a 1, ella va a corresponder al precio. Por eso, cada indicador tiene que considerarse separadamente. Es más, hace falta considerar directamente cada una de las variantes de la aplicación de cada indicador.

iAC (Accelerator Oscillator)

Este indicador no tiene parámetros, es idéntico al indicador OsMA con determinados parámetros, por eso no va a utilizarse.  

iAD (Accumulation/Distribution)

Es un indicador muy específico. El rango de valores del indicador no está definido, su línea es demasiado quebrada, lo que no vale para determinar la tendencia sin un tratamiento correspondiente (por ejemplo, el suavizado). No va a usarse. 

iADX (Average Directional Index)

Versión 1. Posición de las líneas PDI y MDI. PDI encima de MDI — tendencia a la alza, PDI debajo de MDI — tendencia a la baja.

Parámetros

  1. int adx_period. 

Versión 2. No depende de la dirección. La posición de la línea ADX respecto a un determinado nivel. ADX encima del nivel — compra y venta permitidas. Aparte del parámetro del indicador, será necesario un parámetro adicional para el nivel. 

 Parámetros:

  1. int adx_period — período;
  2. double level — nivel. 

iADXWilder (Average Directional Index by Welles Wilder)

Lo mismo que para el indicador ADX.

iAlligator (Alligator)

El indicador representa tres medias móviles con diferentes períodos y diferentes desplazamientos. Se puede inventar muchas maneras de aplicar este indicador, pero no limitaremos a una variante.

Variante 1. Si la línea rápida (labios) está por encima de la línea media (dientes), la media está por encima de la lenta (mandíbulas), entonces la tendencia es ascendiente; si la posición de las líneas es contraria, la tendencia es descendiente.  

Parámetros

    1. int jaw_period — período de mandíbulas; 
    2. int jaw_shift — desplazamiento de mandíbulas; 
    3. int teeth_period — período de dientes;
    4. int teeth_shift — desplazamiento de dientes; 
    5. int lips_period — período de labios; 
    6. int lips_shift — desplazamiento de labios;
    7. ENUM_MA_METHOD ma_method — tipo de suavizado; 
    8. ENUM_APPLIED_PRICE applied_price — tipo del precio.

iAMA (Adaptive Moving Average)

El indicador representa una media móvil. Vamos a utilizar dos versiones del uso de todas las medias.

Versión 1. Inclinación de la línea. Además de los parámetros estándar del indicador, nos hará falta un parámetro que determine el desplazamiento del segundo punto del indicador respecto al que se verifica la inclinación. 

Parámetros

    1. int ama_period — período de AMA; 
    2. int fast_ma_period — período de la media rápida;
    3. int slow_ma_period — período de la media lenta; 
    4. int ama_shift — desplazamiento del indicador horizontalmente; 
    5. ENUM_APPLIED_PRICE  applied_price — tipo de precio;
    6. int shift2 — desplazamiento del punto respecto al que se verifica la inclinación.

Versión 2. Dos líneas: rápida y lenta. Si la línea rápida está por encima de la lenta, la tendencia es alcista, si la rápida esta por debajo de la lenta, la tendencia es bajista. Necesitaremos dos conjuntos iguales de los parámetros estándar.

Parámetros

    1. int ama_period1 — período de AMA; 
    2. int fast_ma_period1 — período de la media rápida;
    3. int slow_ma_period1 — período de la media lenta; 
    4. int ama_shift1 — desplazamiento del indicador horizontalmente; 
    5. ENUM_APPLIED_PRICE  applied_price1 — tipo del precio;
    6. int ama_period2 — período de AMA; 
    7. int fast_ma_period2 — período de la media rápida;
    8. int slow_ma_period2 — período de la media lenta; 
    9. int ama_shift2 — desplazamiento del indicador horizontalmente; 
    10. ENUM_APPLIED_PRICE  applied_price2 — tipo del precio.
Nota: las variables con el número 1 al final se utilizan para la línea rápida, con 2, para la lenta. 
iAO (Awesome Oscillator)


Este indicador no tiene parámetros, es idéntico al indicador MACD con determinados parámetros, por eso no va a utilizarse.   

iATR (Average True Range)

El indicador no depende de la dirección, se usa una variante.

Variante 1: no depende de la dirección. Posición de la línea ATR respecto al nivel. ATR encima del nivel — compra y venta permitidas. Aparte del parámetro del indicador, será necesario un parámetro adicional para el nivel. 

 Parámetros:

  1. int ma_period — período;
  2. double level — nivel.  

iBearsPower (Bears Power) y iBullsPower (Bulls Power)

Los indicadores BearsPower y BullsPower son asimétricos, es decir, deben usarse en pareja: uno para las señales de compra, otro para las señales de venta. Para determinar la tendencia a la alza, vamos a usar BullsPower, para determinar la tendencia a la baja, usaremos BearsPower. Serán usadas dos variantes: según la posición del indicador respecto al nivel y según la inclinación.

Variante 1. Si BullsPower está por encima del nivel establecido, se permiten las compras, si BearsPower está por debajo del valor negativo del mismo nivel, se permiten las ventas. Aparte de los parámetros estándar, será necesario un parámetro adicional para el nivel.

Parámetros:

    1. int ma_period — período;
    2. double level — nivel.   

Variante 2. Dirección del indicador. Para esta variante será necesario un parámetro adicional que determine el desplazamiento del segundo punto respecto al que se determina la ainclinación.

Parámetros:

    1. int ma_period — período;
    2. int shift2 — desplazamiento del segundo punto.   

iBands (Bollinger Bands)

Este indicador representa un canal.

Variante 1. Si el precio cruza el límite superior hacia arriba, comienza la tendencia alcista; si el precio cruza la línea central hacia abajo, la tendencia se cancela. De la misma forma, este indicador trabaja para otro lado para la línea inferior y la tendencia bajista. 

Parámetros:

    1. int bands_period — período para calcular la línea media;
    2. int bands_shift — desplazamiento del indicador horizontalmente; 
    3. double — número de desviaciones estándar; 
    4. ENUM_APPLIED_PRICE  applied_price — tipo de precio.

iCCI (Commodity Channel Index)

Este indicador representa un oscilador en la subventana. La línea del indicador es bastante quebrada incluso para los períodos grandes, por eso la variante con la inclinación no va a usarse.

Variante 1. Posición respecto al nivel. Si el indicador está por encima del nivel, la tendencia es ascendiente, si está por debajo del valor negativo del mismo nivel, la tendencia es descendiente. 

Parámetros:

    1. int ma_period — período promedio; 
    2. ENUM_APPLIED_PRICE  applied_price — tipo de precio o handle; 
    3. double level — nivel.   

iChaikin (Chaikin Oscillator)

La aplicación de este indicador es semejante a la aplicación del indicador CCI.

Variante 1. Posición respecto al nivel. Si el indicador está por encima del nivel, la tendencia es ascendiente, si está por debajo del valor negativo del mismo nivel, la tendencia es descendiente. 

Parámetros:

    1. int fast_ma_period — período rápido;  
    2. int ow_ma_period — período lento; 
    3. ENUM_MA_METHOD ma_method — tipo de suavizado; 
    4. ENUM_APPLIED_VOLUME  applied_volume — volumen utilizado; 
    5. double level — nivel. 

iDEMA (Double Exponential Moving Average)

Este indicador representa una media móvil y se aplica de la misma forma que el indicador AMA considerado antes.

Variante 1. Inclinación de la línea. 

Parámetros

    1. int ma_period — período promedio;
    2. ENUM_APPLIED_PRICE  applied_price — tipo de precio;
    3. int ma_shift — desplazamiento del indicador horizontalmente; 
    4. int shift2 — desplazamiento del punto respecto al que se verifica la inclinación.

Variante 2. Dos líneas: rápida y lenta.

Parámetros

    1. int ma_period1 — período promedio;
    2. ENUM_APPLIED_PRICE  applied_price1 — tipo del precio;
    3. int ma_shift1 — desplazamiento del indicador horizontalmente; 
    4. int ma_period2 — período promedio;
    5. ENUM_APPLIED_PRICE  applied_price2 — tipo del precio;
    6. int ma_shift2 — desplazamiento del indicador horizontalmente.
Nota: las variables con el número 1 al final se utilizan para la línea rápida, con 2, para la lenta. 

iDeMarker (DeMarker)

Este indicador representa un oscilador en la subventana, que se cambia en el rango de 0 a 1. El nivel neutral del indicador es el nivel 0,5. La línea del indicador quebrada no vale para la variante con la inclinación.

Variante 1. Posición respecto al nivel. El nivel para la venta va a establecerse a través de los parámetros, y el nivel para la compra va a calcularse de forma simétrica respecto al nivel 0,5. 

Parámetros:

  1. int ma_period — período promedio;
  2. double level — nivel de venta.

iEnvelopes (Envelopes)

Este indicador representa un canal, como las bandas de Bollinger, pero se aplica de manera más fácil.

Variante 1. Si el precio está por encima del borde superior del canal, la compra está permitida; si está por debajo del borde inferior, se permite la venta. 

Parámetros:

  1. int ma_period — período para calcular la línea media;
  2. int ma_shift — desplazamiento del indicador horizontalmente; 
  3. ENUM_MA_METHOD ma_method — tipo de suavizado;
  4. ENUM_APPLIED_PRICE applied_price — tipo de precio; 
  5. double deviation — desviación de los bordes desde la línea media.  

iForce (Force Index)

Este indicador representa un oscilador en la subventana que se cambia alrededor del nivel 0. Su aplicación es semejante a la aplicación del indicador CCI.

Variante 1. Posición respecto al nivel. 

Parámetros

  1. int ma_period — período promedio; 
  2. ENUM_MA_METHOD ma_method — tipo de suavizado; 
  3. ENUM_APPLIED_VOLUME applied_volume — tipo de volumen para el cálculo;
  4. double level — nivel.  

iFractals (Fractals)

Este indicador no tiene parámetros.

Variante 1. La definición se realiza de acuerdo con el tipo del último fractal. Si el último fractal está encima, se permiten las ventas, si está abajo, las compras. 

iFrAMA (Fractal Adaptive Moving Average)

Este indicador representa una media móvil y se aplica de la misma forma que el indicador AMA. 

Variante 1. Inclinación de la línea. 

Parámetros

  1. int ma_period — período promedio; 
  2. int ma_shift — desplazamiento del indicador horizontalmente; 
  3. ENUM_APPLIED_PRICE  applied_price — tipo de precio;
  4. int shift2 — desplazamiento del punto respecto al que se verifica la inclinación.

Variante 2. Dos líneas: rápida y lenta.

Parámetros

    1. int ma_period1 — período promedio; 
    2. int ma_shift1 — desplazamiento del indicador horizontalmente; 
    3. ENUM_APPLIED_PRICE  applied_price1 — tipo del precio;
    4. int ma_period2 — período promedio; 
    5. int ma_shift2 — desplazamiento del indicador horizontalmente; 
    6. ENUM_APPLIED_PRICE  applied_price2 — tipo del precio.
Nota: las variables con el número 1 al final se utilizan para la línea rápida, con 2, para la lenta. 

iGator (Gator Oscillator)

Este indicador se calcula a base del indicador Alligator. Toda la información que él facilita se puede obtener directamente desde el indicador Alligator, por eso no vamos a utilizarlo . 

iIchimoku (Ichimoku Kinko Hyo)

En cuanto a este indicador, nos puede interesar el par de líneas: TenkanKijun y la nube. En la nube se puede destacar dos líneas que alteran su posición (ya una encima, ya otra). Al final, hay dos variantes del uso de este indicador.

Variante 1. Posición de las líneas Tenkan y Kijun. Si la línea Tenkan (roja) está por encima de la línea Kijun(azul), la tendencia es ascendiente, si Tenkan está por debajo de Kijun, entonces la tendencia es descendiente.

Parámetros: 

    1. int tenkan_sen — período Tenkan-sen; 
    2. int kijun_sen — período Kijun-sen; 
    3. int senkou_span_b — período Senkou Span B.  

Variante 2. Según la dirección de la nube. La nube se define por dos líneas: SpanA y SpanB. Si la línea SpanA está por encima de la línea SpanB, la tendencia es alcista, de lo contrario, es bajista.

Parámetros: 

    1. int tenkan_sen — período Tenkan-sen; 
    2. int kijun_sen — período Kijun-sen; 
    3. int senkou_span_b — período Senkou Span B.  
iBWMFI (Market Facilitation Index by Bill Williams)

No vamos a usar este indicador por causa de su alta especificidad. 

iMomentum (Momentum)

Este indicador representa un oscilador en la subventana. La línea del indicador es muy quebrada, por tanto no conviene para determinar la tendencia según la inclinación. Nos queda sólo la opción según el nivel.

Variante 1. Nivel. Nivel neutral: 100. Aparte de los parámetros estándar del indicador, necesitaremos un parámetro para el nivel de ventas, mientras que el nivel de compras va a calcularse de forma simétrica respecto al nivel 100.

Parámetros:

    1. int mom_period — período del promedio; 
    2. ENUM_APPLIED_PRICE applied_price — tipo del precio o handle;
    3. double level — nivel de venta. 

iMFI (Money Flow Index)

Este indicador representa un oscilador en la subventana, que se cambia de 0 a 100. Nivel neutral: 50. Su línea quebrada no conviene para determinar la tendencia según la dirección de la línea.

Variante 1. Según el nivel. Aparte de los parámetros estándar, necesitaremos un parámetro para determinar el nivel de ventas, mientras que el nivel de compras va a calcularse de forma simétrica respecto al nivel 50. Si la línea del indicador está por encima del nivel de compra, la tendencia es ascendiente, si está por debajo del nivel de venta, la tendencia es descendiente. 

Parámetros: 

    1. int ma_period — período promedio;
    2. ENUM_APPLIED_VOLUME applied_volume — tipo de volumen para el cálculo;
    3. double level — nivel de venta. 

iMA (Moving Average)

Es una media móvil habitual.

Variante 1. Inclinación de la línea.

Parámetros

    1. int ma_period — período promedio; 
    2. int ma_shift — desplazamiento del indicador horizontalmente; 
    3. ENUM_MA_METHOD ma_method — tipo de suavizado; 
    4. ENUM_APPLIED_PRICE applied_price — tipo del precio;
    5. int shift2 — desplazamiento del punto respecto al que se verifica la inclinación.

Variante 2. Dos líneas: rápida y lenta.

Parámetros

  1. int ma_period1 — período promedio; 
  2. int ma_shift1 — desplazamiento del indicador horizontalmente; 
  3. ENUM_MA_METHOD ma_method1 — tipo de suavizado; 
  4. ENUM_APPLIED_PRICE applied_price1 — tipo del precio;
  5. int ma_period2 — período promedio; 
  6. int ma_shift2 — desplazamiento del indicador horizontalmente; 
  7. ENUM_MA_METHOD ma_method2 — tipo de suavizado; 
  8. ENUM_APPLIED_PRICE applied_price2 — tipo del precio;
Nota: las variables con el número 1 al final se utilizan para la línea rápida, con 2, para la lenta. 


iOsMA (Moving Average of Oscillator (MACD histogram)

El oscilador OsMA representa un histograma en la subventana. Puede haber dos variantes: según el nivel y según la dirección.

Variante 1. Según el nivel. Si el histograma está por encima del nivel, se permiten las compras, si está por debajo del valor negativo del mismo nivel, se permiten las ventas.

Parámetros: 

  1. int fast_ema_period — período de la media rápida; 
  2. int slow_ema_period — período de la media lenta; 
  3. int signal_period — período promedio de la diferencia;
  4. ENUM_APPLIED_PRICE applied_price — tipo de precio o handle; 
  5. double level — nivel.

Variante 2. Según la dirección. Si el histograma indica hacia arriba, se permiten las compras, si indica hacia abajo, las ventas. Nos hará falta un parámetro adicional para desplazar el segundo punto, respecto al que se verifica la dirección. 

Parámetros:

  1. int fast_ema_period — período de la media rápida; 
  2. int slow_ema_period — período de la media lenta; 
  3. int signal_period — período promedio de la diferencia;
  4. ENUM_APPLIED_PRICE applied_price — tipo de precio o handle; 
  5. int shift2 — desplazamiento del segundo punto para determinar la dirección.  

iMACD (Moving Averages Convergence-Divergence)

El indicador MACD visualiza el histograma y la línea de señal en la subventana. Se puede usar este indicador de tres maneras diferentes: según el nivel del histograma, según su dirección y según su posición respecto a la línea de señal. Sin embargo, lo último es un caso particular de la verificación del indicador OsMA según el nivel (con el valor del nivel 0), por tanto no se va a usar. 

Variante 1. Según el nivel. Si el histograma está por encima del nivel, se permiten las compras, si está por debajo del valor negativo del mismo nivel, se permiten las ventas.

Parámetros: 

  1. int fast_ema_period — período de la media rápida; 
  2. int slow_ema_period — período de la media lenta; 
  3. int signal_period — período promedio de la diferencia;
  4. ENUM_APPLIED_PRICE applied_price — tipo de precio o handle; 
  5. double level — nivel.

Variante 2. Según la dirección. Si el histograma indica hacia arriba, se permiten las compras, si indica hacia abajo, las ventas. Nos hará falta un parámetro adicional para desplazar el segundo punto, respecto al que se verifica la dirección. 

Parámetros:

  1. int fast_ema_period — período de la media rápida; 
  2. int slow_ema_period — período de la media lenta; 
  3. int signal_period — período promedio de la diferencia;
  4. ENUM_APPLIED_PRICE applied_price — tipo de precio o handle; 
  5. int shift2 — desplazamiento del segundo punto para determinar la dirección.  

iOBV (On Balance Volume)

Este indicador, igual que el indicador AD, no tiene un rango de valores determinado y está muy quebrado. No se va a usar. 

iSAR (Parabolic Stop And Reverse System)

Este indicador se visualiza en el gráfico de precios, dibuja una línea compuesta de los puntos. Dependiendo de la dirección del movimiento del precio, la línea se ubica encima o debajo del precio.

Variante 1. Posición respecto al precio. El precio está por encima de la línea del indicador, la tendencia es ascendiente, el precio está por debajo de la línea del indicador, la tendencia es descendiente. 

Parámetros:

  1. double step — paso del cambio del precio — coeficiente de la aceleración;
  2. double maximum — paso máximo. 

iRSI (Relative Strength Index)

Este indicador se ubica en la subventana. Hay una variante de su aplicación que parece a la aplicación del indicador MFI.

Variante 1. Según el nivel.

Parámetros: 

  1. int ma_period — período promedio; 
  2. ENUM_APPLIED_PRICE  price — tipo del precio;
  3. double level — nivel de venta. 

iRVI (Relative Vigor Index)

El oscilador se ubica en la subventana con la línea de señal. El valor se altera alrededor de cero.

Variante 1. Según el nivel. Si está por encima del nivel, se permite comprar, si está por debajo del valor negativo del nivel, se permite vender.

Parámetros:

    1. int ma_period — período promedio;
    2. double level — nivel.  

Variante 2. Línea principal y línea de señal.

Parámetros:

    1. int ma_period — período promedio. 

iStdDev (Standard Deviation)

Este indicador no depende de la dirección. Parece al indicador ATR.

Variante 1. Según el nivel. 

Parámetros: 

    1. int ma_period — período promedio; 
    2. int ma_shift — desplazamiento del indicador horizontalmente; 
    3. ENUM_MA_METHOD ma_method — tipo de suavizado; 
    4. ENUM_APPLIED_PRICE applied_price — tipo del precio;
    5. double level — nivel.  

iStochastic (Stochastic Oscillator)

El oscilador se ubica en la subventana con la línea principal y la línea de señal.

Variante 1. Inclinación de la línea principal. 

Parámetros:

  1. int Kperiod — período K;
  2. int Dperiod — período D;
  3. int slowing — suavizado definitivo; 
  4. ENUM_MA_METHOD ma_method — tipo de suavizado; 
  5. ENUM_STO_PRICE price_field — precio;
  6. int shift2 — desplazamiento del segundo punto para determinar la inclinación.  

Variante 2. Según el nivel. El valor del indicador se cambia de 0 a 100, el nivel neutral es 50. Se necesita un parámetro adicional para el nivel de venta, el nivel de compra va a calcularse. 

Parámetros:

  1. int Kperiod — período K;
  2. int Dperiod —  período D;
  3. int slowing — suavizado definitivo; 
  4. ENUM_MA_METHOD ma_method — tipo de suavizado; 
  5. ENUM_STO_PRICE price_field — precio;
  6. double level — nivel.

Variante 3. Posición de la línea principal y línea de señal.

Parámetros:

  1. int Kperiod — período K;
  2. int Dperiod — período D;
  3. int slowing — suavizado definitivo; 
  4. ENUM_MA_METHOD ma_method — tipo de suavizado; 
  5. ENUM_STO_PRICE price_field — precio.

iTEMA (Triple Exponential Moving Average)

Es otra media móvil.

Variante 1. Inclinación de la línea.

Parámetros

  1. int ma_period — período promedio; 
  2. int ma_shift — desplazamiento del indicador horizontalmente; 
  3. ENUM_APPLIED_PRICE applied_price — tipo del precio;
  4. int shift2 — desplazamiento del punto respecto al que se verifica la inclinación.

Variante 2. Dos líneas: rápida y lenta.

Parámetros

  1. int ma_period1 — período promedio; 
  2. int ma_shift1 — desplazamiento del indicador horizontalmente; 
  3. ENUM_APPLIED_PRICE applied_price1 — tipo del precio;
  4. int ma_period2 — período promedio; 
  5. int ma_shift2 — desplazamiento del indicador horizontalmente; 
  6. ENUM_APPLIED_PRICE applied_price2 — tipo del precio;
Nota. Las variables con el número 1 al final se utilizan para la línea rápida, con 2, para la lenta. 

iTriX (Triple Exponential Moving Averages Oscillator)

Este indicador se ubica en la subventana. Una variante es la inclinación de la línea, y para variar, como segunda opción probaremos la variante con dos líneas: rápida y lenta. 

Variante 1. Inclinación de la línea.

Parámetros

  1. int ma_period — período promedio; 
  2. int ma_shift — desplazamiento del indicador horizontalmente; 
  3. ENUM_APPLIED_PRICE applied_price — tipo del precio;
  4. int shift2 — desplazamiento del punto respecto al que se verifica la inclinación.
Nota: la función iTriX no tiene el parámetro ma_shift, pero aun así se puede desplazar el indicador. En cualquier caso, el desplazamiento no va a ejecutarse a través del parámetro shift (será definido como 0 para todos los indicadores), sino a través del cálculo del índice de la barra.

Variante 2. Dos líneas: rápida y lenta.

Parámetros

  1. int ma_period1 — período promedio; 
  2. int ma_shift1 — desplazamiento del indicador horizontalmente; 
  3. ENUM_APPLIED_PRICE applied_price1 — tipo del precio;
  4. int ma_period2 — período promedio; 
  5. int ma_shift2 — desplazamiento del indicador horizontalmente; 
  6. ENUM_APPLIED_PRICE applied_price2 — tipo del precio;
Nota. Las variables con el número 1 al final se utilizan para la línea rápida, con 2, para la lenta. 

iWPR (Williams' Percent Range)

El indicador es idéntico a la línea principal del estocástico durante la ralentización 1, pero con la escala invertida. No se va a usar.

iVIDyA (Variable Index Dynamic Average)

Es otra media móvil.

Variante 1. Inclinación de la línea.

Parámetros

  1. int cmo_period — período CMO 
  2. ema_period — período de suavizado 
  3. ma_shift — desplazamiento del indicador horizontalmente 
  4. ENUM_APPLIED_PRICE applied_price — tipo del precio;
  5. int shift2 — desplazamiento del punto respecto al que se verifica la inclinación.
Variante 2. Dos líneas: rápida y lenta.

Parámetros

  1. int ma_period1 — período promedio; 
  2. int ma_shift1 — desplazamiento del indicador horizontalmente; 
  3. ENUM_APPLIED_PRICE applied_price1 — tipo del precio;
  4. int ma_period2 — período promedio; 
  5. int ma_shift2 — desplazamiento del indicador horizontalmente; 
  6. ENUM_APPLIED_PRICE applied_price2 — tipo del precio;
Nota: las variables con el número 1 al final se utilizan para la línea rápida, con 2, para la lenta. 

iVolumes (Volumes)

Las indicaciones del indicador no dependen de la dirección del movimiento del precio. Este indicador se puede utilizar como el indicador ATR o STD.

Variante 1. Nivel. Si el valor del indicador está por encima del nivel, se permite comprar y vender. 

Parámetros:

  1. ENUM_APPLIED_VOLUME  applied_volume — tipo de volumen;
  2. double level — nivel.  

Una vez definidos los indicadores y los modos de su uso, escribiremos la enumeración de todos sus tipos. En la carpeta Includes se crea la carpeta UniTrend, y dentro de ella se crea el archivo UniTrendDefines.mqh con la enumeración: 

enum EType{    Type_ADX_PDIMDI,          // posición de las líneas PDI y MDI del indicador ADX    Type_ADX_Level,           // posición de las líneas ADX respecto al nivel    Type_ADXW_PDIMDI,         // posición de las líneas PDI y MDI del indicador ADX Wilder    Type_ADXW_Level,          // posición de las líneas ADX respecto al nivel del ADX Wilder    Type_Alligator,           // Alligator    Type_AMA_Dir,             // dirección de АМА          Type_AMA_2MA,             // dos АМА    Type_ATR_Level,           // ATR    Type_BuBe_Level,          // Bulls/Bears Power y nivel    Type_BuBe_Dir,            // dirección de Bulls/Bears Power    Type_Bands,               // bandas de Bollinger      Type_CCI_Level,           // CCI y nivel    Type_Chaikin_Level,       // oscilador de Chaikin y nivel    Type_DEMA_Dir,            // dirección de DEMA        Type_DEMA_2MA,            // dos DEMA        Type_DeMarker_Level,      // DeMarker y nivel    Type_Envelopes,           // Envoltura    Type_Force_Level,         // oscilador Force y nivel    Type_Fractals,            // fractales    Type_FrAMA_Dir,           // dirección de FrAMA        Type_FrAMA_2MA,           // dos FrAMA    Type_Ichimoku_TK,         // Ichimoku: Tenkan y Kijun    Type_Ichimoku_SASB,       // Ichimoku: nube          Type_Momentum_Level,      // Momentum y nivel    Type_MFI_Level,           // MFI y nivel    Type_MA_Dir,              // dirección de МА          Type_MA_2MA,              // dos MA    Type_OsMA_Dir,            // dirección de OsMA    Type_OsMA_Level,          // OsMA y nivel    Type_MACD_Dir,            // dirección de MACD    Type_MACD_Level,          // MACD y nivel    Type_SAR,                 // SAR    Type_RSI_Level,           // RSI y nivel    Type_RVI_Level,           // RVI y nivel    Type_RVI_MS,              // línea principal y línea de señal de RVI    Type_STD_Level,           // desviación estándar y nivel    Type_Sto_Dir,             // dirección de estocástico    Type_Sto_Level,           // estocástico y nivel    Type_Sto_MS,              // línea principal y línea de señal del estocástico            Type_TEMA_Dir,            // dirección de TEMA    Type_TEMA_2MA,            // dos TEMA    Type_TriX_Dir,            // dirección de TriX    Type_TriX_2MA,            // dos TriX    Type_VIDyA_Dir,           // dirección de VIDyA    Type_VIDyA_2MA,           // dos VIDyA    Type_Volumes              // volumen        

};

Parámetros externos

Una vez analizadas las descripciones arriba mencionadas, se puede definir el conjunto necesario de parámetros externos. En la tabla 1 se muestran todos los parámetros necesarios y sus tipos. Básicamente, van a usarse las variables con el prefijo "f_" (del inglés fast — rápido). Para las variantes con la media móvil rápida y lenta se utiliza el segundo grupo de parámetros con el prefijo "s" (del inglés slow — lento).

Tabla 1. Parámetros externos y sus tipos 

Tipo Nombre
int  f_period1
int  f_shift1
int  f_period2
int  f_shift2
int  f_period3
int  f_shift3
ENUM_MA_METHOD  f_method
ENUM_APPLIED_PRICE  f_price
ENUM_APPLIED_VOLUME   f_volume
ENUM_STO_PRICE  f_sto_price
double  f_level
int  f_dot2shift
double  f_step 
double  f_maximum 
int  s_period1
int  s_shift1
int  s_period2
int  s_shift2
int  s_period3
int 
s_shift3
ENUM_MA_METHOD  s_method
ENUM_APPLIED_PRICE  s_price
int mult 
int  level_digits 
int  sar_step_digits 
int  sar_maximum_digits 

Además de los parámetros listados en las descripciones de los indicadores, en la parte inferior de la tabla se encuentran varios parámetros adicionales: 

  • mult — coeficiente de multiplicación de parámetros medidos en puntos, dependiendo del número de caracteres y cotizaciones. Los valores de algunos indicadores, por ejemplo MACD y OsMA, tienen una dimensionalidad del precio, por eso será más conveniente establecer su nivel en puntos. Por consiguiente, será más cómodo ajustar los valores de los parámetros en función del número de caracteres en las cotizaciones.
  • level_digits — número de dígitos después de la coma para el parámetro del nivel. En la interfaz gráfica, para establecer el valor del nivel, se utiliza el control CSpinBox (campo de edición con botones "+" y "-"), por eso para diferentes indicadores sería muy conveniente establecer un cambio mínimo diferente (la cantidad en la que se cambia el valor cuando se pulsa en el botón "+" o "-").
  • sar_step_digits — número de dígitos después de la coma para el parámetro step del indicador SAR.
  • sar_maximum_digits — número de dígitos después de la coma para el parámetro maximum del indicador SAR.
Es complicado definir de entrada los valores correspondientes para los parámetros level_digits, sar_step_digitssar_maximum_digits, por eso añadimos las constantes al archivo UniTrendDefines.mqh , luego se puede ajustar fácilmente sus valores:
#define ADX_LEVEL_DIGITS         0 // para el indicador ADX
#define ADXW_LEVEL_DIGITS        0 // para el indicador ADX Wilder
#define ATR_LEVEL_DIGITS         1 // para el indicador ATR
#define BUBE_LEVELS_DIGITS       1 // para el indicadores Bulls/Bears Power
#define CCI_LEVEL_DIGITS         0 // para el indicador CCI 
#define CHAIKIN_LEVEL_DIGITS     0 // para el oscilador Chaikin
#define DEMARKER_LEVEL_DIGITS    2 // para el indicador Demarker  
#define FORCE_LEVEL_DIGITS       3 // para el indicador Force 
#define MOMENTUM_LEVEL_DIGITS    2 // para el indicador Momentum 
#define MFI_LEVEL_DIGITS         0 // para el indicador 
#define OSMA_LEVBEL_DIGITS       2 // para el indicador OsMA
#define MACD_LEVEL_DIGITS        2 // para el indicador MACD
#define RSI_LEVEL_DIGITS         0 // para el indicador RSI
#define RVI_LEVEL_DIGITS         2 // para el indicador RVI
#define STD_LEVEL_DIGITS         1 // para el indicador STD
#define STO_LEVEL_DIGITS         0 // para el indicador Stochastic
#define BANDS_LEVEL_DIGITS       1 // para las bandas de Bollinger
#define ENVELOPES_LEVEL_DIGITS   2 // para el indicador Envelopes
#define SAR_STEP_DIGITS          3 // para el indicador SAR (parámetro step)
      #define SAR_MAXIMUM_DIGITS       2 // para el indicador SAR (parámetro maximum)
Para los indicadores ATR, Bulls/Bears, OsMA, MACD, STD el valor del nivel va a establecerse en puntos. 

A base de la tabla 1, crearemos la estructura:

struct SExtParams{
   int                 f_period1;
   int                 f_shift1;
   int                 f_period2;
   int                 f_shift2;
   int                 f_period3;
   int                 f_shift3;
   long                f_method;
   long                f_price;
   long                f_volume;
   long                f_sto_price;
   double              f_level;
   int                 f_dot2shift;
   double              f_step;  
   double              f_maximum;  
   int                 s_period1;
   int                 s_shift1;
   int                 s_period2;
   int                 s_shift2;
   int                 s_period3;
   int                 s_shift3;
   long                s_method;
   long                s_price;
   int                 mult;
   int                 level_digits;
   int                 sar_step_digits;
   int                 sar_maximum_digits;      
      };

La combinación de los parámetros en una estructura permitirá separarlos del resto del código, lo que será muy conveniente a la hora de modificar el indicador durante la adición de nuevos tipos de detección de tendencia, si los parámetros disponibles son insuficientes. Eso también simplifica considerablemente la transferencia de parámetros en las funciones y métodos de las clases.

Puesto que el indicador creado en este artículo va a mostrar la tendencia como una serie de iconos (eso es igual para todos los indicadores), no será difícil añadir al indicador una función del aviso sobre el comienzo de la tendencia. A algunos les puede interesar sólo las notificaciones sobre una tendencia formada (indicaciones en una barra formada), a otros les interesa el primer momento del surgimiento de una tendencia nueva (en una barra en formación). Por eso vamos a escribir las enumeraciones para seleccionar el tipo de la notificación:

enum EAlerts{
   Alerts_off=0,   // alerta desactivada
   Alerts_Bar0=1,  // notificación sobre la barra en formación
   Alerts_Bar1=2   // notificación sobre la barra formada
      }; 

Creación del indicador

La creación de las clases para los indicadores universales fue considerada en detalle en los artículos "Oscilador universal con interfaz gráfica" y "Canal universal con interfaz gráfica". Vamos a ver las particularidades de su creación en relación a este caso.

Todos los métodos arriba mencionados para determinar la tendencia se dividen en dos categorías: con uno o dos indicadores. En un grupo, después de cargar el indicador, es necesario verificar un manejador (handle), en otro grupo, dos manejadores. Entonces, la clase base va a tener dos clases derivadas con formas diferente para verificar los handles. En su lugar, estas subclases van a tener sus propias clases derivadas donde va a determinarse la tendencia.

Clase base:

class CUniTrend{
   protected:
  
      int m_handle1;
      int m_handle2;
      
      string m_name;
      string m_help;
      
      int m_ci;
      
      double m_b1[1];
      double m_b2[1];      
      double m_b3[1];    
      double m_b4[1];
            
      int m_shift;
      int m_shift1;    
      int m_shift2;
      int m_shift3;
      
      int m_dot2shift;
      
      double m_level;      
      
   public:
  
      void CUniTrend(){
         m_handle1=INVALID_HANDLE;
         m_handle2=INVALID_HANDLE;
      }
      
      void ~CUniTrend(){
         if(m_handle1!=INVALID_HANDLE){
            IndicatorRelease(m_handle1);
         }
         if(m_handle2!=INVALID_HANDLE){
            IndicatorRelease(m_handle2);
         }        
      }
  
      virtual int Calculate( const int rates_total,
                     const int prev_calculated,
                     double & upBuffer[],
                     double & dnBuffer[]
      ){
         return(rates_total);
      }
      
      virtual bool Calculated(){
         return(false);
      }
      
      virtual bool CheckHandles(){
         return(true);
      }      
      
      string Name(){
         return(m_name);
      }    
      string Help(){
         return(m_help);
      }
      };

La sección protected tiene declaradas varias variables auxiliares que serán útiles en las clases derivadas. En el constructor se inicializan las variables para los handles de indicadores, mientras que en el destructor se realiza la liberación de los handles. Los demás métodos son virtuales.

La clase derivada para la variante con un indicador:

class CUniTrend1:public CUniTrend{
   public:
      bool Calculated(){
         if(BarsCalculated(m_handle1)>0){
            return(true);
         }  
         else{
            return(false);
         }    
      }
      
      bool CheckHandles(){
         return(m_handle1!=INVALID_HANDLE);
      }
      };

En la clase hay dos métodos reales: CheckHandle() — para verificar si el indicador ha sido cargado con éxito, y Calculated() — para saber si el indicador ha sido calculado completamente y si se puede actualizar el contenido de los búferes que reflejan la tendencia.

La clase derivada para la variante con dos indicadores:

class CUniTrend2:public CUniTrend{
   public:
      bool Calculated(){
         if(BarsCalculated(m_handle1)>0 && BarsCalculated(m_handle2)>0){
            return(true);
         }  
         else{
            return(false);
         }    
      }      
      
      bool CheckHandles(){
         return(m_handle1!=INVALID_HANDLE && m_handle2!=INVALID_HANDLE);
      }
      };

Todas las clases con diferentes variantes de determinación de la tendencia serán las clases derivadas en relación a la clase CUniTrend1 o CUniTrend2. Vamos a analizar una clase derivada:

class CUniTrend_ADX_PDIMDI:public CUniTrend1{
   private:  
   public:
  
      void CUniTrend_ADX_PDIMDI( bool use_default,
                                 bool keep_previous,
                                 SExtParams & par){
        
         // establecimiento de parámetros por defecto
        
         if(use_default){
            if(keep_previous){
               if(par.f_period1==PARAMETER_EMPTY)par.f_period1=14;
            }
            else{
               par.f_period1=14;
            }      
         }          
         // carga del indicador
         m_handle1=iADX(Symbol(),Period(),par.f_period1);
         // formación del nombre del indicador y de la línea con la ayuda sobre los parámetros
         m_name=StringFormat( "iADX_PDIMDI(%i)",
                              par.f_period1
                            );
  
         m_help=StringFormat( "adx_period - f_period1(%i)",
                              par.f_period1
                            );
      }
      
      int Calculate( const int rates_total,
                     const int prev_calculated,
                     double & upBuffer[],
                     double & dnBuffer[]
      ){
         int start;
        
         if(prev_calculated==0){
            start=1;
         }
         else{
            start=prev_calculated-1;
         }
      
         for(int i=start;i<rates_total;i++){
        
            upBuffer[i]=EMPTY_VALUE;        
            dnBuffer[i]=EMPTY_VALUE;
        
            m_ci=rates_total-i-1;
            
            if(CopyBuffer(m_handle1,PLUSDI_LINE,m_ci,1,m_b1)==-1){
               return(0);
            }
            
            if(CopyBuffer(m_handle1,MINUSDI_LINE,m_ci,1,m_b2)==-1){
               return(0);
            }
            
            if(m_b1[0]>m_b2[0]){
               upBuffer[i]=1;
            }
            else if(m_b1[0]<m_b2[0]){
               dnBuffer[i]=-1;            
            }
            
         }      
      
         return(rates_total);
      }
      };

En el constructor de la clase, los lugares principales de importancia están comentados, el método Calculate() es semejante a la función estándar OnCalculate() del indicador, la creación del código para esta función es la misma que la creación del código del indicador.

En el apéndice del artículo, todas las clases de los indicadores se encuentran en el archivo Include/UniTrend/UniTrendIndicators.mqh.

Ahora que tenemos todas las clases, podemos crear un indicador simple para determinar la tendencia igual como lo hemos hecho en los artículos sobre el oscilador y canal universal. El indicador hecho sin interfaz gráfica se encuentra en el archivo adjunto Indicators/iUniTrend.mq5.

Creación de la interfaz gráfica

Todas las clases de la iterfaz gráfica se ubican en los archivos UniTrendForm.mqh y UniTrendControl.mqh. En el archivo UniTrendForm.mqh se encuentra la clase del formulario, y en el archivo UniTrendControl.mqh se ubican las clases del control universal para la introducción de los parámetros del indicador. No tiene sentido considerar en detalle la creación de la clase del formulario, ya que lo hemos hecho en los artículos sobre la creación del oscilador y canal universal y en en el artículo "Controles gráficos personalizados. Parte 3. Formularios". Vamos a considerar la creación del control universal. 

La base del control universal es la clase base CUniTrendControl. En la sección public de la clase se encuentran sólo los métodos virtuales, en la sección protected se ubican algunos métodos auxiliares para el trabajo con las listas desplegables: métodos para rellenar las listas con opciones y métodos para establecer los puntos seleccionados para las listas. A continuación, se muestra el código de la clase base con comentarios:

class CUniTrendControl{
   protected:
      
      /* función del cálculo del cambio mínimo del valor según
         el número de dígitos decimales para el parámetro del nivel
      */
      double SolveChange(int d){
         return(NormalizeDouble(1.0/pow(10,d),d));  
      }
      
      // llenado de la lista con opciones ENUM_MA_METHOD
      void AddVariantsMethod(CComBox & cb){
         for(int i=0;i<ArraySize(e_method);i++){
            cb.AddItem(EnumToString((ENUM_MA_METHOD)e_method[i]));
         }
      }
      
      // llenado de la lista con opciones ENUM_APPLIED_PRICE
      void AddVariantsPrice(CComBox & cb){
         for(int i=0;i<ArraySize(e_price);i++){
            cb.AddItem(EnumToString((ENUM_APPLIED_PRICE)e_price[i]));
         }
      }      
      
      // llenado de la lista con opciones ENUM_APPLIED_VOLUME
      void AddVariantsVolume(CComBox & cb){
         for(int i=0;i<ArraySize(e_volume);i++){
            cb.AddItem(EnumToString((ENUM_APPLIED_VOLUME)e_volume[i]));
         }
      }  
      
      // llenado de la lista con opciones ENUM_STO_PRICE     
      void AddVariantsStoPrice(CComBox & cb){
         for(int i=0;i<ArraySize(e_sto_price);i++){
            cb.AddItem(EnumToString((ENUM_STO_PRICE)e_sto_price[i]));
         }
      }      
      
      // obtención del índice del valor para ENUM_MA_METHOD  
      int MethodIndex(long val){
         for(int i=ArraySize(e_method)-1;i>=0;i--){
            if(e_method[i]==val){
               return(i);
            }
         }
         return(-1);
      }
      
      // obtención del índice del valor para ENUM_APPLIED_PRICE
      int PriceIndex(long val){
         for(int i=ArraySize(e_price)-1;i>=0;i--){
            if(e_price[i]==val){
               return(i);
            }
         }
         return(-1);
      }  
      
      // obtención del índice del valor para ENUM_APPLIED_VOLUME   
      int VolumeIndex(long val){
         for(int i=ArraySize(e_volume)-1;i>=0;i--){
            if(e_volume[i]==val){
               return(i);
            }
         }
         return(-1);
      }  
      
      // obtención del índice del valor para ENUM_STO_PRICE     
      int StoPriceIndex(long val){
         for(int i=ArraySize(e_sto_price)-1;i>=0;i--){
            if(e_sto_price[i]==val){
               return(i);
            }
         }
         return(-1);
      }      
      
   public:
      
      // inicialización de los controles
      virtual void Init(SExtParams & par){}
      
      //--- establecimiento de valores 
      virtual void SetValues(SExtParams & par){}      
      
      // obtención de valores
      virtual void GetValues(SExtParams & par){}
      
       //--- visualización de controles
      virtual void Show(int x,int y){}      
      
      // ocultación de controles
      virtual void Hide(){}      
      
      // número de controles para calcular el alto del formulario
      virtual int ControlsCount(){
         return(0);
      }
      
      // procesamiento de eventos
      virtual int Event(int id,long lparam,double dparam,string sparam){
         return(0);
      }
      
};
    

Vamos a considerar una clase derivada para la variante para determinar la tendencia según la línea de ADX y según el nivel (dos controles):

class CUniTrendControl_ADX_Level: public CUniTrendControl{
   private:
  
      // punteros a los controles simples
      CSpinInputBox m_f_period1;
      CSpinInputBox m_f_level;
  
   public:
  
      // inicialización de los controles
      void Init(SExtParams & par){
         m_f_period1.Init("f_period1",SPIN_BOX_WIDTH,1," adx_period");
         m_f_period1.SetMinValue(1);
         m_f_period1.SetReadOnly(false);
         m_f_level.Init("f_level",COMBO_BOX_WIDTH,this.SolveChange(par.level_digits)," level");
         m_f_level.SetMinValue(0);
         m_f_level.SetReadOnly(false);
      }
  
      //--- establecimiento de valores 
      void SetValues(SExtParams & par){        
         m_f_period1.SetValue(par.f_period1);
         m_f_level.SetValue(par.f_level);
      }
  
      // obtención de valores
      void GetValues(SExtParams & par){
         par.f_period1=(int)m_f_period1.Value();
         par.f_level=m_f_level.Value();
      }    
  
       //--- visualización de controles
      void Show(int x,int y){
         m_f_period1.Show(x,y);
         y+=20;
         m_f_level.Show(x,y);
      }      
  
      // ocultación de controles
      void Hide(){
         m_f_period1.Hide();
         m_f_level.Hide();
      }
  
      // número de controles para calcular el alto del formulario
      int ControlsCount(){
         return(2);
      }
  
       // procesamiento de eventos de controles
      int Event(int id,long lparam,double dparam,string sparam){
         int e1=m_f_period1.Event(id,lparam,dparam,sparam);
         int e2=m_f_level.Event(id,lparam,dparam,sparam);
         if(e1!=0 || e2!=0){
            return(1);
         }
         return(0);
      }
};
    

La variable tipo SExtParams se pasa a los métodos SetValues() y GetValues(), en cada subclase se utilizan sólo los campos necesarios de la estructura. Es necesario establecer el valor mínimo del cambio para el control del nivel, eso se hace durante la inicialización del control, por eso la estructura con parámetros también se pasa al método Init(). En general, la creación de las clases derivadas corresponde a todos los principios de la creación de los controles descritos en el artículo «Controles gráficos personalizados. Parte 1. Creando un control simple», excepto que en este caso, no se crean todos los métodos, sino los más necesarios. 

Añadiendo la interfaz gráfica al indicador

Esta fase de la creación del indicador parece mucho a la fase correspondiente durante la creación del indicador universal de tendencia y durante la creación del canal universal. Vamos a ver solamente las diferencias.

Antes, cuando el indicador trabajaba en modo del uso de los valores predefinidos (UseDefault=true), todos los parámetros se inicializaban con los valores -1. Ahora esta opción no conviene, ya que el parámetro del nivel puede ser negativo para algunos indicadores. Por eso la inicialización de las variables se realiza con los valores de la constante PARAMETER_EMPTY declarada en el archivo UniTrendDefines.mqh. Esta constante tiene el valor INT_MAX (un valor que excede mucho los valores reales de los niveles).

Existe otra pequeña diferencia en la función OnTimer(): para verificar si el indicador está calculado, se llama al método Calculated(), porque para algunas variantes de la tendencia es necesario comprobar un indicador, mientras que para otras, dos. Esto puede saberse solamente dentro de la clase del indicador.

Como resultado, obtenemos otro indicador universal y muy cómodo (fig. 10).

Fig. 10. Indicador universal de tendencia con interfaz gráfica
Fig. 10. Indicador universal de tendencia con interfaz gráfica

Obsérvese: además de la visualización del nombre de la variante seleccionada de la lista en el formulario, el tipo del indicador utilizado se muestra en la esquina superior izquierda de la subventana del indicador. Se muestra no sólo el tipo, sino también los valores de todos los parámetros (entre paréntesis). Si el valor del nivel se establece en puntos, se registra la expresión del valor real del nivel (fig. 11).

 
Fig. 11. Visualización del parámetro establecido en puntos

El indicador totalmente hecho se encuentra en el archivo adjunto Indicators/iUniTrendGUI.mq5.  .

Conclusión 

En total, el indicador incluye 46 diferentes opciones para determinar la tendencia. La interfaz gráfica que ofrece la posibilidad de cambiar rápidamente los parámetros de los indicadores y sus tipos permite explorar el historial de una forma cómoda. La presencia de la función de notificación hace que el indicador sea muy útil también para la aplicación práctica real.

Un nuevo enfoque en la creación de la interfaz gráfica tiene sus ventajas y sus defectos. Como uno de los inconvenientes se puede señalar el volumen grande del código y, por consiguiente, el volumen extenso del trabajo. A diferencia de las clases que controlan la visibilidad de los controles (como en el oscilador y canal universal), aquí para cada variante de la determinación de la tendencia, ha sido creado un control prácticamente de completa funcionalidad. No obstante, la separación e independencia más clara del código del control compuesto del código del formulario han facilitado considerablemente su desarrollo, y eso facilita también la futura ampliación del indicador en caso de la necesidad.

Anexo

El apéndice incluye el archivo con todos los ficheros necesarios. Todos los ficheros se ubican en las carpetas tal como deben ubicarse en el terminal.