
Como preparar cotações do MetaTrader 5 para outros aplicativos
Conteúdo
Introdução1. Tópicos abordados
2. Formato de dados
3. Parâmetros externos do programa
4. Verificação dos parâmetros inseridos pelo usuário
5. Variáveis globais
6. Painel de informações
7. Bloco principal do aplicativo
8. Criação de pastas e arquivamento de dados
Conclusão
Introdução
Antes de começar a estudar MQL5, eu tentei utilizar diversos outros aplicativos para desenvolvimento de sistemas de negociação. Não posso dizer que foi uma perda de tempo. Alguns desses aplicativos contêm algumas ferramentas úteis que permitem que os usuários economizem tempo, lidem com muitos problemas, destruam alguns mitos e selecionem rapidamente alguma direção de desenvolvimento adicional sem conhecimento de linguagens de programação.
Esses aplicativos precisam de dados históricos. Devido à ausência de um determinado formato de dados padrão, eles geralmente tinham que ser editados antes de serem utilizados (por exemplo, no Excel) para se adequarem ao formato aplicável ao programa necessário. Mesmo que você pudesse calcular todos os detalhes necessários, muitas coisas ainda tinham que ser feitas manualmente. Os usuários podem encontrar diferentes versões de scripts projetados para copiar as cotações do MetaTrader 4 para o formato necessário. Se houver necessidade, também podemos desenvolver a versão do script para MQL5.
1. Tópicos abordados
O artigo aborda os seguintes tópicos:
- Trabalhar com a lista de símbolo na janela Market Watch e com a lista de símbolo comum no servidor.
- Verificar a profundidade dos dados disponíveis e baixar a quantidade faltante, se necessário, com o gerenciamento correto de diversas situações.
- Exibição de informações referentes aos dados solicitados no gráfico do painel personalizado e no diário.
- Preparação de dados para arquivamento em formato definido pelo usuário.
- Criação de diretórios de arquivos.
- Arquivamento de dados.
2. Formato de dados
Apresentarei um exemplo de preparação de dados a serem utilizados no NeuroShell DayTrader Professional (NSDT). Eu experimentei as versões 5 e 6 do NSDT e descobri que elas têm diferentes requisitos em relação ao formato de dados. Os dados de data e hora da versão 5 do NSDT devem estar em colunas diferentes. A primeira linha do arquivo deve ter o seguinte aspecto:
"Date" "Time" "Open" "High" "Low" "Close" "Volume"
A linha do cabeçalho na versão 6 do NSDT deve ter um aspecto diferente para permitir que o aplicativo aceite um arquivo. Isso significa que a data e a hora devem estar na mesma coluna:
Date,Open,High,Low,Close,Volume
O MetaTrader 5 permite que os usuários salvem cotações em arquivos *.csv. Os dados em um arquivo terão o seguinte aspecto:
Figura 1. Dados salvos pelo terminal MetaTrader 5
Entretanto, não podemos simplesmente editar a linha do cabeçalho, porque a data deve ter outro formato. Para a versão 5 do NSDT:
dd.mm.aaaa,hh:mm,Open,High,Low,Close,VolumePara a versão 6 do NSDT:
dd/mm/aaaa hh:mm,Open,High,Low,Close,Volume
Listas suspensas serão usadas nos parâmetros externos do script para que os usuários possam selecionar o formato necessário. Além da seleção dos formatos do cabeçalho e data, possibilitaremos que os usuários selecionem o número de símbolos, dados nos quais eles desejam armazenar em arquivos. Para isso, prepararemos três versões:
- Apenas escreva os dados no símbolo atual, no gráfico em que (APENAS O SíMBOLO ATUAL) o script foi executado.
- Escreva os dados nos símbolos localizados na janela "Market Watch" (SíMBOLOS MARKETWATCH).
- Escreva os dados em todos os símbolos disponíveis no servidor (LISTA COMPLETA DE SíMBOLOS).
Vamos inserir o código a seguir antes dos parâmetros externos no código do script para criar essas listas:
//_________________________________ // HEADER_FORMATS_ENUMERATION enum FORMAT_HEADERS { NSDT_5 = 0, // "Date" "Time" "Open" "High" "Low" "Close" "Volume" NSDT_6 = 1 // Date,Open,High,Low,Close,Volume }; //--- //___________________________ // ENUMERATION_OF_DATA_FORMATS enum FORMAT_DATETIME { SEP_POINT1 = 0, // dd.mm.yyyy hh:mm SEP_POINT2 = 1, // dd.mm.yyyy, hh:mm SEP_SLASH1 = 2, // dd/mm/yyyy hh:mm SEP_SLASH2 = 3 // dd/mm/yyyy, hh:mm }; //--- //____________________________ // ENUMERATION_OF_FILING_MODES enum CURRENT_MARKETWATCH { CURRENT = 0, // ONLY CURRENT SYMBOLS MARKETWATCH = 1, // MARKETWATCH SYMBOLS ALL_LIST_SYMBOLS = 2 // ALL LIST SYMBOLS };
Você pode encontrar mais informações sobre enumerações na Referência MQL5.
3. Parâmetros externos do programa
Agora, podemos criar a lista completa de todos os parâmetros externos do script:
//____________________________________________________________________ //+------------------------------------------------------------------+ //| EXTERNAL_PARAMETERS | //+------------------------------------------------------------------+ input datetime start_date = D'01.01.2011'; // Start Date input datetime end_date = D'18.09.2012'; // End Date input FORMAT_HEADERS format_headers = NSDT_5; // Format Headers input FORMAT_DATETIME format_date = SEP_POINT2; // Format Datetime input CURRENT_MARKETWATCH curr_mwatch = CURRENT; // Mode Write Symbols input bool clear_mwatch = true; // Clear Market Watch input bool show_progress = true; // Show Progress (%)
Os parâmetros externos são usados para as seguintes finalidades:
- Os usuários podem especificar um intervalo de datas utilizando os parâmetros Data de início (start_date) e Data de término (end_date).
- A lista suspensa Formatar cabeçalhos (format_headers) permite que os usuários selecionem um formato de cabeçalho.
- A lista suspensa Formatar Data e hora (format_date) permite que os usuários selecionem os formatos de data e hora.
- A lista suspensa Modo de escrita de símbolos (curr_mwatch) permite que os usuários selecionem o número de símbolos para arquivamento.
- Se o parâmetro Limpar Market Watch (clear_mwatch) for verdadeiro, isso permite que os usuários apaguem todos os símbolos da janela Market Watch após o arquivamento. Isso apenas se aplica aos símbolos com gráficos que não estão ativos no momento.
- O parâmetro Exibir progresso (%) (show_progress) exibe o progresso do arquivamento no painel de dados. O arquivamento será mais rápido se este parâmetro estiver desabilitado.
A figura abaixo mostra como será o aspecto dos parâmetros externos durante a execução:
Figura 2. Parâmetros externos do programa
4. Verificação dos parâmetros inseridos pelo usuário
Vamos criar a função para verificação dos parâmetros inseridos pelo usuário antes do código base. Por exemplo, a data de início no parâmetro Start Date deve ser inserido antes da data do parâmetro End Date. O formato dos cabeçalhos deve ser compatível com os formatos de data e hora. Se o usuário cometeu alguns erros ao configurar os parâmetros, a seguinte mensagem de alerta será exibida e o programa irá parar.
Exemplo de mensagem de alerta:
Figura 3. Exemplo de mensagem de erro sobre parâmetros incorretamente especificados
Função ValidationParameters():
//____________________________________________________________________ //+------------------------------------------------------------------+ //| CHECKING_CORRECTNESS_OF_PARAMETERS | //+------------------------------------------------------------------+ bool ValidationParameters() { if(start_date>=end_date) { MessageBox("The start date should be earlier than the ending one!\n\n" "Application cannot continue. Please retry.", //--- "Parameter error!",MB_ICONERROR); //--- return(true); } //--- if(format_headers==NSDT_5 && (format_date==SEP_POINT1 || format_date==SEP_SLASH1)) { MessageBox("For the headers of the following format:\n\n" "\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\"\n\n" "Date/time format can be selected out of two versions:\n\n" "dd.mm.yyyy, hh:mm\n" "dd/mm/yyyy, hh:mm\n\n" "Application cannot continue. Please retry.", //--- "Header and date/time formats do not match!",MB_ICONERROR); //--- return(true); } //--- if(format_headers==NSDT_6 && (format_date==SEP_POINT2 || format_date==SEP_SLASH2)) { MessageBox("For the headers of the following format:\n\n" "Date,Open,High,Low,Close,Volume\n\n" "Date/time format can be selected out of two versions:\n\n" "dd.mm.yyyy hh:mm\n" "dd/mm/yyyy hh:mm\n\n" "Application cannot continue. Please retry.", //--- "Header and date/time formats do not match!",MB_ICONERROR); //--- return(true); } //--- return(false); }
5. Variáveis globais
Em seguida, devemos determinar todas as variáveis globais e as séries que serão utilizadas no script:
//____________________________________________________________________ //+------------------------------------------------------------------+ //| GLOBAL_VARIABLES_AND_ARRAYS | //+------------------------------------------------------------------+ MqlRates rates[]; // Array for copying data //--- string symbols[]; // Symbol array //--- // Array of graphic object names string arr_nmobj[22]= { "fon","hd01", "nm01","nm02","nm03","nm04","nm05","nm06","nm07","nm08","nm09","nm10", "nm11","nm12","nm13","nm14","nm15","nm16","nm17","nm18","nm19","nm20" }; //--- // Array of displayed text containing graphic objects string arr_txtobj[21]; //--- string path=""; // File path int cnt_symb=0; // Number of symbols int sz_arr_symb=0; // Symbol array size int bars=0; // Number of bars according to the specified TF int copied_bars=0; // Number of bars copied for writing double pgs_pcnt=0; // Writing progress int hFl=INVALID_HANDLE; // File handle //--- string // Variables for data formatting sdt="", // Date line dd="", // Day mm="", // Month yyyy="", // Year tm="", // Time sep=""; // Separator //--- int max_bars=0; // Maximum number of bars in the terminal settings //--- datetime first_date=0, // First available data in a specified period first_termnl_date=0, // First available data in the terminal's database first_server_date=0, // First available data in the server's database check_start_date=0; // Checked correct date value
6. Painel de informações
Agora, devemos lidar com os elementos que serão exibidos no painel de informações. Três tipos de objetos gráficos podem ser utilizados como fundo:
- O mais simples e evidente – "Rótulo retângulo" (OBJ_RECTANGLE_LABEL).
- Os usuários que desejam ter uma interface com aparência única podem utilizar um objeto "Bitmap" (OBJ_BITMAP).
- Um objeto de "Edição" (OBJ_EDIT) também pode ser usado como fundo. Habilite a propriedade "somente leitura" para eliminar a possibilidade de inserir um texto. Também há outra vantagem na utilização do objeto de "Edição". Caso você tenha criado um painel de informações em um Expert Advisor e você deseja que ele tenha a mesma aparência durante os testes no modo de visualização, até agora, o último método é o único que possibilita fazer isso. Nem OBJ_RECTANGLE_LABEL, nem OBJ_BITMAP são exibidos durante os testes no modo de visualização.
Embora, em nosso caso, apenas tenhamos desenvolvido um script em vez de um Expert Advisor, o fundo com o objeto OBJ_EDIT será utilizado como exemplo: O resultado é exibido na figura abaixo:
Figura 4. Painel de informações
Vamos descrever todos os dados apresentados no painel:
- Symbol (atual/total) – símbolo, os dados em que é baixado/copiado/escrito no momento. O número à esquerda dentro dos parênteses mostra o número de símbolo atual. O número à esquerda mostra o número comum de símbolos com os quais o script trabalhará.
- Path Symbol – O caminho de símbolo ou a categoria a que ele pertence. Se você acessar o menu de contexto clicando na janela Market Watch e selecionar "Símbolos...", a janela com a lista de todos os símbolos aparecerá. Você pode saber mais sobre isso no guia do usuário do terminal.
- Timeframe – período (período de tempo). O período de tempo em que o script será executado/usado.
- Input Start Date – data de início de dados especificado por um usuário nos parâmetros do script.
- First Date (H1) – a primeira data de dados disponível (barra) do período de tempo atual.
- First Terminal Date (M1) – a primeira data disponível do período de tempo M1 em dados do terminal já existentes.
- First Server Date (M1) – a primeira data disponível do período de tempo M1 no servidor.
- Max. Bars In Options Terminal – número máximo de barras a serem exibidas no gráfico especificado nas configurações do terminal.
- Copied Bars – número de barras copiadas a serem escritas.
- Progress Value Current Symbol – valor percentual de dados escritos do símbolo atual.
Abaixo, segue o código desse painel de informações:
//____________________________________________________________________ //+------------------------------------------------------------------+ //| INFORMATION_PANEL | //|------------------------------------------------------------------+ void InfoTable(int s) { int fnt_sz=8; // Font size string fnt="Calibri"; // Header font color clr=clrWhiteSmoke; // Color //--- int xH=300; int height_pnl=0; int yV1=1,yV2=12,xV1=165,xV2=335,xV3=1; //--- string sf="",stf="",ssf=""; bool flg_sf=false,flg_stf=false,flg_ssf=false; //--- if(show_progress) { height_pnl=138; } else { height_pnl=126; } //--- flg_sf=SeriesInfoInteger(symbols[s],_Period,SERIES_FIRSTDATE,first_date); flg_stf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_termnl_date); flg_ssf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date); //--- if(flg_sf) { sf=TSdm(first_date); } else { sf="?"; } if(flg_stf) { stf=TSdm(first_termnl_date); } else { stf="?"; } if(flg_ssf) { ssf=TSdm(first_server_date); } else { ssf="?"; } //--- if(cnt_symb==0) { cnt_symb=1; } //--- int anchor1=ANCHOR_LEFT_UPPER,anchor2=ANCHOR_RIGHT_UPPER,corner=CORNER_LEFT_UPPER; //--- string path_symbol=SymbolInfoString(symbols[s],SYMBOL_PATH); path_symbol=StringSubstr(path_symbol,0,StringLen(path_symbol)-StringLen(symbols[s])); //--- arr_txtobj[0]="INFO TABLE"; arr_txtobj[1]="Symbol (current / total) : "; arr_txtobj[2]=""+symbols[s]+" ("+IS(s+1)+"/"+IS(cnt_symb)+")"; arr_txtobj[3]="Path Symbol : "; arr_txtobj[4]=path_symbol; arr_txtobj[5]="Timeframe : "; arr_txtobj[6]=gStrTF(_Period); arr_txtobj[7]="Input Start Date : "; arr_txtobj[8]=TSdm(start_date); arr_txtobj[9]="First Date (H1) : "; arr_txtobj[10]=sf; arr_txtobj[11]="First Terminal Date (M1) : "; arr_txtobj[12]=stf; arr_txtobj[13]="First Server Date (M1) : "; arr_txtobj[14]=ssf; arr_txtobj[15]="Max. Bars In Options Terminal : "; arr_txtobj[16]=IS(max_bars); arr_txtobj[17]="Copied Bars : "; arr_txtobj[18]=IS(copied_bars); arr_txtobj[19]="Progress Value Current Symbol : "; arr_txtobj[20]=DS(pgs_pcnt,2)+"%"; //--- Create_Edit(0,0,arr_nmobj[0],"",corner,fnt,fnt_sz,clrDimGray,clrDimGray,345,height_pnl,xV3,yV1,2,C'15,15,15'); //--- Create_Edit(0,0,arr_nmobj[1],arr_txtobj[0],corner,fnt,8,clrWhite,C'64,0,0',345,12,xV3,yV1,2,clrFireBrick); //--- Create_Label(0,arr_nmobj[2],arr_txtobj[1],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2,0); Create_Label(0,arr_nmobj[3],arr_txtobj[2],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2,0); //--- Create_Label(0,arr_nmobj[4],arr_txtobj[3],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*2,0); Create_Label(0,arr_nmobj[5],arr_txtobj[4],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*2,0); //--- Create_Label(0,arr_nmobj[6],arr_txtobj[5],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*3,0); Create_Label(0,arr_nmobj[7],arr_txtobj[6],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*3,0); //--- Create_Label(0,arr_nmobj[8],arr_txtobj[7],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*4,0); Create_Label(0,arr_nmobj[9],arr_txtobj[8],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*4,0); //--- Create_Label(0,arr_nmobj[10],arr_txtobj[9],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*5,0); Create_Label(0,arr_nmobj[11],arr_txtobj[10],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*5,0); //--- Create_Label(0,arr_nmobj[12],arr_txtobj[11],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*6,0); Create_Label(0,arr_nmobj[13],arr_txtobj[12],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*6,0); //--- Create_Label(0,arr_nmobj[14],arr_txtobj[13],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*7,0); Create_Label(0,arr_nmobj[15],arr_txtobj[14],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*7,0); //--- Create_Label(0,arr_nmobj[16],arr_txtobj[15],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*8,0); Create_Label(0,arr_nmobj[17],arr_txtobj[16],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*8,0); //--- Create_Label(0,arr_nmobj[18],arr_txtobj[17],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*9,0); Create_Label(0,arr_nmobj[19],arr_txtobj[18],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*9,0); //--- if(show_progress) { Create_Label(0,arr_nmobj[20],arr_txtobj[19],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*10,0); Create_Label(0,arr_nmobj[21],arr_txtobj[20],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*10,0); } } //____________________________________________________________________ //+------------------------------------------------------------------+ //| CREATING_LABEL_OBJECT | //+------------------------------------------------------------------+ void Create_Label(long chrt_id, // chart id string lable_nm, // object name string rename, // displayed name long anchor, // anchor point long corner, // attachment corner string font_bsc, // font int font_size, // font size color font_clr, // font color int x_dist, // X scale coordinate int y_dist, // Y scale coordinate long zorder) // priority { if(ObjectCreate(chrt_id,lable_nm,OBJ_LABEL,0,0,0)) // creating object { ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,rename); // set name ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc); // set font ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr); // set font color ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ANCHOR,anchor); // set anchor point ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner); // set attachment corner ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size); // set font size ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist); // set X coordinates ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist); // set Y coordinates ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false); // unable to highlight the object, if FALSE ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder); // Higher/lower priority ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n"); // no tooltip, if "\n" } } //____________________________________________________________________ //+------------------------------------------------------------------+ //| CREATING_EDIT_OBJECT | //+------------------------------------------------------------------+ void Create_Edit(long chrt_id, // chart id int nmb_win, // window (subwindow) index string lable_nm, // object name string text, // displayed text long corner, // attachment corner string font_bsc, // font int font_size, // font size color font_clr, // font color color font_clr_brd, // font color int xsize, // width int ysize, // height int x_dist, // X scale coordinate int y_dist, // Y scale coordinate long zorder, // priority color clr) // background color { if(ObjectCreate(chrt_id,lable_nm,OBJ_EDIT,nmb_win,0,0)) // creating object { ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,text); // set name ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner); // set attachment corner ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc); // set font ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ALIGN,ALIGN_CENTER); // center alignment ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size); // set font size ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr); // font color ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BORDER_COLOR,font_clr_brd); // background color ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BGCOLOR,clr); // background color ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XSIZE,xsize); // width ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YSIZE,ysize); // height ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist); // set X coordinate ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist); // set Y coordinate ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false); // unable to highlight the object, if FALSE ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder); // Higher/lower priority ObjectSetInteger(chrt_id,lable_nm,OBJPROP_READONLY,true); // Read only ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n"); // no tooltip if "\n" } }
Após a operação do script ter sido finalizada ou após o script ter sido apagado por um usuário antes do tempo, todos os objetos gráficos criados pelo script devem ser apagados. A função a seguir será usada para isso:
//____________________________________________________________________ //+------------------------------------------------------------------+ //| DELETE_ALL_GRAPHICAL_OBJECTS_CREATED_BY_THE_SCRIPT | //+------------------------------------------------------------------+ void DelAllScriptObjects() { // Receive the size of graphical object names array int sz_arr1=ArraySize(arr_nmobj); //--- // Delete all objects for(int i=0; i<sz_arr1; i++) { DelObjbyName(arr_nmobj[i]); } } //____________________________________________________________________ //+------------------------------------------------------------------+ //| DELETE_OBJECTS_BY_NAME | //+------------------------------------------------------------------+ int DelObjbyName(string Name) { int nm_obj=0; bool res=false; //--- nm_obj=ObjectFind(ChartID(),Name); //--- if(nm_obj>=0) { res=ObjectDelete(ChartID(),Name); //--- if(!res) { Print("Object deletion error: - "+ErrorDesc(Error())+""); return(false); } } //--- return(res); }
7. Bloco principal do aplicativo
OnStart() é a função principal dos scripts. Esta é a função utilizada para chamar todas as outras funções para execução. Os detalhes da operação do programa são exibidos no código:
//____________________________________________________________________ //+------------------------------------------------------------------+ //| SCRIPT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | //+------------------------------------------------------------------+ void OnStart() { // If user-defined parameters are incorrect, // error message is shown and the program is closed if(ValidationParameters()) { return; } //--- max_bars=TerminalInfoInteger(TERMINAL_MAXBARS); // Receive available number of bars in the window //--- GetSymbolsToArray(); // Filling symbol array with names sz_arr_symb=ArraySize(symbols); // Receive symbol array size //--- SetSeparateForFormatDate(); // Set a separator for date format //--- // Revise all symbols and write their data to file for(int s=0; s<=sz_arr_symb-1; s++) { copied_bars=0; // Reset copied bars variable to zero for writing pgs_pcnt=0.0; // Reset variable of the symbol data writing progress //--- InfoTable(s); ChartRedraw(); //--- // Receive current symbol data int res=GetDataCurrentSymbol(s); //--- if(res==0) { BC } // If zero, break the loop or start the next iteration //--- if(res==2) // Program operation interrupted by user { DelAllScriptObjects(); // Deleted objects created by the script from the chart //--- Print("------\nUser deleted the script!"); break; } //--- // Receive the path for creating the file and create directories for them // If the string is empty, break the loop or start the next iteration if((path=CheckCreateGetPath(s))=="") { BC } //--- WriteDataToFile(s); // Write data to file } //--- // Delete symbols from Market Watch window if necessary DelSymbolsFromMarketWatch(); //--- // Delete objects created by the script from the chart Sleep(1000); DelAllScriptObjects(); }
Vamos analisar as funções em que as atividades principais acontecem.
A série de símbolos (symbols[]) está preenchida com nomes de símbolos na função GetSymbolsToArray(). O tamanho da série, bem como o número de símbolos dentro dela, dependerá da variante escolhida pelo usuário no parâmetro Modo de escrita de símbolos (curr_mwatch).
Se o usuário escolher dados de apenas um símbolo, o tamanho da série é igual a 1.
ArrayResize(symbols,1); // Set the array size to be equal to 1 symbols[0]=_Symbol; // Specify the current symbol's name
Se o usuário quiser receber dados de todos os símbolos da janela Market Watch ou de todos os símbolos disponíveis, o tamanho da série será definido pela seguinte função:
int SymbolsTotal( bool selected // true – only MarketWatch symbols );
Para evitar a criação de dois blocos para duas variantes com código quase idêntico, faremos a função ponteiro MWatchOrAllList(), que terá retorno verdadeiro ou falso. Esse valor define o local do qual será obtida a lista de símbolos - somente da janela Market Watch (verdadeiro) ou da lista comum de símbolos disponíveis (falso).
//____________________________________________________________________ //+------------------------------------------------------------------+ //| POINTER_TO_MARKET_WATCH_WINDOW_OR_TO_COMMON_LIST | //+------------------------------------------------------------------+ bool MWatchOrAllList() { if(curr_mwatch==MARKETWATCH) { return(true); } if(curr_mwatch==ALL_LIST_SYMBOLS) { return(false); } //--- return(true); }
Após obter o número de símbolos do circuito, devemos passar por toda a lista e inserir o nome do simbolo na série em cada iteração, aumentando o tamanho da série em um. O nome do símbolo, por sua vez, é obtido pelo número do índice, utilizando a função SymbolName().
int SymbolName( int pos, // list index number bool selected // true – only MarketWatch symbols );
A função ponteiro MWatchOrAllList() também é usada na função SymbolName() para selecionar a lista de símbolos. O código completo da função GetSymbolsToArray():
//____________________________________________________________________ //+------------------------------------------------------------------+ //| FILLING_SYMBOL_ARRAY_WITH_NAMES | //+------------------------------------------------------------------+ void GetSymbolsToArray() { // If only the current symbol data is required if(curr_mwatch==CURRENT) { ArrayResize(symbols,1); symbols[0]=_Symbol; } //--- // If data on all symbols from Market Watch window or // or the entire symbol list is required if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS) { // Receive the number of symbols in Market Watch window cnt_symb=SymbolsTotal(MWatchOrAllList()); //--- for(int i=0; i<=cnt_symb-1; i++) { string nm_symb=""; //--- ArrayResize(symbols,i+1); // Increase the array size by one once again //--- // Receive a name of a symbol from Market Watch window nm_symb=SymbolName(i,MWatchOrAllList()); symbols[i]=nm_symb; // Put the symbol name into the array } } }
A função SetSeparateForFormatDate() é muito simples. Ela é usada para definir que tipo de separador será utilizado na data, dependendo da escolha do usuário na lista suspensa do parâmetro Formato de dados (format_date).
//____________________________________________________________________ //+------------------------------------------------------------------+ //| DEFINING_SEPARATOR_FOR_DATE_FORMAT | //+------------------------------------------------------------------+ void SetSeparateForFormatDate() { switch(format_date) { case SEP_POINT1 : case SEP_POINT2 : sep="."; break; // Full point as a separator case SEP_SLASH1 : case SEP_SLASH2 : sep="/"; break; // Slash as a separator } }
O circuito básico com diversas verificações vem em seguida. Se todas as verificações forem exitosas, os dados são escritos no arquivo. Caso contrário, o circuito é quebrado, todos os objetos são apagados do gráfico e o script é removido (em caso de script único) ou a próxima iteração é iniciada (em caso de mais de um símbolo). Cada símbolo da série symbols[] é consistentemente chamado no circuito. O número do índice é enviado a cada função do circuito. Assim, a sequência correta de todas as funções é preservada.
Os dados do símbolo atual do circuito são recebidos em cada iteração logo no início do corpo do circuito. A função GetDataCurrentSymbol() é utilizada para isso. Vamos ver o que acontece nesta função.
A disponibilidade de dados é verificada com o uso da função CheckLoadHistory() antes da cópia de dados do símbolo para a série rate[]. Essa função é fornecida pelos desenvolvedores como exemplo. A sua versão inicial pode ser encontrada na Referência MQL5. Apenas fizemos algumas correções para utilizá-la neste script. A Referência contém uma descrição detalhada (estudá-la também seria uma boa ideia), por essa razão, não apresentaremos aqui a nossa versão, visto que ela é quase idêntica. Além disso, ela pode ser encontrada no código com os comentários detalhados.
O único detalhe que pode ser mencionado agora é que a função CheckLoadHistory() retorna o código de erro ou de execução exitosa, de acordo com o qual a mensagem adequada do bloco do operador de permuta é salvo no diário. De acordo com o código recebido, a função GetDataCurrentSymbol() continua com a sua operação ou retorna o seu código.
Se tudo correr bem, os dados históricos são copiados através da utilização da função CopyRates(). O tamanho da série é salvo na variável global. Em seguida, realiza-se a saída da função acompanhada pelo retorno do código 1. Caso ocorra algum erro, a função suspende a sua operação no operador de permuta e retorna o código 0 ou 2.
//____________________________________________________________________ //+------------------------------------------------------------------+ //| RECEIVE_SYMBOL_DATA | //+------------------------------------------------------------------+ int GetDataCurrentSymbol(int s) { Print("------\n№"+IS(s+1)+" >>>"); // Save a symbol number in the journal //--- // Check and download the necessary amount of requested data int res=CheckLoadHistory(s,_Period); //--- InfoTable(s); ChartRedraw(); // Update the data in the data table //--- switch(res) { case -1 : Print("Unknown symbol "+symbols[s]+" (code: -1)!"); return(0); case -2 : Print("Number of requested bars exceeds the maximum number that can be displayed on a chart (code: -2)!...\n" "...The available amount of data will be used for writing."); break; //--- case -3 : Print("Execution interrupted by user (code: -3)!"); return(2); case -4 : Print("Download failed (code: -4)!"); return(0); case 0 : Print("All symbol data downloaded (code: 0)."); break; case 1 : Print("Time series data is sufficient (code: 1)."); break; case 2 : Print("Time series created based on existing terminal data (code: 2)."); break; //--- default : Print("Execution result is not defined!"); } //--- // Copy data to the array if(CopyRates(symbols[s],_Period,check_start_date,end_date,rates)<=0) { Print("Error when copying symbol data "+symbols[s]+" - ",ErrorDesc(Error())+""); return(0); } else { copied_bars=ArraySize(rates); // Receive array size //--- Print("Symbol: ",symbols[s],"; Timeframe: ",gStrTF(_Period),"; Copied bars: ",copied_bars); } //--- return(1); // Return 1, if all is well }
Após isso, o programa está novamente localizado no corpo do circuito principal da função OnStart(). O código é designado pela variável local res e a verificação é realizada de acordo com o valor dela. O valor zero representa um erro. Isso significa que os dados do símbolo atual do circuito não podem ser escritos. A explicação do erro é salva no diário e se decide se o circuito deve ser quebrado (quebrar) ou se a próxima iteração deve ser iniciada (continuar).
if(res==0) { BC } // If zero, the loop is interrupted or the next iteration starts
A linha de código acima mostra que essa seleção é realizada por caracteres BC misteriosos. Isso é uma macro expansão. Informações adicionais podem ser encontradas na Referência MQL5. A única coisa que deve ser mencionada aqui é que as expressões completas (escritas em uma linha) podem ser copiadas em uma entrada curta conforme exibido no exemplo acima (BC). Em alguns casos, esse método pode ser ainda mais conveniente e compacto que uma função. No caso em análise, isso tem o seguinte aspecto:
// Macro expansion with further action selection #define BC if(curr_mwatch==CURRENT) { break; } if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS) { continue; }
Abaixo, seguem outros exemplos de macro expansões utilizadas neste script:
#define nmf __FUNCTION__+": " // Macro expansion of the function name before sending the message to the journal //--- #define TRM_DP TerminalInfoString(TERMINAL_DATA_PATH) // Folder for storing the terminal data
Se GetDataCurrentSymbol() retornar 2, o programa foi deletado por um usuário. O MQL5 tem a função IsStopped() para identificar este evento. Essa função pode ser muito útil em circuitos para interromper a operação do programa corretamente e na hora certa. Se a função retornar "verdadeiro", então há cerca de três segundos para a realização de todas as ações antes que o programa seja deletado à força. Em nosso caso, todos os objetos gráficos são removidos e a mensagem é enviada ao diário:
if(res==2) // Program execution interrupted by user { DelAllScriptObjects(); // Delete all objects created by the script from the chart //--- Print("------\nUser deleted the script!"); break; }
8. Criação de pastas e arquivamento de dados
A função CheckCreateGetPath() verifica a presença da pasta de dados raiz. Vamos chamá-la de DATA_OHLC e colocá-la em C:\Metatrader 5\MQL5\Files. Ela conterá arquivos com os nomes dos símbolos. Os arquivos para gravação de dados serão criados aqui.
Se a pasta raiz ou a pasta para o símbolo atual do circuito não existir, a função a cria. Se tudo correr bem, a função retorna uma string contendo o caminho para a criação do arquivo. A função retorna uma string vazia em caso de erro ou de uma tentativa do usuário de apagar o gráfico.
O código abaixo contém comentários detalhados que torna a compreensão mais fácil:
//____________________________________________________________________ //+------------------------------------------------------------------+ //| CHECK_DIRECTORY_AND_CREATE_NECESSARY_DATA_FOLDERS | //+------------------------------------------------------------------+ string CheckCreateGetPath(int s) { int i=1; long search=-1; string ffname="",lpath=""; string file="*.csv",folder="*"; string root="DATA_OHLC\\", // Root data folder fSmb=symbols[s]+"\\", // Symbol name fTF=gStrTF(_Period)+"\\"; // Symbol time frame //--- bool flgROOT=false,flgSYMBOL=false; //--- //+------------------------------------------------------------------+ //| SEARCHING_FOR_DATA_OHLC_ROOT_FOLDER | //+------------------------------------------------------------------+ lpath=folder; search=FileFindFirst(lpath,ffname); // Set search handle in Metatrader 5\MQL5\Files //--- Print("Directory: ",TRM_DP+"\\MQL5\\Files\\"); //--- // Set the flag if the first folder is a root one if(ffname==root) { flgROOT=true; Print("Root folder "+root+" present"); } //--- if(search!=INVALID_HANDLE) // If search handle received { if(!flgROOT) // If the first folder is not a root one { // Sort out all files searching for the root folder while(FileFindNext(search,ffname)) { if(IsStopped()) // Execution interrupted by user { // Delete objects created by the script from the chart DelAllScriptObjects(); //--- Print("------\nUser deleted the script!"); return(""); } //--- if(ffname==root) // Set the flag if found { flgROOT=true; Print("Root folder "+root+" present"); break; } } } //--- FileFindClose(search); search=-1; // Close root folder search handle } else { Print("Error when receiving the search handle or directory "+TRM_DP+" is empty: ",ErrorDesc(Error())); } //--- //+------------------------------------------------------------------+ //| SEARCHING_SYMBOL_FOLDER | //+------------------------------------------------------------------+ lpath=root+folder; //--- // Set search handle in the root folder ..\Files\DATA OHLC\ search=FileFindFirst(lpath,ffname); //--- // Set the flag if the first folder of the current symbol if(ffname==fSmb) { flgSYMBOL=true; Print("Symbol folder "+fSmb+" present"); } //--- if(search!=INVALID_HANDLE) // If search handle is received { if(!flgSYMBOL) // If the first folder is not of the current symbol { // Sort out all the files in the root folder searching the symbol folder while(FileFindNext(search,ffname)) { if(IsStopped()) // Execution interrupted by user { // Delete objects created by the script from the chart DelAllScriptObjects(); //--- Print("------\nUser deleted the script!"); return(""); } //--- if(ffname==fSmb) // Set the flag if found { flgSYMBOL=true; Print("Symbol folder"+fSmb+" present"); break; } } } //--- FileFindClose(search); search=-1; // Close symbol folder search handle } else { Print("Error when receiving search handle or the directory "+path+" is empty"); } //--- //+------------------------------------------------------------------+ //| CREATE_NECESSARY_DIRECTORIES_ACCORDING_TO_CHECK_RESULTS | //+------------------------------------------------------------------+ if(!flgROOT) // If there is no DATA_OHLC... root folder { if(FolderCreate("DATA_OHLC")) // ...we should create it { Print("..\DATA_OHLC\\ root folder created"); } else { Print("Error when creating DATA_OHLC: root folder",ErrorDesc(Error())); return(""); } } //--- if(!flgSYMBOL) // If there is no folder of the symbol, the values of which should be received... { if(FolderCreate(root+symbols[s])) // ...we should create it { Print("..\DATA_OHLC\\" symbol folder created+fSmb+""); //--- return(root+symbols[s]+"\\"); // Return the path for creating the file for writing } else { Print("Error when creating ..\DATA_OHLC\\ symbol folder"+fSmb+"\: ",ErrorDesc(Error())); return(""); } } //--- if(flgROOT && flgSYMBOL) { return(root+symbols[s]+"\\"); // Return the path for creating the file for writing } //--- return(""); }
Se a função CheckCreateGetPath() retorna uma linha vazia, o circuito é interrompido ou a próxima iteração inicia a utilização da macro expansão (BC) já familiar:
// Receive the path for creating a file and create directories for them // If the line is empty, the loop is interrupted or the next iteration starts if((path=CheckCreateGetPath(s))=="") { BC }
Se você chegou a esta etapa, isso significa que os dados foram copiados com êxito e que a variável do caminho de string contém o caminho para a criação do arquivo para gravação de dados do símbolo atual do circuito.
Crie a função WriteDataToFile() para escrever os dados no arquivo. [Caminho]+[nome do arquivo] é gerado no início da função. Um nome de arquivo consiste no nome do símbolo e no período de tempo atual. Por exemplo, EURUSD_H1.csv. Se já existir um arquivo com o mesmo nome, ele apenas é aberto para inscrição. Os dados anteriormente escritos serão apagados. No lugar deles, novos dados serão escritos. Se o arquivo é criado/aberto com êxito, a função FileOpen() retorna o handle que será usado para acessar o arquivo.
Verificar o handle. Se ele estiver presente, a linha do cabeçalho está escrita. A linha apropriada será escrita de acordo com quais cabeçalhos tenham sido selecionados pelo usuário. O circuito principal de escrita dos dados históricos inicia subsequentemente.
Antes de escrever a próxima linha, ele deve ser convertido para o formato especificado pelo usuário. Para fazer isso, devemos receber o horário de abertura da barra e organizar dia, mês e ano separadamente por variáveis utilizando a função StringSubstr(). Em seguida, devemos definir se a data e a hora estarão localizadas em coluna única ou em colunas separadas dependendo do formato especificado pelo usuário. Então, todas as partes são agrupadas em uma linha com o uso da função StringConcatenate(). Após todas as linhas serem escritas, o arquivo é fechado pela função FileClose().
O código completo da função WriteDataToFile() é exibido abaixo:
//____________________________________________________________________ //+------------------------------------------------------------------+ //| WRITE_DATA_TO_FILE | //+------------------------------------------------------------------+ void WriteDataToFile(int s) { // Number of decimal places in the symbol price int dgt=(int)SymbolInfoInteger(symbols[s],SYMBOL_DIGITS); //--- string nm_fl=path+symbols[s]+"_"+gStrTF(_Period)+".csv"; // File name //--- // Receive file handle for writing hFl=FileOpen(nm_fl,FILE_WRITE|FILE_CSV|FILE_ANSI,','); //--- if(hFl>0) // If the handle is received { // Write the headers if(format_headers==NSDT_5) { FileWrite(hFl,"\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\""); } //--- if(format_headers==NSDT_6) { FileWrite(hFl,"Date","Open","High","Low","Close","Volume"); } //--- // Write the data for(int i=0; i<=copied_bars-1; i++) { if(IsStopped()) // If program execution interrupted by a user { DelAllScriptObjects(); // Delete objects created by the script from the chart //--- Print("------\nUser deleted the script!"); break; } //--- sdt=TSdm(rates[i].time); // Bar open time //--- // Divide the date by year, month and time yyyy=StringSubstr(sdt,0,4); mm=StringSubstr(sdt,5,2); dd=StringSubstr(sdt,8,2); tm=StringSubstr(sdt,11); //--- string sep_dt_tm=""; // Separator of Date and Time columns //--- // Join the data with the separator in the necessary order if(format_date==SEP_POINT1 || format_date==SEP_SLASH1) { sep_dt_tm=" "; } if(format_date==SEP_POINT2 || format_date==SEP_SLASH2) { sep_dt_tm=","; } //--- // Join everything in one line StringConcatenate(sdt,dd,sep,mm,sep,yyyy,sep_dt_tm,tm); //--- FileWrite(hFl, sdt,// Date-time DS_dgt(rates[i].open,dgt), // Open price DS_dgt(rates[i].high,dgt), // High price DS_dgt(rates[i].low,dgt), // Low price DS_dgt(rates[i].close,dgt), // Close price IS((int)rates[i].tick_volume)); // Tick volume price //--- // Update writing progress value for the current symbol pgs_pcnt=((double)(i+1)/copied_bars)*100; //--- // Update data in the table InfoTable(s); if(show_progress) { ChartRedraw(); } } //--- FileClose(hFl); // Close the file } else { Print("Error when creating/opening file!"); } }
Esta foi a última função do circuito básico da função OnStart(). Se este não fosse o último símbolo, tudo seria repetido para o símbolo seguinte. Caso contrário, o circuito é quebrado. Se o usuário especificou a limpeza da lista de símbolos na janela Market Watch nos parâmetros do script, os símbolos com gráficos atuais inativos serão apagados pela função DelSymbolsFromMarketWatch(). Em seguida, todos os objetos gráficos criados pelo script são apagados e o programa é interrompido. Os dados estão prontos para uso.
Os detalhes sobre como baixar dados para o NeuroShell DayTrader Professional podem ser encontrados no meu blog. O vídeo abaixo mostra a operação do script:
Conclusão
Independentemente do programa que utilizei para desenvolver estratégias de negociação, sempre encontrei alguma limitação que evitou um maior desenvolvimento das minhas ideias. Finalmente, percebi que neste caso, a programação é essencial. MQL5 é a melhor solução para aqueles que realmente desejam ter sucesso. Entretanto, outros programas para análise de dados e desenvolvimento de estratégias de negociação também podem ser úteis na busca de novas ideias. Eu teria levado muito mais tempo para encontrá-las se tivesse utilizado somente uma ferramenta.
Boa sorte!
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/502





- 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