Anotação de dados na análise de série temporal (Parte 1): Criação de um conjunto de dados com rótulos de tendência usando um gráfico EA
Introdução
Quando desenvolvemos modelos de inteligência artificial, geralmente precisamos treinar os dados primeiro. Dados de boa qualidade nos permitem obter resultados duas vezes melhores com a metade do esforço de treinamento e validação do modelo. Entretanto, os dados relacionados a moedas ou ações são peculiares. Eles possuem informações de mercado e de tempo complexas, têm um processo de rotulação difícil, mas, mesmo assim, podemos analisar facilmente a tendência dos dados históricos disponíveis no gráfico.
Esta seção apresenta um método para criar conjuntos de dados rotulados por tendências usando gráficos do Expert Advisor. Você pode manusear intuitivamente os dados implementando suas ideias. É claro que você também pode usar esse método para expandir e personalizar seus próprios conjuntos de dados!
Conteúdo:
- Formatação dos dados para rotulação
- Inicialização de gráficos e arquivos
- Lógica de trabalho
- Organização de dados e gravação em arquivo
- Apêndice: exemplo completo de código do EA
Formatação dos dados para rotulação
Quando recebemos dados sobre moedas ou ações que vêm de um cliente (este artigo não aborda dados externos lidos de arquivos ou baixados de outros sites), a situação geral é a seguinte:
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 |
Acima, vemos como aparecem os dados das cinco séries temporais. Os valores Close e Open estão relacionados entre si do início ao fim, e a relação é muito forte. Partimos do princípio de que as duas primeiras linhas são de uma tendência ascendente e as demais são de uma tendência descendente (os dados acima são apenas um exemplo). O método convencional de rotulação dividiria os dados em duas 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 |
Em seguida, o método informa ao nosso modelo qual parte representa uma tendência ascendente e qual representa uma tendência descendente, mas ao fazer isso, ignora seus atributos comuns e compromete a integridade dos dados. Como podemos resolver esse problema?
Temos a possibilidade de adicionar uma agrupação de tendências à nossa série temporal da seguinte forma (tome os cinco pedaços de dados acima como exemplo ou siga as suposições mencionadas anteriormente):
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 |
Mas se quisermos implementar a análise do desenvolvimento da tendência no modelo, em particular o quanto a tendência atual se desenvolveu (por exemplo, a teoria das ondas nos diz que uma tendência geral geralmente inclui uma fase de tendência e uma fase de correção, uma fase de tendência tem 5 fases de onda e uma fase de correção tem 3 ajustes de onda, etc.), precisamos dividir adicionalmente os dados. Podemos fazer isso adicionando outra coluna que represente o desenvolvimento da tendência contida nos dados (supondo que os primeiros 2 dos próximos 10 dados estejam em tendência ascendente, os últimos 5 estejam em tendência ascendente e o restante no meio esteja em tendência descendente), assim:
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. Trend_group, que define uma tendência ascendente, é igual a 0
2. Trend_group, que define uma tendência descendente, é igual a 1.
Então, começaremos a controlar o gráfico no lado do cliente, colocando os dados de acordo com o padrão desejado.
Inicialização de gráficos e arquivos
Como precisamos olhar para o gráfico para rotular os dados, ele deve ser rolado de acordo com o que fazemos manualmente, por isso, precisamos desativar CHART_AUTOSCROLL e CHART_SHIFT:
ChartSetInteger (0, CHART_AUTOSCROLL, false); ChartSetInteger (0, CHART_SHIFT, true); ChartSetInteger (0, CHART_MOUSE_SCROLL ,1);Nota: A parte em verde do código é destinada ao controle do gráfico usando a roda do mouse
Ao inicializar o arquivo, primeiramente é necessário verificar se existe um arquivo de rotulação e um arquivo de dados históricos, e então armazenar o nome do arquivo na variável 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: Usamos o laço do - while, que difere do laço while porque primeiro executa a instrução e depois avalia a expressão. O principal problema aqui é a inicialização do nome
int hd= FileFindFirst("*",name,0);
Se houver um arquivo original rotulado, vamos abri-lo e obter o último tempo rotulado pela função read_csv():
read_csv(file_handle,a);Em seguida, rolamos o gráfico até o último tempo rotulado:
shift = - iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]); ChartNavigate(0, CHART_END ,shift);
Criamos um arquivo de histórico se não houver nenhum:
file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t), FILE_WRITE | FILE_CSV | FILE_READ);Depois, rolamos o gráfico até a posição especificada pela variável global start_t.
shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t); ChartNavigate(0,CHART_END,shift);Adicionamos uma linha vermelha vertical para rotular a coluna inicial:
ObjectCreate (0,"Start",OBJ_VLINE,0,(datetime)start_t,0)A lógica dessa parte está estruturada da seguinte forma:
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); }Atenção: Como queremos mover o gráfico para a esquerda, antes da função iBarShift(), é necessário adicionar "-".
shift = -iBarShift(Symbol(), PERIOD_CURRENT ,(datetime)start_t);Claro que a lógica também pode ser implementada na função ChartNavigate(), por exemplo:
ChartNavigate(0,CHART_END,-shift);No entanto, o código deste artigo é implementado usando o primeiro 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 - timeframe para dar início;
2. Shift variable - número de colunas a serem deslocadas; o código de exemplo mostra o número de colunas que serão deslocadas por meio da conversão do tempo especificado;
3. A função read_csv() será definida posteriormente.
void read_csv(int hd, string &arry[]) { int i= 0; while(!FileIsEnding(hd)) { ArrayResize(arry,i+1); arry[i]= FileReadString(hd); i++; } }
Nota: Usamos um laço while para encontrar a linha final do arquivo de anotações históricas, obter a última linha de dados no arquivo e encontrar o tempo de término da nossa última anotação. A anotação rolará o gráfico de preços até o gráfico de barras, para que possamos continuar comentando a partir daqui.
Lógica de trabalho
- Home — ir para a última barra do gráfico;
- End — ir para a primeira barra do gráfico;
- Page Up — mover o gráfico uma janela para trás;
- Page Down — mover o gráfico uma janela para frente;
- Ctrl+I — abrir a janela com a lista de indicadores;
- Ctrl+B — abrir a janela com a lista de objetos;
- Alt+1 — o gráfico é exibido como uma série de barras;
- Alt+2 — o gráfico é exibido como uma sequência de velas japonesas;
- Alt+3 — o gráfico é exibido como uma linha que conecta os preços de fechamento;
- Ctrl+G — mostrar/ocultar a grade na janela do gráfico;
- "+" — aumentar o zoom no gráfico;
- "-" — reduzir a escala do gráfico;
- F12 — rolar o gráfico passo a passo (barra por barra);
- F8 — abrir a janela de propriedades;
- Backspace — remover o último objeto adicionado ao gráfico;
- Excluir — remover todos os objetos selecionados;
- Ctrl+Z — desfazer o apagamento do último objeto.
#define KEY_B 66 #define KEY_S 83
2) Pressionamos s para rotular o fim da tendência ascendente, a variável typ ainda é 0, a variável tp é definida como end, a cor da seta ainda é clrBlue e o número de rótulos Num permanece inalterado. Só precisamos aumentar a variável no início do segmento de dados, e a inversão de first é usada para indicar que pressionar o botão novamente fará com que a parte "inicial" do segmento de dados rotulado seja executada.
3) Depois de executar a instrução switch, vamos chamar a função ChartRedraw() para redesenhar o 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. variável type — 0 significa tendência ascendente, 1 — tendência descendente;
2. variável "Num" - número de rótulos, que será intuitivamente exibido no gráfico;
3. variável first controla que nossos rótulos sempre vêm em pares, o que garante que cada grupo represente b e s ou s e b, evitando confusão;
4. variável tp é usada para determinar o início ou fim do segmento de dados.
2. Clique com o botão esquerdo do mouse no gráfico para definir a posição do rótulo
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. A função ChartXYToTimePrice() é usada principalmente para obter as propriedades do gráfico no local do clique, incluindo o tempo atual e o preço. A variável global dt é necessária para obter o tempo atual;
2. Com o clique do mouse, também precisamos determinar se a ação atual é o início ou o fim do segmento de dados. Usamos a variável global tp para avaliação.
3. Ações a serem realizadas
Se você deseja rotular uma tendência ascendente, primeiro pressione a tecla b, clique com o botão esquerdo do mouse na coluna que começa a ser rotulada no gráfico, então pressione a tecla s, e então clique com o botão esquerdo do mouse no final da coluna no gráfico para completar a rotulação. Uma par de setas azuis aparecerá no gráfico, como mostrado na imagem abaixo:
Se você deseja rotular uma tendência descendente, primeiro pressione a tecla s, clique com o botão esquerdo do mouse na coluna que começa a ser rotulada no gráfico, então pressione a tecla b, e então clique com o botão esquerdo do mouse no final da coluna no gráfico. Após completar a rotulação, um par de setas vermelhas aparecerá, como mostrado na imagem abaixo:
A coluna de saída de rotulação exibirá a ação de rotulação a qualquer momento, o que é muito conveniente para acompanhar o processo, conforme mostrado na figura:
Nota: Esta parte, na verdade, poderia ser melhor otimizada, por exemplo, adicionando uma função para desfazer a última ação, permitindo que você ajuste a posição do rótulo a qualquer momento, além de evitar operações incorretas, mas sou uma pessoa preguiçosa... (^о^)
Organização de dados e gravação em arquivo
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. Precisamos escrever o cabeçalho do índice na primeira vez que o arquivo for gravado;
2. Trend_group é na verdade um tipo de variável global;
3. Não chamamos FileClose() neste ponto porque nossa rotulação ainda não está concluída. Vamos chamar essa função na função OnDeinit(), para gravar o resultado final no arquivo.
4. É necessário prestar especial atenção à parte amarela do código.
if(FileTell(file_handle)==2)Ao determinar a presença de dados (certamente, também é possível usar outros métodos, por exemplo, adicionar uma variável para atribuir um valor a ela durante a inicialização), se não houver dados no arquivo, é preciso adicionar um cabeçalho como este:
FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");Se houver dados no arquivo, não será necessário adicionar um cabeçalho; caso contrário, os dados serão cortados, o que é muito importante!
Vamos verificar se os diferentes segmentos de dados são consistentes:
Apêndice: exemplo completo de código do EA
1. Definição de variáveis globais e constantes. O parâmetro start_t pode ser definido a partir dos dados em segundos desde 01.01.1970. Claro, ele também pode ser definido usando o datetime padrão ou a variável de entrada input int start_t=1403037112; para que possa ser alterado a qualquer momento em execuções futuras do Expert Advisor:#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: Claro que você também pode definir uma tecla como uma variável de entrada conforme preferir.
input int KEY_B=66; input int KEY_S=83;
A vantagem é que, se você achar que as teclas não são fáceis de usar, poderá alterá-las como quiser cada vez que executar o EA até ficar satisfeito, já que o código não será alterado temporariamente.
2. A função OnInit(), onde inicializamos nossos preparativos:
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 todas as nossas operações com teclado e mouse no gráfico foram concluídas, colocamos as principais funções lógicas na função 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: Modificamos o código acima ao implementar essa função.
if (id==CHARTEVENT_CLICK&&(typ!=3))
A razão pela qual fazemos isso é muito simples: evitamos operações incorretas causadas por cliques acidentais do mouse e usamos a variável typ para controlar a validade da ação do mouse. Quando rotulamos uma tendência, executamos a função file_write(). Adicionamos a seguinte linha no final da função.
typ=3;
Então, você pode usar o mouse para periodicamente gerenciar o gráfico antes de começar a próxima série de rotulação sem qualquer ação adicional, até encontrar a posição adequada e estar pronto para rotular a próxima tendência.
4. Implementação da função de gravação de dados - 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. Implementação da função de leitura de arquivo - 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. Existe outro problema importante que não foi abordado aqui, o identificador do arquivo file_handle, aberto durante a inicialização do Expert Advisor, não é liberado. Liberamos o identificador na última função OnDeinit(). Ao chamar a função FileClose(file_handle), todos os dados serão gravados no arquivo csv, portanto, é especialmente importante não tentar abrir o arquivo csv durante a operação do Expert Advisor:
void OnDeinit(const int reason) { FileClose(file_handle); Print("Write data!"); }
Nota: O código mostrado neste artigo é apenas para demonstração. Se você deseja usar este código na prática, é recomendável aprimorá-lo adicionalmente. Este artigo vem acompanhado do arquivo CSV e do arquivo MQL5 final usados na demonstração. O próximo artigo da série descreverá como anotar dados através de um cliente usando Python.
Obrigado por sua atenção!
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/13225
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso