English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Criação e publicação de relatórios de negócios e notificação SMS

Criação e publicação de relatórios de negócios e notificação SMS

MetaTrader 5Exemplos | 2 janeiro 2014, 07:05
2 342 0
Denis Zyatkevich
Denis Zyatkevich

Introdução

Este artigo descreve como gerar um relatório de resultados de negócios (utilizando o Consultor Especialista, o indicador ou o script) como um arquivo HTML e carregá-lo por FTP para o servidor WWW. Também levaremos em consideração o envio de notificações de eventos de negócios por SMS para o celular.

Para estar mais à vontade com o material descrito neste artigo, é recomendado ao leitor estar familiarizado com HTML (HyperText Markup Language).

Para implementar os relatórios de carregamento precisamos de um servidor WWW (pode ser qualquer computador) que possa aceitar dados por FTP. Para implementar a possibilidade de recebimento de notificações sobre eventos de negócios por SMS, precisamos de um gateway E-MAIL-SMS (este serviço é fornecido pela maioria das operadoras de celular e empresas prestadoras de serviços).

1. Criando um relatório e o enviando por FTP

Criaremos um programa MQL5 que gera um relatório de negócios e o envia pelo protocolo FTP. Inicialmente o faremos como um script. No futuro podemos utilizá-lo como um bloco finalizado que pode ser inserido dentro dos Consultores Especialistas e dos Indicadores. Por exemplo, no Consultor Especialista você pode utilizar esse bloco como gerenciador de eventos Trade ou Timer para executar esse bloco após o pedido de negócio ou para determinar algumas ações para o evento ChartEvent. Nos indicadores você pode incluir esse bloco para os gerenciadores de evento Timer ou ChartEvent.

O exemplo de relatório, criado pelo programa, é exibido nas Figuras 1, 2 e 3. Ou você pode baixar este relatório pelo link localizado no fim do artigo.

Figura 1. Exemplo de relatório - tabela de negociações e posições.

Figura 1. Exemplo de relatório - tabela de negociações e posições

Figura 2. Exemplo de relatório - gráfico de saldo.

Figura 2. Exemplo de relatório - gráfico de saldo

Figura 3. Exemplo de relatório - gráfico de preço no instrumento atual

Figura 3. Exemplo de relatório - gráfico de preço no instrumento atual

Na tabela de negociações e posições (Figura 1), para comodidade, todas as negociações são divididas em posições. O lado esquerdo da tabela mostra o volume, horário e preço para entrar no mercado (de posições de abertura e acréscimos). O lado direito da tabela mostra os mesmos parâmetros para sair do mercado (fechamento parcial ou completo da posição). As entradas/saídas de negociações são divididas em duas partes - o fechamento de uma posição e a abertura da próxima.

Abaixo da tabela de negociações e posições é mostrado o gráfico de saldo (eixo horizontal - horário), e na parte inferior - o gráfico de preço do instrumento atual.

O programa cria os arquivos "report.html", "picture1.gif" e "picture2.gif" (arquivo html de relatório, arquivos de imagem do gráfico de saldo e do gráfico de preço) na pasta MetaTarder5_istall_dir\MQL5\Files. E a publicação em FTP é habilitada nas configurações de terminal - ela envia esses três arquivos para o servidor especificado. Além disso, precisaremos de três outros arquivos - imagens com setas apontando na direção da posição aberta - compra ou venda ("buy.gif" e "sell.gif"). Você pode pegar essas imagens (o link de download está no fim deste artigo) ou desenhá-las você mesmo em qualquer editor de gráficos. Estes dois arquivos devem ser colocados na mesma pasta do servidor WWW com o arquivo "report.html".

Como parâmetro de entrada, o programa aceita o horário de início e fim do período para o qual o relatório é gerado. Em nosso exemplo, o fim do período do relatório é o horário atual e o usuário seleciona a variante do período do relatório: período total, último dia, última semana, último mês ou último ano.

Algumas palavras sobre como o relatório é criado. O servidor de negócios é solicitado para todo o histórico de negociações. As negociações obtidas são processadas uma após outra. O arranjo deal_status[] armazena informações sobre se a negociação está processada ou não. Os índices de elemento deste arranjo são os números da negociação recebidos da lista do servidor de negócios das negociações. E os valores dos elementos são interpretados da seguinte forma: 0 - a negociação ainda não foi processada, 1 - a negociação já foi parcialmente processada (entrada/saída), 127 - a negociação já foi processada (outros valores não são utilizados sendo reservados para uso posterior).

O arranjo symb_list[] contêm a lista do nome dos instrumentos financeiros pelos qual a negociação foi conduzida, e o arranjo lots_list[] array - volumes de posições abertas para cada instrumento no momento do processamento da negociação. Valores positivos de volume correspondem a posições longas, negativos - a curtos. Se o volume for igual a zero, significa que esta ferramenta não tem posições abertas. Se, durante o processamento de negociações, um instrumento financeiro que não estiver na lista (no arranjo symb_list[]) for encontrado - ele é adicionado nele e o número de instrumentos financeiros (a variável symb_total) é aumentado em 1.

Em cada processamento de negociação cada negociação subsequente é analisada pelo mesmo instrumento financeiro até que a posição esteja fechada ou até a entrada/saída. Somente aquelas negociações são analisadas, para as quais o valor do arranjo deal_status[] for menor que 127. Após do processamento da negociação, o elemento correspondente do arranjo deal_status[] é designado como valor 127 e se a negociação estiver na entrada/saída da posição - com o valor 1. Se o horário em que a posição foi aberta se igualar ao período do relatório (definido pelas variáveis StartTime e EndTime) - esta posição é gravada ao relatório (todas as entradas e saídas).

Além da tabela de negociações, um novo gráfico para o instrumento financeiro atual é aberto. Para este gráfico, todas as propriedades necessárias são oferecidas, e usando a função ChartScreenShot() uma captura de tela é feita - de modo que tenhamos um arquivo de imagem com o gráfico de preço para o instrumento atual. A seguir, nesse gráfico o gráfico de preço é ocultado e as mudanças do gráfico de saldo são desenhadas e depois outra captura de tela é criada.

Quando dois arquivos de imagem com gráficos e arquivo HTML com relatório são criados, a capacidade de enviar arquivos por FTP é verificada. Se isto for permitido - os arquivos "report.html", "picture1.gif" e "picture2.gif" são enviados usando a função SendFTP(), de acordo com as configurações especificadas no MetaTrader 5.

Inicialize o MetaQuotes Language Editor e inicie a criação de um script. Defina as constantes - o tempo de espera de atualização do gráfico (em segundos), a largura e altura do gráfico de preço e a largura máxima do gráfico de saldo. O período do gráfico o qual irá exibir a curva de mudança do saldo é escolhido dependendo da duração do período do relatório e da largura máxima do gráfico. A largura do gráfico é ajustada ao tamanho necessário para o gráfico de saldo.

A altura do gráfico é automaticamente calculada como sendo metade da largura do mesmo. Além disso, especificaremos a largura do eixo vertical como constante - é o número de pixels pelo qual a área gráfica é diminuída comparada a largura da imagem devido ao eixo vertical.

#define timeout 10           // chart refresh timeout
#define Picture1_width 800   // max width of chart in report
#define Picture2_width 800   // width of price chart in report
#define Picture2_height 600  // height of price chart in report
#define Axis_Width 59        // width of vertical axis (in pixels)

Especifique que parâmetros de entrada serão solicitados ao usuário.

// request input parameters
#property script_show_inputs

Crie a enumeração dos períodos do relatório.

// enumeration of report periods
enum report_periods
  {
   All_periods,
   Last_day,
   Last_week,
   Last_month,
   Last_year
  };

Peça ao usuário o período do relatório (por padrão é o período total).

// ask for report period
input report_periods ReportPeriod=0;

Escreva o conjunto da função OnStart().

void OnStart()
  {

Determine o início e o fim do período do relatório.

  datetime StartTime=0;           // beginning of report period
  datetime EndTime=TimeCurrent(); // end of report period

  // calculating the beginning of report period
  switch(ReportPeriod)
    {
     case 1:
        StartTime=EndTime-86400;    // day
        break;
     case 2:
        StartTime=EndTime-604800;   // week
        break;
     case 3:
        StartTime=EndTime-2592000;  // month
        break;
     case 4:
        StartTime=EndTime-31536000; // year
        break;
    }
  // if none of the options is executed, then StartTime=0 (entire period)

Mostre as variáveis que serão utilizadas no programa. A finalidade das variáveis é descrito nos comentários.

   int total_deals_number;  // number of deals for history data
   int file_handle;         // file handle
   int i,j;                 // loop counters 
   int symb_total;          // number of instruments, that were traded
   int symb_pointer;        // pointer to current instrument
   char deal_status[];      // state of deal (processed/not processed)
   ulong ticket;            // ticket of deal
   long hChart;             // chart id

   double balance;           // current balance value
   double balance_prev;      // previous balance value
   double lot_current;       // volume of current deal
   double lots_list[];       // list of open volumes by instruments
   double current_swap;      // swap of current deal
   double current_profit;    // profit of current deal
   double max_val,min_val;   // maximal and minimal value
   
   string symb_list[];       // list of instruments, that were traded
   string in_table_volume;   // volume of entering position
   string in_table_time;     // time of entering position
   string in_table_price;    // price of entering position
   string out_table_volume;  // volume of exiting position
   string out_table_time;    // time of exiting position
   string out_table_price;   // price of exiting position
   string out_table_swap;    // swap of exiting position
   string out_table_profit;  // profit of exiting position

   bool symb_flag;           // flag that instrument is in the list

   datetime time_prev;           // previous value of time
   datetime time_curr;           // current value of time
   datetime position_StartTime;  // time of first enter to position
   datetime position_EndTime;    // time of last exit from position
   
   ENUM_TIMEFRAMES Picture1_period;  // period of balance chart

Abra um novo gráfico e defina suas propriedades - é o gráfico de preço que será colocado na parte inferior do relatório.

 // open a new chart and set its properties
hChart=ChartOpen(Symbol(),0);
ChartSetInteger(hChart,CHART_MODE,CHART_BARS);            // bars chart
ChartSetInteger(hChart,CHART_AUTOSCROLL,true);            // autoscroll enabled
ChartSetInteger(hChart,CHART_COLOR_BACKGROUND,White);     // white background
ChartSetInteger(hChart,CHART_COLOR_FOREGROUND,Black);     // axes and labels are black
ChartSetInteger(hChart,CHART_SHOW_OHLC,false);            // OHLC are not shown
ChartSetInteger(hChart,CHART_SHOW_BID_LINE,true);         // show BID line
ChartSetInteger(hChart,CHART_SHOW_ASK_LINE,false);        // hide ASK line
ChartSetInteger(hChart,CHART_SHOW_LAST_LINE,false);       // hide LAST line
ChartSetInteger(hChart,CHART_SHOW_GRID,true);             // show grid
ChartSetInteger(hChart,CHART_SHOW_PERIOD_SEP,true);       // show period separators
ChartSetInteger(hChart,CHART_COLOR_GRID,LightGray);       // grid is light-gray
ChartSetInteger(hChart,CHART_COLOR_CHART_LINE,Black);     // chart lines are black
ChartSetInteger(hChart,CHART_COLOR_CHART_UP,Black);       // up bars are black
ChartSetInteger(hChart,CHART_COLOR_CHART_DOWN,Black);     // down bars are black
ChartSetInteger(hChart,CHART_COLOR_BID,Gray);             // BID line is gray
ChartSetInteger(hChart,CHART_COLOR_VOLUME,Green);         // volumes and orders levels are green
ChartSetInteger(hChart,CHART_COLOR_STOP_LEVEL,Red);       // SL and TP levels are red
ChartSetString(hChart,CHART_COMMENT,ChartSymbol(hChart)); // comment contains instrument <end segm

Capture a imagem de um gráfico e salve o como "picture2.gif".

// save chart as image file
ChartScreenShot(hChart,"picture2.gif",Picture2_width,Picture2_height);

Solicite o histórico de negociações para o período total de existência da conta.

// request deals history for entire period
HistorySelect(0,TimeCurrent());

Abra o arquivo "report.html" dentro do qual escreveremos a página HTML com o relatório (codificação ANSI).

// open report file
file_handle=FileOpen("report.html",FILE_WRITE|FILE_ANSI);

Escreva a parte inicial do documento HTML:

  • Inicie o documento HTML (<html>)
  • Título que será mostrado no topo de sua janela de navegador (<head><title>Expert Trade Report</title></head>)
  • Início da parte principal do documento HTML com a cor da tela de fundo (<body bgcolor='#EFEFEF'>)
  • Alinhamento do centro (<center>)
  • Título das negociações e tabela de posições (<h2>Trade Report</h2>)
  • Início das negociações e tabela de posições com alinhamento, largura de margem, cor da tela de fundo, cor da margem, espaçamento de célula e preenchimento de célula (<table align='center' border='1' bgcolor='#FFFFFF' bordercolor='#7F7FFF' cellspacing='0' cellpadding='0'>)
  • Título da tabela.
// write the beginning of HTML
   FileWrite(file_handle,"<html>"+
                           "<head>"+
                              "<title>Expert Trade Report</title>"+
                           "</head>"+
                              "<body bgcolor='#EFEFEF'>"+
                              "<center>"+
                              "<h2>Trade Report</h2>"+
                              "<table align='center' border='1' bgcolor='#FFFFFF' bordercolor='#7F7FFF' cellspacing='0' cellpadding='0'>"+
                                 "<tr>"+
                                    "<th rowspan=2>SYMBOL</th>"+
                                    "<th rowspan=2>Direction</th>"+
                                    "<th colspan=3>Open</th>"+
                                    "<th colspan=3>Close</th>"+
                                    "<th rowspan=2>Swap</th>"+
                                    "<th rowspan=2>Profit</th>"+
                                 "</tr>"+
                                 "<tr>"+
                                    "<th>Volume</th>"+
                                    "<th>Time</th>"+
                                    "<th>Price</th>"+
                                    "<th>Volume</th>"+
                                    "<th>Time</th>"+
                                    "<th>Price</th>"+
                                 "</tr>");

Obter o número de negociações na lista.

// number of deals in history
total_deals_number=HistoryDealsTotal();

Configurar as dimensões para os arranjos symb_list[], lots_list[] e deal_status[].

// setting dimensions for the instruments list, the volumes list and the deals state arrays
ArrayResize(symb_list,total_deals_number);
ArrayResize(lots_list,total_deals_number);
ArrayResize(deal_status,total_deals_number);

Inicializar todos os elementos do arranjo deal_status[] com o valor 0 - todas as negociações não estão processadas.

// setting all elements of array with value 0 - deals are not processed
ArrayInitialize(deal_status,0);

Configurar os valores iniciais de saldo e variável, utilizados para armazenar o valor anterior do saldo.

balance=0;       // initial balance
balance_prev=0;  // previous balance

Configurar o valor inicial da variável utilizada para armazenar o número de instrumentos financeiros na lista.

// number of instruments in the list
symb_total=0;

Criar um ciclo que processe sequencialmente cada negociação da lista.

// processing all deals in history
for(i=0;i<total_deals_number;i++)
  {

Selecione a negociação atual e obtenha seu tick.

//select deal, get ticket
ticket=HistoryDealGetTicket(i);

Alterar o saldo pela quantidade de lucro na negociação atual.

// changing balance
balance+=HistoryDealGetDouble(ticket,DEAL_PROFIT);

Obter o horário da negociação - será utilizado frequentemente mais adiante.

// reading the time of deal
time_curr=HistoryDealGetInteger(ticket,DEAL_TIME);

Se for a primeira negociação na lista - precisamos ajustar os limites do período do relatório e selecionar o período para o gráfico de saldo, dependendo da duração do período do relatório e da largura da região na qual o gráfico estará plotado. Configurar os valores iniciais dos saldos máximos e mínimos (estas variáveis serão usadas para configurar o máximo e o mínimo no gráfico).

// if this is the first deal
if(i==0)
  {
   // if the report period starts before the first deal,
   // then the report period will start from the first deal
   if(StartTime<time_curr) StartTime=time_curr;
   // if report period ends before the current time,
   // then the end of report period corresponds to the current time
   if(EndTime>TimeCurrent()) EndTime=TimeCurrent();
   // initial values of maximal and minimal balances
   // are equal to the current balance
   max_val=balance;
   min_val=balance;
   // calculating the period of balance chart depending on the duration of
   // report period
   Picture1_period=PERIOD_M1;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)) Picture1_period=PERIOD_M2;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*120) Picture1_period=PERIOD_M3;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*180) Picture1_period=PERIOD_M4;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*240) Picture1_period=PERIOD_M5;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*300) Picture1_period=PERIOD_M6;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*360) Picture1_period=PERIOD_M10;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*600) Picture1_period=PERIOD_M12;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*720) Picture1_period=PERIOD_M15;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*900) Picture1_period=PERIOD_M20;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*1200) Picture1_period=PERIOD_M30;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*1800) Picture1_period=PERIOD_H1;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*3600) Picture1_period=PERIOD_H2;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*7200) Picture1_period=PERIOD_H3;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*10800) Picture1_period=PERIOD_H4;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*14400) Picture1_period=PERIOD_H6;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*21600) Picture1_period=PERIOD_H8;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*28800) Picture1_period=PERIOD_H12;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*43200) Picture1_period=PERIOD_D1;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*86400) Picture1_period=PERIOD_W1;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*604800) Picture1_period=PERIOD_MN1;
   // changing the period of opened chart
   ChartSetSymbolPeriod(hChart,Symbol(),Picture1_period);
  }

Se a negociação não for a primeira - crie o objeto "linha" usando qual mudança no gráfico de saldo foi plotada. A linha é plotada somente se pelo menos uma tiver seu final no período do relatório. Se as duas extremidades estiverem no período do relatório - a linha será "grossa". A cor da linha de saldo é verde. Se o saldo estiver além do intervalo de saldo mínimo e máximo - este intervalo é ajustado.

else
  // if this is not the first deal
  {
   // plotting the balance line, if the deal is in the report period,
   // and setting properties of the balance line
   if(time_curr>=StartTime && time_prev<=EndTime)
     {
      ObjectCreate(hChart,IntegerToString(i),OBJ_TREND,0,time_prev,balance_prev,time_curr,balance);
      ObjectSetInteger(hChart,IntegerToString(i),OBJPROP_COLOR,Green);
      // if both ends of line are in the report period,
      // it will be "thick"
      if(time_prev>=StartTime && time_curr<=EndTime)
        ObjectSetInteger(hChart,IntegerToString(i),OBJPROP_WIDTH,2);
     }
   // if new value of balance exceeds the range
   // of minimal and maximal values, it must be adjusted
   if(balance<min_val) min_val=balance;
   if(balance>max_val) max_val=balance;
  }

Atribua o valor anterior de tempo à variável correspondente.

// changing the previous time value
time_prev=time_curr;

Se a negociação ainda não foi processada - processe-a.

// if the deal has not been processed yet
if(deal_status[i]<127)
  {

Se esta negociação for de saldo em amortização e estiver no período do relatório - a cadeia correspondente é escrita no relatório. A negociação é marcada como processada.

// If this deal is balance charge
if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BALANCE)
  {
   // if it's in the report period - write the corresponding string to report.
   if(time_curr>=StartTime && time_curr<=EndTime)
     FileWrite(file_handle,"<tr><td colspan='9'>Balance:</td><td align='right'>",HistoryDealGetDouble(ticket,DEAL_PROFIT),
     "</td></tr>");
   // mark deal as processed
   deal_status[i]=127;
  }

Se esta negociação for de compra ou venda, verifique se este instrumento está na lista (arranjo symb_list[]). Se não estiver - coloque o na lista. A variável symb_pointer indica o elemento do arranjo symb_list[] o qual contém o nome do instrumento da negociação atual.

// if this deal is buy or sell
if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY || HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL)
  {
   // check if there is instrument of this deal in the list
   symb_flag=false;
   for(j=0;j<symb_total;j++)
     {
      if(symb_list[j]==HistoryDealGetString(ticket,DEAL_SYMBOL))
        {
         symb_flag=true;
         symb_pointer=j;
        }
     }
   // if there is no instrument of this deal in the list
   if(symb_flag==false)
     {
      symb_list[symb_total]=HistoryDealGetString(ticket,DEAL_SYMBOL);
      lots_list[symb_total]=0;
      symb_pointer=symb_total;
      symb_total++;
     }

Configure os valores iniciais das variáveis position_StartTime e position_EndTime que armazenam o tempo de vida da posição inicial e final.

// set the initial value for the beginning time of deal
position_StartTime=time_curr;
// set the initial value for the end time of deal
position_EndTime=time_curr;

As variáveis in_table_volume, in_table_time, in_table_price, out_table_volume, out_table_time, out_table_price, out_table_swap e out_table_profit irão armazenar tabelas que estarão dentro das células da tabela maior: o volume, horário e preço de entrada no mercado; volume, horário, preço, swap e lucro de saída do mercado. A variável in_table_volume também armazenará o nome do instrumento financeiro e vincular a uma imagem que corresponde a direção da posição aberta. Atribua a todas estas variáveis valores iniciais.

// creating the string in report - instrument, position direction, beginning of table for volumes to enter the market
if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY)
   StringConcatenate(in_table_volume,"<tr><td align='left'>",symb_list[symb_pointer],
   "</td><td align='center'><img src='buy.gif'></td><td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>");

if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL)
   StringConcatenate(in_table_volume,"<tr><td align='left'>",symb_list[symb_pointer],
   "</td><td align='center'><img src='sell.gif'></td><td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>");
// creating the beginning of time table to enter the market
in_table_time="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of price table to enter the market
in_table_price="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of volume table to exit the market
out_table_volume="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of time table to exit the market
out_table_time="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of price table to exit the market
out_table_price="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of swap table to exit the market
out_table_swap="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of profit table to exit the market
out_table_profit="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";

Processe todas as negociações iniciando com a atual até que a posição esteja encerrada. Processe todas elas se as mesmas não foram processadas antes.

// process all deals for this position starting with the current(until position is closed)
for(j=i;j<total_deals_number;j++)
  {
   // if the deal has not been processed yet - process it
   if(deal_status[j]<127)
     {

Selecione a negociação e obtenha seu tick.

// select deal, get ticket
ticket=HistoryDealGetTicket(j);

Se a negociação estiver no mesmo instrumento que a posição aberta - processe-a. Obtenha o horário da negociação. Se o horário da negociação for além do intervalo de horário - estenda o intervalo. Obtenha o volume da negociação.

// if the instrument of deal matches the instrument of position, that is processed
if(symb_list[symb_pointer]==HistoryDealGetString(ticket,DEAL_SYMBOL))
  {
   // get the deal time
   time_curr=HistoryDealGetInteger(ticket,DEAL_TIME);
   // If the deal time goes beyond the range of position time
   // - extend position time
   if(time_curr<position_StartTime) position_StartTime=time_curr;
   if(time_curr>position_EndTime) position_EndTime=time_curr;
   // get the volume of deal
   lot_current=HistoryDealGetDouble(ticket,DEAL_VOLUME);

Negociações de compra e venda são processadas separadamente. Comece com as negociações de compra.

// if this deal is buy
if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY)
  {

Se você já tiver aberto a posição para venda - esta negociação para compra sairá do mercado. E se o volume da negociação será maior do que o volume da posição a descoberto - este será a entrada/saída. Atribua variáveis de cadeia com os valores desejados. Atribua o arranjo deal_status[] com o valor 127, se a negociação estiver completamente processada ou com o valor 1 se for a entrada/saída e esta negociação tiver que ser analisada para outra posição.

// if position is opened for sell - this will be exit from market
if(NormalizeDouble(lots_list[symb_pointer],2)<0)
  {
   // if buy volume is greater than volume of opened short position - then this is in/out
   if(NormalizeDouble(lot_current+lots_list[symb_pointer],2)>0)
     {
      // creating table of volumes to exit the market - indicate only volume of opened short position
      StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(-lots_list[symb_pointer],2),"</td></tr>");
      // mark position as partially processed
      deal_status[j]=1;
     }
   else
     {
      // if buy volume is equal or less than volume of opened short position - then this is partial or full close
      // creating the volume table to exit the market
      StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>");
      // mark deal as processed
      deal_status[j]=127;
     }

   // creating the time table to exit the market
   StringConcatenate(out_table_time,out_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>");

   // creating the price table to exit the market
   StringConcatenate(out_table_price,out_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
   (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>");

   // get the swap of current deal
   current_swap=HistoryDealGetDouble(ticket,DEAL_SWAP);

   // if swap is equal to zero - create empty string of the swap table to exit the market
   if(NormalizeDouble(current_swap,2)==0) StringConcatenate(out_table_swap,out_table_swap,"<tr></tr>");
   // else create the swap string in the swap table to exit the market
   else StringConcatenate(out_table_swap,out_table_swap,"<tr><td align='right'>",DoubleToString(current_swap,2),"</td></tr>");

   // get the profit of current deal
   current_profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);

   // if profit is negative (loss) - it is displayed as red in the profit table to exit the market
   if(NormalizeDouble(current_profit,2)<0) StringConcatenate(out_table_profit,out_table_profit,"<tr><td align=right><SPAN style='COLOR: #EF0000'>",
   DoubleToString(current_profit,2),"</SPAN></td></tr>");
   // else - it is displayed as green
   else StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'><SPAN style='COLOR: #00EF00'>",
        DoubleToString(current_profit,2),"</SPAN></td></tr>");
  }

Se você já tiver aberto a posição de alta - a compra nesta negociação será a entrada no mercado (o primeiro ou o acréscimo). Se o elemento do arranjo deal_status[] que corresponde a esta negociação tiver o valor 1 - significa que a entrada/saída foi feita. Atribua variáveis de cadeia com os valores desejados e marque a negociação como processada (atribua o elemento correspondente do arranjo deal_status[] com o valor 127).

else
   // if position is opened for buy - this will be the enter to the market
   {
    // if this deal has been already partially processed (in/out)
    if(deal_status[j]==1)
      {
       // create the volume table of entering the market (volume, formed after in/out, is put here)
       StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lots_list[symb_pointer],2),"</td></tr>");
       // indemnity of volume change, which will be produced (the volume of this deal is already taken into account)
       lots_list[symb_pointer]-=lot_current;
      }
    // if this deal has not been processed yet, create the volume table to enter the market
    else StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>");

    // creating the time table of entering the market
    StringConcatenate(in_table_time,in_table_time,"<tr><td align center>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>");

    // creating the price table of entering the market
    StringConcatenate(in_table_price,in_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
    (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>");

    // mark deal as processed
    deal_status[j]=127;
   }

Altere o volume da posição para o volume da negociação atual. Se a posição estiver fechada (o volume for igual a zero) - pare de processar esta posição (saia do ciclo que contém a variável j) e procure pela próxima negociação não processada (no ciclo que contém a variável i).

 // change of position volume by the current instrument, taking into account the volume of current deal
 lots_list[symb_pointer]+=lot_current;
 // if the volume of opened position by the current instrument became equal to zero - position is closed
 if(NormalizeDouble(lots_list[symb_pointer],2)==0 || deal_status[j]==1) break;
}

As negociações de venda são processadas de modo semelhante e depois saímos do ciclo que contém a variável j.

       // if this deal is sell
       if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL)
         {
          // if position has been already opened for buy - this will be the exit from market
          if(NormalizeDouble(lots_list[symb_pointer],2)>0)
            {
             // if sell volume is greater than volume of opened long position - then this is in/out
             if(NormalizeDouble(lot_current-lots_list[symb_pointer],2)>0)
               {
                // creating table of volumes to exit the market - indicate only volume of opened long position
                StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lots_list[symb_pointer],2),"</td></tr>");
                // mark position as partially processed
                deal_status[j]=1;
               }
             else
               {
                // if sell volume is equal or greater than volume of opened short position - then this is partial or full close
                // creating the volume table to exit the market
                StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>");
                // mark deal as processed
                deal_status[j]=127;
               }

             // creating the time table to exit the market
             StringConcatenate(out_table_time,out_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>");

             // creating the price table to exit the market
             StringConcatenate(out_table_price,out_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
             (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>");

             // get the swap of current deal
             current_swap=HistoryDealGetDouble(ticket,DEAL_SWAP);

             // if swap is equal to zero - create empty string of the swap table to exit the market
             if(NormalizeDouble(current_swap,2)==0) StringConcatenate(out_table_swap,out_table_swap,"<tr></tr>");
             // else create the swap string in the swap table to exit the market
             else StringConcatenate(out_table_swap,out_table_swap,"<tr><td align='right'>",DoubleToString(current_swap,2),"</td></tr>");

             // get the profit of current deal
             current_profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);

             // if profit is negative (loss) - it is displayed as red in the profit table to exit the market
             if(NormalizeDouble(current_profit,2)<0) StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'>
             <SPAN style='COLOR: #EF0000'>",DoubleToString(current_profit,2),"</SPAN></td></tr>");
             // else - it is displayed as green
             else StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'><SPAN style='COLOR: #00EF00'>",
                  DoubleToString(current_profit,2),"</SPAN></td></tr>");
            }
          else
            // if position is opened for sell - this will be the enter to the market
            {
             // if this deal has been already partially processed (in/out)
             if(deal_status[j]==1)
               {
                // create the volume table of entering the market (volume, formed after in/out, is put here)
                StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(-lots_list[symb_pointer],2),"</td></tr>");

                // indemnity of volume change, which will be produced (the volume of this deal is already taken into account)
                lots_list[symb_pointer]+=lot_current;
               }
             // if this deal has not been processed yet, create the volume table to enter the market
             else StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>");

             // creating the time table of entering the market
             StringConcatenate(in_table_time,in_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>");

             // creating the price table of entering the market
             StringConcatenate(in_table_price,in_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
             (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>");

             // mark deal as processed
             deal_status[j]=127;
            }
          // change of position volume by the current instrument, taking into account the volume of current deal
          lots_list[symb_pointer]-=lot_current;
          // if the volume of opened position by the current instrument became equal to zero - position is closed
          if(NormalizeDouble(lots_list[symb_pointer],2)==0 || deal_status[j]==1) break;
         }
      }
   }
}

Se o horário em que a posição foi aberta estiver no período do relatório (pelo menos parcialmente) - o registro correspondente é destinado ao arquivo "report.html".

// if the position period is in the the report period - the position is printed to report
if(position_EndTime>=StartTime && position_StartTime<=EndTime) FileWrite(file_handle,
in_table_volume,"</table></td>",
in_table_time,"</table></td>",
in_table_price,"</table></td>",
out_table_volume,"</table></td>",
out_table_time,"</table></td>",
out_table_price,"</table></td>",
out_table_swap,"</table></td>",
out_table_profit,"</table></td></tr>");

Atribua a variável balance_prev o valor do saldo. Saia do ciclo que contém a variável i.

   }
 // changing balance
 balance_prev=balance;
}

Escreva o fim do arquivo HTML (contém links para as imagens, o final do alinhamento do centro, o final da parte principal, o documento HTML do final). Feche o arquivo "report.html".

// create the end of html-file
FileWrite(file_handle,"

Balance Chart

<img src=
'picture1.gif'>

Price Chart

<img src=
'picture2.gif'>"); // close file FileClose(file_handle);

Não aguardar pela atualização do gráfico mais tempo do que o especificado na constante do tempo de espera.

// get current time
time_curr=TimeCurrent();
// waiting for chart update
while(SeriesInfoInteger(Symbol(),Picture1_period,SERIES_BARS_COUNT)==0 && TimeCurrent()-time_curr<timeout) Sleep(1000);

Configurar o máximo e mínimo estabelecido no gráfico.

// setting maximal and minimal values for the balance chart (10% indent from upper and lower boundaries)
ChartSetDouble(hChart,CHART_FIXED_MAX,max_val+(max_val-min_val)/10);
ChartSetDouble(hChart,CHART_FIXED_MIN,min_val-(max_val-min_val)/10);

Configurar as propriedades do gráfico de saldo.

// setting properties of the balance chart
ChartSetInteger(hChart,CHART_MODE,CHART_LINE);                // chart as line
ChartSetInteger(hChart,CHART_FOREGROUND,false);               // chart on foreground
ChartSetInteger(hChart,CHART_SHOW_BID_LINE,false);            // hide BID line
ChartSetInteger(hChart,CHART_COLOR_VOLUME,White);             // volumes and orders levels are white
ChartSetInteger(hChart,CHART_COLOR_STOP_LEVEL,White);         // SL and TP levels are white
ChartSetInteger(hChart,CHART_SHOW_GRID,true);                 // show grid
ChartSetInteger(hChart,CHART_COLOR_GRID,LightGray);           // grid is light-gray
ChartSetInteger(hChart,CHART_SHOW_PERIOD_SEP,false);          // hide period separators
ChartSetInteger(hChart,CHART_SHOW_VOLUMES,CHART_VOLUME_HIDE); // hide volumes
ChartSetInteger(hChart,CHART_COLOR_CHART_LINE,White);         // chart is white
ChartSetInteger(hChart,CHART_SCALE,0);                        // minimal scale
ChartSetInteger(hChart,CHART_SCALEFIX,true);                  // fixed scale on vertical axis
ChartSetInteger(hChart,CHART_SHIFT,false);                    // no chart shift
ChartSetInteger(hChart,CHART_AUTOSCROLL,true);                // autoscroll enabled
ChartSetString(hChart,CHART_COMMENT,"BALANCE");               // comment on chart

Redesenhar o gráfico de saldo.

// redraw the balance chart
ChartRedraw(hChart);
Sleep(8000);

Capture a imagem do gráfico (salve a imagem "picture1.gif"). A largura do gráfico se ajusta à largura do período do relatório (mas devido aos feriados as imprecisões ocorrem com frequência e o gráfico torna-se mais largo que a curva de mudança do saldo), a altura é calculada como sendo metade da largura.

// screen shooting the balance chart
ChartScreenShot(hChart,"picture1.gif",(int)(EndTime-StartTime)/PeriodSeconds(Picture1_period),
(int)(EndTime-StartTime)/PeriodSeconds(Picture1_period)/2,ALIGN_RIGHT);

Apague todos os objetos do gráfico e feche-o.

// delete all objects from the balance chart
ObjectsDeleteAll(hChart);
// close chart
ChartClose(hChart);

Se o envio de arquivos por FTP for permitido, envie três arquivos: "report.html", picture1.gif " "e" " picture2.gif ".

// if report publication is enabled - send via FTP
// HTML-file and two images - price chart and balance chart
if(TerminalInfoInteger(TERMINAL_FTP_ENABLED))
   {
    SendFTP("report.html");
    SendFTP("picture1.gif");
    SendFTP("picture2.gif");
   }
}

Por enquanto, a descrição do programa está completa. Para enviar arquivos por FTP, você deve ajustar as configurações do MetaTrader 5 - vá no menu Ferramentas, depois em Opções e abra a aba Editor (Figura 4).

Figura 4. Opções do relatório de publicação por FTP.

Figura 4. Opções do relatório de publicação por FTP

Na caixa de diálogo Opções você precisa marcar a opção "Permitir", especificar o número da conta, o endereço FTP, o caminho, o login e a senha para acesso. A periodicidade da atualização não importa.

Agora você pode executar o script. Após executar, o gráfico de saldo aparece na tela por alguns segundos e então desaparece. No Diário você pode encontrar possíveis erros e ver se os arquivos foram enviados por FTP. Se tudo funcionar bem, então três novos arquivos aparecerão no servidor na pasta especificada. Se você colocar lá dois arquivos com imagens de seta e se o servidor WWW estiver configurado e funcionando - você pode abrir um relatório pelo navegador web.

2. Enviando notificações por SMS para o celular

Existem momentos em que você está distante do seu computador e outros aparelhos eletrônicos e você tem somente um celular em mãos. Mas você deseja controlar os negócios em sua conta ou acompanhar cotações para um instrumento financeiro. Nesse caso, você pode ajustar o envio de notificações por SMS para o celular. Muitas operadoras de celular (e empresas prestadoras de serviço) oferecem serviço de EMAIL-SMS que permite a você receber mensagens como cartas, enviadas a um endereço de e-mail específico.

Para isso você deve possuir uma caixa de e-mail (particularmente, você deve saber seu servidor SMTP). Ajuste as configurações de seu MetaTrader 5 - vá ao menu Ferramentas, depois em Opções e abra a aba Email (Figura 5).

Figura 5. Estabelecimento do envio de notificações por email

Figura 5. Estabelecimento do envio de notificações por e-mail

Marque a opção "Permitir", especifique o endereço do servidor SMTP, o login e a senha , o endereço do remetente (seu e-mail) e endereço do destinatário - o endereço de e-mail utilizado para enviar mensagens SMS (verifique com sua operadora de celular). Se tudo estiver correto, então quando você clicar no botão "Testar" uma mensagem de verificação será enviada (veja informações adicionais no Diário).

A maneira mais fácil de ser notificado quando o preço atingir um certo patamar - é criar um alerta. Para fazer isso, abra a aba da "Caixa de Ferramentas" adequada, clique direito e escolha "Criar" (Figura 6).

Figura 6. Criação de alerta.

Figura 6. Criação de alerta.

Nesta janela, marque a opção "Permitir", escolha a ação "Email", selecione o instrumento financeiro, a condição, insira o valor para a condição e escreva o texto da mensagem. Em "Limite de repetições" insira 1 se você não quiser que a mensagem chegue várias vezes. Quando todos os campos estiverem preenchidos, clique em OK.

Se enviarmos uma mensagem pelo programa MQL5, teremos mais possibilidades. Usaremos a função SendMail(). Ela tem dois parâmetros. Primeiro - o título, segundo - o corpo da mensagem.

Você pode acionar a função SendMail() depois do pedido de negócio (função OrderSend()) ou no gerenciador de evento Trade. Dessa forma teremos notificações de eventos de negócios - entrada no mercado, realização de pedidos, encerramento de posições. Ou você pode colocar o SendMail() dentro da função OnTimer() - receberemos notificações periódicas sobre cotações atuais. Você pode organizar o envio de notificações para quando certos sinais de negócios aparecerem - quando as linhas do indicador se cruzam, quando o preço atinge algumas linhas e patamares, etc.

Vamos levar em consideração alguns exemplos.

Se no Consultor Especialista ou no Script você substituir

OrderSend(request,result};

com o seguinte:

string msg_subj,msg_text;
if(OrderSend(request,result))
  {
   switch(request.action)
     {
      case TRADE_ACTION_DEAL:
         switch(request.type)
           {
            case ORDER_TYPE_BUY:
               StringConcatenate(msg_text,"Buy ",result.volume," ",request.symbol," at price ",result.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_SELL:
               StringConcatenate(msg_text,"Sell ",result.volume," ",request.symbol," at price ",result.price,", SL=",request.sl,", TP=",request.tp);
               break;
           }
         break;
      case TRADE_ACTION_PENDING:
         switch(request.type)
           {
            case ORDER_TYPE_BUY_LIMIT:
               StringConcatenate(msg_text,"Set BuyLimit ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_SELL_LIMIT:
               StringConcatenate(msg_text,"Set SellLimit ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_BUY_STOP:
               StringConcatenate(msg_text,"Set BuyStop ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_SELL_STOP:
               StringConcatenate(msg_text,"Set SellStop ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_BUY_STOP_LIMIT:
               StringConcatenate(msg_text,"Set BuyStopLimit ",result.volume," ",request.symbol," at price ",request.price,", stoplimit=",request.stoplimit,
               ", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_SELL_STOP_LIMIT:
               StringConcatenate(msg_text,"Set SellStop ",result.volume," ",request.symbol," at price ",request.price,", stoplimit=",request.stoplimit,
               ", SL=",request.sl,", TP=",request.tp);
               break;
           }
         break;
       case TRADE_ACTION_SLTP:
          StringConcatenate(msg_text,"Modify SL&TP. SL=",request.sl,", TP=",request.tp);
          break;
       case TRADE_ACTION_MODIFY:
          StringConcatenate(msg_text,"Modify Order",result.price,", SL=",request.sl,", TP=",request.tp);
          break;
       case TRADE_ACTION_REMOVE:
          msg_text="Delete Order";
          break;
     }
  }
  else msg_text="Error!";
StringConcatenate(msg_subj,AccountInfoInteger(ACCOUNT_LOGIN),"-",AccountInfoString(ACCOUNT_COMPANY));
SendMail(msg_subj,msg_text);

então depois do pedido de negócio a função OrderSend() enviará uma mensagem utilizando a função SendMail(). Isto incluirá informações sobre o número da conta, nome de um corretor e as ações realizadas (compra, venda, pedido pendente a ser feito, modificações ou anulação do pedido), como a seguir:

59181-MetaQuotes Software Corp. Buy 0.1 EURUSD at price 1.23809, SL=1.2345, TP=1.2415

E se em qualquer Consultor Especialista ou Indicador dentro do conjunto do OnInit() você iniciará o temporizador utilizando a função EventSetTimer() (ela tem apenas um parâmetro - o período de tempo em segundos):

void OnInit()
  {
   EventSetTimer(3600);
  }

No OnDeinit() não se esqueça de desligá-lo utilizando o EventKillTimer():

void OnDeinit(const int reason)
  {
   EventKillTimer();
  }

E no OnTimer() de enviar mensagens usando o SendMail():

void OnTimer()
  {
   SendMail(Symbol(),DoubleToString(SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits));
  }

Então você receberá mensagens sobre o preço do instrumento financeiro atual com o período especificado.

Conclusão

Este artigo descreve com usar o programa MQL5 para criar um HTML e arquivos de imagem e como carregá-los para um servidor WWW por FTP. Também descreve como configurar o envio de notificações para o seu celular por SMS.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/61

Arquivos anexados |
sendreport_en.mq5 (32.06 KB)
report.zip (33.08 KB)
Criando Painéis de Controle Ativo no MQL5 para Negociação Criando Painéis de Controle Ativo no MQL5 para Negociação
O artigo cobre o problema do desenvolvimento de painéis de controle ativo no MQL5. Os elementos de interface são gerenciados pelo mecanismo de manipulação de evento. Além disso, a opção de uma configuração flexível das propriedades dos elementos de controle está disponível. O painel de controle ativo permite o trabalho com posições, bem como a configuração, modificação e exclusão de ordens pendentes e mercado.
Um exemplo de uma estratégia de negociação baseada na diferença de fuso horário em diferentes continentes Um exemplo de uma estratégia de negociação baseada na diferença de fuso horário em diferentes continentes
Navegando na internet é fácil encontrar muitas estratégias que darão a você uma série de recomendações. Vamos pegar uma abordagem interna e ver o processo de criação de estratégia, com base nas diferenças de fusos horários em diferentes continentes.
Conexão do Expert Advisor com ICQ no MQL5 Conexão do Expert Advisor com ICQ no MQL5
Este artigo descreve o método de troca de informação entre o Expert Advisor e os usuários do ICQ, são apresentados vários exemplos. O material fornecido será interessante para aqueles que queiram receber informações de negócio remotamente de um terminal de cliente através de um cliente ICQ em seus celulares ou PDA.
Algoritmos genéticos - é fácil! Algoritmos genéticos - é fácil!
Neste artigo o autor fala sobre cálculos evolutivos com o uso de um algoritmo genético desenvolvido pessoalmente. Ele demonstra o funcionamento do algoritmo, usando exemplos e fornece recomendações práticas para seu uso.