Cómo crear y testear personalmente los instrumentos de la Bolsa de Moscú en MetaTrader 5

Dmitrii Troshin | 19 marzo, 2019

Introducción

Como se sabe, los mercados financieros pueden ser bursátiles y extrabursátiles. Para poder operar en el mercado extrabursátil Forex, tenemos a nuestra disposición las herramientas modernas MetaTrader y MetaEditor, que están en desarrollo constante. Usándolas, no sólo podemos automatizar el trading, sino también testear nuestros algoritmos en los datos históricos.

¿Pero qué pasará si decidimos usar nuestras propias ideas para negociar en la bolsa? Algunos de los terminales bursátiles tienen los lenguajes de programación integrados. Por ejemplo, el popular terminal Transaq dispone del lenguaje de programación ATF (Advanced Trading Facility). Desde luego, no puede compararse de ninguna manera con MQL5, es más, no cuenta con el Simulador de Estrategias. Por eso, en esta situación, una buena solución sería obtener los datos bursátiles y optimizar los algoritmos comerciales en el Simulador de Estrategias de MetaTrader.

Para resolver este problema, existe la posibilidad de crear los símbolos personalizados. Este proceso se describe en detalle en el artículo Creación y simulación de símbolos en MetaTrader 5. Lo único que necesitamos es obtener los datos en formato de un archivo CSV (TXT), e importar el historial de precios siguiendo los pasos descritos en el artículo.

Todo eso sería bastante fácil, si no fuera por la diferencia entre los formatos de datos. Por ejemplo, usamos la web bursátil rusa más popular finam.ru. Se puede descargar las cotizaciones aquí:



Exportando las cotizaciones de la Bolsa de Moscú


Los formatos de datos ofrecidos por Finam son los siguientes:




Los formatos de datos disponibles son: «yyyymmdd», «yymmdd», «ddmmyy», «dd/mm/yy», «mm/dd/yy». Nuestro formato:



Nuestro formato «yyyy.mm.dd» no se encuentra entre ellos. Es decir, en finam.ru hay prácticamente todos los formatos, a excepción del nuestro. 

Aparte de eso, no hay que limitarse con un solo recurso bursátil. Los formatos de otros recursos también pueden ser inconvenientes. Necesitamos un cierto orden de datos, mientras que las cotizaciones pueden almacenarse en cualquier otro orden, por ejemplo: Open, Close, High, Low. 

Pues bien, nuestra tarea es convertir los datos ubicados en un orden aleatorio y de formatos diferentes, en nuestro formato. Eso nos permitirá recibir los datos para MetaTrader 5 usando cualquiera fuente. Luego, creamos un símbolo personalizado a base de los datos recibidos, usando las herramientas de MQL5, para poder realizar las simulaciones.

Me gustaría mencionar algunas complicaciones relacionadas con la importación de las cotizaciones bursátiles.

Claro que en la bolsa hay spread, precio Ask y Bid. No obstante, todos estos valores existen solamente en el «momento», Profundidad de Mercado. Luego, se escribe sólo el precio de la transacción, independientemente de los precios de su ejecución (Bid o Ask). Necesitamos el spread para el terminal. Para eso, simplemente se añade un spread fijo, porque es imposible restaurar el spread en la «Profundidad de Mercado». Si es esencial para alguien, puede imitarlo de alguna manera. Por ejemplo, como fue descrito en el artículo  Modelando series temporales con ayuda de símbolos personalizados según las leyes de distribución establecida.Alternativamente, se puede escribir una función simple que representa la dependencia del spread y la volatilidad Spread = f(High-Low).

Si trabajamos con los timeframes, el uso deun spread fijo es bastante aceptable. El error no será significante en los períodos grandes. Pero según mi parecer, la modelación del spread será imprescindible en caso de los ticks. Formato de los ticks en la bolsa:


Nuestro formato:



Aquí, aparte del LAST, hay que definir ASK y BID. Los datos están clasificados con la precisión de milisegundos. En la bolsa, simplemente tenemos un flujo de precios. Los datos en la primera imagen parecen simplemente en la división de un loto grande en partes. En términos de Forex, aquí no hay ningunos ticks. Puede tratarse de Bid o Ask, o simultáneamente de ambos. En adición a eso, necesitamos clasificar artificialmentelas transacciones por el tiempo, añadiendo milisegundos.

Ahora, este artículo se convierte suavemente de la «importación de datos» en la «modelación de datos», es decir, en el artículo mencionado un poco más arriba. Debido a eso, para no inducir a error, decidí no publicar el programa para importar los ticks construido según el principio ASK=BID(+spread)=LAST. El spread es significativo cuando se trabaja con milisegundos, por eso, durante la simulación, es necesario escoger un método apropiado de su modelación.

Después de eso, corregir el código de importación de los ticks es la cuestión de algunos minutos. Basta con sustituir la estructura MqlRates por MqlTick. La función  CustomRatesUpdate() se sustituye por CustomTicksAdd().

Otra cosa importante está relacionada con la imposibilidad de considerar todos los posibles formatos de datos. Por ejemplo, se puede escribir los números con separadores de las órdenes (1 000 000), o en vez del separador decimal (punto), se puede usar la coma (3,14). Hay otro caso aún peor, cuando el separador de datos y el separador decimal se representan por un punto y coma (cómo distinguirlos). Aquí, vamos a considerar solamente los formatos más comunes. Si aparece un formato no estándar, tendrá que escribirlo por sí solo.

Además de eso, en la bolsa no hay historial de ticks, sólo el volumen de la transacción. Por eso, aquí, usamos el volumen bursátil =VOL=TICKVOL.

El artículo está dividido en dos partes. La primera parte incluye la descripción del código. Sirve para familiarizarse con el código, para poder editarlo en caso de los formatos de datos no estándar. La segunda parte contiene una guía paso a paso (Manual de Usuario). Servirá para aquellos a quienes no les interesa la programación, sino les interesa solamente la funcionalidad. Si Usted opera con los datos (sobre todo desde finam.ru) de formatos estándar, puede proseguir directamente a la parte 2.

Parte 1. Descripción del código

Aquí, se muestra sólo una parte del código, ya que el código completo se encuentra en los archivos adjuntos.

Primero, introducimos los parámetros necesarios: posiciones de los datos en la cadena, parámetros del archivo, nombre del símbolo, etc.

input int SkipString        =1;                               // Número de cadenas omitidas
input string mark1          ="Posición y formato de la hora"; // Hora
input DATE indate           =yyyymmdd;                        // Formato inicial de la fecha
input TIME intime           =hhdmmdss;                        // Formato inicial de la hora
input int DatePosition      =1;                               // Posición de la fecha
input int TimePosition      =2;                               // Posición de la hora
//------------------------------------------------------------------+
input string mark2          ="Posiciones de los datos de precios";        // Precio 
input int OpenPosition      =3;                               // Posiciones del precio de apertura
input int HighPosition      =4;                               // Posición del máximo del precio
input int LowPosiotion      =5;                               // Posición del mínimo del precio   
input int ClosePosition     =6;                               // Posición de la hora del cierre
input int VolumePosition    =7;                               // Posición del volumen
input string mark3          ="Parámetros del archivo";               // Archivo
//-------------------------------------------------------------------+
input string InFileName     ="sb";                            // Nombre del archivo inicial
input DELIMITER Delimiter   =comma;                           // Divisor
input CODE StrType          =ansi;                            // Tipo de la cadena
input string mark4          ="Otros parámetro";              // Otros
//-------------------------------------------------------------------+
input string spread         ="2";                             // Spread fijo en puntos
input string Name           ="SberFX";                        // Nombre del símbolo creado


Fueron creadas las enumeraciones para algunos datos, por ejemplo: para el formato de la fecha y la hora:

enum DATE
{
yyyycmmcdd, // yyyy.mm.dd
yyyymmdd,   // yyyymmdd
yymmdd,     // yymmdd
ddmmyy,     // ddmmyy   
ddslmmslyy, // dd/mm/yy
mmslddslyy  // mm/dd/yy
// Los formados adicionales se añaden aquí
};

enum TIME
{
hhmmss,     // hhmmss
hhmm,       // hhmm
hhdmmdss,   // hh:mm:ss
hhdmm       // hh:mm
// Los formados adicionales se añaden aquí
};


Si falta un formato necesario, hay que añadirlo.

Luego, se abre el archivo adicional. Para una edición conveniente de datos formateados, sugiero guardarlos en el archivo CSV. Los escribimos de manera paralela en la estructura  MqlRates para la creación automática del símbolo personalizado.

// Open in file
    
  int out =FileOpen(InFileName,FILE_READ|StrType|FILE_TXT);
  if(out==INVALID_HANDLE)
  {
   Alert("Falta al abrir el archivo de lectura");
   return; 
  }
// Open out file
  int in =FileOpen(Name+"(f).csv",FILE_WRITE|FILE_ANSI|FILE_CSV);
  if(in==INVALID_HANDLE)
  {
   Alert("Falta al abrir el archivo de escritura");
   return; 
  }
  //---Insert caption string  
  string Caption ="<DATE>\t<TIME>\t<OPEN>\t<HIGH>\t<LOW>\t<CLOSE>\t<TICKVOL>\t<VOL>\t<SPREAD>";
  FileWrite(in,Caption);
//-----------------------------------------------------------
string fdate="",ftime="",open="";
string high="",low="",close="",vol="";
int left=0,right=0;

string str="",temp="";
for(int i=0;i<SkipString;i++)
   {
   str =FileReadString(out);
   i++;
   }
MqlRates Rs[];
ArrayResize(Rs,43200,43200);  //43200 en un mes
datetime time =0;


El código inicial tiene que encontrarse en el catálogo MQL5/Files. La variable externa SkipString representa el número de las líneas en el encabezado del archivo a saltar. Para poder usar los espacios y tabulaciones como separadores, el archivo se abre con la bandera  FILE_TXT. 

Luego, tenemos que extraer los datos desde la cadena. La posición de datos se establece por los parámetros de esntrada. La enumeración se empieza desde uno. Como ejemplo, usamos las cotizaciones del Sberbank.


Aquí la posición de la fecha es 1, de la hora es 2, etc. SkipString=1.

Para analizar la cadena, se puede usar la función StringSplit(), pero para monitorear los errores en el archivo inicial, sería mejor escribir las funciones personalizadas. Se puede añadir el análisis de datos a ellas. Aunque, desde luego, es más fácil usar el código StringSplit(). La primera función -busca los límites de datos- recibe la cadena, separador y la posición. Escribe los límites en las variables «a» y «b» que se pasan por referencia.

//---Buscar los límites de la posición de datos-------------------------------------+
bool SearchBorders(string str,int pos,int &a,int &b,DELIMITER delim)
{
// Variables auxiliares
int left=0,right=0;
int count=0;
int start=0;
string delimiter="";
//-------------------------------------------------------------------+

switch(delim)
{
case comma : delimiter =",";
   break; 
case tab : delimiter ="/t";
   break;
case space : delimiter =" ";
   break;
case semicolon : delimiter =";";
   break;
}

while(count!=pos||right!=-1)
   {
   right =StringFind(str,delimiter,start);
      
   if(right==-1&&count==0){Print("Wrong date");return false;} //Datos incorrectos
   
   if(right==-1)
      {
      right =StringLen(str)-1;
      a =left;
      b =right;
      break;
      }
   
   count++;
      if(count==pos)
      {
      a =left;
      b =right-1;
      return true;
      }
   left =right+1;
   start =left;
   }

return true;
}


Ahora, obtenemos los datos correspondientes usando la función StringSubstr() . Es necesario convertir los valores obtenidos en el formato necesario. Para eso, escribimos las funciones de conversión de la fecha y la hora. Por ejemplo, el código de la función de la conversión de la fecha:

//---Formateo de la fecha---------------------------------------------+
//2017.01.02
string DateFormat(string str,DATE date)
{

string res="";
string yy="";
 
switch(date)
  { 
   case yyyycmmcdd :  //nuestro formato
      res =str;
      if(StringLen(res)!=10)res=""; // comprobación del formato de la fecha
   case yyyymmdd :
      res =StringSubstr(str,0,4)+"."+StringSubstr(str,4,2)+"."+StringSubstr(str,6,2);
      if(StringLen(res)!=10)res=""; // comprobación en el formato de la fecha    
      break;
   case yymmdd :
      yy =StringSubstr(str,0,2);
      if(StringToInteger(yy)>=70)
         yy ="19"+yy;
      else
         yy ="20"+yy;
      res =yy+"."+StringSubstr(str,2,2)+"."+StringSubstr(str,4,2);
      if(StringLen(res)!=10)res=""; // comprobación del formato de la fecha  
      break;
//---Otros formatos (Código completo en el archivo)----------------
//Si es necesario, añadimos otros formatos      
   default :
      break; 

  }

return res;
}

Si no hay formato necesario (por ejemplo, la fecha en forma del 01 de Enero de 18), hay que adicionarlo. Aquí, se verifica si los datos recibidos corresponden al formato necesario  (en caso de un error en el archivo fuente)   if(StringLen(res)!=10) res="";Entiendo que es una comprobación bastante superficial. Pero el análisis de datos no es una tarea fácil, y para su filtro tendríamos que hacer un programa separado. En caso del error, la función devuelve res =,"" entonces esta línea simplemente se ignora.  

Aquí, tenemos prevista la siguiente conversión para los formatos tipo ddmmaa, donde el año se escribe con dos cifras. Los años >=70 se convierten en 19aa, los valores menos, se convierten en 20aa.

Después de la conversión de los formatos, grabamos los datos en las variables correspondientes y formamos una cadena final.

while(!FileIsEnding(out))
   {
   str =FileReadString(out);
   count++;
//---fdate-----------------------------
   if(SearchBorders(str,DatePosition,left,right,Delimiter))
   {
   temp =StringSubstr(str,left,right-left+1);
   fdate =DateFormat(temp,indate);
   if(fdate==""){Print("Error in line ",count);continue;}
   }
   else {Print("Error en la cadena   ",count);continue;}
//---Otros datos se procesan de la misma forma


Si se encuentra un error en las funciones SearchBorders, DateFormat o TimeFormat, la cadena se ignora y su número de orden se graba usando la función Print(). Todas las enumeraciones y las funciones de conversión de los formatos se encuentran en el archivo de inclusión FormatFunctions.mqh 

Luego, se forma y se escribe la cadena resultante. Los datos se asignan a los elementos correspondiente de la estructura MqlRates.

//-------------------------------------------------------------------+
   str =fdate+","+ftime+","+open+","+high+","+low+","+close+","+vol+","+vol+","+Spread;
   FileWrite(in,str);
//---Llenando MqlRates --------------------------------------------+
   Rs[i].time               =time;
   Rs[i].open               =StringToDouble(open);
   Rs[i].high               =StringToDouble(high);
   Rs[i].low                =StringToDouble(low);
   Rs[i].close              =StringToDouble(close);
   Rs[i].real_volume        =StringToInteger(vol);
   Rs[i].tick_volume        =StringToInteger(vol);
   Rs[i].spread             =int(StringToInteger(Spread));
   i++;
//-------------------------------------------------------------------+   
   }


Después de leer todas las cadenas, el array dinámico recibe su tamaño final y los archivos se cierran:

   ArrayResize(Rs,i);
   FileClose(out);
   FileClose(in);

Ahora, tenemos todo listo para crear nuestro símbolo personalizado. Además de esto, tenemos un archivo CSV, que puede ser fácilmente editado directamente en MetaEditor. Teniendo el archivo CSV, podemos crear los símbolos personalizados usando los métodos estándar del terminal MetaTrader 5.



Creando los símbolos personalizados usando MQL5

Ahora, cuando tenemos preparados todos los datos, solamente nos queda añadir un símbolo personalizado.

   CustomSymbolCreate(Name);
   CustomRatesUpdate(Name,Rs);

Para importar las cotizaciones, se usa la función CustomRatesUpdate(). Eso significa que se puede usar el programa no sólo para crear un símbolo, sino también para añadir datos nuevos. Si el símbolo ya existe, la función  CustomSymbolCreate() devolverá -1 (menos uno) y la ejecución del programa continuará, así las cotizaciones serán actualizadas a través de la función  CustomRatesUpdate(). El símbolo se muestra en la ventana MarketWatch y se resalta con el color verde.



Ahora, podemos abrir el gráfico y asegurarnos de que todo funciona correctamente:


Gráfico de EURUSD


Definición de las especificaciones (propiedades del símbolo)

Al testear un instrumento, podemos necesitar la configuración de varias características suyas (especificaciones). Escribí un archivo separado de inclusión «Specification» que permitirá la edición adecuada de las propiedades. En la función SetSpecifications(), se establecen las propiedades del símbolo. Aquí, se reúnen todas las propiedades del símbolo incluidas en las enumeraciones   ENUM_SYMBOL_INFO_INTEGER, ENUM_SYMBOL_INFO_DOUBLE , ENUM_SYMBOL_INFO_STRING.

void SetSpecifications(string Name)
   {  
//---Integer Properties-------------------------------------
//   CustomSymbolSetInteger(Name,SYMBOL_CUSTOM,true);                       // bool Indica en que el símbolo es personalizado
//   CustomSymbolSetInteger(Name,SYMBOL_BACKGROUND_COLOR,clrGreen);         // color Color del fondo con el que resalta el símbolo en Market Watch
//   Otras propiedades Integer
//---Double Properties ---------------------------------------------------   
//   CustomSymbolSetDouble(Name,SYMBOL_BID,0);                              // Bid - la mejor oferta de venta 
//   CustomSymbolSetDouble(Name,SYMBOL_BIDHIGH,0);                          // Bid máximo al día
//   CustomSymbolSetDouble(Name,SYMBOL_BIDLOW,0);                           // Bid mínimo al día
//   Otras propiedades Double
//---String Properties-----------------------------------------------+
//   CustomSymbolSetString(Name,SYMBOL_BASIS,"");                           // Nombre del activo base para un instrumento derivado
//   CustomSymbolSetString(Name,SYMBOL_CURRENCY_BASE,"");                   // Divisa base del instrumento
//   CustomSymbolSetString(Name,SYMBOL_CURRENCY_PROFIT,"");                 // Divisa del beneficio
//   Otras propiedades String
}


Ejecutamos esta función después de la función CustomSymbolCreate. No se sabe de antemano qué tipo de instrumento es éste: futuro, acción u opción. Por eso, la mayoría de las propiedades simplemente no son necesarias y serán comentadas. Sólo algunas de las líneas no están comentadas en el código fuente:

   CustomSymbolSetInteger(Name,SYMBOL_CUSTOM,true);                       // bool Indica en que el símbolo es personalizado
   CustomSymbolSetInteger(Name,SYMBOL_BACKGROUND_COLOR,clrGreen);         // color Color del fondo con el que se resalta el símbolo en Market Watch
   CustomSymbolSetInteger(Name,SYMBOL_SELECT,true);                       // bool Indica en que el símbolo está seleccionado en Market Watch 
   CustomSymbolSetInteger(Name,SYMBOL_VISIBLE,true);                      // bool Indica en que el símbolo seleccionado se muestra en Market Watch


Los siguientes parámetros tampoco están comentados para los fines de la simulación: volumen mínimo, paso del volumen, paso del precio, tamaño del punto, es decir, sólo las características más necesarias. Estas características corresponden a las acciones de Sberbank. Está claro que el conjunto de las propiedades y sus características serán diferentes para otros instrumentos.

   CustomSymbolSetDouble(name,SYMBOL_POINT,0.01);               // Valor de un punto
   CustomSymbolSetDouble(name,SYMBOL_VOLUME_MIN,1);             // Volumen mínimo para una orden
   CustomSymbolSetDouble(name,SYMBOL_VOLUME_STEP,1);            // Paso mínimo del cambio del volumen
   CustomSymbolSetInteger(name,SYMBOL_DIGITS,2);                // int Número de dígitos después de la coma 
   CustomSymbolSetInteger(name,SYMBOL_SPREAD,2);                // int Tamaño del spread en puntos 
   CustomSymbolSetInteger(name,SYMBOL_SPREAD_FLOAT,false);      // bool Indicio del spread flotante
   CustomSymbolSetDouble(name,SYMBOL_TRADE_TICK_SIZE,0.01);	// Cambio mínimo del precio


Todo sería bien, si no tuviéramos que recompilar el código cada vez para establecer propiedades nuevas. Sería conveniente tener la posibilidad de realizar todo el trabajo simplemente introduciendo los parámetros. Por eso, tuve que cambiar un poco el enfoque. Las propiedades del símbolo van a ubicarse en un simple archivo de texto Specifications.txt que se edita manualmente para cada símbolo nuevo. En este caso, no hace falta recompilar el código.

Además, es mejor editar el archivo de texto en MetaEditor, principalmente, debido al resalto acostumbrado de los parámetros y datos. Las propiedades están escritas en el siguiente formato:



Los datos se separan por comás. Las cadenas se analizan de la siguiente manera:

   while(!FileIsEnding(handle))
     {
     str =FileReadString(handle);
//--- Salto de líneas ------------------------+     
     if(str=="") continue;
     if(StringFind(str,"//")<10) continue;
//------------------------------------------+     
     sub =StringSplit(str,u_sep,split);
     if(sub<2) continue;
     SetProperties(SName,split[0],split[1]);
     }


La línea se omite si está vacía o el símbolo del comentario "//" se encuentra al principio (posición <10). Luego la cadena se divide en sucadenas por la función  StringSplit(). Luego, estas cadenas se pasan a la función SetProperties() para establecer las propiedades del símbolo. Estructura del código de la función:

void SetProperties(string name,string str1,string str2)
   {
   int n =StringTrimLeft(str1);
       n =StringTrimRight(str1);
       n =StringTrimLeft(str2);
       n =StringTrimRight(str2);
       
   if(str1=="SYMBOL_CUSTOM")
      {
      if(str2=="0"||str2=="false"){CustomSymbolSetInteger(name,SYMBOL_CUSTOM,false);}
      else {CustomSymbolSetInteger(name,SYMBOL_CUSTOM,true);}
      return;
      }
   if(str1=="SYMBOL_BACKGROUND_COLOR")
      {
      CustomSymbolSetInteger(name,SYMBOL_BACKGROUND_COLOR,StringToInteger(str2));
      return;
      }
   if(str1=="SYMBOL_CHART_MODE")
      {
      if(str2=="SYMBOL_CHART_MODE_BID"){CustomSymbolSetInteger(name,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_BID);}
      if(str2=="SYMBOL_CHART_MODE_LAST"){CustomSymbolSetInteger(name,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_LAST);}
      return;
      }
//--- Otras propiedades del símbolo
}


Si el usuario deja los espacios y los signos de tabulación al editar el código, se añaden dos funciones más:  StringTrimLeft()y StringTrimRight().

El código fuente se encuentra en el archivo de inclusión PropertiesSet.mqh.

Ahora, todas las propiedades del símbolo se establecen en el archivo de texto adjunto y la recompilación no es necesaria. Adicionalmente, se adjuntan ambas versiones del código, mientras que la primera versión de la definición de las propiedades a través del archivo incluido simplemente está comentado.


Interfaz

Para una redacción conveniente del código, los parámetros de entrada se introducen a través de los parámetros input. Pero si no hay nada que editar, podemos pensar en las interfaces. Desarrollé un panel de controlde los parámetros de entrada para una versión final:


Brevemente sobre el código del panel de entrada. Aquí, se usa el conjunto estándar de los controles desde los archivos de inclusión:

#include <Controls\Dialog.mqh>
#include <Controls\Label.mqh>
#include <Controls\Button.mqh>
#include <Controls\ComboBox.mqh>

Para el botón «OK» fue creado el manipulador (handle) de eventos.

//+------------------------------------------------------------------+ 
//| Event Handling                                                   | 
//+------------------------------------------------------------------+ 
EVENT_MAP_BEGIN(CFormatPanel) 
ON_EVENT(ON_CLICK,BOK,OnClickButton) 
EVENT_MAP_END(CAppDialog)

void CFormatPanel::OnClickButton(void) 
  { 
// Programa escrito encima
  } 

Ahora, prácticamente, el código entero del programa descrito encima se traspasa en este manipulador de eventos. Los parámetros externos se convierten en las variables locales.

long SkipString        =1;                              // Número de cadenas omitidas
DATE indate           =yyyymmdd;                        // Formato inicial de la fecha
TIME intime           =hhdmmdss;                        // Formato inicial de la hora
int DatePosition      =1;                               // Posición de la fecha
int TimePosition      =2;                               // Posición de la hora
// otros parámetros

La función Create() fue escrita para cada control. Por tanto, los valores necesarios se añaden a la lista de los controles después ejecutar esta función. Por ejemplo, para el formato de la fecha:

//-----------ComboBox Date Format------------------------------------+     
    if(!CreateComboBox(CDateFormat,"ComDateFormat",x0,y0+h+1,x0+w,y0+2*h+1))
     {
      return false;
     }
   CDateFormat.ListViewItems(6);
   CDateFormat.AddItem(" yyyy.mm.dd",0);
   CDateFormat.AddItem(" yyyymmdd",1);
   CDateFormat.AddItem(" yymmdd",2);
   CDateFormat.AddItem(" ddmmyy",3);
   CDateFormat.AddItem(" dd/mm/yy",4);
   CDateFormat.AddItem(" mm/dd/yy",5);
   CDateFormat.Select(1);
     }

Luego, estos valores se devuelven desde los campos de edición a las variables correspondientes:

long sw;  
SkipString =StringToInteger(ESkip.Text());

sw =CDateFormat.Value();
switch(int(sw))
{
   case 0 :indate =yyyycmmcdd;
      break;
   case 1 :indate =yyyymmdd;
      break;
   case 2 :indate =yymmdd;
      break;
   case 3 :indate =ddmmyy;
      break;
   case 4 :indate =ddslmmslyy;
      break;
   case 5 :indate =mmslddslyy;
      break;                
}
//Otras variables

 

Está claro que esta versión es mucho mayor, y si hace falta editar el código, es mejor trabajar con la versión input.


Parte 2. Guía paso a paso

En esta parte, se describen paso a paso las acciones que hace falta ejecutar para crear un símbolo personalizado de la bolsa de valores. Esta guía sirve para el caso cuando tenemos las cotizaciones de los formatos estándar y no necesitamos la edición del código. Por ejemplo, si hemos recibido las cotizaciones de finam.ru.  Si las cotizaciones están en algún formato no estándar, es necesario editar el código descrito en la parte 1.

Entonces, tenemos un archivo fuente con cotizaciones bursátiles de algún instrumento financiero. Por ejemplo, lo hemos recibido del sitio finam.ru, como ha sido dicho al principio del artículo. No olvidemos de que necesitamos las cotizaciones precisamente del timeframe de un minuto,

Hay dos opciones de la importación de datos descritas en el artículo. Se puede usar el script CreateCustomSymbol y el Asesor Experto CreateSymbolPanel que tiene el panel de entrada de los parámetros. Ambos ejecutan exactamente el mismo trabajo. Por ejemplo, vamos a considerar la operación con el panel de entrada de datos. En los ejemplos de demostración, se usan las cotizaciones de Sberbank en la Bolsa de Moscú. Se encuentran en el archivo adjunto sb.csv.

1. Colocación de archivos

Antes de nada, tenemos que colocar nuestro archivo con cotizaciones en la carpeta MQL5/Files. Eso está relacionado con el concepto de programación en el lenguaje MQL5,  donde, por razones de seguridad, el trabajo con los archivos se encuentra bajo un estricto control. La manera más fácil de encontrar la carpeta necesaria es abrirla en MetaTrader. Para eso, haga clic derecho en la carpeta «Archivo» (en la ventana del navegador), y seleccione «Abrir carpeta» (en el menú contextual).


El archivo fuente con datos tiene que ser colocado precisamente en esta carpeta (el lugar donde hay que colocar los archivos del programa se describe al final de la sección «Archivos»). Ahora, se puede abrirlo en MetaEditor.



El archivo de texto Specifications.txt. tiene que ubicarse en la misma carpeta. Establece las propiedades del símbolo.

2. Introducción de parámetros

El siguiente paso es determinar las posiciones y el formato de datos, seleccionar las propiedades del archivo y establecer el nombre para el símbolo personalizado. El ejemplo de cómo rellenar los campos se muestra en las siguientes imágenes:


Hay que pasar estos datos al panel. En esta versión, se usa el spread fijo en puntos, el spread flotante no se modela. Por tanto, introduzca el valor del spread según sus necesidades.



Escriba el nombre completo del archivo, incluyendo la extensión.

Ahora, antes de hacer clic en el botón «OK», hay que establecer las especificaciones necesarias del símbolo. Se encuentran en el archivo de texto Specifications.txt que hemos colocado en el carpeta MQL5/Files.

Además, es mucho mejor editar el archivo de texto en MetaEditor. Sobre todo, eso se debe al resalto de datos cómodo y conveniente para la edición. Si no comprende alguna propiedad, utilice la ayuda, colocando el cursor sobre ella y pulsando el botón «F1».



Las propiedades se resaltan en rojo, los valores, en verde. Las propiedades (//) comentadas y no usadas, se muestran en gris. Obsérvese que los datos se separan con coma. Al editar las propiedades, no se puede borrarlas. Para evitar los errores, mantenga el formato existente.

Para editar una propiedad, primero, elimine su comentario (quite (//)), y luego, establezca el valor aopropiado. Sólo un conjunto mínimo de propiedades está disponible en el archivo adjunto: paso del precio, valor del punto, lote mínimo, etc.

Estas características (en el archivo inicial) corresponden a las acciones de Sberbank en la Bolsa de Moscú. Estas características serán diferentes para otros instrumentos, y habrá que editarlas. 

El conjunto mínimo de las propiedades necesarias se encuentra al principio del archivo.

Normalmente, para las acciones el número de dígitos tras la coma (SYMBOL_DIGITS) será igual a 2, el coste del punto es 0,01 rublo. El número de dígitos es 0 y el punto es igual a 1 rublo para los futuros. Véase las especificaciones en moex.com.

Después de establecer las propiedades necesarias, hacemos clic en «OK». El símbolo creado aparece en la ventana. En mi ejemplo, es verde.


Abra el gráfico para comprobar:



Todo está bien, ahora podemos empezar la simulación de estrategias para el símbolo.

La simulación del símbolo personalizado no se diferencia en nada de la prueba del símbolo habitual. Lo importante es configurar correctamente sus especificaciones.

Como ejemplo, vamos a usar cualquier EA estándar desde la entrega del terminal (Moving Average) y utilizamos nuestros datos:

 


Todo funciona tal como se esperaba. Para añadir nuevas cotizaciones o modificar las propiedades, basta con volver a ejecutar las acciones descritas para el símbolo ya existente. Si las especificaciones no han cambiado, simplemente pulsamos «OK» sin editar las propiedades.


Archivos

El archivo comprimido incluye los ficheros en los directorios en los cuales tiene que ubicarlos en su ordenador: