English Русский Español Deutsch 日本語
preview
Desenvolvendo um sistema de Replay (Parte 34): Sistema de Ordens (III)

Desenvolvendo um sistema de Replay (Parte 34): Sistema de Ordens (III)

MetaTrader 5Exemplos | 7 novembro 2023, 10:08
248 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay (Parte 33): Sistema de Ordens (II), expliquei como iremos proceder para construir o nosso sistema de ordens. Naquele artigo, expliquei grande parte do código e mostrei um pouco sobre os desafios e problemas que teremos que solucionar. Apesar de parecer ser algo simples e de fácil implementação. O que temos de fato que fazer esta muito longe disto. Então neste artigo vamos avançar um pouco mais em direção ao que realmente temos e precisamos fazer em termos de implementação. No entanto, ficou faltando falar e explicar, um último procedimento que se encontra presente na classe C_Manager, e também comentar o código do Expert Advisor. No caso do código do Expert Advisor, focarei basicamente a questão das partes que sofreram modificações. Assim você poderá tenha uma ideia do como ele irá se comportar, sem que seja preciso você fazer uso do mesmo na plataforma MetaTrader 5 a fim de testar as coisas. Mas sinta-se a vontade de fazer os testes que desejar. Já que o código estará no anexo deste artigo.

Muitos podem achar desnecessário ficar testando o sistema conforme ele vai sendo mostrado. Esperando assim ter um sistema mais completo antes de realmente o experimentar. Esta ideia não é muito adequada. Isto por que, se você não entender como o sistema funciona agora, irá ter grandes problemas para entender como ele estará funcionando no futuro. E pior, não conseguirá adaptar as coisas de maneira a conseguir gerar algo que você gostaria de ver. Porém por um motivo ou outro eu não venha a mostrar como fazer, ou não venha a me interessar em desenvolver. Vendo o sistema se desenvolver ao poucos, você pode observar com mais atenção alguns detalhes, experimentar outros, ou esperar que a evolução do mesmo até um dado ponto, onde você realmente sente que é o momento de colocar ele na plataforma, e analisar como ele estará se comportando.

Sei que muita gente gosta de fato pegar e utilizar um sistema mais maduro. Enquanto outros gostam de vê-lo crescendo e se desenvolvendo. Bem, mas vamos começar vendo o procedimento que ficou faltando. E já que a explicação do mesmo é um tema bastante longo, vamos para um novo tópico.


A função principal da classe C_Manager: DispatchMessage

Esta função, ou melhor dizendo procedimento, é com toda a certeza o cerne de toda classe que estamos criando. Ela concentra o tratamento dos eventos que são gerados, e enviados para o nosso programa, vindos da plataforma MetaTrader 5, alguns destes eventos dizemos a plataforma para nos enviar. Como por exemplo o evento CHARTEVENT_MOUSE_MOVE. Enquanto outros são enviados, mas o nosso programa pode os ignorar, por não ter de fato nenhuma utilidade para o projeto, que estaremos criando. Um exemplo seria CHARTEVENT_OBJECT_CLICK.

O fato de concentrarmos, todo o tratamento de eventos dentro das classes em que estamos trabalhando, facilita bastante a questão de fazer o projeto em módulos. Apesar de parecer algo custoso a primeira vista, você com o tempo acaba vendo que isto facilita, e muito o deslocamento de código de um projeto para outro, agilizando assim o desenvolvimento de novos projetos.

Aqui temos duas questões:

  • A primeira, é que o fato de concentrarmos o tratamento de eventos em um único local. Além de facilitar a portabilidade e o deslocamento de códigos entre projetos. Reduz a quantidade de código que precisamos, realmente colocar dentro do código principal, no caso o Expert Advisor. Isto facilita muito a questão da depuração. Reduzindo a quantidade de erros que podem acontecer, seja no reuso das classes, seja no tratamento dos eventos.
  • A segunda questão e esta talvez seja um pouco mais complicada e que envolve a forma como cada um programa irá trabalhar, é o fato de que em alguns tipos de código precisamos que as coisas acontecem em uma dada sequencia. É muito comum as pessoas criarem um código que precisam ser executados em uma sequencia exata, caso contrário o mesmo irá falhar ou produzir resultados errados.

Sabendo destas duas questões, podemos ver o código do procedimento. Assim conseguir saber e entender por que ele esta sendo feito como você poderá notar logo abaixo:

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      static double price = 0;
      bool bBuy, bSell;
                                
      def_AcessTerminal.DispatchMessage(id, lparam, dparam, sparam);
      def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam);
      switch (id)
      {
         case CHARTEVENT_KEYDOWN:
            if (TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL))
            {
               if (TerminalInfoInteger(TERMINAL_KEYSTATE_UP))  ToMarket(ORDER_TYPE_BUY);
               if (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN))ToMarket(ORDER_TYPE_SELL);
            }
            break;
         case CHARTEVENT_MOUSE_MOVE:
            bBuy = def_AcessMouse.CheckClick(C_Mouse::eSHIFT_Press);
            bSell = def_AcessMouse.CheckClick(C_Mouse::eCTRL_Press);
            if (bBuy != bSell)
            {
               if (!m_Objects.bCreate)
               {
                  def_AcessTerminal.CreateObjectGraphics(def_LINE_PRICE, OBJ_HLINE, m_Objects.corPrice, 0);
                  def_AcessTerminal.CreateObjectGraphics(def_LINE_STOP, OBJ_HLINE, m_Objects.corStop, 0);
                  def_AcessTerminal.CreateObjectGraphics(def_LINE_TAKE, OBJ_HLINE, m_Objects.corTake, 0);
                  EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, "");
                  m_Objects.bCreate = true;
               }
               ObjectMove(def_InfoTerminal.ID, def_LINE_PRICE, 0, 0, def_InfoMouse.Position.Price);
               ObjectMove(def_InfoTerminal.ID, def_LINE_TAKE, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceTake, m_Infos.Leverage) * (bBuy ? 1 : -1)));
               ObjectMove(def_InfoTerminal.ID, def_LINE_STOP, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceStop, m_Infos.Leverage) * (bSell ? 1 : -1)));
               if ((def_AcessMouse.CheckClick(C_Mouse::eClickLeft)) && (price == 0)) CreateOrder((bBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL), price = def_InfoMouse.Position.Price);
            }else if (m_Objects.bCreate)
            {
               EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, "");
               ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix);
               m_Objects.bCreate = false;
               price = 0;
            }
            break;
         }
      }

    Não fique com receio deste código acima. Apesar de ele parecer complicado a primeira vista, ele não deve de fato lhe dar medo. Tudo que estamos fazendo, são coisas bastante simples e relativamente comuns. Porém a aparência do código, nos dá a primeira impressão, de ser algo muito complexo e de difícil compreensão. Então vamos por partes, como diria JACK. Começaremos entendendo as primeiras chamadas vista no código. E para explicar com mais calma, dividirei o mesmo em fragmentos. Assim, acredito que a explicação será bem fácil e será melhor assimilada. Já que iremos focar em poucos pontos. Você não precisará ficar rolando a página para ver onde estaremos na explicação.

    def_AcessTerminal.DispatchMessage(id, lparam, dparam, sparam);
    def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam);
    

    Lembram que a pouco, falei que em alguns momentos precisamos que as coisas sejam feitas em uma dada sequencia de acontecimentos ?!?! Pois bem, estas duas linhas acima fazem justamente isto. Elas irão garantir que não nos esqueçamos, ou pior, que coloquemos no código do Expert Advisor, o tratamento dos eventos em uma sequencia errada. Não que isto venha a trazer qualquer influencia agora, neste estágio de codificação. Mas quanto menos código tivermos que nos preocupar em ter que colocar no Expert Advisor, melhor será no futuro. Já que o esquecimento, ou ausência de um determinado tratamento de eventos, pode vim a fazer com que todo o nosso projeto trabalhe de maneira completamente inesperada, ou diferente do que gostariamos.

    Muito bem. Feito estas observações, podemos começar observando o evento CHARTEVENT_KEYDOWN. Este irá tratar os disparos que forem feitos quando pressionamos alguma tecla.

    case CHARTEVENT_KEYDOWN:
            if (TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL))
            {
                    if (TerminalInfoInteger(TERMINAL_KEYSTATE_UP))  ToMarket(ORDER_TYPE_BUY);
                    if (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN))ToMarket(ORDER_TYPE_SELL);
            }
            break;
    

    Aqui temos um situação, que pode lhe parece, ser um pouco confusa, já que pela documentação a variável lparam, irá conter o código da tecla pressionada. Isto de fato acontece, mas o problema aqui, é que precisamos fazer a coisa, de uma maneira um pouco diferente. Ao pressionar uma tecla, o sistema operacional irá gerar um evento. Se o MetaTrader 5 estiver recebendo o foco do sistema operacional, este irá repassar o evento gerado. Como nosso código conta com um tratador, especifico para tratar as teclas pressionadas, este irá isolar o tratamento das teclas de outros tipos de eventos.

    Nota: O código visto acima, não precisa de fato ser colocado no evento CHARTEVENT_KEYDOWN, ele poderia ser colocado fora deste evento. No entanto, ao colocarmos ele no evento CHARTEVENT_KEYDOWN, impedimos um tipo de situação um pouco constrangedora. Tal situação ocorre quando executamos a analise de condição das teclas, ou seja um evento CHARTEVENT_KEYDOWN, quando a plataforma estiver nos alertando de algum outro tipo de evento, que foi disparado por algum motivo.

    Pense na questão bizarra de tratar o estado do teclado, quando foi disparado um evento, por exemplo: CHARTEVENT_CHART_CHANGE, que é disparado quando ocorre alguma mudança no gráfico. E o nosso programa ali, verificando o estado do teclado. Este tipo de coisa não faz nenhum sentido pratico, além de custar tempo de execução. Por conta disto, é que estou isolando a analise de estado do teclado, em um evento CHARTEVENT_KEYDOWN.

    Mas voltando ao código, você pode notar que estou utilizando a função TerminalInfoInteger a fim de saber e isolar um dado código de teclado. Se não for feito desta forma, teríamos um sobre trabalho a fim de verificar se a tecla CTRL estaria pressionada ao mesmo tempo que fosse pressionada uma outra tecla, no caso SETA PARA CIMA ou SETA PRA BAIXO. E é justamente isto que estamos fazendo. Precisamos de uma combinação de teclas para que o nosso programa, no caso o Expert Advisor, saiba o que deverá ser feito em termos de programação. No caso de pressionarmos a combinação CTRL + SETA PARA CIMA, o Expert Advisor deverá entender isto como, um desejo nosso em fazer uma compra a mercado. Se a combinação for CTRL + SETA PARA BAIXO, o Expert Advisor irá vender a mercado. Vejam que apesar de termos os valores das teclas sendo indicadas de forma individual na variável lparam, isto não nos favorece a ponto de podermos trabalhar com combinação de teclas. Mas fazendo da maneira como esta sendo feita, o fato de pressionar apenas e somente uma das teclas da combinação, não irá de maneira alguma fazer com que o Expert Advisor receba a indicação de operar a mercado.

    Caso você imagine que esta combinação pode entrar de alguma forma em conflito com algo que você esteja utilizando, bastará modificar a mesma. Mas tenha o cuidado de manter o código dentro do evento CHARTEVENT_KEYDOWN, para poder tirar proveito do fato de que ele será apenas executado quando o MetaTrader 5, disparar um evento de teclado. Evitando assim que o código seja executado sem necessidade. Uma outra coisa é que o código de teclado visível na variável lparam segue uma tabela que varia de região para região, o que complica bastante as coisas, caso você queira vender ou disponibilizar o executável. Mas usando da forma como estou mostrando, tal tabela não será de fato usada.

    Agora vamos ver o próximo tratador de eventos, CHARTEVENT_MOUSE_MOVE. Mas para facilitar a explicação, vou dividir ele em pequenos fragmentos, seguindo obviamente a ordem que eles aparecem no código da classe.

    case CHARTEVENT_MOUSE_MOVE:
            bBuy = def_AcessMouse.CheckClick(C_Mouse::eSHIFT_Press);
            bSell = def_AcessMouse.CheckClick(C_Mouse::eCTRL_Press);
    

    Observem aqui uma coisa. Aqui estamos usando a classe C_Study a fim de ter acesso a classe C_Mouse. Não se esqueça deste detalhe. Mas observem, que diferente do tratador de eventos visto acima, CHARTEVENT_KEYDOWN, aqui estamos capturando o estado de teclas. Mas estas refletem ao mouse. Ficou confuso ?!?! Na verdade, estas teclas realmente se referem ao mouse, e não ao teclado alfanumérico em si. Mas por que ?!?! Você está tentando me confundir ?!?! Nada disto meu caro leitor. O fato de você poder pressionar as teclas SHIFT e CTRL no teclado alfanumérico, e ainda assim conseguir trabalhar isto dentro da classe C_Mouse, se deve ao fato de que não é bem assim que as coisas funcionam. Estas teclas SHIFT e CTRL de fato se referem ao mouse. Mas não qualquer mouse. Estou falando de um tipo bem especifico, mais ou menos como o visto na imagem 01, logo abaixo:

    Figura 01

    Imagem 01

    É este tipo de mouse no qual estamos falando. Já que ele conta com botões extras em seu corpo. Então para o sistema operacional, e por consequência para a plataforma e para o nosso programa. As teclas SHIFT e CTRL no qual estamos nos referindo, na verdade são parte do mouse. Mas como pode ser que o seu mouse não conte com este tipo de botões extras. O sistema operacional lhe permite fazer uso do teclado. E por conta disto a plataforma, assim como o programa fará com que o código seja interpretado da maneira correta. Então não confunda as teclas SHIFT e CTRL do evento  CHARTEVENT_KEYDOWN, com estas usadas aqui, no evento  CHARTEVENT_MOUSE_MOVE.

    Pois bem, agora que temos o status das teclas SHIFT e CTRL. Podemos ver o restante do código de evento. Este pode ser visto no fragmento logo abaixo:

            if (bBuy != bSell)
            {
                    if (!m_Objects.bCreate)
                    {
                            def_AcessTerminal.CreateObjectGraphics(def_LINE_PRICE, OBJ_HLINE, m_Objects.corPrice, 0);
                            def_AcessTerminal.CreateObjectGraphics(def_LINE_STOP, OBJ_HLINE, m_Objects.corStop, 0);
                            def_AcessTerminal.CreateObjectGraphics(def_LINE_TAKE, OBJ_HLINE, m_Objects.corTake, 0);
                            EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, "");
                            m_Objects.bCreate = true;
                    }
                    ObjectMove(def_InfoTerminal.ID, def_LINE_PRICE, 0, 0, def_InfoMouse.Position.Price);
                    ObjectMove(def_InfoTerminal.ID, def_LINE_TAKE, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceTake, m_Infos.Leverage) * (bBuy ? 1 : -1)));
                    ObjectMove(def_InfoTerminal.ID, def_LINE_STOP, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceStop, m_Infos.Leverage) * (bSell ? 1 : -1)));
                    if ((def_AcessMouse.CheckClick(C_Mouse::eClickLeft)) && (price == 0)) CreateOrder((bBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL), price = def_InfoMouse.Position.Price);
            }else if (m_Objects.bCreate)
            {
                    EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, "");
                    ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix);
                    m_Objects.bCreate = false;
                    price = 0;
            }
    

    Apesar deste código parecer complicado, ele no bem da verdade está fazendo três coisas bem simples. Estas poderiam estar isoladas em alguma rotina, ou procedimento privativo dentro da classe. Mas por hora, ficarão aqui, de maneira a simplificar um pouco as coisas, pelo menos no quesito de chamadas. A primeira das coisas a serem feitas, é quando estamos dizendo, a plataforma, via Expert Advisor, que gostaríamos de enviar uma ordem pendente. Mas antes de fato fazer isto, queremos ver onde estarão os limites e onde a ordem irá ser posicionada. Para fazer isto fazemos uso de três objetos que são criados neste ponto do código. Observem que junto desta criação, estaremos enviando um evento ao sistema de classe C_Mouse, a fim de dizer para a classe C_Mouse, que o mouse deverá ser ocultado, nos próximos instantes. Esta é a primeira fase.

    Uma vez com os objetos que precisamos no gráfico, os movemos de uma maneira bastante especifica. Isto é feito usando este conjunto de funções. Mas se o usuário, informar que o ponto desejado para enviar a ordem, está sendo aquele visto no gráfico por meio dos objetos criados, iremos executar um pedido de colocação de ordem pendente. Notem como os testes estão sendo feitos, a fim de que possamos saber o que está acontecendo com o mouse e no gráfico.

    Já no terceiro e último ponto, as coisas se desenvolvem da seguinte maneira: Primeiro é enviado um evento ao sistema de classe C_Mouse, para que o mouse volte a ser visível no gráfico. Logo depois iremos remover os objetos que foram criados.

    Agora tem uma coisa importante neste código acima. Algo muito importante, e que você deve sempre ficar bastante atento, ao programar o seus próprios código. Quem está começando na área de programação e se este é o seu caso, pode não ter percebido uma coisa interessante, mas que ao mesmo tempo perigosa neste código acima. Isto se não for feito de forma adequada. Estou falando da RECURSÃO. E sim, neste código acima estamos fazendo o uso de recursão, e se isto não for bem planejado, você acabará entrando em um loop infinito. Assim que o sistema entrar na região do código que faz o uso de recursão, ele poderá nunca mais sair dali.

    Para entender como esta recursão está acontecendo. Veja a figura 02, logo abaixo:

    Figura 02

    Figura 02 : Fluxo de mensagens internas.

    A seta verde, na figura 02 é justamente onde a recursão acontece. Mas e no código, como isto realmente está acontecendo ??? Para ver isto, bastará observar o fragmento abaixo. Onde estou mostrando o procedimento DispatchMessage, presente na classe C_Manager.

    void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
       {
    
    // ...                          
    
          def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam);
          switch (id)
          {
    
    // ...
    
             case CHARTEVENT_MOUSE_MOVE:
    // ...
                if (bBuy != bSell)
                {
                   if (!m_Objects.bCreate)
                   {
    // ...
                      EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, "");
                   }
    
    // ...
    
                }else if (m_Objects.bCreate)
                {
                   EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, "");
    
    // ...
                }
                break;
             }
          }
    

    Ao observar a figura 02, talvez não fique muito claro para você a recursão, e apenas olhar o código acima também pode não ficar claro o suficiente. Mas quando unimos a figura 02, ao código acima, podemos notar como a recursão acontece. Se isto não for bem planejado, você terá sérios problemas. Então vamos a explicação, para que você aspirante, consiga compreender o poder da recursão, ao mesmo tempo o seu perigo. Sempre que você precisar fazer algo muito complicado, seja lá o que for. É possível dividir o processo em passos menores, ou mais simples. Estes passos podem se repetir, uma dada quantidade de vezes, a fim de que teremos como resultado, algo bem mais complexo. Porém que seguirá um conceito simples. Este é o poder da recursão. Mas ela não é usada apenas e somente neste tipo de cenário. Apesar de na grande maioria das vezes, está ligada a isto, podemos usá-la em outras questões.

    O fragmento acima é justamente uma destas questões. Para acompanhar a explicação, peço que observem também a figura 02. Tudo se inicia quando o usuário, gera um evento, que faz a plataforma MetaTrader 5, disparar uma chamada a função OnChartEvent. Ao fazer isto, esta função chama a rotina DispatchMessage, dentro da classe C_Manager, Neste ponto temos a chamada ao procedimento de tratamento de eventos, presente por motivo de herança, a classe C_Mouse. Em especial a rotina DispatchMessage presente nesta classe. Quando a rotina retorna, o programa continua de onde parou. E entramos no tratamento de evento, a fim de verificar se o usuário, está querendo ou não que as linhas auxiliares sejam criadas, ou removidas. Quem decide isto é o programa. Mas de qualquer forma, teremos em algum momento, a execução de uma chamada EventChartCustom. Neste momento a recursão é disparada.

    O que de fato acontece, é que esta chamada irá fazer com que a plataforma MetaTrader 5, efetue um novo chamado a função OnChartEvent. Fazendo com que esta seja executada novamente. Chamando a rotina DispatchMessage, da classe C_Manager. Esta por sua vez iria, efetuar novamente a chamada, por motivo de herança, a classe C_Mouse, a fim de executar o procedimento customizado. Fazendo o indicador do mouse, aparecer ou desaparecer, dependendo do caso. Mas por conta da recursão, o código não irá retornar, como muitos podem imaginar, inicialmente. Ele de fato irá retornar, mas irá novamente fazer com que todo o código presente na rotina DispatchMessage, na classe C_Manager seja executada. E é aqui onde mora o perigo, se você colocar uma chamada de modo que o evento customizado, que foi tratado na classe C_Mouse, também apareça na classe C_Manager. Ele também será tratado na classe C_Manager. E se por ventura, você fizer com que o evento volte a ser tratado na classe C_Mouse, fazendo o uso da função EventChartCustom, iremos acabar caindo em um loop infinito.

    Mas como a classe C_Manager, não tem o mesmo evento e isto é importante, que é tratado pela classe C_Mouse. Iremos retornar a função OnChartEvent, que será integralmente executada. Quando a função OnChartEvent tiver sito executada, ela irá retorna ao ponto onde a chamada EventChartCustom, que pode ser visto no fragmento acima, foi executada. Fazendo com que todo o restante do código do procedimento DispatchMessage, presente na classe C_Manager, seja executado. Quanto este terminar, iremos, novamente retornar a função OnChartEvent, onde ela será novamente, executada integralmente. Liberando assim finalmente, a plataforma para poder executar outro tipo de evento, quando este viver a acontecer.

    Então ao fazer uma chamada EventChartCustom, teremos no mínimo a execução integral de duas vezes o código presente na função OnChartEvent. Isto devido a recursão. Parece ser pouco eficiente, mas o fato, é que o código por ser simples, não traz muito custo para a performance geral da plataforma. Mas é bom você está sempre ciente do que está de fato acontecendo. O custo da recursão neste caso, é bem baixo, frente ao fato de termos um código mais modular. Mas em algumas situações, pode ser que este custo, não compense, e faça assim com que o código fique muito lento. Neste caso temos que tomar algum outro tipo de medida. Mas não é o caso neste momento.

    Acredito ter explicado em detalhes este procedimento DispatchMessage, presente na classe C_Manager. E mesmo que ele pareça ser algo bastante complicado, o fato é que estamos longe de ter algo realmente complicado aqui. Pois o sistema não é ainda capaz de trabalhar com um modelo de crossorder. Para fazer isto, este procedimento DispatchMessage deverá passar por grandes mudanças. Mas isto irá ficar para o futuro.

    Então vamos ver como ficou o código do Expert Advisor, a ponto de podermos utilizar o sistema de maneira adequada.


    Analisando as atualizações no Expert Advisor

    Apesar de no momento o Expert Advisor já poder enviar ordens e nos ajudar a operar dentro de uma janela de tempo. O mesmo não sofreu assim grandes mudanças em seu código. Mas mesmo assim existe uma parte do código que merece algum destaque. Por conta disto teremos uma explicação, a fim de que todos possam compreender o que está acontecendo ali. Este ponto que merece destaque, está ligado a interação com o usuário, e ao código presente no evento OnInit. Mas vamos começar com a parte referente a interação com o usuário. Este pode ser visto no fragmento abaixo:

    input group "Mouse";
    input color     user00 = clrBlack;      //Price Line
    input color     user01 = clrPaleGreen;  //Positive Study
    input color     user02 = clrLightCoral; //Negative Study
    input group "Trade";
    input uint      user10 = 1;             //Leverage
    input double    user11 = 100;           //Take Profit ( Finance )
    input double    user12 = 75;            //Stop Loss ( Finance )
    input bool      user13 = true;          //Is Day Trade
    //+------------------------------------------------------------------+
    input group "Control of Time"
    input string    user20  = "00:00 - 00:00";      //Sunday
    input string    user21  = "09:05 - 17:35";      //Monday
    input string    user22  = "10:05 - 16:50";      //Tuesday
    input string    user23  = "09:45 - 13:38";      //Wednesday
    input string    user24  = "11:07 - 15:00";      //Thursday
    input string    user25  = "12:55 - 18:25";      //Friday
    input string    user26  = "00:00 - 00:00";      //Saturday
    

    Este fragmento acima, é responsável por interagir com o usuário. Aqui temos dois novos grupos de informações, que podem ser acessadas e configuradas pelo usuário. No primeiro grupo, o usuário pode informar como será feito a operação, seja ela a mercado, ou pendente. Os valores configurados aqui, são em suma, simples de serem compreendidos. O valor que representa a alavancagem ( user 10),  deverá ser preenchido, de modo a representar a quantidade de vezes, que você estará utilizando o volume mínimo, exigido para se opera o ativo. No caso do forex, você muito provavelmente irá usar um valor de 100, ou algo do tipo, a fim de operar, buscando uma boa margem a ser trabalhada. Caso contrário, você estará trabalhando na ordem de centavos, o que fará com que as linhas de limite fiquem muito distantes do ponto onde você esperava vê-las . No caso de estar operando na bolsa e no fracionário, você deverá informar o numero de ações a serem usadas; Agora se não for no fracionário, você deverá informar a quantidade de lotes. Para operações em futuros, você deverá informar a quantidade de contratos. Em fim, algo trivial. Na parte sobre o take profit ( user11 ) e stop loss ( user12 ), você não deverá informa a quantidade de pontos, e sim o financeiro a ser utilizado. Este valor será ajustado de maneira adequado pelo código, a fim de representar a posição correta no preço do ativo. A última variável ( user13 ) serve apenas para informa, se você estará montando uma posição mais longa, ou se será algo curto, que irá encerrar no final do pregão.

    Nota importante: No entanto, teste este mecanismo com cautela, já que algumas corretoras tem servidores apenas para day trade e outro para posição. Procure se informa antes sobre tal coisa junto a sua corretora.

    Agora no segundo grupo, temos algo que você precisará experimentar antes de realmente configurar de maneira adequada. Não que seja algo complexo, ou de difícil compreensão, mas sim por motivos de que você precisa entender que estes valores irão dizer de que momento até que momento o Expert Advisor permitirá que você lançar ordens ou pendurar ordens. A questão sobre gerenciar, encerrar ou mesmo modificar as ordens já não irão depender necessariamente do Expert Advisor. Isto será de responsabilidade da plataforma MetaTrader 5. Pelo menos por hora.

    Então você pode configurar uma janela de horário, onde o Expert Advisor permitirá você operar utilizando os recursos presentes nele. E isto deve ser feito visando o pensamento de que a configuração é feita voltada para semana, e não para um dia especifico, ou uma data especial.

    Para entender isto, é preciso ver o código OnInit. Este pode ser visto logo abaixo:

    int OnInit()
    {
            string szInfo;
            
            terminal = new C_Terminal();
            study    = new C_Study(terminal, user00, user01, user02);
            manager  = new C_Manager(terminal, study, user00, user02, user01, def_MagicNumber, user12, user11, user10, user13);
            
            if (_LastError != ERR_SUCCESS) return INIT_FAILED;
            
            for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++)
            {
                    switch (c0)
                    {
                            case SUNDAY     : szInfo = user20; break;
                            case MONDAY     : szInfo = user21; break;
                            case TUESDAY    : szInfo = user22; break;
                            case WEDNESDAY  : szInfo = user23; break;
                            case THURSDAY   : szInfo = user24; break;
                            case FRIDAY     : szInfo = user25; break;
                            case SATURDAY   : szInfo = user26; break;
                    }
                    (*manager).SetInfoCtrl(c0, szInfo);
            }
    
            MarketBookAdd(def_InfoTerminal.szSymbol);
            OnBookEvent(def_InfoTerminal.szSymbol);
            EventSetMillisecondTimer(500);
    
            return INIT_SUCCEEDED;
    }
    

    A parte que realmente nos interessa neste código OnInit, está marcada em destaque no código acima. O que estamos fazendo aqui, é informar de maneira completa, como o Expert Advisor deverá se comportar durante toda uma semana. Não apenas em um dia especifico, mas durante toda a semana. Existem ativos, ou mercados, neste caso o forex, onde as negociações são praticamente continuas, não sendo finalizadas em um determinado momento do dia. Se você pensa-se em usar um sistema de controle, baseado em janelas de tempo, em um Expert Advisor que poderia ficar ligado 24 horas por dia. Acabaria tendo problemas na virada do dia, ou seja, assim que fosse 23:59:59, o Expert Advisor teria que ser retirado, e retornar no próximo segundo, a fim de saber qual seria a nova janela de tempo. Mas utilizando este método acima, o mesmo pode ficar ligado 24 horas, durante 7 dias na semana, por 52 semanas no ano. Sem que o mesmo venha a ficar perdido, sem saber qual seria a janela de tempo que deveria ser utilizada. Sei que apenas observando este código, muitos podem não conseguir entender como isto de fato se dá. Por isto é preciso experimentar o Expert Advisor em funcionamento, a fim de conseguir compreender o funcionamento deste sistema. Mas este sistema não é novidade. Ele já foi explorado anteriormente:  Aprendendo a construindo um Expert Advisor que opera de forma automática ( Parte 10 ): Automação ( II ), para mais detalhes veja o artigo informado.


    Conclusão

    Apesar do sistema parecer bastante estável e bem versátil. Ele começou a ser acometido de uma falha. Esta é aparentemente bastante estranha e desconcertante, já que não faz sentido tal falha aparecer. E o motivo é que a falha apareceu, em um ponto que não sofreu nenhum tipo de modificação, edição, ou coisa que justifica-se o seu surgimento. Mas mesmo nestas condição, falarei sobre isto no próximo artigo. No anexo você terá acesso ao código no seu atual estágio de desenvolvimento. Podendo assim ser estudado e analisado em detalhes. Já que no artigo anterior não foi mostrado as melhorias feitas. 

    Observem o fato do que foi comentado no artigo passado. Onde falei sobre o sistema de selecionar objetos, dando apenas um clique, diferente do modo padrão da plataforma que é usando duplo clique. Mas de qualquer forma, o bom mesmo, é você vê o sistema funcionando na sua plataforma. E assim tirar suas próprias conclusões, ao  vê-lo funcionando. Ali pertinho, onde você pode fazer os seus próprios testes.



    Arquivos anexados |
    Files_-_BOLSA.zip (1358.24 KB)
    Files_-_FOREX.zip (3743.96 KB)
    Files_-_FUTUROS.zip (11397.51 KB)
    Desenvolvendo um agente de Aprendizado por Reforço em MQL5 com Integração RestAPI(Parte 1): Usando RestAPIs em MQL5 Desenvolvendo um agente de Aprendizado por Reforço em MQL5 com Integração RestAPI(Parte 1): Usando RestAPIs em MQL5
    Este artigo aborda a importância das APIs (Interfaces de Programação de Aplicativos) na comunicação entre diferentes aplicativos e sistemas de software. Ele destaca o papel das APIs na simplificação da interação entre aplicativos, permitindo que eles compartilhem dados e funcionalidades de maneira eficiente.
    Redes neurais de maneira fácil (Parte 48): métodos para reduzir a superestimação dos valores da função Q Redes neurais de maneira fácil (Parte 48): métodos para reduzir a superestimação dos valores da função Q
    No artigo anterior, nós exploramos o método DDPG, projetado para treinar modelos em espaços de ação contínua. No entanto, como outros métodos de aprendizado Q, ele está sujeito ao problema da sobreavaliação dos valores da função Q. Esse problema geralmente leva eventualmente ao treinamento de um agente com uma estratégia não otimizada. Neste artigo, examinaremos algumas abordagens para superar o problema mencionado.
    Desenvolvimento de um Cliente MQTT para o MetaTrader 5: Metodologia TDD Desenvolvimento de um Cliente MQTT para o MetaTrader 5: Metodologia TDD
    Este artigo apresenta a primeira tentativa de desenvolver um cliente MQTT nativo para o MQL5. MQTT é um protocolo de troca de dados no formato "publicador - assinante". Ele é leve, aberto, simples e projetado para ser facilmente implementado. Isso o torna aplicável em muitas situações.
    Teoria das Categorias em MQL5 (Parte 12): Ordem Teoria das Categorias em MQL5 (Parte 12): Ordem
    Este artigo faz parte de uma série sobre a implementação de grafos usando a teoria das categorias no MQL5 e é dedicado à teoria da ordem (Order Theory). Consideraremos dois tipos básicos de ordenação e exploraremos como os conceitos de relação de ordem podem auxiliar os conjuntos monoidais na tomada de decisões de negociação.