Descargar MetaTrader 5

Cómo preparar las cotizaciones MetaTrader 5 para otros programas

7 marzo 2014, 12:47
Anatoli Kazharski
0
704

Contenido

Introducción
1. Cuestiones tratadas
2. Formato de datos
3. Parámetros externos del programa
4. Comprobación de los parámetros introducidos por el usuario
5. Variables globales
6. Panel de información
7. Bloque principal del programa
8. Cómo crear carpetas y grabar datos en un archivo
Conclusión


Introducción

Antes de comenzar a estudiar MQL5, probé a usar muchos otros programas de desarrollo de sistemas comerciales. No puedo decir que hay perdido el tiempo. Por supuesto que en algunos de ellos se pueden encontrar instrumentos útiles que permiten ahorrar tiempo, aclararse con bastantes cuestiones, cazar algunos mitos y elegir bastante rápido el camino a seguir sin conocer lenguajes de programación.

Para trabajar con estos programas es necesario disponer de datos históricos. Ya que no existe un estándar determinado de formato de datos, muchas veces, antes de ponerse a usarlos, había que redactarlos (por ejemplo, con Excel) en un formato que fuese comprendido por tal o cual programa. Incluso si logras descubrir cómo funcionan todos los detalles, luego te das cuenta de que hay que hacer mucho trabajo de manera manual. En diversos foros se pueden encontrar varias versiones de scripts para copiar las cotizaciones de MetaTrader 4 al formato necesario. En caso de que sea necesario, podemos desarrollar también una versión del script para MQL5.


1. Cuestiones tratadas

En el artículo trataremos las siguientes cuestiones:

  • Cómo trabajar con la lista de símbolos en la ventana Observación del Mercado y con la lista general de símbolos en el servidor.
  • Comprobación de la profundidad accesible de los datos y, en caso necesario, la descarga de la cantidad que haga falta, con el manejo adecuado de las diferentes situaciones.
  • Visualización de la información de los datos requeridos en el panel personalizado del gráfico y en el diario.
  • Cómo preparar los datos para su grabación (anotación) en el formato indicado por el usuario.
  • Cómo crear un directorio para los archivos.
  • Cómo grabar los datos en un archivo.


2. Formato de los datos

Voy a mostrar un ejemplo sobre la preparación de datos para su en uso en el programa NeuroShell DayTrader Professional (en lo sucesivo NSDT). He intentado usar las versiones 5 y 6 del NSDT y me he dado cuenta de que estas versiones tienen requerimientos diferentes en lo que respecta al formato de los datos En la 5 versión del NSDT, los datos sobre la fecha y la hora deben estar ubicadas en columnas diferentes. La primera línea del archivo deberá tener el siguiente aspecto:

"Date" "Time" "Open" "High" "Low" "Close" "Volume"

En la 6 versión del NSDT, la línea de encabezado ya deberá tener otro aspecto, para que el programa acepte el archivo. Es decir, que la fecha y la hora debrán ubicarse en la misma columna:

Date,Open,High,Low,Close,Volume

En MetaTrader 5 existe una opción estándar para guardar las cotizaciones en un archivo de formato *.csv. Los datos en el archivo tienen el aspecto siguiente:

Dib. 1. Datos guardados por el terminal MetaTrader 5

Dib. 1. Datos guardados por el terminal MetaTrader 5


Simplemente con redactar la línea de los encabezados no será suficiente, dado que esta deberá tener un formato distinto. Para la 5 versión del NSDT será:

dd.mm.yyyy,hh:mm,Open,High,Low,Close,Volume
Para la 6 versión del NSDT será:
dd/mm/yyyy hh:mm,Open,High,Low,Close,Volume

En los parámetros externos del script utilizaremos listas desplegables, en las que el usuario podrá elegir el formato necesario. Además de la elección de formato de los encabezados y las fechas, le damos al usuario la posibilidad de elegir la cantidad de símbolos cuyos datos quiere guardar en los archivos. Para esto dispondremos de tres opciones:

  • Grabar los datos sólo del símbolo actual (ONLY CURRENT SYMBOL), en cuyo gráfico fue lanzado el script.
  • Grabar en los archivos los datos de los símbolos que se encuentren en la ventana de Observación del Mercado (MARKETWATCH SYMBOLS).
  • Grabar los datos de todos los símbolos accesibles en el servidor (ALL LIST SYMBOLS).

Para crear estos tipos de lista, en el código del script, antes de los parámetros externos tenemos que introducir el código siguiente:

//_________________________________
// ENUMERACIÓN_DE _LOS_FORMATOS_DE_ENCABEZADO
enum FORMAT_HEADERS
  {
   NSDT_5 = 0, // "Date" "Time" "Open" "High" "Low" "Close" "Volume"
   NSDT_6 = 1  // Date,Open,High,Low,Close,Volume
  };
//---
//___________________________
// ENUMERACIÓN_DE _LOS_FORMATOS_DE_FECHA
enum FORMAT_DATETIME
  {
   SEP_POINT1 = 0, // dd.mm.yyyy hh:mm
   SEP_POINT2 = 1, // dd.mm.yyyy, hh:mm
   SEP_SLASH1 = 2, // dd/mm/yyyy hh:mm
   SEP_SLASH2 = 3  // dd/mm/yyyy, hh:mm
  };
//---
//____________________________
// ENUMERACIÓN_DE_LOS_MODOS_DE_GRABACIÓN
enum CURRENT_MARKETWATCH
  {
   CURRENT          = 0, // ONLY CURRENT SYMBOLS
   MARKETWATCH      = 1, // MARKETWATCH SYMBOLS
   ALL_LIST_SYMBOLS = 2  // ALL LIST SYMBOLS
  };

Puede leer con más detalle acerca de las enumeraciones en la Guía de MQL5.


3. Parámetros externos del programa

Ahora ya se puede formar la lista completa de parámetros externos del script:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| PARÁMETROS_EXTERNOS                                              |
//+------------------------------------------------------------------+
input datetime            start_date     = D'01.01.2011'; // Start Date
input datetime            end_date       = D'18.09.2012'; // End Date
input FORMAT_HEADERS      format_headers = NSDT_5;     // Format Headers
input FORMAT_DATETIME     format_date    = SEP_POINT2; // Format Datetime
input CURRENT_MARKETWATCH curr_mwatch    = CURRENT;    // Mode Write Symbols
input bool                clear_mwatch   = true;        // Clear Market Watch
input bool                show_progress  = true;        // Show Progress (%)

Los parámetros externos se usan para lo siguiente:

  • Con ayuda de los parámetros Start Date (start_date) y End Date (end_date) el usuario puede establecer el diapsón de fechas para la grabación.
  • En la lista desplegable Format Headers (format_headers) se puede elegir el formato del encabezado.
  • En la lista desplegable Format Datetime (format_date) se puede elegir el formato de la fecha y la hora.
  • En la lista desplegable Mode Write Symbols (curr_mwatch) se puede elegir la cantidad de símbolos para la grabación.
  • Si el parámetro Clear Market Watch (clear_mwatch) es true, esto permitirá al usuario eliminar de la ventana de Observación del Mercado todos los símbolos después de la grabación. Esto sólo concierne a los símbolos cuyos gráficos no se encuentren abiertos en el momento actual.
  • El parámetro Show Progress (%) (show_progress) muestra en el panel de información el progreso de la grabación del archivo. Si el parámetro se encuentra desactivado, el proceso tendrá lugar más rápido.

Este es el aspecto que tendrán los parámetros externos durante el inicio:

Dib. 2. Ventana de parámetros externos del programa

Dib. 2. Ventana de parámetros externos del programa


4. Comprobación de los parámetros introducidos por el usuario

Crearemos una función que comprobará la corrección de los parámetros introducidos por el usuario antes del código base. Por ejemplo, la fecha de inicio en el parámetro Start Date deberá estar antes que la de End Date. El formato de los encabezados debe corresponderse con el formato de la fecha y la hora. Si el usuario se ha equivocado al configurar los parámetros, entonces aparecerá la advertencia correspondiente y el programa se detendrá.

Ejemplo de advertencia:

Dib. 3. Ejemplo de advertencia sobre la introducción incorrecta de los valores

Dib. 3. Ejemplo de advertencia sobre valores incorrectos


Función ValidationParameters():

//____________________________________________________________________
//+------------------------------------------------------------------+
//| COMPROBACIÓN_DE_LA_CORRECCIÓN_DE_LOS_PARÁMETROS                  |
//+------------------------------------------------------------------+
bool ValidationParameters()
  {
   if(start_date>=end_date)
     {
      MessageBox("¡La fecha inicial debe ser anterior a la fecha final!\n\n"
                 "El programa no puede continuar, por favor, inténtelo de nuevo". Inténtelo de nuevo.",
                 //---
                 "¡Error en los parámetros!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   if(format_headers==NSDT_5 && 
      (format_date==SEP_POINT1 || format_date==SEP_SLASH1))
     {
      MessageBox("Para los encabezados del siguiente formato:\n\n"
                 "\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\"\n\n"
                 "Puede seleccionar el formato de fecha/hora de dos tipos:\n\n"
                 "dd.mm.yyyy, hh:mm\n"
                 "dd/mm/yyyy, hh:mm\n\n"
                 "El programa no puede continuar. Inténtelo de nuevo.",
                 //---
                 "¡El formato de los encabezados de la fecha/hora no coincide!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   if(format_headers==NSDT_6 && 
      (format_date==SEP_POINT2 || format_date==SEP_SLASH2))
     {
      MessageBox("Para los encabezados del siguiente formato:\n\n"
                 "Date,Open,High,Low,Close,Volume\n\n"
                 "Puede seleccionar el formato de fecha/hora de dos tipos:\n\n"
                 "dd.mm.yyyy hh:mm\n"
                 "dd/mm/yyyy hh:mm\n\n"
                 "El programa no puede continuar. Inténtelo de nuevo.",
                 //---
                 "¡El formato de los encabezados de la fecha/hora no coincide!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   return(false);
  }


5. Variables globales

Ahora vamos a determinar todas las variables globales y las matrices que se van a utilizar en el script:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| VARIABLES_GLOBALES_Y_MATRICES                                    |
//+------------------------------------------------------------------+
MqlRates rates[]; // Matriz para la copia de datos
//---
string symbols[]; // Matriz de símbolos
//---
// Matriz de nombres de objetos gráficos
string arr_nmobj[22]=
  {
   "fon","hd01",
   "nm01","nm02","nm03","nm04","nm05","nm06","nm07","nm08","nm09","nm10",
   "nm11","nm12","nm13","nm14","nm15","nm16","nm17","nm18","nm19","nm20"
  };
//---
// Matriz del texto representado por objetos gráficos
string arr_txtobj[21];
//---
string path="";         // Ruta al archivo
int cnt_symb=0;         // Cantidad de símbolos
int sz_arr_symb=0;      // Tamaño de la matriz de los símbolos
int bars=0;             // Cantidad de barras especificada por el TF
int copied_bars=0;      // Número de barras copiadas para la escritura
double pgs_pcnt=0;      // Progreso de la escritura
int hFl=INVALID_HANDLE;  // Descriptor de archivo
//---
string   // Variables para el formateo de la fecha
sdt="",  // Línea de la fecha
dd="",   // Día
mm="",   // Mes
yyyy="", // Año
tm="",   // Hora
sep="";  // Separador
//---
int max_bars=0; // Cantidad máxima de barras en la configuración del terminal
//---
datetime
first_date=0,        // Primera fecha disponible en el periodo indicado
first_termnl_date=0, // Primera fecha disponible en la base de datos del terminal
first_server_date=0, // Primera fecha disponible en la base de datos del servidor
check_start_date=0;  // Valor de fecha comprobado correctamente


6. Panel de información

Ahora hay que aclarar lo que va a aparecer en el panel de información. Se pueden usar tres objetos gráficos como fondo:

  • El objeto más simple y obvio es, en primer lugar – "La etiqueta rectangular" (OBJ_RECTANGLE_LABEL).
  • El que quiera transmitir a su interfaz un aspecto único, podrá utilizar el objeto gráfico "Mapa de bits" (OBJ_BITMAP).
  • También se puede usar como fondo el objeto gráfico "Editar" (OBJ_EDIT). Estableciendo la propiedad "sólo lectura" se puede excluir la posibilidad de introducir un texto. Existe otra ventaja más a la hora de usar el objeto "Editar". Si ha creado un panel de información en el experto y quiere que tenga el mismo aspecto durante el test en el modo de visualización, la única manera de hacerlo es con la ayuda de este objeto. Ni OBJ_RECTANGLE_LABEL, ni OBJ_BITMAP se muestran durante el test en el modo visualización.

Aunque en nuestro caso se ha creado un script, y no un experto, aun así, como ejemplo pondremos un fondo con el objeto OBJ_EDIT. Puede ver el resultado final en el dibujo de abajo:

Dib. 4. Aspecto exterior del panel de información

Dib. 4. Aspecto exterior del panel de información


Vamos a describir toda la información que aparece en el panel:

  • Symbol (current/total) – es el símbolo cuyos datos son descargados/copiados/grabados en el momento actual. El número entre paréntisis a la izquierda muestra el símbolo actual. A la derecha se nos muestra la cantidad de símbolos con la que va a funcionar el script.
  • Path Symbol – es la ruta hasta el símbolo o la categoría del símbolo a la que pertenece. Si hace surgir el menú de contexto en la ventana de Observación del Mercado (pulsando el botón derecho del ratón) y elige "Símbolos...", se abrirá una ventana en la que podrá ver la lista completa de todos los símbolos. Puede obtener información más detallada sobre ello en la Guía del terminal.
  • Timeframe – periodo (timeframe). Se utilizará el timeframe en el que se inició el script.
  • Input Start Date – es la fecha de inicio de los datos que el usuario indicó en los parámetros del script.
  • First Date (H1) – es la primera fecha accesible (barra) de los datos del time frame actual.
  • First Terminal Date (M1) – es la primera fecha accesible del time frame M1 en los datos del terminal ya existentes.
  • First Server Date (M1) – es la primera fecha accesible del time frame M1 en el servidor.
  • Max. Bars In Options Terminal – es la cantidad máxima de barras que se pueden representar en el gráfico, y está establecida en la configuración del terminal.
  • Copied Bars – es la cantidad de barras copiada para su posterior grabación.
  • Progress Value Current Symbol – es el valor porcentual de los datos del símbolo actual.

Más abajo tenemos el código de ese tipo de panel de información:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| PANEL_DE_INFORMACIÓN                                             |
//|------------------------------------------------------------------+
void InfoTable(int s)
  {
   int fnt_sz=8;            // Tamaño de la fuente
   string fnt="Calibri";     // Fuente del encabezado
   color clr=clrWhiteSmoke;  // Color
//---
   int xH=300;
   int height_pnl=0;
   int yV1=1,yV2=12,xV1=165,xV2=335,xV3=1;
//---
   string sf="",stf="",ssf="";
   bool flg_sf=false,flg_stf=false,flg_ssf=false;
//---
   if(show_progress) { height_pnl=138; } else { height_pnl=126; }
//---
   flg_sf=SeriesInfoInteger(symbols[s],_Period,SERIES_FIRSTDATE,first_date);
   flg_stf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_termnl_date);
   flg_ssf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date);
//---
   if(flg_sf) { sf=TSdm(first_date); } else { sf="?"; }
   if(flg_stf) { stf=TSdm(first_termnl_date); } else { stf="?"; }
   if(flg_ssf) { ssf=TSdm(first_server_date); } else { ssf="?"; }
//---
   if(cnt_symb==0) { cnt_symb=1; }
//---
   int anchor1=ANCHOR_LEFT_UPPER,anchor2=ANCHOR_RIGHT_UPPER,corner=CORNER_LEFT_UPPER;
//---
   string path_symbol=SymbolInfoString(symbols[s],SYMBOL_PATH);
   path_symbol=StringSubstr(path_symbol,0,StringLen(path_symbol)-StringLen(symbols[s]));
//---
   arr_txtobj[0]="INFO TABLE";
   arr_txtobj[1]="Symbol (current / total) : ";
   arr_txtobj[2]=""+symbols[s]+" ("+IS(s+1)+"/"+IS(cnt_symb)+")";
   arr_txtobj[3]="Path Symbol : ";
   arr_txtobj[4]=path_symbol;
   arr_txtobj[5]="Timeframe : ";
   arr_txtobj[6]=gStrTF(_Period);
   arr_txtobj[7]="Input Start Date : ";
   arr_txtobj[8]=TSdm(start_date);
   arr_txtobj[9]="First Date (H1) : ";
   arr_txtobj[10]=sf;
   arr_txtobj[11]="First Terminal Date (M1) : ";
   arr_txtobj[12]=stf;
   arr_txtobj[13]="First Server Date (M1) : ";
   arr_txtobj[14]=ssf;
   arr_txtobj[15]="Max. Bars In Options Terminal : ";
   arr_txtobj[16]=IS(max_bars);
   arr_txtobj[17]="Copied Bars : ";
   arr_txtobj[18]=IS(copied_bars);
   arr_txtobj[19]="Progress Value Current Symbol : ";
   arr_txtobj[20]=DS(pgs_pcnt,2)+"%";
//---
   Create_Edit(0,0,arr_nmobj[0],"",corner,fnt,fnt_sz,clrDimGray,clrDimGray,345,height_pnl,xV3,yV1,2,C'15,15,15');
//---
   Create_Edit(0,0,arr_nmobj[1],arr_txtobj[0],corner,fnt,8,clrWhite,C'64,0,0',345,12,xV3,yV1,2,clrFireBrick);
//---
   Create_Label(0,arr_nmobj[2],arr_txtobj[1],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2,0);
   Create_Label(0,arr_nmobj[3],arr_txtobj[2],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2,0);
//---
   Create_Label(0,arr_nmobj[4],arr_txtobj[3],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*2,0);
   Create_Label(0,arr_nmobj[5],arr_txtobj[4],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*2,0);
//---
   Create_Label(0,arr_nmobj[6],arr_txtobj[5],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*3,0);
   Create_Label(0,arr_nmobj[7],arr_txtobj[6],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*3,0);
//---
   Create_Label(0,arr_nmobj[8],arr_txtobj[7],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*4,0);
   Create_Label(0,arr_nmobj[9],arr_txtobj[8],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*4,0);
//---
   Create_Label(0,arr_nmobj[10],arr_txtobj[9],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*5,0);
   Create_Label(0,arr_nmobj[11],arr_txtobj[10],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*5,0);
//---
   Create_Label(0,arr_nmobj[12],arr_txtobj[11],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*6,0);
   Create_Label(0,arr_nmobj[13],arr_txtobj[12],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*6,0);
//---
   Create_Label(0,arr_nmobj[14],arr_txtobj[13],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*7,0);
   Create_Label(0,arr_nmobj[15],arr_txtobj[14],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*7,0);
//---
   Create_Label(0,arr_nmobj[16],arr_txtobj[15],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*8,0);
   Create_Label(0,arr_nmobj[17],arr_txtobj[16],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*8,0);
//---
   Create_Label(0,arr_nmobj[18],arr_txtobj[17],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*9,0);
   Create_Label(0,arr_nmobj[19],arr_txtobj[18],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*9,0);
//---
   if(show_progress)
     {
      Create_Label(0,arr_nmobj[20],arr_txtobj[19],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*10,0);
      Create_Label(0,arr_nmobj[21],arr_txtobj[20],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*10,0);
     }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| CREACIÓN_DEL_OBJETO_LABEL                                        |
//+------------------------------------------------------------------+
void Create_Label(long   chrt_id,   // id del gráfico
                  string lable_nm,  // nombre del objeto
                  string rename,    // nombre representado
                  long   anchor,    // punto de vinculación
                  long   corner,    // ángulo de vinculación
                  string font_bsc,  // fuente
                  int    font_size, // tamaño de la fuente
                  color  font_clr,  // color de la fuente
                  int    x_dist,    // escala de coordenadas X
                  int    y_dist,    // escala de coordenadas Y
                  long   zorder)    // prioridad
  {
   if(ObjectCreate(chrt_id,lable_nm,OBJ_LABEL,0,0,0)) // creación de un objeto
     {
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,rename);          // configurar nombre
      ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc);        // configurar fuente
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr);      // configurar color de la fuente
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ANCHOR,anchor);       // configurar punto de vinculación
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner);       // configurar ángulo de vinculación
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size);  // configurar tamaño de la fuente
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist);    // configurar coordenadas X
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist);    // configurar coordenadas Y
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false);     // imposible destacar el objeto, si FALSE
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder);       // Prioridad más alta/más baja
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n");         // no hay descripción emergente, si "\n"
     }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| CREACIÓN_DE_UN_OBJETO_EDIT                                       |
//+------------------------------------------------------------------+
void Create_Edit(long   chrt_id,       // id del gráfico
                 int    nmb_win,       // número de la ventana (sub-ventana)
                 string lable_nm,      // nombre del objeto
                 string text,          // nombre representado
                 long   corner,        // ángulo de vinculación
                 string font_bsc,      // fuente
                 int    font_size,     // tamaño de la fuente
                 color  font_clr,      // color de la fuente
                 color  font_clr_brd,  // color de la fuente
                 int    xsize,         // anchura
                 int    ysize,         // altura
                 int    x_dist,        // escala de coordenadas X
                 int    y_dist,        // escala de coordenadas Y
                 long   zorder,        // prioridad
                 color  clr)           // color del fondo
  {
   if(ObjectCreate(chrt_id,lable_nm,OBJ_EDIT,nmb_win,0,0)) // creación de un objeto
     {
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,text);                     // configurar nombre
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner);                // configurar ángulo de vinculación
      ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc);                 // configurar fuente
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ALIGN,ALIGN_CENTER);         // centrar
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size);           // configurar tamaño de la fuente
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr);               // color de la fuente
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BORDER_COLOR,font_clr_brd);    // color del fondo
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BGCOLOR,clr);                  // color del fondo
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XSIZE,xsize);                  // anchura
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YSIZE,ysize);                  // altura
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist);             // configurar coordenadas X
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist);             // configurar coordenadas Y
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false);              // imposible destacar el objeto, si FALSE
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder);                // Prioridad más alta/más baja
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_READONLY,true);                // Sólo lectura
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n");                  // no hay descripción emergente, si "\n"
     }
  }

Después de que el script finalice su trabajo, o si el usuario elimina el script del gráfico antes de tiempo, es necesario eliminar todos los objetos gráficos creados por el script. Para ello usaremos las siguientes funciones:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| BORRAR_TODOS_LOS_OBJETOS_GRÁFICOS_CREADOS_POR_EL_SCRIPT          |
//+------------------------------------------------------------------+
void DelAllScriptObjects()
  {
// Obtenemos el tamaño de la matriz de los nombres de los objetos gráficos
   int sz_arr1=ArraySize(arr_nmobj);
//---
// Borramos todos los objetos
   for(int i=0; i<sz_arr1; i++)
     { DelObjbyName(arr_nmobj[i]);  }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| BORRAR_LOS_OBJETOS_POR_NOMBRE                                    |
//+------------------------------------------------------------------+
int DelObjbyName(string Name)
  {
   int nm_obj=0;
   bool res=false;
//---
   nm_obj=ObjectFind(ChartID(),Name);
//---
   if(nm_obj>=0)
     {
      res=ObjectDelete(ChartID(),Name);
      //---
      if(!res) { Print("Error al eliminar un objeto: - "+ErrorDesc(Error())+""); return(false); }
     }
//---
   return(res);
  }


7. Bloque principal del programa

La principal función en los scripts es OnStart(). Esta es la función que invoca a las demás para que puedan ejecutarse. Las bases del funcionamiento del programa están descritas con detalle en el siguiente código:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| SCRIPT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
//+------------------------------------------------------------------+
void OnStart()
  {
// Si los parámetros introducidos por el usuario son incorrectos,
// se mostrará un mensaje de error y el programa se cerrará
   if(ValidationParameters()) { return; }
//---
   max_bars=TerminalInfoInteger(TERMINAL_MAXBARS); // Obtener el número de barras disponible en la ventana
//---
   GetSymbolsToArray();           // Llenamos la matriz de símbolos con nombres
   sz_arr_symb=ArraySize(symbols); // Obtenemos el tamaño de la matriz de símbolos
//---
   SetSeparateForFormatDate();    // Establecemos el separador para el formato de la fecha
//---
// Revisamos todos los símbolos y grabamos sus datos en un archivo
   for(int s=0; s<=sz_arr_symb-1; s++)
     {
      copied_bars=0; // Ponemos a cero la variable de barras copiadas para la grabación
      pgs_pcnt=0.0;  // Ponemos a cero la variable del progreso de la grabación de los datos de símbolo 
      //---
      InfoTable(s); ChartRedraw();
      //---
      // Obtemos los datos del símbolo actual
      int res=GetDataCurrentSymbol(s);
      //---
      if(res==0) { BC } // Si es cero, entonces interrumpimos el ciclo o pasamos a la siguiente iteración
      //---
      if(res==2)        // Ejecución del programa interrumpida por el usuario
        {
         DelAllScriptObjects(); // Eliminamos los objetos del gráfico creado por el script
         //---
         Print("------\nEl usuario ha eliminado el script!"); break;
        }
      //---
      // Obtemos la ruta para la creación de un archivo y creamos un directorio para ellos
      // Si la línea está vacía, entonces interrumpimos el ciclo, o bien pasamos a la siguiente iteración
      if((path=CheckCreateGetPath(s))=="") { BC }
      //---
      WriteDataToFile(s); // Grabamos los datos en un archivo
     }
//---
// De ser necesario, se eliminan los símbolos de la ventana Observación del Mercado
   DelSymbolsFromMarketWatch();
//---
// Eliminamos los objetos del gráfico que ha creado el script
   Sleep(1000); DelAllScriptObjects();
  }

Veamos las funciones en las que suceden las acciones principales.

En la función GetSymbolsToArray() la matriz de símbolos (symbols[]) se llena con nombres de símbolos. El tamaño de la matriz y la cantidad de símbolos en ella dependen de qué variante ha elegido el usuario en el parámetro Mode Write Symbols (curr_mwatch).

Si el usuario sólo necesita datos sobre un símbolo, entonces el tamaño de la matriz será igual a 1.

ArrayResize(symbols,1); // Establecemos el tamaño de la matriz igual 1
symbols[0]=_Symbol;     // Especificamos el nombre del símbolo actual

Si el usuario necesita los datos de todos los símbolos de la ventana Observación del Mercado (MarketWatch) o todos los símbolos en general, entonces el tamaño de la matriz estará determinado por la función:

int SymbolsTotal(
   bool selected   // true – sólo los símbolos en MarketWatch
);

Para no hacer dos bloques para las dos versiones, prácticamente con el mismo código, haremos una función indicadora MWatchOrAllList(), que devolverá true o false. Este valor determinará de dónde se cogerá la lista de símbolos. Sólo de la ventana de Observación del Mercado (true) o de la lista general de símbolos (false).

//____________________________________________________________________
//+----------------------------------------------------------------------+
//| INDICADOR_DE_LA_VENTANA_OBSERVACIÓN_DEL_MERCADO_O_DE_LA_LISTA_GENERAL|
//+----------------------------------------------------------------------+
bool MWatchOrAllList()
  {
   if(curr_mwatch==MARKETWATCH) { return(true); }
   if(curr_mwatch==ALL_LIST_SYMBOLS) { return(false); }
//---
   return(true);
  }

Después de obtener la cantidad de símbolos en el ciclo, repasamos la lista entera, aumentando en cada iteración el tamaño de la matriz en uno y tras obtener el nombre del símbolo a través del número de índice, introducimos la matriz, con ayuda de la función SymbolName().

int SymbolName(
   int pos,        // número en la lista
   bool selected   // true – sólo símbolos de MarketWatch
);

En la función SymbolName() también utilizamos la función-indicador MWatchOrAllList() para elegir la lista de símbolos. El código completo de la función GetSymbolsToArray() es el siguiente:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| LLENA_LA_MATRIZ_DE_SÍMBOLOS_CON_NOMBRES                          |
//+------------------------------------------------------------------+
void GetSymbolsToArray()
  {
// Si sólo son necesarios los datos del último símbolo
   if(curr_mwatch==CURRENT)
     { ArrayResize(symbols,1); symbols[0]=_Symbol; }
//---
// Si son necesarios los datos de todos los símbolos de la ventana "Observación del Mercado (MarketWatch)" o
// de la lista completa de símbolos
   if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS)
     {
      // Obtenemos la cantidad de símbolos en la ventana "Observación del Mercado (MarketWatch)"
      cnt_symb=SymbolsTotal(MWatchOrAllList());
      //---
      for(int i=0; i<=cnt_symb-1; i++)
        {
         string nm_symb="";
         //---
         ArrayResize(symbols,i+1); // Aumentamos el tamaño de la matriz en una unidad
         //---
         // Obtenemos el nombre del símbolo en la ventana "Observación del Mercado (MarketWatch)"
         nm_symb=SymbolName(i,MWatchOrAllList());
         symbols[i]=nm_symb; // Ponemos el nombre del símbolo en la matriz
        }
     }
  }

La función SetSeparateForFormatDate() es muy sencilla. Con su ayuda determinamos qué separador se usará en la fecha, dependiendo de lo que elija el usuario en la lista desplegable del parámetro Format Date (format_date).

//____________________________________________________________________
//+------------------------------------------------------------------+
//| DEFINIMOS_EL_SEPARADOR_PARA_EL_FORMATO_DE_LA_FECHA               |
//+------------------------------------------------------------------+
void SetSeparateForFormatDate()
  {
   switch(format_date)
     {
      case SEP_POINT1 : case SEP_POINT2 : sep="."; break; // Punto como separación
      case SEP_SLASH1 : case SEP_SLASH2 : sep="/"; break; // Barra como separador (barra (slash))
     }
  }

A continuación va el ciclo principal, en el que se llevan a cabo diferentes comprobaciones. Después de estas, si todo va como es debido, los datos son grabados en el archivo. Si no, el ciclo se detiene, todos los objetos son eliminados del gráfico y el script finaliza su funcionamiento (en caso de que tenga un símbolo). O tiene lugar el paso a la siguiente iteración (si hay más de un símbolo). Cada símbolo de la matriz symbols[] es invocado de modo consecutivo en el ciclo. En cada función dentro de este ciclo se transmite un número (índice). De esta forma se puede respetar con exactitud la secuencia en todas las funciones.

En el mismo inicio del cuerpo del ciclo, en cada iteración, obtenemos datos del símbolo actual en el ciclo. Para ello se usa la función GetDataCurrentSymbol(). Veamos lo que sucede con esta función.

Antes de copiar los datos del símbolo en la matriz rate[], se lleva a cabo una comprobación sobre la accesibilidad de los datos con ayuda de la función CheckLoadHistory(). Esta función se proporciona a los desarrolladores como ejemplo. Se puede ver su versión inicial en la Guía de MQL5. Lo único que he hecho ha sido corregirla un poco para usarla en este script. En la Guía tenemos una descripción bastante detallada (es preferible estudiársela también), por eso no voy a presentar aquí mi propia variante, que es prácticamente idéntica. Además, se la puede encontrar en el código, acompañada de comentarios detallados.

Lo único que se puede escribir ahora es que la función CheckLoadHistory() devuelve el código de error o el código de la correcta ejecución, de acuerdo con cual se emite el mensaje correspondiente en el diario del bloque operador-interruptor switch. Dependiendo del código recibido, la función GetDataCurrentSymbol(), o bien continuará con la ejecución de su operación, o bien devolverá su código.

Si todo ha salido bien, entonces a continuación se realizará la copia de los datos históricos, con la ayuda de la función CopyRates(). En la variable global se guarda el tamaño de la matriz, y después se llevará a cabo la salida de la función, acompañado del retorno del código 1. Si algo ha salido mal, entonces la función finalizará su realización en el operador switch y devolverá el código 0 o 2.

//____________________________________________________________________
//+------------------------------------------------------------------+
//| OBTENEMOS_LOS_DATOS_DE_LOS_SÍMBOLOS                              |
//+------------------------------------------------------------------+
int GetDataCurrentSymbol(int s)
  {
   Print("------\n№"+IS(s+1)+" >>>"); // Guardamos el número del símbolo en el diario
//---
// Comprueba y descarga la cantidad necesaria de datos solicitados
   int res=CheckLoadHistory(s,_Period);
//---
   InfoTable(s); ChartRedraw(); // Actualizamos los datos en la tabla de información
//---
   switch(res)
     {
      case -1 : Print("Símbolo desconocido "+symbols[s]+" (code: -1)!");                         return(0);
      case -2 :
         Print("Existen más barras solicitadas de las que se pueden mostrar en el gráfico (code: -2)!...\n"
               "...Se usará para la grabación la cantidad de información disponible.");            break;
      //---
      case -3 : Print("la ejecución ha sido interrumpida por el usuario (code: -3)!");                    return(2);
      case -4 : Print("La descarga ha fallado (code: -4)!");                              return(0);
      case  0 : Print("Todos los datos del símbolo han sido descargados (code: 0).");                               break;
      case  1 : Print("Los datos de las series temporales son suficientes (code: 1).");                break;
      case  2 : Print("La serie temporal ha sido creada de los datos existentes en el terminal (code: 2).");          break;
      //---
      default : Print("El resultado de la ejecución no está definido!");
     }
//---
// Copiamos los datos en la matriz
   if(CopyRates(symbols[s],_Period,check_start_date,end_date,rates)<=0)
     { Print("Error al copiar los datos del símbolo "+symbols[s]+" - ",ErrorDesc(Error())+""); return(0); }
   else
     {
      copied_bars=ArraySize(rates); // Obtenemos el tamaño de la matriz
      //---
      Print("Symbol: ",symbols[s],"; Timeframe: ",gStrTF(_Period),"; Copied bars: ",copied_bars);
     }
//---
   return(1); // Retornamos 1, si ha ido bien
  }

Después de esto, el programa se encuentra de nuevo en el cuerpo del ciclo principal en la función OnStart(). El código es asignado a la variable local res, y se lleva a cabo la comprobación según su valor. Si es igual a 0, entonces ha habido un error que indica que no se pueden grabar los datos del símbolo actual en el ciclo. La aclaración del error se ha guardado en el diario y ahora se toma la decisión de interrumpir el ciclo (break) o pasar a la siguiente iteración (continue).

if(res==0) { BC } // Si cero, entonces interrumpimos el ciclo, o bien pasamos a la siguiente iteración

En la línea de código más arriba se puede ver que esta elección es ejecutada por parte de unos misteriosos símbolos BC. Se trata de una macroinstrucción. Puede obtener información más detallada en la Guía de MQL5. Aquí sólo quiero hacer notar que las expresiones enteras (en una línea) se pueden introducir en una anotación breve, como se muestra en el ejemplo de más arriba (BC). En ciertos casos, este método puede ser incluso más cómodo y compacto que una función. En este caso, tiene el aspecto siguiente:

// Macroinstrucción con la posibilidad de elegir la acción posterior 
#define BC if(curr_mwatch==CURRENT) { break; } if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS) { continue; }

Otros ejemplos de macroinstrucciones utilizadas en este script:

#define nmf __FUNCTION__+": " // Macroinstrucción del nombre de la función antes de enviar el mensaje al diario
//---
#define TRM_DP TerminalInfoString(TERMINAL_DATA_PATH) // Carpeta en la que se guardan los datos del terminal

Si la función GetDataCurrentSymbol() retorna 2, significa que el programa ha sido eliminado por el usuario. Para determinar este hecho en MQL5 existe la función IsStopped(). Se recomienda usar esta función en los ciclos, para tener tiempo de finalizar la ejecución del programa en su momento. Si la función retorna true, significa que disponemos de unos 3 segundos para efectuar todas las acciones antes de la eliminación forzosa del programa. En nuestro caso se borran todos los objetos gráficos y el mensaje es enviado al diario:

if(res==2) // Ejecución del programa interrumpida por el usuario
   {
    DelAllScriptObjects(); // Eliminamos del gráfico los objetos creados por el script 
    //---
    Print("------\nEl usuario ha eliminado el script!"); break;
   }


8. Creamos las carpetas y grabamos los datos en el archivo

La función CheckCreateGetPath() comprueba la existencia de carpetas raíz para los datos. La nombraremos DATA_OHLC y se encontrará en el directorio C:\Metatrader 5\MQL5\Files. En ellas se hallarán las carpetas con los nombres de los símbolos. A su vez, en dichas carpetas se crearán los archivos para la grabación de datos.

Si la carpeta raíz o la carpeta para el símbolo actual no existe, entonces la función la creará. Si todo resulta bien, entonces la función retorna la línea con la ruta según la cual debe crearse el archivo. La función retorna una línea vacía en el caso de que haya un error cuando el usuario intente eliminar el programa del gráfico.

El código de abajo contiene comentarios detallados, que lo harán más fácil de comprender:

//____________________________________________________________________
//+----------------------------------------------------------------------+
//| COMPRUEBA_EL_DIRECTORIO_Y_CREA_LAS_CARPETAS_NECESARIAS_PARA_LOS_DATOS|
//+----------------------------------------------------------------------+
string CheckCreateGetPath(int s)
  {
   int i=1;
   long search=-1;
   string ffname="",lpath="";
   string file="*.csv",folder="*";
   string
   root="DATA_OHLC\\",         // Carpeta raíz de datos
   fSmb=symbols[s]+"\\",     // Nombre del símbolo
   fTF=gStrTF(_Period)+"\\"; // Time frame del símbolo
//---
   bool flgROOT=false,flgSYMBOL=false;
//---
//+------------------------------------------------------------------+
//| BUSCAMOS_LA_CARPETA_RAÍZ_DATA_OHLC                               |
//+------------------------------------------------------------------+
   lpath=folder;
   search=FileFindFirst(lpath,ffname); // Establecemos el handle de la búsqueda en - Metatrader 5\MQL5\Files
//---
   Print("Directorio: ",TRM_DP+"\\MQL5\\Files\\");
//---
// Si la primera carpeta es la raíz, ponemos la bandera
   if(ffname==root)
     { flgROOT=true; Print("La carpeta raíz "+root+" existe"); }
//---
   if(search!=INVALID_HANDLE) // Si obtenemos el handle de búsqueda
     {
      if(!flgROOT) // Si la primera carpeta no ha sido raíz
        {
         // Buscamos en todos los archivos para encontrar la carpeta raíz
         while(FileFindNext(search,ffname))
           {
            if(IsStopped()) // La ejecución ha sido interrumpida por el usuario
              {
               // Eliminamos de la gráfica los objetos creados por el script
               DelAllScriptObjects();
               //---
               Print("------\nEl usuario ha eliminado el script!"); return("");
              }
            //---
            if(ffname==root) // Si la encontramos, ponemos una bandera
              { flgROOT=true; Print("La carpeta raíz "+root+" existe"); break; }
           }
        }
      //---
      FileFindClose(search); search=-1; // Cerramos el handle de búsqueda de la carpeta raíz
     }
   else { Print("Error al obtener handle de búsqueda o directorio "+TRM_DP+" пуста: ",ErrorDesc(Error())); }
//---
//+------------------------------------------------------------------+
//| BUSCAMOS_LA_CARPETA_DEL_SÍMBOLO                                  |
//+------------------------------------------------------------------+
   lpath=root+folder;
//---
// Establecemos el handle de búsqueda en la carpeta raíz ..\Files\DATA OHLC\
   search=FileFindFirst(lpath,ffname);
//---
// Si la primera es la carpeta del símbolo actual, ponemos una bandera
   if(ffname==fSmb) { flgSYMBOL=true; Print("La carpeta del símbolo "+fSmb+" existe"); }
//---
   if(search!=INVALID_HANDLE) // Si hemos obtenido el handle de búsqueda
     {
      if(!flgSYMBOL) // Si la primera no es la carpeta del símbolo actual
        {
         // Buscamos en todos los archivos en la carpeta raíz para encontrar la carpeta del símbolo
         while(FileFindNext(search,ffname))
           {
            if(IsStopped()) // La ejecución ha sido interrumpida por el usuario
              {
               // Eliminamos de la gráfica los objetos creados por el script
               DelAllScriptObjects();
               //---
               Print("------\nEl usuario ha eliminado el script!"); return("");
              }
            //---
            if(ffname==fSmb) // Si la encontramos, ponemos una bandera
              { flgSYMBOL=true; Print("La carpeta del símbolo "+fSmb+" existe"); break; }
           }
        }
      //---
      FileFindClose(search); search=-1; // Cerramos el handle de búsqueda de la carpeta del símbolo
     }
   else { Print("Error al obtener handle de búsqueda o directorio "+path+" пуста"); }
//---
//+---------------------------------------------------------------------+
//| CREAMOS_LOS_CATÁLOGOS_NECESARIOS_SEGÚN_LOS_RESULTADOS_DE_LA_BÚSQUEDA|
//+---------------------------------------------------------------------+
   if(!flgROOT) // Si no tenemos la carpeta raíz DATA_OHLC...
     {
      if(FolderCreate("DATA_OHLC")) // ...la creamos
        { Print("Ha sido creada la carpeta raíz..\DATA_OHLC\\"); }
      else
        { Print("Error al crear la carpeta raíz DATA_OHLC: ",ErrorDesc(Error())); return(""); }
     }
//---
   if(!flgSYMBOL) // Si no tenemos la carpeta del símbolo cuyos valores debemos obtener...
     {
      if(FolderCreate(root+symbols[s])) // ...la creamos
        {
         Print("Creada la carpeta del símbolo ..\DATA_OHLC\\"+fSmb+"");
         //---
         return(root+symbols[s]+"\\"); // Retornamos la ruta en la que se creará el archivo para la grabación
        }
      else
        { Print("Error al crear la carpeta del símbolo ..\DATA_OHLC\\"+fSmb+"\: ",ErrorDesc(Error())); return(""); }
     }
//---
   if(flgROOT && flgSYMBOL)
     {
      return(root+symbols[s]+"\\"); // Retornamos la ruta en la que se creará el archivo para la grabación
     }
//---
   return("");
  }

Si la función CheckCreateGetPath() retornó una línea vacía, entonces interrumpimos el ciclo o pasamos a la siguiente iteración, usando ya la macroinstrucción conocida (BC):

// Obtenemos la ruta para la creación del archivo y creamos los directorios para ellos
// Si la línea está vacía, interrumpimos el ciclo, o bien pasamos a la siguiente iteración
if((path=CheckCreateGetPath(s))=="") { BC }

Si hemos llegado hasta este lugar, significa que los datos han sido copiados con éxito, las carpetas han sido creadas y la variable lineal path contiene la ruta en la que se debe crear el archivo para la grabación de los datos del símbolo actual en el ciclo.

Para grabar los datos en el archivo, creamos la función WriteDataToFile(). Al principio de esta función se genera [ruta]+[nombre del archivo]. El nombre del archivo consta del nombre del símbolo y del time frame actual. Por ejemplo, EURUSD_H1.csv. Si ya tenemos un archivo con ese nombre, entonces simplemente se abrirá para grabar. Los datos grabados en él con anterioridad serán eliminados. En lugar de ellos se grabarán datos nuevos. Si hemos creado/abierto el archivo con éxito, la función FileOpen() retornará un handle, que se utilizará para acceder al archivo.

Comprobamos que el handle exista. Si es así, entonces grabamos la línea de los encabezados. Dependiendo de los encabezados elegidos por el usuario, se grabará la línea correspondiente. A continuación comenzará el ciclo principal de grabación de datos históricos.

Antes de grabar la siguiente línea, hay que ponerla en el formato indicado por el usuario. Para ello deberemos obtener la hora de apertura de la barra, y con ayuda de la función StringSubstr() distribuir separadamente, por variables, el día, el mes, el año y la hora. Después, dependiendo del formato de fecha que haya escogido el usuario, determinaremos si la fecha y la hora se encontrarán en la misma columna o en diferentes. Después unimos todas las partes en una línea con ayuda de la función StringConcatenate(). Después de grabar todas las líneas, cerramos el archivo con la función FileClose().

Más abajo se muestra el código completo de la función WriteDataToFile():

//____________________________________________________________________
//+------------------------------------------------------------------+
//| GRABAMOS_LOS_DATOS_EN_UN_ARCHIVO                                 |
//+------------------------------------------------------------------+
void WriteDataToFile(int s)
  {
// Cantidad de símbolos en el precio del símbolo después de la coma
   int dgt=(int)SymbolInfoInteger(symbols[s],SYMBOL_DIGITS);
//---
   string nm_fl=path+symbols[s]+"_"+gStrTF(_Period)+".csv"; // Nombre del archivo
//---
// Obtemos el handle del archivo para grabar
   hFl=FileOpen(nm_fl,FILE_WRITE|FILE_CSV|FILE_ANSI,',');
//---
   if(hFl>0) // Si obtenemos el handle
     {
      // Grabamos los encabezados
      if(format_headers==NSDT_5)
        { FileWrite(hFl,"\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\""); }
      //---
      if(format_headers==NSDT_6)
        { FileWrite(hFl,"Date","Open","High","Low","Close","Volume"); }
      //---
      // Grabamos los datos
      for(int i=0; i<=copied_bars-1; i++)
        {
         if(IsStopped()) // Si la ejecución del programa ha sido interrumpida por el usuario
           {
            DelAllScriptObjects(); // Eliminamos del gráfico los objetos creados por el script
            //---
            Print("------\nEl usuario ha eliminado el script!"); break;
           }
         //---
         sdt=TSdm(rates[i].time); // Hora de apertura de la barra
         //---
         // Dividimos la fecha en año, mes, día, hora
         yyyy=StringSubstr(sdt,0,4);
         mm=StringSubstr(sdt,5,2);
         dd=StringSubstr(sdt,8,2);
         tm=StringSubstr(sdt,11);
         //---
         string sep_dt_tm=""; // Separador de las columnas Date y Time
         //---
         // Unimos los datos con el separador y los ponemos en el orden necesario
         if(format_date==SEP_POINT1 || format_date==SEP_SLASH1) { sep_dt_tm=" "; }
         if(format_date==SEP_POINT2 || format_date==SEP_SLASH2) { sep_dt_tm=","; }
         //---
         // Lo unimos todo en una sola línea
         StringConcatenate(sdt,dd,sep,mm,sep,yyyy,sep_dt_tm,tm);
         //---
         FileWrite(hFl,
                   sdt,// Fecha-Hora
                   DS_dgt(rates[i].open,dgt),      // Precio de apertura
                   DS_dgt(rates[i].high,dgt),      // Precio máximo
                   DS_dgt(rates[i].low,dgt),       // Precio mínimo
                   DS_dgt(rates[i].close,dgt),     // Precio de cierre
                   IS((int)rates[i].tick_volume)); // Precio de volumen de tick
         //---
         // Actualizamos el valor del progreso de la grabación para el símbolo actual
         pgs_pcnt=((double)(i+1)/copied_bars)*100;
         //---
         // Actualizamos los datos de la tabla
         InfoTable(s); if(show_progress) { ChartRedraw(); }
        }
      //---
      FileClose(hFl); // Cerramos el archivo
     }
   else { Print("Error al crear/abrir el archivo!"); }
  }

Esta ha sido la última función en el bloque principal de la función OnStart(). Si no se trataba del último símbolo, entonces repetimos exactamente lo mismo para el último. En este caso se produce la salida del ciclo. Si en los parámetros del script, el usuario indicó limpiar la lista símbolos en la ventana de Observación del mercado, entonces los símbolos cuyos gráficos estén abiertos en el momento actual, serán eliminados por la función DelSymbolsFromMarketWatch(). Después de ello, todos los objetos gráficos creados por el script son eliminados, y el programa finaliza su trabajo. Los datos están listos para ser usados.

Es posible ver un ejemplo sobre cómo cargar datos en NeuroShell DayTrader Professional en mi blog. Más abajo tenemos un vídeo donde se muestra el funcionamiento del script:



Conclusión

Se utilice el programa que se utilice para desarrollar estrategias comerciales, en mi caso siempre me he encontrado con ciertas limitaciones en todas partes, que no me permitieron seguir desarrollando mis ideas. Al final, he llegado a la conclusión de que sin programción no se puede llegar muy lejos. Si tenéis intenciones serias de lograr vuestros objetivos, entonces MQL5 es la mejor opción. De todas formas, al estudiar otros programas de análisis de datos y desarrollo de estrategias comerciales, se pueden encontrar buenas ideas para un desarrollo posterior. Es más que posible que no hubiese llegado a dichas conclusiones, de usar sólo un instrumento.

¡Os deseo buena suerte con vuestro trabajo!

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

Archivos adjuntos |
writedatatofile.mq5 (68.96 KB)
Carry Trading Estadístico Carry Trading Estadístico

Algoritmo de protección estadística de posiciones abiertas con swap (permutaciones) positivas contra movimientos no deseados de las cotizaciones. Para compensar el riesgo potencial que supone el movimiento de las cotizaciones en dirección opuesta a la posición abierta, en este artículo se presenta la variante Carry Trading de estrategia protegida.

Uso de MetaTrader 5 como proveedor de señales comerciales para MetaTrader 4 Uso de MetaTrader 5 como proveedor de señales comerciales para MetaTrader 4

En este artículo se discuten las particularidades del uso de MetaTrader 5 como proveedor de señales comerciales para MetaTrader 4. Ustedes conocerán cómo crear un sencillo proveedor de señales desde MetaTrader 5 y cómo conectarlo a varios terminales MetaTrader 4. Además, conocerán cómo copiar en tiempo real las transacciones de los participantes de Automated Trading Championship a su cuenta real en MetaTrader 4.

Una librería par la construcción de gráficos mediante Google Chart API Una librería par la construcción de gráficos mediante Google Chart API

La construcción de distintos tipos de diagramas constituye una parte esencial del análisis de la situación del mercado y de las pruebas de los sistemas de trading. Con frecuencia, para construir un diagrama sofisticado, es necesario organizar los datos de las salidas en un archivo, y luego utilizarlos en otras aplicaciones como MS Excel. Esto no es muy práctico y nos priva de la posibilidad de actualizar los datos de manera dinámica. Google Chart API proporciona los medios para crear gráficos en línea, mediante el envío de una petición especial al servidor. En este artículo, trataremos de automatizar el proceso de creación de esta petición y obtener el gráfico a partir del servidor de Google.

Solución sin DLL para la comunicación entre terminales MetaTrader 5 mediante canalizaciones con nombre Solución sin DLL para la comunicación entre terminales MetaTrader 5 mediante canalizaciones con nombre

El artículo describe el modo de implementar la comunicación entre procesos, entre terminales de cliente MetaTrader 5 mediante canalizaciones con nombre. Se desarrolla la clase CNamedPipes para utilizar las canalizaciones con nombre. Con el fin de probar su uso y medir el rendimiento de la conexión, se proporciona el tick del indicador y los scripts del servidor y el cliente. El uso de las canalizaciones con nombre es suficiente para las cotizaciones en tiempo real.