English Русский 中文 Deutsch 日本語 Português
Cómo desarrollar y depurar rápidamente cualquier estrategia de scalping en MetaTrader 5

Cómo desarrollar y depurar rápidamente cualquier estrategia de scalping en MetaTrader 5

MetaTrader 5Ejemplos | 7 septiembre 2016, 10:48
3 395 0
MetaQuotes
MetaQuotes

"No se puede confiar en nadie, pero en mí, sí se puede"  (с) Depurador

Los sistemas automáticos de scalping se consideran por derecho propio la cima del trading automático, y precisamente por ello, son a la vez los más complejos a la hora de escribir el código. En este artículo vamos a mostrar cómo se pueden construir estrategias basadas en el análisis de ticks entrantes con la ayuda de los recursos de depuración incorporados y de la simulación visual. Para desarrollar las reglas de entrada y salida con frecuencia se necesitan años de comercio manual. Pero con la ayuda de MetaTrader 5 usted podrá comprobar cualquier estrategia similar en la historia real.


La idea comercial basada en ticks

Ante todo, necesitamos crear un indicador que construya gráficos de ticks, es decir, gráficos  en los que se puede ver cada cambio de precio. Uno de los primeros indicadores semejantes lo podrá encontrar en la Biblioteca — https://www.mql5.com/es/code/89. A diferencia de los habituales, en los gráficos de ticks, al llegar cualquier tick es necesario desplazar todo el gráfico hacia atrás.


Vamos a tomar como base de la idea comprobada una serie de cambios de precio entre dos ticks consecutivos, será aproximadamente esta secuencia en puntos:

+1, 0, +2, -1, 0, +1, -2, -1, +1, -5, -1, +1, 0, -1, +1, 0, +2, -1, +1, +6, -1, +1,...

La ley de la distribución normal dice que el 99 % de los cambios entre dos ticks cabe los límites de 3 sigmas.  Probaremos en el modo de tiempo real a calcular en cada tick la desviación típica y a marcar los saltos bruscos del precio con signos de color rojo y azul. De esta forma, intentaremos elegir de forma visual una estrategia para utilizar estos saltos bruscos: comerciar en la dirección del cambio o usar el "retorno a la media". Como puede ver, la idea es muy sencilla, y seguro que la mayoría de los amantes de las matemáticas ha recorrido este camino.


Creando el indicador de ticks

En el MetaEditor iniciamos el Wizard MQL, indicamos el nombre  y los dos parámetros de entrada:

  • ticks — cuántos ticks van a usarse para calcular la desviación típica.
  • gap — coeficiente para obtener el intervalo en sigmas.

A continuación, marcamos "Indicador en una ventana aparte" e indicamos las 2 construcciones gráficas que van a representar la información en la subventana: la línea para ticks y las flechas de colores para las señales sobre la aparición de cambios bruscos de precio.


Introducimos en el molde obtenido los cambios marcados en amarillo

//+------------------------------------------------------------------+
//|                                              TickSpikeHunter.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   2
//--- plot TickPrice
#property indicator_label1  "TickPrice"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot Signal
#property indicator_label2  "Signal"
#property indicator_type2   DRAW_COLOR_ARROW
#property indicator_color2  clrRed,clrBlue,C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0'
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- input parameters
input int      ticks=50;         // número de ticks en los cálculos
input double   gap=3.0;          // anchura del canal en sigmas
//--- indicator buffers
double         TickPriceBuffer[];
double         SignalBuffer[];
double         SignalColors[];
//--- contador de cambios de precio
int ticks_counter;
//--- primera llamada del indicador
bool first;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,TickPriceBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,SignalBuffer,INDICATOR_DATA);
   SetIndexBuffer(2,SignalColors,INDICATOR_COLOR_INDEX);
//--- indicamos los valores vacíos que se deben ignorar durante el dibujado  
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);
//--- mostraremos las señales con la forma de este signo
   PlotIndexSetInteger(1,PLOT_ARROW,159);
//--- inicialización de las variables globales
   ticks_counter=0;
   first=true;
//--- el programa se ha inicializado con éxito
   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[])
  {
//---
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Ahora solo queda añadir el código al manejador predeterminado de ticks entrantes OnCalculate(). Con la primera llamada de la función reseteamos los valores en los búferes de indicador, y también para mayor comodidad definimos para ellos el atributo de la serie temporal, de esta forma, la indexación la harán de derecha a izquierda. Esto permitirá dirigirse al valor más reciente del búfer de indicador por el índice cero, es decir, en TickPriceBuffer[0] se guardará el valor del último  tick.

Además,  el procesamiento principal de los ticks lo introduciremos en la función aparte ApplyTick():

//+------------------------------------------------------------------+
//| 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[])
  {
//--- con la primera llamada, reseteamos los búferes de indicador y establecemos el atributo de la serie
   if(first)
     {
      ZeroMemory(TickPriceBuffer);
      ZeroMemory(SignalBuffer);
      ZeroMemory(SignalColors);
      //--- las matrices de la serie van de delante hacia atrás, así es más cómodo en este caso
      ArraySetAsSeries(SignalBuffer,true);
      ArraySetAsSeries(TickPriceBuffer,true);
      ArraySetAsSeries(SignalColors,true);
      first=false;
     }
//--- tomamos como precio el valor Close actual
   double lastprice=close[rates_total-1];
//--- calculamos los ticks
   ticks_counter++;
   ApplyTick(lastprice); // realizamos los cálculos y el desplazamiento en los búferes   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| adopta el tick para los cálculos                                 |
//+------------------------------------------------------------------+
void ApplyTick(double price)
  {
   int size=ArraySize(TickPriceBuffer);
   ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1);
   ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1);
   ArrayCopy(SignalColors,SignalColors,1,0,size-1);
//--- anotamos el último valor del precio
   TickPriceBuffer[0]=price;
//---
  }

La función ApplyTick() por ahora solo realiza las acciones más sencillas: desplaza todos los valores del búfer en una posición en la historia y anota en TickPriceBuffer[0] el último tick. Iniciamos la depuración en el indicador y observamos durante algún tiempo.

Podemos ver que el precio Bid conforme al que se ha construido Close de la vela actual permanece inmutable con mucha frecuencia, y por eso el gráfico se dibuja con trozos en forma de "meseta". Vamos a corregir un poco el código para conseguir solo una forma de "sierra", para que se haga más comprensible a la vista.

//--- calculamos solo si el precio ha cambiado
   if(lastprice!=TickPriceBuffer[0])
     {
      ticks_counter++;      // contamos los ticks
      ApplyTick(lastprice); // realizamos los cálculos y el desplazamiento en los búferes
     }

Bien, ya hemos creado la primera versión del indicador, ahora ya no tenemos crecimiento cero del precio.


Añadimos un búfer auxiliar y el cálculo de la desviación típica

Para calcular la desviación necesitamos una matriz adicional que guardará los incrementos de precio en cada tick. En calidad de matriz de este tipo, añadiremos un búfer de indicador más, así como el código correspondiente  en los lugares necesarios:

#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2
...
//--- indicator buffers
double         TickPriceBuffer[];
double         SignalBuffer[];
double         DeltaTickBuffer[];
double         ColorsBuffers[];
...
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,TickPriceBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,SignalBuffer,INDICATOR_DATA);
   SetIndexBuffer(2,SignalColors,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(3,DeltaTickBuffer,INDICATOR_CALCULATIONS);
...
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const ...)

//--- con la primera llamada, reseteamos los búferes de indicador y establecemos el atributo de la serie
   if(first)
     {
      ZeroMemory(TickPriceBuffer);
      ZeroMemory(SignalBuffer);
      ZeroMemory(SignalColors);
      ZeroMemory(DeltaTickBuffer);
      //--- las matrices de la serie van de delante hacia atrás, así es más cómodo en este caso
      ArraySetAsSeries(TickPriceBuffer,true);
      ArraySetAsSeries(SignalBuffer,true);
      ArraySetAsSeries(SignalColors,true);
      ArraySetAsSeries(DeltaTickBuffer,true);
      first=false;
     }
...
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| adopta el tick para los cálculos                                 |
//+------------------------------------------------------------------+
void ApplyTick(double price)
  {
   int size=ArraySize(TickPriceBuffer);
   ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1);
   ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1);
   ArrayCopy(SignalColors,SignalColors,1,0,size-1);  
   ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,size-1);
//--- anotamos el último valor del precio
   TickPriceBuffer[0]=price;
//--- calculamos la diferencia con el valor anterior
   DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1];
//--- obtenemos la  desviación típica
   double stddev=getStdDev(ticks);  

Ahora estamos preparados para calcular la desviación típica. Primero escribimos la función getStdDev(), que hace todos los cálculos directamente, recorriendo todos los elementos de la matriz solamente los ciclos necesarios.

//+------------------------------------------------------------------+
//| calcula directamente la desviación estándar                      |
//+------------------------------------------------------------------+
double getStdDev(int number)
  {
   double summ=0,sum2=0,average,stddev;
//--- calculamos la suma de los cambios y calculamos la esperanza matemática
   for(int i=0;i<ticks;i++)
      summ+=DeltaTickBuffer[i];
   average=summ/ticks;
//--- ahora calculamos la desviación típica
   sum2=0;
   for(int i=0;i<ticks;i++)
      sum2+=(DeltaTickBuffer[i]-average)*(DeltaTickBuffer[i]-average);
   stddev=MathSqrt(sum2/(number-1));
   return (stddev);
  }

Después, terminamos de escribir el bloque responsable de la colocación de señales en el gráfico ticks, es decir, de poner círculos de color rojo y azul

//+------------------------------------------------------------------+
//| adopta el tick para los cálculos                                 |
//+------------------------------------------------------------------+
void ApplyTick(double price)
  {
   int size=ArraySize(TickPriceBuffer);
   ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,size-1);
   ArrayCopy(SignalBuffer,SignalBuffer,1,0,size-1);
   ArrayCopy(SignalColors,SignalColors,1,0,size-1);
   ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,size-1);   
//--- anotamos el último valor del precio
   TickPriceBuffer[0]=price;
//--- calculamos la diferencia con el valor anterior
   DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1];   
//--- obtenemos la  desviación típica
   double stddev=getStdDev(ticks);   
//--- si el cambio de precio ha superado el umbral establecido
   if(MathAbs(DeltaTickBuffer[0])>gap*stddev) // con el primer tick se mostrará la señal, lo dejamos como característica
     {
      SignalBuffer[0]=price;     // ponemos un punto
      string col="Red";          // por defecto, un punto de color rojo
      if(DeltaTickBuffer[0]>0)   // el precio ha aumentado bruscamente
        {
         SignalColors[0]=1;      // entonces un punto de color azul
         col="Blue";             // lo recordamos para mostrarlo en el registro
        }
      else                       // el precio ha caído bruscamente
      ignalColors[0]=0;         // punto de color azul
      //--- mostramos la entrada en el diario de Expertos
      PrintFormat("tick=%G change=%.1f pts, trigger=%.3f pts,  stddev=%.3f pts %s",
                  TickPriceBuffer[0],DeltaTickBuffer[0]/_Point,gap*stddev/_Point,stddev/_Point,col);
     }
   else SignalBuffer[0]=0;       // no hay señal      
//---
  }

Pulsamos el botón F5 (Comienzo de la depración/continuación de la ejecución) y observamos en el terminal MetaTrader 5 cómo funciona nuestro indicador.

Ahora ha llegado el momento de la depuración del código, que permitirá detectar los errores y acelerar el funcionamiento del programa.


Perfilado del código para acelerar el funcionamiento

Para los programas que funcionan en el modo de tiempo real, la velocidad de ejecución es de capital importancia. El entorno de desarrollo del MetaEditor permite de forma cómoda y rápida valorar el gasto de tiempo en la ejecución de este u otro fragmento de programa. Para ello es imprescindible iniciar el perfilado de código y dejar funcionar al programa un cierto tiempo. Para el perfilado del indicador bastará con un minuto.


Como puede ver, la mayor parte del tiempo se ha invertido en el procesamiento de la función ApplyTick(), que ha sido llamada 41 veces desde la función OnCalculate(). El propio OnCalculate() ha sido llamado 143 veces, pero solo en 41 casos el precio en el tick anterior se ha diferenciado del precio del anterior. Además, en la propia función ApplyTick(), la mayor parte del tiempo la han ocupado las llamadas de las funciones ArrayCopy(), que ejecutan solo acciones auxiliares y no realizan cálculos, para los cuales se ha inventado este indicador. El cálculo de la desviación típica en la línea 111 de código solo ha ocupado el 0.57% del tiempo total de ejecución del programa. 

Vamos a intentar reducir los gastos improductivos, para ello, probaremos a no copiar todos los elementos de las matrices  (TickPriceBuffer и т.д), sino solo los 200 últimos. Y es que en el gráfico bastará con ver los 200 últimos valores, además, el número de ticks en una sesión comercial puede alcanzar las decenas o centenares de miles. No hay necesidad de verlos todos. Por eso introduciremos el parámetro de entrada shift=200, que establece  el número de valores desplazables. Añada al código las líneas destacadas en amarillo:

//--- input parameters
input int      ticks=50;         // número de ticks en los cálculos
input int      shift=200;        // número de valores desplazados
input double   gap=3.0;          // anchura del canal en sigmas
...
void ApplyTick(double price)
  {
//--- cuántos elementos desplazamos en los búferes de indicador en cada tick
   int move=ArraySize(TickPriceBuffer)-1;
   if(shift!=0) move=shift;
   ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,move);
   ArrayCopy(SignalBuffer,SignalBuffer,1,0,move);
   ArrayCopy(SignalColors,SignalColors,1,0,move);
   ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,move);


Iniciamos de nuevo el perfilador y vemos un nuevo resultado: el tiempo de copiado de las matrices ha caído en cientos o miles de veces, ahora el tiempo principal lo ocupa la llamada de StdDev(), que es responsable del cálculo de la desviación típica.


De esta forma, hemos acelerado el funcionamiento de la función ApplyTick() varias veces, lo que nos proporciona un ahorro considerable al optimizar la estrategia y cuando el programa funcione en el modo de tiempo real.  Y es que los recursos de cálculo nunca están de sobra.


Optimización analítica del código

A veces incluso un código que se haya escrito de forma óptima puede aumentar su velocidad de funcionamiento. En este caso, el cálculo de la desviación típica se puede acelerar si reescribimos un poco la fórmula. 


De esta forma, podemos simplemente calcular el cuadrado de la suma y la suma de los cuadrados del incremento: esto nos permitirá realizar menos operaciones matemáticas en cada tick.  En cada tick sencillamente quitamos el elemento que cae de la matriz y añadimos el elemento de entrada de la matriz a las variables que contienen las sumas.

Creamos la nueva función getStdDevOptimized(), en la que usamos el método ya conocido de desplazamiento de los valores de la matriz dentro sí misma.

//+------------------------------------------------------------------+
//| calcula la desviación típica según las fórmulas                  |
//+------------------------------------------------------------------+
double getStdDevOptimized(int number)
  {
//---
   static double X2[],X[],X2sum=0,Xsum=0;
   static bool firstcall=true;
//--- primera llamada
   if(firstcall)
     {
      //--- indicamos el tamaño de las matrices dinámicas en 1 unidad más que el número de ticks
      ArrayResize(X2,ticks+1);
      ArrayResize(X,ticks+1);
      //--- nos garantizamos valores cero al inicio de los cálculos
      ZeroMemory(X2);
      ZeroMemory(X);

      firstcall=false;
     }
//--- desplazamos las matrices
   ArrayCopy(X,X,1,0,ticks);
   ArrayCopy(X2,X2,1,0,ticks);
//--- calculamos los nuevos valores de las sumas
   X[0]=DeltaTickBuffer[0];
   X2[0]=DeltaTickBuffer[0]*DeltaTickBuffer[0];
//--- calculamos las nuevas sumas
   Xsum=Xsum+X[0]-X[ticks];
   X2sum=X2sum+X2[0]-X2[ticks];
//--- cuadrado de la desviación típica
   double S2=(1.0/(ticks-1))*(X2sum-Xsum*Xsum/ticks);
//--- calculamos la suma de los ticks y calculamos la esperanza matemática
   double stddev=MathSqrt(S2);
//---
   return (stddev);
  } 

Añadimos a la función ApplyTick() el cálculo de la desviación típica mediante el segundo método a través de la función getStdDevOptimized() e iniciamos de nuevo el perfilado.

//--- calculamos la diferencia con el valor anterior
   DeltaTickBuffer[0]=TickPriceBuffer[0]-TickPriceBuffer[1];
//--- obtenemos la  desviación típica
   double stddev=getStdDev(ticks);
   double std_opt=getStdDevOptimized(ticks);

Resultado de la ejecución:


Podemos ver que la nueva función getStdDevOptimized() necesita la mitad de tiempo, un 7,12%, que el cálculo directo en getStdDev(), un 15,50%. Asimismo, recomendamos echar un vistazo al artículo 3 Métodos de Aceleración de Indicadores según el Ejemplo de Regresión Lineal.

Por cierto, volviendo a la llamada de las funciones estándar, en este indicador obtenemos el precio de la serie temporal close[], que se construye conforme a los precios Bid. Hay otros dos métodos más para obtener este precio, con la ayuda de las funciones SymbolInfoDouble() y SymbolInfoTick(). Vamos a añadir estas llamadas al código y a hacer de nuevo el perfilado.


Como puede ver, aquí también hay diferencia en lo que respecta a la velocidad de funcionamiento. Y es posible entenderlo, puesto que la lectura del precio preparado de close[] no necesita gastos en comparación con la llamada de las funciones universales.

Depuración con ticks reales en el simulador

Al escribir indicadores y robots comerciales no resulta posible prever todas las situaciones posibles que pueden suceder al trabajar online. Por suerte, el MetaEditor permite también realizar la depuración con datos históricos. Solo tiene que iniciar la depuración en el modo de simulación visual y podrá comprobar su programa en el intervalo de historia designado. Usted podrá acelerar, detener o hacer retroceder la simulación hasta la fecha que necesite.

Importante: en la ventana de Depuración indique el modo de modelado "Cada tick basado en ticks reales". Esto le permitirá usar para la depuración las cotizaciones anotadas realmente y que guarda el servidor comercial. En el primer inicio de la simulación se cargarán de forma automática en su computadora.


Si estos parámetros no se establecen en el MetaEditor, entonces en el modo visual se usarán los ajustes del simulador por defecto. Indique en ellos el modo "Cada tick basado en ticks reales".



Podemos ver que en el gráfico de ticks aparecen cortes extraños. Significa que se ha cometido algún error en el algoritmo. No sabemos cuánto tiempo haría falta para realizar su simulación en tiempo real. En este caso, según muestra el Diario de simulación visual, se ve que estos extraños cortes surgen en el momento en que aperece una barra. ¡Exacto! — hemos olvidado que al pasar a una nueva barra, el tamaño de los búferes de indicador aumenta en 1 de forma automática. Introducimos correcciones en el código:

void ApplyTick(double price)
  {
//--- recordaremos el tamaño de la matriz TickPriceBuffer, que es igual al número de barras en el gráfico
   static int prev_size=0;
   int size=ArraySize(TickPriceBuffer);
//--- si el tamaño de los búferes de indicador no ha cambiado, entonces desplazamos todos los elementos en 1 posición hacia atrás
   if(size==prev_size)
     {
      //--- cuántos elementos desplazamos en los búferes de indicador en cada tick
      int move=ArraySize(TickPriceBuffer)-1;
      if(shift!=0) move=shift;
      ArrayCopy(TickPriceBuffer,TickPriceBuffer,1,0,move);
      ArrayCopy(SignalBuffer,SignalBuffer,1,0,move);
      ArrayCopy(SignalColors,SignalColors,1,0,move);
      ArrayCopy(DeltaTickBuffer,DeltaTickBuffer,1,0,move);
     }
   prev_size=size;
//--- anotamos el último valor del precio
   TickPriceBuffer[0]=price;
//--- calculamos la diferencia con el valor anterior

Iniciamos la simulación visual y ponemos un punto de interrupción para captar el momento de apertura de una nueva barra. Añadimos los valores a observar y nos aseguramos de que lo hemos hecho todo bien: el número de barras en el gráfico ha aumentado en una unidad, el volumen de ticks de la barra actual es igual a 1, se trata del primer tick de la nueva barra.


Pues bueno, ya hemos realizado la optimización del código, hemos corregido los errores, hemos medido el tiempo de ejecución de diferentes funciones, y ahora el indicador está listo para funcionar. Podemos iniciar la simulación visual y observar qué sucede después de que las señales aparezcan en el gráfico de ticks. ¿Podemos mejorar algo más en el código del indicador? ¡Un perfeccionista del código diría que sí! Aún no hemos intentado usar el búfer circular para acelerar el trabajo.  Quien así lo desee, podrá comprobar por sí mismo si esto aumentará la productividad.


MetaEditor es el laboratorio comercial para el desarrollo de estrategias comerciales

Para escribir un sistema comercial automático es importante tener, no solo un entorno cómodo de desarrollo y un potente lenguaje de programación, sino también herramientas adicionales para depurar y calibrar cualquier programa.  En este artículo, lo hemos mostrado de la siguiente forma:

  1. hemos creado un gráfico de ticks en un par de minutos en la primera aproximación;
  2. hemos usado en el gráfico la depuración en el modo de tiempo real, con la ayuda de la tecla F5;
  3. hemos iniciado el perfilado para detectar los lugares inefectivos en el código;
  4. hemos realizado una depuración rápida basándonos en datos históricos en el modo de simulación visual;
  5. hemos analizado los valores de las variables necesarias durante el proceso de depuración.

El desarrollo de un indicador que muestre señales comerciales, con frecuencia es el primer paso imprescindible a la hora de escribir un robot comercial. La visualización ayuda a elaborar reglas comerciales o bien a rebatir una idea antes incluso de comenzar a trabajar en su proyecto.

¡Use todas las posibilidades del entorno de desarrollo de MetaEditor para crear robots comerciales efectivos!

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

Archivos adjuntos |
Asesor experto multiplataforma: reutilizando los componentes de la Biblioteca Estándar MQL5 Asesor experto multiplataforma: reutilizando los componentes de la Biblioteca Estándar MQL5
En la Biblioteca Estándar MQL5 hay ciertos componentes que pueden resultar útiles en las versiones de los asesores expertos multiplataforma para MQL4. En esta artículo analizaremos los métodos de creación de ciertos componentes de la Biblioteca Estándar MQL5 que son compatibles con el compilador MQL4.
Red neuronal profunda con Stacked RBM. Auto-aprendizaje, auto-control Red neuronal profunda con Stacked RBM. Auto-aprendizaje, auto-control
El artículo es la continuación de artículos anteriores sobre neuroredes profundas y elección de predictores. En este veremos las particularidades de una neurored iniciada con Stacked RBM, así como su implementación en el paquete "darch".
Recetas MQL5 - Señales comerciales de los canales móviles Recetas MQL5 - Señales comerciales de los canales móviles
En el artículo se muestra el proceso de desarrollo e implemementación de una clase-señalizadora en base a los canales móviles. A cada versión de la señal le sigue una estrategia comercial con los resultados de la simulación. Para crear las clases derivadas se usan las ​​clases de Biblioteca estándar.
Interfaces gráficas X: Actualizaciones para la librería Easy And Fast (build 2) Interfaces gráficas X: Actualizaciones para la librería Easy And Fast (build 2)
Desde la anterior publicación del artículo de esta serie, la librería Easy And Fast ha adquirido nuevas posibilidades. Ha sido hecha la optimización parcial del esquema y del código de la librería, eso ha reducido un poco el consumo de recursos de la CPU. Algunos métodos que se repiten con frecuencia en muchas clases de los controles han sido traspasados a la clase base CElement.