Discussão do artigo "Avaliação visual e ajuste da negociação no MetaTrader 5"

 

Novo artigo Avaliação visual e ajuste da negociação no MetaTrader 5 foi publicado:

No testador de estratégias, é possível não apenas otimizar os parâmetros do robô de negociação. Vamos mostrar como avaliar, após o fato, o histórico de negociação de sua conta e fazer ajustes na negociação dentro do testador, alterando os tamanhos dos stop orders das posições abertas.

Imagine a seguinte situação: em uma conta específica, há uma negociação relativamente ativa sendo realizada há bastante tempo, com diferentes instrumentos, por vários EAs e até mesmo manualmente. E agora, passado algum tempo, queremos ver os resultados desse trabalho. Claro que é possível conferir os relatórios padrão de negociação no terminal pressionando Alt+E. Também dá para carregar os ícones das negociações no gráfico e visualizar as aberturas e fechamentos das posições. Mas e se quisermos ver de forma dinâmica como foi realizada a negociação, onde e como as posições foram abertas e fechadas? Ver separadamente por cada símbolo, ou todos de uma vez, os momentos de abertura e fechamento das posições, em quais níveis os stop orders foram colocados e se seu tamanho foi justificado. E se depois nos perguntarmos "o que teria acontecido se..." (e aqui cabem várias opções: outros stops, com algoritmos e critérios diferentes, uso de trailing nas posições, ou mover os stops para o ponto de equilíbrio etc.); e depois ainda testar todos esses "e se" com resultados visíveis e claros. Como a negociação poderia ter mudado se...

Para resolver esse tipo de questão, na verdade, já temos tudo o que precisamos. Basta carregar o histórico da conta em um arquivo (todas as negociações realizadas) e, em seguida, executar um EA no testador de estratégias que leia essas negociações do arquivo e abra/feche posições no testador do terminal cliente. Com um EA assim, podemos adicionar código para alterar as condições de saída das posições e comparar como a negociação teria mudado, e o que teria acontecido se...

E o que isso nos oferece? Mais uma ferramenta para buscar melhores resultados, para fazer ajustes na negociação que já estava sendo feita na conta; o teste visual permite ver em tempo real se as posições foram abertas corretamente em determinado instrumento, se foram fechadas no momento certo, e assim por diante. E o mais importante: podemos simplesmente adicionar um novo algoritmo no código do EA, testar, obter um resultado e aplicar as correções nos EAs que estão operando nessa conta.


Autor: Artyom Trishkin

 

Tenho uma abordagem um pouco diferente. Como cada robô grava um registro de suas negociações, basta criar um script. Quando o executo em um gráfico de símbolos, ele solicita o número do robô (mágico) e, a partir do arquivo com esse símbolo e esse número no nome, ele carrega as negociações no gráfico - todas ou apenas longas/curtas (pode ser útil separá-las). Essa abordagem permite trabalhar com compensação, o que é de fundamental importância para mim. E mesmo com robôs que negociam em modo de teste - sem fazer negociações, mas apenas simulando, calculando e registrando os resultados em um arquivo.

 
Muito obrigado pelo artigo - com certeza vou testar e observar....
Preciso apenas de fragmentos de código para registrar valores e indicadores de transações em estruturas.
 
//+------------------------------------------------------------------+
//|| Estrutura de transações. Usada para criar um arquivo de histórico de transações.
//+------------------------------------------------------------------+
struct SDeal
  {
   ulong             ticket;                 // Deal Ticket
   long              order;                  // Ordem com base na qual a transação foi aberta
   long              pos_id;                 // Identificador de posição
   long              time_msc;               // Tempo em milissegundos
   datetime          time;                   // Hora.
   double            volume;                 // Volume
   double            price;                  // Preço
   double            profit;                 // Lucro
   double            commission;             // Comissão sobre a transação
   double            swap;                   // Swap acumulado no fechamento
   double            fee;                    // Pagamento pela transação, cobrado imediatamente após a transação ser feita
   double            sl;                     // Nível de stop loss
   double            tp;                     // Nível de Take Profit
   ENUM_DEAL_TYPE    type;                   // Tipo
   ENUM_DEAL_ENTRY   entry;                  // Método para alterar a posição
   ENUM_DEAL_REASON  reason;                 // Motivo ou fonte para a realização da transação
   long              magic;                  // ID de especialista
   int               digits;                 // Caractere de dígitos
   ushort            symbol[16];             // Símbolo
   ushort            comment[64];            // Comentário sobre a transação
   ushort            external_id[256];       // Identificador da transação no sistema de negociação externo (na Bolsa)
   
//--- Configuração das propriedades da string
   bool              SetSymbol(const string deal_symbol)          { return(::StringToShortArray(deal_symbol, symbol)==deal_symbol.Length());                }
   bool              SetComment(const string deal_comment)        { return(::StringToShortArray(deal_comment, comment)==deal_comment.Length());             }
   bool              SetExternalID(const string deal_external_id) { return(::StringToShortArray(deal_external_id, external_id)==deal_external_id.Length()); }
                       
//--- Propriedades da string de retorno
   string            Symbol(void)                                 { return(::ShortArrayToString(symbol));                                                   }
   string            Comment(void)                                { return(::ShortArrayToString(comment));                                                  }
   string            ExternalID(void)                             { return(::ShortArrayToString(external_id));                                              }
  };

//+------------------------------------------------------------------+
//| Salva as transações do histórico em uma matriz
//+------------------------------------------------------------------+
int SaveDealsToArray(SDeal &array[], bool logs=false)
  {
//--- estrutura da transação
   SDeal deal={};
   
//--- solicitar o histórico de negócios no intervalo desde o início até o momento atual 
   if(!HistorySelect(0, TimeCurrent()))
     {
      Print("HistorySelect() failed. Error ", GetLastError());
      return 0;
     }
   
//--- número total de negócios na lista 
   int total=HistoryDealsTotal(); 

//--- processar cada transação 
   for(int i=0; i<total; i++) 
     { 
      //--- obter um tíquete do próximo negócio (o negócio é automaticamente selecionado para obter suas propriedades)
      ulong ticket=HistoryDealGetTicket(i);
      if(ticket==0)
         continue;
      
      //--- salvar apenas o balanço patrimonial e as transações comerciais
      ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket, DEAL_TYPE);
      if(deal_type!=DEAL_TYPE_BUY && deal_type!=DEAL_TYPE_SELL && deal_type!=DEAL_TYPE_BALANCE)
         continue;
      
      //--- armazenar as propriedades da transação na estrutura
      deal.ticket=ticket;
      deal.type=deal_type;
      deal.order=HistoryDealGetInteger(ticket, DEAL_ORDER);
      deal.entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket, DEAL_ENTRY);
      deal.reason=(ENUM_DEAL_REASON)HistoryDealGetInteger(ticket, DEAL_REASON);
      deal.time=(datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
      deal.time_msc=HistoryDealGetInteger(ticket, DEAL_TIME_MSC);
      deal.pos_id=HistoryDealGetInteger(ticket, DEAL_POSITION_ID);
      deal.volume=HistoryDealGetDouble(ticket, DEAL_VOLUME);
      deal.price=HistoryDealGetDouble(ticket, DEAL_PRICE);
      deal.profit=HistoryDealGetDouble(ticket, DEAL_PROFIT);
      deal.commission=HistoryDealGetDouble(ticket, DEAL_COMMISSION);
      deal.swap=HistoryDealGetDouble(ticket, DEAL_SWAP);
      deal.fee=HistoryDealGetDouble(ticket, DEAL_FEE);
      deal.sl=HistoryDealGetDouble(ticket, DEAL_SL);
      deal.tp=HistoryDealGetDouble(ticket, DEAL_TP);
      deal.magic=HistoryDealGetInteger(ticket, DEAL_MAGIC);
      deal.SetSymbol(HistoryDealGetString(ticket, DEAL_SYMBOL));
      deal.SetComment(HistoryDealGetString(ticket, DEAL_COMMENT));
      deal.SetExternalID(HistoryDealGetString(ticket, DEAL_EXTERNAL_ID));
      deal.digits=(int)SymbolInfoInteger(deal.Symbol(), SYMBOL_DIGITS);
      
      //--- incrementar a matriz e
      int size=(int)array.Size();
      ResetLastError();
      if(ArrayResize(array, size+1, total)!=size+1)
        {
         Print("ArrayResize() failed. Error ", GetLastError());
         continue;
        }
      //--- armazenar a transação na matriz
      array[size]=deal;
      //--- se ativado, envia a descrição da transação salva para o diário
      if(logs)
         DealPrint(deal, i);
     }
//--- retorna o número de negócios armazenados na matriz
   return (int)array.Size();
  }

Não entendo por que a lógica da arquitetura é ignorada.

  • Se a matriz for preenchida elemento por elemento, deve haver uma função para preencher um elemento.
  • Se um elemento é uma estrutura, ele deve ser preenchido por si mesmo.
  • Se você alocar um espaço de reserva para a matriz de uma só vez, por que precisa executar o "redimensionamento" a cada preenchimento?


Por que não escrever, por exemplo, assim?

//+------------------------------------------------------------------+
//|| Estrutura de transações. Usada para criar um arquivo de histórico de transações.
//+------------------------------------------------------------------+
struct SDeal
  {
   ulong             ticket;                 // Deal Ticket
   long              order;                  // Ordem com base na qual a transação foi aberta
   long              pos_id;                 // Identificador de posição
   long              time_msc;               // Tempo em milissegundos
   datetime          time;                   // Hora.
   double            volume;                 // Volume
   double            price;                  // Preço
   double            profit;                 // Lucro
   double            commission;             // Comissão sobre a transação
   double            swap;                   // Swap acumulado no fechamento
   double            fee;                    // Pagamento pela transação, cobrado imediatamente após a transação ser feita
   double            sl;                     // Nível de stop loss
   double            tp;                     // Nível de Take Profit
   ENUM_DEAL_TYPE    type;                   // Tipo
   ENUM_DEAL_ENTRY   entry;                  // Método para alterar a posição
   ENUM_DEAL_REASON  reason;                 // Motivo ou fonte para a realização da transação
   long              magic;                  // ID de especialista
   int               digits;                 // Caractere de dígitos
   ushort            symbol[16];             // Símbolo
   ushort            comment[64];            // Comentário sobre a transação
   ushort            external_id[256];       // Identificador da transação no sistema de negociação externo (na Bolsa)
   
//--- Configuração das propriedades da string
   bool              SetSymbol(const string deal_symbol)          { return(::StringToShortArray(deal_symbol, symbol)==deal_symbol.Length());                }
   bool              SetComment(const string deal_comment)        { return(::StringToShortArray(deal_comment, comment)==deal_comment.Length());             }
   bool              SetExternalID(const string deal_external_id) { return(::StringToShortArray(deal_external_id, external_id)==deal_external_id.Length()); }
                       
//--- Propriedades da string de retorno
   string            Symbol(void)                                 { return(::ShortArrayToString(symbol));                                                   }
   string            Comment(void)                                { return(::ShortArrayToString(comment));                                                  }
   string            ExternalID(void)                             { return(::ShortArrayToString(external_id));                                              }
   
   bool Set( const ulong _ticket )   
   {
      this.ticket=_ticket;
      this.type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(_ticket, DEAL_TYPE);
      this.order=HistoryDealGetInteger(_ticket, DEAL_ORDER);
      this.entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(_ticket, DEAL_ENTRY);
      this.reason=(ENUM_DEAL_REASON)HistoryDealGetInteger(_ticket, DEAL_REASON);
      this.time=(datetime)HistoryDealGetInteger(_ticket, DEAL_TIME);
      this.time_msc=HistoryDealGetInteger(_ticket, DEAL_TIME_MSC);
      this.pos_id=HistoryDealGetInteger(_ticket, DEAL_POSITION_ID);
      this.volume=HistoryDealGetDouble(_ticket, DEAL_VOLUME);
      this.price=HistoryDealGetDouble(_ticket, DEAL_PRICE);
      this.profit=HistoryDealGetDouble(_ticket, DEAL_PROFIT);
      this.commission=HistoryDealGetDouble(_ticket, DEAL_COMMISSION);
      this.swap=HistoryDealGetDouble(_ticket, DEAL_SWAP);
      this.fee=HistoryDealGetDouble(_ticket, DEAL_FEE);
      this.sl=HistoryDealGetDouble(_ticket, DEAL_SL);
      this.tp=HistoryDealGetDouble(_ticket, DEAL_TP);
      this.magic=HistoryDealGetInteger(_ticket, DEAL_MAGIC);
      this.SetSymbol(HistoryDealGetString(_ticket, DEAL_SYMBOL));
      this.SetComment(HistoryDealGetString(_ticket, DEAL_COMMENT));
      this.SetExternalID(HistoryDealGetString(_ticket, DEAL_EXTERNAL_ID));
      this.digits=(int)SymbolInfoInteger(this.Symbol(), SYMBOL_DIGITS);
      
      return((bool)this.time);
   }
  };

bool SetDeal( SDeal &deal, const ulong ticket )
{
  //--- salvar apenas o balanço patrimonial e as transações comerciais
  const ENUM_DEAL_TYPE deal_type = (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket, DEAL_TYPE);

  return((deal_type!=DEAL_TYPE_BUY && deal_type!=DEAL_TYPE_SELL && deal_type!=DEAL_TYPE_BALANCE) && deal.Set(ticket));
}

//+------------------------------------------------------------------+
//| Salva as transações do histórico em uma matriz
//+------------------------------------------------------------------+
int SaveDealsToArray(SDeal &array[], bool logs=false)
  {
   int Amount = 0;
   
//--- solicitar o histórico de negócios no intervalo desde o início até o momento atual 
   if(HistorySelect(0, INT_MAX))
   {
  //--- número total de negócios na lista 
     const int total=ArrayResize(array, HistoryDealsTotal()); 
  
  //--- processar cada transação 
     for(int i=0; i<total; i++) 
       if (SetDeal(array[Amount], HistoryDealGetTicket(i))) 
         Amount++;
   }

//--- retorna o número de negócios armazenados na matriz
   return(ArrayResize(array, Amount));
  }


Eu mesmo estou longe de escrever código de forma exemplar. Mas vamos ser mais sensatos de alguma forma.

 
fxsaber #:
  • Se o elemento for uma estrutura, ele deverá ser preenchido automaticamente.

Lá vamos nós de novo.

//+------------------------------------------------------------------+
//| Retorna uma descrição da transação|
//+------------------------------------------------------------------+
string DealDescription(SDeal &deal, const int index)
  {
   string indexs=StringFormat("% 5d", index);
   if(deal.type!=DEAL_TYPE_BALANCE)
      return(StringFormat("%s: deal #%I64u %s, type %s, Position #%I64d %s (magic %I64d), Price %.*f at %s, sl %.*f, tp %.*f",
                          indexs, deal.ticket, DealEntryDescription(deal.entry), DealTypeDescription(deal.type),
                          deal.pos_id, deal.Symbol(), deal.magic, deal.digits, deal.price,
                          TimeToString(deal.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS), deal.digits, deal.sl, deal.digits, deal.tp));
   else
      return(StringFormat("%s: deal #%I64u %s, type %s %.2f %s at %s",
                          indexs, deal.ticket, DealEntryDescription(deal.entry), DealTypeDescription(deal.type),
                          deal.profit, AccountInfoString(ACCOUNT_CURRENCY), TimeToString(deal.time)));
  }

Por que a estrutura não imprime a si mesma?

 
fxsaber #:
Se um elemento for uma estrutura, ele deverá ser preenchido automaticamente.

Mostre as estruturas padrão de autopublicação e autopreenchimento, por favor.

Esses códigos são escritos para simplificar a compreensão.

Não há nenhum objetivo de envolver tudo em uma monstruosidade ilegível de uma linha, mesmo que funcione.

Os artigos são escritos para transmitir a essência da forma mais clara possível, não o virtuosismo da codificação. Há tópicos especiais para virtuosismo, e você participa deles. E eles são úteis.

Os artigos têm uma finalidade diferente.

 
Artyom Trishkin #:

Mostre as estruturas padrão de auto-impressão e auto-preenchimento, por favor.

Se estivermos falando de estruturas MQ, elas não estão disponíveis na MQL5. Os autores não podiam saber o que e quem precisaria delas. Portanto, há apenas a base e a possibilidade de herança a partir delas para dotar as estruturas padrão com a funcionalidade exigida pelo usuário.

Esses códigos foram escritos para simplificar a compreensão.

Não há nenhum propósito em envolver tudo em uma monstruosidade ilegível empacotada em uma única linha, mesmo que funcione.

Não sugiro tal coisa.

Os artigos são escritos para transmitir a essência da forma mais clara possível, não o virtuosismo da escrita do código. Há tópicos especiais para virtuosismo e você participa deles. E eles são úteis.

Os artigos têm uma finalidade diferente.

O virtuosismo é o autor do livro-texto. Sou a favor da lógica.


Se um objeto pode fazer algo por si mesmo, sem ajuda, ele deve fazê-lo, e não por ele. Deve haver uma hierarquia mínima na arquitetura da escrita do código. Em seu código, você propõe apenas a substituição de várias matrizes separadas de propriedades de transação por uma única matriz de propriedades de transação usando uma estrutura. Bem, você não usa quase nada da estrutura (leitura/gravação e equiparação). O mesmo estilo processual de trabalhar com propriedades de transação.

 
fxsaber #:

Se estivermos falando de estruturas MQ, elas não estão disponíveis na MQL5. Os autores não podiam saber o que e quem precisaria delas. Portanto, há apenas a base e a possibilidade de herança a partir delas para dotar as estruturas padrão com a funcionalidade exigida pelo usuário.

Eu não ofereço tal coisa.

A virtuosidade é o autor do Tutorial. Eu defendo a lógica.


Se um objeto puder fazer algo por si mesmo, sem ajuda, ele deve fazer isso, e não para isso. Deve haver uma hierarquia mínima na arquitetura da escrita do código. Em seu código, você propõe apenas a substituição de várias matrizes separadas de propriedades de transação por uma única matriz de propriedades de transação usando uma estrutura. Bem, você não usa quase nada da estrutura (leitura/gravação e equiparação). O mesmo estilo processual de trabalhar com propriedades de transação.

Eu uso os recursos que você sugere nos artigos sobre a biblioteca. Isso não é necessário aqui.

 
//+------------------------------------------------------------------+
//|| Abre o arquivo para gravação e retorna o identificador
//+------------------------------------------------------------------+
bool FileOpenToWrite(int &handle)
  {
   ResetLastError();
   handle=FileOpen(PATH, FILE_WRITE|FILE_BIN|FILE_COMMON);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: FileOpen() failed. Error %d",__FUNCTION__, GetLastError());
      return false;
     }
//--- com sucesso
   return true;
  }
//+------------------------------------------------------------------+
//|| Abre o arquivo para leitura e retorna o identificador
//+------------------------------------------------------------------+
bool FileOpenToRead(int &handle)
  {
   ResetLastError();
   handle=FileOpen(PATH, FILE_READ|FILE_BIN|FILE_COMMON);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: FileOpen() failed. Error %d",__FUNCTION__, GetLastError());
      return false;
     }
//--- com sucesso
   return true;
  }
//+------------------------------------------------------------------+
//| Salva os dados da transação da matriz || em um arquivo
//+------------------------------------------------------------------+
bool FileWriteDealsFromArray(SDeal &array[], ulong &file_size)
  {
//--- se uma matriz vazia for passada - informe isso e retorne false
   if(array.Size()==0)
     {
      PrintFormat("%s: Error! Empty deals array passed",__FUNCTION__);
      return false;
     }
     
//--- abrir o arquivo para gravação, obter seu identificador
   int handle=INVALID_HANDLE;
   if(!FileOpenToWrite(handle))
      return false;
   
//--- mover o ponteiro do arquivo para o final do arquivo 
   bool res=true;
   ResetLastError();
   res&=FileSeek(handle, 0, SEEK_END);
   if(!res)
      PrintFormat("%s: FileSeek(SEEK_END) failed. Error %d",__FUNCTION__, GetLastError());
   
//--- escreva os dados da matriz no final do arquivo 
   file_size=0;
   res&=(FileWriteArray(handle, array)==array.Size());
   if(!res)
      PrintFormat("%s: FileWriteArray() failed. Error ",__FUNCTION__, GetLastError());
   else
      file_size=FileSize(handle);

//--- fechar o arquivo 
   FileClose(handle);
   return res;
  }
//+------------------------------------------------------------------+
//| Carrega os dados da transação do arquivo para a matriz
//+------------------------------------------------------------------+
bool FileReadDealsToArray(SDeal &array[], ulong &file_size)
  {
//--- abrir o arquivo para leitura, obter seu identificador
   int handle=INVALID_HANDLE;
   if(!FileOpenToRead(handle))
      return false;
   
//--- mover o ponteiro do arquivo para o final do arquivo 
   bool res=true;
   ResetLastError();
   
//--- ler dados do arquivo em uma matriz
   file_size=0;
// res=(FileReadArray(handle, array)>0);
   res=(ArrayResize(array, FileReadArray(handle, array))>0);
   if(!res)
      PrintFormat("%s: FileWriteArray() failed. Error ",__FUNCTION__, GetLastError());
   else
      file_size=FileSize(handle);

//--- fechar o arquivo 
   FileClose(handle);
   return res;
  }

Há um erro frequente em destacar e corrigir isso.

Mas, ainda assim, por que não escrever todo esse código de forma diferente?

//+------------------------------------------------------------------+
//| Salva os dados da transação da matriz || em um arquivo
//+------------------------------------------------------------------+
bool FileWriteDealsFromArray(SDeal &array[], long &file_size)
{
  return((file_size = FileSave(PATH, array, FILE_COMMON) * sizeof(SDeal)) > 0);
}

//+------------------------------------------------------------------+
//| Carrega os dados da transação do arquivo para a matriz
//+------------------------------------------------------------------+ 
bool FileReadDealsToArray2(SDeal &array[], long &file_size)
{
  return((file_size = ArrayResize(array, (int)FileLoad(PATH, array, FILE_COMMON)) * sizeof(SDeal)) > 0);
}
 
fxsaber #:
Mas, ainda assim, por que não escrever todo esse código de forma diferente?

O artigo é destinado a um nível de treinamento bastante baixo. Seu objetivo é abranger uma grande parte dos leitores. Aqueles que são experientes e experientes não precisam desses artigos. Eles têm seus próprios bigodes.)

 
Artyom Trishkin #:

O artigo é destinado a um nível de treinamento bastante baixo. A fim de atingir uma ampla seção do público leitor.

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação

MetaTrader 5 Strategy Tester: erros, bugs, sugestões para melhorar seu trabalho

fxsaber, 2019.09.06 15:45

Um bom recurso que você está ignorando

MqlTick tiks[];

if (FileLoad("deribit1.out.bin", ticks))
{
// ....