
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
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:
- Definiendo el formato de los datos de la etiqueta
- Inicializando los gráficos y archivos
- Lógica de funcionamiento
- Organizamos los datos y los escribimos en un archivo
- 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
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
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.
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.
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
- 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.
#define KEY_B 66 #define KEY_S 83

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.

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:

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:

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:
Nota: 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
datetime Start; MqlRates rates[]; ArraySetAsSeries(rates, false);
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()); }
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!

Comprobemos ahora la coherencia entre los distintos segmentos de datos:

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





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso