English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
O jogador de negociação baseado no histórico de acordo

O jogador de negociação baseado no histórico de acordo

MetaTrader 5Negociação | 12 fevereiro 2014, 13:26
1 801 0
Mykola Demko
Mykola Demko

Ver uma vez é melhor que ouvir duas

A análise visual do histórico de comércio é uma parte significativa do trabalho analítico de um comerciante. Se não fosse assim, não haveria análise técnica que fizesse o mundo dos números girar no mundo das imagens. Bem, é evidente já que 80% da percepção humana é feita com os olhos. Estatísticas que generalizam as informações, não mostram muitas nuances. E somente a visualização com a sua percepção intuitiva do mundo dos dígitos pode cruzar os Ts. Eles dizem, ver uma vez é melhor que ouvir duas.

Neste artigo, não vamos considerar como criar um Expert Advisor destinado a automação de exibição visual do histórico de comércio. Vamos discutir as questões de passagem de informações entre objetos, planejamento de grandes aplicações, gerenciamento de gráficos, sincronização de informações de símbolos diferentes, etc.

Tradicionalmente, primeiramente quero lhe falar sobre os benefícios da aplicação de jogador e script associado e então moveremos para a análise de código.


Executando acordos no testador de estratégia do MetaTrader 5

A operação do jogador é baseada no relatório HTML do MetaTrader 5. Então, o histórico do campeonato de comércio automatizado de 2010 pode ser obtido se registrando em uma conta necessária do ATC-2010 e salvando o histórico de comércio como um relatório HTML.

Já que o servidor do Campeonato de comércio automatizado de 2008 está suspenso, não podemos fazê-lo da mesma maneira. O site contém o relatório geral de todos os competidores na forma de um arquivo zip único: Automated_Trading_Championship_2008_All_Trades.zip.

O arquivo "Automated Trading Championship 2008 All Trades.zip" deve ser descompactado na pasta \Files do diretório de instalação do MetaTrader 5.

Para analisar o histórico do Campeonato de comércio automatizado de 2008 você precisa executar o script Report Parser MT4 que analisará o histórico, fazer uma seleção para o login especificado e salvá-lo em um arquivo binário. Este arquivo binário é lido pelo Expert Advisor Player Report.

O Player Report EA deve ser executado no testador de estratégia com o login especificado necessário. Quando o testador tiver terminado, salve o relatório no formato HTML. O login especificado não afeta o resultado do teste, mas ele será exibido no relatório como o parâmetro de entrada "login". Ele permite um discernimento futuro dos relatórios. Já que os relatórios são criados pelo mesmo Expert Advisor, é recomendado dar nomes a eles que sejam diferentes do padrão.

O script Report Parser MT4 também tem o parâmetro de entrada "login" onde você deve especificar o login de um competidor o qual você quer ver o histórico. Se você não souber o login de um competidor mas você sabe o apelido, inicie o script com o valor zero (padrão) do login. Neste caso, o script não fará uma seleção pelo login; ele somente criará um arquivo csv onde todos os logins estão listados em ordem alfabética. O nome do arquivo é "Automated Trading Championship 2008 All Trades_plus". Assim que você achar o participante necessário neste arquivo, execute o script mais uma vez com o login especificado.

Assim o tandem do script do Report Parser MT4 e o Player Report EA criam o relatório HTML padrão do Testador de estratégia do MetaTrader 5 na base do histórico de comércio do formato do MetaTrader 4.

O Expert Advisor do Player Report não executa as transações exatamente como elas foram executadas na realidade, ele o faz de maneira aproximada. As razões para isso são as cotas diferentes, arredondamento do tempo para minutos no relatório e desvios durante a execução. Na maioria dos casos a diferença em vários pontos acontece em 10% das transações. Mas é suficiente diminuir o lucro no testador de estratégia de ~170 milhares a ~160 milhares, por exemplo. Tudo depende do volume de acordos com desvio.


Operação do jogador

Como eu mencionei previamente, o jogador pode ser usado para a visualização do histórico de transações do Campeonato de comércio automatizado de 2008 usando aplicações adicionais e do Campeonato de comércio automatizado de 2010 pode ser visualizado diretamente.

Além disso, o jogador suporta todos os relatórios do MetaTrader 5, assim você visualizar o histórico de comércio de qualquer execução do Expert Advisor no testador de estratégia ou histórico de transações manuais que não são formatados pelo testador, mas salvos como um relatório a partir da aba "History" da janela "Toolbox".

Parâmetros do Expert Advisor do Player History Trades exp v5:

Um relatório do testador de estratégia do MetaTrader 5 é usado como arquivo de entrada para o jogador do histórico de acordos. Ele é o nome de um relatório que deve ser especificado como parâmetro de entrada do Player History Trades exp v5 EA "nome do arquivo html do relatório do testador de estratégia". Quando iniciar o jogador, um usuário pode especificar um período de jogo nas variáveis de entrada "início do histórico" e "fim do histórico".

Se essas variáveis não forem ajustadas, o jogador as pegará do histórico de acordos iniciando do primeiro acordo e terminando no último acordo. O número de símbolos usados para o acordo não faz diferença. Somente o momento do primeiro e da último acordo na conta é considerado.

Além disso, um usuário pode ajustar os nomes dos símbolos cujos gráficos devem ser analisados. Os nomes devem ser especificados como uma enumeração na variável "lista de gráficos requeridos". A análise desta variável não é sensível ao caso e tipo do separador. Se a variável não for ajustada, todos os símbolos negociados na conta serão abertos. E, às vezes, eles são vários.

Por exemplo, Manov usou 12 pares de moedas em sua transação. Eu recomendo não usar mais de quatro símbolos por vez. Primeiramente, é conveniente organizá-los; em segundo lugar muitos gráficos diminuem a velocidade do jogo. Já que cada símbolo é processado no loop geral, o aumento no número de símbolos leva à desaceleração da geração de ticks.

O jogador também trabalhará mesmo que você especifique um símbolo que não foi usado na transação. Neste caso, o gráfico não exibirá quaisquer acordos ele será como os outros gráficos. Além disso, ele terá o indicador de equilíbrio anexado; entretanto, ele exibirá somente o histórico do equilíbrio geral em quaisquer das suas variantes.

Eu pulei, intencionalmente, a descrição do parâmetro "Excluir gráfico quando excluir o EA". Ele se relaciona com o comportamento do Expert Advisor e não com o seu gerenciamento. O ponto é que o Expert Advisor analisa muitas informações para a sua operação. Eu decidi que algumas informações que o EA tem serão úteis para a análise na forma dos arquivos. O Expert Advisor cria arquivos csv que contém operações de comércio para cada símbolo e o arquivo com equilíbrios de todos os símbolos, que podem ser úteis para a detecção de um símbolo em uma cesta com várias moedas.

A mesma variável é também usada para a exclusão de gráficos abertos automaticamente pelo Expert Advisor. Tradicionalmente, o EA deve limpar o seu local de trabalho no fim da operação. Mas se um usuário quiser analisar com atenção um gráfico sem o controle EA, ele deve iniciar o EA com o parâmetro "excluir gráficos quando excluir o EA" ajustado para 'falso'.

Os seguintes parâmetros não são tão importantes.

O período do gerador ajusta o parâmetro inicial do gerador de tick. O termo "tick" aqui não é usado com o seu significado clássico; ele significa a variação de nível. No Expert Advisor os ticks são gerados de acordo com quatro pontos de barras. O parâmetro "Período do gerador" ajusta o estado inicial do gerador. Futuramente, você poderá modificar este parâmetro no jogador enquanto ele trabalha.

Porque não geramos todos os períodos começando do M1? Porquê precisamos modificar o período do gerador? O problema é que as barras com timeframes maiores contém muitas barras M1, então, nós podemos precisar aumentar a velocidade do processo de geração. Por isso a possibilidade de modificação do período é implementada. Nem todos os timeframes são implementados no gerador, somente alguns. A maneira de mudá-lo no código será descrita mais tarde.

O parâmetro "fonte dos comentários de acordos" pode ser útil, por exemplo, quando os comentários de acordos impedem a visualização dos próprios acordos. Se você ajustar o tamanho para 1, a inscrição parecerá uma linha fina e não impedirá a visualização. Assim você poderá ver o volume do acordo e posição na aba "Lista de objetos" conforme você descobre o nome do objeto na descrição.

O histórico de acordos é desenhado com transações diferentes mas a linha desenhada depende do tipo de posição.

Usando "cor das operações de compra" e "cor das operações de venda" você pode ajustar as cores que quiser.

Exibição do histórico de transações

Na captura de tela acima, você pode ver que o nível de posição normalmente difere do nível de acordo.

Mas o lucro é calculado com base no nível da posição. Então eu decidi exibir a posição com uma linha de tendência e conectar os níveis de posição e acordo usando uma linha vertical. O comentário perto do nível de posição exibe a seguinte informação:

[deal volume|position volume]

Se o tipo de acordo não corresponde com o tipo de posição (por exemplo, há um fechamento parcial) então os volumes serão exibidos com sinais adicionais:

[<deal volume>|position volume]

Em primeiro lugar você pode ver o volume de acordo conforme ele é exibido no relatório de acordos; o volume de posição é calculado com base no estado anterior de posição e as modificações feitas pelo acordo.

O parâmetro "número de velocidades" regula o número de passos de diminuição da velocidade de playback. O jogador é iniciado com velocidade máxima. Depois, você poderá diminuir e aumentar a velocidade dentro do valor do parâmetro "número de velocidades". Então, o botão velocidade e período do gerador compõem uma gama total de ferramentas para o gerenciamento da velocidade de playback do histórico de transação.

E o último parâmetro é o "tamanho vertical do botão de progresso". Eu o fiz para usuários que preferem botões grandes da barra de progresso. Geralmente o meu alvo era evitar esconder o gráfico atrás dos controles. Por isso, o parâmetro "tamanho vertical do botão de progresso" é ajustado em 8.

Agora vamos mover os controles do jogador.

Controles do jogador

A velocidade é controlada usando as setas da esquerda e direita. O modo de controle depende do estado do botão do meio (quadrado). No estado de não pressionado ele modifica a velocidade, no estado pressionado ele modifica o período do gerador.

O objeto de controle do indicador de equilíbrio é exibido como totalmente oval, mas na verdade é feito com dois botões grandes que excedem muito as bordas do seu tamanho visual. O botão esquerdo destina-se a adição e exclusão do indicador de equilíbrio do gráfico e o botão direito controla o conteúdo de dados.

No estado "All", a informação sobre o equilíbrio total da conta é exibido; o estado "Soma" é destinado a exibição de uma amostra de equilíbrio para o símbolo do gráfico que o indicador esteja executando ligado. O controle do indicador é assíncrono, que significa que o indicador pode ser executado em um gráfico e não executado em outro.

O indicador de equilíbrio

O objeto de controle do indicador de equilíbrio é a única exceção na sincronização dos gráficos; todos os outros objetos de controle são sincronizados. Em outras palavras, modificações feitas para um símbolo são feitas automaticamente para os outros símbolos.

O play/stop exibe que operação será executada assim que você pressioná-lo. Durante o playback, duas linhas pequenas são exibidas mostrando que a operação será interrompida se você pressioná-las. E vice-versa, o triângulo é exibido no estado de interrupção. Então, se você pressioná-la, o jogador iniciará a sua operação.

A linha de progresso consiste de 100 botões de controle feitos como gatilhos - se um botão for pressionado, todos os outros botões se tornam não pressionados. Já que são 100 botões, o período de playback é dividido em 100 partes. Se o número de barras não for divisível por 100, o restante será adicionado na última parte. Por isso, os ajustes incluem os parâmetros "início do histórico" e "fim do histórico". Com a modificação desses parâmetros você pode navegar pelo período necessário do histórico.

Pressionando um botão, um usuário modifica a data do gerador interno de ticks e navega a barra zero. Se não for pressionado mas o tempo interno do gerador já se moveu para fora do botão ativo, o jogador executará a ligação correspondente sozinho.

Então o objeto da "linha de progresso" é tanto o indicador do progresso quanto o controle de navegação ativo. Os objetos de controle do jogador são escondidos automaticamente e expandidos no meio do gráfico; então se você pressionar um certo botão da linha de progresso, expandirá o gráfico em tela cheia.

Agora vamos falar sobre o comportamento dos gráficos que são gerenciados pelo jogador. O jogador executa a sincronização de todos os gráficos, mas isso não significa que qualquer mudança de escala, tipo de gráfico, esquema de cor, movimentação da barra zero, etc., realizada no gráfico principal, seja repetida em outros gráficos.

As mudanças incluem a modificação do timeframe. Aqui devemos notar que o gráfico considerado como principal pelo jogador é aquele onde os controles são exibidos, não aquele com a linha de atividade azul. Normalmente é um e o mesmo gráfico, mas não é sempre assim. Para ativar um gráfico, clique nele no campo gráfico.

Há um recurso para o uso do jogador. Se dois objetos estão no mesmo campo, os botões param de funcionar. Por isso, às vezes, quando a linha de lance cruza o campo do jogador, para pressionar um botão você precisará mudar para outro gráfico ou mudar a escala vertical do gráfico.


O vídeo demonstra o playback de transação do Manov, um dos participantes do ATC 2010. Para fazer isso, eu conectei a sua conta no terminal de cliente usando os parâmetros login=630165 e a senha=MetaTrader. O relatório de transação foi salvo com o nome de ReportHistory-630165.html na pasta terminal_data_folder\MQL5\Files. Você pode baixar este fichário como um arquivo e descompactá-lo na pasta especificada.


Preparando para iniciar

  1. Para fazer tudo funcionar, faça o download do player_history_trades.zip e o descompacte na pasta terminal_data_folder/MQL5/Indicators.
  2. Abra a pasta copiada Player History Trades e compile quatro arquivos em seu diretório raiz no MetaEditor. A sequência de compilação dos arquivos não importa.
  3. Certifique-se que o período necessário de histórico para todos os símbolos na transação esteja disponível no timeframe M1. Para fazer isso, abra manualmente o gráfico necessário com o timeframe M1, coloque uma linha vertical e abra a Lista de objetos usando a combinação de teclas Ctrl+B do menu de contexto. Então, troque a data da linha vertical para a data de início da transação.
  4. Então, pressione o botão "Show". Se não houver cotas, pode ser por duas razões. Ou elas não foram baixadas ou o parâmetro "Max bars in chart" é muito pequeno. Para consultar vá para Tools->Options->Charts.

Agora tudo deve funcionar.


Início do desenvolvimento

Para desenvolver uma aplicação, você deve ter um plano, que futuramente se transforma em um diagrama de bloco conforme você o estuda e, então, se transforma no código. Mas o próprio projeto começa mais cedo. O ponto de partida de qualquer projeto são as propriedades da aplicação requeridas pelo usuário. Então, quais propriedades o jogador do histórico de transação deve ter?

  1. Sendo de moeda múltipla;
  2. Abertura automática dos gráficos necessários;
  3. Interface de navegação conveniente e uma possibilidade de rolar o histórico em ambas as direções;
  4. Exibição simultânea em todos os gráficos;
  5. Start/pause do playback;
  6. Uma possibilidade de escolher (e o modo padrão) o número de símbolos dos gráficos que são exibidos;
  7. Uma possibilidade de escolher (e o modo padrão) um período em que o jogador trabalhará;
  8. Exibição do histórico de acordos em um gráfico;
  9. Exibição do histórico de equilíbrio e equidade;
  10. Exibição separada de equilíbrio (equidade) de um símbolo e o equilíbrio total (equidade) de uma conta.

Os primeiros quatro itens determinam o conceito geral. As outras propriedades determinam a direção da implementação dos métodos.

O plano geral de operação do jogador:

  1. Carregar um relatório HTML;
  2. Analisá-lo em acordos e restaure o histórico de posições;
  3. Preparar acordos como uma fila de ordens para abertura/fechamento;
  4. Com um comando do usuário, iniciar a exibição das dinâmicas de histórico de acordo com cálculo das taxas necessárias na forma de indicadores (gráficos de equidade, redução, etc.);
  5. Organizar a exibição de um painel de informação no gráfico com as outras taxas.

Além disso, um Expert Advisor especial é necessário para a transação no testador de estratégia de acordo com um relatório do MetaTrader 4:

  1. Os acordos analisados devem ser escritos como um arquivo de dados binários para o Expert Advisor;
  2. Crie o relatório do testador de estratégia do MEtaTrader 5.

Este é o esquema geral para o início do desenvolvimento, uma especificação de requerimentos. Se você tiver isso, você pode planejar a escrita do código de cima para baixo, do conceito para a implementação da funcionalidade.

Para não estender o artigo, adiante eu descreverei somente as partes mais significantes do código. Você não deve encontrar nenhum problema quando ler o código já que está bem comentado.


Pedidos e acordos

Atualmente há dois tipos de conceitos de transações. O conceito antigo que era usado no MetaTrader 4, e o usado para a ligação real e usado no MetaTrader 5, chamado conceito "netting". A descrição detalhada da diferença entre eles é fornecida no artigo Pedidos, posições, e acordos no MetaTrader 5.

Eu vou descrever somente a diferença significante. No MetaTrader 4, um pedido pode ser representado como um recipiente que guarda informações sobre hora da abertura, preço de abertura e volume de transação. E enquanto a porta do recipiente estiver aberta, ele está no estado de transação ativa. Assim que você fechar o recipiente, todas as informações dele são movidas para o histórico.

No MetaTrader 5, as posições são usadas como os recipientes. Mas a diferença significante é a ausência de histórico de posições. Há somente o histórico comum de pedidos e acordos. E, apesar do histórico conter todas as informações necessárias para a restauração do histórico de posições, você precisa gastar algum tempo na reorganização do pensamento.

Você pode descobrir a qual posição um pedido selecionado ou acordo pertence usando os identificadores ORDER_POSITION_ID e DEAL_POSITION_ID respectivamente. Assim, para converter o histórico em um formato adequado para o MetaTrader 5, eu dividirei os pedidos do histórico do MetaTrader 4 em duas transações separadas - transações de abertura e fechamento.


O analisador HTML

Para aqueles que não estão familiarizados com as gírias de computação, eu descreverei o que a palavra análise significa. Analisar significa a análise sintática (gramatical ou lexical) de um texto ou qualquer sequência de item do vocabulário (símbolos, palavras, bytes, etc.) que verifica a correspondência do texto de entrada a uma gramática específica e compõe uma árvore de análise, de acordo com a qual você pode executar cálculos futuramente ou transformações.

Duas classes grandes de CTable e CHTML são usadas no analisador. O uso da classe CTable é descrito em detalhes no artigo Tabelas eletrônicas no MQL5 por isso eu não vou descrever novamente.

Para analisar HTML, eu desenvolvi a classe CHTML. De acordo com a minha ideia principal, a sua descrição deveria ter se tornado um artigo. Mas a classe é muito simples para a criação de um artigo, então eu darei uma descrição concisa dela.

O conceito geral da classe pode ser descrito pelo termo 'tag'. Uma tag pode ser representada como uma função com delimitações. Por exemplo, Tag (cabeçalho, caixa), onde 'cabeçalho' é o título da tag (as variáveis da tag que controlam a aparência da página são normalmente especificadas lá) e 'caixa' é o conteúdo do recipiente da tag. Tais tags consistem a linguagem HTML inteira.

A estrutura geral da classe pode ser representada como um chamado de objetos de três estágios. A instância da classe CHTML cria os objetos de todas as tags possíveis em seu corpo. As funções das tags são criadas por um modelo e elas diferem entre si somente por seus nomes e ajustes de duas flags.

Uma flag determina a presença do cabeçalho e a outra determina a presença da caixa. Estas flags permitem a apresentação de todas as tags com uma estrutura comum. Cada instância de tag cria uma instância da classe CTegs em seu corpo. Esta classe contém métodos comuns para todas as tags e ela executa operações principais de pesquisa de uma tag necessária no corpo de um documento.

Então, é assim que a chamada de três estágios se parece:

h.td.base.casket

Esta inscrição significa que o objeto 'h' chama o valor da variável da 'caixa' através do objeto 'td' (que é uma instância da tag casket) através do objeto aninhado 'base' (que é um objeto da classe CTegs).

A classe também inclui métodos de procura das tags, eles também são combinados no método público

h.td.Search(text,start);

que retorna o ponto de busca do final da tag e preenche as variáveis 'cabeçalho' e 'caixa' da tag.

As outras funções preparadas na classe não são usadas, então, não vou descrevê-las, há várias outras coisas interessantes para falar.

No final da descrição do trabalho com documentos HTML, quero mencionar que dois tipos de analisadores são usados no artigo; eles diferem somente pelo tipo de salvamento das informações obtidas do arquivo. O primeiro tipo usa o salvamento do documento inteiro em uma variável simples do tipo 'string', ele é usado no jogador. O segundo tipo usa uma análise de linha por linha do relatório. Ele é usado no script para preparar o histórico do Campeonato 2008.

Porquê eu uso duas abordagens? A questão é para a operação correta das funções da classe CTegs, a tag inteira deve ser colocada na string analisada. E isto nem sempre é possível. Por exemplo, no caso de tags como tabela, html, corpo (elas são de linhas múltiplas). Uma variável de tipo string permite a gravação (de acordo com os meus cálculos) 32750 símbolos sem os símbolos de tabulação. E com '\r' (depois de cada 32748-th símbolo) eu consegui armazenar até 2 000 000 símbolos; depois de alcançar esse valor eu parei as minhas tentativas. Provavelmente mais símbolos podem ser armazenados.

Então porquê usamos duas abordagens? O ponto é para uma análise universal do jogador que você precisa para achar a tabela apropriada. As tabelas requeridas para o relatório do testador e para o relatório do histórico de acordo estão localizadas em lugares diferentes. Para manter a versatilidade (para que o analisador entenda ambos os relatórios), eu uso o esquema de busca na tabela com a tag 'td' contendo 'deals'.

A estrutura dos relatórios do campeonato 2008 é conhecida e não há necessidade de buscar a tabela necessária. Entretanto, o documento do relatório é grande (35 MB) e o posicionamento do relatório inteiro para uma variável única pode levar muito tempo. Esta situação estipula a segunda abordagem da análise.


O jogador

10 requisitos para o jogador são descritos na seção "Início do desenvolvimento". Já que a moeda múltipla está no primeiro lugar, o Expert Advisor deve gerenciar os gráficos. Será lógico se cada gráfico for processado por um objeto separado que tenha todas as funcionalidades requeridas para que o jogador trabalhe.

Já que trabalhamos com histórico, precisamos um exemplo separado de histórico para uma operação ininterrupta ao invés de esperar que consigamos isso a qualquer tempo. Além disso, o uso repetido do mesmo histórico pode ser exagerado em comparação com mantê-lo no jogador. No final, o seguinte esquema aparece:

O esquema geral do jogador de histórico de negociação

A Programação com objeto orientado (POO) permite criar aplicações bem grandes usando sistemas de blocos. A parte desenvolvida do código do Expert Advisor pode ser previamente criada em um script, depurada e então conectada ao Expeert Advisor com uma adaptação mínima.

Tal esquema de desenvolvimento é conveniente, porque você tem certeza que o código conectado não contém erros (porque ele trabalha no script sem erros) e quaisquer bugs encontrados são os erros da adaptação. Não há vantagem quando um código é criado de baixo para cima quando você descreve tudo em um lugar como um procedimento. E um novo bug aparece em qualquer lugar da aplicação.

Então a programação de cima para baixo tem as vantagens em simplicidade e velocidade de criação de uma aplicação. Você pode perguntar "O que é simples aqui?", eu responderia com uma parábola - é difícil aprender a andar de bicicleta, mas uma vez que você aprende você nem notará o processo. Você simplesmente aproveita o passeio rápido. Uma vez que você aprende a sintaxe da OOP (POO), você tem uma grande vantagem.


Para continuar a narrativa, preciso descrever três termos da OOP: Associação, agregação e composição.

  • Associação significa uma conexão entre objetos. Agregação e Composição são casos particulares da associação;
  • Agregação indica que os objetos estão conectados com o relacionamento de "part-of". A agregação pode ser múltipla, exemplo, um objeto pode ser agregado a várias classes ou objetos;
  • Composição é uma variante estrita de agregação. Além do requerimento "part-of", esta "parte" não pode pertencer a diferentes "donos" simultaneamente e ela é excluída quando o dono for excluído.

Já que a associação inclui a agregação e a composição, durante uma análise detalhada todos os casos que não podem ser descritos como agregação ou composição são chamados de associação. Geralmente, todos os três idiomas são chamados de associação.

class Base
  {
public:
                     Base(void){};
                    ~Base(void){};
   int               a;
  };
//+------------------------------------------------------------------+

class A_Association
  {
public:
                     A_Association(void){};
                    ~A_Association(void){};
   void              Association(Base *a_){};
   // At association, data of the bound object 
   // will be available through the object pointer only in the method, 
   // where the pointer is passed.
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class A_Aggregation
  {
   Base             *a;
public:
                     A_Aggregation(void){};
                    ~A_Aggregation(void){};
   void              Aggregation(Base *a_){a=a_;};
   // At aggregation, data of the bound object 
   // will be available through the object pointer in any method of the class.
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class A_Composition
  {
   Base             *a;
public:
                     A_Composition(void){ a=new Base;};
                    ~A_Composition(void){delete a;};
   // At composition, the object becomes the class member.
  };

Há uma função no MQL5 para a passagem de um ponteiro por um parâmetro:

GetPointer(pointer)

O seu parâmetro é o ponteiro do objeto.

Por exemplo:

void OnStart()
  {
   Base a; 
   A_Association b;
   b.Association(GetPointer(a));
  }


As função chamadas no OnInit() do meu código normalmente usam associação. A composição é aplicada na classe CHTML. E eu uso agregação e composição juntas para a ligação de objetos dentro da classe CPlayer. Por exemplo, usando agregação, os objetos das classes CChartData e SBase criam um campo comum de dados para todos os objetos criados usando composição no jogador.

Visualmente pode ser representado da seguinte forma:

Ligação de dados

As classes, nas quais os objetos são criados de maneira composta na classe CPlayer, têm uma estrutura modelo com uma expansão de funcionalidade futura. O uso de modelos é descrito no artigo Uso de pseudo modelos como alternativa aos modelos C++, então eu não vou dar esta descrição detalhada aqui.

Um modelo para a classe se parece com o seguinte:

//this_is_the_start_point
//+******************************************************************+
class _XXX_
  {
private:
   long              chart_id;
   string            name;
   SBase            *s;
   CChartData       *d;
public:
   bool              state;
                     _XXX_(){state=0;};
                    ~_XXX_(){};
   void              Create(long Chart_id, SBase *base, CChartData *data);
   void              Update();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void _XXX_::Create(long Chart_id, SBase *base, CChartData *data)
  {
   chart_id=Chart_id;
   s=base; // binding data to the player structure
   d=data; // binding data to the chart structure
   name=" "+ChartSymbol(chart_id);

   if(ObjectFind(chart_id,name)<0)// if there is no object yet
     {//--- try to create the object         
      if(ObjectCreate(chart_id,name,OBJ_TREND,0,0,0,0,0))
        {//---
        }
      else
        {//--- failed to create the object, tell about it
         Print("Failed to create the object"+name+". Error code ",GetLastError());
         ResetLastError();
        }
     }       
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void _XXX_::Update()
  {
  };
//+******************************************************************+
//this_is_the_end_point

Então, eu criei as classes vazias pelo modelo, as conectei, verifiquei se elas processam todos os requisitos corretamente e somente depois disso eu comecei a preencher as classes compostas com as funcionalidades necessárias. Por isso é chamada de programação de cima para baixo. No caso de alguma falha, você sabe onde procurar a sua razão.

Agora, como o conceito geral de construção está claro, podemos proceder com o específico.

Primeiramente, vamos dar uma olhada na operação das funções do Expert Advisor Player History Trades exp v5.

A função OnInit(), como de costume, prepara as informações. Ela cria um objeto da classe CParser_Tester que analisa um relatório do testador de estratégia, obtém a lista de todos os instrumentos financeiros de negociação, processos de negócios, calcula volumes e níveis de posições e então desenha o histórico no gráfico. O último item descreve a razão pela qual o objeto não é excluído logo depois da passagem dos dados. A questão é que a informação é preparada antes dos gráficos serem abertos. E os objetos gráficos precisam de uma ID de gráfico para o desenho. Por isso o objeto da classe CParser_Tester é excluído mais tarde.

Depois, conforme temos os nomes dos símbolos usados para a negociação, a função Balance_process() é chamada, ela calcula os equilíbrios e equidades de todos os símbolos que passam por ela, assim como o equilíbrio total e equidade baseada no histórico M1.

Nesta parte, a aplicação é especialmente sensível quanto a alguma falta de informação; por isso, eu implementei a interrupção de execução do EA para o caso de um dos símbolos não ter sido baixado. Quando a aplicação para de executar, ela exibe um alerta com a informação do símbolo para quais requisitos a serem baixados.

O resultado do trabalho da função Balance_process() são arquivos binários do histórico de equilíbrio e equidade no M1 que são depois cortados nos períodos necessários pelo indicador de equilíbrio. Bem, eu estou indo um pouco além, a operação do indicador de equilíbrio será descrita mais tarde.

O próximo passo de início do Expert Advisor é a seleção dos símbolos. Neste momento, analisamos o parâmetro de entrada "lista de gráficos necessários"; neste caso, o símbolo necessário está na lista "Market Watch", adicione-a no banco de dados de símbolos. Desta maneira, nos protegemos de "tolos", como um usuário pode especificar um abracadabra em vez de um nome de símbolo ou imprimir errado.

Como temos a lista de símbolos verificada requisitada por um usuário para a abertura, podemos abrir os gráficos. Isto é feito com a seguinte função:

ChartOpen(symbol,period)

Esta função abre um gráfico com o símbolo e o período passando por ele nos parâmetros. Teoricamente, esta função retorna a ID do gráfico aberto mas isso não acontece sempre.

Como resultado de perder a ID, a aplicação tem falha no funcionamento. Para evitar isso, criamos duas funções:

ChartTotal(arrayID);   // get the list of charts before opening additional charts
CurrentChart(arrayID); // get the list of chart for operation

Uma função é executada antes da abertura dos gráficos e a outra é executada depois. A função ChartTotal() obtém a lista de gráficos que foram abertos antes da execução do EA (incluindo o gráfico que o EA está sendo executado) e salva as suas IDs no banco de dados de entrada.

A função CurrentChart() obtém esta informação, cria uma lista nova considerando os gráficos já abertos; então, de acordo com a diferença das listas, ela passa as IDs dos gráficos criados pelo EA para o banco de dados paramétrico. Este esquema é confiável, pois ele trabalha de acordo com o fato da abertura do gráfico.

Agora como temos as IDs dos gráficos necessários podemos tê-las sob controle. Para fazer isso, passe por todos os gráficos em um loop e usando o objeto do CParser_Tester (como você lembra, eu disse previamente que nós o precisaríamos) desenhe o histórico de negociações e crie os objetos para o gerenciamento do gráfico.

A última adição na OnInit - cria o temporizador e o chama para o trabalho. Todas as outras ações serão executadas na OnTimer().

O primeiro problema na criação do jogador aparece no estágio inicial de desenvolvimento. Este é o problema da criação de um temporizador. A função EventSetTimer(timer) permite a criação de um temporizador com uma frequência de não menos de 1 segundo. Com esta variante, os ticks seriam gerados uma vez por segundo. Mesmo com a limitação da visão humana, um segundo é muito tempo. Eu preciso de pelo menos 100 milissegundos.

Por isso, eu implementei um loop dentro do temporizador; ele existe muitos milissegundos antes do aparecimento de um novo evento do temporizador. Mas esta implementação tornou muitas soluções técnicas impossíveis. Por exemplo, a possibilidade de receber eventos desaparece, pois eles esperam constantemente pelo temporizador para sair do ciclo. E a impossibilidade de receber eventos dissolve a possibilidade de posicionar os objetos dos jogadores no indicador e executar cálculos paralelos de todos os gráficos simultaneamente. Mas mesmo com o processamento consequente dos gráficos o Expert Advisor funciona bem rápido.

O evento da ativação do gráfico é substituído com a classe de composição CClick, em que os objetos criam um sinal de modificação do gráfico ativo sendo processado no ciclo da função Click(n). A função Click() é um gatilho que rastreia modificações do botão de ativação do gráfico. Se ela detectar que o botão é pressionado, ela comuta todos os outros objetos para o estado passivo. O botão de ativação do gráfico está sempre perto do usuário, mas não está visível, pois ele tem o tamanho do gráfico inteiro, ele é colorido como o fundo e está no fundo. Quando um gráfico é ativado, o botão é movido para trás das bordas visíveis do gráfico, que permite ver os objetos gráficos dos controles do jogador, que são escondidos pelo botão da ativação do gráfico no modo passivo.

Mais tarde, conforme detectamos o gráfico principal usando a função Click(), vai para o cálculo do movimento de tempo, chama a função Progress(Time) do jogador ativo. Esta função executa os seguintes cálculos: verifica se um usuário executa as funções de navegação - se não, verifica se é hora de ir para a próxima barra; se for o momento então ela verifica se o progresso deve ser movido para a próxima seção ou não.

No final, como saímos da função Progress(), o ciclo tem as informações sobre o tempo atual, que são usadas para cálculos futuros. Então os ajustes do gráfico ativo são copiados para os gráficos escravos. Isto é feito usando um loop na função CopyPlayer(n). Depois disso, na função Play(Time) vá para a execução de todas as modificações que devem ser feitas no gráfico para fazer um usuário pensar que o tempo se move, as cotas vêm e a negociação é executada.


Classes compostas do jogador.

  1. CArrayRuler* - grava e procura informações para um movimento rápido entre as barras de uma timeframe atual;
  2. CRuler* - grava e procura informações no histórico M1 para a geração de tiques;
  3. CSpeed - controla os ajustes de velocidade e o período do gerador de tiques;
  4. CProgress - combina todos os botões de progresso em um único objeto, verifica que somente um botão seja pressionado, mudas as cores dos botões;
  5. CPlay - é encarregado de iniciar e finalizar o jogador, também controla o indicador de equilíbrio;
  6. CClick - é encarregado dos sinais de ativação do gráfico;
  7. CBackGround - o objeto esconde a barra zero dos usuários, assim como esconde barras futuras quando o gráfico muda quando o estado da borda direita está acionado;
  8. CBarDraw - desenha a barra zero dependendo da escala e tipo do gráfico (barras, castiçais ou linha);
  9. CHistoryDraw - cria uma ilusão para o usuário de que a última negociação muda em tempo real.

* - as classes não incluem objetos gráficos.

Como eu já mencionei, os objetos das classes CChartData e SBase criam um campo comum de dados para todos os objetos criados dentro do jogador usando agregação. O objeto da classe CChartData é usado para a gravação e atualização de informações sobre o gráfico assim como o seu gerenciamento. Quanto ao gerenciamento do gráfico, queremos dizer mudar o seu ajuste copiando os ajustes do gráfico principal. É assim que a sincronização de gráficos é executada. Um usuário somente faz um sinal inicial mudando os ajustes do gráfico ativo e, então, várias funções do jogador fazem o resto das operações de sincronização.

É assim que é feito:

A função CopyPlayer(n), descrita no Expert Advisor, chama a função CPlayer::Copy(CPlayer *base) em um loop passando através de associações o ponteiro para o jogador do gráfico ativo. Dentro da CPlayer::Copy(CPlayer *base), a partir do ponteiro do jogador, o ponteiro do objeto CChartData do jogador ativo é passado através de associações. Então, as informações sobre o estado do gráfico ativo são colocadas no objeto da classe CChartDATA do gráfico escravo para a cópia. Depois disso, a informação é atualizada na função CPlayer::Update(), onde todas as verificações necessárias são executadas e todos os objetos são comutados para os estados necessários.

Anteriormente, eu prometi lhe dizer como adicionar períodos na lista de períodos disponíveis do gerador. Para fazer isso, abra o arquivo incluso "Player5_1.mqh". O banco de dados estático TFarray[] está declarado no início do arquivo. Um período necessário deve ser adicionado em seu lugar na enumeração que preenche o banco de dados, e não se esqueça de mudar o tamanho do banco de dados e a variável CountTF. Depois disso, compile o Expert Advisor Player History Trades exp v5.


Os gráficos de equilíbrio e redução

O indicador de equilíbrio é gerenciado a partir do objeto da classe CPlay. Ele contém métodos de controle e botões.

Os métodos de controle do indicador são:

   Ind_Balance_Create();                 // add the indicator
   IndicatorDelete(int ind_total);     // delete the indicator
   EventIndicators(bool &prev_state);   // send an event to the indicator
   StateIndicators();                  // state of the indicator, state checks

Os métodos de adição/exclusão de trabalho dependendo do estado do botão name_on_balance. Eles usam as funções padrão do MQL5 IndicatorCreate() e ChartIndicatorDelete().

O indicador recebe um evento e executa cálculos localizados na função OnChartEvent() do indicador dependendo do código do evento. Os eventos são divididos em três tipos.

Eles são "atualizar o indicador", "calcular o equilíbrio total" e "calcular o equilíbrio para um símbolo". Então, quando enviar eventos, dependendo do estado do botão name_all_balance, o usuário controla o tipo de cálculo. Mas o próprio código do indicador não contém nenhuma análise do histórico de negociação, cálculo de posição ou recálculo de lucro. O indicador não precisa disto.

O indicador de equilíbrio é destinado a exibição de dados de histórico, então, não há razão de refazer tudo cada vez que você muda o tipo ou adiciona/remove o indicador. O indicador lê o arquivo binário dos dados calculados para o timeframe M1 e, então, dependendo do timeframe atual do gráfico, divide os dados.

Este arquivo binário é preparado pela função Balance_Process() chamada na OnInit(). Se o usuário adiciona um símbolo que não foi usado para a negociação e não há arquivo binário correspondente, então o indicador exibirá o histórico do equilíbrio total em ambas as variantes.

Agora vamos falar sobre o formato dos dados que passam pelo indicador. Para dividir a informação corretamente, não é suficiente saber quatro pontos de uma barra (Abertura, Alto, Baixo e Fechamento).

Além disso, você precisa saber qual o primeiro - alto ou baixo. Para restaurar a informação, a função Balance_Process() usa o mesmo princípio do modo "1 minuto OHLC" do testador de estratégia - se o preço de fechamento de uma barra estiver mais baixo que o preço de abertura então o segundo ponto é o máximo se não ele é o mínimo.

O mesmo esquema é usado para o terceiro ponto. Como resultado, obtemos o formato dos dados (abertura, segundo ponto, terceiro ponto, fechamento) onde tudo é consistente e definitivo. Este formato é usado para dividir o histórico M1 em cotas. E o resultado é usado para calcular o histórico de equilíbrio e equidade, dependendo da análise do histórico de negociação (no mesmo formato).


Conclusão

Concluindo, eu quero dizer que este desenvolvimento não finge ser um visualizador do testador, ainda que possamos usá-lo dessa forma. Entretanto, se as ideias implementadas nele parecerem úteis no visualizador real, eu ficarei feliz. O desenvolvimento do jogador serve como uma ajuda aos negociadores e criadores de EA com preparação para o próximo campeonato e com o trabalho duro de analisar as estratégias de negociação.

Além disso, quero dizer que a linguagem MQL5 é uma ferramenta poderosa para a programação que permite a implementação de grandes projetos. Se você ainda está lendo este artigo então você provavelmente notou que o projeto "jogador" consiste de quase 8.000 linhas de código. Eu não consigo imaginar criar tal código no MQL4 e o problema não é descrever tudo isso com procedimentos. Se houver um desenvolvimento pronto, ele pode ser refeito no estilo de procedimento. Mas desenvolver tais projetos do nada é muito difícil.

Boa sorte!

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

Arquivos anexados |
Gráficos e diagramas em HTML Gráficos e diagramas em HTML
Hoje é difícil encontrar um computador que não tenha um WEB-browser instalado. Por um longo tempo os browsers têm evoluído e melhorado. Este artigo discute o modo simples e seguro de criar gráficos e diagramas, com base nas informações obtidas a partir do terminal de cliente MetaTrader 5 para exibí-los no navegador.
Moving Mini-Max: um Novo Indicador para a Análise Técnica e sua Implementação no MQL5 Moving Mini-Max: um Novo Indicador para a Análise Técnica e sua Implementação no MQL5
No seguinte artigo, descrevo um processo de implementação do indicador Moving Mini-Max com base em um documento de Z.G.Silagadze 'Moving Mini-max: a new indicator for technical analysis'. A ideia do indicador baseia-se na simulação do fenômeno de tunelamento quântico, proposto por G. Gamov na teoria de desintegração alfa.
Implementação de Indicators como classes por exemplos de Zigzag e ATR Implementação de Indicators como classes por exemplos de Zigzag e ATR
O debate sobre uma forma ideal para calcular indicadores é infinito. Onde devemos calcular o indicador - no indicador em si ou dentro da lógica inteira no Expert Advisor que o usa? O artigo descreve uma das variáveis do movimento do código fonte de um indicador personalizado iCustom direto no código de um Expert Advisor ou script com otimização de cálculos e modelagem do valor prev_calculated.
Conectando NeuroSolutions Neuronets Conectando NeuroSolutions Neuronets
Além da criação de neuronets, o suite de software NeuroSolutions permite exportá-los como DLLs. Este artigo descreve o processo de criação de um neuronet, gerando um DLL e conectando-o a um Expert Advisor para negociação no MetaTrader 5.