English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Como preparar cotações do MetaTrader 5 para outros aplicativos

Como preparar cotações do MetaTrader 5 para outros aplicativos

MetaTrader 5Exemplos | 13 março 2014, 15:33
2 157 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo

Introdução
1. 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

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,Volume
Para 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

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. Alerta de valores incorretamente especificados

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

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

Arquivos anexados |
writedatatofile.mq5 (68.96 KB)
Comunicando-se com o MetaTrader 5 utilizando pipes nomeados sem DLLs Comunicando-se com o MetaTrader 5 utilizando pipes nomeados sem DLLs
Muitos desenvolvedores encontram o mesmo problema - como chegar ao sandbox do terminal sem utilizar DLLs arriscados. Um dos métodos mais fáceis e seguros é utilizar pipes nomeados padrão que funcionam como operações de arquivo normais. Eles permitem que você organize a comunicação cliente-servidor interprocessadores entre programas. Dê uma olhada em exemplos práticos em C++ e MQL5 que incluem servidor, cliente, a troca de dados entre eles e avaliação de desempenho.
Como comprar um robô negociação, no Mercado MetaTrader, e instalá-lo? Como comprar um robô negociação, no Mercado MetaTrader, e instalá-lo?
Cada produto disponível no Mercado MetaTrader pode ser comprado tanto através das plataformas de negociação MetaTrader 4 e MetaTrader 5, quanto diretamente no site MQL5.com. Selecione o produto que melhor se adapta à sua maneira de trabalhar, pague por ele conveniente e não se esqueça de ativá-lo.
Como escrever uma boa descrição para um produto do mercado Como escrever uma boa descrição para um produto do mercado
O MQL5 Market tem muitos produtos à venda, mas algumas de suas descrições deixam muito a desejar. Muitos textos obviamente precisam de melhoria, visto que negociadores comuns não são capazes de compreendê-los. Este artigo o ajuda a colocar seu produto em uma luz favorável. Use nossas recomendações para escrever uma descrição atrativa que facilmente mostrará aos clientes o que exatamente você está vendendo.
Redes neurais: Da teoria à prática Redes neurais: Da teoria à prática
Atualmente, todo negociador já deve ter ouvido falar sobre redes neurais e sabe como é interessante utilizá-las. A maioria acredita que as pessoas que sabem lidar com redes neurais são algum tipo de super-humano. Neste artigo, tentaremos explicar a arquitetura da rede neural, descrever as suas aplicações e apresentar exemplos de uso prático.