English Русский 中文 Español Deutsch 日本語
preview
Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 10): Automação (II)

Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 10): Automação (II)

MetaTrader 5Negociação | 16 dezembro 2022, 09:54
758 8
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Aprendendo a construindo um EA que opera de forma automática (Parte 09): Automação (I), mostrei como você faz para criar um sistema de breakeven e trailing stop, que usam dois modos diferentes. Um que utilizamos a linha de stop, em posições do tipo OCO. E uma outra forma, que usado uma ordem pendente, para servir como ponto de stop. Com expliquei lá, temos vantagens e desvantagens nestes métodos.

Mas mesmo usando um sistema, bem simples de automação, e dando toda a explicação de como fazer para gerar os cálculos. Aquele EA, não é de fato um EA automatizado. Apesar de ele já contar com o primeiro nível de automação, ele ainda assim, é operado de forma manual. Bem, para ser mais preciso, semi manual. Já que a parte responsável, por mover a linha, ou ordem de stop, é feita pelo próprio EA, com base nas configurações definidas pelo usuário.

Neste artigo, iremos ver como adicionar, mais um nível de automação no EA, de forma que ele possa ficar o tempo todo no gráfico, em que ele será utilizado. Não se preocupem, isto não quer dizer que ele irá ficar 24 horas, lançando ordens sem parar, por nem um instante. Mesmo por que, mostrei como você cria, uma forma de controlar este possível disparate no sistema, a fim de evitar, que o EA fique operando um volume maior, do que o aceito por você.

O tipo de automação, que irei mostrar aqui, é algo que muitas plataforma é oferecido, como sendo uma forma de fazer, com que o operador, não fique todo o tempo operando. Você define um horário, no qual poderá de fato enviar ordens, ou abrir posição, e dentro daquele horário definido, basicamente e teoricamente você poderá usar a plataforma.

O fato de ser dito, que o uso da plataforma naquele horário será teórico, se deve ao fato, de que você podem vim a desabilitar o sistema, então toda aquela metodologia, vai por água abaixo. Mas cada um sabe, ou deveria saber, quando dar um tempo, mas diferente de um operador humano, um EA, não irá fazer as coisas desta forma.

Devo ressaltar, que você deve sempre selar por qualquer coisa que venha a ser adicionado, ou removido do EA, a fim de que ele trabalhe dentro de uma determinada metodologia, mas de uma forma ou de outra, você nunca deve de fato deixar, ou permitir, que o EA fique operando sem ser supervisionado. Isto é um conselho que estou dando, no qual você deverá sempre ter em mente. NUNCA DEIXE UM EA, OPERAR SEM SUPERVISÃO.

Mas como vamos criar de fato o controle de horário ?!?!

Existem diversas maneiras, e formas de fazer isto. Este tipo de implementação, depende muito mais da forma como o programador deseja colocar ele no código do EA, do que necessariamente, da programação que deverá ser feita. Já que a intenção, destes artigos, é mostrar uma forma de criar um EA automático, e tentando fazer isto, de um maneira que seja, a mais simples possível, iremos fazer com que o código implementado, possa ser removido com facilidade, ao mesmo tempo, que você também poderá usá-lo, em um EA manual, caso deseje implantar uma forma de controle de seu próprio método de operar. Bem, mas isto é algo de nível pessoal de cada individuo. 


Planejamento

A questão de fato, que faz todo o sistema de desenvolvimento, ser bem interessante. É que cada pessoa, pode pensar em maneiras, e meios diferentes de promover um determinado tipo de conceito. Ou até mesmo a forma como a coisa em si irá funcionar, mesmo quando os resultados serão bem parecidos, ou próximos um do outro. A maneira de implementar isto pode variar.

Mas aqui no MQL5, sinto falta de uma forma de programação orientada a objetos, presentes no C++, a chamada herança múltipla. Se bem que se esta metodologia for mal aplicada, você poderá ter sérios problemas, ao usar a herança múltipla. Já que para fazer uso correto da mesma, você precisa tomar alguns cuidados durante a programação. Mas mesmo sem contar com este recurso do C++, ainda assim, é possível gerar alguns tipos de código, com o que será feito aqui, mantendo as coisas dentro do sistema de herança. Ao mesmo tempo que promovemos a ocultação do código.

Para entender, a diferença entre o que seria usar a herança múltipla, e não usar ela, veja as figuras abaixo. Nota: A classe C_ControlOfTime, é o nome da classe que usaremos para controlar a faixa de horários, permitidos para o EA funcionar.

Figura 01

Figura 01 - Modelagem usando Herança Múltipla

Figura 02

Figura 02 - Modelagem sem Herança Múltipla.

Reparem que a diferença, entre a figura 01 e a figura 02, é o fato de que na primeira figura, a classe C_Manager, irá receber via herança. Os métodos implementados em ambas classes: C_Orders e C_ControlOfTime, de maneira que ela, a classe C_Manager, iria crescer rapidamente. Mas como não podemos fazer isto no MQL5, temos que usar uma outra abordagem, e esta abordagem, é a mostrada na figura 02, onde a classe C_ControlOfTime, irá herdar a classe C_Orders.

Mas por que não fazer o contrário ?!?! O motivo é o EA. Não quero que de forma alguma, ele tenha acesso a classe C_Orders diretamente. No entanto, ele precisará ter acesso o que estará implementado na classe C_ControlOfTime. Mas como disse acima, a grande graça em programar, é que muitas vezes, podemos seguir caminhos diferentes, e no final ter algo cujo funcionamento, será bem parecido com algo que outro programador, possa ter criado.

O que mostro, é apenas uma entre tantas possibilidade, de se obter o mesmo resultado. O que de fato importa: É o resultado. A forma de se obter ele, pouco importa. Desde é claro, você faça as coisas de modo a não comprometer, a integridade do código, que você esteja criando. Sinta-se livre para criar sua própria técnica, e maneira de fazer as coisas, pois a programação nos permite fazer isto.


Últimos detalhes a serem analisados antes da implementação

Uma vez definida a ideia de usar a modelagem de classe, vista na figura 02, passamos a então segunda etapa de planejamento, a fim de criar a classe de controle de faixa horária.

O que devemos definir neste momento é: Como será feita a definição da faixa de horário, isto em termos de formas que um operador possa usar, a fim de definir facilmente esta faixa. Uma das forma, seria usar um arquivo, este iria conter os dados das faixas de horário plausíveis, de serem adotadas pelo EA.

Se bem que o uso de um arquivo, é uma questão bastante controvérsia para este tipo de coisa. Já que ao usar um arquivo, você pode acabar dando mais liberdade para o operador definir diversas faixas de horário, dentro de um mesmo dia. O que em alguns casos é bastante razoável, dado o fato de que em muitos mercados, podemos ter momentos de bastante stress. Onde o EA pode acabar se dando mal, caso esteja sendo executado naquela faixa de horário.

Em compensação, a parte de como de fato as definições de horário, seriam implementadas dentro do arquivo, pode em alguns casos, acabar complicando muito uma tarefa, que para a grande maioria dos operadores, é algo simples. Já que ele irá fazer com que o EA, permaneça em execução, apenas dentro de uma única faixa de horário.

Neste caso, e em grande maioria das vezes, este será um fato muito mais comum, do que possa parecer. Assim podemos fazer algo um pouco melhor, e até mesmo algo que nos permite ficar em um meio termo. Já que a plataforma MetaTrader 5, nos permite salvar e carregar as configurações, que desejamos em arquivos. Então bastaria você criar uma configuração, para um dado período. Como por exemplo: Pode-se ter uma configuração a ser usada na parte da manhã, e outra para a parte da tarde. Onde assim que o EA for bloqueado pelo sistema de controle de faixa horária, seja por conta que o operador irá descansar um pouco, ele, operador, poderá quando for colocar o EA novamente em funcionamento, carregar diretamente na plataforma MetaTrader 5, um arquivo de configuração, que será mantido pela própria plataforma. Isto ao meu ver é de grande ajuda, já que nos poupa o trabalho, de criar arquivos de configuração, apenas para este proposito.

Figura 03

Figura 03 - Área de configuração do EA

Na figura 03, você poderá ver o sistema de configuração do EA. Uma vez que você tenha configurado ele, poderá salvar esta configuração, usado o botão < SALVAR >. Quando desejar carregar alguma configuração salva, você usaria o botão < ABRIR >. Desta forma você pode ter um sistema, onde precisaremos implementar bem menos código. Ao mesmo tempo, que teremos uma maior confiabilidade no nosso sistema. Já que parte do trabalho, será executado pelo próprio MetaTrader 5. Nos poupando imensamente o trabalho de testes, a fim de garantir que tudo estará funcionando de maneira adequada.

Um último detalhe a ser definido, é com relação as ordens ou posições fora da faixa de horário. O que fazer com elas ?!?! Pois bem, o EA não poderá enviar pedidos de abertura de posição, ou posicionar uma ordem fora da faixa. No entanto, ele poderá manipular uma ordem, ou fechar uma posição, caso estas já estejam no servidor fora da faixa. Mas se você desejar, pode também bloquear isto, modificar ou manter esta minha politica. Mas esta escolha, deixo para que você faça, segundo a sua própria politica de operação.


O nascimento da classe C_ControlOfTime

A primeira coisa a ser feita, dentro do arquivo de cabeçalho C_ControlOfTime.mqh, será criar o código visto abaixo:

#include "C_Orders.mqh"
//+------------------------------------------------------------------+
class C_ControlOfTime : protected C_Orders
{
        private :
                struct st_00
                {
                        datetime Init,
                                 End;
                }m_InfoCtrl[SATURDAY + 1];
//+------------------------------------------------------------------+
        public  :

//... Procedimentos da classe ...

};

Reparem em uma coisa, estamos adicionando o arquivo de cabeçalho C_Orders.mqh, isto para ter acesso a classe C_Orders. Desta forma, a classe C_ControlOfTime, irá herdar a classe C_Orders, usando o método protegido. Já expliquei as consequências de usar este tipo de herança, em outro artigo desta mesma serie. O artigo é este daqui:  Aprendendo a construindo um EA que opera de forma automática ( Parte 05 ) - Gatilhos manuais ( II ).

Agora dentro da clausula privada da classe de controle, foi adicionada uma estrutura, que será usada como sendo um array, com 7 elementos. Mas por que não definir 7, ao invés de usar esta declaração doida ?!?! O fato de fazer assim, é que o valor SATURDAY, é definido internamente na linguagem MQL5, como sendo uma enumeração, ENUM_DAY_OF_WEEK, assim fica mais claro, o fato de que iremos usar os dias da semana, para acessar o array.

Isto é denominado, como sendo um aumento do nível da linguagem, já que para alguém que irá ler o código, uma palavra, é bem mais expressiva, que um valor numérico. Muito bem, dentro desta estrutura, temos apenas dois elementos, um que indica o ponto inicial, para operações do EA, e outro que indica, o ponto onde o EA, não mais poderá operar, ambos elementos são do tipo datetime.

Definido este array, podemos passar para o nosso primeiro código, dentro da classe, este é o constructor da classe, que pode ser visto abaixo:

                C_ControlOfTime(const ulong magic)
                        :C_Orders(magic)
                        {
                                ResetLastError();
                                for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++) ZeroMemory(m_InfoCtrl[c0]);
                        }

Para muita gente, isto daqui pode ser bem estranho, mas é apenas uma forma de programação, e um nível um pouco mais alto do que normalmente vemos. O fato de que dizer, que é uma programação de alto nível, nada tem haver com a minha experiência em programação, como já disse:

É mais simples entender um código, cuja informações se parecem com uma linguagem natural, do que um código, mais voltado a valores numéricos

Por conta disto, que o laço neste constructor, parece quase falar o que ele esta fazendo, isto é que define se um código, é de alto nível, ou de nível mais baixo.

Acredito que você não encontra, de fato nenhum tipo de problema em entender este constructor. Pois já expliquei, em outros momentos, como funciona um código no constructor, na dúvida, veja os artigos anteriores desta mesma serie. A única coisa aqui diferente, é de fato o laço, onde indicamos uma variável, que irá iniciar com o valor definido no Domingo ( SUNDAY ), e irá terminar seu trabalho no Sábado ( SATURDAY ), mas fora isto ,não existe nada muito complicado.

O detalhe que este laço, somente tem um funcionamento correto, pelo fato que na enumeração, o Domingo, é definido como sendo o primeiro dia da semana, e o sábado, é definido como sendo o último dia da semana. No entanto, se a segunda-feira ( MONDAY ), fosse definida como sendo o primeiro dia, este laço iria falhar, gerando um erro no momento que o código fosse ser executado pela plataforma MetaTrader 5. Este é o único cuidado, que você deve sempre ter, ao usar um código de alto nível, já que se as definições, estiverem incorretas, o código irá gerar diversas falhas de RUN-TIME ( falhas no momento que ele for colocado para ser executado ).

Feito isto, podemos passar para o próximo procedimento da nossa classe:

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
                        {
                                string szRes[];
                                bool bLocal;
                                
                                if (_LastError != ERR_SUCCESS) return;
                                if ((index > SATURDAY) || (index < SUNDAY)) return;
                                if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
                                {
                                        m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
                                        m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
                                        bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
                                        if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
                                }
                                if ((_LastError != ERR_SUCCESS) || (!bLocal))
                                {
                                        Print("Error in the declaration of the time of day: ", EnumToString(index));
                                        ExpertRemove();
                                }
                        }

Este merece um pouco mais de explicação, já que não é tão simples assim, para quem esteja aprendendo, ou tem muito pouco tempo de experiência. Sei disto, pois também já estive no seu lugar, meu caro leitor. Então, para explicar com mais detalhes, e clareza o código acima, vamos desmembra-lo em partes menores.

if (_LastError != ERR_SUCCESS) return;
if ((index > SATURDAY) || (index < SUNDAY)) return;

Sempre que possível você deve testar, se existe algum tipo de erro no sistema, e já que este código, é extremamente sensível, e sujeito a erros, dada a natureza do que estaremos tratando. Iniciamos verificando se ouve alguma falha, antes de que esta rotina especifica, viesse a ser chamada, caso tenhamos tido alguma falha, o procedimento será encerrado de imediato.

Um segundo teste, também se faz necessário, este é para evitar falhas do tipo casualidade. Pois se por qualquer motivo, fizermos uma chamada, cujo dia da semana, que na visão do processador, não passa de um numero. E este pode ser qualquer um, não estiver no intervalo entre o sábado ( SATURDAY ) e o domingo ( SUNDAY ). Não iremos continuar a executar de nenhuma outra linha dentro da função.

Uma vez que esta primeira etapa, foi perfeitamente aceita, e código passou por estes primeiros testes. Iremos fazer uma tradução do conteúdo passado pelo chamador:

if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
{
        m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
        m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
        bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
        if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
}

Aqui temos um código bem mais interessante de ser observado e compreendido, principalmente por quem esteja começando a aprender a programação. Aqui usamos a função StringSplit, de forma a " quebrar " a informação que recebemos do chamador em duas partes, o carácter que indica onde a informação será quebrada, é o sinal de menos ( - ), o que queremos e esperamos são duas informações. Caso venhamos a ter um numero diferente, isto será tido como sendo um erro, como pode ser visto nos exemplos abaixo:

12:34 - - 18:34 <-- Isto é um erro
12:32  -  18:34 <-- Informação correta
12:32     18:34 <-- Isto é um erro
       -  18:34 <-- Isto é um erro
12:34  -        <-- Isto é um erro

O conteúdo interno da informação, não importa para a função StringSplit. Ela apenas irá "quebrar" a informação, seguindo o que estará sendo usado como delimitador entre as informações. Esta rotina é extremamente útil em diversos momento, estude ela com calma. Pois ela ajuda e muito a separar as coisas dentro de uma cadeia de caracteres.

Uma vez que temos duas informa informações. Iremos usar a função StringToTime, para traduzir estas informações para um código no formato de data.

Aqui temos um detalhe: Como a informação que estaremos dando, contem apenas e somente valores de hora e minuto. E não estamos interessados em informar, a questão da data em particular. Mas nada impede de você informar uma data especifica. No entanto, como esta implementado, a data será ignorada. Você somente precisará informar o horário. A função StringToTime, irá adicionar automaticamente a data atual para nos. Isto de fato não é um problema, se não fosse um fato que será visto mais para frente.

Então para eliminar o valor da data atual, que foi adicionada pela função StringToTime. Usamos esta fatoração, onde o resultado será apenas o valor do horário informado pelo chamador. Caso você não queira de fato efetuar a remoção da data, que a função StringToTime adicionou, e esta será sempre a data atual, bastará remover esta fatoração indicada.

Temos um primeiro teste depois que os valores foram convertido. Isto é importante, pois não queremos ter que nos preocupar depois com este tipo de problema. Aqui testamos a fim de verificar se o horário inicial, é menor ou igual, que o valor indicado como horário final. Se isto estiver correto, não precisaremos nos preocupar futuramente, caso contrário, iremos informa que os valores não são adequados.

Temos também um outro teste, pois já que não estamos informando uma data, irá ser gerando um erro de RUN-TIME ( erro de tempo de execução ). Este tipo de erro, costuma ser bem chato. Mas aqui lidamos com ele de uma maneira bem adequada. Já que, ao ser detectado tal erro, iremos remover a indicação de que ele aconteceu. Já que sabemos previamente que não será informado uma data, e esta irá gerar este erro.

Nota importante: Sempre que você, durante os testes do EA, notar que esta sendo disparado um erro de RUN-TIME, que não viola a integridade do EA a ponto de ele se tornar instável ou inseguro. Adicione depois do código, que possa estar gerando tal erro, este tipo de teste, a fim de minimizar a geração de mensagens de erros desta natureza. Alguns dos erros de RUN-TIME, pode ser ignorados, por serem de baixa gravidade, não comprometendo de forma alguma o trabalho do EA. Já outros precisam ser tratados de uma outra maneira, fazendo com que o EA, deixe de continuar no gráfico. Isto se chama: ROBUSTEZ IMPOSTA. pelo fato de você estar ciente que a falha pode acontecer. Mas ainda assim, nota que ela não compromete o sistema.

Depois de passar pelo sistema que faz a conversão dos valores. Iremos ter o seguinte fragmento:

if ((_LastError != ERR_SUCCESS) || (!bLocal))
{
        Print("Error in the declaration of the time of day: ", EnumToString(index));
        ExpertRemove();
}

Observem uma coisa importante aqui: Caso o sistema tenha gerado algum erro mais grave durante a conversão dos valores, esta variável constante, irá ter um valor diferente de ERR_SUCESS. Isto indica que não podemos confiar nos dados que foram utilizados, ou informados pelo chamado. Igual condição temos, caso esta variável daqui, estiver com um valor falso, ou seja, alguma coisa, em algum ponto faz com que ela falha-se. De qualquer forma, iremos informar isto para o operador via mensagem que será impressa no terminal, e um pedido para encerramento do EA, será protocolado na plataforma MT5.

Desta forma, acredito que tenha dado para compreender toda a função, já que ela foi detalhadamente explicada. Mas ainda não acabou, temos mais uma outra função a vista:

virtual const bool CtrlTimeIsPassed(void) final
                        {
                                datetime dt;
                                MqlDateTime mdt;
                                
                                TimeToStruct(TimeLocal(), mdt);
                                dt = (mdt.hour * 3600) + (mdt.min * 60);
                                return ((m_InfoCtrl[mdt.day_of_week].Init <= dt) && (m_InfoCtrl[mdt.day_of_week].End >= dt));
                        }

Aqui temos uma questão, que de certa forma parece intrigante, mas ao mesmo tempo merece ser explorada, e o motivo é: Nunca aceite algo, como sendo uma verdade, antes mesmo de experimentar possíveis variações, que podem ter os mesmos resultados, mas de uma forma, consideravelmente mais simples.

Mas antes de entrar nesta questão. Vamos entender a função acima. Ela irá capturar a hora e data local e converte-la em uma estrutura, de forma a desmembrar os dados contido ali. Depois de ter feito isto, iremos montar um valor que irá conter apenas e somente a hora e os minutos. Pois foram estes os valores que usamos na rotina anterior, onde usamos apenas hora e minutos. Este é o tão problema mencionando anteriormente, se você não remove-se o valor da data na rotina de conversão, aqui você precisaria adicionar o valor da data. Uma vez que temos estes valores, iremos testar eles para verificar se estão dentro da faixa, se estiverem, retornaremos verdadeiro, se estiverem fora da faixa definida para o dia, retornaremos falso.

Agora vem uma outra questão. Por que fazer todo este trabalho de capturar, desmembrar, reconstituir, para finalmente testar se o horário local, está ou não dentro de uma faixa de valores ?!?! Não seria mais simples, usar pura e simplesmente, um código, onde capturamos a hora local e testamos ele, frente a faixa definida ?!?! Sim, de fato seria bem mais simples. Mas, e sempre existe um mas, temos um pequeno problema: O dia da semana.

Se você fosse definir a faixa de uso do EA, baseado apenas e somente no dia atual, tudo bem. Apenas saber a hora local seria suficiente. Mas acontece que estamos definindo valores, para os 7 dias da semana. E a forma mais simples, de saber que dia da semana estamos, é usando exatamente todo o trabalho, que foi feito na rotina acima.

Então de fato, tudo depende de como você esteja implementando o sistema. Se você o está implementando, de maneira a usar ele, de uma forma mais simples. As rotinas e procedimentos, que você precisa, de fato criar e definir, serão consideravelmente mais simples. Se você, esta criando ele, para um uso mais abrangente, as rotinas e procedimentos, serão consideravelmente mais complicadas.

Por conta disto, que você precisa pensar e analisar, antes realmente começar a codificar. Ou você irá acabar em um beco sem saída. Onde um código recém construído, faz com que os códigos mais antigos, não consigam atender a demanda. Assim você tem que mudar os códigos mais antigos, e quando se dá conta, estará todo enrolado com tamanha confusão. Precisando descartar tudo e começa do zero.

Antes de mudar de assunto, gostaria de enfatizar um detalhe da rotina acima. Se você por um acaso desejar que o EA fique, 24 horas direto ligado. Com a plataforma em funcionamento, durante todo este tempo. Pode ficar com receio, de que o EA na virada do dia, não saiba o momento de começar a voltar a operar. Isto de fato não irá acontecer, já que a cada chamada a função, ela irá reavaliar toda a situação, e irá usar o dia da semana atual. A fim de verificar, se o EA poderá ou não efetuar alguma operação.

Por exemplo, suponhamos que você diga ao EA, que ele poderá operar na segunda, entre 04:15 até as 22:50. Na terça ele poderá fazer isto, entre 3:15 e 20:45. Você pode simplesmente ligar ele, na segunda e deixá-lo rodando até na terça. Pois assim que o dia virar, da segunda para a terça. Automaticamente ele irá começar a olhar, qual é o período permitido para operar na terça ... Por conta disto, que foi decidido usar o modo semana, e não uma definição baseada no dia atual.

Talvez tenha passado batido, alguns detalhes desta classe, mas deixei para falar disto apenas agora, por não querer complicar em demasia a explicação, do funcionamento das rotinas. Mas vamos ver com calma, um detalhe bastante importante, quando se lida com herança de funções. Se você reparar irá ver que tanto a rotina SetInfoCtrl, quanto CtrlTimeIsPassed, tem declarações bastante estranhas. Por que desta declarações ?!?! Qual o sentido, ou utilidade em se fazer isto ?!?! Vamos ver com calma as declarações, elas estão em destaque abaixo:

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
//+------------------------------------------------------------------+
virtual const bool CtrlTimeIsPassed(void) final
//+------------------------------------------------------------------+

Aqui, cada uma das palavras, tem motivo e necessidade de serem ditas, em cada uma das declarações, nada e absolutamente nada, aqui esta sendo colocado para enfeitar o código, se bem que alguns costumam fazer isto, mas isto já seria uma outra historia.

Vamos lá, o que realmente importa nestas declarações, é a palavra reservada final. Esta é a grande questão da existência da palavra virtual na declaração. O fato é que, quando você cria uma classe, você pode trabalhar nela de diversas maneiras, sobrescrevendo métodos, modificando a forma como uma função da classe pai, será executada em uma classe filho, criando novas formas baseadas em um trabalho mais primitivo, enfim ... Quando você, em uma classe, adiciona em uma declaração de função, ou procedimento, a palavra final, da forma que esta sedo mostrado acima, você estará dizendo, ao compilador, que uma classe filho, não poderá, modificar de forma alguma, nem mesmo sobrescrevendo, o que é muito comum, a função, ou procedimento, escrito na classe pai.

Vou frisar isto novamente, pois é importante: Ao usar a palavra final, na declaração, você esta dizendo ao compilador, que uma classe filho, não poderá, modificar de forma alguma, nem mesmo sobrescrevendo, a função ou procedimento, herdado, que esteja escrito na classe pai, que recebeu a palavra final, na declaração.

Desta forma, garantimos que se alguma classe, que herdar esta classe daqui, com os seus métodos e variáveis, tentar modificar, estes métodos, esta tentativa irá ser considerada um erro, e o programa não será compilado. A palavra virtual, serve justamente para promover a possibilidade de mudança, mas a palavra final evita tal mudança. Mas no frigir dos ovos, quem realmente dita a regra, é a palavra final. Então sempre que desejar garantir, que um procedimento ou função, não venha a sofre modificação indevida, dentro de uma classe filho, trave esta possibilidade de mudança, adicionando uma declaração, conforme mostrada acima.

Isto irá evitar, muita dor de cabeça se você estiver trabalhando com muitas classe, e com um nível profundo de herança.


Forjando a classe C_ControlOfTime na classe C_Manager e usando no EA

Agora finalmente, podemos fazer a ligação da classe C_ControlOfTime, com a classe C_Manager, a fim de que o EA, passe a ter um novo tipo de parâmetro de trabalho, para fazer isto, promovemos a seguinte mudança no código da classe C_Manager:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Orders.mqh"
#include "C_ControlOfTime.mqh"
//+------------------------------------------------------------------+
#define def_MAX_LEVERAGE                10
#define def_ORDER_FINISH                false
//+------------------------------------------------------------------+
class C_Manager : private C_Orders
class C_Manager : public C_ControlOfTime

As linha riscadas, foram removidas do código original, e no lugar delas novas linhas surgiram. Mas repare o fato de que a classe C_Manager, irá herdar de forma publica a classe C_ControlOfTime. Estamos simplesmente incrementando, tudo que esta na classe C_ControlOfTime, para dentro da classe C_Manager. Isto aumenta a capacidade da classe C_Manager, sem de fato aumentar o seu código. Caso não queiramos mais as capacidades, que foram adicionadas ao herdar a classe C_ControlOfTime. Tudo que precisamos fazer, é remover esta herança, e algum possível ponto de referencia que estará dentro, da classe C_Manager. Simples assim.

Desta forma, não modificamos o nível de confiabilidade da classe C_Manager. Pois ela continuará funcionando, com o máximo de segurança, estabilidade e robustez, como se nada tivesse acontecido. Caso a classe C_ControlOfTime, começe a provocar instabilidades na classe C_Manager, bastará remover a classe C_ControlOfTime, que a classe C_Manager, voltará a ficar estável.

Percebem o por que do meu imenso prazer, em criar tudo em forma de classes, e não como sendo funções espalhadas ?!?! A coisa vai aumentando, e melhorando muito rapidamente, e de forma a sempre ter o maior nível possível de estabilidade, e confiabilidade, que a linguagem possa nos fornecer.

Agora já que os constructores, precisam ser de alguma maneira referenciados. Veja no fragmento abaixo como estará o novo constructor da classe:

//+------------------------------------------------------------------+
                C_Manager(const ulong magic, double FinanceStop, double FinanceTake, uint Leverage, bool IsDayTrade, double Trigger)
                        :C_ControlOfTime(magic),
                        :C_Orders(magic),
                        m_bAccountHedging(false),
                        m_TicketPending(0),
                        m_Trigger(Trigger)
                        {
                                string szInfo;
                                
                                ResetLastError();
                                ZeroMemory(m_Position);
                                m_InfosManager.FinanceStop = FinanceStop;

// ... Restante do código interno do constructor ....

                        }
//+------------------------------------------------------------------+

A mesma coisa que aconteceu na declaração, acontece aqui também, a linha riscada, foi removida. No lugar dela veio uma nova linha, a fim de passar os dados para o constructor da classe C_ControlOfTime. Este sim irá referenciar o constructor da classe C_Orders, a fim de que este receba o numero mágico, que precisará para envio das ordens.

Agora para terminar este tópico, os pontos de real uso do controle de horário, é feito nas seguintes funções:

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                if (!CtrlTimeIsPassed()) return;
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_TicketPending > 0) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                m_TicketPending = C_Orders::CreateOrder(type, Price, (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceStop), (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceTake), m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                ulong tmp;
                                
                                if (!CtrlTimeIsPassed()) return;
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                tmp = C_Orders::ToMarket(type, (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceStop), (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceTake), m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                                m_Position.Ticket = (m_bAccountHedging ? tmp : (m_Position.Ticket > 0 ? m_Position.Ticket : tmp));
                        }
//+------------------------------------------------------------------+

Nenhuma outra função dentro do EA, ou da classe C_Manager, estará de fato fazendo o uso de uma faixa de horário. Mas se você desejar, pode acrescentar este controle, adicionando exatamente a linha em destaque, as funções que precisam ser controladas, como a do gatilho de trailing stop, isto caso você deseje fazer isto, da mesma forma. Se não deseja usar o controle em algumas destas funções, bastará retirar a linha que foi adicionada. Gostaram da forma como o código foi planejado ?!?! Mas tudo isto não funciona de fato, sem um outro ponto, este é visto no código do EA.

No código do EA, as únicas mudanças, realmente necessárias, podem ser vista abaixo:

#include <Generic Auto Trader\C_Manager.mqh>
#include <Generic Auto Trader\C_Mouse.mqh>
//+------------------------------------------------------------------+
C_Manager *manager;
C_Mouse  *mouse;
//+------------------------------------------------------------------+
input int       user01   = 1;                   //Leverage Factor
input double    user02   = 100;                 //Take Profit ( FINANCE )
input double    user03   = 75;                  //Stop Loss ( FINANCE )
input bool      user04   = true;                //Day Trade ?
input color     user05  = clrBlack;             //Price Line Color
input color     user06  = clrForestGreen;       //Take Line Color 
input color     user07  = clrFireBrick;         //Stop Line Color
input double    user08  = 35;                   //BreakEven ( FINANCE )
//+------------------------------------------------------------------+
input string    user90  = "00:00 - 00:00";      //Sunday
input string    user91  = "09:05 - 17:35";      //Monday
input string    user92  = "10:05 - 16:50";      //Tuesday
input string    user93  = "09:45 - 13:38";      //Wednesday
input string    user94  = "11:07 - 15:00";      //Thursday
input string    user95  = "12:55 - 16:25";      //Friday
input string    user96  = "00:00 - 00:00";      //Saturday
//+------------------------------------------------------------------+
#define def_MAGIC_NUMBER 987654321
//+------------------------------------------------------------------+
int OnInit()
{
        string szInfo;
        
        manager = new C_Manager(def_MAGIC_NUMBER, user03, user02, user01, user04, user08);
        mouse = new C_Mouse(user05, user06, user07, user03, user02, user01);
        for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++)
        {
                switch (c0)
                {
                        case SUNDAY     : szInfo = user90; break;
                        case MONDAY     : szInfo = user91; break;
                        case TUESDAY    : szInfo = user92; break;
                        case WEDNESDAY  : szInfo = user93; break;
                        case THURSDAY   : szInfo = user94; break;
                        case FRIDAY     : szInfo = user95; break;
                        case SATURDAY   : szInfo = user96; break;
                }
                (*manager).SetInfoCtrl(c0, szInfo);
        }
        (*manager).CheckToleranceLevel();
        EventSetMillisecondTimer(100);

        return INIT_SUCCEEDED;
}

Foi necessário, apenas adicionar os pontos, onde irá acontecer a interação com o usuário do código. Este laço, que irá capturar os dados, e os jogar para dentro do sistema, a fim de que ele possa ser utilizado pela classe C_Manager, controlando assim, de que horário, até que horário, o EA de fato poderá operar. Nesta parte do código, temos um comportamento bastante simples de ser compreendido, não merecendo desta maneira, nenhuma explicação adicional.


Conclusão

Aqui neste artigo, mostrei como você faz para adicionar, um controle a fim de permitir que o EA, opere dentro de uma faixa de horário, apesar do sistema ser bastante simples, vale uma última explicação.

Caso você coloque um horário superior a 24 horas, no sistema de interação. Este será corrigido para a hora mais próxima das 24 horas, ou seja se você deseja, no caso de trabalhar no FOREX, que o EA opere até as 22:59. Deverá tomar este cuidado de digitar exatamente este valor, caso você digite 25:59, o sistema de tradução irá mudar isto para 23:59.

Não foi adicionado um teste extra para analisar esta falha na digitação, já que isto é bem pouco comum, mas pode acontecer. No momento, que explicava sobre o sistema de tradução, esta questão havia passado batida, deixei para comentar ela aqui, e assim promover um teste, a fim de verificar esta condição, este pode ser visto abaixo. Mas não se preocupe, no código do anexo, já conterá estas mudanças:

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
                        {
                                string szRes[], sz1[];
                                bool bLocal;
                                
                                if (_LastError != ERR_SUCCESS) return;
                                if ((index > SATURDAY) || (index < SUNDAY)) return;
                                if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
                                {
                                        m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
                                        m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
                                        bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
                                        for (char c0 = 0; (c0 <= 1) && (bLocal); c0++)
                                                if (bLocal = (StringSplit(szRes[0], ':', sz1) == 2))
                                                        bLocal = (StringToInteger(sz1[0]) <= 23) && (StringToInteger(sz1[1]) <= 59);
                                        if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
                                }
                                if ((_LastError != ERR_SUCCESS) || (!bLocal))
                                {
                                        Print("Error in the declaration of the time of day: ", EnumToString(index));
                                        ExpertRemove();
                                }
                        }

Foi preciso adicionar esta nova variável, e este código em destaque, que faz exatamente a decomposição dos dados de horário, a fim de verificar, se a hora passada está abaixo dos valor máximo possível de ser utilizado dentro de 24 horas.


Arquivos anexados |
Últimos Comentários | Ir para discussão (8)
Daniel Jose
Daniel Jose | 29 dez 2022 em 10:39
filipetagli #:

Boa noite prezados!

Após alguns cursos em vídeos sobre programação MQL5 e bastante leitura da documentação estou no artigo dois da série. Vi que é bem completo. Espero que receba tudo de bom pelo empenho na digitação. A explicação prática de poo me fará muito bem. Agora as 01:46 do dia 29/12/2022 vou continuar no aprendizado. Espero voltar aqui em breve com as estratégias prontas!

Obrigado pelo FEEDBACK ... 😁👍

napalermo
napalermo | 7 jan 2023 em 00:26

Olá Daniel,

Poxa, em primeiro lugar gostaria de parabenizar pelo excelente trabalho.

não tinha visto nada tão bem detalhado e principalmente não com o foco de apenas fazer algo mas de fazer o melhor,

sempre buscando atualizar cada detalhe para um melhor funcionamento, não importa funcionar mas da melhor forma que visualizou naquele momento.

tudo organizado com orientação a objeto. realmente estou achando demais. Parabéns mesmo e que você receba em dobro tudo que tem nos dado em ferramentas e conhecimento.


cheguei em seus artigos ontem, rsrs estou lendo sem parar ia testar mas queria usar já a ferramenta no dia a dia. quero vir fazendo cada cada um para ver na pratica e assimilar cada item.

mas quando cheguei no artigo 31 do Desenvolvendo um EA de negociação do zero achei que tinha encerrado e vi que começou outro. minha duvida é se é sequencia? ou é outro. 

conheço um pouco de programação .net mas de mql nada. comecei a olhar e tive a sorte de já achar seus artigos.


minha duvida é bem simples e de leigo. quando eu compilar o código ele já vai colocar todas as pastas no locais. ou eu devo já colocar essa pasta num lugar especifico.

poderia me auxiliar nesse inicio? ou me mostrar algum artigo que possa me ajudar nisso.

de qualquer forma gostaria muito de agradecer pelo conhecimento que estou aprendendo com você.


Anderson

Daniel Jose
Daniel Jose | 7 jan 2023 em 11:12
napalermo #:

Olá Daniel,

Poxa, em primeiro lugar gostaria de parabenizar pelo excelente trabalho.

não tinha visto nada tão bem detalhado e principalmente não com o foco de apenas fazer algo mas de fazer o melhor,

sempre buscando atualizar cada detalhe para um melhor funcionamento, não importa funcionar mas da melhor forma que visualizou naquele momento.

tudo organizado com orientação a objeto. realmente estou achando demais. Parabéns mesmo e que você receba em dobro tudo que tem nos dado em ferramentas e conhecimento.


cheguei em seus artigos ontem, rsrs estou lendo sem parar ia testar mas queria usar já a ferramenta no dia a dia. quero vir fazendo cada cada um para ver na pratica e assimilar cada item.

mas quando cheguei no artigo 31 do Desenvolvendo um EA de negociação do zero achei que tinha encerrado e vi que começou outro. minha duvida é se é sequencia? ou é outro. 

conheço um pouco de programação .net mas de mql nada. comecei a olhar e tive a sorte de já achar seus artigos.


minha duvida é bem simples e de leigo. quando eu compilar o código ele já vai colocar todas as pastas no locais. ou eu devo já colocar essa pasta num lugar especifico.

poderia me auxiliar nesse inicio? ou me mostrar algum artigo que possa me ajudar nisso.

de qualquer forma gostaria muito de agradecer pelo conhecimento que estou aprendendo com você.


Anderson

Já que você tem conhecimento em programação .NET a coisa será bem mais simples. Bem, se você usa C/C++ na programação .NET, já que ela não se resume apenas em C/C++ 😁👍 ... Mas nos artigos em que disponibilizo o código fonte no anexo, você deve apenas descompactar o anexo no diretório MQL5, que os arquivos serão colocados nos devidos lugares. Já com relação a compilação, fazer isto via MetaEditor é bem mais simples, pois você não precisa configurar todas aquelas coisas que normalmente são necessárias quando fazemos uso de um Compilador ou LinkEditor. Aqui o executável sempre será gerado na pasta onde o arquivo MQL se encontra, já os arquivos MQH você irá seguir as mesmas regras do C/C++, ou seja pode colocar eles em qualquer local, mas por organização, é preferível você os deixar junto de outros arquivos de cabeçalho, ou junto ao arquivo MQL principal, você é livre para fazer como queira 😁👍.

Os artigos antes desta sequência do EA 100% automático, são uma outra sequência. Lá o foco é mostrar como criar um sistema VISUAL de ordens e posições, assim como ter acesso a indicadores e construir indicadores ... material bem básico e simples ...👍

Agora um detalhe: Estou fazendo uma outra sequência de artigos, cujo conteúdo é muito mais avançado e complexo. Em um dado momento irá parar de colocar no anexo os códigos fontes, sendo estes colocados no meio do artigo. Neste caso você deverá conhecer de fato programação C/C++ a fim de conseguir compilar o código e o modificar. Estou fazendo isto, pois de fato a próxima sequência é de um material muito mais complicado e que envolve uso de coisas que não é voltada para o usuário final, e sim para programadores. No entanto, mesmo que você não tenha conhecimento em C/C++, não precisará se preocupar, pois irei de tempos em tempos disponibilizar o código já compilado, pronto para uso 😁👍.

PS: Obrigado pelo FEEDBACK ...

napalermo
napalermo | 8 jan 2023 em 02:12

Obrigado mesmo vou seguir aqui então esses que já estão aqui. 

e ver esses indicadores de TapeReading fiquei muito curioso.

as pessoas diziam que não dava para ter volume real como nas plataformas da b3.

vou testar aqui. 

e tem material de estudo pra caramba..rsrsr

Bora estudar.

Daniel Jose
Daniel Jose | 8 jan 2023 em 11:11
napalermo #:

Obrigado mesmo vou seguir aqui então esses que já estão aqui. 

e ver esses indicadores de TapeReading fiquei muito curioso.

as pessoas diziam que não dava para ter volume real como nas plataformas da b3.

vou testar aqui. 

e tem material de estudo pra caramba..rsrsr

Bora estudar.

Apesar da B3 fornecer 2 tipo de volume, algumas plataformas criam um terceiro. A B3 nos informa apenas e somente o volume negociado, não confunda isto com volume financeiro, pois são coisas totalmente diferentes, apesar de estarem correlacionados. E também o volume de ticks, este indica a quantidade de negócios executados em uma determinada faixa de tempo. Este tipo de coisa será melhor compreendido na próxima sequencia de artigos que irei postar em breve. Outras plataformas criam um terceiro tipo de volume, que é o volume financeiro, mas o volume financeiro, nada mais é do que você pegar o volume negociado e multiplicar pelo valor de cada negocio, algo trivial. Então o MetaTrader 5 não coloca este volume em seus indicadores ... Apesar de existir um discrepância no Times & Trade entre o MetaTrader e outras plataformas como o Profit Chart, esta discrepância se deve ao tipo de informação que outras plataformas fazem uso, já que o MetaTrader 5 ignora a informação referente a casa de origem. Esta informação é fornecida pela B3, no entanto a estrutura do MetaTrader 5 a ignora pois não tem utilidade para os operadores, então algumas plataformas fazem junção entre alguns dados, isto gera as tais discrepâncias na informação no Times & Trade, mas no grosso modo a informação base se mantem, podendo assim ser usada para negociar ... 😁👍

DoEasy. Controles (Parte 19): Rolagem de guias no elemento TabControl, eventos de objetos WinForms DoEasy. Controles (Parte 19): Rolagem de guias no elemento TabControl, eventos de objetos WinForms
Neste artigo, veremos como podemos criar uma funcionalidade para a rolagem dos cabeçalhos das guias no controle TabControl por meio de botões de rolagem. Essa funcionalidade organizará os cabeçalhos das guias em uma única linha em ambos os lados do controle.
Redes neurais de maneira fácil (Parte 29): Algoritmo ator-crítico de vantagem (Advantage actor-critic) Redes neurais de maneira fácil (Parte 29): Algoritmo ator-crítico de vantagem (Advantage actor-critic)
Nos artigos anteriores desta série, conhecemos 2 algoritmos de aprendizado por reforço. Cada um deles tem suas próprias vantagens e desvantagens. Como costuma acontecer quando nos deparamos com esses casos, surge a ideia de combinar os dois métodos em um algoritmo que incorpore o melhor dos dois. E assim compensar as deficiências de cada um deles. Falaremos sobre tal combinação de métodos neste artigo.
Redes neurais de maneira fácil (Parte 30): Algoritmos genéticos Redes neurais de maneira fácil (Parte 30): Algoritmos genéticos
Hoje quero apresentar-lhes um método de aprendizado um pouco diferente. Pode-se dizer que é emprestado da teoria da evolução de Darwin. É provavelmente menos controlável do que os métodos discutidos anteriormente. Mas, mesmo assim, permite também treinar modelos indiferenciados.
Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 09): Automação (I) Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 09): Automação (I)
Criar um Expert Advisor automático não é uma tarefa muito complicada. Mas sem o devido conhecimento você pode acabar fazendo muita bobagem. Neste artigo iremos ver como se dá a construção do primeiro nível de automação. Então a questão aqui é aprender a criar o gatilho para promover o Breakeven e o Trailing Stop.