English Русский 中文 Deutsch 日本語 Português
preview
Marcado de datos en el análisis de series temporales (Parte 1):Creamos un conjunto de datos con marcadores de tendencia utilizando el gráfico de un asesor

Marcado de datos en el análisis de series temporales (Parte 1):Creamos un conjunto de datos con marcadores de tendencia utilizando el gráfico de un asesor

MetaTrader 5Asesores Expertos | 8 febrero 2024, 10:18
160 0
Yuqiang Pan
Yuqiang Pan

Introducción

Al desarrollar modelos de inteligencia artificial, con frecuencia tenemos que preparar primero los datos. Unos datos de buena calidad nos permitirán obtener el doble de resultados invirtiendo la mitad de esfuerzo en entrenar y comprobar el modelo. Sin embargo, los datos sobre divisas o acciones son especiales, pues contienen información compleja sobre el mercado y su tiempo de funcionamiento, por lo que marcar los datos resulta difícil, pero podemos analizar fácilmente la tendencia con los datos históricos disponibles en el gráfico.

 

Esta sección presenta un método para crear conjuntos de datos marcados por tendencias usando los gráficos de un asesor. De esta forma, podremos manipular intuitivamente los datos aplicando nuestras ideas. Obviamente, también podremos utilizar este método para ampliar y personalizar nuestros propios conjuntos de datos.

Contenido:

  1. Definiendo el formato de los datos de la etiqueta
  2. Inicializando los gráficos y archivos
  3. Lógica de funcionamiento
  4. Organizamos los datos y los escribimos en un archivo
  5. Apéndice: ejemplo completo del código del asesor


Definiendo el formato de los datos de la etiqueta

Al obtener los datos sobre las divisas o acciones de un cliente (en este artículo no hablaremos de datos externos leídos de archivos o descargados de otros sitios web), la situación general es la siguiente:

Time Open High Low Close Tick_volume
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264

La imagen anterior muestra el aspecto de los datos de las cinco series temporales. Los valores Close y Open están vinculados de principio a fin, y el vínculo es muy fuerte. Partiremos de que las dos primeras filas serán tendencias alcistas y el resto tendencias bajistas (los datos anteriores son solo un ejemplo). Un método de marcado común dividirá los datos en dos partes:

Time

Open High   Low   Close Tick_volume
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210

Time Open High   Low Close Tick_volume
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264

El método le dice entonces a nuestro modelo qué parte representa una tendencia alcista y qué parte representa una tendencia bajista, pero esto ignorará sus atributos comunes y comprometerá la integridad de los datos. ¿Cómo podemos resolver este problema?

Un método posible consistirá en añadir una agrupación de tendencias a nuestras series temporales del siguiente modo (tome los cinco datos anteriores como ejemplo o siga los supuestos anteriores):

Time Open High Low Close Tick_volume Trend_group
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173 0
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210 0
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212 1
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392 1
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264 1

Pero si queremos aplicar en el modelo el análisis de la evolución de la tendencia, y en concreto, hasta qué punto se ha desarrollado la tendencia actual (por ejemplo, la teoría de ondas nos dice que una tendencia general suele incluir una etapa de tendencia y una etapa de corrección; la etapa de tendencia tiene 5 etapas de onda, mientras que la etapa de corrección tiene 3 correcciones de onda, etc.), tendremos que marcar más los datos. Podemos hacerlo añadiendo otra columna que represente la evolución de la tendencia en los datos (suponiendo que los 2 primeros de los 10 datos siguientes sean alcistas y los 5 últimos alcistas, y el resto en el medio sean bajistas), así:

Time Open High Low Close Tick_volume Trend_group Trend_index
2021-12-10 03:15:00 1776.38 1777.94 1775.47 1777.71 565 0 0
2021-12-10 03:30:00 1777.75 1778.93 1777.68 1778.61 406 0 1
2021-12-10 03:45:00 1778.58 1778.78 1777.65 1778.16 388 1 0
2021-12-10 04:00:00 1778.14 1779.42 1778.06 1779.14 393 1 1
2021-12-10 04:15:00 1779.16 1779.49 1778.42 1779.31 451 1 2
2021-12-10 04:30:00 1779.22 1779.42 1778.36 1778.37 306 0 0
2021-12-10 04:45:00 1778.42 1778.51 1777.60 1777.78 411 0 1
2021-12-10 05:00:00 1777.81 1778.68 1777.61 1778.57 372 0 2
2021-12-10 05:15:00 1778.54 1779.29 1778.42 1779.02 413 0 3
2021-12-10 05:30:00 1778.97 1779.49 1778.48 1778.50 278 0 4

Notas:

1. El Trend_group que define una tendencia alcista, es 0

2. El Trend_group que define una tendencia bajista, es 1.

 A continuación, empezaremos a gestionar el gráfico en el lado del cliente, colocando los datos según el patrón que deseemos.


Inicializando los gráficos y archivos

    Inicialización del gráfico

          Como necesitamos mirar el gráfico para marcar los datos, deberemos desplazarnos de acuerdo con nuestras operaciones manuales, por lo que necesitaremos desactivar CHART_AUTOSCROLL y CHART_SHIFT:

           ChartSetInteger (0, CHART_AUTOSCROLL, false);
          
            ChartSetInteger (0, CHART_SHIFT, true);
          
            ChartSetInteger (0, CHART_MOUSE_SCROLL ,1);
          Nota: La parte verde del código sirve para controlar el gráfico con la rueda del ratón
            Inicialización de archivos

                Al inicializar un archivo, primero deberemos comprobar si existe un archivo de etiqueta y un archivo de datos históricos, y luego almacenar el nombre del archivo en la variable reName:

                 do
                     {
                       //---Find if there are files that match the chart
                       if (StringFind(name, Symbol())!=-1 && StringFind(name,".csv")!=-1)
                         reName=name;
                     }
                
                   while (FileFindNext(hd,name));
                Nota: Luego utilizaremos un ciclo do - while, que se diferencia del ciclo while en que primero ejecuta el operador y luego evalúa la expresión. El principal problema aquí será la inicialización del nombre
                int hd= FileFindFirst("*",name,0);


                  Si el archivo original marcado existe, lo abriremos y obtendremos la última fecha y hora marcada con read_csv():

                    read_csv(file_handle,a);
                    A continuación, nos desplazaremos por el gráfico hasta la última hora anotada:
                    shift = - iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                    ChartNavigate(0, CHART_END ,shift);


                    Luego crearemos un archivo histórico, si no existiera:

                    file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t), FILE_WRITE | FILE_CSV | FILE_READ);
                    A continuación, desplazaremos el diagrama hasta la posición especificada por la variable global start_t.
                     shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                      ChartNavigate(0,CHART_END,shift);
                    Después añadiremos una línea roja vertical para marcar la columna inicial:
                     ObjectCreate (0,"Start",OBJ_VLINE,0,(datetime)start_t,0)
                    La lógica de esta parte se organizará de la forma que sigue:
                     if (FileIsExist(reName))
                         {
                          file_handle = FileOpen(reName, FILE_WRITE | FILE_CSV | FILE_READ );
                           string a[];
                           int i= 0 ;
                          read_csv(file_handle,a);
                          i = ArraySize (a);
                          shift = -iBarShift(Symbol(), PERIOD_CURRENT,(datetime)a[i-8]);
                           ChartNavigate(0,CHART_END,shift);
                         }
                       else
                         {
                          file_handle = FileOpen (StringFormat ("%s%d-%d.csv", Symbol(), Period(),start_t), FILE_WRITE | FILE_CSV | FILE_READ );
                           Print ("There is no history file,create file:" , StringFormat ( "%s%d-%d",Symbol(), Period(),start_t));
                           shift = - iBarShift (Symbol(), PERIOD_CURRENT ,(datetime)start_t);
                           ChartNavigate (0, CHART_END ,shift);
                           ObjectCreate (0,"Start", OBJ_VLINE,0,(datetime)start_t,0);
                         }
                    Atención: Como queremos desplazar el gráfico a la izquierda, antes de iBarShift(), necesitaremos añadir "-".
                    shift = -iBarShift(Symbol(), PERIOD_CURRENT ,(datetime)start_t);
                    Por supuesto, la lógica también podemos implementarla en la función ChartNavigate(), por ejemplo:
                    ChartNavigate(0,CHART_END,-shift);
                    Sin embargo, el código de este artículo se implementará utilizando el primer método.
                      Implementaremos estas acciones de inicialización en nuestro OnInit(), incluyendo la definición de las variables necesarias. Lo más importante será aclarar hacia dónde queremos desplazar el gráfico y empezar el marcado. Controlaremos esto principalmente con las variables shift y start_t. El código final tendrá el aspecto siguiente:
                        int OnInit()
                          {
                        //---initial
                           string name;
                           string reName="1";
                           int hd=FileFindFirst("*",name,0);
                           int shift;
                        
                           ChartSetInteger(0,CHART_AUTOSCROLL,false);
                           ChartSetInteger(0,CHART_SHIFT,false);
                           ChartSetInteger(0,CHART_MOUSE_SCROLL,1);
                        
                        
                           do
                             {
                              //---check File
                              if(StringFind(name,Symbol())!=-1 && StringFind(name,".csv")!=-1)
                                 reName=name;
                             }
                           while(FileFindNext(hd,name));
                        
                           if(FileIsExist(reName))
                             {
                              file_handle = FileOpen(reName,FILE_WRITE|FILE_CSV|FILE_READ);
                              string a[];
                              int i=0;
                              read_csv(file_handle,a);
                              i = ArraySize(a);
                              shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                              ChartNavigate(0,CHART_END,shift);
                             }
                           else
                             {
                              file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t),FILE_WRITE|FILE_CSV|FILE_READ);
                              Print(FileTell(file_handle));
                              Print("No history file,create file:",StringFormat("%s%d-%d",Symbol(),Period(),start_t));
                              shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                              ChartNavigate(0,CHART_END,shift);
                              ObjectCreate(0,"Start",OBJ_VLINE,0,(datetime)start_t,0);
                             }
                           return(INIT_SUCCEEDED);
                          }

                          Notas:

                          1. start_t variable - marco temporal para el inicio;

                          2. shift variable - número de columnas a desplazar, el ejemplo de código muestra el número de columnas que serán desplazadas mediante la conversión del tiempo especificado;

                          3. La función read_csv () la definiremos más abajo.

                            Definición de read_csv():
                               void read_csv(int hd,
                                             string &arry[])
                                {
                                 int i= 0;
                                 while(!FileIsEnding(hd))
                                   {
                                    ArrayResize(arry,i+1);
                                    arry[i]= FileReadString(hd);
                                    i++;
                                   }
                                }

                              Nota: Utilizaremos un ciclo while para encontrar la línea final del archivo histórico de anotaciones, obtener la última línea de datos del archivo y encontrar la hora final de nuestra última anotación. La anotación se desplazará por el gráfico de precios hasta el gráfico de barras para que podamos seguir comentando desde aquí.


                              Lógica de funcionamiento

                                Gestión de gráficos: Podrá encontrar fácilmente esta sección en la guía de ayuda del cliente.
                                  • Home - ir a la última barra del gráfico;
                                  • End - ir a la primera barra del gráfico;
                                  • Page Up - desplaza el gráfico una ventana hacia atrás;
                                  • Page Down - desplaza el gráfico una ventana hacia delante;
                                  • Ctrl+I - abre la ventana con la lista de indicadores;
                                  • Ctrl+B - abre la ventana con la lista de objetos;
                                  • Alt+1 - muestra el gráfico como una secuencia de barras;
                                  • Alt+2 - muestra el gráfico como una secuencia de velas japonesas;
                                  • Alt+3 - muestra el gráfico como una línea que une los precios de cierre;
                                  • Ctrl+G - muestra/oculta la cuadrícula en la ventana del gráfico;
                                  • "+" - amplia la escala del gráfico;
                                  • "-" - reduce la escala del gráfico;
                                  • F12 - desplaza paso a paso el gráfico (barra a barra);
                                  • F8 - abre la ventana de propiedades;
                                  • Backspace - elimina del gráfico el último objeto añadido;
                                  • Delete - borra todos los objetos seleccionados;
                                  • Ctrl+Z - cancela la eliminación del último objeto.
                                    Lógica de control:
                                      1. Pulsaremos la tecla para indicar al asesor qué tipo de tendencia supondrán los datos marcados a continuación.
                                        Vamos a definir las teclas b y s. Se define por el código virtual de la tecla:
                                           #define KEY_B     66
                                           #define KEY_S     83
                                            Luego pulsaremos b seguido de s para una tendencia alcista o s seguido de b para una tendencia bajista. Tomaremos como ejemplo una tendencia alcista:
                                              1) Pulsaremos b para una tendencia alcista. Después estableceremos la variable typ en 0, la variable tp en start, el color de la flecha en clrBlue y aumentaremos en 1 el número de etiquetas Num. Solo deberemos incrementar la variable al principio del segmento de datos y especificar que al pulsar nuevamente el botón se ejecutará la parte "final" del segmento de datos marcado invirtiéndolo;
                                              b_press
                                              2) Pulsaremos s para marcar el final de la tendencia alcista, la variable typ seguirá siendo 0, la variable tp la estableceremos en end, el color de la flecha seguirá siendo clrBlue y el número de etiquetas Num no cambiará. Solo necesitaremos incrementar la variable al principio del segmento de datos, mientras que utilizaremos la inversión first para indicar que al pulsar nuevamente el botón se ejecutará la parte de "inicio" del segmento de datos marcado. s_press
                                              3) Después de ejecutar la sentencia switch, llamaremos a la función ChartRedraw() para redibujar el gráfico.
                                              if(id==CHARTEVENT_KEYDOWN)
                                                   {
                                                    switch(lparam)
                                                      {
                                                       case KEY_B:
                                                          if(first)
                                                            {
                                                             col=clrBlue ;
                                                             typ =0;
                                                             Num+=1;
                                                             tp = "start";
                                                            }
                                                          else
                                                            {
                                                             col=clrRed ;
                                                             typ = 1;
                                                             tp = "end";
                                                            }
                                                          ob =OBJ_ARROW_BUY;
                                                          first = !first;
                                                          Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                          break;
                                                       case KEY_S:
                                                          if(first)
                                                            {
                                                             col=clrRed ;
                                                             typ =1;
                                                             Num+=1;
                                                             tp = "start";
                                                            }
                                                          else
                                                            {
                                                             col=clrBlue ;
                                                             typ = 0;
                                                             tp = "end";
                                                            }
                                                          ob =OBJ_ARROW_SELL;
                                                          first = !first;
                                                          Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                          break;
                                              
                                                       default:
                                                          Print("You pressed:"+lparam+" key, do nothing!");
                                                      }
                                                    ChartRedraw(0);
                                                   }

                                              Notas:

                                              1. variable type — 0 indicará una tendencia alcista, 1 indicará una tendencia bajista;

                                              2. variable "Num" — número de marcas, se mostrará intuitivamente en el gráfico;

                                              3. la variable first controlará que nuestras etiquetas siempre vayan en pares, asegurando que cada grupo represente b y s, o bien s y b, evitando confusiones;

                                              4. la variable tp se utilizará para determinar el inicio o el final de un segmento de datos.

                                              2. Clicaremos con el botón izquierdo del ratón en el gráfico para determinar la posición de la marca

                                              if(id==CHARTEVENT_CLICK)
                                                   {
                                                    //--- definition
                                                    int x=(int)lparam;
                                                    int y=(int)dparam;
                                                    datetime dt    =0;
                                                    double   price =0;
                                                    int      window=0;
                                                    if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                      {
                                                       ObjectCreate(0,Name,ob,window,dt,price);
                                                       ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                       //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                       if(tp=="start")
                                                          Start=dt;
                                                       else
                                                         {
                                                          if(file_handle)
                                                             file_write(Start,dt);
                                                         }
                                                       ChartRedraw(0);
                                                      }
                                                    else
                                                       Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                   }
                                              //--- object delete
                                                 if(id==CHARTEVENT_OBJECT_DELETE)
                                                   {
                                                    Print("The object with name ",sparam," has been deleted");
                                                   }
                                              //--- object create
                                                 if(id==CHARTEVENT_OBJECT_CREATE)
                                                   {
                                                    Print("The object with name ",sparam," has been created!");
                                                   }

                                              Notas:

                                              1. La función ChartXYToTimePrice() se utilizará principalmente para recuperar las propiedades del gráfico en la ubicación del clic, incluyendo la hora y el precio actuales. La variable global dt será necesaria para obtener la hora actual;

                                              2. Al clicar, también tendremos que determinar si la acción actual es el principio o el final de un segmento de datos. Para la estimación, utilizaremos la variable global tp. 

                                              3. Acciones necesarias


                                                Si deseamos marcar una tendencia alcista, primero pulsaremos la tecla b, haremos clic con el botón izquierdo del ratón en la columna que empieza a designarse en el gráfico, después pulsaremos la tecla s y, a continuación, haremos clic con el botón izquierdo del ratón al final de la columna en el icono para completar el marcado. Aparecerán un par de flechas azules en el gráfico, como se muestra en la imagen siguiente:

                                                  hacia arriba


                                                      Si deseamos marcar una tendencia bajista, primero pulsaremos la tecla s, haremos clic con el botón izquierdo del ratón en la columna que empieza a designarse en el gráfico, después pulsaremos la tecla b y, a continuación, haremos clic con el botón izquierdo del ratón al final de la columna en el gráfico. Una vez completado el marcado, aparecerán un par de flechas rojas como se muestra en la imagen siguiente:

                                                        hacia abajo


                                                          En la columna de muestra del marcado se mostrará en cualquier momento la acción de marcado, lo cual resulta muy práctico para seguir el proceso, como se muestra en la figura:

                                                            outNota: En realidad esta parte podría optimizarse mejor, por ejemplo añadiendo una función para deshacer la última acción, así se podría ajustar la posición de la etiqueta en cualquier momento y evitar operaciones erróneas, pero soy muy perezoso a ese respecto... (^о^)


                                                            Organizamos los datos y los escribimos en un archivo

                                                              Ahora definiremos las variables Start y MqlRatesrates[] para almacenar la hora de inicio y la serie de datos de tendencia:
                                                                datetime Start;
                                                                MqlRates rates[];
                                                                ArraySetAsSeries(rates, false);
                                                                  Nota: 1. Aquí no necesitaremos determinar la hora de finalización, ya que la última hora obtenida del gráfico será la hora de finalización; 2. La bandera en la función ArraySetAsSeries(rates,false) será igual a false para asegurarnos de que los periodos de tiempo están conectados secuencialmente.
                                                                    Cuando tp = "end" escribiremos el segmento de datos en el archivo (parte verde del código):
                                                                         if(id==CHARTEVENT_CLICK)
                                                                           {
                                                                            //--- definition
                                                                            int x=(int)lparam;
                                                                            int y=(int)dparam;
                                                                            datetime dt    =0;
                                                                            double   price =0;
                                                                            int      window=0;
                                                                            if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                                              {
                                                                               ObjectCreate(0,Name,ob,window,dt,price);
                                                                               ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                                               //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                                               if(tp=="start")
                                                                                  Start=dt;
                                                                               else
                                                                                 {
                                                                                  if(file_handle)
                                                                                     file_write(Start,dt);
                                                                                 }
                                                                               ChartRedraw(0);
                                                                              }
                                                                            else
                                                                               Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                                           }

                                                                        Luego obtendremos los datos del segmento utilizando CopyRates() y añadiremos las columnas trend_group y trend_index, observando cada dato contenido en rates[]. Necesitaremos implementar estas funciones en la función file_write():
                                                                          void file_write(datetime start,
                                                                                          datetime end)
                                                                            {
                                                                             MqlRates rates[];
                                                                             ArraySetAsSeries(rates,false);
                                                                             int n_cp=CopyRates(Symbol(),PERIOD_CURRENT,start,end,rates);
                                                                             if(n_cp>0)
                                                                               {
                                                                                if(FileTell(file_handle)==2)
                                                                                  {
                                                                                   FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                                   for(int i=0; i<n_cp; i++)
                                                                                     {
                                                                                      FileWrite(file_handle,
                                                                                                rates[i].time,
                                                                                                rates[i].open,
                                                                                                rates[i].high,
                                                                                                rates[i].low,
                                                                                                rates[i].close,
                                                                                                rates[i].tick_volume,
                                                                                                typ,
                                                                                                i);
                                                                                     }
                                                                                  }
                                                                                else
                                                                                  {
                                                                                   for(int i=0; i<n_cp; i++)
                                                                                     {
                                                                                      FileWrite(file_handle,
                                                                                                rates[i].time,
                                                                                                rates[i].open,
                                                                                                rates[i].high,
                                                                                                rates[i].low,
                                                                                                rates[i].close,
                                                                                                rates[i].tick_volume,
                                                                                                typ,
                                                                                                i);
                                                                                     }
                                                                                  }
                                                                               }
                                                                             else
                                                                                Print("No data copied!");
                                                                             FileFlush(file_handle);
                                                                             typ=3;
                                                                            }



                                                                          Notas:

                                                                          1. Necesitaremos escribir el encabezado del índice la primera vez que se escriba el archivo;

                                                                          2. Trend_group es en realidad una variable typ global;

                                                                          3. No hemos llamado aquí a FileClose() porque nuestro marcado aún no está completo. Llamaremos a esta función en la función OnDeinit() para escribir el resultado final en un archivo.

                                                                          4. Deberemos prestar especial atención a la parte amarilla del código.

                                                                          if(FileTell(file_handle)==2)
                                                                          Al determinar la presencia de datos (obviamente, podemos utilizar otros métodos, como la adición de una variable para asignarle un valor en la inicialización), si no existen datos en el archivo, deberemos añadir un encabezado como este:
                                                                          FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                          Si hay datos en el archivo, no será necesario añadir un encabezado, de lo contrario los datos se cortarán, ¡esto es muy importante!
                                                                            Un ejemplo de archivo grabado:
                                                                              data_0


                                                                                Comprobemos ahora la coherencia entre los distintos segmentos de datos:

                                                                                  data_1


                                                                                  Apéndice: ejemplo completo del código del asesor

                                                                                  1. Definición de variables y constantes globales. El parámetro start_t podrá determinarse a partir de los datos por segundo desde el 01.01.1970. Por supuesto, también podremos definirlo utilizando la fecha y hora estándar o la variable de entrada int start_t=1403037112; de forma que pueda modificarse en cualquier momento durante el posterior inicio del asesor:
                                                                                  #define KEY_B     66
                                                                                  #define KEY_S     83
                                                                                  
                                                                                  
                                                                                  int Num= 0;
                                                                                  int typ= 3;
                                                                                  string Name;
                                                                                  string tp;
                                                                                  color col;
                                                                                  bool first= true;
                                                                                  ENUM_OBJECT ob;
                                                                                  int file_handle=0;
                                                                                  int start_t=1403037112;
                                                                                  datetime Start;

                                                                                  Nota: Obviamente, también podremos definir un botón como variable de entrada según nuestras preferencias personales.

                                                                                  input int KEY_B=66;
                                                                                  input int KEY_S=83;

                                                                                  La ventaja es que si creemos que los botones no son fáciles de usar, podremos cambiarlos a nuestro gusto cada vez que ejecutemos el asesor hasta que estemos satisfechos y el código se cambie temporalmente.


                                                                                  2. Función OnInit(), donde inicializaremos nuestras preparaciones:

                                                                                  int OnInit()
                                                                                    {
                                                                                  //---initial
                                                                                     string name;
                                                                                     string reName="1";
                                                                                     int hd=FileFindFirst("*",name,0);
                                                                                     int shift;
                                                                                  
                                                                                     ChartSetInteger(0,CHART_AUTOSCROLL,false);
                                                                                     ChartSetInteger(0,CHART_SHIFT,false);
                                                                                     ChartSetInteger(0,CHART_MOUSE_SCROLL,1);
                                                                                  
                                                                                  
                                                                                     do
                                                                                       {
                                                                                        //---check File
                                                                                        if(StringFind(name,Symbol())!=-1 && StringFind(name,".csv")!=-1)
                                                                                           reName=name;
                                                                                       }
                                                                                     while(FileFindNext(hd,name));
                                                                                  
                                                                                     if(FileIsExist(reName))
                                                                                       {
                                                                                        file_handle = FileOpen(reName,FILE_WRITE|FILE_CSV|FILE_READ);
                                                                                        string a[];
                                                                                        int i=0;
                                                                                        read_csv(file_handle,a);
                                                                                        i = ArraySize(a);
                                                                                        shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                                                                                        ChartNavigate(0,CHART_END,shift);
                                                                                       }
                                                                                     else
                                                                                       {
                                                                                        file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t),FILE_WRITE|FILE_CSV|FILE_READ);
                                                                                        Print(FileTell(file_handle));
                                                                                        Print("No history file,create file:",StringFormat("%s%d-%d",Symbol(),Period(),start_t));
                                                                                        shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                                                                                        ChartNavigate(0,CHART_END,shift);
                                                                                        ObjectCreate(0,"Start",OBJ_VLINE,0,(datetime)start_t,0);
                                                                                       }
                                                                                  //---
                                                                                     Print("EA:",MQL5InfoString(MQL5_PROGRAM_NAME),"Working!");
                                                                                  //---
                                                                                     ChartSetInteger(ChartID(),CHART_EVENT_OBJECT_CREATE,true);
                                                                                  //---
                                                                                     ChartSetInteger(ChartID(),CHART_EVENT_OBJECT_DELETE,true);
                                                                                  //---
                                                                                     ChartRedraw(0);
                                                                                  //---
                                                                                     return(INIT_SUCCEEDED);
                                                                                    }


                                                                                  3. Como ya hemos completado todas nuestras operaciones de teclado y ratón sobre el gráfico, ahora hemos colocado las funciones lógicas básicas en la función OnChartEvent():

                                                                                  void OnChartEvent(const int id,
                                                                                                    const long &lparam,
                                                                                                    const double &dparam,
                                                                                                    const string &sparam)
                                                                                    {
                                                                                  //Comment(__FUNCTION__,": id=",id," lparam=",lparam," dparam=",dparam," sparam=",sparam);
                                                                                     if(id==CHARTEVENT_KEYDOWN)
                                                                                       {
                                                                                        switch(lparam)
                                                                                          {
                                                                                           case KEY_B:
                                                                                              if(first)
                                                                                                {
                                                                                                 col=clrBlue ;
                                                                                                 typ =0;
                                                                                                 Num+=1;
                                                                                                 tp = "start";
                                                                                                }
                                                                                              else
                                                                                                {
                                                                                                 col=clrRed ;
                                                                                                 typ = 1;
                                                                                                 tp = "end";
                                                                                                }
                                                                                              ob =OBJ_ARROW_BUY;
                                                                                              first = !first;
                                                                                              Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                                                              break;
                                                                                           case KEY_S:
                                                                                              if(first)
                                                                                                {
                                                                                                 col=clrRed ;
                                                                                                 typ =1;
                                                                                                 Num+=1;
                                                                                                 tp = "start";
                                                                                                }
                                                                                              else
                                                                                                {
                                                                                                 col=clrBlue ;
                                                                                                 typ = 0;
                                                                                                 tp = "end";
                                                                                                }
                                                                                              ob =OBJ_ARROW_SELL;
                                                                                              first = !first;
                                                                                              Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                                                              break;
                                                                                  
                                                                                           default:
                                                                                              Print("You pressed:"+lparam+" key, do nothing!");
                                                                                          }
                                                                                        ChartRedraw(0);
                                                                                       }
                                                                                  //---
                                                                                     if(id==CHARTEVENT_CLICK&&(typ!=3))
                                                                                       {
                                                                                        //--- definition
                                                                                        int x=(int)lparam;
                                                                                        int y=(int)dparam;
                                                                                        datetime dt    =0;
                                                                                        double   price =0;
                                                                                        int      window=0;
                                                                                        if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                                                          {
                                                                                           ObjectCreate(0,Name,ob,window,dt,price);
                                                                                           ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                                                           //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                                                           if(tp=="start")
                                                                                              Start=dt;
                                                                                           else
                                                                                             {
                                                                                              if(file_handle)
                                                                                                 file_write(Start,dt);
                                                                                             }
                                                                                           ChartRedraw(0);
                                                                                          }
                                                                                        else
                                                                                           Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                                                       }
                                                                                  //--- object delete
                                                                                     if(id==CHARTEVENT_OBJECT_DELETE)
                                                                                       {
                                                                                        Print("The object with name ",sparam," has been deleted");
                                                                                       }
                                                                                  //--- object create
                                                                                     if(id==CHARTEVENT_OBJECT_CREATE)
                                                                                       {
                                                                                        Print("The object with name ",sparam," has been created!");
                                                                                       }
                                                                                  
                                                                                    }

                                                                                  Nota: Hemos cambiado el código anterior al implementar esta función

                                                                                   if (id==CHARTEVENT_CLICK&&(typ!=3))

                                                                                  La razón es muy simple: así evitaremos operaciones incorrectas causadas por clics accidentales del ratón, y utilizaremos la variable typ para controlar que la acción del ratón sea aceptable. Al marcar una tendencia, ejecutaremos file_write(). Luego añadiremos la siguiente línea al final de la función

                                                                                  typ=3;

                                                                                  A continuación, podremos utilizar el ratón para controlar periódicamente el gráfico antes de iniciar la siguiente serie de marcados sin ninguna acción adicional hasta que encontremos una posición adecuada y estemos listos para marcar la siguiente tendencia.


                                                                                  4. Implementación de la función de escritura de datos - file_write():

                                                                                  void file_write(datetime start,
                                                                                                  datetime end)
                                                                                    {
                                                                                     MqlRates rates[];
                                                                                     ArraySetAsSeries(rates,false);
                                                                                     int n_cp=CopyRates(Symbol(),PERIOD_CURRENT,start,end,rates);
                                                                                     if(n_cp>0)
                                                                                       {
                                                                                        if(FileTell(file_handle)==2)
                                                                                          {
                                                                                           FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                                           for(int i=0; i<n_cp; i++)
                                                                                             {
                                                                                              FileWrite(file_handle,
                                                                                                        rates[i].time,
                                                                                                        rates[i].open,
                                                                                                        rates[i].high,
                                                                                                        rates[i].low,
                                                                                                        rates[i].close,
                                                                                                        rates[i].tick_volume,
                                                                                                        typ,
                                                                                                        i);
                                                                                             }
                                                                                          }
                                                                                        else
                                                                                          {
                                                                                           for(int i=0; i<n_cp; i++)
                                                                                             {
                                                                                              FileWrite(file_handle,
                                                                                                        rates[i].time,
                                                                                                        rates[i].open,
                                                                                                        rates[i].high,
                                                                                                        rates[i].low,
                                                                                                        rates[i].close,
                                                                                                        rates[i].tick_volume,
                                                                                                        typ,
                                                                                                        i);
                                                                                             }
                                                                                          }
                                                                                       }
                                                                                     else
                                                                                        Print("No data copied!");
                                                                                     FileFlush(file_handle);
                                                                                     typ=3;
                                                                                    }

                                                                                  5. Implementación de la función de lectura de archivos - read_csv():
                                                                                  void read_csv(int hd,
                                                                                                string &arry[])
                                                                                    {
                                                                                     int i=0;
                                                                                     while(!FileIsEnding(hd))
                                                                                       {
                                                                                        ArrayResize(arry,i+1);
                                                                                        arry[i]=FileReadString(hd);
                                                                                        i++;
                                                                                       }
                                                                                    }

                                                                                  6. Hay otra cuestión importante que no hemos tratado aquí: el file_handle abierto durante la inicialización del asesor no se libera. Liberaremos el manejador en la última función OnDeinit(). Al llamar a la función FileClose(file_handle), todos los datos se escribirán en el archivo csv, por lo que resultará especialmente importante no intentar abrir el archivo csv mientras se ejecuta el asesor:
                                                                                  void OnDeinit(const int reason)
                                                                                    {
                                                                                     FileClose(file_handle);
                                                                                     Print("Write data!");
                                                                                    }


                                                                                  Nota: El código mostrado en este artículo tiene fines exclusivamente demostrativos. Si desea utilizar este código en la práctica, le recomendamos mejorarlo. Adjuntamos al artículo el archivo CSV y el archivo MQL5 final utilizados en la demostración. En el próximo artículo de la serie explicaremos cómo anotar datos a través de un cliente utilizando Python.

                                                                                  ¡Gracias por su atención!

                                                                                  Traducción del inglés realizada por MetaQuotes Ltd.
                                                                                  Artículo original: https://www.mql5.com/en/articles/13225

                                                                                  Archivos adjuntos |
                                                                                  Desarrollo de un sistema de repetición (Parte 26): Proyecto Expert Advisor — Clase C_Terminal Desarrollo de un sistema de repetición (Parte 26): Proyecto Expert Advisor — Clase C_Terminal
                                                                                  Podemos comenzar a elaborar un EA para uso en repetición/simulación. Sin embargo, necesitamos algo refinado, no solo una solución cualquiera. No debemos, no obstante, ser intimidados por la complejidad inicial. Es esencial iniciar de algún punto, si no, acabaremos por acomodarnos, reflexionando sobre la dificultad del desafío sin realmente intentar superarlo. La esencia de la programación es exactamente esa: enfrentar un obstáculo y buscar superarlo a través de estudio, pruebas y extensa investigación.
                                                                                  Redes neuronales: así de sencillo (Parte 56): Utilizamos la norma nuclear para incentivar la exploración Redes neuronales: así de sencillo (Parte 56): Utilizamos la norma nuclear para incentivar la exploración
                                                                                  La exploración del entorno en tareas de aprendizaje por refuerzo es un problema relevante. Con anterioridad, ya hemos analizado algunos de estos enfoques. Hoy le propongo introducir otro método basado en la maximización de la norma nuclear, que permite a los agentes identificar estados del entorno con un alto grado de novedad y diversidad.
                                                                                  Desarrollo de un sistema de repetición (Parte 27): Proyecto Expert Advisor — Clase C_Mouse (I) Desarrollo de un sistema de repetición (Parte 27): Proyecto Expert Advisor — Clase C_Mouse (I)
                                                                                  En este artículo, daremos vida a la clase C_Mouse. Está diseñada para permitir programar al más alto nivel posible. Sin embargo, hablar de programar a niveles altos o bajos no está relacionado con incluir palabrotas o jerga en el código. Todo lo contrario. Cuando mencionamos programación de alto o bajo nivel, nos referimos a lo fácil o difícil que es para otro programador entender el código.
                                                                                  Desarrollo de un sistema de repetición — Simulación de mercado (Parte 25): Preparación para la próxima etapa Desarrollo de un sistema de repetición — Simulación de mercado (Parte 25): Preparación para la próxima etapa
                                                                                  En este artículo, concluimos la primera fase del desarrollo del sistema de repetición y simulador. Con este hito, afirmo, estimado lector, que el sistema ha alcanzado un nivel avanzado, abriendo camino para la incorporación de nuevas funcionalidades. El objetivo es enriquecer aún más el sistema, convirtiéndolo en una herramienta poderosa para estudios y para el desarrollo de análisis de mercado.