
Indicadores MTF como herramienta de análisis técnico
Introducción
La mayoría de nosotros estamos de acuerdo en que el proceso de análisis de la situación actual de mercado comienza por el estudio de los marcos temporales mayores del gráfico. Esto sucede hasta que pasemos al gráfico en el que realizamos las transacciones. Esta opción de análisis es una de las condiciones del comercio exitoso, indispensable para lograr un enfoque profesional de dicha tarea. Normalmente, para ello abrimos varias ventanas o alternamos entre periodos del gráfico, si usamos el mismo conjunto de herramientas. Bien, ya hemos superado el periodo de análisis.
¿Qué hacemos a continuación? ¿Quedarnos sin ver lo que sucede en los marcos temporales mayores, o bien continuar «saltando» entre ventanas y periodos? Todo va bien si trabajamos en el marco temporal Н1 u otro superior, pues disponemos de tiempo suficiente para realizar una valoración minuciosa. Pero, ¿y si trabajamos en М1-М15? Necesitamos esa información, y esta es a veces totalmente imprescindible. Y no en algún sitio tres o cuatro pestañas después, sino aquí y ahora. Y esto concierne especialmente a las estrategias de MTF, basadas en la valoración simultánea de varios marcos temporales, tales como las «Ondas de Wolfe» o la «Triple Pantalla de Elder».
Los tráders que deben actuar de forma semejante, se encuentran bajo un fuerte estrés mental y visual. Al realizar transacciones en marcos temporales menores, resulta sencillo omitir señales rentables de los marcos mayores. Todo ello provoca decisiones precipitadas por parte del tráder, como el cierre prematuro de una transacción o la omisión del momento de viraje del mercado. Las consecuencias de semejantes errores las conocemos de sobra. En estas situaciones, solo nos queda encomendarnos a nuestra experiencia o la velocidad en la obtención de información.
Pero existe una solución a este problema, se trata de un indicador que obtiene información de diferentes MT para este o varios instrumentos comerciales, y que además la muestra en la pantalla, permitiendo con ello valorar el estado del mercado de manera operativa. Aparte de la información básica, puede mostrar el estado real de la tendencia de mercado y recomendar ciertas acciones comerciales.
Particularidades del algoritmo
La principal diferencia respecto a los clásicos es el procesamiento de la información general de todos los intervalos temporales o instrumentos comerciales, con la posterior transmisión de la misma al periodo actual. Cualquier tipo de indicador (oscilador, de tendencia, de volumen, etcétera), o bien una combinación de estos, puede ser de marco temporal múltiple. Estos se calculan según su principal algoritmo, y transmiten la información teniendo en cuenta el intervalo temporal indcado en los ajustes.
La configuración no se diferencia de la de sus congéneres, con la diferencia de que poseen un parámetro (o un grupo de ellos) en el que se indica una lista de intervalos temporales, y en algunos casos, una lista de instrumentos comerciales, desde los que llegará la información necesaria para crear una imagen más certera de lo que sucede. La muestra del resultado puede realizarse tanto en la ventana principal del gráfico, como por separado, y también tener forma de señal, combinando grupos según el tipo de instrumento.
Clasificación de los indicadores de marco temporal múltiple
Están representados en todas las clases estándar; muchos de ellos son comlejos, es decir, combinan en sí información de cálculo con elementos gráficos. Podemos dividirlos en los siguientes grupos:
1. Informativos – muestran en la pantalla datos e información adicional sin señales ni construcciones gráficas. Podemos considerar un ejemplo clásico de este tipo el indicador MultiTimeFrame. Este representa la hora de cierre de la vela de cada marco temporal, el Ask y el Bid de las parejas de divisas elegidas, el estado de la propia vela (UP, DOWN, DOJI) y el volumen. La pantalla de los indicadores de este tipo está llena de un gran volumen de información útil, pero poco adecuada para el comercio, solo para la visualización.
Fig. 1. Indicadores informativos
Este grupo está también representado por instrumentos de pleno derecho, que se pueden usar perfectamente para la toma de decisiones comerciales. Estos muestran en la pantalla los resultados del análisis de varios indicadores estándar sin necesidad de instalación en el gráfico (el cálculo se realiza automáticamente), y acompañan el proceso con recomendaciones comerciales.
Fig. 2. Señales de los indicadores informativos
Fig. 3. Señales de los indicadores informativos
2. Los indicadores gráficos muestran en la pantalla la construcción del mismo instrumento, pero en diferentes MT. Este es el aspecto del envoltorio estándar МА(13) con MT distintos.
Fig. 4. Indicadores gráficos
Otro tipo más de construcción gráfica es un grupo de gráficos con diferente periodo de cálculo. Este enfoque se implementa usando matemáticas simples. Es decir, un Estocático (5.3.3) en М5 tendrá los parámetros(15.3.9) con М15, y con М30, otros distintos (30.3.18).
Fig. 5. Indicadores gráficos con periodos de cálculo distintos
Debemos atribuir la variante de solución mencionada más arriba a la clase MTF con un matiz. Semejante enfoque no siempre se puede implementar, y en ciertos casos, los defectos de este instrumento son tan sustanciales que su aplicación no resulta útil. Más adelante hablaremos sobre cuándo es aplicable este método, así como sobre sus ventajas y desventajas.
Podemos considerar en un grupo aparte a los llamados indicadores de señal. Para aliviar un poco de construcciones gráficas la pantalla de trabajo, el indicador forma líneas punteadas (de señal) o bloques de gráficos que representan la dirección de la tendencia u otros parámetros. Este es el aspecto del MTF_Coral estándar resuelto de la forma mencionada:
Fig. 6. Indicadores de señal
Asimismo, podemos mencionar un grupo al que pondríamos el nombre de «Una ventana dentro de otra». La característica distintiva de este grupo es que representa en la misma ventana del gráfico principal los gráficos de otros marcos temporales o indicadores.
Fig. 7. Indicador del tipo "Una ventana dentro de otra"
Fig. 7.1. Indicador del tipo "Una ventana dentro de otra"
Otro ejemplo de la solución All_Woodies CCI.
Fig. 7.1. Indicador del tipo "Una ventana dentro de otra" All_Woodies CCI
Debemos mencionar aparte los indicadores de marco temporal múltiple (MTF) de volatilidad. Podemos incluir en ese grupo MTF Candles.
Fig. 8. Indicador de volatilidad MTF Candles
Modos de implementación
Anteriormente, hemos analizado los principales tipos de indicadores de MTF. Ahora, vamos a hablar usando ejemplos sencillos de los principales métodos de implementación de la variante lineal, y también analizaremos las particularidades de cada solución.
Indicadores de periodo múltiple. Usando como ejemplo la МА, vamos a analizar la siguiente tarea: crear una variante con cambio de periodo de cálculo para representar tres MT diferentes. Establecemos los parámetros básicos y nuestras variables:
//---- indicator settings #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_type3 DRAW_LINE #property indicator_color1 Blue #property indicator_color2 Red #property indicator_color3 Lime #property indicator_width1 1 #property indicator_width2 1 #property indicator_width3 1 //---- input parameters input ENUM_TIMEFRAMES tf1 = 1; // Time Frame (1) input ENUM_TIMEFRAMES tf2 = 5; // Time Frame (2) input ENUM_TIMEFRAMES tf3 = 15; // Time Frame (3) input int maPeriod = 13; // MA period input int Shift = 0; // Shift input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Moving average method input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied price //---- indicator buffers double ExtBuf1[]; double ExtBuf2[]; double ExtBuf3[]; //---- handles for moving averages int ExtHandle1; int ExtHandle2; int ExtHandle3; //--- bars minimum for calculation int ExtBarsMinimum; //--- int period1=0; int period2=0; int period3=0;
Ahora, inicializamos los datos de las matrices con la condición de que el MT en el que está ubicado sea <= a los establecidos en las variables.
void OnInit() { int timeframe; //---- indicator buffers mapping SetIndexBuffer(0,ExtBuf1,INDICATOR_DATA); SetIndexBuffer(1,ExtBuf2,INDICATOR_DATA); SetIndexBuffer(2,ExtBuf3,INDICATOR_DATA); //--- timeframe =_Period; //--- if(tf1>=timeframe) { period1=maPeriod*(int)MathFloor(tf1/timeframe); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,period1-1); //sets first bar from what index will be drawn PlotIndexSetInteger(0,PLOT_SHIFT,Shift); //line shifts when drawing PlotIndexSetString(0,PLOT_LABEL,"MA("+string(period1)+")"); //name for DataWindow ExtHandle1=iMA(NULL,0,period1,0,InpMAMethod,InpAppliedPrice); //get MA's handles } //---- if(tf2>=timeframe) { period2=maPeriod*(int)MathFloor(tf2/timeframe); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,period2-1); //sets first bar from what index will be drawn PlotIndexSetInteger(1,PLOT_SHIFT,Shift); //line shifts when drawing PlotIndexSetString(1,PLOT_LABEL,"MA("+string(period2)+")"); //name for DataWindow ExtHandle2=iMA(NULL,0,period2,0,InpMAMethod,InpAppliedPrice); //get MA's handles } //---- if(tf3>=timeframe) { period3=maPeriod*(int)MathFloor(tf3/timeframe); PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,period3-1); //sets first bar from what index will be drawn PlotIndexSetInteger(2,PLOT_SHIFT,Shift); //line shifts when drawing PlotIndexSetString(2,PLOT_LABEL,"MA("+string(period3)+")"); //name for DataWindow ExtHandle3=iMA(NULL,0,period3,0,InpMAMethod,InpAppliedPrice); //get MA's handles } //--- set accuracy IndicatorSetInteger(INDICATOR_DIGITS,_Digits); //--- bars minimum for calculation int per=MathMax(period3,MathMax(period1,period2)); ExtBarsMinimum=per+Shift; //--- initialization done }
Ciclo principal para comprobar la inicialización y el cálculo:
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 &tick_volume[], const long &volume[], const int &spread[]) { //--- check for rates total if(rates_total<ExtBarsMinimum) return(0); // not enough bars for calculation //--- not all data may be calculated int calculated=BarsCalculated(ExtHandle1); if(calculated<rates_total&&period1!=0) { Print("Not all data of ExtHandle1 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } calculated=BarsCalculated(ExtHandle2); if(calculated<rates_total&&period2!=0) { Print("Not all data of ExtHandle2 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } calculated=BarsCalculated(ExtHandle3); if(calculated<rates_total&&period3!=0) { Print("Not all data of ExtHandle3 is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } //--- we can copy not all data int to_copy; if(prev_calculated>rates_total || prev_calculated<0) to_copy=rates_total; else { to_copy=rates_total-prev_calculated; if(prev_calculated>0) to_copy++; } //---- get ma buffers if(IsStopped()) return(0); //Checking for stop flag if(period1!=0) if(CopyBuffer(ExtHandle1,0,0,to_copy,ExtBuf1)<=0) { Print("getting ExtHandle1 is failed! Error",GetLastError()); return(0); } if(IsStopped()) return(0); //Checking for stop flag if(period2!=0) if(CopyBuffer(ExtHandle2,0,0,to_copy,ExtBuf2)<=0) { Print("getting ExtHandle2 is failed! Error",GetLastError()); return(0); } if(IsStopped()) return(0); //Checking for stop flag if(period3!=0) if(CopyBuffer(ExtHandle3,0,0,to_copy,ExtBuf3)<=0) { Print("getting ExtHandle3 is failed! Error",GetLastError()); return(0); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Vamos a echar un vistazo al resultado obtenido.
Fig. 9. Implementando el indicador MTF
Ya hemos analizado un ejemplo en el que es necesario usar un indicador en diferentes MT utilizando el aumento del periodo de cálculo. Introduciendo una pequeña modificación, podremos usarlo, además, con la posibilidad de indicar los periodos para cada línea de forma independiente. Este método de escritura puede parecer poco útil. No hay nada más simple que calcular un periodo por uno mismo e iniciar varios indicadores simultáneamente. Pero hay casos en los que esta variante, con todos sus defectos, resulta la ideal. Entre estos casos podemos incluir la situación en la que se observan dos (tres) osciladores no normalizados en una misma ventana. Debido a lo vasto de su amplitud, estos osciladores se desvían respecto a la línea central, lo que complica su integración. Esta variante permite excluir esta desventaja.
Indicadores MTF
En lugar de las funciones que tan bien conocemos en MQL4, iClose(), iHigh(), iLow(), iOpen(), iTime(), iVolume(), en MQL5 tenemos ahora CopyTime(), CopyClose(), CopyHigh(), CopyLow(), CopyOpen(), CopyTime(), CopyVolume(), mientras que las funciones iCustom, iMA, iCCI, iMACD, etcétera, se implementan a través de CopyBuffer(). Cada una de ellas tiene sus ventajas y desventajas. En nuestro caso, vamos a hablar solo de MQL5. A la hora de escribir, puede que necesitemos la lista completa de frames desde el М1 al MN1, y esto supone 26 variantes. Y si usamos varios símbolos o instrumentos comerciales, este número aumentará exponencialmente. En la mayoría de los casos, no hay necesidad de copiar toda la historia. Para la mayoría de los indicadores informativos, el número de barras se limita a dos. Por eso, para no inflar el texto del código hasta el infinito, será más útil anotar estos comandos con funciones aparte y llamarlos multitud de veces.
Para la función de la serie temporal CopyClose(), la función tendrá el aspecto siguiente:
//+------------------------------------------------------------------+ double _iClose(string symbol,int tf,int index) { if(index < 0) return(-1); double buf[]; ENUM_TIMEFRAMES timeframe=TFMigrate(tf); if(CopyClose(symbol,timeframe, index, 1, buf)>0) return(buf[0]); else return(-1); } //+------------------------------------------------------------------+
Para la llamada de WPR:
//+------------------------------------------------------------------+ double _iWPR(string symbol, int tf, int period, int shift) { ENUM_TIMEFRAMES timeframe=TFMigrate(tf); int handle=iWPR(symbol,timeframe,period); if(handle<0) { Print("The iWPR object not created: Error ",GetLastError()); return(-1); } else return(_CopyBuffer(handle,shift)); } //+------------------------------------------------------------------+ double _CopyBuffer(int handle,int shift) { double buf[]; if(CopyBuffer(handle,0,shift,1,buf)>0) return(buf[0]); return(EMPTY_VALUE); } //+------------------------------------------------------------------+
En los casos en los que hay varias líneas, la función _CopyBuffer se puede anotar de la forma:
//+------------------------------------------------------------------+ double _CopyBuffer(int handle,int index,int shift) { double buf[]; switch(index) { case 0: if(CopyBuffer(handle,0,shift,1,buf)>0) return(buf[0]); break; case 1: if(CopyBuffer(handle,1,shift,1,buf)>0) return(buf[0]); break; case 2: if(CopyBuffer(handle,2,shift,1,buf)>0) return(buf[0]); break; case 3: if(CopyBuffer(handle,3,shift,1,buf)>0) return(buf[0]); break; case 4: if(CopyBuffer(handle,4,shift,1,buf)>0) return(buf[0]); break; default: break; } return(EMPTY_VALUE); } //+------------------------------------------------------------------+
y la función _iWPR cambiará la línea
return(_CopyBuffer(handle,shift)
a
return(_CopyBuffer(handle,0,shift)
Para ambos casos, la función TFMigrate() tendrá este aspecto:
//+------------------------------------------------------------------+ ENUM_TIMEFRAMES TFMigrate(int tf) { switch(tf) { case 0: return(PERIOD_CURRENT); case 1: return(PERIOD_M1); case 5: return(PERIOD_M5); case 15: return(PERIOD_M15); case 30: return(PERIOD_M30); case 60: return(PERIOD_H1); case 240: return(PERIOD_H4); case 1440: return(PERIOD_D1); case 10080: return(PERIOD_W1); case 43200: return(PERIOD_MN1); case 2: return(PERIOD_M2); case 3: return(PERIOD_M3); case 4: return(PERIOD_M4); case 6: return(PERIOD_M6); case 10: return(PERIOD_M10); case 12: return(PERIOD_M12); case 20: return(PERIOD_M20); case 16385: return(PERIOD_H1); case 16386: return(PERIOD_H2); case 16387: return(PERIOD_H3); case 16388: return(PERIOD_H4); case 16390: return(PERIOD_H6); case 16392: return(PERIOD_H8); case 16396: return(PERIOD_H12); case 16408: return(PERIOD_D1); case 32769: return(PERIOD_W1); case 49153: return(PERIOD_MN1); default: return(PERIOD_CURRENT); } } //+------------------------------------------------------------------+
Como ya hemos dicho, para este tipo, normalmente se necesita un número limitado de elementos (barras) en el cálculo. Pero a veces es deseable calcular toda la historia. Y aquí tenemos que estar muy atentos. Debemos recordar que el número de barras en la historia de un MT menor será superior al de un MT mayor. Debemos tener en cuenta este factor al crear este instrumento. El método más sencillo consiste en su menor número y usar este valor para el cálculo. Resulta más complicado determinar esta magnitud para cada MT por separado. Igualmente, en ocasiones (especialmente en los indicadores informativos), se necesita información solo después del cierre de la barra, y no hay necesidad de recalcular los MT mayores en cada tick del menor. Si consideramos dicho aspecto, esto reucirá significativamente el potencial de este instrumento, que es bastante grande por sus peculiaridades.
La escritura de indicadores informativos (fig 1, fig 2, fig 3) no se diferencia en nada de la escritura de los clásicos, por eso, vamos a pasar directamente al análisis de otro tema más interesante: la clase de los indicadores gráficos. Mientras que los informativos solo necesitan la información actual sobre el estado del mercado y nuestro conjunto de instrumentos, los gráficos presentan ciertas exigencias respecto a la construcción. Todos sabemos que para formar el periodo М5, necesitamos 5 barras del periodo М1, para el periodo М15, tres barras de М5, etc. Es decir, durante la formación de la línea en М5, la línea de М15 se dibuja durante 3 barras. La posición de la línea no se fija, y cambia mientras la vela de М15 no se cierre. Por este motivo, surge la necesidad de vincular la formación a la hora de apertura de la vela. Vamos a ver una variante en la que se hace esto, usando como ejemplo la misma МА.
//---- indicator settings #property indicator_chart_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_type1 DRAW_LINE #property indicator_color1 Blue #property indicator_width1 1 //---- input parameters input ENUM_TIMEFRAMES tf = 5; // Time Frame input int maPeriod = 13; // MA period input int Shift = 0; // Shift input ENUM_MA_METHOD InpMAMethod = MODE_SMA; // Moving average method input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; // Applied price input int Bars_Calculated = 500; //---- indicator buffers double ExtMA[]; //---- handles for moving averages int MA_Handle; //--- bars minimum for calculation int ExtBarsMinimum; ENUM_TIMEFRAMES _tf; int pf; //--- we will keep the number of values in the Moving Average indicator int bars_calculated=0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { _tf=tf; ENUM_TIMEFRAMES timeframe; int draw_shift=Shift;// initial value PLOT_SHIFT int draw_begin=maPeriod;// initial value PLOT_DRAW_BEGIN //--- timeframe=_Period; if(_tf<=timeframe)_tf=timeframe;// if the TF is less than or is equal to the current one, set it to PERIOD_CURRENT pf=(int)MathFloor(_tf/timeframe);// calculate coefficient for PLOT_DRAW_BEGIN, PLOT_SHIFT and the number of calculation bars. draw_begin=maPeriod*pf;// calculate PLOT_DRAW_BEGIN draw_shift=Shift*pf;// calculate PLOT_SHIFT //---- indicator buffers mapping SetIndexBuffer(0,ExtMA,INDICATOR_DATA); //--- PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,draw_begin-pf); //sets first bar from what index will be drawn PlotIndexSetInteger(0,PLOT_SHIFT,draw_shift); //line shifts when drawing PlotIndexSetString(0,PLOT_LABEL,"MA("+string(tf)+" "+string(maPeriod)+")");//name for DataWindow //--- MA_Handle=iMA(NULL,_tf,maPeriod,0,InpMAMethod,InpAppliedPrice); //get MA's handles if(MA_Handle==INVALID_HANDLE) { Print("getting MA Handle is failed! Error",GetLastError()); return(INIT_FAILED); } //--- set accuracy IndicatorSetInteger(INDICATOR_DIGITS,_Digits); //--- bars minimum for calculation ExtBarsMinimum=draw_begin+draw_shift;// calculate the minimum required number of bars for the calculation //--- initialization done return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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 &tick_volume[], const long &volume[], const int &spread[]) { //--- check for rates total if(rates_total<ExtBarsMinimum) return(0); // not enough bars for calculation int limit; //--- apply timeseries indexing to array elements ArraySetAsSeries(time,true); ArraySetAsSeries(ExtMA,true); //--- detect start position //--- calculations of the necessary amount of data to be copied //--- and the 'limit' starting index for the bars recalculation loop if(prev_calculated>rates_total || prev_calculated<=0|| calculated!=bars_calculated)// checking for the first start of the indicator calculation { limit=rates_total-ExtBarsMinimum-1; // starting index for calculation of all bars } else { limit=(rates_total-prev_calculated)+pf+1; // starting index for calculation of new bars } if(Bars_Calculated!=0) limit=MathMin(Bars_Calculated,limit);
No vamos a buscar la barra según el número (iBarShift()), sino que vamos a copiar directamente los valores por tiempo.
//--- main cycle for(int i=limit;i>=0 && !IsStopped();i--) { ExtMA[i]=_CopyBuffer(MA_Handle,time[i]); } //--- bars_calculated=calculated; Use here the above mentioned function. //+--------- CopyBuffer MA Handle ----------------------------------+ double _CopyBuffer(int handle,datetime start_time) { double buf[]; if(CopyBuffer(handle,0,start_time,1,buf)>0) return(buf[0]); return(EMPTY_VALUE); } //+-------------------- END -----------------------------------------+
Aquí tenemos nuestro resultado:
Fig. 10. Línea del indicador MTF
Este método se puede usar con éxito en todos los tipos de indicadores lineales. El principal defecto se puede ver bien en la figura: los desniveles bruscos. Si bien para una МА esto es en cierta medida una ventaja, es decir (unos niveles de apoyo-resistencia bien definidos), para los osciladores con los que usamos los patrones, en cambio, esto dificultará nuestra misión de detectarlos y construirlos. Y para aquellos como WPR o CCI, esta solución no es asumible en absoluto, ya que el aspecto de las líneas quedará irreconocible.
Para resolver este problema, basta con calcular la última barra teniendo en cuenta los coeficientes de peso. Para mayor universalidad, añadiremos la variable global Interpolate, que permitirá usar ambas soluciones.
input bool Interpolate = true; //--- main cycle for(int i=limit;i>=0 && !IsStopped();i--) { int n; datetime t=time[i]; ExtMA[i]=_CopyBuffer(MA_Handle,t); if(!Interpolate) continue; //--- datetime times= _iTime(t); for(n = 1; i+n<rates_total && time[i+n]>= times; n++) continue; double factor=1.0/n; for(int k=1; k<n; k++) ExtMA[i+k]=k*factor*ExtMA[i+n]+(1.0-k*factor)*ExtMA[i]; } //---
En esta variante, surge la necesidad de añadir la función _iTime, que determinará el número de la barra partiendo de su hora de apertura para el gráfico actual.
//+------------------------------------------------------------------+ datetime _iTime(datetime start_time) { if(start_time < 0) return(-1); datetime Arr[]; if(CopyTime(NULL,_tf, start_time, 1, Arr)>0) return(Arr[0]); else return(-1); } //+------------------------------------------------------------------+
Ahora nuestra línea ha adquirido un aspecto más familiar.
Fig. 11. Línea del indicador MTF con _iTime
Si bien no parece práctico escribir sistemas tan complejos y exigentes desde el punto de vista del gasto de recursos, estos tienen ventajas, y en ocasiones resultan insustituibles. En los casos en los que se usa la promediación clásica (МА, Alligator, etc.), al aumentar el periodo de cálculo, se observa un cierto retardo en comparación con la versión MTF. Esto se percibe especialmente en los periodos menores del valor esperado.
Fig. 12. Retardo en el indicador MTF MA
Fig. 13. Retardo en el indicador MTF Stochastic
Si bien para los indicadores sencillos como МА y Alligator, esto puede no ser tan significativo, sin embargo, para aquellos que suponen un sistema complejo de dos o más МА, tales como MACD, AO, etc., esto puede tener mucha importancia. Tanto más que los mencionados АО o АС y otros semejantes no tienen en absoluto posibilidad de cambiar el periodo de promediación. Y para los indicadores cuya línea no se suaviza (WPR, CCI, etc.), resulta bastante complicado conseguir un buen resultado con un pequeño aumento del periodo de cálculo, pues el nivel de ruido es muy alto.
Fig. 14 Indicador MTF WRP
Fig. 15 Indicador MTF CCI
En las figuras 14-15 se ve que también podemos usarlos con éxito como suavizador para aquellos casos en los que tal posibilidad no se contempla en el algoritmo.
Este tipo, aparte de su función inmediata, puede ejecutar otra muy práctica: compensar los defectos del simulador de estrategias de MetaTrader 5 en el modo de visualización. Al crear asesores MTF para comerciar o analizar la efectividad de este tipo de estrategias, nos encontramos con que no podemos observar simultáneamente en la pantalla la posición de los indicadores de distintos MT, sino que, al finalizar la simulación, obtenemos un conjunto de pestañas dependiendo del número de periodos usados. Vamos a usar como ejemplo el asesor de la estrategia «Triple Pantalla de Elder», del artículo «Libro de Recetas MQL5: Desarrollo de un Marco de Trabajo para un Sistema de Trading Basado en la Estrategia de Triple Pantalla», de Anatoli Kazharski. Recordemos cómo es esta estrategia en la variante clásica: el primer MT es el mayor, por ejemplo, de una semana, de un día o de 4 horas. Con su ayuda, se determina la tendencia principal. El segundo MT se diferencia del primero en 1 o 2 órdenes. Con su ayuda, determinamos la finalización de la corrección. El tercer MT se diferencia en otro orden más. Según este, detectamos un punto de entrada rentable.
En la primera ventana, normalmente М30-W1, ubicamos MACD (12,26,1) y EMA con un periodo 13. En la segunda pantalla, М5-D1, tendremos el Estocástico (Stochastic Oscillator) (5,3,3). La tercera pantalla puede ser de M1 a H4, la usaremos para colocar órdenes Stop en la dirección de la tendencia principal.
Fig. 16. Triple Pantalla de Elder
El autor se ha desviado un poco de esta variante, pero el concepto de la «Triple Pantalla» se ha mantenido. Durante la simulación y al finalizar la misma, observamos esta imagen:
Fig. 17. Simulación de la estrategia "Triple Pantalla de Elder"
Esta variante no nos permite analizar al completo el funcionamiento del asesor (estrategia).
Vamos a escribir nuestra propia estrategia usando nuestras propias herramientas; esta será más similar a la versión clásica de la estrategia. La creación de asesores utilizando como base los datos de los indicadores no se distingue del trabajo con sus equivalentes clásicos. El código principal tendrá el aspecto siguiente.
#define EXPERT_MAGIC 1234502 //--- #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh> #include <Trade\OrderInfo.mqh> //+------------------------------------------------------------------+ enum compTF { A, //m1-m5-m15 B, //m5-m15-h1 C, //m15-h1-h4 E //h1-h4-d1 }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input string s0="//--- input parameters Lots+Trailing ---//"; input double InpLots =0.1; // Lots input int InpTakeProfit =150; // Take Profit (in pips) input int InpStopLoss =60; // StopLoss (in pips) input int InpLevel_S =20; // Level Signal input compTF InpTFreg =2; input string s1="//--- input parameters MA ---//"; input int Signal_MA_PeriodMA =21; // Period of averaging input string s2="//--- input parameters MACD ---//"; input int Signal_MACD_PeriodFast =12; // Period of fast EMA input int Signal_MACD_PeriodSlow =26; // Period of slow EMA input string s3="//--- input parameters Stochastic ---//"; input int Signal_Stoch_PeriodK =5; // K-period input int Signal_Stoch_PeriodD =3; // D-period input string s4="//--- input parameters Trailing ---//"; //--- inputs for trailing input int InpTrailingStop =25; // Trailing Stop Level (in pips) input int InpOffset =5; // Distance from the price (in pips) //--- int ExtTimeOut=10; // time interval between trading operations in seconds int barsCalculated=3; datetime t=0; datetime time[]; //+------------------------------------------------------------------+ //| AC Sample expert class | //+------------------------------------------------------------------+ class CSampleExpert { protected: double m_adjusted_point; // 3- or 5-digit value CTrade m_trade; // trading object CSymbolInfo m_symbol; // symbol information CPositionInfo m_position; // trading position CAccountInfo m_account; // account information COrderInfo m_order; // order information //--- indicators ENUM_TIMEFRAMES mas_tf[3]; // array of timeframes int handle_MACD; // MACD indicator handle int handle_MA; // MA indicator handle int handle_Stochastic; // Stochastic indicator handle //--- indicator buffers double macd_buff[]; // MACD indicator main buffer double ma_buff[]; // MA indicator main buffer double stoch_buff[]; // Stochastic indicator main buffer //--- double close[]; double open[]; double low[]; double high[]; //--- indicator data to process double macd_ind_0; double macd_ind_1; double ma_ind; double stoch_ind_0; double stoch_ind_1; int level_stoch; //--- trailing stop data to process double m_traling_stop; double m_take_profit; double m_stop_losse; public: CSampleExpert(void); ~CSampleExpert(void); bool Init(void); void Deinit(void); bool Processing(void); protected: bool InitCheckParameters(const int digits_adjust); bool Copy(void); // bool InitIndicators(void); bool LongModified(void); bool ShortModified(void); bool LongOpened(void); // check Long condition bool ShortOpened(void); // check Short condition bool OpenSellStop(void); // place the SELLSTOP order bool OpenBuyStop(void); // place the BUYSTOP order bool OrderModifySellStop(void); // modify the SELLSTOP order bool OrderModifyBuyStop(void); // modify the BUYSTOP order bool DellSellStop(void); // delete the SELLSTOP order bool DellBuyStop(void); // delete the BUYSTOP order }; //--- global expert CSampleExpert ExtExpert; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSampleExpert::CSampleExpert(void) : m_adjusted_point(0), handle_MACD(INVALID_HANDLE), handle_Stochastic(INVALID_HANDLE), handle_MA(INVALID_HANDLE), macd_ind_0(0), macd_ind_1(0), stoch_ind_0(0), stoch_ind_1(0), ma_ind(0), m_traling_stop(0), m_take_profit(0) { ArraySetAsSeries(macd_buff,true); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSampleExpert::~CSampleExpert(void) { } //+------------------------------------------------------------------+ //| Initialization and verification of input parameters | //+------------------------------------------------------------------+ bool CSampleExpert::Init(void) { //--- initializing general information m_symbol.Name(Symbol()); // symbol m_trade.SetExpertMagicNumber(EXPERT_MAGIC); // magic m_trade.SetMarginMode(); m_trade.SetTypeFillingBySymbol(Symbol()); //--- tuning for 3 or 5 digits int digits_adjust=1; if(m_symbol.Digits()==3 || m_symbol.Digits()==5) digits_adjust=10; m_adjusted_point=m_symbol.Point()*digits_adjust; //--- setting the default deviation for trading m_traling_stop =InpTrailingStop*m_adjusted_point; m_take_profit =InpTakeProfit*m_adjusted_point; m_stop_losse =InpStopLoss*m_adjusted_point; //--- set default deviation for trading in adjusted points m_trade.SetDeviationInPoints(3*digits_adjust); //--- int x=InpTFreg; switch(x) { case 0: {mas_tf[0]=PERIOD_M1;mas_tf[1]=PERIOD_M5;mas_tf[2]=PERIOD_M15;} break; case 1: {mas_tf[0]=PERIOD_M5;mas_tf[1]=PERIOD_M15;mas_tf[2]=PERIOD_H1;} break; case 2: {mas_tf[0]=PERIOD_M15;mas_tf[1]=PERIOD_H1;mas_tf[2]=PERIOD_H4;} break; case 3: {mas_tf[0]=PERIOD_H1;mas_tf[1]=PERIOD_H4;mas_tf[2]=PERIOD_D1;} break; } //--- if(!InitCheckParameters(digits_adjust)) return(false); if(!InitIndicators()) return(false); //--- result return(true); } //+------------------------------------------------------------------+ //| Verification of input parameters | //+------------------------------------------------------------------+ bool CSampleExpert::InitCheckParameters(const int digits_adjust) { //--- checking source data if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel()) { printf("Take Profit must be greater than %d",m_symbol.StopsLevel()); return(false); } if(InpTrailingStop*digits_adjust<m_symbol.StopsLevel()) { printf("Trailing Stop must be greater than %d",m_symbol.StopsLevel()); return(false); } //--- checking the lot value if(InpLots<m_symbol.LotsMin() || InpLots>m_symbol.LotsMax()) { printf("Lots must be in the range between %f and %f",m_symbol.LotsMin(),m_symbol.LotsMax()); return(false); } if(MathAbs(InpLots/m_symbol.LotsStep()-MathRound(InpLots/m_symbol.LotsStep()))>1.0E-10) { printf("The amount does not correspond to the lot step %f",m_symbol.LotsStep()); return(false); } //--- warning if(InpTakeProfit<=InpTrailingStop) printf("Warning: Trailing Stop must be less than Take Profit"); //--- result return(true); } //+------------------------------------------------------------------+ //| Initialization of indicators | //+------------------------------------------------------------------+ bool CSampleExpert::InitIndicators(void) { //--- create the MACD indicator if(handle_MACD==INVALID_HANDLE) { //--- handle_MACD=iCustom(NULL,0,"MTF\\Oscillators\\MTF_MACD",mas_tf[2],Signal_MACD_PeriodFast,Signal_MACD_PeriodSlow); //--- if(handle_MACD==INVALID_HANDLE) { printf("Error occurred while creating MACD"); return(false); } } //--- create the MA indicator if(handle_MA==INVALID_HANDLE) { //--- handle_MA=iCustom(NULL,0,"MTF\\Trend\\MA_MultiTF",mas_tf[2],Signal_MA_PeriodMA,0,MODE_EMA,PRICE_CLOSE); //--- if(handle_MA==INVALID_HANDLE) { printf("Error occurred while creating MA"); return(false); } } //--- create the Stochastic indicator if(handle_Stochastic==INVALID_HANDLE) { //--- handle_Stochastic=iCustom(NULL,0,"MTF\\Oscillators\\MTF_Stochastic",mas_tf[1],Signal_Stoch_PeriodK,Signal_Stoch_PeriodD); //--- if(handle_Stochastic==INVALID_HANDLE) { printf("Error occurred while creating Stochastic"); return(false); } } //--- result return(true); } //+------------------------------------------------------------------+ //| Modifying the long position | //+------------------------------------------------------------------+ bool CSampleExpert::LongModified(void) { bool res=false; //--- checking the trailing stop if(InpTrailingStop>0) { if(m_symbol.Bid()-m_position.PriceOpen()>m_adjusted_point*InpTrailingStop) { double sl=NormalizeDouble(m_symbol.Bid()-m_traling_stop,m_symbol.Digits()); double tp=m_position.TakeProfit(); if(m_position.StopLoss()<sl || m_position.StopLoss()==0.0) { //--- modifying the position if(m_trade.PositionModify(Symbol(),sl,tp)) printf("Long position by %s to be modified",Symbol()); else { printf("Error modifying position by %s : '%s'",Symbol(),m_trade.ResultComment()); printf("Modify parameters : SL=%f,TP=%f",sl,tp); } //--- was modified and must exit the Expert Advisor res=true; } } } //--- result return(res); } //+------------------------------------------------------------------+ //| Modifying the short position | //+------------------------------------------------------------------+ bool CSampleExpert::ShortModified(void) { bool res=false; //--- checking the trailing stop if(InpTrailingStop>0) { if((m_position.PriceOpen()-m_symbol.Ask())>(m_adjusted_point*InpTrailingStop)) { double sl=NormalizeDouble(m_symbol.Ask()+m_traling_stop,m_symbol.Digits()); double tp=m_position.TakeProfit(); if(m_position.StopLoss()>sl || m_position.StopLoss()==0.0) { //--- modifying the position if(m_trade.PositionModify(Symbol(),sl,tp)) printf("Short position by %s to be modified",Symbol()); else { printf("Error modifying position by %s : '%s'",Symbol(),m_trade.ResultComment()); printf("Modify parameters : SL=%f,TP=%f",sl,tp); } //--- was modified and must exit the Expert Advisor res=true; } } } //--- result return(res); } //+------------------------------------------------------------------+ //| Checking conditions for opening a long position | //+------------------------------------------------------------------+ bool CSampleExpert::LongOpened(void) { bool res=false; level_stoch=InpLevel_S; //--- checking the possibility to open a long (BUY) position if(stoch_ind_1<level_stoch && stoch_ind_0>level_stoch && macd_ind_1>macd_ind_0 && ma_ind<close[1] && ma_ind<open[1])//&& ma_ind<close[1] && ma_ind<open[1] { //--- exit the EA res=true; } //--- result return(res); } //+------------------------------------------------------------------+ //| Opening a Buy Stop order | //+------------------------------------------------------------------+ bool CSampleExpert::OpenBuyStop(void) { bool res=false; double tp=0,sl=0; //--- if(LongOpened()) { res=true; //--- declare and initialize the trade request and result MqlTradeRequest request={0}; MqlTradeResult result={0}; //--- pending order placing parameters request.action =TRADE_ACTION_PENDING; // trading operation type request.symbol =Symbol(); // symbol request.deviation=5; // allowed deviation from the price request.volume =InpLots; // volume in lots request.magic =EXPERT_MAGIC; // order MagicNumber double offset=InpOffset; // point distance from the current price to place the order double price; // order trigger price double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); // point size int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // number of decimal places (digits) //--- operation type request.type=ORDER_TYPE_BUY_STOP; // order type price=high[1]+offset*m_adjusted_point; // open price request.price=NormalizeDouble(price,digits); // normalized open price tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // add the new Stop Loss value to the structure request.tp =NormalizeDouble(tp,_Digits); // add the new Take Profit value to the structure //--- sending a request if(!OrderSend(request,result)) {res=false;printf("OrderSend error %d",GetLastError());} // if unable to send the request, output the error code //--- information about the operation printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- exit the EA } //--- result return(res); } //+------------------------------------------------------------------+ //| Checking conditions for opening a short position | //+------------------------------------------------------------------+ bool CSampleExpert::ShortOpened(void) { bool res=false; level_stoch=100-InpLevel_S; //--- checking the possibility of a short position (SELL) if(stoch_ind_1>level_stoch && stoch_ind_0<level_stoch && macd_ind_1<macd_ind_0 && ma_ind>close[1] && ma_ind>open[1]) { //--- exit the EA res=true; } //--- result return(res); } //+------------------------------------------------------------------+ //| Opening a Sell Stop order | //+------------------------------------------------------------------+ bool CSampleExpert::OpenSellStop(void) { bool res=false; double tp=0,sl=0; //--- if(ShortOpened()) { res=true; //--- declare and initialize the trade request and result MqlTradeRequest request={0}; MqlTradeResult result={0}; //--- pending order placing parameters request.action =TRADE_ACTION_PENDING; // trading operation type request.symbol =Symbol(); // symbol request.deviation=5; // allowed deviation from the price request.volume=InpLots; // volume in lots request.magic=EXPERT_MAGIC; // order MagicNumber int offset=InpOffset; // point distance from the current price to place the order double price; // order trigger price int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // number of decimal places (digits) request.type=ORDER_TYPE_SELL_STOP; // order type price=low[1]-offset*m_adjusted_point; // open price request.price=NormalizeDouble(price,digits); // normalized open price tp=price-m_take_profit; sl=price+m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // add the new Stop Loss value to the structure request.tp =NormalizeDouble(tp,_Digits); // add the new Take Profit value to the structure //--- sending a request if(!OrderSend(request,result)) {res=false;printf("OrderSend error %d",GetLastError());} // if unable to send the request, output the error code //--- information about the operation printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- exit the EA } //--- result return(res); } //+------------------------------------------------------------------+ //| The main function returns true if any of the positions | //| is being processed | //+------------------------------------------------------------------+ bool CSampleExpert::Processing(void) { // MqlDateTime dt; //--- Update frequency if(!m_symbol.RefreshRates()) return(false); //--- Update indicators if(BarsCalculated(handle_Stochastic)<barsCalculated) return(false); if(CopyBuffer(handle_Stochastic,0,0,barsCalculated,stoch_buff)!=barsCalculated) return(false); //--- if(BarsCalculated(handle_MACD)<barsCalculated) return(false); if(CopyBuffer(handle_MACD,0,0,barsCalculated,macd_buff)!=barsCalculated) return(false); //--- if(BarsCalculated(handle_MA)<barsCalculated) return(false); if(CopyBuffer(handle_MA,0,0,barsCalculated,ma_buff)!=barsCalculated) return(false); //--- if(!Copy())return(false); //--- to simplify programming and provide a faster access //--- data are located in internal variables macd_ind_0 = macd_buff[1]; macd_ind_1 = macd_buff[0]; ma_ind = ma_buff[1]; stoch_ind_0 = stoch_buff[1]; stoch_ind_1 = stoch_buff[0]; //--- it is important to correctly exit it ... //--- First check if the position exists - try to select it bool bord=false,sord=false; ulong ticket; //+--- There are open positions and trailing stop is enabled --------+ if(m_position.Select(Symbol()) && PositionsTotal()>0 && m_traling_stop!=0) { if(m_position.PositionType()==POSITION_TYPE_BUY) { bord=true; //--- modifying the long position if(LongModified()) return(true); } else { sord=true; //--- modifying the short position if(ShortModified()) return(true); } } //+--------- trading STOP orders ------------------------------------+ // In this cycle, check all placed pending orders one by one for(int i=0;i<OrdersTotal();i++) { // select each of orders and getting its ticket ticket=OrderGetTicket(i); // handling Buy Stop orders if(m_order.OrderType()==ORDER_TYPE_BUY_STOP) { // setting the flag indicating that there is a Buy Stop order bord=true; //--- It is important to enter the market correctly, move the order if necessary if(bord)OrderModifyBuyStop(); // modify the BUYSTOP order } // handling Sell Stop orders if(m_order.OrderType()==ORDER_TYPE_SELL_STOP) { // setting the flag indicating that there is a Sell Stop order sord=true; //--- It is important to enter the market correctly, move the order if necessary if(sord)OrderModifySellStop(); // modify the SELLSTOP order } } //--- If there are no orders, place --------------------------------+ if(!sord)if(OpenSellStop()){sord=true;return(true);} if(!bord)if(OpenBuyStop()){bord=true;return(true);} //--- exit without position handling return(false); } //+------------------------------------------------------------------+ //|Changing parameters of the earlier placed Sell Stop trading order | //+------------------------------------------------------------------+ bool CSampleExpert::OrderModifySellStop(void) { bool res=true; ulong ticket; double tp=0,sl=0; double offset=InpOffset; // point distance from the current price to place the order double price; // order trigger price int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // number of decimal places (digits) // declare and initialize the trade request and result MqlTradeRequest request={0}; MqlTradeResult result={0}; int total=OrdersTotal(); // number of pending orders //--- iterate over all placed pending orders for(int i=total-1; i>=0; i--) { // select each of orders and getting its ticket ticket=OrderGetTicket(i); // handling Sell Stop orders if(m_order.OrderType()==ORDER_TYPE_SELL_STOP) { ulong magic=OrderGetInteger(ORDER_MAGIC); // order MagicNumber //--- if MagicNumber matches if(magic==EXPERT_MAGIC) { price=low[1]-offset*m_adjusted_point; // open price if(price>m_order.PriceOpen()) // check the conditions if(low[1]>low[2]) // check the conditions { request.action=TRADE_ACTION_MODIFY; // trading operation type request.order = OrderGetTicket(i); // order ticket request.symbol =Symbol(); // symbol request.deviation=InpOffset; // allowed deviation from the price price=low[1]-offset*m_adjusted_point; // open price request.price=NormalizeDouble(price,digits); // normalized open price tp=price-m_take_profit; sl=price+m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // add the new Stop Loss value to the structure request.tp =NormalizeDouble(tp,_Digits); // add the new Take Profit value to the structure //--- sending a request if(!OrderSend(request,result)) { // setting the flag indicating that the Sell Stop order has not been deleted res=true; printf("OrderSend error %d",GetLastError()); } // if unable to send the request, output the error code //--- information about the operation printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } } } return(res); } //+------------------------------------------------------------------+ //|Changing parameters of the earlier placed Buy Stop trading order | //+------------------------------------------------------------------+ bool CSampleExpert::OrderModifyBuyStop(void) { bool res=true; ulong ticket; double tp=0,sl=0; double offset=InpOffset; // point distance from the current price to place the order double price; // order trigger price int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // number of decimal places (digits) //--- declare and initialize the trade request and result MqlTradeRequest request={0}; MqlTradeResult result={0}; int total=OrdersTotal(); // number of pending orders //--- iterate over all placed pending orders for(int i=total-1; i>=0; i--) { // select each of orders and getting its ticket ticket=OrderGetTicket(i); // handling Buy Stop orders if(m_order.OrderType()==ORDER_TYPE_BUY_STOP) { ulong magic=OrderGetInteger(ORDER_MAGIC); // order MagicNumber //--- if MagicNumber matches if(magic==EXPERT_MAGIC) { price=high[1]+offset*m_adjusted_point; // open price if(price<m_order.PriceOpen()) // check the conditions { request.action=TRADE_ACTION_MODIFY; // trading operation type request.symbol =Symbol(); // symbol request.action=TRADE_ACTION_MODIFY; // trading operation type request.order = OrderGetTicket(i); // order ticket request.symbol =Symbol(); // symbol request.deviation=InpOffset; // allowed deviation from the price //--- set the price level, take profit and stop loss request.price=NormalizeDouble(price,digits); // normalized open price tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // add the new Stop Loss value to the structure request.tp =NormalizeDouble(tp,_Digits); // add the new Take Profit value to the structure //--- sending a request if(!OrderSend(request,result)) { // setting the flag indicating that the Buy Stop order has not been deleted res=true; printf("OrderSend error %d",GetLastError()); } // if unable to send the request, output the error code //--- information about the operation printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } } } return(res); } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- creating all required object if(!ExtExpert.Init()) return(INIT_FAILED); //--- ok return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Tick processing function | //+------------------------------------------------------------------+ void OnTick(void) { static datetime limit_time=0; // the last trade processing time + timeout //--- do not handle is timeout if(TimeCurrent()>=limit_time) { //--- checking data if(Bars(Symbol(),Period())>barsCalculated) { //--- checking the limit time in terms of timeout in seconds, if processed if(ExtExpert.Processing()) limit_time=TimeCurrent()+ExtTimeOut; } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CSampleExpert::Copy(void) { //--- amount to copy int copied=3; //--- ArrayResize(high,copied); ArrayResize(low,copied); ArrayResize(close,copied); ArrayResize(open,copied); //+------- Setting the array indexing direction ---------------------+ ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); ArraySetAsSeries(open,true); //--- copying bar prices if(CopyHigh(NULL,mas_tf[0],0,copied,high)<0) {printf("No copied High",GetLastError());return(false);} //--- if(CopyLow(NULL,mas_tf[0],0,copied,low)<0) {printf("No copied Low",GetLastError());return(false);} //--- if(CopyClose(NULL,mas_tf[2],0,copied,close)<0) {printf("No copied Close",GetLastError());return(false);} //--- if(CopyOpen(NULL,mas_tf[2],0,copied,open)<0) {printf("No copied Open",GetLastError());return(false);} //--- return(true); } //+------------------------------------------------------------------+
El uso de tales herramientas da la imagen siguiente:
Fig. 18. Simulación del asesor con nuestras herramientas MTF
Conclusión
A pesar de que los autores de los módulos programáticos de MQL5 no han previsto la posibilidad de crear tales algoritmos directamente, estos indicadores tienen razón de ser. En algunas situaciones, tales como la posibilidad de analizar simultáneamente el estado del mercado en MT diferentes, el aumento de la efectividad del funcionamiento del simulador de estrategias y el aumento de la calidad de suavizado, son insustituibles. Estas variantes de código también tinen algunos defectos, lo cual abre un amplio campo de trabajo respecto al lenguaje de programación MQL.
Archivos adjuntos.
Nombre | Tipo | Ruta |
---|---|---|
MA_MultiPeriod | Trend | MQL5\Indicators\MA_MultiPeriod.mq5 |
MA_MultiTF | Trend | MQL5\Indicators\MTF\Trend\MA_MultiTF.mq5 |
MTF_BB | Trend | MQL5\Indicators\MTF\Trend\MTF_BB.mq5 |
MTF_Envelopes | Trend | MQL5\Indicators\MTF\Trend\MTF_Envelopes.mq5 |
MTF_ParabolicSAR | Trend | MQL5\Indicators\MTF\Trend\MTF_ParabolicSAR.mq5 |
MTF_StdDev | Trend | MQL5\Indicators\MTF\Trend\MTF_StdDev.mq5 |
MTF_CCI | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_CCI.mq5 |
MTF_Force_Index | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_Force_Index.mq5 |
MTF_MACD | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_MACD.mq5 |
MTF_Momentum | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_Momentum.mq5 |
MTF_RSI | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_RSI.mq5 |
MTF_RVI | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_RVI.mq5 |
MTF_Stochastic | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_Stochastic.mq5 |
MTF_WPR | Oscillators | MQL5\Indicators\MTF\Oscillators\MTF_WPR.mq5 |
Triple Screen Trading System 1.0 | Expert |
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/2837





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso