English Русский 中文 Deutsch 日本語 Português
Cómo crear un indicador de gráficos no estándar para MetaTrader Market

Cómo crear un indicador de gráficos no estándar para MetaTrader Market

MetaTrader 4Ejemplos | 9 agosto 2016, 15:30
2 890 0
Vladimir Karputov
Vladimir Karputov

Índice

 

Desde las velas japonesas a los gráficos Renko

A día de hoy, el tipo de gráfico más popular entre los tráders es el de velas japonesas, que ayuda a valorar fácilmente la situación actual en el mercado. Los gráficos de velas dan una estupenda imagen visual del desarrollo del precio durante el intervalo de tiempo abarcado por la vela. Pero algunos tráders consideran una desventaja que los gráficos tengan un componente temporal, y prefieren trabajar solo con el cambio de precio. Así aparecieron los gráficos de "Punto-figura", "Renko", "Kagi", "Range bars", equivolumen, etc. 

Con la ayuda de gráficos offline, de la programación en el lenguaje MQL4 y un poco de experiencia, usted podrá conseguir todos estos gráficos en MetaTrader 4. Existe la posibilidad de crear gráficos con instrumentos sintéticos propios (que no tenga el bróker, o que incluso ni existan) y marcos temporales no estándar, que no existen en la plataforma. La mayoría de los desarrolladores usan para ello las llamadas DLL y esquemas complejos. En este artículo, explicaremos cómo crear indicadores del tipo "dos en uno" de cualquier complejidad,  y que, no solo no necesiten de conocimientos de DLL, sino que puedan ser fácilmente publicados en el Mercado en forma de producto, puesto que son aplicaciones totalmente finalizadas e independientes.

Podrá encontrar y descargar ejemplos de este artículo en forma de aplicaciones gratuitas del Mercado:

  • USDx Chart MT4 - el indicador "USDx Chart MT4" construye un gráfico autónomo en el que, en lugar de las barras y velas acostumbradas, se dibuja el índice del dólar. 
  • Renko Chart MT4 - el indicador Renko Chart MT4 crea un gráfico autónomo Renko en el que todas las barras tiene el aspecto de "ladrillos" Renko. Los propios "ladrillos" no tienen sombra, y el tamaño del "ladrillo" se establece en los ajustes.

El indicador que crea el gráfico autónomo del símbolo y/o periodo no estándar no tiene necesidad de llamar DLL, todo se resuelve con los recursos de MQL4. Para ello, hay que aplicar el siguiente esquema de trabajo: el propio indicador puede funcionar tanto como gráfico online, como de gráfico offline. Además, el indicador cambiará su funcionalidad dependiendo de donde funcione: en un gráfico online o de modo autónomo.

En el gráfico online, el indicador funciona en el modo de "mantenimiento": reúne y compone las cotizaciones, crea un gráfico autónomo (tanto con un periodo estándar, como no estándar), y lo actualiza. En un gráfico autónomo, este indicador funciona de la misma forma que cualquier otro, analiza las cotizaciones y  construye diferentes objetos y figuras. Destacaremos que todos los ejemplos en el artículo se basan en el nuevo script estándar PeriodConverter.mq4.


1. Indicador "IndCreateOffline", que creará un gráfico autónomo

Pondremos al indicador el nombre IndCreateOffline. En el indicador solo habrá un parámetro de entrada, que será responsable del periodo del gráfico autónomo. Hablaremos de él un poco más abajo. El indicador IndCreateOffline cumplirá solo una tarea: crear un archivo *.hst y abrir el gráfico autónomo.

En el indicador se usarán dos funciones principales. La primera se usa una vez, en ella se crea el propio archivo *.hst y se da forma al encabezamiento del archivo. La segunda función sirve para anotar las cotizaciones en el archivo *.hst.

1.1. Editando los encabezamientos del indicador

Incluso si nos da pereza hacerlo directamente, aun así debemos añadir una descripción al inicio del archivo.  Cuando haya pasado cierto tiempo, nos ayudará a recordar cuál es la misión de dicho indicador.

#property version   "1.00"
#property description "The indicator creates an offline chart"
#property strict
#property indicator_chart_window

Por supuesto que, en teoría, un gráfico autónomo se puede crear con cualquier periodo. Sin embargo, vamos a limitar nuestra desbocada fantasía de tráder al caso en el que a alguien le interese un periodo, digamos de 10000000. Vamos a mostrar una enumeración en la que se pueden elegir solo cuatro variantes: dos, tres, cuatro y seis minutos:

#property indicator_chart_window
//+------------------------------------------------------------------+
//| Enumerations of periods offline chart                            |
//+------------------------------------------------------------------+
enum ENUM_OFF_TIMEFRAMES
  {
   M2=2,                      // period M2
   M3=3,                      // period M3
   M4=4,                      // period M4
   M6=6,                      // period M6
  };
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |

El siguiente paso es añadir al encabezamiento las variables globales (no las confunda con las variables globales del terminal):

   M6=6,                      // period M6
  };
//--- input parameter
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     crash=false;         // false -> error in the code
int      HandleHistory=-1;    // handle for the opened "*.hst" file
datetime time0;               //
ulong    last_fpos=0;         //
long     last_volume=0;       //
int      periodseconds;       //
int      i_period;            //
MqlRates rate;                //
long     ChartOffID=-1;       // ID of the offline chart
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |

1.2. Control del tipo de gráfico

Nuestro indicador IndCreateOffline debe iniciarse solo en un gráfico online, porque solo así se puede asegurar que los datos sean correctos. Podemos determinar el tipo de gráfico en el que se ha colocado el indicador con la ayuda de la propiedad CHART_IS_OFFLINE. Añadimos después de OnCalculate() la función IsOffline, que retornará el tipo de gráfico:

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+ 
//| The function checks offline mode of the chart                    | 
//+------------------------------------------------------------------+ 
bool IsOffline(const long chart_ID=0)
  {
   bool offline=ChartGetInteger(chart_ID,CHART_IS_OFFLINE);
   return(offline);
  }
//+------------------------------------------------------------------+

La llamada de la función IsOffline la haremos en OnInit():

int OnInit()
  {
   if(!IsOffline(ChartID()) && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//---
   return(INIT_SUCCEEDED);
  }

Preste atención a que si el indicador se encuentra en un gráfico online (IsOffline(ChartID())==false) de un periodo que no sea igual a PERIOD_M1, entonces en ese caso a la variable crash se le asigna el valor true. Qué nos da esto: con crash==true el indicador se quedará en el gráfico online, pero no hará nada. En este caso, recibiremos este mensaje en la pestaña "Expertos":

IndCreateOffline EURUSD,H1: The period on the online chart must be "M1"!

Sin embargo, el propio indicador permanecerá en el gráfico online y esperará a que el usuario cambie el periodo a PERIOD_M1.

¿Por qué es tan importante precisamente el periodo PERIOD_M1? Aquí hay dos aspectos importantes.

Aspecto 1: la formación del periodo total del gráfico autónomo. Veamos el ejemplo del script PeriodConverter.mq4,  donde se calcula el periodo total del gráfico autónomo:

#property show_inputs
input int InpPeriodMultiplier=3; // Period multiplier factor
int       ExtHandle=-1;
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   datetime time0;
   ulong    last_fpos=0;
   long     last_volume=0;
   int      i,start_pos,periodseconds;
   int      cnt=0;
//---- History header
   int      file_version=401;
   string   c_copyright;
   string   c_symbol=Symbol();
   int      i_period=Period()*InpPeriodMultiplier;
   int      i_digits=Digits;
   int      i_unused[13];
   MqlRates rate;
//---  

Con estos parámetros de entrada, el periodo del gráfico online al que se ha fijado el script es igual a "PERIOD_M3". Con el valor InpPeriodMultiplier=3 obtendremos un gráfico autónomo con un periodo 3. Sin embargo, en realidad obtendremos un periodo del gráfico autónomo igual a 9:

   i_period=Period()*InpPeriodMultiplier=3*3=9

De esa forma, para obtener un periodo de 3, hay que usar un gráfico online con un periodo PERIOD_M1. 

Aspecto 2: anotar la historia en un archivo. Al formar el archivo de la historia se usan los datos de las matrices-series temporales Open[], Low[], High[], Volume[], Time[]. Todas ellas usan los datos del gráfico actual del periodo actual. ¿Y qué puede ser más preciso que la formación de cualquier periodo artificial basado en los datos de un gráfico con el periodo "PERIOD_M1"? Correcto: solo un gráfico con el periodo PERIOD_M1. 

Los cambios del indicador que hemos descrito más arriba se pueden ver en el archivo IndCreateOfflineStep1.mq4.

1.3. Función para la creación del encabezamiento del archivo de la historia

Llamaremos a la función responsable de la creación del encabezamiento del archivo de la historia CreateHeader():

bool CreateHeader(
   const ENUM_OFF_TIMEFRAMES offline_period // period of offline chart
   );

Parámetros

offline_period

[in]  Periodo del gráfico autónomo. 

Valor devuelto

true, si se ha creado con éxito el archivo de la historia, y false en caso de error.

Listado completo de la función:

//+------------------------------------------------------------------+ 
//| The function checks offline mode of the chart                    | 
//+------------------------------------------------------------------+ 
bool IsOffline(const long chart_ID=0)
  {
   bool offline=ChartGetInteger(chart_ID,CHART_IS_OFFLINE);
   return(offline);
  }
//+------------------------------------------------------------------+
//| Create history header                                            |
//+------------------------------------------------------------------+
bool CreateHeader(const ENUM_OFF_TIMEFRAMES offline_period)
  {
//---- History header
   int      file_version=401;
   string   c_copyright;
   string   c_symbol=Symbol();
   i_period=Period()*offline_period;
   int      i_digits=Digits;
   int      i_unused[13];
//---  
   ResetLastError();
   HandleHistory=FileOpenHistory(c_symbol+(string)i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_ANSI);
   if(HandleHistory<0)
     {
      Print("Error open ",c_symbol+(string)i_period,".hst file ",GetLastError());
      return(false);
     }
   c_copyright="(C)opyright 2003, MetaQuotes Software Corp.";
   ArrayInitialize(i_unused,0);
//--- write history file header
   FileWriteInteger(HandleHistory,file_version,LONG_VALUE);
   FileWriteString(HandleHistory,c_copyright,64);
   FileWriteString(HandleHistory,c_symbol,12);
   FileWriteInteger(HandleHistory,i_period,LONG_VALUE);
   FileWriteInteger(HandleHistory,i_digits,LONG_VALUE);
   FileWriteInteger(HandleHistory,0,LONG_VALUE);
   FileWriteInteger(HandleHistory,0,LONG_VALUE);
   FileWriteArray(HandleHistory,i_unused,0,13);
   return(true);
  }
//+------------------------------------------------------------------+

Aparte de la propia creación del archivo de la historia "*.hst" y la creación del encabezamiento del archivo (unas cuantas primera líneas adicionales), en la función CreateHeader() se recuerda el manejador del archivo creado en la variable HandleHistory.

1.4. Primera anotación de la historia en un archivo *.hst

Después de crear el archivo *.hst y de rellenar su encabezamiento, hay que realizar la primera anotación de la historia de cotizaciones en el archivo, es decir, hay que rellenar el archivo con toda la historia que sea actual en este momento. La función responsable de la primera anotación de la historia en el archivo será FirstWriteHistory() (se puede ver en el indicador).

¿Cuándo aparece la situación "primera anotación en la historia" en nuestro indicador? Lo lógico sería presuponer que esto sucederá cuando el indicador realice la carga por primera vez.

Podemos (y debemos) controlar la primera carga en el indicador según el valor de la variable prev_calculated. El valor prev_calculated==0 indica precisamente que está sucediendo la primera carga. Sin embargo, al mismo tiempo, prev_calculated==0 también puede indicar que la carga no es la primera, y que han sido añadidas a la historia cotizaciones anteriores. Cuando editemos el código de OnCalculate(), hablaremos de lo que hay que hacer al actualizar la historia.

1.5. Anotando las cotizaciones online

Después de crear y rellenar el encabezamiento del archivo *.hst y de la primera anotación se puede proceder a la anotación de las cotizaciones online. La responsable de ello es la función CollectTicks() (se puede ver en el indicador).

Es posible ver los cambios del indicador descritos más arriba en el archivo IndCreateOfflineStep2.mq4

1.6. Editando la función OnCalculate()

Introducimos la variable first_start. Esta guardará el valor true después del primer inicio. En otras palabras, con first_start==true sabremos que nuestro indicador aún no ha creado el archivo *.hst.

//--- input parameter
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     first_start=true;    // true -> it's first start
bool     crash=false;         // false -> error in the code

Algoritmo de la función OnCalculate() de nuestro indicador:

algorithm

Fig. 1. Algoritmo de la función OnCalculate() 

La versión final del indicador se puede ver en el archivo IndCreateOffline.mq4

 

2. Iniciando el indicador en un gráfico autónomo

2.1. Actualización del gráfico autónomo con la ayuda de ChartSetSymbolPeriod() 

Observación importante: para actualizar el gráfico autónomo, en lugar de ChartRedraw() hay que llamar ChartSetSymbolPeriod() con los parámetros actuales. La llamada de ChartSetSymbolPeriod() tiene lugar en la función CollectTicks() con un intervalo no mayor a 1 vez cada 3 segundos.

Asimismo, hay que tener en cuenta un detalle del funcionamiento del gráfico autónomo: el indicador fijado al gráfico autónomo con cada actualización recibirá en su función OnCalculate() prev_calculated==0. Debemos recordar este detalle específico. Más abajo mostraremos un método que tiene en cuenta este detalle específico. 

2.2. Modo de mantenimiento 

Qué queremos lograr: el mismo indicador debe funcionar tanto en un gráfico online normal, como en un gráfico autónomo. Además, el comportamiento del indicador cambia dependiendo del gráfico en el que se encuentre, online o autónomo. Cuando el indicador se encuentra en un gráfico online, su funcionalidad es muy parecida al indicador IndCreateOffline.mq4, que ya hemos visto más arriba. Y en el modo autónomo comienza a funcionar como un indicador corriente.

Bien, vamos a poner a nuestro indicador el nombre IndMACDDoubleDuty, se construirá sobre IndCreateOffline.mq4  que hemos visto más arriba y el indicador estándar MACD.mq4. Por favor, prepare un borrador de su indicador: en MetaEditor'e abra el archivo IndCreateOffline.mq4, después el menú "archivo" -> "Guardar como..." e introduzca el nombre del indicador IndMACDDoubleDuty.mq4

Añadiremos de inmediato la descripción del indicador, nuestro indicador ahora crea gráficos autónomos y tienen una misión doble:

//+------------------------------------------------------------------+
//|                                            IndMACDDoubleDuty.mq4 |
//|                        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 description "The indicator creates an offline chart."
#property description "May work on an online chart and off-line graph."
#property strict
#property indicator_chart_window 

El indicador funcionará en el modo de "mantenimiento" si ha sido iniciado en un gráfico online con un periodo PERIOD_M1. En este modo, realiza las siguientes funciones:

  • crea un archivo *.hst y lo rellena;
  • crea un gráfico autónomo basado en el archivo *.hst ;
  • conforme van llegando las cotizaciones, va completando la historia en el archivo *.hst y actualiza el gráfico autónomo.
Para recordar en qué modo funciona el indicador, introduciremos la variable mode_offline:

//--- input parameter
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     first_start=true;    // true -> it's first start
bool     crash=false;         // false -> error in the code
bool     mode_offline=true;   // true -> on the offline chart
int      HandleHistory=-1;    // handle for the opened "*.hst" file
datetime time0;               //

Por consiguiente, OnInit() cambiará un poco:

int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//---
   return(INIT_SUCCEEDED);
  }

Introducimos los cambios en OnCalculate():

//+------------------------------------------------------------------+
//| 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[])
  {
//---
   if(crash)
      return(rates_total);

   if(!mode_offline) // work in the online chart 
     {
      if(prev_calculated==0 && first_start) // first start
        {
         .
         .
         .
         first_start=false;
        }
      //---
      CollectTicks();
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

En esta etapa, el indicador, al fijarlo a un gráfico online con el periodo PERIOD_M1, pasa al modo de "mantenimiento" y crea un gráfico autónomo.

Los cambios del indicador descritos más arriba, se pueden ver en el archivo IndMACDDoubleDutyStep1.mq4

2.3. Copiando el indicador a un gráfico autónomo

Añdiremos al indicador IndMACDDoubleDuty una nueva funcionalidad: ahora, dentro del modo de "mantenimiento", el indicador debe transmitir su copia al gráfico autónomo creado. A ello nos ayudarán estas funciones: ChartSaveTemplate y ChartApplyTemplate. Ahora el algoritmo OnCalcalculate() tendrá el aspecto siguiente:

algorithm_2

Fig. 2. Algoritmo de la función OnCalculate()  

Añadimos al código de OnCalculate() una funcionalidad adicional:

         else
            Print(__FUNCTION__,"Opening offline chart id=",ChartOffID);
         ResetLastError();
         if(!ChartSaveTemplate(0,"IndMACDDoubleDuty"))
           {
            Print("Error save template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
         ResetLastError();
         if(!ChartApplyTemplate(ChartOffID,"IndMACDDoubleDuty"))
           {
            Print("Error apply template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
        }
      //---
      if(prev_calculated==0 && !first_start) // a deeper history downloaded or history blanks filled

Ahora nuestro indicador, al fijarlo a un gráfico online con el periodo PERIOD_M1, pasa al modo de "mantenimiento", crea un gráfico autónomo y se copia en él.

Los cambios del indicador descritos más arriba, se pueden ver en el archivo IndMACDDoubleDutyStep2.  

2.4. Integración en MACD.mq4

Nuestro indicador ya se ubica a sí mismo en el gráfico autónomo, pero por el momento no representa y no calcula nada. Vamos a corregir esto: integramos nuestro indicador en el MACD.mq4 estándar. 

Primero insertaremos los parámetros de entrada del indicador MACD.mq4 en nuestro código:

#property strict

#include <MovingAverages.mqh>

//--- MACD indicator settings
#property  indicator_separate_window
#property  indicator_buffers 2
#property  indicator_color1  Silver
#property  indicator_color2  Red
#property  indicator_width1  2
//--- indicator parameters
input int InpFastEMA=12;   // Fast EMA Period
input int InpSlowEMA=26;   // Slow EMA Period
input int InpSignalSMA=9;  // Signal SMA Period
//--- indicator buffers
double    ExtMacdBuffer[];
double    ExtSignalBuffer[];
//--- right input parameters flag
bool      ExtParameters=false;
//+------------------------------------------------------------------+
//| Enumerations of periods offline chart                            |
//+------------------------------------------------------------------+ 

Después añadimos el código a OnInit(). Aquí debemos destacar que la inicialización de los parámetros del indicador MACD deberá tener lugar en cualquier condición, tanto en un gráfico autónomo, como online, incluso en el caso de que el periodo del gráfico online sea distinto a PERIOD_M1:

int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//--- init MACD indicator
   IndicatorDigits(Digits+1);
//--- drawing settings
   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexStyle(1,DRAW_LINE);
   SetIndexDrawBegin(1,InpSignalSMA);
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtMacdBuffer);
   SetIndexBuffer(1,ExtSignalBuffer);
//--- name for DataWindow and indicator subwindow label
   IndicatorShortName("MACD("+IntegerToString(InpFastEMA)+","+IntegerToString(InpSlowEMA)+","+IntegerToString(InpSignalSMA)+")");
   SetIndexLabel(0,"MACD");
   SetIndexLabel(1,"Signal");
//--- check for input parameters
   if(InpFastEMA<=1 || InpSlowEMA<=1 || InpSignalSMA<=1 || InpFastEMA>=InpSlowEMA)
     {
      Print("Wrong input parameters");
      ExtParameters=false;
      return(INIT_FAILED);
     }
   else
      ExtParameters=true;
//---
   return(INIT_SUCCEEDED);
  }

Con el siguiente paso, modificaremos un poco el principio del código OnCalculate(). Si estamos en un gráfico online y su periodo no es igual a PERIOD_M1, deberemos darle la posibilidad de calcular los parámetros MACD. Era así:

const long &volume[],
                const int &spread[])
  {
//---
   if(crash)
      return(rates_total);

   if(!mode_offline) // work in the online chart 
     {
      if(prev_calculated==0 && first_start) // first start
        {

 y ahora será así:

const long &volume[],
                const int &spread[])
  {
//---
   if(!mode_offline && !crash) // work in the online chart 
     {
      if(prev_calculated==0 && first_start) // first start
        {

Después añadiremos el código del cálculo de los parámetros del indicador MACD al final de OnCalculate():

         FirstWriteHistory(ExtOffPeriod);
         first_start=false;
        }
      //---
      CollectTicks();
     }
//---
   int i,limit;
//---
   if(rates_total<=InpSignalSMA || !ExtParameters)
      return(0);
//--- last counted bar will be recounted
   limit=rates_total-prev_calculated;
   if(prev_calculated>0)
      limit++;
//--- macd counted in the 1-st buffer
   for(i=0; i<limit; i++)
      ExtMacdBuffer[i]=iMA(NULL,0,InpFastEMA,0,MODE_EMA,PRICE_CLOSE,i)-
                       iMA(NULL,0,InpSlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//--- signal line counted in the 2-nd buffer
   SimpleMAOnBuffer(rates_total,prev_calculated,0,InpSignalSMA,ExtMacdBuffer,ExtSignalBuffer);
//--- return value of prev_calculated for next call
   return(rates_total);
  }

2.5. Cálculo económico del indicador en un gráfico autónomo

Recordemos el detalle descrito al principio del apartado 2:

Asimismo, hay que tener en cuenta un detalle del funcionamiento del gráfico autónomo: el indicador fijado al gráfico autónomo con cada actualización recibirá en su función OnCalculate() prev_calculated==0. Debemos recordar este detalle específico. Más abajo mostraremos un método que tiene en cuenta este detalle específico. 

Y otra cosa que no debemos olvidar: en OnCalculate() el valor prev_calculated==0 puede referirse a dos posibilidades:

  1. o bien se trata del primer inicio del indicador;
  2. o se ha completado la historia.

En ambos casos, el indicador debe recalcular todas las barras en el gráfico. Cuando esto se debe ejecutar solo una vez (durante la carga), se trata de algo normal. Pero en el gráfico autónomo, obtendremos prev_calculated==0 con cada actualización (aproximadamente una vez cada 2-3 segundos), y el indicador recalculará todas las barras. Se trata de un gasto de recursos muy poco económico. Por eso aplicaremos un pequeño truco: cuando el indicador se encuentre en un gráfico autónomo, guardará y comparará el número de barras (variable rates_total) y la hora de la barra más a la derecha en el gráfico.

Paso 1: Al principio de OnCalculate() anunciamos dos variables estáticas y una pseudo-variable:

                const long &volume[],
                const int &spread[])
  {
//---
   static int static_rates_total=0;
   static datetime static_time_close=0;
   int pseudo_prev_calculated=prev_calculated;
//---
   if(!mode_offline && !crash) // work in the online chart 
     {

Paso 2: En el bloque de código del cálculo de los valores del indicador, sustituimos la variable prev_calculated por pseudo_prev_calculated:

      CollectTicks();
     }
//---
   int i,limit;
//---
   if(rates_total<=InpSignalSMA || !ExtParameters)
      return(0);
//--- last counted bar will be recounted
   limit=rates_total-pseudo_prev_calculated;
   if(pseudo_prev_calculated>0)
      limit++;
//--- macd counted in the 1-st buffer
   for(i=0; i<limit; i++)
      ExtMacdBuffer[i]=iMA(NULL,0,InpFastEMA,0,MODE_EMA,PRICE_CLOSE,i)-
                       iMA(NULL,0,InpSlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//--- signal line counted in the 2-nd buffer
   SimpleMAOnBuffer(rates_total,pseudo_prev_calculated,0,InpSignalSMA,ExtMacdBuffer,ExtSignalBuffer);
//---
   if(mode_offline) // work in the offline chart 

Paso 3: Aquí calcularemos el valor para la pseudo-variable, si el indicador funciona en un gráfico autónomo. 

      CollectTicks();
     }
//---
   if(mode_offline) // work in the offline chart 
     {
      if(time[0]>static_time_close) // new bar
        {
         if(static_time_close==0)
            pseudo_prev_calculated=0;
         else // search bar at which time[0]==static_time_close
           {
            for(int i=0;i<rates_total;i++)
              {
               if(time[i]==static_time_close)
                 {
                  pseudo_prev_calculated=rates_total-i;
                  break;
                 }
              }
           }
        }
      else
        {
         pseudo_prev_calculated=rates_total;
        }
      //---
      static_rates_total=rates_total;
      static_time_close=time[0];
     }
//---
   int i,limit;

Ahora nuestro indicador en el gráfico autónomo calcula sus valores de forma económica. Además, en este modo, procesa correctamente las interrupciones en la conexión: después de un corte, se calculan solo las barras añadidas de nuevo.

2.6. Cargando la historia. Gráfico autónomo

Queda decidir qué hay que hacer si en un gráfico online se ha actualizado la historia de nuestro indicador (y además prev_calculated==0). Yo propongo resolver esta situación con la ayuda de las variables globales del terminal. El algoritmo es el siguiente: si en un gráfico online obtenemos prev_calculated==0 (no importa si ha tenido lugar el primer inicio o se ha actualizado la historia), simplemente creamos una variable global. El indicador en el gráfico autónomo con cada actualización comprueba la presencia de la variable global: si existe (significa que en el gráfico online teníamos prev_calculated==0), entonces el indicador se recalculará por completo y eliminará la variable global.

Añadiremos al encabezamiento una variable en la que guardaremos el nombre de la futura variable global del terminal, y en OnInit() generaremos este nombre:

MqlRates rate;                //
long     ChartOffID=-1;       // ID of the offline chart
string   NameGlVariable="";   // name global variable
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
   NameGlVariable=Symbol()+(string)ExtOffPeriod;
//--- init MACD indicator
   IndicatorDigits(Digits+1);
//--- drawing settings

Añadimos el código de creación de la variable global del terminal, si se cumple la condición prev_calculated==0 y el indicador se encuentra en un gráfico online:

         if(!ChartApplyTemplate(ChartOffID,"IndMACDDoubleDuty"))
           {
            Print("Error apply template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
         //---
         ResetLastError();
         if(GlobalVariableSet(NameGlVariable,0.0)==0) // creates a new global variable
           {
            Print("Failed to creates a new global variable ",GetLastError());
           }
        }
      //---
      if(prev_calculated==0 && !first_start) // a deeper history downloaded or history blanks filled
        {
         Print("a deeper history downloaded or history blanks filled. first_start=",first_start);
         if(CreateHeader(ExtOffPeriod))
            first_start=false;
         else
           {
            crash=true;
            return(rates_total);
           }
         //---
         FirstWriteHistory(ExtOffPeriod);
         first_start=false;
         //---
         ResetLastError();
         if(GlobalVariableSet(NameGlVariable,0.0)==0) // creates a new global variable
           {
            Print("Failed to creates a new global variable ",GetLastError());
           }
        }
      //---
      CollectTicks();

Y el último cambio: la comprobación de la existencia de la variable global del indicador en el gráfico autónomo:

      else
        {
         pseudo_prev_calculated=rates_total;
        }
      //---
      if(GlobalVariableCheck(NameGlVariable))
        {
         pseudo_prev_calculated=0;
         GlobalVariableDel(NameGlVariable);
        }
      //---
      Print("rates_total=",rates_total,"; prev_calculated=",
            prev_calculated,"; pseudo_prev_calculated=",pseudo_prev_calculated);
      static_rates_total=rates_total;
      static_time_close=time[0];
     }
//---
   int i,limit;

La versión final del indicador, con los últimos cambios: IndMACDDoubleDuty.mq4.  

 

3. Indicador que representa barras Renko

Las barras Renko las construiremos según la misma tecnología que se usó en los puntos 1. El indicador "IndCreateOffline", que creará el gráfico autónomo  y 2. El inicio del indicador en el gráfico autónomo. Es decir, como conclusión obtendremos un gráfico autónomo, pero solo que en este caso, el archivo de la historia *.hst contendrá barras de un mismo tamaño, también conocidas como "ladrillos". El tamaño de los "ladrillos" se determina en los ajustes del indicador, en puntos.

Barras Renko  

Fig. 3. Barras Renko 

Antes de comenzar a construir, hay que tener en cuenta varias reglas de la formación del archivo de la historia *.hst para representar las barras Renko.

3.1. Reglas de construcción de las barras Renko

Regla1: la anotación de OHLC en el archivo de la historia *.hst deberá ser correcta, sobre todo en lo que respecta a los valores High y Low. En el caso contrario, el terminal no representará ninguna anotación incorrecta. Ejemplo de anotación correcta e incorrecta en el archivo de la historia *.hst de la vela alcista:

Ejemplo de anotación correcta e incorrecta  

Fig. 4. Ejemplo de anotación correcta e incorrecta 

Regla 2: aunque estemos construyendo barras Range, que no tienen anclaje temporal, el propio formato del archivo de la historia *.hst necesita de la existencia del parámetro time, la hora de comienzo del periodo. Por eso es obligatorio anotar el parámetro time.

Regla 3: el parámetro time debe ser diferente en todas las barras. Si anotamos para todas las barras el mismo parámetro time, esa anotación será incorrecta, y el terminal simplemente no representará el gráfico. Pero también hay una particularidad agradable: el parámetro time se puede anotar con una diferencia de 1 segundo. Por ejemplo, una barra la anotaremos con un parámetro time=2016.02.10 09:08:00, y la siguiente con un parámetro  time=2016.02.10 09:08:01.

3.2. Formación de los "ladrillos" sobre datos históricos

Este método no aspira al título de algoritmo perfecto. He elegido un enfoque simplificado, puesto que la tarea más importante de este artículo es mostrar cómo formar el archivo de la historia *.hst. En el indicador "IndRange.mq4", al dibujar ladrillos en base a la historia, se analizan los valores High[i] y Low[i] del periodo actual. Es decir, si fijamos el indicador "IndRange.mq4" a un gráfico con el periodo M5, el indicador "IndRange.mq4" analizará la historia del periodo M5 actual, al darse el primer inicio. 

Claro que si lo desea, podrá modernizar el algoritmo de dibujado en base a la historia y tener en cuenta el movimiento de los precios en el marco temporal más pequeño, М1. Esquema general de funcionamiento:

  • si High[i] es mayor que high del anterior ladrillo en un tamaño equivalente a un ladrillo, entonces se dibujarán uno o más ladrillos por encima del anterior ladrillo;
  • si Low[i] es menor que low del anterior ladrillo en un tamaño equivalente a un ladrillo, entonces se dibujarán uno o más ladrillos por encima del anterior ladrillo;

El ladrillo anterior es alcista

Fig. 5. El ladrillo anterior es alcista     


 El ladrillo anterior es bajista

Fig. 6. El ladrillo anterior es bajista

Un detalle interesante: las coordenadas de las barras (Open, High, Low, Close, Time y los volúmenes) se guardan en la estructura rate, que se declara en el encabezamiento del indicador

MqlRates rate;                   //

y esta estructura con cada anotación en el archivo de la historia *.hst no se resetea, si no que simplemente se reescribe. Gracias a esto es fácil implementar los algoritmos que se presentan en la fig. 5 y en la fig. 6., y es posible dar una fórmula general:

  • cuando High[i] es mayor que high del anterior ladrillo (además no importa si el ladrillo anterior ha sido alcista o bajista) en un tamaño equivalente a un ladrillo, las coordenadas para la nueva barra se calculan según el siguiente esquema:  

Open = High; Low = Open; Close = Low + Renko size; High = Close

 

  • cuando Low[i] es menor que low del anterior ladrillo (además no importa si el ladrillo anterior ha sido alcista o bajista) en un tamaño equivalente a un ladrillo, las coordenadas para la nueva barra se calculan según el siguiente esquema:  

Low = OpenHigh = OpenClose = High - Renko sizeLow = Close

Y este es el aspecto que tienen las fórmulas en el indicador "IndRange.mq4", en la función FirstWriteHistory():

//+------------------------------------------------------------------+
//| First Write History                                              |
//+------------------------------------------------------------------+
bool FirstWriteHistory(const int offline_period)
  {
   int      i,start_pos;
   int      cnt=0;
//--- write history file
   periodseconds=offline_period*60;
   start_pos=Bars-1;
   rate.open=Open[start_pos];
   rate.close=Close[start_pos];
   rate.low=Low[start_pos];
   rate.high=High[start_pos];
   rate.tick_volume=(long)Volume[start_pos];
   rate.spread=0;
   rate.real_volume=0;
//--- normalize open time
   rate.time=D'1980.07.19 12:30:27';
   for(i=start_pos-1; i>=0; i--)
     {
      if(IsStopped())
         break;
      while((High[i]-rate.high)>SizeRenko*Point())
        {
         rate.time+=1;
         rate.open=rate.high;
         rate.low=rate.open;
         rate.close=NormalizeDouble(rate.low+SizeRenko*Point(),Digits);
         rate.high=rate.close;
         last_fpos=FileTell(HandleHistory);
         uint byteswritten=FileWriteStruct(HandleHistory,rate);
         //--- check the number of bytes written 
         if(byteswritten==0)
            PrintFormat("Error read data. Error code=%d",GetLastError());
         else
            cnt++;
        }
      while((Low[i]-rate.low)<-SizeRenko*Point())
        {
         rate.time+=1;
         rate.open=rate.low;
         rate.high=rate.open;
         rate.close=NormalizeDouble(rate.high-SizeRenko*Point(),Digits);
         rate.low=rate.close;
         last_fpos=FileTell(HandleHistory);
         uint byteswritten=FileWriteStruct(HandleHistory,rate);
         //--- check the number of bytes written 
         if(byteswritten==0)
            PrintFormat("Error read data. Error code=%d",GetLastError());
         else
            cnt++;
        }
     }
   FileFlush(HandleHistory);
   PrintFormat("%d record(s) written",cnt);
   return(true);
  }

 

El indicador "IndRange.mq4" siempre forma el nombre del archivo de la historia *.hst según la regla siguiente: "Nombre del símbolo actual"+"7"+".hst". Por ejemplo, para el símbolo "EURUSD", el archivo de la historia cambiará el nombre "EURUSD7.hst".

3.3. Funcionamiento del indicador online

Cuando el indicador trabaja online, en lugar del precio High[i], hay que analizar el precio Close[0], el precio de cierre en la barra cero (más a la derecha):

//+------------------------------------------------------------------+
//| Collect Ticks                                                    |
//+------------------------------------------------------------------+
bool CollectTicks()
  {
   static datetime last_time;//=TimeLocal()-5;
   long     chart_id=0;
   datetime cur_time=TimeLocal();
//---
   while((Close[0]-rate.high)>SizeRenko*Point())
     {
      rate.time+=1;
      rate.open=rate.high;
      rate.low=rate.open;
      rate.close=NormalizeDouble(rate.low+SizeRenko*Point(),Digits);
      rate.high=rate.close;
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- check the number of bytes written  
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
     }
   while((Close[0]-rate.low)<-SizeRenko*Point())
     {
      rate.time+=1;
      rate.open=rate.low;
      rate.high=rate.open;
      rate.close=NormalizeDouble(rate.high-SizeRenko*Point(),Digits);
      rate.low=rate.close;
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- check the number of bytes written 
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
     }
//--- refresh window not frequently than 1 time in 2 seconds
   if(cur_time-last_time>=3)
     {
      FileFlush(HandleHistory);
      ChartSetSymbolPeriod(ChartOffID,Symbol(),i_period);
      last_time=cur_time;
     }
   return(true);
  }


4. Indicador que creará un símbolo no estándar, el índice del dólar USDx

Observaciones importantes al algoritmo  del indicador "IndUSDx.mq4": el indicador del índice del dólar no aspira a que la fórmula de cálculo del índice sea absolutamente exacta, ya que para nosotros ahora lo más importante es mostrar cómo formar el archivo de la historia *.hst de un símbolo no estándar. En definitiva, obtendremos un gráfico autónomo, y las barras de este gráfico representarán el índice calculado del dólar. Asimismo, el indicador "IndUSDx.mq4" creará un gráfico autónomo solo una vez: o bien cuando el indicador se une al gráfico por primera vez, o bien tras cambiar el periodo del gráfico.

La fórmula para el cálculo del índice del dólar y el conjunto de símbolos se han tomado basándose en el código: Indicador simple del índice del dólar

Para la fórmula de cálculo del índice del dólar hay que obtener los datos Time, Open y Close de los símbolos "EURUSD", "GBPUSD", "USDCHF", "USDJPY", "AUDUSD", "USDCAD" y "NZDUSD". Para que sea más cómodo guardar y recurrir a los datos, se ha introducido una estructura OHLS (se ha declarado en el encabezamiento del indicador):

//--- structuts
struct   OHLS
  {
   datetime          ohls_time[];
   double            ohls_open[];
   double            ohls_close[];
  };

En la estructura OHLS, como elementos intervienen las matrices para el guardado Time, Open y Close. En la descripción de la estructura se declararán a la vez varios objetos, las estructuras OHLS, en las que se guardarán los datos de los símbolos calculados:

//--- structuts
struct   OHLS
  {
   datetime          ohls_time[];
   double            ohls_open[];
   double            ohls_close[];
  };
OHLS     OHLS_EURUSD,OHLS_GBPUSD,OHLS_USDCHF,OHLS_USDJPY,OHLS_AUDUSD,OHLS_USDCAD,OHLS_NZDUSD;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()

En OnInit() tiene lugar la llamada de la función SelectSymbols():

//+------------------------------------------------------------------+
//| Select Symbols                                                   |
//+------------------------------------------------------------------+
bool SelectSymbols()
  {
   bool rezult=true;
   string arr_symbols[7]={"EURUSD","GBPUSD","USDCHF","USDJPY","AUDUSD","USDCAD","NZDUSD"};
   for(int i=0;i<ArraySize(arr_symbols);i++)
      rezult+=SymbolSelect(arr_symbols[i],true);
//---
   return(rezult);
  }

La función SelectSymbols(), con la ayuda de SymbolSelect, elige los símbolos que participan en la fórmula del índice del dólar, en la ventana MarketWatch.

En OnCalculate(), durante el primer inicio se llama la función CopyCloseSymbols(). Aquí tiene lugar la solicitud de datos sobre los símbolos a calcular y el rellenado de las estructuras de los símbolos:

//+------------------------------------------------------------------+
//| CopyClose Symbols                                                |
//+------------------------------------------------------------------+
bool CopyCloseSymbols(const int rates)
  {
   int copied=0;
   int copy_time=0,copy_open=0,copy_close=0;
   copy_time=CopyTime("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_open);
   copy_close=CopyClose("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"EURUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("GBPUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("GBPUSD",Period(),0,rates,OHLS_GBPUSD.ohls_open);
   copy_close=CopyClose("GBPUSD",Period(),0,rates,OHLS_GBPUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"GBPUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDCHF",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDCHF",Period(),0,rates,OHLS_USDCHF.ohls_open);
   copy_close=CopyClose("USDCHF",Period(),0,rates,OHLS_USDCHF.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDCHF\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDJPY",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDJPY",Period(),0,rates,OHLS_USDJPY.ohls_open);
   copy_close=CopyClose("USDJPY",Period(),0,rates,OHLS_USDJPY.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDJPY\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("AUDUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("AUDUSD",Period(),0,rates,OHLS_AUDUSD.ohls_open);
   copy_close=CopyClose("AUDUSD",Period(),0,rates,OHLS_AUDUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"AUDUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDCAD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDCAD",Period(),0,rates,OHLS_USDCAD.ohls_open);
   copy_close=CopyClose("USDCAD",Period(),0,rates,OHLS_USDCAD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDCAD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("NZDUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("NZDUSD",Period(),0,rates,OHLS_NZDUSD.ohls_open);
   copy_close=CopyClose("NZDUSD",Period(),0,rates,OHLS_NZDUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"NZDUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }
//---
   return(true);
  }

Si en la función CopyCloseSymbols() se ha descargado menos historia de un símbolo que la del valor indicado, entonces se muestra un mensaje con el nombre del símbolo y el valor real de la historia descargada del símbolo.

En caso de que se rellenen con éxito las estructuras, tendrá lugar la llamada de la funcionalidad principal, la función FirstWriteHistory(), que rellena con la historia el archivo *.hst:

//+------------------------------------------------------------------+
//| First Write History                                              |
//+------------------------------------------------------------------+
bool FirstWriteHistory(const int rates)
  {
   int      i;
   int      cnt=0;
   rate.tick_volume=0;
   rate.spread=0;
   rate.real_volume=0;
   for(i=0;i<rates;i++)
     {
      rate.time=OHLS_EURUSD.ohls_time[i];
      rate.open=(100*MathPow(OHLS_EURUSD.ohls_open[i],0.125)+100*MathPow(OHLS_GBPUSD.ohls_open[i],0.125)+
                 100*MathPow(OHLS_USDCHF.ohls_open[i],0.125)+100*MathPow(OHLS_USDJPY.ohls_open[i],0.125)+
                 100*MathPow(OHLS_AUDUSD.ohls_open[i],0.125)+100*MathPow(OHLS_USDCAD.ohls_open[i],0.125)+
                 100*MathPow(OHLS_NZDUSD.ohls_open[i],0.125))/8.0;

      rate.close=(100*MathPow(OHLS_EURUSD.ohls_close[i],0.125)+100*MathPow(OHLS_GBPUSD.ohls_close[i],0.125)+
                  100*MathPow(OHLS_USDCHF.ohls_close[i],0.125)+100*MathPow(OHLS_USDJPY.ohls_close[i],0.125)+
                  100*MathPow(OHLS_AUDUSD.ohls_close[i],0.125)+100*MathPow(OHLS_USDCAD.ohls_close[i],0.125)+
                  100*MathPow(OHLS_NZDUSD.ohls_close[i],0.125))/8.0;

      if(rate.open>rate.close)
        {
         rate.high=rate.open;
         rate.low=rate.close;
        }
      else
        {
         rate.high=rate.close;
         rate.low=rate.open;
        }
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- check the number of bytes written 
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
      else
         cnt++;
     }
   FileFlush(HandleHistory);
   PrintFormat("%d record(s) written",cnt);
   return(true);
  }

Resultado del funcionamiento de "IndUSDx.mq4": 

 Indicador "IndUSDx.mq4

Fig. 7. Indicador "IndUSDx.mq4 

  

Conclusión

Resulta que en los gráficos autónomos, al igual que en los gráficos online, es posible realizar un recálculo de los indicadores bastante económico. Bien es cierto que hay que introducir cambios en el código del indicador para considerar las singularidades de la actualización del gráfico autónomo, y precisamente, tener en cuenta que al realizar la actualización del gráfico autónomo, todos los indicadores obtienen en OnCalculate() el valor prev_calculate==0.

Asimismo, en el artículo se mostrado cómo formar el archivo de la historia *.hst , cambiando con ello de forma radical los parámetros de las barras representadas en el gráfico. 


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

Evaluando la efectividad de los sistemas comerciales mediante el análisis de sus componentes Evaluando la efectividad de los sistemas comerciales mediante el análisis de sus componentes
En este artículo vamos a investigar la efectividad de los sistemas comerciales complejos mediante el análisis de la efectividad de sus componentes por separado. Cualquier análisis, sea de tipo gráfico, basado en indicadores o de cualquier otro tipo, es uno de los componentes clave para comerciar con éxito en los mercados financieros. Este artículo es una investigación sui generis de varios sistemas comerciales sencillos independientes, en la que se analiza su efectividad y la utilidad de su aplicación conjunta.
LifeHack para tráders: un back test está bien, pero cuatro están mucho mejor LifeHack para tráders: un back test está bien, pero cuatro están mucho mejor
A cualquier tráder le surge la misma pregunta antes de la primera simulación: "¿cuál de los cuatro modos debo utilizar?" Cada uno de los modos propuestos tiene sus ventajas y peculiaridades, por eso haremos la tarea más simple, ¡iniciaremos todos los modos a la vez con solo un botón! En el artículo se muestra cómo con la ayuda de Win API y un poco de magia se pueden ver los cuatro gráficos de simulación.
Lógica difusa para crear estrategias de trading manual Lógica difusa para crear estrategias de trading manual
Este artículo sugiere las maneras de mejorar la estrategia de trading manual mediante la aplicación de teoría de conjuntos difusa. Como ejemplo hemos incluido una descripción paso a paso en la búsqueda de la estrategia y la selección de sus parámetros, seguido de la aplicación de lógica difusa para desenfocar criterios demasiado formales para entrar en el mercado. Así, después de la modificación de la estrategia obtenemos condiciones flexibles para la apertura de una posición que tiene una reacción razonable a una situación de mercado.
Simulación de estrategias comerciales con ticks reales Simulación de estrategias comerciales con ticks reales
En este artículo le mostraremos los resultados de la simulación de una estrategia comercial sencilla en 3 modos: "1 minuto OHLC", "Todos los ticks" y "Cada tick en base a ticks reales" usando los ticks guardados en la historia.