- Manejadores y contadores de propietarios de indicadores
- Una forma sencilla de crear instancias de indicadores: iCustom
- Comprobación del número de barras calculadas: BarsCalculated
- Obtención de datos de series temporales a partir de un indicador: CopyBuffer
- Soporte para múltiples símbolos y marcos temporales
- Visión general de los indicadores integrados
- Utilización de los indicadores integrados
- Forma avanzada de crear indicadores: IndicatorCreate
- Creación flexible de indicadores con IndicatorCreate
- Visión general de las funciones de gestión de indicadores en el gráfico
- Combinar salida a ventanas principal y auxiliar
- Leer datos de gráficos que tienen un desplazamiento
- Borrar instancias de indicadores: IndicatorRelease
- Obtener la configuración del indicador por su manejador
- Definir la fuente de datos de un indicador
Obtención de datos de series temporales a partir de un indicador: CopyBuffer
Un programa MQL puede leer datos de los búferes públicos del indicador por su manejador. Recordemos que en los indicadores personalizados, dichos búferes son arrays especificados en el código fuente en llamadas a la función SetIndexBuffer.
La API de MQL5 proporciona la función CopyBuffer para la lectura de búferes; la función tiene 3 formas.
int CopyBuffer(int handle, int buffer, int offset, int count, double &array[])
int CopyBuffer(int handle, int buffer, datetime start, int count, double &array[])
int CopyBuffer(int handle, int buffer, datetime start, datetime stop, double &array[])
El parámetro handle especifica el manejador recibido de la llamada iCustom u otras funciones (para obtener más detalles, consulte las secciones sobre IndicatorCreate e indicadores integrados). El parámetro buffer establece el índice del búfer del indicador del que se van a solicitar los datos. La numeración se realiza empezando por 0.
Los elementos recibidos de la serie temporal solicitada se introducen en el conjunto array por referencia.
Las tres variantes de la función difieren en la forma de especificar el rango de marcas de tiempo (start/stop) o números (offset) y la cantidad (count) de barras para los que se obtienen los datos. Los fundamentos del trabajo con estos parámetros coinciden plenamente con lo que estudiamos en Visión general de las funciones Copy para obtener arrays de cotizaciones. En particular, los elementos de datos copiados en offset y count se cuentan del presente al pasado, es decir, la posición inicial igual a 0 significa la barra actual. Los elementos del array receptor se ordenan físicamente del pasado al presente (sin embargo, este direccionamiento puede invertirse a nivel lógico utilizando ArraySetAsSeries).
CopyBuffer es un análogo de funciones para leer series temporales integradas del tipo Copy Open, CopyClose, entre otros. La principal diferencia es que las series temporales con cotizaciones son generadas por el propio terminal, mientras que las series temporales de los búferes de los indicadores son calculadas por indicadores personalizados o integrados. Además, en el caso de los indicadores, establecemos un par específico de símbolo y marco temporal que definen e identifican una serie temporal de antemano, en la función de creación de manejadores como iCustom, y en CopyBuffer esta información se transmite indirectamente a través de handle.
Cuando se copia una cantidad desconocida de datos como array de destino, es conveniente utilizar un array dinámico. En este caso, la función CopyBuffer distribuirá el tamaño del array receptor en función del tamaño de los datos copiados. Si es necesario copiar repetidamente una cantidad conocida de datos, es mejor hacerlo en un búfer asignado estáticamente (local con el modificador de static o de tamaño fijo en el contexto global) para evitar repetidas asignaciones de memoria.
Si el array receptor es un búfer de indicadores (un array previamente registrado en el sistema por la función SetIndexBufer), entonces la indexación en la serie temporal y en el búfer receptor son las mismas (sujeto a una petición para el mismo par símbolo/marco temporal). En este caso, es fácil implementar el rellenado parcial del receptor (en concreto, se utiliza para actualizar las últimas barras, véase un ejemplo más abajo). Si el símbolo o el marco temporal de la serie temporal solicitada no coincide con el símbolo y/o el marco temporal del gráfico actual, la función no devolverá más elementos que el número mínimo de barras en estos dos: origen y destino.
Si un array ordinario (no un búfer) se pasa como el argumento array, la función lo rellenará empezando por los primeros elementos, por entero (en el caso de dinámico) o parcialmente (en el caso de estático, con exceso de tamaño). Por lo tanto, si es necesario copiar parcialmente los valores de indicadores en una ubicación arbitraria en otro array, entonces para estos propósitos es necesario utilizar un array intermedio, en el cual se copia el número requerido de elementos, y desde allí se transfieren al destino final.
La función devuelve el número de elementos copiados o -1 en caso de error, incluida la ausencia temporal de datos preparados.
Dado que, por regla general, los indicadores dependen directa o indirectamente de las series temporales de precios, su cálculo no comienza antes de que se sincronicen las cotizaciones. A este respecto, hay que tener en cuenta los aspectos técnicos de la organización y el almacenamiento de series temporales en el terminal y prepararse para que los datos solicitados no aparezcan inmediatamente. En concreto, podemos recibir 0 o una cantidad inferior a la solicitada. Todos estos casos deben tratarse en función de las circunstancias, como esperar a una compilación o informar de un problema al usuario.
Si las series temporales solicitadas aún no se han construido, o necesitan descargarse del servidor, entonces la función se comporta de forma diferente dependiendo del tipo de programa MQL desde el que se llame.
Cuando se solicitan datos que aún no están listos del indicador, la función devolverá inmediatamente -1, pero se iniciará el proceso de carga y construcción de series temporales.
Al solicitar datos a un Asesor Experto o a un script, se iniciará la descarga desde el servidor y/o la construcción de las series temporales requeridas si los datos pueden construirse a partir del historial local. La función devolverá la cantidad de datos que estarán listos en el tiempo de espera (45 segundos) asignado para la ejecución sincrónica de la función (el código de llamada está esperando a que la función finalice).
Tenga en cuenta que la función CopyBuffer puede leer datos de los búferes independientemente de su modo de funcionamiento, INDICATOR_DATA, INDICATOR_COLOR_INDEX, INDICATOR_CALCULATIONS, mientras que los dos últimos están ocultos para el usuario.
También es importante tener en cuenta que el desplazamiento de la serie temporal puede establecerse en el indicador llamado mediante la propiedad PLOT_SHIFT y afecta al desplazamiento de los datos leídos con CopyBuffer. Por ejemplo, si las líneas del indicador se desplazan hacia el futuro en N barras, en los parámetros CopyBuffer (primera forma) hay que dar offset igual a (- N), es decir, con un menos, ya que la barra de la serie temporal actual tiene un índice de 0, y los índices de las barras futuras con desplazamiento disminuyen en uno en cada barra. En concreto, tal situación surge con el indicador Gator, ya que su gráfico nulo está desplazado hacia delante por el valor del parámetro TeethShift, y el primer diagrama está desplazado por el valor del parámetro LipsShift. La corrección debe hacerse en función de la más alta de ellas. Veremos un ejemplo en la sección Leer datos de gráficos que tienen un desplazamiento.
MQL5 no proporciona herramientas programáticas para encontrar la propiedad PLOT_SHIFT de un indicador de terceros. Por lo tanto, si es necesario, tendrá que solicitar esta información al usuario a través de una variable de entrada.
Trabajaremos con CopyBuffer desde el código del Asesor Experto en el capítulo sobre Asesores Expertos, pero por ahora nos limitaremos a los indicadores.
Vamos a continuar desarrollando un ejemplo con un indicador auxiliar IndWPR. Esta vez, en la versión UseWPR3.mq5 proporcionaremos un búfer de indicador y lo rellenaremos con datos de IndWPR utilizando CopyBuffer. Para ello, aplicaremos las directivas con el número de búferes y los ajustes de renderización.
#property indicator_separate_window
|
En el contexto global, describimos el parámetro de entrada con el periodo WPR, un array para el búfer y una variable con un descriptor.
input int WPRPeriod = 14;
|
El manejador OnInit prácticamente no cambia: sólo se ha añadido la llamada SetIndexBuffer.
int OnInit()
|
En OnCalculate copiaremos los datos sin transformaciones.
int OnCalculate(const int rates_total,
|
Al compilar y ejecutar UseWPR3, obtendremos de hecho una copia del WPR original, con la excepción del ajuste de los niveles, la exactitud de los números y el título. Esto es suficiente para probar el mecanismo, pero normalmente los nuevos indicadores basados en uno o más indicadores auxiliares ofrecen alguna idea y transformación de datos propia. Por lo tanto, desarrollaremos otro indicador que genere señales de compra y venta (desde la posición del trading, no deben considerarse como un modelo, ya que se trata únicamente de una tarea de programación). La idea del indicador se muestra en la siguiente imagen:
Indicadores IndWPR, IndTripleEMA, IndFractals
Utilizamos la salida del WPR de las zonas de sobrecompra y sobreventa como recomendación, respectivamente, para vender y comprar. Para que las señales no reaccionen a fluctuaciones aleatorias, aplicaremos una media móvil triple a WPR y comprobaremos si su valor cruza los límites de las zonas superior e inferior.
Como filtro para estas señales, comprobaremos qué fractal fue el último antes de este momento: un fractal superior significa un retroceso del precio a la baja y confirma una venta, y un fractal inferior significa un retroceso al alza y, por tanto, apoya una compra. Los fractales aparecen con un desfase de un número de barras igual al orden de los fractales.
El nuevo indicador está disponible en el archivo UseWPRFractals.mq5.
Necesitamos tres búferes: dos de señal y uno más para el filtro. Podríamos emitir este último en el modo INDICATOR_CALCULATIONS. En su lugar, hagámoslo el INDICATOR_DATA estándar, pero con el estilo DRAW_NONE; de esta forma no interferirá en el gráfico, pero sus valores serán visibles en la ventana de datos.
Las señales se mostrarán en el gráfico principal (en los precios Close por defecto), por lo que utilizaremos la directiva indicator_chart_window. Todavía podemos llamar a los indicadores del tipo WPR que se dibujan en una ventana separada, ya que todos los indicadores subordinados se pueden calcular sin visualización. Si es necesario, podemos trazarlos, pero hablaremos de ello en el capítulo sobre gráficos (véase ChartIndicatorAdd).
#property indicator_chart_window
|
En las variables de entrada proporcionaremos la posibilidad de especificar el periodo WPR, el periodo de promediación (suavizado) y el orden fractal. Estos son los parámetros de los indicadores subordinados. Además, introducimos la variable offset con el número de la barra en la que se analizarán las señales. El valor 0 (por defecto) significa la barra actual y el análisis en modo tick (nota: las señales de la última barra pueden ser redibujadas; a algunos operadores de trading eso no les gusta). Si hacemos offset igual a 1, analizaremos las barras ya formadas, y tales señales no cambian.
input int PeriodWPR = 11;
|
La variable Threshold define el tamaño de las zonas de sobrecompra y sobreventa como una fracción de ±1.0 (en cada dirección). Por ejemplo, si sigue la configuración clásica de WPR con niveles -20 y -80 en una escala de 0 a -100, entonces Threshold debería ser igual a 0.4.
Se proporcionan los siguientes arrays para los búferes de indicadores.
double UpBuffer[]; // upper signal means overbought, i.e. selling
|
Los manejadores de indicadores se guardarán en variables globales.
int handleWPR, handleEMA3, handleFractals; |
Realizaremos todos los ajustes, como de costumbre, en OnInit. Dado que la función CopyBuffer utiliza la indexación del presente al pasado, para la uniformidad de la lectura de datos fijamos la bandera «serie» (ArraySetAsSeries) para todos los arrays.
int OnInit()
|
En las llamadas de iCustom debe prestarse atención a cómo se crea handleEMA3. Dado que esta media debe calcularse a partir del WPR, pasamos handleWPR (obtenido en la llamada anterior a iCustom) como último parámetro, después de los parámetros reales del indicador IndTripleEMA. Al hacerlo, debemos especificar la lista completa de parámetros de entrada de IndTripleEMA (los parámetros que contiene son int InpPeriodEMA y BEGIN_POLICY InpHandleBegin; utilizamos el segundo parámetro para estudiar la omisión de las barras iniciales y no lo necesitamos ahora, pero debemos pasarlo, así que simplemente lo ponemos a 0). Si omitiéramos el segundo parámetro en la llamada por considerarlo irrelevante en el contexto actual de la aplicación, entonces el manejador handleWPR pasado se interpretaría en el indicador llamado como InpHandleBegin. En consecuencia, IndTripleEMA se aplicaría al precio Close normal.
Cuando no necesitamos pasar un manejador extra, la sintaxis de la llamada iCustom le permite omitir un número arbitrario de últimos parámetros, mientras que éstos recibirán los valores por defecto del código fuente.
En el manejador OnCalculate esperamos a que los indicadores WPR y los fractales estén preparados, y luego calculamos las señales para todo el historial o la última barra utilizando la función auxiliar MarkSignals.
int OnCalculate(const int rates_total,
|
Nos interesa sobre todo trabajar con la función CopyBuffer oculta en MarkSignals. Los valores del WPR suavizado se leerán en el array wpr[2], y los fractales se leerán en peaks[1] y hollows[1].
int MarkSignals(const int bar, const int offset, const double &data[])
|
A continuación, rellenamos los arrays locales mediante tres llamadas a CopyBuffer. Tenga en cuenta que no necesitamos lecturas directas de IndWPR, porque se utiliza en los cálculos de IndTripleEMA. Leemos los datos en el array wpr a través del manejador handleEMA3. También es importante que hay 2 búferes en el indicador fractal, y por lo tanto la función CopyBuffer llamada dos veces con diferentes índices 0 y 1 para los arrays peaks y hollows, respectivamente. Los arrays de fractales se leen con una sangría de FractalOrder, porque un fractal sólo puede formarse en una barra que tenga un cierto número de barras a la izquierda y a la derecha.
if(CopyBuffer(handleEMA3, 0, bar + offset, 2, wpr) == 2
|
A continuación, tomamos de la barra anterior del búfer Filter la dirección anterior del filtro (al principio del historial es 0, pero cuando aparece un fractal alcista o bajista, escribimos ahí +1 o -1, esto se puede ver en el código fuente justo debajo) y la cambiamos en consecuencia cuando se detecta algún fractal nuevo.
int filterdirection = (int)Filter[bar + 1];
|
Por último, analizamos la transición del indicador WPR suavizado de la zona superior o inferior a la zona media, teniendo en cuenta la anchura de las zonas especificadas en Threshold.
// translate 2 WPR values into the range [-1,+1]
|
A continuación se muestra una captura de pantalla del indicador resultante en el gráfico.
Indicador de señal UseWPRFractals basado en WPR, EMA3 y fractales