English Русский 中文 Español Deutsch 日本語
MQL5 para iniciantes: Proteção antivandalismo de objetos gráficos

MQL5 para iniciantes: Proteção antivandalismo de objetos gráficos

MetaTrader 5Indicadores | 28 março 2016, 11:46
2 848 0
Dina Paches
Dina Paches

Vandalismo é uma forma depravada e destrutiva do comportamento humano,
quando os objetos de arte, cultura, pública e de propriedade
privada são destruídos ou profanados.

Wikipedia

Conteúdo:


1. Introdução

Uma das vantagens da linguagem de programação MQL5 são as funções padrões existentes MQL5, onde você pode formar códigos para completar várias tarefas e alcançar objetivos diferentes quando se usa o terminal de negociação MetaTrader 5.

Este artigo, escrito numa linguagem simples e contendo exemplos fáceis, considera duas variantes de programas a implementação da resposta a ações aos objetos gráficos do painel de controle que são apagados ou alterados. Nós vamos mostrar a você como garantir que não existam objetos sem dono presentes no gráfico, após o programa ser excluído, onde o programa pode ter perdido o controle, porque alguém ou algo os renomeou.

Exemplo de um painel de controle antes e depois das propriedades dos objetos serem alteradas manualmente

Fig. 1. Exemplo da aparência de um painel de controle antes e depois das propriedades dos objetos serem alteradas manualmente

Opções para construir as ações de resposta à interferência externa no código, descritas neste artigo, podem não serem redundantes aos casos em que, por exemplo, um programa de terceiros lançados no gráfico e não destinados diretamente a sua limpeza, usam uma função de exclusão de objetos ( objetos DeleteAll() ou uma que você mesmo criar), operando pelos parâmetros definidos nela como:

  • a remoção completa de todos os tipos de objetos gráficos na mesma janela/subjanela com objetos criados manualmente ou usando outros programas;
  • ou a remoção completa dos tipos de objetos que também estão presentes no painel de controle dos seus programas;
  • ou remoção de prefixo que correspondem ao prefixo dos objetos do seu programa.
Essas opções também são relevantes, quando é aconselhável, incluindo a operação correta do programa, providenciar ações para remoção acidental ou intencional de objetos de seu painel de controle ou alteração manual das suas propriedades no código.

Este artigo também pode ser útil para aqueles que começaram a aprender o evento de manipulação na função OnChartEvent().

Deixe-me avisá-lo de imediato que este artigo não abrange a criação de códigos rígidos e reacionários, cujos objetos foram modificados/removidos sem autorização. O principal objetivo dos programas do terminal é resolver as questões que os traders possam ter, portanto a interferência de guerra de robôs não é aceitável aqui.

Para aqueles que preferem ações revolucionárias, gostaria de fazer uma analogia antes de considerar este movimento. Imagine que um faxineiro tenha quebrado o computador de alguém com seu esfregão e pintado toda a sua mesa, acreditando que tenha feito algo lindo. Em resposta o dono dos objetos danificados joga pela janela os equipamentos, os móveis e todos os presentes no escritório juntamente com o faxineiro, esta ação será considerada como inadequada. Será improvável que uma pessoa de comportamento agressivo se beneficie com este tipo de situação.

Antes de prosseguir com exemplos de duas (entre outras) possíveis formas de reagir ao vandalismo em relação aos objetos, eu acredito que também é útil mencionar uma das proteções dos objetos primários existentes nas linguagens de programação MQL5/MQL4.


2. Um dos objetos primários de proteção no MQL5/MQL4

O acesso para criar programas de propriedades do objeto seria mais aberto, sem essa proteção.

Deixe-me explicar o que eu quis dizer. Para fornecer a proteção primária a objetos que são deletados ou modificados através de suas propriedades - nome, descrição, cores, etc., um recurso OBJPROP_HIDDEN está presente e pode ser usado de forma explícita. Ele configura ou remove um banimento, exibindo o nome de um objeto gráfico na lista de objetos a partir do menu terminal: Gráficos -> Objetos -> Lista de objetos. Por padrão, o banimento é configurado para exibição de objetos nos eventos do calendário, histórico de negociação e também objetos criados com programas MQL5.

O banimento explícito (não definido por padrão) pode ter o seguinte esquema no código:

ObjectSetInteger(chart_id,name,OBJPROP_HIDDEN,true);

Onde:

  • ObjectSetInteger é uma função que define o valor da propriedade relevante do objeto;
  • chart_id identifica o gráfico onde o objeto é colocado (zero para o gráfico atual);
  • name é o nome do objeto em que a função é aplicada a;
  • true em combinação com OBJPROP HIDDEN esconde o objeto na lista de objetos (false, cancela o ocultamento).

A aplicação prática deste esquema e coerência no código pode ser encontrada na Documentação. Ao clicar em qualquer link na lista fornecida dos tipos de objetos, você pode visualizar os exemplos de códigos prontos com funções para criar objetos.

Objetos que são banidos por exibir o nome no gráfico na lista de objeto, definidos explicitamente ou por padrão, podem ser visto pressionando o botão Todos, onde são exibidos todos os objetos neste gráfico. Ele atua como uma proteção preliminar de objetos gráficos a partir da intervenção manual em suas configurações através da Lista de objetos.

Lista de objetos no gráfico antes de pressionar o botão "Todos"

Fig. 2. Lista de objetos no gráfico antes de pressionar o botão "Todos"

Lista de objetos no gráfico depois de pressionar o botão "todos"

Fig. 3. Lista de objetos no gráfico depois de pressionar o botão "Todos"

O fato é que os objetos gráficos não podem ser completamente escondidos no gráfico, mesmo se você torná-los invisíveis, é mais uma vantagem do que uma desvantagem. Você pode rapidamente visualizar, alterar ou copiar algo nas propriedades dos objetos através da lista, sem verificar o gráfico inteiro que pode conter vários objetos e barras de anos anteriores. Além disso, os objetos na lista podem ser classificados pelo tipo, nome e alguns outros parâmetros.

Uma das muitas outras vantagens dos terminais de negociação MetaTrader 5 e MetaTrader 4 é a simplicidade em que você mesmo pode escrever assistentes automatizados para estes terminais e utilizando vários assistentes criados por outros. Mas existem as pessoas que nunca erram? Claro que não, o nível de preparação para escrever programas pode ser diferente, assim são as fronteiras éticas internas. A linguagem de programação, assim como as pessoas, pode mudar e melhorar com o tempo.

Portanto, o fato é que você pode garantir a exatidão de formar objetos gráficos com um programa a qualquer momento, é uma vantagem definitiva. Você pode encontrar rapidamente os objetos exigidos na lista e ver suas propriedades, em vez de olhar para eles durante todo o gráfico. Além disso, você pode ter certeza que o gráfico não possui quaisquer objetos por engano ou deliberadamente escondidos, incluindo os que são gerados por erros no código.

Também é muito provável que, exibindo a lista de todos os objetos e selecionando alguns deles para serem deletados, há uma chance de destruir acidentalmente os objetos do painel de controle que devem estar no gráfico nesse ponto.

Antes de prosseguir para as variantes de ações de resposta do programa, podemos tirar proveito das linguagens de programação MQL5 e MQL4 mencionados anteriormente no artigo. Refiro-me a capacidade em formar códigos que usam métodos diferentes para resolver várias tarefas.


3. Estratégia para criar variantes fornecidas aqui

Isso pode ajudá-lo, se necessário, para criar suas próprias soluções no código e salvá-lo de perder tempo na procura de soluções usando rotas erradas ou complicadas.

Para evitar soar muito formal e acadêmico, vou dizer-lhe sobre a estratégia descrita aqui do jeito que naturalmente me veio a cabeça, os códigos mencionados estão anexados no final deste artigo. Para a sua operação você precisa salvar o arquivo ObjectCreateAndSet, localizado na base de código, na pasta do seu terminal Include. Ele contém funções para a criação de objetos e para alterar suas propriedades com as verificações necessárias. Este arquivo não é necessário a operação do indicador anexa, test_types_of_chart_events.


3.1. Ações e considerações antes de decidir sobre a aplicação prática do código

De acordo com a documentação, atualmente você pode obter e processar informações sobre nove tipos de eventos necessários a seus próprios fins e tarefas, excluindo mudanças no gráfico e eventos de outros usuários, por meio da função OnChartEvent(). Alguns deles notificam sobre a remoção, mudança e criação de objetos gráficos.

Primeiramente, eu queria saber como implementar o seguinte no código:

  • A "auto recuperação" de objetos, se eles forem apagados ou alterados por alguém manualmente ou usando um programa;
  • A "saída automática" de um programa a partir do gráfico em tais ações externas.

Ambos têm de ser aplicados simultaneamente com a remoção de objetos que são "alienados" do gráfico devido a alguém ou algo que os tenham renomeados, e sem loops extras de manipulação de eventos.

A primeira ideia que surgiu foi sobre o total das revisões do teste automático do programa de sua propriedade, quando a informação sobre cada caso de remoção ou modificação de objetos é recebida, afinal não parece ser a melhor e mais prática solução disponível. Então eu pensei que era porque o total de verificações automáticas poderiam implicar em um monte de ações desnecessárias e nem sempre justificadas.

Por exemplo, durante o evento de manipulação de ações com objetos, o nome do objeto gráfico, no qual é realizado qualquer ação, é comparado com os nomes sendo monitorados pelo programa. Então em qualquer tipo de evento no programa, as informações sobre ele não vão apenas aos objetos gráficos do programa, mas a todos os objetos no gráfico. O nome do objeto tem o tipo string, e dados em sequência são processados por mais tempo do que outros tipos. O gráfico pode ter vários objetos criados manualmente ou usando outros programas que podem ter um monte de diversas ações aplicadas a eles, formando assim um grande número de eventos.

Abaixo está um esquema de aproximação para a verificação da correspondência dos nomes de objetos, quando a função de envio de notificações sobre eventos de remoção de objetos gráficos está ativada. O esquema é fornecido sem manipulação prévia para diminuir a quantidade de comparações extra, que são aplicadas quando se trabalha com vários objetos gráficos:

if(id==CHARTEVENT_OBJECT_DELETE)
        {
         string deletedChartObject=sparam;
         //---
         for(int i=QUANT_OBJ-1;i>=0;i--)
           {
            if(StringCompare(deletedChartObject,nameObj[i],true)==0)
              {
            /*existe um certo código com ações, se o nome de um assunto que
            chegou durante o evento tem acompanhado o
            nome do objeto do programa completamente*/
               return;
              }
           }
        }

Além disso, a opção "revisão" (com base nos eventos ou periódico) não resolve o problema de renomear os objetos por alguém ou alguma coisa, e, eventualmente, o programa pode perder o controle, deixando objetos "sem dono" para trás.

É por isso que eu pensei em usar e detalhar o sistema "flag". Similar ao alarme, onde uma flag de semáforo igual a 0, por exemplo, significa que ao alterar e excluir os objetos do código, deve ser tratado pelo programa como uma interferência externa não autorizada. O valor da flag altera as ações pessoais de modificação e apaga os objetos fornecidos pelo código, de modo que os eventos de exclusão de mudança não são mais percebidos como uma "invasão externa".

Os eventos de gráfico formam uma fila com sua posterior manipulação no OnChartEvent(), pois o sistema de desencadeamento das flags cria os objetos de "auto recuperação" no código, onde tais casos não foram particularmente claros naquele momento. Também, para evitar simplesmente os processamentos extras e repetidos de loops gerados, quando a manipulação dos eventos de eliminação deste tipo entra em ação.

Por exemplo, um painel constituído por apenas alguns objetos e saída de informações ativado com Print() no código de manipulação de eventos de exclusão dos objetos deste painel. Ao notificar sobre estes eventos, um painel pode facilmente preencher o arquivo log com várias notas dentro de segundos, durante a auto recuperação dos objetos, enquanto fica piscando no gráfico a partir da recriação contínua. E isto acontece apesar da seleção preliminar pelo nome, pois a flag do semáforo e auto recuperação são aplicadas utilizando o seguinte método (ou equivalente):

else  if(id==CHARTEVENT_OBJECT_DELETE)
     {
     if(flagAntivandal==0)
        {
            string deletedChartObject=sparam;
            //---
            for(int i=QUANT_OBJ-1;i>=0;i--)
              {
               //--- ações quando nomes se combinam por completo:
               if(StringCompare(deletedChartObject,nameObj[i],true)==0)
                 {
                  //--- imprime na aba "Experts" do terminal:
                  TEST_PRINT(deletedChartObject);
                  //--- desativa o "alarme" para o tempo de execução das operações dos programas "amigáveis"
                  flagAntivandal=-1;
                  //--- deleta os objetos restantes:
                  ObjectsDeleteAll(0,prefixObj,0,-1);
                  //--- recria painel
                  PanelCreate();
                 //--- redesenha gráfico 
                  ChartRedraw();
                 //--- re-ativa a flag de "proteção" de objetos:
                  flagAntivandal=0;
                  return;
                 }
              }
         return;
        }
      return;
     }//else  if(id==CHARTEVENT_OBJECT_DELETE)

Remoção e criação de objetos vai ser em loop com tal código escrito, formando um fluxo de notificações de eventos. E a remoção de outros painéis de objetos para posterior recuperação pode ser necessária, por exemplo, se a cobertura de outros objetos (botões, campos de entrada etc) são acidentalmente ou intencionalmente eliminados e precisam ser recriados com os que não se sobrepõem a outros objetos.

Se o seu computador não tem memória suficiente, então eu não recomendo usar o código mencionado acima. Se isso acontecer, então você pode testá-lo sozinho, substituindo-o completamente com a manipulação de eventos de exclusão e alterando o código de ensaio anexado no test_count_click_2.

Eu não verifiquei a capacidade máxima para o preenchimento de um arquivo log como resultado do uso de uma flag errada, porque eu fui forçado a desligar os terminais em 2-3 segundos após o início dos eventos de uma falsa "auto recuperação" no máximo, enquanto observava um "piscar" dos objetos e rápidos do programa, com contínuas notas na guia "Expert" do terminal. Eu apaguei um aumento considerável de arquivo log e esvaziei a Lixeira imediatamente.

Além disso, a solução dos casos de objetos restantes do código permaneceu desconhecido. Estava faltando uma "âncora" que poderia ajudar nesta situação.

É por isso que eu decidi refrescar minha memória com informações sobre tipos de eventos gráficos retirados da Documentação, e olhei para a descrição da função ObjectSetString(), onde diz que depois de renomear um objeto, dois eventos são formado simultaneamente:

  • remoção do objeto com o nome antigo,
  • criação de um objeto gráfico com um novo nome.

Depois de ler a seção proriedades dos Objetos, eu decidi que minha âncora seria o tempo de criação dos objetos, que pode ser determinado usando a propriedade OBJPROP_CREATETIME.

Por exemplo, como você pode ver a partir do script test_get_objprop_createtime anexado, o momento da criação do objeto é igual a hora local em um computador onde o terminais rodam:

O momento da criação do objeto é igual a hora local num computador no momento da criação de um objeto.

Fig. 4. O momento da criação do objeto é igual a hora local num computador no momento da criação de um objeto.

Um script de teste cria um botão no gráfico, determina o tempo de sua criação e imprime via Print() nas mensagens da aba Experts. Ele define e imprime vários tipos de tempo na mesma guia do terminal: a hora local do computador, o tempo atual estimado do servidor de negociação, o tempo do último servidor conhecido no momento da última cotação, hora GMT. Em seguida, ele "congela" por 10 segundos e exclui o botão que criou a partir do gráfico, encerrando a sua operação.

O próximo passo incluiu a preparação de um plano de ação para uma formulação mais clara de soluções durante a criação de código subsequente. Essas soluções finais devem ser versátil o suficiente, de modo que no futuro não haja necessidade de escrever ações de recuperação separadas por "cada botão". Eles também deve considerar o princípio da coexistência pacífica no gráfico.


O plano de ação consistiu no seguinte:

3.1.1. Verificar quais os eventos e em que ordem eles são exibidos a um programa de terceiros usado como um observador independente, e por um programa cujos objetos são afetados externamente:

  • ao alterar as propriedades de um objeto gráfico manualmente através das propriedades de diálogo e utilizando outro programa;
  • ao excluir objeto manualmente através da "Lista de objetos" e utilizando outro programa;
  • quando renomear os objetos manualmente através de seu diálogo de propriedade e utilizando outro programa.

Ao mesmo tempo é necessário exibir os tempos da criação de objetos criados e por conveniência anotar os resultados obtidos como tabelas.

Assim, o seguinte programa foi necessário para implementar o primeiro ponto do plano:

3.1.2. Um indicador assistente de programação que atua como um observador externo e descreve os eventos que ocorrem no gráfico.

Eu escolho um indicador que possa operar no gráfico com um Expert Advisor ao mesmo tempo.

Eu não vou discutir esse assistente em profundidade, o seu código completo é anexado com os arquivos test_types_of_chart_events. No entanto, uma vez que possa ser útil para você no futuro, ao trabalhar ou se familiarizar com eventos, vou mencionar aqui o seguinte:

  • Este indicador é projetado para operar com nove tipos de eventos padrão
  • Ele exibe um nome de evento e valor obtido com ele: id, lparam, dparam, sparam (nome do objeto nos eventos com objetos gráficos).
  • Notificações sobre alguns eventos pode ser ativado/desativado, portanto, use um gráfico separado para testar o indicador, que não possui um programa de negociação que opera no momento da execução do código de teste. Caso contrário, você pode desativar as notificações necessárias para o bom funcionamento dos programas.

    Propriedades externas personalizadas de um teste do indicador de observação

Fig. 5. Propriedades externas personalizadas de um teste do indicador de observação

O indicador tem notificações sobre eventos que não estão diretamente ligadas às ações com objetos, desativadas por padrão. Ele possui as notificações ativadas/desativadas para cinco dos nove tipos de eventos padrão que estão operando.

  • Além disso, este código de teste exibe informações sobre os terminais de negociação da aba Experts, usando a função Print(). Assim, evitamos inundar os arquivos que se conectam ao terminal com uma grande quantidade de dados de entrada, os eventos CHARTEVENT_MOUSE_MOVE para mover e clicar no mouse não são exibidos e estão desativados através deste indicador. A exibição das informações sobre esse tipo de evento não é fornecida no código do indicador de teste.

3.1.3. Outros assistentes participantes nos experimentos:

  • script que cria os objetos Button e Edit object no gráfico;
  • script que altera as propriedades dos objetos pelo nome nele especificado e exclui os objetos que ele mudou depois de uma pausa.


3.2. Resultados e experimentos efetuados

Os resultados são apresentados na tabela abaixo. O programa, cujos objetos estão sujeitos a ações, "visualiza" notificações padrões sobre os eventos da mesma forma que outros programas fazem no gráfico, a razão pela qual é apenas fornecida uma tabela . É baseado em ações com os objetos "Button" e "Edit object".

Ação com objeto id lparam dparam sparam Nome do evento notificando sobre as ações realizadas "manualmente" Tempo de criação do objeto ** Nome do evento notificando sobre as ações executadas por programas "amigáveis" e de terceiros
Tempo de criação do objeto **
Criação de novo objeto (não por renomeação) 7
0 0 nome do objeto CHARTEVENT_OBJECT_CREATE hora local no computador onde o terminal é executado CHARTEVENT_OBJECT_CREATE hora local no computador onde o terminal é executado
Mudar o nome de objeto (objeto com o nome anterior é excluído e um novo objeto é criado ao mesmo tempo)
6

7

8
0

0

0
0

0

0
nome do objeto,
nome do objeto,
nome do objeto
CHARTEVENT_OBJECT_DELETE

CHARTEVENT_OBJECT_CREATE

CHARTEVENT_OBJECT_CHANGE
tempo de criação do novo objeto é igual ao tempo de criação do objeto com o nome anterior sem avisos
tempo de criação do novo objeto é igual ao tempo de criação do objeto com o nome anterior
Alterar o fundo do objeto 8 0 0 nome do objeto СHARTEVENT_OBJECT_CHANGE nenhuma alteração sem avisos nenhuma alteração
Alterar as coordenadas do objeto no eixo X
8 0 0 nome do objeto СHARTEVENT_OBJECT_CHANGE nenhuma alteração sem avisos nenhuma alteração
Excluindo objeto 6 0 0 nome do objeto CHARTEVENT_OBJECT_DELETE *** CHARTEVENT_OBJECT_DELETE ***

 

Tabela 1. Fração do que um programa amigável e de terceiros pode "ver" a partir das notificações sobre eventos de criação, alteração e remoção de objetos no gráfico *


Notas:

* Enquanto uma notificação de evento é tratada no programa, outras notificações que aparecem durante ou após o manuseio, formam uma fila para serem manipuladas neste programa.

** Tenho notado que, se um gráfico contém objetos gráficos que não são recriados pelo programa após o terminal ser reiniciado, então após um reinício, a função OBJPROP CreateTime retorna o tempo de criação desses objetos igual a 1970.01.01 00:00:00. Assim, o tempo anterior é "resetado". Isso também acontece com os objetos do gráfico que são colocados manualmente. Quando você alternar períodos de tempo, o tempo de criação de "objetos não é reposto.

*** Se você tentar determinar o tempo de criação do objeto usando OBJPROP_CREATETIME durante a notificação sobre o objeto, excluindo eventos, então uma vez que o objeto já foi excluído, a mensagem de erro 4203 ( identificador incorreto de uma propriedade de objeto gráfico) será exibida.

Se as alterações de propriedade foram executadas programaticamente, não será mostrado essas notificações. Tal como o resto, obviamente isto é feito por uma razão. Porque um único gráfico do terminal pode operar com uma série de programas, incluindo os que executam várias ações com um número de objetos gráficos, podendo ter muitos gráficos abertos com estes programas. Assim suponho que as notificações para programas onde a manipulação de eventos é aplicada, cada mudança programática de propriedades de qualquer objeto gráfico levaria a um aumento considerável de consumo dos recursos.

Para evitar restringir os usuários com um conjunto padrão de tipos de notificação de eventos, os desenvolvedores nos deram oportunidade de criar notificações pessoais em quaisquer eventos específicos individuais no código. A função EventChartCustom() é usada para este finalidade e gera notificações sobre os eventos personalizados específicos a um gráfico ou todos do terminal.

De acordo com os resultados da experiência foram propostas algumas soluções versáteis.

Elas certamente não podem ser aplicadas a qualquer cenário possível. No entanto, eu acredito que são capazes de executar as suas tarefas para painéis de controle de objetos, e também, podem ser úteis ao desenvolvimento das suas próprias opções.

Estas soluções são baseadas em:

  • arranjo das flags no código, incluindo adaptativos, dependendo das tarefas;
  • a operação das funções especificamente concebidas serão estabelecidas como parte dos esquemas das variantes de construção.

O que se segue é comum para estas soluções. Independentemente do que for necessário para organizar a "proteção" de um ou todos os objetos de um painel de controle, da implementação de variantes da auto recuperação de objetos e/ou de programas de saída automática a partir do gráfico, você precisa incluir no código:

  • Variáveis para armazenar flags declaradas no escopo global do programa de visibilidade das variáveis, além de todas as funções, até o bloco da função OnInit() (não confunda com terminais de variáveis globais).
  • A criação de um prefixo sem abreviação comum para objetos "protegidos" em seus nomes.
  • Incluir as notificações para exclusão de objetos (CHART_EVENT_OBJECT_DELETE) nas propriedades dos gráficos, se essas propriedades forem desativadas.
  • Definir a frequência do timer e, consequentemente, incluir a função OnTimer() no código, onde os controles enviam notificações sobre a exclusão de objetos que serão realizados após um tempo predefinido. Um timer é parado na função OnDeinit() usando EventKillTimer().
  • Presença de blocos de manipulação de notificações sobre os eventos CHARTEVENT_OBJECT_CHANGE e CHARTEVENT_OBJECT_DELETE com ações específicas nos casos de intervenções não autorizadas, no corpo de uma função OnChartEvent() no código.

A variável para limpar o gráfico a partir dos objetos renomeados, usada para ser objeto do programa, deve ter as seguintes informações:

  • Determinar e armazenar o tempo de criação de objetos "protegidos" dentro de um array de dados se existe um grande número de objetos, ou em variáveis regulares, se houver apenas alguns objetos protegidos.
  • funções específicas para a exclusão de objetos, incluindo a remoção de objetos no momento de sua criação.

Além disso, é importante implementar ações de "segurança" no código.

Se você só construir um programa de "saída automática" a partir do gráfico, desencadeará alterações não autorizadas/exclusão de objetos no código e, em seguida, dependendo do conjunto de tarefas, uma única variável pode ser suficiente para uma flag com valores, por exemplo:
  • -1: a desativação do "alarme" antes de executar ações com os objetos no código. Além disso, definir o retorno no início de blocos de código para a remoção e alterar o manipulador neste valor da flag de evento, a fim de reduzir o manipulador desnecessário. Então, na verdade, significa que ignora as notificações recebidas sobre os eventos de exclusão e alteração de objetos, deixando os blocos manipularem a notificações sobre tais eventos neste valor de flag:
if(flagAntivandal==-1){return;}
  • 0: ativando o "alarme" para proteger objetos (todos ou parte deles). Ele é definido quando o "alarme" está habilitado após a realização de ações "amigáveis" com objetos. Ao mesmo tempo, a ordem seguinte para as operações de execução são prescritas como uma medida de "segurança" em blocos de código para a mudança de objeto ou manipulação de eventos de remoção:
    • se uma notificação sobre a eventualidade de apagar ou alterar objetos foi recebida pelo valor da flag 0, então, em primeiro lugar, é verificado que o evento ocorreu especificamente com um dos objetos "protegido";
    • se o evento ocorreu com um dos objetos "protegido", em seguida, antes de tomar novas medidas, você deve desabilitar o sinalizador "alarmes", alterando o seu valor para -1, para evitar um loop quando o código do programa é excluído do gráfico;
    • somente após estas ações preliminares é aplicada uma função para remover o programa a partir do gráfico. A função foi criada usando:
      • ChartIndicatorDelete() para um indicador. Um nome abreviado para um indicador tem de ser definido no OnInit() com antecedência:
IndicatorSetString(INDICATOR_SHORTNAME,short_name);
  • ExpertRemovе() para um Expert Advisor. Se você não estiver familiarizado com esta função ainda, então por favor, verifique a documentação para obter uma descrição e exemplo fornecido lá para sua informação.

Pelo menos duas flags serão necessárias, se você está planejando criar o objeto da "auto recuperação" após a interferência não autorizada no código, onde os objetos serão excluídos e novos criados em seu lugar. Um deles é igual à flag anterior nas suas funções. A segunda é necessária para evitar o loop do código a partir da possivel recepção de eventos de exclusão durante a ações de recuperação dos objetos. As ações serão um pouco mais complicadas aqui. A segunda fag é adaptativa e serve para um auto-ajuste automático para alcançar bom funcionamento do "alarme" e manipulação dos eventos de remoção de objetos. Ela "descarrega" a manipulação de eventos desnecessários, estarei olhando para ela mais adiante no exemplo 4.2.

Agora, tendo dito tudo isso, vamos passar para os exemplos.


4. Esquemas de variantes de execução durante a modificação ou remoção de objetos de programas não autorizados

Exemplo de implementação simultânea de uma "auto recuperação" no que diz respeito a alguns objetos e uma "saída automática" do gráfico quando altera/exclui outros objetos dentro do código fornecidos no capítulo 5 deste artigo. Descreve o esquema de código do programa operacional que também pode se tornar seu assistente, se necessário.

Vamos progredir de questões simples para complexas, utilizando o exemplo de outro código de teste que cria um painel sobre o gráfico para calcular cliques do mouse sobre seus objetos, "Sim" e "Não".

Este painel de código de teste anexo é semelhante aos da linguagem de negociação dos terminais

Fig. 6. Painel de código de teste anexo é semelhante aos da linguagem de negociação dos terminais

Este é um código de teste simples que facilita o acompanhamento dos esquemas de operação (descritos abaixo) nos dois tipos de ação:

O teste inicial do código do indicador, com os esquemas descritos abaixo não inclusos, está anexado ao artigo com o nome test_count_click_0. Durante a execução dos dois esquemas, duas variedades deste código de teste foram criadas com uma consideração das adições feitas. É por isso que você pode ver a operação de cada esquema imediatamente e individualmente como um código completo, e também testar a sua operação no gráfico removendo ou excluindo objetos desses códigos.

As linhas adicionais para a saída Print() estão incluídas nos códigos anexos, onde este esquema é aplicado, para que você possa visualizar a ordem de manipulação com métodos implementados ao alterar ou remover objetos. Abaixo estão as linhas de código sem auxilio dos comandos de saída Print().


4.1. "Saída automática" de programa do gráfico com a remoção dos objetos modificados

A versão completa do código de teste está aanexado com o nome test_count_click_1.

4.1.1. Criação do arquivo com este esquema no código:

O arquivo test_count_click_0 está salvo com o nome test_count_click_1. Isso pode ser feito pela abertura do arquivo anexado contendo o número 0 no título na MetaEditor, em seguida escolha Arquivo -> Salvar como... no menu superior do MetaEditor.

4.1.2. Variáveis declaradas além de todas as funções no âmbito da visibilidade a todas as partes dos programas, ou seja, até o bloco da função OnInit():

O nome indicadores em #define é trocado por uma substituição interna posterior:

#define NAME_INDICATOR             "count_click_1: "

a variável é adicionada a flag semáforo:

int flagAntivandal;
Seguido por um array com dimensão igual à quantidade de objetos "protegidos" para armazenar os tempos de criação dos objetos:
datetime timeCreateObj[QUANT_OBJ];

Uma array de texto para armazenar duas mensagens de aviso no caso dos indicadores de "saída automática" do gráfico serem adicionados. Um para a remoção bem sucedida no gráfico, o segundo - se ocorrer um erro durante esta operação. O array é declarado no âmbito de visibilidade para todas as partes do programa, uma vez que a saída destas mensagens é fornecida em dois idiomas (inglês e russo), dependendo do idioma do terminal.

string textDelInd[2];

Além disso, as mensagens armazenadas nesse array podem ser úteis, fornecendo a saída do indicador no gráfico, devido à recepção da razão de desinicialização REASON_INITFAILED no OnInit()

4.1.3. Na função LanguageTerminal(), responsável por exibir as mensagens dos textos dependendo do idioma terminais de negociação, temos o seguinte:

Os textos das mensagens são adicionados em ambos os programas de "saída automática", bem ou mal sucedidos, a partir do gráfico.

void LanguageTerminal()
  {
   string language_t=NULL;
   language_t=TerminalInfoString(TERMINAL_LANGUAGE);
//---
   if(language_t=="Russian")
     {
      textObj[3]="Да: ";
      textObj[4]="Нет: ";
      textObj[5]="Всего: ";
      //---
      StringConcatenate(textDelInd[0],"Удалось удалиться индикатору: \"",
                        prefixObj,"\". Код ошибки: ");
      StringConcatenate(textDelInd[1],"Удалился с графика индикатор: \"",
                        prefixObj,"\".");
     }
   else
     {
      textObj[3]="Yes: ";
      textObj[4]="No: ";
      textObj[5]="All: ";
      //---
      StringConcatenate(textDelInd[0],"Failed to delete indicator: \"",
                        prefixObj,"\". Error code: ");
      StringConcatenate(textDelInd[1],
                        "Withdrew from chart indicator: \"",
                        prefixObj,"\".");
     }
//---
   return;
  }

4.1.4. Na função PanelCreate() são criados os objetos do painel de controle:

Depois de criar objetos, no momento da sua criação, é determinado e armazenado no array. Numa versão simples isto pode se parecer mais ou menos assim:

void PanelCreate(const long chart_ID=0,const int sub_window=0)
  {
   for(int i=NUMBER_ALL;i>=0;i--)
     {
      //--- campos para mostrar o número de cliques:
      if(ObjectFind(chart_ID,nameObj[i])<0)
        {
         EditCreate(chart_ID,nameObj[i],sub_window,X_DISTANCE+WIDTH,
                    Y_DISTANCE+(HEIGHT)*(i),WIDTH,HEIGHT,
                    textObj[i],"Arial",FONT_SIZE,"\n",ALIGN_RIGHT,true,
                    CORNER_PANEL,clrText[i],CLR_PANEL,CLR_BORDER);
         //--- determinar e armazenar o tempo de criação do objeto:
         CreateTimeGet(chart_ID,nameObj[i],timeCreateObj[i]);
        }
     }
//---
   int correct=NUMBER_ALL+1;
//---
   for(int i=QUANT_OBJ-1;i>=correct;i--)
     {
      //--- campos com notas explicativas:
      if(ObjectFind(chart_ID,nameObj[i])<0)
        {
         EditCreate(chart_ID,nameObj[i],sub_window,X_DISTANCE+(WIDTH*2),
                    Y_DISTANCE+(HEIGHT)*(i-correct),WIDTH,HEIGHT,
                    textObj[i],"Arial",FONT_SIZE,"\n",ALIGN_LEFT,true,
                    CORNER_PANEL,clrText[i-correct],CLR_PANEL,CLR_BORDER);
         //--- determinar e armazenar o tempo de criação do objeto:
         CreateTimeGet(chart_ID,nameObj[i],timeCreateObj[i]);
        }
     }
   return;
  }

4.1.5. A variante da função para determinar e armazenar o momento da criação do objeto numa variável:

bool CreateTimeGet(const long chart_ID,
                   const string &name,
                   datetime &value
                   )
  {
   datetime res=0;
   if(!ObjectGetInteger(chart_ID,name,OBJPROP_CREATETIME,0,res))
     {
      Print(LINE_NUMBER,__FUNCTION__,", Error = ",
            GetLastError(),", name: ",name);
      return(false);
     }
   value=res;
   return(true);
  }

No caso, o programa está no gráfico durante a reinicialização de emergência do terminal, por exemplo: reinicialização por congelamento, foi adicionada uma função de revisão para a criação de tempo dos objetos:

bool RevisionCreateTime(int quant,datetime &time_create[])
  {
   datetime t=0;
   for(int i=quant-1;i>=0;i--)
     {
      t=time_create[i];
      if(t==0)
        {
         Print(LINE_NUMBER,__FUNCTION__,", Error create time: ",
               TimeToString(t,TIME_DATE|TIME_SECONDS));
         return(false);
        }
     }
//---
   return(true);
  }

Esta chamada da função é colocada na OnInit() após a PanelCreate().

Parto do princípio de que, se após o encerramento da emergência no terminal e seu reinício subsequente, essa função retorna false, então antes que aconteça, uma mensagem de erro 4102 a partir da função CreateTimeGet(), indicando que o gráfico não responde, pode aparecer na aba "Experts" do terminal.

4.1.6. No bloco da função OnInit():

4.1.6.1. Anterior a função para a criação de painéis de controle, a flag do semáforo adicionada é inicializada com o valor -1. Isso significa que o alarme é desativado no tempo de execução de ações "amigáveis" com objetos. Em particular, para evitar decepções quando o período de tempo do gráfico for alterado.

flagAntivandal=-1;

4.1.6.2. O nome abreviado do indicador é definido, se ainda não foi, será necessário a remoção forçada do programa do gráfico.

No exemplo de código de teste fornecido, o nome abreviado do indicador foi definido anteriormente e igual ao prefixo dos objetos que consiste no nome dos indicadores, símbolo e período do gráfico onde será definido:

//--- criar prefixo para nomes de objetos de código e nome abreviado do indicador:
   StringConcatenate(prefixObj,NAME_INDICATOR,Symbol(),"_",EnumToString(Period()));
//--- define o nome abreviado do indicador:
   IndicatorSetString(INDICATOR_SHORTNAME,prefixObj);

4.1.6.3. Nós habilitamos o alarme de "proteção", isto após a função de criação do painel de controle e revisão do array com o tempo de criação dos objetos:

//--- cria um painel:
   PanelCreate();
/*revisão do array com o tempo de criação dos objetos, e que encerra 
o programa caso algo dê errado:*/
   if(!RevisionCreateTime(QUANT_OBJ,timeCreateObj))
     {return(REASON_INITFAILED);}
/*vamos escolher a flag para a posição de resposta
aos eventos de remoção e alteração de objetos:*/
   flagAntivandal=0;


4.1.6.4. Ativamos a notificação sobre eventos de objetos removidos do gráfico, se ele está desativado nas propriedades:

//--- ativar a notificação sobre eventos de exclusão de objetos gráficos
 bool res=true;
 ChartEventObjectDeleteGet(res);
 if(res!=true)
 {ChartEventObjectDeleteSet(true);}


Nota: ChartEventObjectDeleteGet() e ChartEventObjectDeleteSet() são funções prontas da página com exemplos de como trabalhar com gráficos na Documentação.

4.1.6.5. Antes de sair do OnInit(), vamos definir a frequência do timer em segundos, a fim de verificar se as notificações sobre eventos de exclusão de objetos serão desativadas nas propriedades do gráfico durante o funcionamento deste programa:

EventSetTimer(5);

4.1.7. No bloco da função OnDeinit():

Nós especificamos que o "alarme" deve ser desativado:

flagAntivandal=-1;

Nós especificamos o término de geração de eventos a partir do timer:

EventKillTimer();

A exclusão do indicador se o código da razão de desinicialização REASON_INITFAILED vem do OnInit():

//--- exclusão do indicador se o REASON_INITFAILED veio do OnInit().
   if(reason==REASON_INITFAILED)
     {
      ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
     }

4.1.8. No bloco da função OnTimer():

Usando copiar e colar, replicamos as linhas de código a partir do ponto 4.1.6.4 para ativar as notificações sobre eventos de exclusão dos objetos no gráfico com um temporizador, no caso deles serem desativados por outro programa.

4.1.9. Criando ações na funçao OnChartEvent():

4.1.9.1. Se a manipilação é fornecida apenas para "medidas de segurança", em caso de alteração e exclusão de objetos, então a manipulação desses eventos pode ser combinado num único bloco.

4.1.9.2. Então a primeira coisa é verificar a flag, caso o "alarme" seja ativado ao receber uma notificação sobre o evento de mudança/exclusão do objeto. Se o valor da bandeira é igual a -1 (o "alarme" está desativado), então ele sai do bloco da manipulação de eventos.

4.1.9.3. Quando o valor da flag for igual a 0 (o "alarme" está ativado), você pode definir primeiro uma verificação para combinar o nome do objeto obtido com um prefixo dos objetos de programas na notificação. A função StringFind() é utilizado para esta finalidade, por isso não há necessidade de interagir todos os arrays dos nomes dos objetos de programas com cada notificação sobre as mudanças e remoção dos objetos do gráfico. Neste caso, um prefixo definido em OnInit() não é abreviado, devido ao fato que o filtro é melhor. A formação de prefixo é fornecida em p.4.1.6.2.

Se não houver nenhuma correspondência de prefixo, ocorre a saída do manipulador de eventos do bloco.

4.1.9.4. Se houver uma correspondência pelo prefixo, somente então a notificação de eventos recebida é verificada para uma combinação completa de um nome com os objetos dos programas "protegidos". Quando os nomes coincidem totalmente, o valor da flag de proteção é alterado para -1 (desativado), a fim de evitar a criação de loops extras, quando um programa é excluído do gráfico.

4.1.9.5. Se um objeto está "sem dono", por ter sido renomeado, a busca de objetos com o mesmo tempo de criação igual ao com notificação é executado, com mais uma remoção, se for encontrado.

4.1.9.6. Só então o programa é removido a partir do gráfico.

Ações descritas criadas no código:

else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)
     {
      if(flagAntivandal==-1)//ignore o evento, se o "alarme estiver desativado"
        return;
      else if(flagAntivandal==0)//se o alarme estiver ativado,
        {
         //--- olhar por uma correspondência pelo prefixo:
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)//se houver uma correspondência pelo prefixo
           {
            string chartObject=sparam;
            findPrefixObject=-1;
            //---
            for(int i=QUANT_OBJ-1;i>=0;i--)
              {
               //--- ações quando nomes se combinam por completo:
               if(StringCompare(chartObject,nameObj[i],true)==0)
                 {
                  //--- desativar o alarme para evitar looping nas operações de remoção:
                  flagAntivandal=-1;
/*remover objetos cujo tempo de criação é igual ao tempo de criação de objetos excluídos ou
 alterados:*/
                  ObDelCreateTimeExcept0(timeCreateObj[i],0,0,OBJ_EDIT);
                  //--- remove indicador do gráfico:
                  ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
                  //---
                  ChartRedraw();
                  return;
                 }
              }//for(int i=QUANT_OBJ-1;i>=0;i--)
            return;
           }//if(findPrefixObject==0)
         return;
        }//if(flagAntivandal==0)
      return;
     }//if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)  

4.1.10. A função ObDelCreateTimeExcept0() para a remoção de objetos no momento de sua criação:

O esquema da funções para a exclusão de objetos pelo seu tempo de criação pode ser parecido com este

void ObDelCreateTimeExcept0(datetime &create_time,// hora da criação
                            long chart_ID=0,// identificador dos gráficos
                            int sub_window=-1,// número da subjanela
                            ENUM_OBJECT type=-1)// -1 = todos os tipos de objetos
  {
/*sair da função, se o tempo do objeto = 0(1970.01.01 00:00:00),
 por isso não há necessidade de excluir objetos manualmente ou através de objetos criados por programação, 
 cujo tempo de criação é zerado após 
 O terminal ser reiniciado, a partir do gráfico:*/
   if(create_time==0){return;}
   int quant_obj=0;
   quant_obj=ObjectsTotal(chart_ID,sub_window,type);
//---
   if(quant_obj>0)
     {
      datetime create_time_x=0;
      string obj_name=NULL;
      //---
      for(int i=quant_obj-1;i>=0;i--)
        {
         obj_name=ObjectName(chart_ID,i,sub_window,type);
         create_time_x=(datetime)ObjectGetInteger(chart_ID,obj_name,
                        OBJPROP_CREATETIME);
         //---
         if(create_time_x==create_time)
           {ObDelete(chart_ID,obj_name);}
        }
     }
   return;
  }
//+------------------------------------------------------------------+


Ele tem um filtro no interior, por isso se os dados de criação do objeto "protegido" for igual a 0 (que significa: 1970.01.01 00:00:00), então o manipulador de eventos sobre ele deve ser descontinuado. Isso é necessário para evitar a exclusão de objetos criados manualmente ou através de programação, cujo tempo de criação é zerado após o terminal ser reiniciado, a partir do gráfico.

4.1.11. A função ObDelete() no código acima:

É tirado da biblioteca ObjectCreateAndSet e tem uma saída de erro fornecida quando os objetos são deletados:

bool ObDelete(long chart_ID,string name)
  {
   if(ObjectFind(chart_ID,name)>-1)
     {
      ResetLastError();
      if(!ObjectDelete(chart_ID,name))
        {
         Print(LINE_NUMBER,__FUNCTION__,", Error Code = ",
               GetLastError(),", name: ",name);
         return(false);
        }
     }
   return(true);
  }

4.1.12. Função para apagar um indicador:

//+------------------------------------------------------------------+
//| Deletando um indicador do gráfico                                |
//+------------------------------------------------------------------+
void ChIndicatorDelete(const string short_name,//nome abreviado do indicador
                       const string &text_0,//texto quando há um erro ao excluir um indicador
                       const string &text_1,//texto quando um indicador é excluído com sucesso
                       const long  chart_id=0,// identificador do gráfico
                       const int   sub_window=-1// número da subjanela
                       )
  {
   bool res=ChartIndicatorDelete(chart_id,sub_window,short_name);
//---
   if(!res){Print(LINE_NUMBER,text_0,GetLastError());}
   else Print(LINE_NUMBER,text_1);
  }
//+------------------------------------------------------------------+

onde LINE_NUMBER é um texto geral no início das mensagens que contém o número da linha de código definido em #define:

#define LINE_NUMBER    "Line: ",__LINE__,", "

Agora vamos para o próximo esquema.


4.2. "Auto recuperação" dos objetos do programa

Criar a auto recuperação dos objetos requer uma atenção mais séria no arranjo e mudança das flags de valores para evitar um loop de funcionamento do programa.

O esquema apresentado baseia-se numa modificação do código anterior test_count_click_1. O código completo pode ser examinado e testado, você pode encontrá-lo no arquivo anexado chamado test_count_click_2.

4.2.1. Criando um arquivo com este esquema no código:

O arquivo test_count_click_1 está salvo com o nome test_count_click_2.

4.2.2. Variáveis declaradas estão além de todas as funções no âmbito da visibilidade a todas as partes dos programas, ou seja, antes do bloco da função OnInit():

O nome dos indicadores é substituído em #define para uma substituição interna posterior:

#define NAME_INDICATOR             "count_click_2: "

Outra variável é adicionada, desta vez para uma flag de adaptação auxiliar, além da simples flag principal descrita anteriormente:

int flagAntivandal, flagResetEventDelete;

A variável também é criada para as mensagens de texto antes de realizar as operações de recuperação:

string textRestoring;

4.2.3. A função LanguageTerminal() é responsável pela exibição das mensagens dos textos, dependendo do idioma nos terminais de negociação:

Ele contém uma mensagem exibida antes das operações de recuperação, no caso de remoção não autorizada da mudança dos objetos no painel:

void LanguageTerminal()
  {
   string language_t=NULL;
   language_t=TerminalInfoString(TERMINAL_LANGUAGE);
//---
   if(language_t=="Russian")
     {
      textObj[3]="Да: ";
      textObj[4]="Нет: ";
      textObj[5]="Всего: ";
      //---
      textRestoring="Уведомление о страховом случае: объект ";
      //---
      StringConcatenate(textDelInd[0],"Удалось удалиться индикатору: \"",
                        prefixObj,"\". Код ошибки: ");
      StringConcatenate(textDelInd[1],"Удалился с графика индикатор: \"",
                        prefixObj,"\".");
     }
   else
     {
      textObj[3]="Yes: ";
      textObj[4]="No: ";
      textObj[5]="All: ";
      //---
      textRestoring="Notification of the insured event: object ";
      //---
      StringConcatenate(textDelInd[0],"Failed to delete indicator: \"",
                        prefixObj,"\". Error code: ");
      StringConcatenate(textDelInd[1],
                        "Withdrew from chart indicator: \"",
                        prefixObj,"\".");
     }
//---
   return;
  }

Eu usei esse texto assumindo que as opções consideradas agem como uma rede de segurança em caso de eventos indesejáveis.

4.2.4. No bloco da função OnInit():

Nós inicializamos ambos as flags com um valor -1 (o alarme é completamente desativado) antes da função de criação do objeto:

flagAntivandal = flagResetEventDelete = -1;

Após o bloco de criação de objetos do painel e uma revisão do array de tempo para a criação de objetos - inicialização da flag com o valor 0 (ativando o "alarme"):

//--- cria um painel:
   PanelCreate();
/*revisão do array com o tempo de criação dos objetos, e que encerra 
o programa caso algo dê errado:*/
   if(!RevisionCreateTime(QUANT_OBJ,timeCreateObj))
     {return(REASON_INITFAILED);}
/*define flags antivandalismo na posição de resposta a 
eventos de exclusão e alteração de objetos:*/
   flagAntivandal=flagResetEventDelete=0;

4.2.5. Vamos continuar com a função OnChartEvent():

Vamos mudar o bloco abaixo, já existente, da manipulação das notificações sobre eventos de alteração e exclusão dos objetos. No novo bloco, as ações seguem o esquema que também é comum para manipular eventos de remoção e mudança dos objetos:

  • Antes de mais nada, verifique se a "proteção" está habilitada. Se não, então a saída é realizada a partir do evento de manipulação do bloco.
  • Então, se a flag auxiliar responsável por redefinir os eventos de exclusão extras é maior do que 0, as notificações recebidas sobre os eventos de exclusão alteram os objetos dos painéis que são resetadas até a flag igualar a 0.
  • Somente quando as flags principais e auxiliares são iguais a zero, as ações de recuperação são possíveis em caso de alteração ou remoção dos objetos não autorizados.
else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)
     {
      if(flagAntivandal==-1){return;}
      else if(flagResetEventDelete>0)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)
           {
            findPrefixObject=-1;
/*redefinindo valores dos eventos de "counter flag" para prevenção subsequente 
de um loop da operação de código a partir da fila dos eventos construídos de remoção de objetos
que aparecem como resultado de ações de recuperação:*/
            flagResetEventDelete=flagResetEventDelete-1;
            //---
            return;
           }
         return;
        }
      else if(flagAntivandal==0 && flagResetEventDelete==0)
        {
         //--- examine uma combinação pelo prefixo:
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)//se houver uma correspondência pelo prefixo
           {
            string chartObject=sparam;//o nome do objeto no evento
            findPrefixObject=-1;
/*verificando se um objeto é "protegido" e tende a recuperar 
            medidas, se for:*/
            RestoringObjArrayAll(chartObject,prefixObj,flagAntivandal,
                                     flagResetEventDelete,QUANT_OBJ,nameObj,
                                     timeCreateObj);
            return;
           }//if(findPrefixObject==0)
         return;
        }//else if(flagAntivandal==0 && flagResetEventDelete==0)
      return;
     }//else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)

Principais atividades de recuperação são criadas na função RestoringObjArrayAll(). Várias de tais funções podem ser aplicadas simultaneamente em bloco para diferentes grupos de objetos. Você pode encontrar um exemplo relevante no quinto capítulo deste artigo. O nome do objeto por evento de exclusão ou alteração de quedas no âmbito desta função após uma verificação, se ela corresponde aos objetos "protegidos".

4.2.6. A função RestoringObjArrayAll().

Ele contém as seguintes ações:

  • Se a confirmação para uma combinação completa pelo nome mostra que o objeto de evento é "protegido", então:
    • a principal flag de "alarme" obtém valor -1 (o "alarme" está desativado para o tempo das ações "amigáveis");
    • no caso de um evento de exclusão de objeto, uma flag auxiliar é definida igual ao montante global de objetos "protegidas" menos um, porque se o evento de exclusão de objetos é manipulado, isso significa que pelo menos neste momento um objeto foi excluído.
  • Após essas ações preliminares os objetos protegidos remanescentes são excluídos pelo prefixo usando a função ObDeletePrefixFlag() mostrada abaixo, enquanto os objetos excluídos são calculados. Com base neste cálculo, o valor da flag auxiliar flagResetEventDelete é corrigida, se necessário.

E uma vez que durante a exclusão de objetos, os eventos de exclusão aparecem, eles não vão ser posteriormente manipulados até que o valor da flag auxiliar não seja redefinida para 0, como são tratados nesta parte do código:

else if(flagResetEventDelete>0)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)
           {
            findPrefixObject=-1;
/*redefinindo valores dos eventos de "counter flag" para prevenção subsequente  
de um loop da operação de código a partir da fila dos eventos construídos de remoção de objetos
que aparecem como resultado de ações de recuperação:*/
            flagResetEventDelete=flagResetEventDelete-1;
            //---
            return;
           }
         return;
        }


    • Quando os objetos são excluídos pelo prefixo, e a flag auxiliar adota o valor igual ao número atual da "demissão" dos eventos não necessários para a manipulação, uma função para redesenhar um gráfico é colocada. Somente então novas ações seguem.
    • O redesenho do gráfico é seguido pela busca e remoção de objetos com o tempo de criação igual ao tempo da criação dos objetos no evento que foi recebido. No caso do evento ser formado depois do objeto renomeado, e um objeto "sem dono" apareceu no gráfico. Ele deve ser removido usando a função ObDelCreateTimeExcept0(), já conhecida por nós no subtítulo p.4.1.10.
    • Etapas finais de manipulação: recria um painel ou a sua parte que está sendo recuperada.
    • Finalmente, redesenha o gráfico e atribui um valor igual a 0 (definido no "alarm") para a flag do semáforo principal. A esta altura o valor necessário para a flag adaptativa auxiliar foi formada automaticamente, de modo que não necessita escrever qualquer coisa para ela.

//+---------------------------------------------------------------------------------------+
//|Função da "auto recuperação" de objetos na OnChartEvent()                              |
//+---------------------------------------------------------------------------------------+
//|string sparam = nome do objeto transmitido para OnChartEvent()                         |
//|string prefix_obj = prefixo comum de objetos protegidos                                |
//|int &flag_antivandal = flag para ativar/desativar o alarme                             |
//|int &flag_reset = flag para resetar eventos                                            |
//|int quant_obj = quantidade total de objetos no array                                   |
//|"protegido"                                                                            |
//|string &name[] = array dos nomes dos objetos "protegidos"                              |
//|datetime &time_create[] = array de tempo de criação desses objetos                     |
//|int quant_obj_all=-1 quantidade total de objetos recuperáveis                          |
//|do programa, >= quant_obj (se -1, então iguala a quant_obj)                            |
//|const long chart_ID = 0 identificador de gráficos                                      |
//|const int sub_window = 0 índice da janela                                              |
//|int start_pos=0 posição nos nomes dos objetos para começar a procurar o prefixo com    |
//+---------------------------------------------------------------------------------------+
int RestoringObjArrayAll(const string sparam,
                         const string prefix_obj,
                         int &flag_antivandal,
                         int &flag_reset,
                         const int quant_obj,
                         string &name[],
                         datetime &time_create[],
                         int quant_obj_all=-1,
                         const long chart_ID=0,
                         const int sub_window=0,
                         const int start_pos=0
                         )
  {
//--- aceita os resultados aqui:
   int res=-1;
/*Verificação para correção da entrada da quantidade de objetos
   dos parâmetros externos da função. O fato é que se parâmetros externos errados 
   desta função foram inseridos durante a criação do código,
   serão conhecidos somente após o evento "alarme".
   É por isso que você precisa de antemão ter a certeza de
   configurar os parâmetros externos corretos para esta função ao criar o código.*/
   if(quant_obj<=0)//Se a quantidade especificada de objetos protegidos <= 0
     {
      Print(LINE_NUMBER,__FUNCTION__,
            ", Error Code. Valor errado: quant_obj =",quant_obj);
      return(res);
     }
   else   if(quant_obj>quant_obj_all && quant_obj_all!=-1)
     {
      Print(LINE_NUMBER,__FUNCTION__,
            ", Error Code. Não é o valor correto: quant_obj_all");
      return(res);
     }
   else if(quant_obj_all==-1){quant_obj_all=quant_obj;}
//--- variável para um valor de comparação do nome completo de um objeto:
   int comparison=-1;
/*Loop para verificar o nome do objeto obtido a partir de eventos com nomes 
 no array dos objetos protegidos:*/
   for(int i=quant_obj-1;i>=0;i--)
     {
      comparison=StringCompare(sparam,name[i],true);
      //--- ações quando nomes se combinam por completo:
      if(comparison==0)
        {
         comparison=-1;
         res=1;
         //--- notificação sobre a ocorrência de caso "seguro":
         Print(LINE_NUMBER,textRestoring,sparam);
/*enquanto o programa restaura objetos, configuramos a flag 
para não reagir à eventos de exclusão dos objetos:*/
         flag_antivandal=-1;
/*Valor inicial do "counter flag" dos eventos que não são mais necessários 
para a manipulação dos eventos futuros. Para a prevenção consequente 
de loop do código a partir da fila de construção dos eventos de exclusão dos 
objetos como resultado de ações de recuperação:*/
         flag_reset=quant_obj_all-1;
         //--- removendo objetos restantes protegidos de um array, se for o caso
         ObDeletePrefixFlag(flag_reset,prefix_obj,chart_ID,sub_window,start_pos);
         //--- redesenhar o gráfico:
         ChartRedraw();
         //--- a remoção de objetos renomeados, se houver:
         ObDelCreateTimeExcept0(time_create[i],chart_ID,sub_window);
         //--- recriação de objetos:
         PanelCreate();
         //--- redesenho final do gráfico:
         ChartRedraw();
         //--- habilitando o alarme:
         flag_antivandal=0;
         //---
         return(res);
        }
     }//for(int i=QUANT_OBJECTS-1;i>=0;i--)
   return(res);
  }

Nota: A função tem a seleção pelo tipo de objeto ausente por uma razão. Durante a "auto recuperação" dos objetos, a intenção de diminuir a quantidade de manipulação pelo estreitamento do círculo de manipulação por tipo de objeto pode levar a um efeito oposto, assim a auto recuperação de todos os objetos de painéis recriados e excluídos seria de diferentes tipos. Portanto, do meu ponto de vista, o melhor é não aplicar a estratégia de estreitar o círculo de manipulação por tipos específicos de objetos, se no ponto de interferência não autorizado, os objetos de painel na tabela consistem em vários tipos. E assumindo que pode haver aqueles que vão ignorar este aviso, a opção é removida da função acima.

Você pode visualizar os efeitos indesejáveis ao escrever e usar a seleção dos tipos de objetos durante a "auto recuperação" no exemplo do quinto capítulo do artigo, onde objetos de diferentes tipos estão presentes no painel. Você só tem que iniciar o Print() nos blocos de código para manipular eventos e recuperar com antecedência.

Devo acrescentar também, que você pode facilmente poupar recursos, reduzindo a quantidade de manipuladores no código por meio das restrições de entrada por tipo de objeto na variante da "saída automática" do programa a partir do gráfico, como descrito acima.

4.2.7. A função ObDeletePrefixFlag() destina-se a remover objetos pelo prefixo e, se necessário, corrigir o valor da flag auxiliar:

Eu confiei no esquema de um código usado no livro de Sergei Kovalev's, e que mais tarde foi mencionado no fórum MQL4 por Artem Trishkin e pelo usuário 7777877, desconhecido para mim. Eu não prestei muita atenção, é por isso que estou grato à aqueles que tocaram no assunto no fórum, uma vez que a base do esquema é bastante universal e tem sido de grande ajuda para mim em muitas tarefas diferentes.

Basicamente, esta variedade é aplicada aqui:

//+------------------------------------------------------------------+
//| int &flag_reset = flag para repor eventos de objetos de exclusão |
//| string prefix_obj = prefixo geral nos nomes dos objetos          |
//| long  chart_ID = identificador dos gráficos                      | 
//| int   sub_window=-1 = índice da janela (-1 = todos)              |
//| int start_pos=-1 = inicia a posição de um prefixo geral          | 
//| substring no nome do objeto                                      |
//+------------------------------------------------------------------+
int ObDeletePrefixFlag(int &flag_reset,
                       string prefix_obj,
                       const long chart_ID=0,
                       const int sub_window=0,
                       int start_pos=0)

  {
   int quant_obj=ObjectsTotal(chart_ID,sub_window,-1);
   if(quant_obj>0)
     {
      int count=0;
      int prefix_len=StringLen(prefix_obj);
      string find_substr=NULL,obj_name=NULL;
//---
      for(int i=quant_obj-1;i>=0;i--)
        {
         obj_name=ObjectName(chart_ID,i,sub_window,-1);
         find_substr=StringSubstr(obj_name,start_pos,prefix_len);

         if(StringCompare(find_substr,prefix_obj,true)==0)
           {
            count=count+1;
            ObDelete(chart_ID,obj_name);
           }
        }
      if(count>0 && flag_reset<count)
        {flag_reset=count;}
     }
   return(flag_reset);
  }

A operação do código pode ser verificada de acordo com:
  • execução do código de teste ligado no gráfico;
  • clicando nos objetos pertencentes ao "Yes" e "No", aumentando os valores a partir do zero a outro valor;
  • e depois tentando mudar estes objetos de painel através do diálogo de propriedades ou excluindo-os.
Após a execução das ações de recuperação automática, o painel terá números "clicados" mais vezes do que quando não estavam no momento da mudança e remoção de seus objetos, uma vez que a quantidade de cliques é salvo na array de dados, usado durante a criação do painel.

No entanto, diversas respostas a vários objectos podem ser criadas.

Nós apresentamos a parte principal do procedimento, exceto em alguns casos especiais, tais como:

  • exclusão do manipulador de eventos;
  • alteração das propriedades dos objetos por um programa diferente.

Agora vamos avançar para um exemplo de implementação prática, onde o programa responde de ambos os sentidos a exclusão e mudança dos seus objetos.


5. Exemplo de implementação de duas opções de resposta num programa

A seleção de um indicador não é aleatória, onde a implementação de duas opções das reações de respostas a ações não autorizadas seriam fornecidas passo a passo:

  • Se nós fornecermos o exemplo de uma co-implementação destas opções com base num código mais complexo, o leitor pode ficar confuso.
  • Mesmo que a opção anexada abaixo seja simples, ela fornece muitas informações práticas, incluindo as condições de "batalha".
  • Também, o indicador pode ser útil em operação com objetos, incluindo as relevantes ao assunto deste artigo.

E, se necessário, no futuro poderá ser estendido pela expansão da lista dos valores exibidos.

Um código completo é ligado sob o nome id_name_object. Aqui vou descrever:

  • um mapa aos arrays e variáveis para nomes dos objetos salvos e um mapa do objeto;
  • Código geral dos esquemas;
  • seções do código que estão diretamente relacionados ao assunto do artigo.

Finalidade do indicador fornecido: ao clicar em qualquer objeto gráfico, mostra o nome do objeto clicado, seu tempo de criação e o seu tipo (além de si próprio). Se necessário, você pode copiar estes valores dos campos onde são exibidos. Durante uma tentativa de alterar ou remover os valores exibidos no painel, sua restauração será fornecida. Isso vale separadamente a partir das opções consideradas no artigo e como um complemento a elas.

Além disso, existem as seguintes características:

  • "Minimização" da parte principal do painel no gráfico;
  • A "auto recuperação" de objetos, se qualquer objeto da parte principal do painel foi alterado ou excluído;
  • A "saída automática" de um indicador no gráfico, se o botão do painel minimizado é eliminado ou alterado com base no princípio: "se é minimizado, então não é muito necessário no momento";
  • as duas últimas medidas são implementadas levando em consideração que os objetos têm de ser excluídos após uma possível mudança do nome a partir do gráfico;
  • exibindo títulos de painéis em russo, se o terminal está em russo, e em Inglês para qualquer outra língua.

O aparecimento de um painel de controle dependendo do idioma do terminal

Fig. 7. O aparecimento de um painel de controle dependendo do idioma do terminal

Mapa para arrays e uma variável para armazenar os nomes criados no painel de objetos

Fig. 8. Mapa para arrays e uma variável para armazenar os nomes criados no painel de objetos


Nome
Tipo de objeto
Finalidade
Onde o texto está armazenado
Fonte
Cor do texto *
Fundo * Cor da borda *
Tempo de criação é armazenado
nameRectLabel
OBJ_RECTANGLE_LABEL
Telas
---
--- ---
CLR_PANEL clrNONE
timeCreateRectLabel
nameButton[0]
OBJ_BUTTON
Botão para a remoção de identificador a partir do gráfico
textButtUchar[0]**
"Wingdings"
CLR_TEXT
CLR_PANEL clrNONE
timeCreateButton[0]
nameButton[1] OBJ_BUTTON
Botão para o painel de "minimização"
textButtUchar[1]**
"Wingdings"
CLR_TEXT
CLR_PANEL clrNONE
timeCreateButton[1]
nameButton[2] OBJ_BUTTON
Botão para o painel de "maximização" depois de "minimizá-lo"
textButtUchar[2]**
"Wingdings"
CLR_TEXT
CLR_PANEL clrNONE
timeCreateButton[2]
nameEdit0[0]
OBJ_EDIT
"Nome do objeto:" etiqueta
textEdit0[0]
"Arial"
CLR_TEXT_BACK
CLR_PANEL CLR_BORDER
timeCreateEdit0[0]
nameEdit0[1]
OBJ_EDIT
"Tempo de criação:" etiqueta textEdit0[1] "Arial"
CLR_TEXT_BACK
CLR_PANEL CLR_BORDER
timeCreateEdit0[1]
nameEdit0[2]
OBJ_EDIT
"Tipo do objeto:" etiqueta textEdit0[2] "Arial"
CLR_TEXT_BACK
CLR_PANEL CLR_BORDER
timeCreateEdit0[2]
nameEdit1[0]
OBJ_EDIT
Campo para exibição do nome do objeto textEdit1[0]
"Arial"
CLR_TEXT
CLR_PANEL CLR_BORDER
timeCreateEdit1[0]
nameEdit1[1] OBJ_EDIT
Campo para a exibição de tempo da criação textEdit1[1] "Arial"
CLR_TEXT
CLR_PANEL CLR_BORDER
timeCreateEdit1[1]
nameEdit1[2] OBJ_EDIT
Campo para a exibição do tipo de objeto
textEdit1[2] "Arial"
CLR_TEXT
CLR_PANEL CLR_BORDER
timeCreateEdit1[2]

 

Table 2. Mapa para objetos do painel

* Colors set with #define:

#define CLR_PANEL                  clrSilver//contexto geral do painel
#define CLR_TEXT                   C'39,39,39'//cor principal do texto
#define CLR_TEXT_BACK              C'150,150,150' //cor do texto sombreado 
#define CLR_BORDER                 clrLavender//cor dos quadros

** Symbols "Wingdings":

uchar textButtUchar[3]={251,225,226};


5.1. Variáveis externas:

O tamanho do painel é calculado com base no tamanho de um botão. Portanto, a largura e a altura do tamanho do botão e da fonte são exibidas nas propriedades personalizadas externas. Isto permite aumentar o painel e textos exibidos sem fazer ajustes no próprio código.

Propriedades personalizadas externas de um indicador

Fig. 9. Propriedades personalizadas externas de um indicador


5.2. Variáveis para flags declaradas além de qualquer função no âmbito da visibilidade para todas as partes do programa, isto é, até o bloco da função OnInit().

Duas flags são declaradas:

int flagClick, flagResetEventDelete;

flagClick é uma flag que define o rastreamento das notificações aos cliques nos botões do painel. Seus valores são:

0
Acompanha as notificações sobre eventos de cliques nos botões de "minimização" do painel e remoção de identificador a partir do gráfico, ou seja, este valor é fornecido quando a parte principal do painel está no gráfico.
-1
As ações de "minimização" são definida antes, ou pelo contrário, a "maximização" do painel no gráfico, e também, antes da ação de exclusão de identificador a partir do gráfico, após o clique no botão correspondente.
1 Quando monitora as notificações sobre eventos clicando no botão "maximizar".

 

Também age como a principal flag "anti vandalismo" (foi nomeada flagAntivandal nos códigos anteriores deste artigo), no caso de modificação não autorizada ou remoção de objetos. O valor 0 habilita o "alarme" da parte principal do painel 1 — do botão "maximizar", e -1 desativa o "alarme" durante as ações em curso, se for o caso.

Se você deseja criar ums flag principal "anti vandalismo" adaptativa por valor, já que uma flag auxiliar reseta a manipulação de eventos no código, então será necessário uma variável separada para esta finalidade.

flagResetEventDelete é uma flag usada para reiniciar a manipulação de eventos desnecessários com valores de adaptação, ao implementar a opção "auto recuperação" para objetos alterados ou excluídos.

Outras variáveis declaradas nesta área podem ser vistas no código anexado.


5.3. No bloco da função OnInit():

  • As flags obtêm o valor -1 antes de criarem um painel no gráfico.
  • Array para textos dos campos de painéis, onde, consequentemente, valores dos objetos de gráfico clicados serão exibidos, o array é preenchido com valores vazios por enquanto.
  • O prefixo de um objeto comum é criado e um nome abreviado é arbitrariamente atribuído ao indicador.
  • Os nomes dos objetos do painel são criados com base no prefixo comum formado.
  • Será determinado qual idioma a usar nos títulos do painel, as mensagens sobre a recuperação de objetos e os indicadores de remoção forçada serão de acordo com o idioma do terminal.
  • Restrição à largura e altura do botão está definida, no caso os parâmetros externos têm um tamanho incorreto.
  • Parte principal do painel é criada no gráfico.
  • Arrays são revistos com o tempo de criação dos objetos.
  • As flags flagClick e flagResetEventDelete possuem o valor atribuído igual a 0.
  • Então é feita uma tentativa para habilitar a notificação dos eventos de exclusão de objetos no gráfico, se ela estiver desativado nas propriedades.
  • Frequência do timer é definida em segundos.

int OnInit()
  {
   flagClick=flagResetEventDelete=-1;
/*array para campos de texto do painel onde 
   os valores dos objetos gráficos clicados serão consequentemente exibidos:*/
   for(int i=QUANT_OBJ_EDIT-1;i>=0;i--){textEdit1[i]=" ";}
//+------------------------------------------------------------------+
/*O nome abreviado do indicador:*/
   StringConcatenate(prefixObj,INDICATOR_NAME,"_",Symbol(),"_",
                     EnumToString(Period()));
   IndicatorSetString(INDICATOR_SHORTNAME,prefixObj);
//--- criando o nome do objeto
   NameObjectPanel(nameRectLabel,prefixObj,"rl");
   NameObjectPanel(QUANT_OBJ_BUTTON,nameButton,prefixObj,"but");
   NameObjectPanel(QUANT_OBJ_EDIT,nameEdit0,prefixObj,"ed0");
   NameObjectPanel(QUANT_OBJ_EDIT,nameEdit1,prefixObj,"ed1");
//+------------------------------------------------------------------+
/*Textos do Indicador, depende do idioma do terminal de negociação:*/
   LanguageTerminal();
//+------------------------------------------------------------------+
/*restrições da largura e altura de um botão:*/
   if(objWidthHeight>=20 && objWidthHeight<=300)
     {obj0WidthHeight=objWidthHeight;}
   else if(objWidthHeight>300){obj0WidthHeight=300;}
   else {obj0WidthHeight=20;}
//--- a criação de um painel de controle no gráfico
   PanelCreate();
/*revisão dos arrays com o tempo de criação dos objetos e finalização do programa
se algo de errado acontece:
(Apenas dois dos três botões estão presentes na parte principal do painel,
portanto, o seu valor durante a revisão é um a menos)*/
   if(!RevisionCreateTime(QUANT_OBJ_BUTTON-1,timeCreateButton))
     {return(REASON_INITFAILED);}
   if(!RevisionCreateTime(QUANT_OBJ_EDIT,timeCreateEdit0))
     {return(REASON_INITFAILED);}
   if(!RevisionCreateTime(QUANT_OBJ_EDIT,timeCreateEdit1))
     {return(REASON_INITFAILED);}
//---
   flagClick=flagResetEventDelete=0;
//--- incluindo as notificações sobre eventos de remoção dos objetos gráficos:
   bool res;
   ChartEventObjectDeleteGet(res);
   if(res!=true)
     {ChartEventObjectDeleteSet(true);}
//--- criar timer
   EventSetTimer(8);
//---
   return(INIT_SUCCEEDED);
  }

5.4. Na função PanelCreate():

Os objetos do painel estão divididos em diferentes grupos de nomes, a fim de simplificar a mudança de sua aparência no futuro. O painel em si é colocado no canto superior direito do gráfico. Assim, a distâncias em pixels ao longo do eixo X e Y determinam a posição dos pontos superior esquerdo dos objetos em relação ao canto superior direito do gráfico.

Os casos de mudanças de nomes não autorizados dos objetos no momento de sua criação serão definidos e registrados nos arrays previstos para esta finalidade. Uma variável separada é fornecida para a tela do painel.

void PanelCreate(const long chart_ID=0,//ID do gráfico 
                 const int sub_window=0)// número da subjanela
  {
//--- largura da tela do painel:
   int widthPanel=(obj0WidthHeight*11)+(CONTROLS_GAP_XY*2);
//--- altura da tela do painel:
   int heightPanel=(obj0WidthHeight*6)+(CONTROLS_GAP_XY*8);
//--- a distância ao longo do eixo х:
   int x_dist=X_DISTANCE+widthPanel;
//---
   if(ObjectFind(chart_ID,nameRectLabel)<0)//telas
     {
      RectLabelCreate(chart_ID,nameRectLabel,sub_window,x_dist,
                      Y_DISTANCE,widthPanel,heightPanel,"\n",CLR_PANEL,
                      BORDER_RAISED,CORNER_PANEL,clrNONE,STYLE_SOLID,1,
                      false,false,true);
      //--- determina e registra o tempo de criação de objeto:
      CreateTimeGet(chart_ID,nameRectLabel,timeCreateRectLabel);
     }
//---
   x_dist=X_DISTANCE+CONTROLS_GAP_XY;
   int y_dist=Y_DISTANCE+CONTROLS_GAP_XY;
//---
   for(int i=QUANT_OBJ_BUTTON-2;i>=0;i--)
     {
      //--- botões para excluir e minimizar o indicador:
      if(ObjectFind(chart_ID,nameButton[i])<0)
        {
         ButtonCreate(chart_ID,nameButton[i],sub_window,
                      x_dist+(obj0WidthHeight*(i+1)),y_dist,obj0WidthHeight,
                      obj0WidthHeight,CORNER_PANEL,
                      CharToString(textButtUchar[i]),"\n","Wingdings",
                      font_sz+1,CLR_TEXT,CLR_PANEL,clrNONE);
         //--- determina e registra o tempo de criação de objeto:
         CreateTimeGet(chart_ID,nameButton[i],timeCreateButton[i]);
        }
     }
//---
   x_dist=X_DISTANCE+widthPanel-CONTROLS_GAP_XY;
   int y_dist_plus=(CONTROLS_GAP_XY*2)+(obj0WidthHeight*2);
//---
   for(int i=QUANT_OBJ_EDIT-1;i>=0;i--)
     {
      //--- nomes das propriedades do objeto:
      if(ObjectFind(chart_ID,nameEdit0[i])<0)
        {
         EditCreate(chart_ID,nameEdit0[i],sub_window,x_dist,
                    y_dist+(y_dist_plus*i),obj0WidthHeight*8,
                    obj0WidthHeight,textEdit0[i],"Arial",font_sz,
                    "\n",ALIGN_CENTER,true,CORNER_RIGHT_UPPER,
                    CLR_TEXT_BACK,CLR_PANEL,CLR_BORDER);
         //--- determina e registra o tempo de criação de objeto:
         CreateTimeGet(chart_ID,nameEdit0[i],timeCreateEdit0[i]);
        }
     }
//---
   y_dist=Y_DISTANCE+obj0WidthHeight+(CONTROLS_GAP_XY*2);
//---
   for(int i=QUANT_OBJ_EDIT-1;i>=0;i--)
     {
      //--- para a exibição de textos de valores de propriedade dos objetos:
      if(ObjectFind(chart_ID,nameEdit1[i])<0)
        {
         EditCreate(chart_ID,nameEdit1[i],sub_window,x_dist,
                    y_dist+(y_dist_plus*i),obj0WidthHeight*11,
                    obj0WidthHeight,textEdit1[i],"Arial",font_sz,
                    "\n",ALIGN_LEFT,true,CORNER_RIGHT_UPPER,
                    CLR_TEXT,CLR_PANEL,CLR_BORDER);
         //--- determina e registra o tempo de criação de objeto:
         CreateTimeGet(chart_ID,nameEdit1[i],timeCreateEdit1[i]);
        }
     }
   return;
  }

5.5. No OnDeinit():

"Desabilitar" a flag principal quando deletar o indicador no gráfico, excluindo os objetos pelo prefixo, parando a geração de eventos a partir do timer e remoção do indicador no gráfico, se uma razão de desinicialização, REASON_INITFAILED, for recebida do OnInit():

void OnDeinit(const int reason)
  {
   flagClick=-1;
   ObjectsDeleteAll(0,prefixObj,0,-1);
   EventKillTimer();
//--- exclusão do indicador se o REASON_INITFAILED foi recebida do OnInit.
   if(reason==REASON_INITFAILED)
     {
      ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
     }
//---
   ChartRedraw();
  }

5.6. No OnTimer():

Verificação ocasionais pelo tempo definido no OnInit(), se a notificação sobre eventos de exclusão de objetos gráficos está incluída nas propriedades do gráfico:

void OnTimer()
  {
//--- ativar uma notificação sobre eventos de exclusão de objetos gráficos
   bool res;
   ChartEventObjectDeleteGet(res);
   if(res!=true)
     {ChartEventObjectDeleteSet(true);}
  }


5.7. A função OnChartEvent():

5.7.1. No bloco de manipulação das notificações sobre eventos dos objetos clicando com o mouse:
Na flag de semáforo flagClick igual a 0, então:
  • Primeiro, as verificações são realizadas para averiguar se foi um clique sobre nos botões da parte principal do painel.
  • Se foi um clique no botão de minimização do painel, então a flag flagClick obtém o valor anterior -1 para ações futuras.
  • Se um botão de "minimizar" foi clicado, então a parte principal do painel é minimizado e o botão "maximizar" é criado no canto superior direito do gráfico.
  • A flagClick é obtida com o valor igual a 1 depois disso, o que implica em arrastar os cliques no painel do botão "maximizar" e sua modificação não será autorizada ou removida.
  • Se fosse um clique no botão para remover o indicador no gráfico, então a flag flagClick também exibe o valor -1, antes de qualquer nova ação. Somente então é feita uma tentativa para remover o indicador a partir do gráfico.
  • Se não fosse um clique nos botões do painel de controle, mas em vez disso, sobre o outro objeto de gráfico, exceto para estes objetos do indicador, então o seu nome, a hora de criação e o tipo são determinados e exibidos neste valor de objeto no painel.

Se a flag semáforo, flagClick , e igual a 1, então clicando no botão "maximizar":

  • Esta flag é obtida com o valor -1, antes de outras ações.
  • O botão é removido e o painel é criado.
  • Depois que a flag flagClick é obtida com o valor 0. A flag auxiliar flagResetEventDelete, responsável por resetar os eventos de exclusão do botão "maximizar", obtém o valor 1.
if(id==CHARTEVENT_OBJECT_CLICK)//id = 1
     {
      if(flagClick==0)//se o painel de controle de parte principal está no gráfico
        {
         string clickedChartObject=sparam;
         findPrefixObject=StringFind(clickedChartObject,prefixObj);
         //---
         if(findPrefixObject==0)
           {
            findPrefixObject=-1;
            //---
            for(int i=1;i>=0;i--)
              {
               if(StringCompare(clickedChartObject,nameButton[i],true)==0)
                 {
                  switch(i)
                    {
                     //--- botão "minimizar":
                     case 1:flagClick=-1;
                     MinimizeTable(nameButton,2,textButtUchar,2,
                                   timeCreateButton,obj0WidthHeight);
                     flagClick=1;return;
                     //--- botão para excluir um indicador no gráfico:
                     case 0:flagClick=-1;
                     ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
                     return;
                     default:return;
                    }
                 }
              }
           }
         else//se o clique não foi nos botões do painel de controle,
           {
            SetValuesTable(clickedChartObject);//vamos definir o valor do objeto
            return;
           }
        }
      //--- se um clique foi no painel "maximizar":
      else if(flagClick==1)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)
           {
            string clickedChartObject=sparam;
            findPrefixObject=-1;
            //--- botão do painel "maximizar":
            if(StringCompare(clickedChartObject,nameButton[2],true)==0)
              {
               flagClick=-1;
               //--- ações principais ao pressionar o botão "maximizar" no painel
               ToExpandTheTable(clickedChartObject);
               //---
               flagClick=0;
               //--- redefine o evento para deletar este botão:
               flagResetEventDelete=1;
               //---
               return;
              }
           }
         return;
        }
      return;
     }//if(id==CHARTEVENT_OBJECT_CLICK)

5.7.2. No bloco de manipulação das notificações sobre finalização da edição de texto no objeto gráfico Edit:
Quando existem valores de objetos gráficos no campo deste painel, eles se tornam disponíveis para copiar a partir desses campos. Portanto, se os valores exibidos pelo indicador foram alterados ou excluídos ao copiar acidentalmente/intencionalmente nesses campos, então o texto anterior é restaurado:
else  if(id==CHARTEVENT_OBJECT_ENDEDIT)//id = 3
     {
      findPrefixObject=StringFind(sparam,prefixObj);
      if(findPrefixObject==0)
        {
         string endEditChartObject=sparam;
         findPrefixObject=-1;
         int comparison=-1;
         //---
         for(int i=QUANT_OBJ_EDIT-1;i>=0;i--)
           {
            if(StringCompare(endEditChartObject,nameEdit1[i],true)==0)
              {
               string textNew=ObjectGetString(0,endEditChartObject,OBJPROP_TEXT);
               comparison=StringCompare(textEdit1[i],textNew,true);
               //---
               if(comparison!=0)
                 {
                  ObSetString(0,nameEdit1[i],OBJPROP_TEXT,textEdit1[i]);
                 }
               //---
               ChartRedraw();
               return;
              }
           }//for(int i=0;i<QUANT_OBJ_EDIT;i++)
         return;
        }//if(findPrefixObject==0)
      return;
     }//if(id==CHARTEVENT_OBJECT_ENDEDIT)

5.7.3. No bloco para manipular notificações sobre a exclusão ou remoção de objetos:
  • Se a flag flagClick é igual a -1, então ao sair do evento ocorre a manipulação do bloco, uma vez que o seu valor é obtido na flag durante as ações "amigáveis".
  • Se a primeira verificação é concluída e revelou que a flag auxiliar, flagResetEventDelete, para eventos resetados excede 0 e o objeto de evento refere-se ao painel de objetos, então os eventos são redefinidos e excluídos do bloco de manipulação.
  • Se as flags flagClick e a flagResetEventDelete são iguais a 0, então quando o nome do objeto do evento corresponde a quaisquer objetos "protegidos", ações de recuperação de parte principal do painel são realizadas usando a função, já conhecida, RestoringObjArrayAll() e as funções de acompanhamento.
  • Se a flag principal flagClick é igual a 1 no evento, então quando o nome do objeto pelo evento combina com o botão "maximizar" do painel, o indicador é removido a partir do gráfico.

else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)
     {
      if(flagClick==-1)return;
      else if(flagResetEventDelete>0)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)
           {
            findPrefixObject=-1;
            flagResetEventDelete=flagResetEventDelete-1;
            //---
            return;
           }
         return;
        }
      //--- verificar se o nosso objeto é excluído/removido:
      else if(flagClick==0 && flagResetEventDelete==0)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         //---
         if(findPrefixObject==0)
           {
            string chartObject=sparam;
            findPrefixObject=-1;
            int res=-1;
            //--- ações se o objeto é a tela do painel de controle:
            res=RestoringObjOneAll(chartObject,prefixObj,flagClick,
                                   flagResetEventDelete,nameRectLabel,
                                   timeCreateRectLabel,QUANT_OBJ_ALL);
            //---
            if(res==1){return;}
            //--- ações se o objeto é o botão do painel de controle:
            res=RestoringObjArrayAll(chartObject,prefixObj,flagClick,
                                     flagResetEventDelete,QUANT_OBJ_BUTTON,
                                     nameButton,timeCreateButton,QUANT_OBJ_ALL);
            //---
            if(res==1){return;}
            //--- ações se o objeto for uma nota (Edit0) no painel de controle:
            res=RestoringObjArrayAll(chartObject,prefixObj,flagClick,
                                     flagResetEventDelete,QUANT_OBJ_EDIT,
                                     nameEdit0,timeCreateEdit0,QUANT_OBJ_ALL);
            //---
            if(res==1){return;}
            //--- ações se o objeto for uma nota (Edit1) no painel de controle:
            res=RestoringObjArrayAll(chartObject,prefixObj,flagClick,
                                     flagResetEventDelete,QUANT_OBJ_EDIT,
                                     nameEdit1,timeCreateEdit1,QUANT_OBJ_ALL);
            //---
            return;
           }
        }
      else if(flagClick==1)//se o botão do painel de minimizar for alterado ou excluído
        {
         string clickedChartObject=sparam;
         if(StringCompare(clickedChartObject,nameButton[2],true)==0)
           {
            flagClick=-1;
            //--- removendo o indicador do gráfico:
            ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
            return;
           }
         return;
        }
      return;
     }//else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)

O novo recurso neste bloco é a função RestoringObjOneAll() aplicada quando da tela do painel é alterada ou eliminada. É similar a função RestoringObjArrayAll() presente aqui e usada anteriormente.
//+-----------------------------------------------------------------------+
//|string sparam = nome do objeto transmitido para OnChartEvent()         |
//|string prefix_obj = prefixo comum dos objetos protegidos               |
//|int &flag_antivandal = flag para habilitar/desbilitar o alarme         |
//|int &flag_reset = flag para resetar eventos                            |
//|string name = nome do objeto                                           |
//|datetime time_create = tempo de criação do objeto                      |
//|int quant_obj_all = número total de objetos recuperáveis               |
//|of the program, >= quant_obj (se -1, então são iguais a quant_obj)     |
//|const long chart_ID = 0 identificador de gráfico                       |
//|const int sub_window = 0 índice da janela                              |
//|int start_pos=0 a posição no nome de objetos para procurar por prefixo |
//+-----------------------------------------------------------------------+
int RestoringObjOneAll(const string sparam,
                       const string prefix_obj,
                       int &flag_antivandal,
                       int &flag_reset,
                       string name,
                       datetime time_create,
                       const int quant_obj_all,
                       const long chart_ID=0,
                       const int sub_window=0,
                       int start_pos=0
                       )
  {
   int res=-1;
   int comparison=-1;
//---
   comparison=StringCompare(sparam,name,true);
//--- ações quando nomes se combinam por completo:
   if(comparison==0)
     {
      res=1;
      comparison=-1;
      //--- notificação sobre a ocorrência de caso "seguro":
      Print(LINE_NUMBER,textRestoring,sparam);
      //---
      flag_antivandal=-1;
      flag_reset=quant_obj_all-1;
      //---
      ObDeletePrefixFlag(flag_reset,prefix_obj,chart_ID,sub_window,start_pos);
      //---
      ChartRedraw();
      //---
      ObDelCreateTimeExcept0(time_create,chart_ID,sub_window,-1);
      //---
      PanelCreate();
      //---
      ChartRedraw();
      //---
      flag_antivandal=0;
      //---
      return(res);
     }
   return(res);
  }

Um código completo do indicador está anexo com o nome: id_name_object.


6. Conclusão

Perceba que os esquemas mais fáceis e suas variações, com base no sistema de flags mencionados neste artigo, podem ser adicionados ao código pronto em relação a um ou muitos objetos, se houver tal necessidade ou possibilidade. Não há nenhuma necessidade de fazer alterações consideráveis nas partes do código que são responsáveis pela operação principal do programa.

Se necessário, outras ações relevantes podem ser providenciadas no código para casos como:

  • alteração de alguns objetos no painel de controle com outro programa, por exemplo, devido à operação incorreta do seu código;
  • exclusão das notificações de eventos quando existem muitas delas.

Por exemplo, você pode implementar algumas verificações adicionais durante as ações "de rotina" no painel, bem como controles de revisão seletivas ou totais sobre os dados importantes exibidos no painel do timer, se for justificável em relação ao código.

No Expert Advisor, por exemplo, você pode organizar uma animação durante alteração ou remoção de objetos gráficos, com ou sem exibição de mensagens não autorizadas, se não for causar uma interferência significativa com o desempenho das atividades de negociação.

Exemplificando, ao exibir mensagens você pode proporcionar a possibilidade da seleção, pressionando o respectivo botão: excluindo o programa do gráfico ou fazendo uma tentativa para restaurar os objetos danificados. Você também pode colocar um contador para exibir um aviso nas situações de remoção não autorizada ou na alteração dos objetos: "Esta é a "Xª" interferência não autorizada nos objetos do programa. Após o "X°" caso, o programa será excluido do gráfico. Por favor, procure o possível motivo desta situação".

Baseado nas grandes oportunidades da linguagem de programação MQL5 e, certamente, com sua mente inquisitiva e cheia de conhecimentos, você poderá criar várias opções, incluindo as que não foram abordadas neste artigo, se necessário.

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

Avaliação e seleção de variáveis para os modelos de aprendizado da máquina Avaliação e seleção de variáveis para os modelos de aprendizado da máquina
Este artigo foca sobre as especificidades de escolha, o pré-condicionamento e avaliação das variáveis de entrada (preditoras) para uso em modelos de aprendizagem da máquina. Novas abordagens e oportunidades de análises preditoras profundas e suas influências no possível sobre-ajuste (overfitting) dos modelos serão consideradas. O resultado global do uso de modelos, em grande parte, depende do resultado desta etapa. Vamos analisar dois pacotes, oferecendo abordagens novas e originais para a seleção dos preditores.
Agora a plataforma MetaTrader 5 possui um sistema de cobertura de registro de posições Agora a plataforma MetaTrader 5 possui um sistema de cobertura de registro de posições
Para ampliar as possibilidades dos traders de retail-Forex, foi adicionado à plataforma a cobertura (segundo sistema de registro). Agora, segundo o instrumento, você pode ter várias posições, incluindo posições opostas. Isto permite implementar estratégias de negociação com o assim chamado bloqueio, por outras palavras, se o preço estiver contra o trader, ele terá a possibilidade de abrir uma posição na direção oposta.
Usando Layouts e Containers para Controles da Interface Gráfica do Usuário (GUI): A Classe CGrid Usando Layouts e Containers para Controles da Interface Gráfica do Usuário (GUI): A Classe CGrid
Este artigo apresenta um método alternativo de criação da Interface Gráfica do Usuário (GUI) com base em layouts e containers, usando um gerenciador de layout - a classe CGrid. A classe CGrid é um comando auxiliar, atua como um container para outros containers e faz o controle usando um layout de grade.
MQL5 Cookbook: Implementando seu próprio Depth of Market (Book de Ofertas) MQL5 Cookbook: Implementando seu próprio Depth of Market (Book de Ofertas)
Este artigo demonstra como utilizar o Depth of Market de forma programática e descreve o princípio de funcionamento da classe CMarketBook, que pode expandir a biblioteca padrão de classes de MQL5 e oferecer métodos convenientes para usar o Depth of Market (DOM). No Brasil o Livro de Ofertas faz o papel do DOM e registra todas as ordens por nível de preço.