Indicadores MTF como herramienta de análisis técnico

Alexander Lasygin | 14 junio, 2019

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

En esta variante, no solo podemos ver cómo trabaja el asesor, sino también analizar los posibles defectos de su funcionamiento, lo que cambia ya cardinalmente nuestra actitud respecto a las posibilidades de este simulador.


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