English Русский 中文 Español Deutsch 日本語
Apresentação personalizada do histórico de negociação e criação de gráficos para relatórios

Apresentação personalizada do histórico de negociação e criação de gráficos para relatórios

MetaTrader 5Exemplos | 24 setembro 2018, 10:08
7 038 0
Andrey Azatskiy
Andrey Azatskiy

Introdução

Todos sabem que, ao operar nos mercados financeiros, é importante monitorar o desempenho e saber analisar o histórico de negociação. Uma retrospectiva de dados não apenas ajuda a acompanhar a dinâmica da negociação, mas também a avaliar a eficácia geral da estratégia utilizada. Como o acompanhamento e a avaliação são muito úteis para os traders que operam manualmente ou que preferem o trading algorítmico, sugiro criar algumas ferramentas implementando essa funcionalidade. 

No coração de qualquer sistema de trading está um algoritmo de negociação formando uma curva de Profit/Loss. Esse algoritmo pode ser comparado a um ativo sintético cujo valor é formado em relação a um ativo subjacente (instrumento negociado). Por exemplo, para opções, há uma fórmula (modelo BSM) que a qualquer momento calcula o valor do ativo sintético com base no preço do ativo subjacente. Mas para um algoritmo de negociação, essa fórmula não existe. Neste caso, consequentemente, a execução do algoritmo pode ser comparada a uma posição longa de um instrumento sintético cuja curva de PL é formada pela lógica programada nele. O lucro gerado por esse 'ativo' pode ser instável em diferentes períodos. Mesmo se estiver sujeito a avaliação por um único modelo econométrico, este modelo não pode ser unificado. Surge a pergunta: como rastrear tanto esse ativo quanto a fase de nossa negociação? Uma solução adequada parece ser realizar uma retrospetiva da negociação desse algoritmo e identificar desvios em relação aos resultados esperados.

Não darei conselhos sobre a análise de algoritmos, em vez disso, apresentarei um conjunto de métodos que ajudarão a dar uma visão completa do histórico de negociação. Com base nos dados obtidos, você pode construir modelos econométricos complexos, determinar perfis e fazer outros raciocínios.

Este artigo será dividido em duas seções. Na primeira seção, falarei sobre métodos de geração de relatórios de negociação a partir das informações armazenadas nos terminais. Quer dizer, esta seção tem a ver com os dados iniciais para análise. Na segunda seção, formaremos os principais indicadores que ajudarão a avaliar a retrospectiva dos dados de negociação selecionados. A amostragem de dados pode variar para todos os ativos, para um ativo, para todo o histórico disponível ou para um determinado período. Os resultados da análise serão apresentados num arquivo separado e brevemente mostrados no terminal.

Tirei os dados para análise do meu histórico de negociação real. Os exemplos de como implementar o código foram criados no período de teste, numa conta de demostração, especificamente com esse objetivo.


Capítulo 1. Preparação de dados para analisar estatísticas de negociação

Todas as análises começam com a preparação dos dados iniciais. No nosso caso, eles são o histórico de negociação de um determinado período. Na memória do MetaTrader 5, são armazenados relatórios detalhados. Porém, às vezes, a informação é supérflua, e detalhes excessivos podem atrapalhar mais do que ajudar. Nesta seção, tentaremos criar um resumo legível e informativo do histórico de negociação.

Histórico de negociação no MetaTrader 5 e métodos de trabalho com ele

O carregamento do histórico de negociação sempre é acompanhado por muitos registros. Em primeiro lugar, cada posição é dividida em transações de entrada/saída que podem ocorrer em vários estágios. Em segundo lugar, às vezes, há situações de compra gradual e de redução de posições. Em terceiro lugar, no histórico existem linhas de 'transações imaginárias' devidas a:

  • fechamento/abertura de posição durante o acréscimo/redução da margem de variação (FORTS);
  • todas as possíveis correções de posições existentes;
  • depósito e retirada de fundos da conta de negociação.

Classificar o histórico usando o tempo de abertura das transações também aumenta a dificuldade para ler os relatórios. Tudo isso, somado ao fato de o período de vida das transações diferir, pode alterar a ordem em que são apresentadas informações da hora de fixação do resultado da transação. Por exemplo, a primeira transação é aberta no dia 1º de junho e fechada no dia 5. A segunda é aberta no dia 2 de junho e no mesmo dia é fechada. Portanto, embora o lucro/perda da segunda apareça mais cedo do que o lucro/perda da primeira, na tabela ela será exibida mais tarde, pois foi aberta mais tarde.

Consequentemente, ao analisar a negociação, um histórico formado assim não é apropriado, embora reflita completamente todo o trabalho realizado na conta. Para trabalhar com a matriz de dados descrita, a MQL5 tem um conjunto útil de ferramentas usado para fazer com que o histórico de negociação fique num formato legível. A primeira coisa que precisamos é dividir a pilha de dados no histórico de negociação, classificando por ativos negociados e transações. Para fazer isso, criamos as seguintes estruturas:

//+------------------------------------------------------------------+
//| Estrutura de dados da transação selecionada                      |
//+------------------------------------------------------------------+
struct DealData
  {
   long              ticket;            // Boleta da transação
   long              order;             // Número da ordem que abre a posição
   datetime          DT;                // Data de abertura da posição
   long              DT_msc;            // Data de abertura da posição em milissegundos 
   ENUM_DEAL_TYPE    type;              // Tipo de posição aberta
   ENUM_DEAL_ENTRY   entry;             // Tipo de entrada na posição
   long              magic;             // Número exclusivo da posição
   ENUM_DEAL_REASON  reason;            // Local de posicionamento da posição
   long              ID;                // ID da posição
   double            volume;            // Volume da posição (lotes)
   double            price;             // Preço de entrada na posição
   double            comission;         // Comissão paga
   double            swap;              // Swap
   double            profit;            // Lucro/perda
   string            symbol;            // Símbolo 
   string            comment;           // Comentário especificado no momento da abertura
   string            ID_external;       // ID externo 
  };
//+------------------------------------------------------------------+
//| Estrutura que armazena todas as negociações da posição           |
//| que foi selecionada por ID                                       |
//+------------------------------------------------------------------+
struct DealKeeper
  {
   DealData          deals[];           /* Lista de todas as transações para dada posição (ou para várias posições se a transação for em outra direção)*/
   string            symbol;            // Símbolo 
   long              ID;                // ID da posição (posições)
   datetime          DT_min;            // data de abertura 
   datetime          DT_max;            // data de fechamento 
  };

Neste código, vemos que a estrutura DealData contém uma descrição redundante dos parâmetros da transação.

Nossa estrutura principal será a DealKeeper. Ela contém uma descrição da posição e todas as transações incluídas nela. O principal filtro de posição no terminal é seu ID, isto é, um indicador que permanece inalterado para todas as transações dessa posição. Daí que, se ocorrer uma reversão de posição, ela preserve o ID original e mude para a posição oposta, fazendo com que a estrutura da DealKeeper tenha duas posições. A filtragem do preenchimento da DealKeeper no nosso código é construída de acordo com o ID da posição.

Consideraremos em detalhes o método de preenchimento da estrutura. A função getHistory da classe CDealHistoryGetter é responsável por isso:

//+-----------------------------------------------------------------------+
//| Classe para extrair o histórico de negociação do terminal e exibi-lo  |
//| convenientemente para análise                                         |
//+-----------------------------------------------------------------------+
class CDealHistoryGetter
  {
public:
   bool              getHistory(DealKeeper &deals[],datetime from,datetime till);                // retorna todos os dados das transações do histórico  
   bool              getIDArr(ID_struct &ID_arr[],datetime from,datetime till);                  // retorna a matriz única do ID das transações
   bool              getDealsDetales(DealDetales &ans[],datetime from,datetime till);            // retorna a matriz de transações em que cada linha é uma transação específica
private:

   void              addArray(ID_struct &Arr[],const ID_struct &value);                          // adicionar à matriz dinâmica
   void              addArray(DealKeeper &Arr[],const DealKeeper &value);                        // adicionar à matriz dinâmica
   void              addArray(DealData &Arr[],const DealData &value);                            // adicionar à matriz dinâmica
   void              addArr(DealDetales &Arr[],DealDetales &value);                              // adicionar à matriz dinâmica
   void              addArr(double &Arr[],double value);                                         // adicionar à matriz dinâmica

/*
    Se houver saídas segundo o InOut, haverá mais de uma posição no inputParam. 
    Se não houver saídas segundo o InOut, haverá apenas uma transação no inputParam! 
*/
   void              getDeals_forSelectedKeeper(DealKeeper &inputParam,DealDetales &ans[]);      // é formado um só registro de acordo com uma posição selecionada para todas as outras posições que existem no inputParam
   double            MA_price(double &prices[],double &lots[]);                                  // é calculado o preço de abertura médio ponderado
   bool              isBorderPoint(DealData &data,BorderPointType &type);                        // obtemos informações para saber se é ponto limite e conhecer o tipo de ponto
   ENUM_DAY_OF_WEEK  getDay(datetime DT);                                                        // obtemos o dia da data
   double            calcContracts(double &Arr[],GetContractType type);                          // obtemos informações sobre o volume da posição mais recente
  };

Analisemos sua implementação:

//+------------------------------------------------------------------+
//| retorna todos os dados das transações do histórico               |
//+------------------------------------------------------------------+
bool CDealHistoryGetter::getHistory(DealKeeper &deals[],datetime from,datetime till)
  {
   ArrayFree(deals);                                     // limpamos a matriz com resultados
   ID_struct ID_arr[];
   if(getIDArr(ID_arr,from,till))                        // obtemos o ID exclusivo 
     {
      int total=ArraySize(ID_arr);
      for(int i=0;i<total;i++)                           // ciclo do ID
        {
         DealKeeper keeper;                              // acumulador de transações da posição
         keeper.ID=ID_arr[i].ID;
         keeper.symbol=ID_arr[i].Symb;
         keeper.DT_max = LONG_MIN;
         keeper.DT_min = LONG_MAX;
         if(HistorySelectByPosition(ID_arr[i].ID))       // selecionamos todas as transações com o ID especificado
           {

Primeiro, você precisa obter o ID exclusivo da posição. Para fazer isso, num ciclo contínuo de histórico, é analisado o ID das transações e é formada uma estrutura com dois campos, nomeadamente com o Símbolo e o ID da posição. Os dados obtidos serão necessários para trabalhar com a função getHistory

Em linguagem MQL5 existe uma função muito útil chamada HistorySelectByPosition. Ela serve para selecionar todo o histórico de transações com o ID transmitido. Com sua ajuda, descartamos imediatamente as operações desnecessárias de nossa lista (processamento de contas, entrada e retirada de fundos, etc.). Como resultado, é memorizado o histórico de todas as mudanças que ocorreram com a posição quando foi aberta. Os dados são classificados por data e hora. Agora precisamos ordená-los com a ajuda da função HistoryDealsTotal, pois ela retorna o número total de transações - com ID especificado - que foram armazenadas na memória.

int total_2=HistoryDealsTotal();
            for(int n=0;n<total_2;n++)                        // Ciclo para transações selecionadas
              {
               long ticket=(long)HistoryDealGetTicket(n);
               DealData data;
               data.ID=keeper.ID;
               data.symbol=keeper.symbol;
               data.ticket= ticket;

               data.DT=(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
               keeper.DT_max=MathMax(keeper.DT_max,data.DT);
               keeper.DT_min=MathMin(keeper.DT_min,data.DT);
               data.order= HistoryDealGetInteger(ticket,DEAL_ORDER);
               data.type = (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket,DEAL_TYPE);
               data.DT_msc=HistoryDealGetInteger(ticket,DEAL_TIME_MSC);
               data.entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);
               data.magic = HistoryDealGetInteger(ticket,DEAL_MAGIC);
               data.reason= (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON);
               data.volume= HistoryDealGetDouble(ticket,DEAL_VOLUME);
               data.price = HistoryDealGetDouble(ticket,DEAL_PRICE);
               data.comission=HistoryDealGetDouble(ticket,DEAL_COMMISSION);
               data.swap=HistoryDealGetDouble(ticket,DEAL_SWAP);
               data.profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);
               data.comment=HistoryDealGetString(ticket,DEAL_COMMENT);
               data.ID_external=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID);

               addArray(keeper.deals,data);                  // Adicionamos transações
              }

            if(ArraySize(keeper.deals)>0)
               addArray(deals,keeper);                       // Adicionamos posições
           }
        }
      return ArraySize(deals) > 0;
     }
   else
      return false;                                          // Se não houver ID únicos
  }

Assim, passando por cada ID único, formamos uma matriz de dados que descreve a posição. A matriz de estruturas DealKeeper reflete um histórico de negociação detalhado para a conta atual durante o período solicitado.

Preparação de dados para análise

Na seção anterior, obtivemos os dados. Agora devemos exportá-los para um arquivo. Aqui está uma lista de transações para uma das posições estudadas:

Ticket Order DT DT msc tipo Entry Magic Reason ID Volume Price Comission Swap Profit Symbol Comment ID external
10761601 69352663 23.11.2017 17:41 1,51146E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 506789 DEAL_REASON_EXPERT 69352663 1 58736 -0,5 0 0 Si-12.17 Open test position 23818051
10761602 69352663 23.11.2017 17:41 1,51146E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 506789 DEAL_REASON_EXPERT 69352663 1 58737 -0,5 0 0 Si-12.17 Open test position 23818052
10766760 0 24.11.2017 13:00 1,51153E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58682 0 0 -109 Si-12.17 [variation margin close]
10766761 0 24.11.2017 13:00 1,51153E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58682 0 0 0 Si-12.17 [variation margin open]
10769881 0 24.11.2017 15:48 1,51154E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58649 0 0 -66 Si-12.17 [variation margin close]
10769882 0 24.11.2017 15:48 1,51154E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58649 0 0 0 Si-12.17 [variation margin open]
10777315 0 27.11.2017 13:00 1,51179E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58420 0 0 -458 Si-12.17 [variation margin close]
10777316 0 27.11.2017 13:00 1,51179E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58420 0 0 0 Si-12.17 [variation margin open]
10780552 0 27.11.2017 15:48 1,5118E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58417 0 0 -6 Si-12.17 [variation margin close]
10780553 0 27.11.2017 15:48 1,5118E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58417 0 0 0 Si-12.17 [variation margin open]
10790453 0 28.11.2017 13:00 1,51187E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58589 0 0 344 Si-12.17 [variation margin close]
10790454 0 28.11.2017 13:00 1,51187E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58589 0 0 0 Si-12.17 [variation margin open]
10793477 0 28.11.2017 15:48 1,51188E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58525 0 0 -128 Si-12.17 [variation margin close]
10793478 0 28.11.2017 15:48 1,51188E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58525 0 0 0 Si-12.17 [variation margin open]
10801186 0 29.11.2017 13:00 1,51196E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58515 0 0 -20 Si-12.17 [variation margin close]
10801187 0 29.11.2017 13:00 1,51196E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58515 0 0 0 Si-12.17 [variation margin open]
10804587 0 29.11.2017 15:48 1,51197E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58531 0 0 32 Si-12.17 [variation margin close]
10804588 0 29.11.2017 15:48 1,51197E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58531 0 0 0 Si-12.17 [variation margin open]
10813418 0 30.11.2017 13:00 1,51205E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58843 0 0 624 Si-12.17 [variation margin close]
10813419 0 30.11.2017 13:00 1,51205E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58843 0 0 0 Si-12.17 [variation margin open]
10816400 0 30.11.2017 15:48 1,51206E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58609 0 0 -468 Si-12.17 [variation margin close]
10816401 0 30.11.2017 15:48 1,51206E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58609 0 0 0 Si-12.17 [variation margin open]
10824628 0 01.12.2017 13:00 1,51213E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58864 0 0 510 Si-12.17 [variation margin close]
10824629 0 01.12.2017 13:00 1,51213E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58864 0 0 0 Si-12.17 [variation margin open]
10828227 0 01.12.2017 15:48 1,51214E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58822 0 0 -84 Si-12.17 [variation margin close]
10828228 0 01.12.2017 15:48 1,51214E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58822 0 0 0 Si-12.17 [variation margin open]
10838074 0 04.12.2017 13:00 1,51239E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59093 0 0 542 Si-12.17 [variation margin close]
10838075 0 04.12.2017 13:00 1,51239E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59093 0 0 0 Si-12.17 [variation margin open]
10840722 0 04.12.2017 15:48 1,5124E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59036 0 0 -114 Si-12.17 [variation margin close]
10840723 0 04.12.2017 15:48 1,5124E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59036 0 0 0 Si-12.17 [variation margin open]
10848185 0 05.12.2017 13:00 1,51248E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58793 0 0 -486 Si-12.17 [variation margin close]
10848186 0 05.12.2017 13:00 1,51248E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58793 0 0 0 Si-12.17 [variation margin open]
10850473 0 05.12.2017 15:48 1,51249E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58881 0 0 176 Si-12.17 [variation margin close]
10850474 0 05.12.2017 15:48 1,51249E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58881 0 0 0 Si-12.17 [variation margin open]
10857862 0 06.12.2017 13:00 1,51257E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59181 0 0 600 Si-12.17 [variation margin close]
10857863 0 06.12.2017 13:00 1,51257E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59181 0 0 0 Si-12.17 [variation margin open]
10860776 0 06.12.2017 15:48 1,51258E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59246 0 0 130 Si-12.17 [variation margin close]
10860777 0 06.12.2017 15:48 1,51258E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59246 0 0 0 Si-12.17 [variation margin open]
10869047 0 07.12.2017 13:00 1,51265E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59325 0 0 158 Si-12.17 [variation margin close]
10869048 0 07.12.2017 13:00 1,51265E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59325 0 0 0 Si-12.17 [variation margin open]
10871856 0 07.12.2017 15:48 1,51266E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59365 0 0 80 Si-12.17 [variation margin close]
10871857 0 07.12.2017 15:48 1,51266E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59365 0 0 0 Si-12.17 [variation margin open]
10879894 0 08.12.2017 13:01 1,51274E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59460 0 0 190 Si-12.17 [variation margin close]
10879895 0 08.12.2017 13:01 1,51274E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59460 0 0 0 Si-12.17 [variation margin open]
10882283 0 08.12.2017 15:48 1,51275E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59421 0 0 -78 Si-12.17 [variation margin close]
10882284 0 08.12.2017 15:48 1,51275E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59421 0 0 0 Si-12.17 [variation margin open]
10888014 0 11.12.2017 13:00 1,513E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59318 0 0 -206 Si-12.17 [variation margin close]
10888015 0 11.12.2017 13:00 1,513E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59318 0 0 0 Si-12.17 [variation margin open]
10890195 0 11.12.2017 15:48 1,51301E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59280 0 0 -76 Si-12.17 [variation margin close]
10890196 0 11.12.2017 15:48 1,51301E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59280 0 0 0 Si-12.17 [variation margin open]
10895808 0 12.12.2017 13:00 1,51308E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58920 0 0 -720 Si-12.17 [variation margin close]
10895809 0 12.12.2017 13:00 1,51308E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58920 0 0 0 Si-12.17 [variation margin open]
10897839 0 12.12.2017 15:48 1,51309E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58909 0 0 -22 Si-12.17 [variation margin close]
10897840 0 12.12.2017 15:48 1,51309E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58909 0 0 0 Si-12.17 [variation margin open]
10903172 0 13.12.2017 13:00 1,51317E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59213 0 0 608 Si-12.17 [variation margin close]
10903173 0 13.12.2017 13:00 1,51317E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59213 0 0 0 Si-12.17 [variation margin open]
10905906 0 13.12.2017 15:48 1,51318E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 59072 0 0 -282 Si-12.17 [variation margin close]
10905907 0 13.12.2017 15:48 1,51318E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 59072 0 0 0 Si-12.17 [variation margin open]
10911277 0 14.12.2017 13:00 1,51326E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 2 58674 0 0 -796 Si-12.17 [variation margin close]
10911278 0 14.12.2017 13:00 1,51326E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 2 58674 0 0 0 Si-12.17 [variation margin open]
10912285 71645351 14.12.2017 14:48 1,51326E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 506789 DEAL_REASON_EXPERT 69352663 1 58661 -0,5 0 -13 Si-12.17 PartialClose position_2 25588426
10913632 0 14.12.2017 15:48 1,51327E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58783 0 0 109 Si-12.17 [variation margin close]
10913633 0 14.12.2017 15:48 1,51327E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58783 0 0 0 Si-12.17 [variation margin open]
10919412 0 15.12.2017 13:00 1,51334E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58912 0 0 129 Si-12.17 [variation margin close]
10919413 0 15.12.2017 13:00 1,51334E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58912 0 0 0 Si-12.17 [variation margin open]
10921766 0 15.12.2017 15:48 1,51335E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58946 0 0 34 Si-12.17 [variation margin close]
10921767 0 15.12.2017 15:48 1,51335E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58946 0 0 0 Si-12.17 [variation margin open]
10927382 0 18.12.2017 13:00 1,5136E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58630 0 0 -316 Si-12.17 [variation margin close]
10927383 0 18.12.2017 13:00 1,5136E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58630 0 0 0 Si-12.17 [variation margin open]
10929913 0 18.12.2017 15:48 1,51361E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58664 0 0 34 Si-12.17 [variation margin close]
10929914 0 18.12.2017 15:48 1,51361E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58664 0 0 0 Si-12.17 [variation margin open]
10934874 0 19.12.2017 13:00 1,51369E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58635 0 0 -29 Si-12.17 [variation margin close]
10934875 0 19.12.2017 13:00 1,51369E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58635 0 0 0 Si-12.17 [variation margin open]
10936988 0 19.12.2017 15:48 1,5137E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58629 0 0 -6 Si-12.17 [variation margin close]
10936989 0 19.12.2017 15:48 1,5137E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58629 0 0 0 Si-12.17 [variation margin open]
10941561 0 20.12.2017 13:00 1,51377E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58657 0 0 28 Si-12.17 [variation margin close]
10941562 0 20.12.2017 13:00 1,51377E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58657 0 0 0 Si-12.17 [variation margin open]
10943405 0 20.12.2017 15:48 1,51378E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58684 0 0 27 Si-12.17 [variation margin close]
10943406 0 20.12.2017 15:48 1,51378E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58684 0 0 0 Si-12.17 [variation margin open]
10948277 0 21.12.2017 13:00 1,51386E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_VMARGIN 69352663 1 58560 0 0 -124 Si-12.17 [variation margin close]
10948278 0 21.12.2017 13:00 1,51386E+12 DEAL_TYPE_BUY DEAL_ENTRY_IN 0 DEAL_REASON_VMARGIN 69352663 1 58560 0 0 0 Si-12.17 [variation margin open]
10949780 0 21.12.2017 15:45 1,51387E+12 DEAL_TYPE_SELL DEAL_ENTRY_OUT 0 DEAL_REASON_CLIENT 69352663 1 58560 0 0 0 Si-12.17 [instrument expiration] 26163141

Este exemplo é retirado do histórico de transações de uma conta de demonstração em futuros do par USDRUR no mercado FORTS. Como visto na tabela, a entrada na posição foi dividida em duas ordens com um lote igual. A saída é dividida em duas partes. O ID que abriu as transações para cada entrada e saída (exceto para a margem de variação) foi diferente de zero. Mas a última transação que encerrou a posição veio com um ID de ordem igual a zero. Isso aconteceu porque a posição foi fechada devido à expiração do ativo.

Embora este histórico de negociação já seja legível e reflita transações (incluindo as 'imaginárias') realizadas numa única posição, ainda continua a ser inconveniente para a análise final de posição e requer mais desenvolvimento. Precisamos de uma tabela, com cada linha refletindo totalmente o resultado financeiro da posição, bem como fornecendo as informações respectivas.

Criemos uma estrutura para isso:

//+------------------------------------------------------------------+
//| Estrutura que armazena o resultado financeiro e                  |
//| as informações básicas da posição selecionada.                   |
//+------------------------------------------------------------------+
struct DealDetales
  {
   string            symbol;                // símbolo 
   datetime          DT_open;               // data de abertura
   ENUM_DAY_OF_WEEK  day_open;              // dia da semana de abertura
   datetime          DT_close;              // data de fechamento
   ENUM_DAY_OF_WEEK  day_close;             // dia da semana de fechamento
   double            volume;                // volume (lotes)
   bool              isLong;                // sinal long/short
   double            price_in;              // preço de entrada na posição
   double            price_out;             // preço de saída da posição
   double            pl_oneLot;             // lucro/preço se operado apenas um lote
   double            pl_forDeal;            // perda/lucro real levando em conta a comissão
   string            open_comment;          // comentário no momento da abertura
   string            close_comment;         // comentário no momento do fechamento
  };

Essa estrutura é formada pelo método getDealsDetales. Utiliza 3 métodos private fundamentais: 

  • isBorderPoint define se a transação atual é de borda (se é colocada pelo trader e se fecha/abre a posição);
  • MA_price calcula o preço real de abertura/fechamento da posição;
  • calcContracts é usado para contar o número de contratos que estavam no mercado durante o período de vida da posição.

Consideremos em detalhes o método MA_price. Por exemplo, se a posição foi aberta em vários contratos e a preços diferentes, seria incorreto fixar o preço da primeira transação como o preço de abertura da posição. Isso levará a erros nos cálculos posteriores. Usamos a média ponderada. Vamos calcular a média dos preços das posições de abertura/fechamento e vamos ponderá-los pelo volume de transações abertas. Esta é a implementação dessa lógica no código:

//+------------------------------------------------------------------+
//| Calculando o preço de abertura ponderado                         |
//+------------------------------------------------------------------+
double CDealHistoryGetter::MA_price(double &prices[],double &lots[])
  {
   double summ=0;                           // Soma de preços ponderados
   double delemetr=0;                       // Soma de pesos
   int total=ArraySize(prices);
   for(int i=0;i<total;i++)                 // Ciclo de soma
     {
      summ+=(prices[i]*lots[i]);
      delemetr+=lots[i];
     }
   return summ/delemetr;                    // Cálculo de média
  }
Em relação aos lotes negociados, este caso é mais complicado, mas não é difícil entendê-lo. Consideremos o exemplo em que gerenciamos dinamicamente uma posição e gradualmente adicionamos ou reduzimos o número de lotes:

In Out
Open (t1) 1 0
t2 2 0
t3 5 3
t4 1 0
t5 0 1
t6 1 0
t7 0 1
t8 1 0
t9 0 1
Close (t10) 0 5
Total  11  11 

Enquanto existia a posição, compramos e vendemos constantemente o ativo (às vezes alternadamente e às vezes quase simultaneamente). Como resultado, acumulamos 11 lotes para compra e para venda. Mas isso não significa que o volume máximo atingido tenha sido igual a 11 lotes. Registramos todas as entradas e saídas em fila: aumento e entradas — com sinal +, enquanto as saídas e reduções — com o sinal -. Fazemos um sumário com eles, como se faz o balanço da curva de PL.


Volume da transação Volume da posição
deal 1 1 1
deal  2 2 3
deal 3 5 8
deal 4 -3 5
deal 5 1 6
deal 6 -1 5
deal 7 1 6
deal 8 -1 5
deal 9 1 6
deal 10 -1 5
deal 11  -5  0

Nesta tabela, vemos claramente que a soma máxima de lotes foi de 8. Esse é o valor procurado.

Veja como a função refletindo o número de lotes é implementada no código:

//+------------------------------------------------------------------+
//| Calculamos qual o volume da posição real                         |
//| e obtemos informações sobre o volume da posição mais recente     |
//+------------------------------------------------------------------+
double CDealHistoryGetter::calcContracts(double &Arr[],GetContractType type)
  {
   int total;

   if((total=ArraySize(Arr))>1)                        // se o tamanho da matriz for mais de um
     {
      double lotArr[];
      addArr(lotArr,Arr[0]);                           // adicionamos o primeiro lote à matriz 
      for(int i=1;i<total;i++)                         // ciclo do segundo elemento da matriz
         addArr(lotArr,(lotArr[i-1]+Arr[i]));          // adicionamos a soma dos lotes passado e atual à matriz (lotArr[i-1]+Arr[i]))

      if(type==GET_REAL_CONTRACT)
         return lotArr[ArrayMaximum(lotArr)];          // retornamos o lote negociado realmente
      else
         return lotArr[ArraySize(lotArr)-1];           // retornamos o último lote negociado
     }
   else
      return Arr[0];
  }

Além da contagem simples do lote, a função também mostra o último lote ativo (no nosso exemplo é 5).

Consideremos a função isBorderPoint que foi criada para filtrar transações desnecessárias. Usando a estrutura de nossos dados, podemos determinar a importância da transação com base em 4 indicadores:

  • ID da ordem;
  • ENUM_DEAL_ENTRY — entrada, saída, reversão;
  • ENUM_DEAL_TYPE — compra/venda;
  • ENUM_DEAL_REASON — maneira como a ordem é posicionada.

Criamos uma enumeração:

//+--------------------------------------------------------------------+
//| Enumeração auxiliar apontando para um tipo de registro específico  |
//| O registro em si é apresentado pela estrutura DealData             |
//+--------------------------------------------------------------------+
enum BorderPointType
  {
   UsualInput,          // tipo de entrada habitual (DEAL_ENTRY_IN) - início da transação
   UsualOutput,         // tipo de saída habitual (DEAL_ENTRY_OUT) - conclusão da transação
   OtherPoint,          // tudo o que ignoramos: aumento de saldo, ajustes de posição, saídas, margem de variação
   InOut,               // reversão da posição (DEAL_ENTRY_INOUT)
   OutBy                // saída da posição usando a posição oposta (DEAL_ENTRY_OUT_BY)
  };
Das cinco opções apresentadas, estamos interessados ​​apenas em quatro. Todas as transações desnecessárias se enquadram na opção OtherPoint. As combinações de parâmetros que satisfazem cada uma das opções de enumeração são apresentadas na tabela. Você pode ver o código da função nos arquivos anexados ao artigo. 
Opções de enumeração ID da ordem ENUM_DEAL_ENTRY ENUM_DEAL_TYPE ENUM_DEAL_REASON
UsualInput  >0 DEAL_ENTRY_IN DEAL_TYPE_BUY DEAL_REASON_CLIENT
DEAL_REASON_EXPERT



DEAL_TYPE_SELL DEAL_REASON_WEB


DEAL_REASON_MOBILE



UsualOut >=0(=0 se houve uma expiração) DEAL_ENTRY_OUT DEAL_TYPE_BUY DEAL_REASON_CLIENT
DEAL_REASON_EXPERT



DEAL_REASON_WEB



DEAL_TYPE_SELL DEAL_REASON_MOBILE


DEAL_REASON_SL



DEAL_REASON_TP



DEAL_REASON_SO



OtherPoint  0 DEAL_ENTRY_IN DEAL_TYPE_BUY DEAL_REASON_ROLLOVER
DEAL_TYPE_SELL



DEAL_TYPE_BALANCE



DEAL_TYPE_CREDIT



DEAL_TYPE_CHARGE



DEAL_TYPE_CORRECTION



DEAL_TYPE_BONUS DEAL_REASON_VMARGIN


DEAL_TYPE_COMMISSION



DEAL_TYPE_COMMISSION_DAILY



DEAL_ENTRY_OUT DEAL_TYPE_COMMISSION_MONTHLY


DEAL_TYPE_COMMISSION_AGENT_DAILY



DEAL_TYPE_COMMISSION_AGENT_MONTHLY



DEAL_TYPE_INTEREST DEAL_REASON_SPLIT


DEAL_TYPE_BUY_CANCELED



DEAL_TYPE_SELL_CANCELED



DEAL_DIVIDEND



DEAL_DIVIDEND_FRANKED



DEAL_TAX



InOut  >0 DEAL_ENTRY_INOUT  DEAL_TYPE_BUY DEAL_REASON_CLIENT
DEAL_REASON_EXPERT



DEAL_TYPE_SELL DEAL_REASON_WEB


DEAL_REASON_MOBILE



OutBy  >0 DEAL_ENTRY_OUT_BY  DEAL_TYPE_BUY DEAL_REASON_CLIENT
DEAL_REASON_EXPERT



DEAL_TYPE_SELL DEAL_REASON_WEB


DEAL_REASON_MOBILE



Vamos formar o histórico necessário usando o método getDeals_forSelectedKeeper. Vamos considerar sua lógica geral e, em seguida, vamos analisar as opções de enumerações descritas acima (começando com a linha 303).

//+------------------------------------------------------------------+
//| Um registro para cada posição selecionada é gerado no inputParam |
//+------------------------------------------------------------------+

void CDealHistoryGetter::getDeals_forSelectedKeeper(DealKeeper &inputParam,DealDetales &ans[])
  {
   ArrayFree(ans);
   int total=ArraySize(inputParam.deals);
   DealDetales detales;                                          // variável em que colocamos os resultados do ciclo subsequente
   detales.symbol=inputParam.symbol;
   detales.volume= 0;
   detales.pl_forDeal=0;
   detales.pl_oneLot=0;
   detales.close_comment= "";
   detales.open_comment = "";
   detales.DT_open=0;

// Sinalizador para adição desta posição ao conjunto
   bool isAdd=false;
   bool firstPL_setted=false;
// Matrizes de preços de entrada, de preços de saída, lotes de entrada, lotes de saída, contratos
   double price_In[],price_Out[],lot_In[],lot_Out[],contracts[]; // linha 404

   for(int i=0;i<total;i++)                                      // ciclo das transações (todas as transações têm um único ID, mas pode haver mais de uma posição se o tipo de transação for InOut)
     {
      BorderPointType type;                                      // tipo de transação, linha 408
      double pl_total=0;

      if(isBorderPoint(inputParam.deals[i],type))                // ficamos sabendo se a transação é de borda e se seu tipo é de linha 301
        {
          // linha 413
        } // linha 414
      else
        {
/*
         Se a transação não for de borda, simplesmente corrigimos o resultado financeiro.
         Neste caso, só pode haver uma margem de variação e várias correções.
         Depósitos e retiradas na/da conta são filtrados quando são obtidos os dados de origem 
*/
         detales.pl_forDeal+=(inputParam.deals[i].profit+inputParam.deals[i].comission);
         detales.pl_oneLot+=inputParam.deals[i].profit/calcContracts(contracts,GET_LAST_CONTRACT);
        }
     }

// Filtramos as posições ativas e sem salvá-las
   if(isAdd && PositionSelect(inputParam.symbol))                 // linha 541
     {
      if(PositionGetInteger(POSITION_IDENTIFIER)==inputParam.ID)
         isAdd=false; 
     }                                                            // linha 546

// Armazenamos as posições fixas e as mais inativas
   if(isAdd)
     {
      detales.price_in=MA_price(price_In,lot_In);
      detales.price_out=MA_price(price_Out,lot_Out);

      detales.volume=calcContracts(contracts,GET_REAL_CONTRACT);
      addArr(ans,detales);
     }
  }

Na linha 404 da função são declaradas matrizes. No futuro, elas serão usadas na condição indicada pelas linas 411 ou 414. Dentro dessa condição, são considerados apenas os pontos de borda (transações que levam à abertura/fechamento ou conclusão/fechamento parcial da posição examinada).

Se a transação não estiver na primeira condição, a única ação necessária será calcular seu lucro/perda. Na verdade, o histórico que transmitimos contém lucros/perdas acumulados, divididos em transações. Cada transação reflete uma mudança no PL, a partir da última transação até à conclusão da analisada. O lucro total da posição é igual à soma do PL de todas essas transações. Se considerarmos o lucro/perda simplesmente como a diferença entre os preços de abertura e de fechamento, perderemos vários fatores: derrapagem, alteração no volume dentro da posição, comissões e taxas.

Da linha 541 à linha 546 do código, ocorre a filtragem das posições abertas que não são salvamos como resultado. Ao final da função, são calculados os preços de abertura e de fechamento, bem como o volume máximo da posição no mercado.

A variável type serve para filtrar os tipos de pontos de borda. Se no momento atual houver uma abertura ou uma transação pré-definida, deveremos atender a condição a partir da linha 413 (veja o texto completo do método no anexo).

if(type==UsualInput)                                                   // caso haja entrada inicial ou amento da posição
  {
   if(detales.DT_open==0)                                              // atribuímos a data de abertura de posição
     {
      detales.DT_open=inputParam.deals[i].DT;
      detales.day_open=getDay(inputParam.deals[i].DT);
     }
   detales.isLong=inputParam.deals[i].type==DEAL_TYPE_BUY;             // definimos a direção da posição
   addArr(price_In,inputParam.deals[i].price);                         // salvamos o preço de entrada 
   addArr(lot_In,inputParam.deals[i].volume);                          // armazenamos o número de lotes

   pl_total=(inputParam.deals[i].profit+inputParam.deals[i].comission);
   detales.pl_forDeal+=pl_total;                                       // lucro/perda da transação, incluindo comissão
   if(!firstPL_setted)
     {
      detales.pl_oneLot=pl_total/inputParam.deals[i].volume;           // lucro/perda da transação, levando em conta a comissão, se negociado um lote
      firstPL_setted=true;
     }
   else
      detales.pl_oneLot=inputParam.deals[i].profit/calcContracts(contracts,GET_LAST_CONTRACT);                 

   if(StringCompare(inputParam.deals[i].comment,"")!=0)                // comentários da transação
      detales.open_comment+=(StringCompare(detales.open_comment,"")==0 ?
                             inputParam.deals[i].comment :
                             (" | "+inputParam.deals[i].comment));
   addArr(contracts,inputParam.deals[i].volume);                       // adicionamos o volume da transação com sinal '+'
  }

Atribuímos a ele a data de abertura da posição e calculamos o lucro (total e de um lote). O fechamento da posição ocorre por uma condição diferente:

if(type==UsualOut || type==OutBy)                         // fechamento da posição
  {
/*
           Não salvamos imediatamente, pois pode haver várias saídas (gradualmente, uma após outra)
           Portanto, para evitar a perda de parte dos dados, basta ativar o sinalizador 
*/
   if(!isAdd)isAdd=true;                                  // sinalizador para salvar a posição 

   detales.DT_close=inputParam.deals[i].DT;               // data de fechamento
   detales.day_close=getDay(inputParam.deals[i].DT);      // dia de fechamento
   addArr(price_Out,inputParam.deals[i].price);           // armazenamos os preços de saída
   addArr(lot_Out,inputParam.deals[i].volume);            // armazenamos o volume de saída

   pl_total=(inputParam.deals[i].profit+inputParam.deals[i].comission);          // Lucro/perda da transação levando em conta a comissão
   detales.pl_forDeal+=pl_total;

   if(i==total-1)
      detales.pl_oneLot+=pl_total/calcContracts(contracts,GET_LAST_CONTRACT);    // lucro/perda da transação, levando em conta a comissão, se negociado um lote
   else
      detales.pl_oneLot+=inputParam.deals[i].profit/calcContracts(contracts,GET_LAST_CONTRACT); // lucro/perda da transação, levando em conta a comissão, se negociado um lote

   if(StringCompare(inputParam.deals[i].comment,"")!=0)                          // Comentários da transação
      detales.close_comment+=(StringCompare(detales.close_comment,"")==0 ?
                              inputParam.deals[i].comment :
                              (" | "+inputParam.deals[i].comment));
   addArr(contracts,-inputParam.deals[i].volume);                                // Adicionamos o volume da transação com sinal "-"
  }

Essa condição atribui a data de saída da posição e calcula o lucro da transação. Entradas e saídas podem ser várias e, portanto, essas condições podem ocorrer repetidamente.

A condição InOut é uma combinação da segunda e da primeira condição. Acontece só uma vez e termina com a reversão da posição.

if(type==InOut)                                                                 // Reversão da posição
  {
/*
           Parte 1:
           Armazenamos a posição anterior
*/
   firstPL_setted=true;
   double closingContract=calcContracts(contracts,GET_LAST_CONTRACT);           // contrato fechado
   double myLot=inputParam.deals[i].volume-closingContract;                     // contrato aberto

   addArr(contracts,-closingContract);                                          // adicionamos o volume da transação com sinal "-"
   detales.volume=calcContracts(contracts,GET_REAL_CONTRACT);                   // obtemos o volume de transação real máximo

   detales.DT_close=inputParam.deals[i].DT;                                     // data de fechamento
   detales.day_close=getDay(inputParam.deals[i].DT);                            // dia de fechamento
   addArr(price_Out,inputParam.deals[i].price);                                 // preço de saída
   addArr(lot_Out,closingContract);                                             // volume de saída

   pl_total=(inputParam.deals[i].profit*closingContract)/inputParam.deals[i].volume;        // calculamos o PL da transação de fechamento
   double commission_total=(inputParam.deals[i].comission*closingContract)/inputParam.deals[i].volume;
   detales.pl_forDeal+=(pl_total+commission_total);
   detales.pl_oneLot+=pl_total/closingContract;                                 // lucro/perda da transação, levando em conta a comissão, se negociado um lote
   if(StringCompare(inputParam.deals[i].comment,"")!=0)                         // salvamos o comentário de fechamento
      detales.open_comment+=(StringCompare(detales.open_comment,"")==0 ?
                             inputParam.deals[i].comment :
                             (" | "+inputParam.deals[i].comment));

   detales.price_in=MA_price(price_In,lot_In);                                  // obtemos o preço de entrada na posição (preço médio)
   detales.price_out=MA_price(price_Out,lot_Out);                               // obtemos o preço de saída da posição (preço médio)
   addArr(ans,detales);                                                         // adicionamos a posição formada
   if(isAdd)isAdd=false;                                                        // redefinimos o sinalizador se ele estiver ativado

                                                                                // Limpeza de parte de dados
   ArrayFree(price_In);
   ArrayFree(price_Out);
   ArrayFree(lot_In);
   ArrayFree(lot_Out);
   ArrayFree(contracts);
   detales.close_comment="";
   detales.open_comment="";
   detales.volume=0;

/*
           Parte 2:
           Salvamos a nova posição, excluindo alguns dados da matriz detales
*/

   addArr(contracts,myLot);                                                     // Adicionamos o lote de abertura de posição            

   pl_total=((inputParam.deals[i].profit+inputParam.deals[i].comission)*myLot)/inputParam.deals[i].volume; // Lucro/perda da transação levando em conta a comissão
   detales.pl_forDeal=pl_total;
   detales.pl_oneLot=pl_total/myLot;                                            // Lucro/perda da transação, levando em conta a comissão, se negociado um lote
   addArr(lot_In,myLot);                                                        // Adicionamos o lote de entrada

   detales.open_comment=inputParam.deals[i].comment;                            // Armazenamos o comentário

   detales.DT_open=inputParam.deals[i].DT;                                      // Armazenamos a data de abertura
   detales.day_open=getDay(inputParam.deals[i].DT);                             // Armazenamos o dia de abertura
   detales.isLong=inputParam.deals[i].type==DEAL_TYPE_BUY;                      // Definimos a direção da transação
   addArr(price_In,inputParam.deals[i].price);                                  // Armazenamos o preço de entrada
  }

O resultado dos cálculos descritos é uma tabela, onde cada linha reflete os parâmetros básicos de posição. Agora podemos realizar todos os cálculos necessários, operando já com posições e não com transações das quais eles consistem.

Instrument Open DT Open day Close DT Close day Contracts Direction Price open Price close PL for 1 lot PL for position Open comment Close comment
RTS-12.17 17.11.2017 19:53 FRIDAY 17.11.2017 19:54 FRIDAY 2.00000000 Long 113200.00000000 113180.00000000 -25.78000000 -55.56000000
RTS-12.17 17.11.2017 19:54 FRIDAY 17.11.2017 19:54 FRIDAY 2.00000000 Short 113175.00000000 113205.00000000 -58.47000000 -79.33000000
RTS-12.17 17.11.2017 19:58 FRIDAY 17.11.2017 19:58 FRIDAY 1.00000000 Short 113240.00000000 113290.00000000 -63.44000000 -63.44000000
RTS-12.17 17.11.2017 19:58 FRIDAY 17.11.2017 19:58 FRIDAY 1.00000000 Long 113290.00000000 113250.00000000 -51.56000000 -51.56000000
Si-12.17 17.11.2017 20:00 FRIDAY 17.11.2017 20:00 FRIDAY 10.00000000 Long 59464.40000000 59452.80000000 -23.86000000 -126.00000000
Si-12.17 17.11.2017 20:00 FRIDAY 17.11.2017 20:00 FRIDAY 5.00000000 Short 59453.20000000 59454.80000000 -5.08666667 -13.00000000
Si-12.17 17.11.2017 20:02 FRIDAY 17.11.2017 20:02 FRIDAY 1.00000000 Short 59460.00000000 59468.00000000 -9.00000000 -9.00000000
Si-12.17 17.11.2017 20:02 FRIDAY 17.11.2017 20:03 FRIDAY 2.00000000 Long 59469.00000000 59460.00000000 -14.50000000 -20.00000000
Si-12.17 21.11.2017 20:50 TUESDAY 21.11.2017 21:06 TUESDAY 2.00000000 Long 59467.00000000 59455.00000000 -13.00000000 -26.00000000
Si-12.17 23.11.2017 17:41 THURSDAY 21.12.2017 15:45 THURSDAY 2.00000000 Long 58736.50000000 58610.50000000 -183.00000000 -253.50000000 Open test position | Open test position PartialClose position_2 | [instrument expiration]
RTS-12.17 23.11.2017 18:07 THURSDAY 14.12.2017 14:45 THURSDAY 1.00000000 Short 115680.00000000 114110.00000000 1822.39000000 1822.39000000 Open test position_2
RTS-3.18 30.01.2018 20:22 TUESDAY 30.01.2018 20:22 TUESDAY 2.00000000 Short 127675.00000000 127710.00000000 -61.01000000 -86.68000000
RTS-3.18 30.01.2018 20:24 TUESDAY 30.01.2018 20:24 TUESDAY 1.00000000 Long 127730.00000000 127710.00000000 -26.49000000 -26.49000000
RTS-3.18 30.01.2018 20:24 TUESDAY 30.01.2018 20:25 TUESDAY 1.00000000 Long 127730.00000000 127680.00000000 -60.21000000 -60.21000000
RTS-3.18 30.01.2018 20:25 TUESDAY 30.01.2018 20:25 TUESDAY 1.00000000 Long 127690.00000000 127660.00000000 -37.72000000 -37.72000000
RTS-3.18 30.01.2018 20:25 TUESDAY 30.01.2018 20:26 TUESDAY 1.00000000 Long 127670.00000000 127640.00000000 -37.73000000 -37.73000000
RTS-3.18 30.01.2018 20:29 TUESDAY 30.01.2018 20:30 TUESDAY 1.00000000 Long 127600.00000000 127540.00000000 -71.45000000 -71.45000000

Capítulo 2. Criando um relatório de negociação personalizado

Agora criamos uma classe que gere um relatório de negociação. Definimos os requisitos para o relatório.

  1. O relatório conterá os gráficos tanto padrão de PL como os aprimorados, a fim de avaliar claramente eficácia. A plotagem de gráficos começa do zero (independentemente do capital inicial), aumentando a objetividade da avaliação.
  2. Também será gerado o gráfico 'Buy and hold' da estratégia, comparável ao gráfico de PL (ele será calculado por uma função). Ambos os gráficos serão construídos em todos os ativos presentes no relatório.
  3. Os principais coeficientes e resultados da negociação devem ser exibidos na forma de uma tabela.
  4. Devem ser construídos gráficos adicionais, por exemplo, o de PL por dias.

Em conclusão, apresentaremos os parâmetros analisados ​​na forma de gráficos variáveis ​​e carregaremos todos os resultados dos cálculos em arquivos csv. Os exemplos dados aqui são baseados no histórico real de um certo período. Este histórico é anexado no final do artigo. O script para visualizar e carregar os dados conterá duas funções, sendo que a primeira mostrará exemplos no histórico de negociações aplicado, e a segunda exibirá, por conta própria, o histórico carregado do seu terminal.

Nesta parte do artigo, haverá um mínimo de código. Em vez disso, nós nos concentraremos em examinar os dados obtidos e descrever seu significado. Todas as partes da classe que cria o relatório são comentadas em detalhes, para que você possa compreendê-las facilmente. Para uma orientação mais conveniente nas funções descritas, anexo a estrutura da classe analisada.

class CReportGetter
  {
public:
                     CReportGetter(DealDetales &history[]);                      // você só pode definir o histórico no formato preparado
                     CReportGetter(DealDetales &history[],double balance);       // você pode especificar tanto o histórico quanto o saldo, para os quais serão feitos os cálculos
                    ~CReportGetter();

   bool              get_PLChart(PL_chartData &pl[],
                                 PL_chartData &pl_forOneLot[],
                                 PL_chartData &Indicative_pl[],
                                 string &Symb[]);                                // gráfico de PL

   bool              get_BuyAndHold(PL_chartData &pl[],
                                    PL_chartData &pl_forOneLot[],
                                    PL_chartData &Indicative_pl[],
                                    string &Symb[]);                             // gráfico 'buy and hold'

   bool              get_PLHistogram(PL_chartData &pl[],
                                     PL_chartData &pl_forOneLot[],
                                     string &Symb[]);                            // histograma de acumulação de lucro e de perdas

   bool              get_PL_forDays(PLForDaysData &ans,
                                    DailyPL_calcBy calcBy,
                                    DailyPL_calcType type,
                                    string &Symb[]);                             // PL dos dias da semana

   bool              get_extremePoints(PL_And_Lose &total,
                                       PL_And_Lose &forDeal,
                                       string &Symb[]);                          // pontos extremos (os indicadores máximo e mínimo para a transação e com a acumulação) 

   bool              get_AbsolutePL(PL_And_Lose &total,
                                    PL_And_Lose &average,
                                    string &Symb[]);                             // indicadores absolutos (PL acumulados e médios)

   bool              get_PL_And_Lose_percents(PL_And_Lose &ans,string &Symb[]);  // diagrama de distribuição de transações lucrativas e não rentáveis

   bool              get_totalResults(TotalResult_struct &res,string &Symb[]);   // tabela de indicadores principais

   bool              get_PLDetales(PL_detales &ans,string &Symb[]);              // breve resumo do gráfico de PL

   void              get_Symbols(string &SymbArr[]);                             // obtenção da lista de símbolos disponíveis no histórico

private:
   DealDetales       history[];                                                  // armazena o histórico de negociação
   double            balance;                                                    // armazena o valor do saldo

   void              addArray(DealDetales &Arr[],DealDetales &value);            // adição à matriz de dados dinâmica
   void              addArray(PL_chartData &Arr[],PL_chartData &value);          // adição à matriz de dados dinâmica
   void              addArray(string &Arr[],string value);                       // adição à matriz de dados dinâmica
   void              addArray(datetime &Arr[],datetime value);                   // adição à matriz de dados dinâmica

   void              sortHistory(DealDetales &arr[],bool byClose);               // classificação do histórico por data de abertura e de fechamento

   void              cmpDay(int i,ENUM_DAY_OF_WEEK day,ENUM_DAY_OF_WEEK etaloneDay,PLDrowDown &ans);      // preenchimento da estrutura PLDrowDown

   bool              isSymb(string &Symb[],int i);                               // Verificando se há um i-ésimo símbolo - no histórico - na matriz Symb

   bool              get_PLChart_byHistory(PL_chartData &pl[],
                                           PL_chartData &pl_forOneLot[],
                                           PL_chartData &Indicative_pl[],
                                           DealDetales &historyData[]);          // Gráficos de PL do histórico transferido

   ENUM_DAY_OF_WEEK  getDay(datetime DT);                                        // Obtendo o dia de negociação a partir da data

  };

3 tipos de gráfico de PL

Para construir um gráfico de lucros e perdas, classificamos os dados iniciais até à data de fechamento da posição. Como será padrão, não vamos explicar sua plotagem. Consistirá em dois gráficos com datas de fechamento de posições no eixo X. O primeiro será o gráfico de PL, enquanto o segundo será o rebaixamento acumulado relativo ao valor máximo de PL, em porcentagem. Consideraremos 3 tipos de gráficos.

  1. Gráfico padrão do lucro acumulado.
  2. Gráfico de lucro acumulado sem considerar o volume de negociação — como se operássemos um lote em todo o histórico e em todos os instrumentos.
  3. Gráfico de lucros e perdas normalizado para o lote com lucro/perda máximo. Se o gráfico de PL estiver na zona vermelha, dividimos a i-ésima perda pelo lote com lucro máximo. Se o gráfico estiver na zona verde, dividimos o i-ésimo lucro pelo lote com perda máximo.

Os dois primeiros gráficos nos dão uma ideia de como a mudança no volume de negociação afeta o lucro/perda. O terceiro gráfico permite imaginar uma situação extrema em que de repente começa uma série de perdas iguais às perdas históricas máximas possíveis por lote (o que praticamente não pode acontecer). Ele mostra quantas perdas máximas podem ser mostradas consecutivamente pelo nosso gráfico de PL antes de atingir 0 (todos os lucros serão reduzidos a zero), ou vice-versa, o que acontecerá quando todas as perdas forem anuladas por uma série de lucros máximos. Abaixo estão os tipos de gráficos considerados:

Gráfico de PL real

Gráfico de PL para 1 lote

PL indicativo

Ao negociar um lote, o gráfico de PL parece muito mais atraente do que o gráfico real. Além disso, na negociação real, os lucros superaram o lote único hipotético apenas em 30%. O tamanho do lote variou de 1 a 10. O rebaixamento em porcentagem atingiu 30 000%. Parece horrível, mas não é assim tão grave. Como já dissemos, o gráfico de PL é construído a partir da marca zero e o rebaixamento é calculado em relação ao valor máximo da curva de PL. Houve um momento em que a perda ainda não atingia seu máximo (zona vermelha no gráfico), o lucro aumentava vários rublos e, depois, caia para - 18 000 rublos. Eis o motivo desses valores absurdos. Na realidade, o rebaixamento não era mais que 25% por lote e 50% — de fato.

Rebaixamento para 1 lote

Rebaixamento total

O gráfico de PL normalizado também está preso num valor. Isso indica a necessidade de mudar a abordagem de negociação (por exemplo, alterando o método de gerenciamento de lotes) e, como opção, otimizar os algoritmos de negociação. Embora o gráfico de PL considerando a mudança nos lotes pareça pior do que o gráfico de um lote para o mesmo período, o gerenciamento de posição deu frutos, pois os rebaixamentos se tornaram mais fracos.

A classe que forma o relatório de negociação é chamada CReportGetter, enquanto o método get_PLChart_byHistory constrói os gráficos. Este método é completamente comentado nos arquivos de código fonte. Ele funciona da seguinte maneira:

  1. É realizada a iteração sobre o histórico transferido.
  2. Cada posição está envolvida no cálculo do PL, do lucro máximo e do rebaixamento.
  3. O rebaixamento é calculado pela acumulação em relação ao lucro máximo alcançado. Se o gráfico de PL começa a cair imediatamente na zona vermelha, o rebaixamento é recalculado a cada atualização dos mínimos. Nesse caso, o lucro máximo será zero e o rebaixamento no i-ésimo e em todos os elementos anteriores é recalculado para a perda máxima. O rebaixamento neste momento é igual a 100%. Logo após o lucro máximo ser maior que zero (a curva de PL entra na parte positiva do gráfico), o rebaixamento é sempre calculado em relação ao lucro máximo.

Realizemos a análise, comparado os 3 gráficos. Há muito mais deficiências do que se estivéssemos considerando o gráfico padrão de lucros e perdas. Os gráficos de PL das estratégias 'buy and hold', como já mencionado, são construídos com base no mesmo algoritmo. Vou exemplificar isso apenas com um dos gráficos. Os outros serão plotados automaticamente após iniciado o script de visualização.

PL da estratégia Buy and hold

O gráfico mostra que se os mesmos ativos fossem negociados usando a estratégia Buy and hold nos mesmos volumes, o gráfico de PL dificilmente convergiria para zero, o rebaixamento seria de 80 000 rublos (em vez de 70 000 de lucro máximo alcançado).

Histograma de acumulação de PL

Este tipo de gráfico consiste em dois histogramas sobrepostos. Sua plotagem é um pouco diferente da do gráfico de PL padrão. O histograma de lucro é criado pelo acúmulo gradual de apenas transações com lucro. Se a i-ésima posição é desfavorável, ignoramos seu valor e escrevemos o valor anterior para o i-ésimo intervalo de tempo. O histograma de transações desfavoráveis é construído com base numa lógica de espelhamento. Abaixo estão os gráficos dos histogramas de lucros/perdas de nossos dados.

Histograma de PL real

Histograma de PL para 1 lote

O gráfico de PL habitual é uma diferença simples entre o histograma de lucros e o de perdas. A primeira coisa que chama a atenção ao analisar é o quanto o lucro realmente difere do lucro negociado. Se o lucro real superar o lucro da negociação de um lote em aproximadamente 30%, a análise do gráfico dos histogramas de lucro dá uma diferença de quase 50%. O segredo da perda de 20% dos lucros está na diferença entre a dinâmica do crescimento do lucro sobre as perdas no primeiro e segundo gráficos. Ilustremos isso. Plotemos nestes histogramas as dinâmicas do lucro e da perda. A fórmula é muito simples:

Fórmula do Fator de Lucro

Dinâmica do Fator de Lucro

Dinâmica Real do Fator de Lucro

Vemos que a dinâmica do lucro aumentou em comparação com as perdas no começo. Este é um cenário ideal, pois a cada nova transação, nosso histograma de lucro é cada vez mais destacado do histograma de transações desfavoráveis. Adicionalmente, se, durante toda a negociação, a dinâmica do crescimento dos lucros sobre as perdas tiver uma inclinação positiva, obteremos um dos melhores resultados. O lucro se acumula cada vez mais rapidamente, enquanto as perdas são adicionadas com a mesma ou menor velocidade em relação ao lucro.

O seguinte trecho de gráficos mostra que o histograma de lucro parou de aumentar a diferença porcentual do histograma de perdas, mas foi interrompida no movimento lateral. Também é um bom sinal quando o movimento lateral do aumento dos lucros sobre perdas tem qualquer intervalo maior do que um. Se o intervalo for um, a curva de PL será reduzida a zero (cada novo lucro será igual a cada nova perda). Se o intervalo de interrupção do nosso histograma for menor que 1, estaremos perante uma perda gradual. Em outras palavras, os gráficos considerados são gráficos de uma mudança gradual no fator lucro. Ilustremos isso:

Histograma de PL dependendo do 'Profit factor'

Gráfico de PL dependendo do 'Profit factor'

Aqui o histograma azul é um adicionamento linear de perda (na verdade, é um gráfico de 0 a 100).

  • Linha verde — histograma de perdas * 1,3,
  • Linha cinza — histograma de perdas * 1,
  • Linha vermelha — histograma de perdas * 0,7.

Os gráficos mostram que o fator de lucro deve sempre ser maior que um. O ideal é se ele crescer gradualmente. Nesse caso, o gráfico de PL crescerá exponencialmente. Infelizmente, embora isso não seja realista a longo prazo, pode acontecer num curto período de sorte, se for aumentado o lote após cada transação lucrativa. Este exemplo permite afirmar com 100% de confiança que a principal coisa na negociação não é como o gráfico de PL muda, mas, sim, como o histograma de transações lucrativas e não rentáveis se comporta e, consequentemente, qual é a dinâmica da mudança do fator de lucro.

Agora vamos falar sobre os 20% de lucros perdidos. No gráfico da dinâmica de crescimento do lucro por lote, vemos que o crescimento nos lucros foi no nível de [1,4 — 1,6]. Isso é maior que [1.2 — 1.4]. Com o tempo, essa diferença devastou 20% do lucro potencial. Em outras palavras, o gerenciamento do tamanho das posições não foi em vão. Se você usar métodos como martingale/antimartingale, esses gráficos - e especialmente os indicadores calculados com base neles - podem fornecer muitas informações úteis para análise. Estes gráficos são construídos pela função get_PLHistogram. 

Lucro e perda por dia

Qualquer gráfico semelhante é familiar a todos que já testaram pelo menos uma vez robôs no testador do terminal. O intuito e a metodologia de construção deste histograma permanecem inalterados, mas acrescentei a possibilidade de construí-lo de acordo com dados "absolutos" (obtidos pelo somatório simples de lucros e perdas por dias) e os dados médios. Na classe anexada, é usada a média pelo método da média simples. Mas outros métodos de cálculo de média (moda/mediana, média ponderada) podem dar resultados mais interessantes. Se quiser, você pode implementar esses tipos de média por conta própria.

Além disso, adicionei a soma de transações (positivas e negativas), pois ela mostra o número de transações por dia. Uma análise conjunta deste histograma e do histograma de PL diário dá uma imagem mais precisa da negociação semanal. Por exemplo, ajudará a rastrear o dia em que são obtidas 50 pequenas perdas e 3 lucros que cobrirão mais do que todas as perdas. Vou exemplificar com um gráfico construído sobre dados absolutos e sobre os preços de fechamento das posições.

PL por dia

Número diário de transações

Como pode ser visto no gráfico de todos os dias da semana, as transações positivas superaram as negativos, porém as negativas sempre foram mais (e essa é geralmente o normal para negociação algorítmica). Além disso, num dos dias não houve mais transações negativas do que positivas, mais de 25%, o que também é geralmente normal.

Gráfico de pontos extremos e indicadores absolutos

O gráfico de pontos extremos é um histograma de 4 colunas divididas em dois grupos. Os pontos extremos são os desvios máximo e mínimo no gráfico de PL (o lucro máximo alcançado e o rebaixamento acumulado máximo). As outras duas colunas são as posições com lucro e perda máximos. Ambos os grupos de dados são construídos com base em dados reais de negociação e não em dados de lucros e perdas para uma transação. O cálculo do lucro máximo na curva de PL é o ponto mais alto. Descrevi acima metodologia para calcular o rebaixamento máximo. Para mais detalhes, vou reduzir tudo a uma fórmula:

Min Max

Gráfico de pontos extremos

Este gráfico mostra qual foi a variação máxima de lucros e perdas. O rebaixamento máximo em comparação com o lucro máximo foi de 30%, enquanto a transação com perda máxima comparada com a transação com perda máxima — 60%. No código, isso é implementado como um ciclo com uma comparação gradual dos dados da curva de PL de acordo com as condições indicadas.

Quanto aos indicadores absolutos, eles são melhor representados na forma de uma tabela. Também dois grupos de dados, o primeiro é a soma de todos os lucros e a soma de todas as perdas, enquanto o segundo é os valores médios de lucros e perdas para o histórico analisado.

Geral Média
Lucro 323237 2244,701
Rebaixamento 261534 1210,806

Tabelas com um breve resumo do gráfico de PL e os principais indicadores dos resultados da negociação

Essas tabelas apresentam o desempenho em números. Programaticamente eles são criados como as seguintes estruturas:

//+------------------------------------------------------------------+
//| Estrutura dos resultados da negociação                           |
//+------------------------------------------------------------------+
struct TotalResult_struct
  {
   double            PL;                                // Lucro ou perda total
   double            PL_to_Balance;                     // Relação entre o PL e o saldo atual
   double            averagePL;                         // Lucro ou perda média
   double            averagePL_to_Balance;              // Relação entre o averagePL e o saldo atual
   double            profitFactor;                      // Fator de lucro
   double            reciveryFactor;                    // Fator de recuperação
   double            winCoef                            // Coeficiente de ganho
   double            dealsInARow_to_reducePLTo_zerro;/* Se é o lucro agora - transações seguidas com a perda máxima por trade
                                                        para reduzir o Lucro atual da conta a zero.
                                                        Se é a perda agora - transações seguidas com a lucro máximo por trade
                                                        para reduzir a perda a zero */

   double            maxDrowDown_byPL;                  // Rebaixamento máximo para o PL
   double            maxDrowDown_forDeal;               // Rebaixamento máximo da transação
   double            maxDrowDown_inPercents;            // Rebaixamento máximo para o PL em porcentagem para o saldo atual
   datetime          maxDrowDown_byPL_DT;               // Data do rebaixamento máximo para o PL
   datetime          maxDrowDown_forDeal_DT             // Data do rebaixamento máximo da transação

   double            maxProfit_byPL;                    // Lucro máximo para o PL
   double            maxProfit_forDeal;                 // Lucro máximo da transação
   double            maxProfit_inPercents;              // Lucro máximo para o PL em porcentagem para o saldo atual
   datetime          maxProfit_byPL_DT;                 // Data do lucro máximo para o PL
   datetime          maxProfit_forDeal_DT;              // Data do lucro máximo da transação
  };
//+------------------------------------------------------------------+
//| Часть структуры PL_detales (declarada abaixo)                    |
//+------------------------------------------------------------------+
struct PL_detales_item
  {
   int               orders;                            // número de transações
   double            orders_in_Percent;                 // número de ordens em % para o número total de ordens
   int               dealsInARow;                       // transações seguidas
   double            totalResult;                       // resultado total na moeda de depósito
   double            averageResult;                     // resultado médio na moeda de depósito
  };
//+-------------------------------------------------------------------+
//| Breve resumo do gráfico de PL dividido em 2 blocos fundamentais   |
//+-------------------------------------------------------------------+
struct PL_detales
  {
   PL_detales_item   profits;                           // Informações sobre transações lucrativas
   PL_detales_item   loses;                             // Informações sobre transações desfavoráveis
  };

A primeira estrutura — TotalResult_struct — é um resmo dos principais indicadores para todo o histórico de negociação solicitado. Ela inclui indicadores necessários (lucro e perda por transação, etc.) e os coeficientes calculados de eficácia de negociação.

A segunda e terceira estruturas estão inextricavelmente ligadas. A principal delas — PL_detales — contém um breve resumo do histograma de lucros e perdas. De acordo com o histórico analisado por nós, foram obtidos os seguintes indicadores:

Indicador Valor
PL 65039
PL para o saldo 21,8986532
PL médio 180,1634349
PL médio para o saldo 0,06066109
Fator de lucro 1,25097242
Fator de recuperação 3,0838154
Coeficiente de ganho 1,87645863
Transações antes de reduzir o PL a zero 24,16908213
Rebaixamento para o PL -23683
Rebaixamento por transação com 1 lote -2691
Rebaixamento para o saldo -0,07974074
Data de rebaixamento para o PL 24.07.2017 13:04
Data de rebaixamento por transação com 1 lote 31.01.2017 21:53
Lucro para o PL 73034
Lucro por transação com 1 lote 11266
Lucro para o saldo 0,24590572
Data de lucro para o PL 27.06.2017 23:42
Data de lucro por transação com 1 lote 14.12.2016 12:51

Esta é a segunda tabela:

Lucros Perdas
Resultado médio 2244,701 -1210,81
Transações seguidas 5 10
Total de transações 144 216
Transações em % 0,398892 0,598338
Resultado geral: 323237 -261534

A distribuição de posições lucrativas e desfavoráveis pode ser representada na forma de um gráfico de pizza:

% de lucros e de rebaixamentos

Como pode ser visto no resultado, 40% das transações foram positivas.

Fim do artigo

Não é nenhum segredo que, ao negociar com algoritmos, não basta apenas otimizá-los e executá-los. Uma inicialização precipitada do algoritmo logo após a otimização sem dividir a estrutura do portfólio pode levar a resultados mais do que inesperados. É claro que está na natureza do trader arriscar, porém, para aqueles que ainda preferem ir um passo à frente, o método de divisão de histórico usado no artigo fornece um recurso interessante - verificar se a seleção dos pesos atribuídos a cada algoritmo de negociação é ótima. 

Será especialmente interessante analisar diferentes métodos de cálculo do portfólio e testá-los no histórico de negociação, comparando o resultado de trading no portfólio recém-formado e o resultado do trading real. Adicionalmente, a negociação com um lote calculada no primeiro capítulo do artigo bastará para cumprir o nosso propósito. Porém, devemos lembrar que os dados obtidos nesses cálculos serão apenas aproximados, uma vez que não consideram liquidez de ativos, comissões e contratempos.


Os seguintes arquivos estão anexados ao artigo:

  1. Arquivo CSV dealHistory.csv com o histórico do leilão (localizado na pasta MQL5\Files\article_4803) com exemplos usados neste artigo.
  2. Arquivos de origem para o MetaTrader 5 localizados na pasta MQL5\Scripts\Get_TradigHistory
  • Projeto de script de teste dividido nas partes descritas abaixo
Arquivos auxiliares:
  • Arquivo ListingFilesDirectory.mqh: biblioteca de funções WinAPI para manipular arquivos em todo o computador.
  • Arquivos Tests.mqh e Tests.mq5: eles salvam o relatório obtido num arquivo.
  • Arquivo PlotCharts.mqh: ele cria gráficos para exibir os relatórios obtidos no próprio terminal.
  • Script de teste Get_TradingHistory.mq5 com duas 2 funções:
    1. A “test_1” toma como parâmetro o caminho para o arquivo com o histórico de testes anexado e, como segundo parâmetro, o caminho para a pasta com os resultados. Essa função gera um relatório sobre os arquivos de teste passados para o primeiro parâmetro da função.
    2. A “test_2” toma como parâmetro apenas o caminho para a segunda pasta (os caminhos devem ser diferentes), onde será salvo seu relatório de negociação.

Arquivos principais:

  • Arquivo DealHistoryGetter.mqh: contém as funções que implementam o mecanismo de carregamento de dados descrito no primeiro capítulo deste artigo.
  • Arquivo ReportGetter.mqh: implementa o mecanismo para criar os próprios relatórios de negociação. A classe contida neste arquivo toma o histórico gerado e o processa.


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

Arquivos anexados |
MQL5.zip (266.19 KB)
950 sites transmitindo o calendário econômico da MetaQuotes 950 sites transmitindo o calendário econômico da MetaQuotes
A adição do widget fornece os sites com um cronograma detalhado de 500 indicadores das maiores economias do mundo. Assim, além do conteúdo principal do site, os traders recebem rapidamente informações atualizadas sobre todos os eventos importantes com explicações e gráficos.
14 000 robôs de negociação no Mercado MetaTrader 14 000 robôs de negociação no Mercado MetaTrader
A maior loja de aplicativos prontos para algotrading já possui 13 970 produtos — entre eles 4 800 robôs, 6 500 indicadores, 2.400 utilitários e outras soluções. Quase metade dos aplicativos (6 000) não podem ser comprados, mas, sim, alugados. Um quarto dos produtos (3 800) é totalmente gratuito.
Redes Neurais Profundas (Parte VII). Ensemble de redes neurais: stacking Redes Neurais Profundas (Parte VII). Ensemble de redes neurais: stacking
Nós continuamos a construir os ensembles. Desta vez, o bagging de ensemble criado anteriormente será complementado com um combinador treinável — uma rede neural profunda. Uma rede neural combina as 7 melhores saídas ensemble após a poda. A segunda obtém todas as 500 saídas do ensemble como entrada, realizando a poda e combinando elas. As redes neurais serão construídas usando o pacote keras/TensorFlow para Python. Os recursos do pacote serão brevemente considerados. Serão realizados os testes e a comparação da qualidade de classificação do bagging e stacking de ensembles.
Teste de padrões de pares de moedas: Aplicação prática e perspectivas reais de negociação. Parte IV Teste de padrões de pares de moedas: Aplicação prática e perspectivas reais de negociação. Parte IV
Este artigo conclui a série dedicada à negociação de cestas de pares de moedas. Aqui nós testamos o padrão restante e discutimos a aplicação de todo o método na negociação real. Serão considerados as entradas e saídas no mercado, busca e análise de padrões e a aplicação de indicadores combinados.