Español
preview
Desenvolvendo um sistema de Replay (Parte 40): Iniciando a segunda fase (I)

Desenvolvendo um sistema de Replay (Parte 40): Iniciando a segunda fase (I)

MetaTrader 5Exemplos | 16 janeiro 2024, 11:22
230 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay (Parte 39): Pavimentando o Terreno (III), mostrei como podemos ter uma comunicação, entre processos de uma maneira a tornar possível fazer algumas coisas. Neste atual momento estamos usando um EA e um indicador. Mas poderemos expandir as coisas conforme for sendo necessário.

A principal e maior vantagem em se ter tal comunicação, é o fato de que podemos criar uma modularização do nosso sistema. Talvez você ainda não tenha entendido o que podemos de fato fazer. Isto quando podemos trocar informações entre processos via um canal " mais seguro " do que usando as variáveis globais de terminal.

Para fazermos isto, e demonstrar como integrar de forma totalmente modular, o nosso sistema de replay / simulador. Vamos dar um passinho para trás e logo depois dar outros passos para frente. Aqui neste artigo iremos remover o sistema de estudos, que usa o mouse do EA. Tornaremos este mesmo sistema de estudos em um indicador. Para assim termos o sistema de estudos compatível com a próxima etapa que iremos montar.

Ao conseguir fazer isto, você irá notar que iremos ser capazes de fazer bem mais coisas depois. É bem provável, que o indicador não seja finalizado totalmente aqui. Já que iremos precisar depois fazer outras coisas. Mas com o tempo ele irá ficar completo. Mas a grande vantagem é que ele será um modulo. Este poderá receber melhorias ou mudanças sem afetar o resto do nosso sistema principal.


Iniciando a codificação.

O nosso código irá se montado a partir do que já temos originalmente em mãos. Iremos fazer o mínimo de mudanças, a fim de tornar o código presente no EA, um indicador.

A primeira coisa a ser feita é vista no código do arquivo de cabeçalho InterProcess.mqh. Este pode ser isto na integra logo abaixo:

01. #property copyright "Daniel Jose"
02. //+------------------------------------------------------------------+
03. #define def_SymbolReplay                    "RePlay"
04. #define def_GlobalVariableReplay            def_SymbolReplay + "_Infos"
05. #define def_GlobalVariableIdGraphics        def_SymbolReplay + "_ID"
06. #define def_GlobalVariableServerTime        def_SymbolReplay + "_Time"
07. #define def_MaxPosSlider                    400
08. //+------------------------------------------------------------------+
09. union u_Interprocess
10. {
11.     union u_0
12.     {
13.             double  df_Value;  // Value of the terminal global variable...
14.             ulong   IdGraphic; // Contains the Graph ID of the asset...
15.     }u_Value;
16.     struct st_0
17.     {
18.             bool    isPlay;     // Indicates whether we are in Play or Pause mode...
19.             bool    isWait;     // Tells the user to wait...
20.             bool    isHedging;  // If true we are in a Hedging account, if false the account is Netting...
21.             bool    isSync;     // If true indicates that the service is synchronized...
22.             ushort  iPosShift;  // Value between 0 and 400...
23.     }s_Infos;
24.     datetime        ServerTime;
25. };
26. //+------------------------------------------------------------------+
27. union uCast_Double
28. {
29.     double   dValue;
30.     long     _long;                    // 1 Information
31.     datetime _datetime;                // 1 Information
32.     int      _int[sizeof(double)];     // 2 Informations
33.     char     _char[sizeof(double)];    // 8 Informations
34. };
35. //+------------------------------------------------------------------+

Código do arquivo InterProcess.mqh

O que nos interessa de fato neste código está entre as linhas 27 e 34. Aqui temos uma união, que irá nos permitir transferir informações entre os processos. Esta união conta neste momento com alguns tipos de dados que precisaremos, e usaremos logo de inicio. A ideia aqui é facilitar a transferência de dados que foi vista no artigo anterior. Mas aqui vamos fazer isto de uma forma pratica e voltada para um proposito.

Feita a inclusão destas linhas no arquivo InterProcess.mqh. Vamos continuar, só que agora, fazendo outras mudança no código. Estas serão feitas arquivo de classe C_Study.mqh.

Para começar, mudaremos algo no inicio da classe. Isto pode ser visto no fragmento abaixo.

class C_Study : public C_Mouse
{
        protected:
                enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
        private :
                enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};

A linha riscada foi removida da clausula privativa e passou a ser declarada na clausula protected. Se bem que isto não faz muita diferença prática. Mas para mim, isto facilita o entendimento. Já que qualquer coisa dentro da clausula privativa, me indica que não poderia ser acessado fora da classe. Mas no caso das enumerações, isto não acontece. Elas são sempre tratadas como se fossem publicas. Independentemente da clausula de definição.

Mas a parte onde realmente ocorreu uma mudança, é vista logo abaixo.

//+------------------------------------------------------------------+
void Update(const eStatusMarket arg)
void Update(void)
{
   datetime dt;
                                
   switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
   switch (m_Info.Status)
   {
      case eCloseMarket : m_Info.szInfo = "Closed Market";
         break;
      case eInReplay    :
      case eInTrading   :
         if ((dt = GetBarTime()) < ULONG_MAX)
         {
            m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
            break;
         }
      case eAuction     : m_Info.szInfo = "Auction";
         break;
      default           : m_Info.szInfo = "ERROR";
   }
   Draw();
}
//+------------------------------------------------------------------+
void Update(const MqlBookInfo &book[])
{
   m_Info.Status = (ArraySize(book) == 0 ? eCloseMarket : (def_InfoTerminal.szSymbol == def_SymbolReplay ? eInReplay : eInTrading));
   for (int c0 = 0; (c0 < ArraySize(book)) && (m_Info.Status != eAuction); c0++)
      if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Info.Status = eAuction;
   this.Update();
}
//+------------------------------------------------------------------+

Todas as linhas que se encontram riscadas foram removidas da classe. Mas no entanto, as linhas destacadas foram adicionadas no lugar das riscadas.

Mas por que disto está acontecendo ?!?! Para entender é preciso que você compreenda. que quando tornamos o código presente no EA, em um indicador. Teremos uma mudança na forma como as coisas podem e serão feitas.

O código em questão, é o que se refere ao uso do mouse, como uma forma de gerar estudos.

Quando usamos a codificação de forma que quem irá executar as coisas é o EA. Podemos fazer as coisas de uma certa maneira. No entanto, quando formos fazer o mesmo tipo de coisa, só que usando um indicador. Precisamos fazer as coisas de uma forma um pouco diferente. Isto por conta que o indicador vive em um thread que se for afetado, irá afetar todos os demais que estiverem presentes no gráfico.

E você não vai querer que o indicador trave em momento algum. Por conta disto teremos que fazer as coisas de uma forma um pouco diferente.

Dito isto vamos ver logo abaixo o código do indicador. Ele está completo e na integra.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property description "This is an indicator for graphical studies using the mouse."
004. #property description "This is an integral part of the Replay / Simulator system."
005. #property description "However it can be used in the real market."
006. #property version "1.40"
007. #property icon "/Images/Market Replay/Icons/Indicators.ico"
008. #property link "https://www.mql5.com/pt/articles/11624"
009. #property indicator_chart_window
010. #property indicator_plots 0
011. #property indicator_buffers 1
012. //+------------------------------------------------------------------+
013. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
014. #include <Market Replay\Auxiliar\InterProcess.mqh>
015. //+------------------------------------------------------------------+
016. C_Terminal *Terminal  = NULL;
017. C_Study    *Study     = NULL;
018. //+------------------------------------------------------------------+
019. input C_Study::eStatusMarket user00 = C_Study::eAuction;   //Market Status
020. input color user01 = clrBlack;                             //Price Line
021. input color user02 = clrPaleGreen;                         //Positive Study
022. input color user03 = clrLightCoral;                        //Negative Study
023. //+------------------------------------------------------------------+
024. C_Study::eStatusMarket m_Status;
025. int m_posBuff = 0;
026. double m_Buff[];
027. //+------------------------------------------------------------------+
028. int OnInit()
029. {
030.    if (!CheckPass("Indicator Mouse Study")) return INIT_FAILED;
031.            
032.    Terminal = new C_Terminal();
033.    Study = new C_Study(Terminal, user01, user02, user03);
034.    if ((*Terminal).GetInfoTerminal().szSymbol != def_SymbolReplay)
035.    {
036.            MarketBookAdd((*Terminal).GetInfoTerminal().szSymbol);
037.            OnBookEvent((*Terminal).GetInfoTerminal().szSymbol);
038.            m_Status = C_Study::eCloseMarket;
039.    }else
040.            m_Status = user00;
041.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
042.    ArrayInitialize(m_Buff, EMPTY_VALUE);
043.    
044.    return INIT_SUCCEEDED;
045. }
046. //+------------------------------------------------------------------+
047. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
048. {
049.    m_posBuff = rates_total - 4;
050.    (*Study).Update(m_Status);      
051.    
052.    return rates_total;
053. }
054. //+------------------------------------------------------------------+
055. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
056. {
057.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
058.    SetBuffer();
059.    
060.    ChartRedraw();
061. }
062. //+------------------------------------------------------------------+
063. void OnBookEvent(const string &symbol)
064. {
065.    MqlBookInfo book[];
066.    C_Study::eStatusMarket loc = m_Status;
067.    
068.    if (symbol != (*Terminal).GetInfoTerminal().szSymbol) return;
069.    MarketBookGet((*Terminal).GetInfoTerminal().szSymbol, book);
070.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
071.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
072.            if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
073.    if (loc != m_Status) (*Study).Update(m_Status);
074. }
075. //+------------------------------------------------------------------+
076. void OnDeinit(const int reason)
077. {
078.    if (reason != REASON_INITFAILED)
079.    {
080.            if ((*Terminal).GetInfoTerminal().szSymbol != def_SymbolReplay)
081.                    MarketBookRelease((*Terminal).GetInfoTerminal().szSymbol);
082.            delete Study;
083.            delete Terminal;
084.    }
085. }
086. //+------------------------------------------------------------------+
087. bool CheckPass(const string szShortName)
088. {
089.    IndicatorSetString(INDICATOR_SHORTNAME, szShortName + "_TMP");
090.    if (ChartWindowFind(ChartID(), szShortName) != -1)
091.    {
092.            ChartIndicatorDelete(ChartID(), 0, szShortName + "_TMP");
093.            Print("Only one instance is allowed...");
094.            
095.            return false;
096.    }
097.    IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
098.    
099.    return true;
100. }
101. //+------------------------------------------------------------------+
102. inline void SetBuffer(void)
103. {
104.    uCast_Double Info;
105.    
106.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
107.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
108.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
109.    m_Buff[m_posBuff + 1] = Info.dValue;
110.    Info._int[0] = (*Study).GetInfoMouse().Position.X;
111.    Info._int[1] = (*Study).GetInfoMouse().Position.Y;
112.    m_Buff[m_posBuff + 2] = Info.dValue;
113.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
114.    m_Buff[m_posBuff + 3] = Info.dValue;
115. }
116. //+------------------------------------------------------------------+


Código fonte do Indicador

Observando o código fonte do indicador e comparando ele com o código visto no artigo Desenvolvendo um sistema de Replay (Parte 39): Pavimentando o Terreno (III), você irá notar muitas semelhanças.

Se você está acompanhando esta sequencia de artigos sobre o sistema de replay / simulador. Não terá grandes problemas em conseguir entender grande parte do código acima. No entanto, existem diversos pontos que merecem algum destaque e uma explicação mais detalhada e profunda. Visto que muitos, não irão de fato conseguir entender o que está se passando, apenas olhando o código. Além do mais, desejo que a ideia, do motivo e o interesse de fazer as coisas assim, fique o mais claras possível. Já que o correto entendimento destes e dos demais códigos fonte, de agora em diante, irá ser de grande importância. Isto para quem de fato deseja entender como o sistema está sendo implementado.

Então vamos começar vendo que entre as linhas 2 e 8 temos informações que irão mudar no decorrer do tempo. Tais informações ligam o código ao artigo no qual ele está sendo criado, modificado ou apresentado. Não é algo realmente trivial. Mas está ali para orientar usuários menos experientes.

Entre as linhas 9 e 11, dizemos ao compilador como o indicador será de fato projetado. Todo e qualquer código de indicador terá pelo menos a linha 9 e a linha 10. TODO. Já a linha 11, informa que iremos fazer uso de um buffer, e que algum processo que queira ler algo via função CopyBuffer, poderá faze-lo tendo acesso ao buffer interno do indicador.

As linhas 13 e 14 são includes, que dizem ao compilador, quais os arquivos de cabeçalho iremos utilizar. Apesar de já termos feito algumas mudanças, nestes arquivos, iremos precisar fazer outras. Mas isto fica para um próximo momento. Por hora, poderemos utilizar os arquivos de cabeçalhos informados, sem receio.

Já nas linhas 16 e 17, declaramos duas variáveis globais internas do indicador. Estas variáveis que são ponteiros, servirão para nos dar acesso de forma controlada as classes. Normalmente não é feita a inicialização default de uma variável global. Visto que o compilador faz isto para nos por padrão. Mas aqui quero deixar claro que de fato tais variáveis ( ponteiros ) não estão apontando para lugar algum da memória. Mas se você não declarar isto explicitamente, como está sendo feito, não deve se esquecer que o compilador, por padrão, irá fazer isto implicitamente.

Entre as linhas 19 e 22 temos as variáveis de configuração externa. A partir deste momento, gostaria que você parasse de ver tais variáveis, como sendo uma forma do usuário acessar, ou configurar o indicador. Se você ficar pensando desta maneira, irá acabar ficando com viseiras que lhe deixaram com uma visão muito míope, não conseguindo desta forma, visualizar o que realmente podemos fazer.

Vamos entender uma coisa. Nenhum, e absolutamente nenhum programa, deve ser pensado como sendo um programa puro e simples. Você deve sempre pensar, que um programa é na verdade algum tipo de função. Isto por que ele recebe alguma informação, processa ela de uma determinada maneira e entrega algum tipo de resultado. Qual é o tipo de coisa, usada na programação que tem este mesmo comportamento ?!?! UMA FUNÇÃO. Então vamos começar a ver as coisas de uma  maneira mais ampla. Vamos parar de ficar imaginando ou sonhando com possiblidades e começar a ver as coisas como elas realmente são. Programas são funções. Não importa o que se pode fazer. Programas são funções.

Desta maneira na linha 19, temos um parâmetro, que quando o indicador for lançado no gráfico, pelo usuário, não terá nenhum sentido. Este parâmetro da linha 19, não existe para que o usuário possa configurar ele. Não. Ele existe para que um outro programa possa fazer uso deste indicador que estamos criando. Mas como assim. Não entendi 🤔. Calma. Não se preocupe com isto agora. Lembre-se: Este é o primeiro artigo desta segunda fase de construção do sistema de replay / simulador. Existem diversas coisas que vão ser mostradas nos próximos artigos, que irá fazer você entender o por que da linha 19 existir. Tenha calma.

Já as linhas 20 até a linha 22, são parâmetros, que de fato fazem sentido estarem ali. Isto por que eles são usados para configurar as cores usadas pelo mouse no gráfico.

Agora temos uma questão. Por que existe a linha 24 ?!? Por que temos um variável global do mesmo tipo que vemos na linha 19 ?!? O motivo é que a variável, ou parâmetro na linha 19 não é considerada de fato uma variável. Ela é considerada uma constante. Por conta disto precisamos de fato declarar uma variável, para ser usada e configurada durante o trabalho do indicador. Este é o motivo da declaração da linha 24.

Já a variável declarada na linha 25, nos serve para como uma forma de memória. Mas com um proposito um pouco mais nobre do que simplesmente ser uma memória. Mas iremos voltar nesta questão em breve. A linha 26 é onde declaramos o nosso buffer para servir de saída do indicador. Atenção: Nos declaramos o buffer, mas o mesmo não está de fato funcionando ainda. Se você tentar fazer qualquer coisa nele, irá ter um disparo de erro de RUN-TIME. Ou se tentar o ler, não irá conseguir fazer isto.

A partir deste ponto finalmente iremos entrar na parte de interação entre o indicador e o MetaTrader 5. Lembre-se: O indicador responde a eventos informados pelo MetaTrader 5. Não importa como o seu código esteja estruturado, ou qual o trabalho que ele deverá executar. Ele sempre irá responder a eventos do MetaTrader 5. Alguns destes eventos podem ser de interação, enquanto outros são de atividade.

Entre estes eventos, temos o evento Init, que quando disparado irá gerar uma chamada a função OnInit. Esta função tem seu inicio na linha 28. Atenção ao fato de que ela não recebe nenhum parâmetro. Mas podemos fazer com que os parâmetros de interação, aqueles declarados entre as linhas 19 e 22, sejam utilizados como parâmetros na função OnInit.

Mas vamos ver como a função OnInit está funcionando. A primeira coisa que fazemos é um check-in. Isto é feito na linha 30, onde chamamos uma função. Então o fluxo de execução é desviado para a linha 87. Então vamos para esta linha para compreender que tipo de check-in é este. E por que se ele falhar a função OnInit retornar uma falha na inicialização do indicador.

A função na linha 87, recebe um argumento, este é o nome curto do indicador. Tal nome sempre deverá ser definido a fim de que possamos garantir algumas condições de uso, ou de trabalho. A primeira coisa que fazemos, e isto na linha 89 é dar um nome temporário ao indicador. Uma vez que o indicador tenha um nome temporário, poderemos continuar a fase de check-in. A próxima coisa que fazemos é procurar, e isto usando a linha 90, o nosso indicador no gráfico. Caso ele já esteja presente no gráfico, o MetaTrader 5 irá indicar onde. Caso não esteja teremos como retorno o valor -1.

Então o teste que esta sendo feito na linha 90, nos diz se o indicador já está ou não presente no gráfico. Caso ele esteja, teremos a execução da linha 92, que irá remover o indicador temporário, ou seja este que estamos tentando lançar no gráfico. Imprimimos uma mensagem avisando isto e logo depois na linha  95 retornamos ao chamador, mas informando que o check-in não teve sucesso.

Caso o teste na linha 90, informe que o indicador não está presente. Iremos executar a linha 97, que irá dizer ao MetaTrader 5, qual será o real nome que o indicador irá ter. E assim na linha 99 retornamos ao chamador, informando que o indicador pode ser lançado com sucesso no gráfico. Mas isto não quer dizer de fato que ele foi posto no gráfico. Apenas informa que o MetaTrader 5, não o encontrou no gráfico. Mas agora ele poderá ser lançado.

Com isto retornamos a linha 30, onde uma decisão será tomada. Caso tenhamos sucesso, iremos passar para a linha 32. No caso de falha o MetaTrader 5 irá disparar um evento, de forma a chamar a função na linha 76. Porém pelo fato de que na linha 30 estamos informando que ocorreu uma falha, assim que a linha 78 for executada, ela irá impedir que o código entre as linhas 80 a 83 venha a se executado. Isto é importante para evitar informações de falhas na saída do indicador.

Mas vamos voltar na linha 32, onde agora iremos de fato começar colocar o indicador em funcionamento. Nesta linha 32, iniciamos o ponteiro para acessar a classe C_Terminal. A partir deste momento, poderemos fazer uso das funções presentes na classe C_Terminal. Logo depois disto, na linha 33, iniciamos o ponteiro para ter acesso a classe C_Study. Esta classe é que irá nos permitir usar o mouse e gerar estudos gráfico. Por conta que ela precisa ter acesso a classe C_Terminal, precisamos seguir esta sequencia de ações.

Agora na linha 34, temos um novo teste. Este teste irá definir se estamos usando o ativo de replay / simulação, ou se estamos usando um ativo diferente. Caso o ativo seja o de replay / simulação, o conteúdo presente no parâmetro de configuração de entrada do indicador, que se encontra declarado na linha 19, será colocado na nossa variável global. Isto é feito na linha 40.

Agora caso a linha 34, indique que estamos trabalhando com outro tipo de ativo, teremos a execução das linhas 36 a 38. Estas linhas irão inicializar o uso do book, dizendo ao MetaTrader 5, que desejamos receber eventos ocorridos no book. Assim com o fato de que na linha 38, dizemos que o mercado se encontra fechado. Esta condição é temporária, já que iremos receber orientação do book, a fim de saber qual será a informação correta.

Agora vem uma linha crucial para o indicador. Esta é a linha 41. Lembra de quando foi dito, que o fato de você declarar, usando as linhas 11 e 26, não garantem que o buffer será e poderá de fato ser acessado. E que se você tenta-se fazer isto, iria ser gerado um erro de RUN-TIME ?!? Pois bem, sem esta linha 41, de fato o indicador não faz nada. Ele é algo quase que totalmente inútil para nos. Mas nesta linha 41, informamos o index, o array e a utilidade do mesmo. Observem o seguinte: No campos do index, normalmente começamos com o valor zero. Isto não é regra, você pode usar um outro valor, porém é de boa prática começar pelo zero. No campo do array, indicamos qual é a variável usada como buffer. Já no terceiro campos, que informa a utilidade do buffer, podemos usar umas das enumerações ENUM_INDEXBUFFER_TYPE, mas já que iremos manter dados usamos INDICATOR_DATA. No entanto nada impede, neste código em particular você usar outro tipo.

Feito isto, executamos a linha 42. Esta de certa forma poderia ser descartada. Mas como quero que o buffer esteja limpo. Fazemos isto agora. Poderíamos adicionar mais coisas aqui, no entanto não vejo tal necessidade. Já que a ideia é simplesmente criar um tipo de cache para os dados do mouse, como você irá perceber nos próximos artigos.

Esta foi a explicação de como o indicador é inicializado. Agora precisamos saber, como ele irá responder, aos eventos disparados pelo MetaTrader 5. Para explicar esta parte, vamos começar vendo o código do evento de book. A função OnBookEvent. Este procedimento se inicia na linha 63, e é chamado pelo MetaTrader 5, toda a vez que ocorrer algo no book.

Se você observar com atenção, irá notar que o conteúdo deste procedimento, presente entre a linhas 70 e 72, é praticamente igual ao que era encontrado na classe C_Study. Mas então por que não deixei este procedimento na classe C_Study ?!?! É difícil explicar o motivo aqui, e agora. Mas acreditem, ele existe e será melhor entendido depois. Em um outro momento futuro. Desta forma, tal fragmento já deve ser de seu conhecimento. Desde é claro, que você esteja acompanhando esta sequencia de artigos e estudando os mesmo. Mas mesmo assim vamos dar uma rápida passada por esta função. Já que ela contém código extras.

Na linha 66, declaramos uma variável local e temporária. Esta irá armazenar temporariamente o valor do status de mercado. O motivo disto, é que este status pode mudar, seja na linha 70, seja na linha 72. Caso isto aconteça, na linha 73 teremos uma chamada a fim de atualizar de maneira confortável a informação no gráfico. Mas antes de vermos esta questão da atualização, vamos dar uma rápida olhada nas demais linhas.

Na linha 68 filtramos os eventos de BOOK para apenas os que fazem parte, de fato do ativo no gráfico. O MetaTrader 5 não faz tal distinção, então se qualquer ativo, que se encontra na janela de observação de mercado, lançar algum evento de book. O MetaTrader 5, irá disparar um evento de book. Por conta, disto temos que fazer a distinção no local.

Feito isto, na linha 69, iremos capturar os dados que estão presente no book. Desta forma poderemos analisá-los conforme a nossa necessidade. Mas primeiro, na linha 70, dizemos ao indicador que o mercado está fechado, ou esta aberto. Atenção a isto: Estamos falando do mercado físico. Ou seja, se o servidor de negociação, está ou não, dentro da janela de negociação permitida. Mas para saber isto, verificamos o fato de que o array de book, tem ou não algo nele.

Agora na linha 71 entramos em um laço. Este irá verificar ponto a ponto, no array, se existe alguma posição indicando, que o ativo se encontra em leilão. O ativo irá se encontrar em estado de leilão, se qualquer uma das duas condições verificadas na linha 72 forem verdadeiras. Por conta de não sabermos exatamente, onde está o BID e o ASK do book, usamos o laço da linha 71. Mas se por um acaso, o ativo que você estiver usando, tem uma posição fixa de BID e ASK. Você pode ignorar o laço removendo a linha 71 e simplesmente indicando qual é o ponto no array, onde está o BID e o ASK. Caso um destes dois, indiquem que o ativo está em leilão, o status será atualizado.

Agora voltemos a questão da atualização. Esta informação não será sempre repassada. Somente teremos uma chamada de atualização de status de mercado, se o valor armazenado na linha 66, for diferente do novo status.

O motivo de se fazer isto aqui, na linha 73. É por conta que quando um ativo entra em leilão, ou em estado de suspensão, durante a janela de negociação. Apenas o evento de book, terá de fato esta informação, e a devida noção de mudança no status. Se fossemos esperar que outro tipo de coisa viesse a acontecesse, poderíamos ficar em uma situação, em que não saberíamos de fato, o que estaria ocorrendo.

E temos uma outra questão, que é o segundo ponto onde a atualização de status acontece. Este é justamente o próximo evento que iremos ver: A função OnCalcule.

A cada atualização no preço, ou surgimento de uma nova barra no gráfico, o MetaTrader 5, dispara um evento. Este evento faz a chamada a função OnCalcule, no código do indicador. Normalmente e naturalmente, sempre queremos que este código execute o mais rápido, quanto for possível faze-lo. O motivo é evitar perder eventos de interesse. Mas diferente do evento de book que informa a mudança de status, assim que ela acontece. O evento OnCalcule, somente fará isto quando algo ocorrer no preço.

Mas mesmo assim, você pode notar que na linha 49, armazenamos e ajustamos um valor. Mas o mesmo não é usado ali. Se quer temos uma chamada para alguma função que precisamos usar este valor. Por que disto ?!?! 🤔. Novamente, precisamos que a função OnCalcule execute o mais rapidamente quanto for possível fazer. E não faz sentido você ficar atualizando o buffer a cada movimento do preço, ou a cada nova barra que surge. Não se esqueça que estamos trabalhando com o mouse, e não com o preço ou barras presentes no gráfico.

Então quem de fato é responsável e precisa atualizar o buffer é o próximo evento disparado pelo MetaTrader 5: A função OnChartEvent.

Esta função OnChartEvent que se inicia na linha 55, é bastante singela e simples. Nela temos uma chamada na linha 57, a fim de que os eventos sejam corretamente tratados pela classe de estudos. Esta por sua vez irá fazer um trabalho interno a fim de conseguir tratar completamente o evento de mouse. Para mais detalhes sobre isto veja Desenvolvendo um sistema de Replay (Parte 31): Projeto EA - Classe C_Mouse (V). Mas independente disto, teremos uma chamada a um procedimento de forma que o fluxo de execução irá parar na linha 102.

Antes de passarmos para a linha 102. Vamos dar uma rápida olhada no evento presente na linha 76. Este já foi mencionado antes, durante a explicação do processo de inicialização. Mas agora vamos ver como este mesmo código irá se comportar quando o indicador de fato for inicializado com sucesso. Quando isto acontece, iremos ter a execução das linhas entre 80 e 83. Na linha 80 iremos verificar qual é o ativo em que o indicador está sendo usando. O motivo disto é que se o ativo for um dos que podemos olhar eventos de book, termos que informar ao MetaTrader 5, que não desejamos mais receber tais eventos, isto é feito na linha 81. Já as linhas 82 e 83 simplesmente irão destruir as classe. Assim teremos a correta remoção do indicador do gráfico.

Já que o código presente a partir da linha 102, é de fato o que queremos e precisamos entender, neste artigo. O mesmo merece ser tratado em um outro tópico. Este se inicia logo abaixo:


Procedimento SetBuffer: Onde a mágica acontece.

Se você observou bem, e vem estudando os artigos desta sequencia. Deve ter notado que no artigo Desenvolvendo um sistema de Replay (Parte 39): Pavimentando o Terreno (III). Existe um tópico inteiro dedicado apenas e somente para explicar como colocar valores no buffer, a fim de enviar informações do indicador para outro processo.

Naquele tópico, expliquei que você deve colocar as informações em um ponto bem especifico. Mas principalmente, ressaltei o fato de que a informação deverá estar sendo colocada sobre uma tutela de tipo double. Talvez lá naquele artigo, você não tenha de fato se dado conta do que podemos fazer. Mas o fato é que podemos ir muito, mais muito mais além do que grande parte tem feito até o momento.

Se você observar o código entre as linhas 102 e 115, irá ver que estou fazendo as coisas de uma maneira. Mas por que estou fazendo assim ?!?!

Para que você não precise ficar rolando o artigo, a fim de acompanhar a explicação. Que tão trazer estas linhas para mais perto da explicação ?! Pois bem, assim ficará mais simples, para você acompanhar o raciocínio.

102. inline void SetBuffer(void)
103. {
104.    uCast_Double Info;
105.    
106.    m_posBuff = (m_posBuff < 0  ? 0 : m_posBuff);
107.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
108.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
109.    m_Buff[m_posBuff + 1] = Info.dValue;
110.    Info._int[0] = (*Study).GetInfoMouse().Position.X;
111.    Info._int[1] = (*Study).GetInfoMouse().Position.Y;
112.    m_Buff[m_posBuff + 2] = Info.dValue;
113.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
114.    m_Buff[m_posBuff + 3] = Info.dValue;
115. }

Vamos começar entendendo o seguinte: Na linha 49, indico que irei lançar 4 valores double dentro do buffer. Mas em que ponto do buffer ?!?! Este ponto, foi explicado no artigo mencionado acima. Iremos usar a posição rates_total. Exatamente 4 posições recuadas, partindo deste ponto. Caso estejamos no replay / simulação, poderemos estar iniciando a partir da posição zero. Isto por termos iniciado nela. Esta inicialização foi feita na linha 25.

Para facilitar as conversões que iremos fazer, e precisaremos fazer. Usamos a linha 104 para declarar uma variável local. Agora atenção ao fato de que, precisamos efetivar um teste na linha 106. Este evita que caso aconteça algo estranho, principalmente por conta da linha 49, que está presente na função OnCalcule. No caso de um ativo real, dificilmente iremos ter um valor negativo, na variável m_posBuff. Mas no ativo de replay / simulação, poderemos ter um valor negativo. Então esta linha 106 corrige isto, fazendo com que m_posBuff aponte para um posição válida.

Caso m_posBuff estivesse apontando, para um index menor que zero, teríamos a quebra do indicador. Agora observem que na linha 107, começamos a atualizar os dados do indicador. este ponto especifico é a parte fácil. Agora vamos a parte complicada. Esta está nas linhas 109, 112 e 114. Nestas lançamos outros valores para o buffer. Isto sempre partindo da posição inicialmente calculada.

Você pode estar pensando: Será que poderíamos lançar estes mesmos valores em outros pontos ?!?  A resposta é um sonoro NÃO. Poderíamos então lança-los em uma outra ordem ?!?! Neste caso a resposta é um SIM, bem simpático. Mas neste caso, você precisará resolver, ou melhor dizendo, corrigir os futuros códigos, a fim de ter as informações corretas. Se por qualquer motivo, você trocar a ordem, com que os valores venham a ser lançados, ou venha a lançar mais valores. Terá obrigatoriamente que corrigir os códigos que vierem a derivar deste indicador aqui. Ou de qualquer outro que você queira montar.

Então é bom, que você começe a desenvolver algum tipo de metodologia. Caso contrário, a coisa irá ficar extremamente confusa, e você não irá conseguir tirar proveito do que o MetaTrader 5 tem a oferecer. Desta forma, a metodologia que estarem usando aqui é a seguinte:

  • Primeiro, e na posição ZERO, iremos armazenar o preço onde a linha do mouse se encontra;
  • Na posição UM, iremos armazenar o valor de tempo onde o ponteiro do mouse se encontra;
  • Na posição DOIS, iremos armazenar os valores em termos de posição de tela, ou seja coordenadas X e Y. Sendo que o valor X vem primeiro e logo depois o valor Y;
  • Na posição TRÊS, iremos armazenar outras coisas extras. No momento o status dos botões do mouse. Isto irá ser colocada no byte menos significativo ( LSB ). Ou seja no byte zero;

Tais regras irão acompanhar este indicador pelo resto de sua vida. Isto até que ele venha a sofrer algum tipo de modificação ou atualização.


Conclusão

Existem algumas outras questões a serem resolvidas. Por conta disto quero que você, acompanhe os artigos, a fim de compreender o que estará sendo colocado no código. Já que muitas das vezes, o código presente no anexo, poderá estar diferente do que esta sendo explicado no artigo. Mas isto tem um motivo. O código no anexo, se encontra estável e perfeitamente funcional. Enquanto o código que estará sendo colocado no artigo, irá passar por algum tipo de mudança. Sendo necessário que você acompanhe os artigos para conseguir entender o código quando o mesmo estiver estável. Se você não fizer tal acompanhamento, não conseguirá compreender o código quando o mesmo estiver estável..

No entanto, para aqueles que não conseguem compilar um código complexo. Que envolve diversas aplicações diferentes. Não se preocupem. De tempos em tempos, irei colocar no anexo, o código já compilado. Sendo assim, todos poderão acompanhar o desenvolvimento do sistema. E mais, o código já compilado, que estará em alguns artigos. Servirá para que você possa compreender, quais aplicações surgirão, quanto tudo estiver perfeitamente compilado. Porém, você que já tem  com um pouco mais de experiência, e quer aperfeiçoar seu entendimento sobre programação. Pode ir adicionando o que está sendo mostrado nos artigos. Assim aprenderá como modificar um sistema de maneira a implementar qualquer outra coisa que você deseje fazer no futuro. No entanto, se você for fazer isto, sugiro que o faça com bastante atenção. E não se esqueça de sempre testar o que está sendo adicionado ao código.

De qualquer maneira, ainda não acabou. No próximo artigo iremos continuar este assunto, e resolver algumas questões que ficaram pendentes.

Arquivos anexados |
Anexo.zip (420.65 KB)
Operações de negociação Estruturas das solicitações e das resposta, descrição e registro Operações de negociação Estruturas das solicitações e das resposta, descrição e registro
Neste artigo, veremos como trabalhar com as estruturas das solicitações de negociação, criar a solicitação, verificá-la antes de enviá-la ao servidor, gerar a resposta do servidor quanto a ela e usar a estrutura das transações. Além disso, criaremos funções simples e convenientes para enviar ordens para o servidor e, com base em tudo o que foi mencionado acima, criar um Expert Advisor que informe sobre as transações.
Tudo o que você precisa saber sobre a estrutura de um programa MQL5 Tudo o que você precisa saber sobre a estrutura de um programa MQL5
Qualquer programa em qualquer linguagem de programação possui uma estrutura específica. Neste artigo, você aprenderá os componentes básicos da estrutura de um programa na linguagem MQL5, o que pode ser extremamente útil ao criar um sistema de negociação ou uma ferramenta de negociação para o MetaTrader 5.
Estratégia de negociação RSI Deep Three Move Estratégia de negociação RSI Deep Three Move
Este artigo apresenta a estratégia de negociação RSI Deep Three Move no MetaTrader 5. O artigo é baseado em uma nova série de pesquisas que demonstram vários métodos de negociação com base no RSI, que é um indicador técnico para medir a força e o impulso de ativos financeiros, incluindo ações, moedas e commodities.
Desenvolvendo um sistema de Replay (Parte 39): Pavimentando o Terreno (III) Desenvolvendo um sistema de Replay (Parte 39): Pavimentando o Terreno (III)
Antes de começarmos a segunda fase de desenvolvimento, é preciso reforçar algumas ideias. Então você sabe como forçar o MQL5 a fazer o que é preciso ser feito ?!?! Já tentou ir além do que a documentação informar ?!?! Se não. Se prepare. Pois irei começar a fazer coisas muito além do que grande parte faz normalmente.