preview
Desenvolvendo um sistema de Replay (Parte 49): Complicando as coisas (I)

Desenvolvendo um sistema de Replay (Parte 49): Complicando as coisas (I)

MetaTrader 5Exemplos | 9 maio 2024, 16:01
315 1
Daniel Jose
Daniel Jose

Introdução

Neste artigo iremos utilizar o que foi visto no artigo Desenvolvendo um sistema de Replay (Parte 48): Entendendo e compreendendo alguns conceitos. Então se você não leu aquele artigo, veja ele, pois o conteúdo visto lá será muito importante para entender o que faremos aqui.

Uma das coisas que mais me deixava incomodado durante os artigos anteriores, era o fato de que o sistema de replay / simulação, continha um indicador que estava visível para o usuário do MetaTrader 5 na área onde os indicadores são listados para se poder coloca-los no gráfico.

Mesmo contando com um travamento que foi implementado durante os artigos, a fim de impedir que o usuário viesse a tentar colocar tal indicador no gráfico, que não fosse o correto, no caso um gráfico diferente do usado pelo serviço de replay / simulação. Ainda assim, o fato de tal indicador está presente entre os outros, me incomodava muito.

Durante todos estes meses, passei tentando e analisando como poderia fazer as coisas da maneira como ao meu ver, parecem ser a mais adequada. Felizmente recentemente consegui encontrar a solução de modo a deixar as coisas melhores. Com isto o indicador de controle, irá deixar de estar presente entre os demais indicadores, e passará a ser parte integral do serviço de replay / simulação.

Ao fazer este tipo de coisa, teremos um grau maior de liberdade com relação a alguns fatores. No entanto, irei fazer as mudanças aos poucos, visto que também irei modificar o indicador a fim de proporcionar um alivio ao MetaTrader 5. Ou seja, iremos deixar de utilizar alguns recursos e passaremos a usar outros da plataforma. Isto irá trazer uma melhor estabilidade, segurança e confiabilidade ao sistema de replay / simulador.

Então vamos ver como isto irá ser dar. Já que as mudanças serão bem interessantes de serem implementadas, trazendo um grande conhecimento de como trabalhar de forma mais avançada no MQL5. Lembrando que isto, que estou fazendo não é um atraso no desenvolvimento do sistema de replay / simulador. O mesmo precisa de fato de algumas coisas, que neste artigo iremos iniciar o estudo, de forma a conseguir codificar outras coisas no futuro.

Então vamos continuar a nossa saga, em direção a uma implementação mais avançada do serviço de replay / simulador.


Iniciando as mudanças

Para deixar claro o que vai ser feito, irei ir com calma, assim você, caro leitor, irá conseguir acompanhar as mudanças que irão de fato acontecer, e precisam acontecer. Muitos podem achar que a forma como irei mostrar as coisas aqui são totalmente desnecessárias. Que poderíamos simplesmente partir para o código já finalizado e pronto. Mas se isto fosse o que deveria ser feito. Então por que ração os artigos anteriores teriam em existir ?!?! Não faz sentido, não é mesmo ??? Mas como aqui o material está sendo voltado para motivar e incentivar as pessoas a produzirem os seus próprios programas e soluções. Devo mostrar como você faz para conseguir limpar um código, já pronto e torná-lo adequado para uso em um modelo mais elaborado. Isto sem que o mesmo se torne algo bizarro e deixe de funcionar.

Então tenha calma. Vamos seguir as coisas sempre pensando em quem tem menos experiência.

Para começar, vamos remover o sistema de ligação da ID do gráfico. Este sistema evita que o indicador de controle, possa ser colocado em mais de um gráfico. Ao mesmo tempo evita que ele seja colocado no gráfico errado. Apesar desta remoção ser feita aqui, depois iremos recolocar este mesmo sistema em funcionamento novamente. No entanto, quando isto vier a ser feito, será feito de forma a manter o indicador no gráfico correto. Mas este trabalho será feito pelo serviço de replay / simulação, e não mais pelo indicador.

Para que esta remoção seja feita de maneira adequada e segura, vamos usar sempre o mesmo ponto comum entre o serviço e o indicador de controle. Este código comum entre ambos é visto logo abaixo:

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

Código fonte Interprocess.mqh

Se você observar na linha 06 temos uma definição. Esta indica tanto para o serviço, quanto para o indicador qual é o nome da variável global de terminal que será usada para passar o ID do gráfico. Se esta linha for removida, ao se tentar compilar o indicador e o serviço, será gerada uma série de erros. E é justamente isto que queremos. De fato, cada um dos pontos de erro que for gerado na compilação, indica onde deveremos de fato atuar a fim de conseguir remover esta comunicação que existe entre o serviço e o indicador, via variável global de terminal.

Para não ficar uma coisa extremamente cansativa e repetitiva. Visto que iremos fazer diversas mudanças. Irei mostrar as coisas em termos de fragmento de código. Mas sempre reportando de onde exatamente você deverá atuar no código original a fim de conseguir fazer o sistema se manter em funcionamento.

Sendo assim, vamos começar vendo o seguinte fato.

01. //+------------------------------------------------------------------+
02. #property service
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property copyright "Daniel Jose"
05. #property version   "1.49"
06. #property description "Replay-Simulator service for MT5 platform."
07. #property description "This is dependent on the Market Replay indicator."
08. #property description "For more details on this version see the article."
09. #property link "https://www.mql5.com/pt/articles/11820"
10. //+------------------------------------------------------------------+
11. #define def_Dependence_01   "Indicators\\Replay\\Market Replay.ex5"
12. #resource "\\" + def_Dependence_01
13. //+------------------------------------------------------------------+
14. #include <Market Replay\Service Graphics\C_Replay.mqh>
15. //+------------------------------------------------------------------+
16. input string           user00 = "Forex - EURUSD.txt";  //Replay configuration file.
17. input ENUM_TIMEFRAMES  user01 = PERIOD_M5;             //Initial graphic time.
18. //+------------------------------------------------------------------+
19. void OnStart()
20. {
21.     C_Replay  *pReplay;
22. 
23.     pReplay = new C_Replay(user00);
24.     if ((*pReplay).ViewReplay(user01))
25.     {
26.             Print("Permission granted. Replay service can now be used...");
27.             while ((*pReplay).LoopEventOnTime(false));
28.     }
29.     delete pReplay;
30. }
31. //+------------------------------------------------------------------+

Código fonte: Serviço Replay.mq5

Neste código, visto acima, que é justamente o código do serviço. Você pode ver que nas linhas 11 e 12 temos algo um pouco diferente do que existia nos códigos anteriores. A diferença é sútil, mas ela existe. No entanto é justamente esta diferença que irá promover o que queremos e iremos de fato fazer. Depois iremos voltar a falar mais sobre isto. Mas a questão aqui é a seguinte: Se o indicador de controle foi ou não recentemente compilado, isto depois que modificamos o código de cabeçalho removendo a definição do nome da variável global de terminal.

E sim, no momento que escrevo este artigo, ainda não contamos com um sistema de compilação MAKE no MetaEditor. Mas quem sabe no futuro os que mantém a plataforma e a linguagem MQL5, venham a colocar tal coisa para nós. Mas até lá temos que tomar certos cuidados.

Antes de continuarmos a explicação, deixe-me fazer uma breve pausa, a fim de explicar o que seria este tal sistema de compilação MAKE. Este tipo de coisa pode ser novidade para muitos. Mas para quem já programa de forma profissional a anos, este sistema é muito bem conhecido. Ele basicamente é o seguinte: Você cria o seu código fonte normalmente. Mas as vezes será preciso criar diversos arquivos executáveis, isto ao mesmo tempo. Pode também ser aproveitado para gerar um único executável. Apesar de não ser tão necessário fazer através do MAKE.

Então antes de compilar de fato as coisas, você cria um outro arquivo. Este é o arquivo MakeFile, que contém uma série de passos, definições, configurações e ajustes necessários para que o compilador e LinkEditor consiga gerar todos os arquivos executáveis. E isto em um só passo. A grande sacada nesta história toda, é caso você modifique 1 e apenas um único arquivo, seja ele de cabeçalho ou de biblioteca. O MAKE irá perceber isto, e irá fazer a compilação novamente apenas e somente dos arquivos realmente necessários. Assim no final você terá todos os executáveis sendo atualizados conforme a necessidade.

Este tipo de coisa tira dos ombros do programador, e joga nos ombros do MAKE o peso de saber quem deve ou não ser atualizado no momento da criação do executável final. Assim você não corre o risco de criar algo, ou corrigir uma falha. Esta ser corrigida em um executável, e permanecer em outro. Isto por conta que o MAKE faz todo o trabalho para nós.

No entanto, até o momento onde este artigo está sendo escrito, tal ferramenta ainda não existe no MetaEditor. Até dá para criar algo parecido usando arquivos de LOTE, via prompt de comando, mas isto seria apenas uma gambiarra e não uma solução ideal. Sendo assim não irei entrar de fato em detalhes em como fazer tal coisa. Mas já que a linha 12 no código acima, diz para o compilador do MQL5, que o executável do indicador de controle deverá ser adicionado ao serviço. Você pode simplesmente pedir para compilar o código do serviço, que o código do indicador será compilado junto. Já falei sobre isto anteriormente. Mas volta a ressaltar uma coisa aqui novamente: Para que o indicador seja adequadamente compilado, já com as atualizações, você a cada vez que compilar o código do serviço, deverá deletar o executável do indicador. Se você não fizer isto, o código do indicador, não será compilado novamente, quando você fizer a compilação do código de serviço. Então preste atenção a isto.

Então ao tentar compilar o código do serviço, você receber a seguinte resultado, que pode ser visto na imagem 01.

Figura 01

Imagem 01 - Resultado da tentativa de compilar o novo código.

Você pode notar que temos 2 erros e um alerta. O alerta não é, e não deve ser a sua primeira preocupação. Primeiro você tem que resolver os erros. E observando a imagem 01, você pode ver onde eles estão, mas basta clicar neles e você será dirigido para o local exato onde ele aconteceu.

O erro que se encontra indicado, na imagem 01, e que está presente na linha 12 é devido ao fato de que o compilador, não conseguiu encontrar o executável a fim de colocá-lo como um recurso do serviço.

No entanto o erro que está sendo indicador na linha 156, faz sim parte do código do serviço, e devemos corrigir ele, para assim conseguir progredir na compilação.

Pois bem, o código onde o erro está pode ser visto no fragmento abaixo. 

141. ~C_Replay()
142.    {
143.            ArrayFree(m_Ticks.Info);
144.            ArrayFree(m_Ticks.Rate);
145.            m_IdReplay = ChartFirst();
146.            do
147.            {
148.                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
149.                            ChartClose(m_IdReplay);
150.            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
151.            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
152.            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
153.            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
154.            CustomSymbolDelete(def_SymbolReplay);
155.            GlobalVariableDel(def_GlobalVariableReplay);
156.            GlobalVariableDel(def_GlobalVariableIdGraphics);
157.            GlobalVariableDel(def_GlobalVariableServerTime);
158.            Print("Finished replay service...");
159.    }

Fragmento do código C_Replay.mqh

Observe que você pode comentar, ou simplesmente remover a linha 156 do código. Já que a mudança será definitiva, vamos remover esta linha neste exato momento. Sendo assim, agora o serviço não irá mais ver esta variável global de terminal. Mas mesmo fazendo a correção no serviço, ao tentar compilar novamente o código, veja o resultado da tentativa na imagem 02.

Figura 02

Imagem 02 - Nova tentativa de compilação.

Observe que agora temos apenas a indicação de um erro, e o alerta não existe mais. Neste momento, muitos com menos experiência ficam pensando como corrigir este erro que está sendo mostrado. Mas, no entanto, eles se esquecem de fato é ler o que o compilador está nos informando.

Preste atenção na imagem 02. Note que logo acima da mensagem de erro, está sendo informado algumas outras coisas. Você NUNCA deve ignorar estas coisas que o compilador está informando. Deve sempre prestar atenção a todas elas. Literalmente TODAS. E na mensagem imediatamente anterior ao erro você pode ver a seguinte informação vinda do compilador:

compiling '\Indicators\Replay\Market Replay.mq5' failed

Esta mensagem é de suma importância, já que ela nos diz que o compilador, por algum motivo, não está conseguindo criar o executável do indicador. Então, já que o código do serviço está sendo compilado, de forma parcial. Devemos voltar a nossa atenção ao código do indicador. Então abrimos o código do mesmo no MetaEditor, e pedimos para que o compilador tente gerar o executável. E antes que alguém pense: Mas por que você está fazendo isto ?!?! Você já não sabe que o código contém erros ?!?! Sim. Já sabemos que ele contém erros. Mas queremos que o compilador nos diga onde. Não é produtivo você simplesmente sair procurando onde eles estão. Faça o compilador dizer para você.

Então ao abrir o código do indicador, e pedir para o compilador gerar o executável, você verá a imagem 03.

Figura 03

Imagem 03 - Tentativa de compilar o indicador de controle.

Mais uma vez, temos a indicação de cinco erros e cinco alertas. Primeiro se preocupe com os erros, depois com os alertas. Então clicado primeiramente no primeiro erro informado, iremos ser dirigidos ao código, no exato local do erro. Um detalhe: Muita gente acha que você deve sair clicando nos erros de maneira aleatória. Mas via de regra você deve fazer da seguinte forma: Procure o primeiro erro na lista. Não importa os demais, vá sempre no primeiro. Faça a correção necessária. E tente compilar novamente o código. Faça isto até que o código seja compilado sem alertas e sem erros. É muito comum, principalmente para os iniciantes, ficarem completamente loucos, quando ao tentar compilar um código, este gere as vezes 100 ou mais erros.

Quando muitas das vezes corrigindo o primeiro erro, ou o que fez gerar o primeiro erro, faz todo o código vim a ser compilado logo depois. Então fica a dica. Procure o primeiro erro informado pelo compilador. Veja como corrigir ele de forma adequada, tentem compilar novamente o código, se um novo erro surgir, vá no primeiro listado e faça a correção, e assim até não haver mais erros. A mesma coisa deve ser feita para o caso dos alertas. Vá sempre no primeiro da lista.

Bem, já que todos os erros informados pelo compilador e visto na imagem 03, estão no mesmo código, é mais simples colocar todo ele a fim de que você possa saber onde estarão as modificações. 

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.49"
07. #property link "https://www.mql5.com/pt/articles/11820"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. #define def_BitShift ((sizeof(ulong) * 8) - 1)
14. //+------------------------------------------------------------------+
15. C_Terminal *terminal = NULL;
16. C_Controls *control = NULL;
17. //+------------------------------------------------------------------+
18. #define def_InfoTerminal (*terminal).GetInfoTerminal()
19. #define def_ShortName       "Market_" + def_SymbolReplay
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23. #define macro_INIT_FAILED { ChartIndicatorDelete(def_InfoTerminal.ID, 0, def_ShortName); return INIT_FAILED; }
24.     u_Interprocess Info;
25.     ulong ul = 1;
26. 
27.     ResetLastError();
28.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED;
29.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
30.     ul <<= def_BitShift;
31.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
32.     if ((def_InfoTerminal.szSymbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
33.     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
34.     if (Info.u_Value.IdGraphic != def_InfoTerminal.ID) macro_INIT_FAILED;
35.     if ((Info.u_Value.IdGraphic >> def_BitShift) == 1) macro_INIT_FAILED;
36.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
37.     Info.u_Value.IdGraphic |= ul;
38.     GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
39.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
40.     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, "");
41.     (*control).Init(Info.s_Infos.isPlay);
42.         
43.     return INIT_SUCCEEDED;
44.     
45. #undef macro_INIT_FAILED
46. }
47. //+------------------------------------------------------------------+
48. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
49. {
50.     static bool bWait = false;
51.     u_Interprocess Info;
52.     
53.     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
54.     if (!bWait)
55.     {
56.             if (Info.s_Infos.isWait)
57.             {
58.                     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, "");
59.                     bWait = true;
60.             }
61.     }else if (!Info.s_Infos.isWait)
62.     {
63.             EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, "");
64.             bWait = false;
65.     }
66.     
67.     return rates_total;
68. }
69. //+------------------------------------------------------------------+
70. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
71. {
72.     (*control).DispatchMessage(id, lparam, dparam, sparam);
73. }
74. //+------------------------------------------------------------------+
75. void OnDeinit(const int reason)
76. {
77.     u_Interprocess Info;
78.     ulong ul = 1;
79. 
80.     switch (reason)
81.     {
82.             case REASON_CHARTCHANGE:
83.                     ul <<= def_BitShift;
84.                     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
85.                     Info.u_Value.IdGraphic ^= ul;
86.                     GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
87.                     break;
88.             case REASON_REMOVE:
89.             case REASON_CHARTCLOSE:
90.                     if (def_InfoTerminal.szSymbol != def_SymbolReplay) break;
91.                     GlobalVariableDel(def_GlobalVariableReplay);
92.                     ChartClose(def_InfoTerminal.ID);
93.                     break;
94.     }
95.     delete control;
96.     delete terminal;
97. }
98. //+------------------------------------------------------------------+

Código fonte do indicador : Market Replay.mq5

Todas as linhas que estão riscadas deixaram de existir no código. Assim ao tentar compilar novamente ele você irá receber a seguinte mensagem vista na imagem 04.

Figura 04

Imagem 04

Note que nesta imagem 04 existe um alerta. Este de fato não atrapalhará o código, mas fica ali atormentando a nossa vida. Sendo assim vá na linha 77 e remova a tal linha e tente compilar novamente o código. E você irá receber a mensagem vista na imagem 05, que é justamente a que queremos. Mais atenção, a linha 77 só pode ser removida, por conta que o compilador nos informou que a variável presente ali, não está sendo utilizada.

Figura 05

Imagem 05 - Compilação feita com sucesso.

Muito bem, muito bem, muito bem. Nosso código foi limpo parcialmente. No entanto, voltando ao código do arquivo de cabeçalho Interprocess.mqh, visto no início deste tópico, você pode notar que ainda temos uma outra coisa, ainda presente no código, que não será de fato mais necessária. Isto por conta que não estamos mais usando a variável global de terminal, a fim de informar ao indicador qual é o ID do gráfico. Então a linha 15 do arquivo Interprocess.mqh deverá ser removida. Mas aí vem a pergunta: Por que eu não havia removido esta linha antes ?!?! O motivo, é por que será preciso tomar um cuidado especial no arquivo de serviço. Mas existe um outro motivo também, e para entender ele, vamos para o próximo tópico.


Radicalizando as decisões

Quando o serviço de replay / simulador foi desenhado, para ser desenvolvido. Uma das coisas que tentei fazer, foi justamente evitar que o usuário viesse a interferir no que deveria ou não estar presente no gráfico.

A forma que encontrei para resolver este problema, foi adicionar uma variável global de terminal, a fim de informar ao indicador, em qual gráfico ele deveria estar de fato presente. Não podendo ser colocado pelo usuário em outro gráfico. De fato, esta solução foi adequada e bastante interessante de ser implementada. Você pode inclusive usar algo parecido, quando desejar assegurar que um indicador, script ou Expert Advisor, não seja colocado em um gráfico, mas isto não vem ao caso agora.

No momento em que fazemos o uso do serviço a fim de controlar o que deverá ou não estar no gráfico, tal sistema se torna completamente desnecessário. Ou melhor dizendo: Já que o indicador, estará presente como sendo um recurso do serviço, e não estará acessível ao usuário. Não faz sentido, continuar manter a codificação da forma como estava sendo feita. O nível de segurança se torna outro e o controle de acesso passa a ser completamente diferente.

Desta forma já que ao remover a variável ID do sistema, devemos fazer isto com calma. Por conta deste fato, não fiz a remoção no tópico anterior. E você logo irá entender o motivo. Pois se a remoção fosse feita antes da hora, ficaria muito difícil fazer as devidas mudanças, e a chance de fazer alguma bobagem seria enorme.

O maior trunfo de um grande programador, é justamente este. Ter CALMA. Atacar o problema, um a um, e ir corrigindo e modificando as coisas de maneira a manter o que antes era possível fazer. E se for necessário e interessante, aumentar as capacidades do programa. Então o lema é: 

Dividir para conquistar.


Então vamos ver como será feita as coisas, a fim de limpar o código. Desta forma, o novo código do arquivo Interprocess.mqh, pode ser visto na integra logo abaixo:

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

Código fonte : Interprocess.mqh

Notem que este código, apesar de não parecer assim tão diferente, irá fazer uma grande diferença no sistema que acabamos de modificar no tópico anterior. Então ao tentar compilar novamente o sistema, não fique surpreso, ou assustado com a quantidade de erros que serão gerados. Tudo que precisaremos fazer será reajustar as coisas a fim de que possamos ter o funcionamento que tínhamos antes. Mas agora o indicador de controle começará a deixar de ser acessível ao usuário. Sendo apenas o serviço o responsável por colocar e manter ele no gráfico correto.

Ao tentar compilar o código do serviço, você irá receber o seguinte retorno no compilador. Este pode ser visto na imagem 06.

Figura 06

Imagem 06 - Muitas falhas. Mas será que temos todas elas ?!?!

Note que a quantidade de erros foi bastante grande, além da quantidade de alertas. Mas com informei acima, vamos começar com o primeiro erro nesta lista. Sendo assim ao começar a fazer as mudanças, elas se iniciarão na linha 28 do arquivo de cabeçalho C_Replay.mqh. Para não ficar uma coisa muito cansativa de ser explicada, vamos então observar o código abaixo, já que a maior parte do que precisaremos fazer será remover u_Value do código, então o código já sem esta referência pode ser visto na integra logo abaixo:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_ConfigService.mqh"
005. //+------------------------------------------------------------------+
006. class C_Replay : private C_ConfigService
007. {
008.    private :
009.            long   m_IdReplay;
010.            struct st01
011.            {
012.                   MqlRates Rate[1];
013.                   datetime memDT;
014.            }m_MountBar;
015.            struct st02
016.            {
017.                   bool    bInit;
018.                   double  PointsPerTick;
019.                   MqlTick tick[1];
020.            }m_Infos;
021. //+------------------------------------------------------------------+
022.            void AdjustPositionToReplay(const bool bViewBuider)
023.                    {
024.                            u_Interprocess Info;
025.                            MqlRates       Rate[def_BarsDiary];
026.                            int            iPos, nCount;
027.                            
028.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
029.                            if (Info.s_Infos.iPosShift == (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks)) return;
030.                            iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / (def_MaxPosSlider + 1)));
031.                            Rate[0].time = macroRemoveSec(m_Ticks.Info[iPos].time);
032.                            CreateBarInReplay(true);
033.                            if (bViewBuider)
034.                            {
035.                                    Info.s_Infos.isWait = true;
036.                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
037.                            }else
038.                            {
039.                                    for(; Rate[0].time > (m_Ticks.Info[m_ReplayCount].time); m_ReplayCount++);
040.                                    for (nCount = 0; m_Ticks.Rate[nCount].time < macroRemoveSec(m_Ticks.Info[iPos].time); nCount++);
041.                                    nCount = CustomRatesUpdate(def_SymbolReplay, m_Ticks.Rate, nCount);
042.                            }
043.                            for (iPos = (iPos > 0 ? iPos - 1 : 0); (m_ReplayCount < iPos) && (!_StopFlag);) CreateBarInReplay(false);
044.                            CustomTicksAdd(def_SymbolReplay, m_Ticks.Info, m_ReplayCount);
045.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
046.                            Info.s_Infos.isWait = false;
047.                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
048.                    }
049. //+------------------------------------------------------------------+
050. inline void CreateBarInReplay(const bool bViewTicks)
051.                    {
052. #define def_Rate m_MountBar.Rate[0]
053. 
054.                            bool    bNew;
055.                            double  dSpread;
056.                            int     iRand = rand();
057.                            
058.                            if (BuildBar1Min(m_ReplayCount, def_Rate, bNew))
059.                            {
060.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
061.                                    if ((!m_Ticks.bTickReal) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
062.                                    {                                               
063.                                            dSpread = m_Infos.PointsPerTick + ((iRand > 29080) && (iRand < 32767) ? ((iRand & 1) == 1 ? m_Infos.PointsPerTick : 0 ) : 0 );
064.                                            if (m_Infos.tick[0].last > m_Infos.tick[0].ask)
065.                                            {
066.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last;
067.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last - dSpread;
068.                                            }else   if (m_Infos.tick[0].last < m_Infos.tick[0].bid)
069.                                            {
070.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last + dSpread;
071.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last;
072.                                            }
073.                                    }
074.                                    if (bViewTicks) CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
075.                                    CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate);
076.                            }
077.                            m_ReplayCount++;
078. #undef def_Rate
079.                    }
080. //+------------------------------------------------------------------+
081.            void ViewInfos(void)
082.                    {
083.                            MqlRates Rate[1];
084.                            
085.                            ChartSetInteger(m_IdReplay, CHART_SHOW_ASK_LINE, m_Ticks.ModePlot == PRICE_FOREX);
086.                            ChartSetInteger(m_IdReplay, CHART_SHOW_BID_LINE, m_Ticks.ModePlot == PRICE_FOREX);
087.                            ChartSetInteger(m_IdReplay, CHART_SHOW_LAST_LINE, m_Ticks.ModePlot == PRICE_EXCHANGE);
088.                            m_Infos.PointsPerTick = SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE);
089.                            m_MountBar.Rate[0].time = 0;
090.                            m_Infos.bInit = true;
091.                            CopyRates(def_SymbolReplay, PERIOD_M1, 0, 1, Rate);
092.                            if ((m_ReplayCount == 0) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
093.                                    for (; m_Ticks.Info[m_ReplayCount].volume_real == 0; m_ReplayCount++);
094.                            if (Rate[0].close > 0)
095.                            {
096.                                    if (m_Ticks.ModePlot == PRICE_EXCHANGE) m_Infos.tick[0].last = Rate[0].close; else
097.                                    {
098.                                            m_Infos.tick[0].bid = Rate[0].close;
099.                                            m_Infos.tick[0].ask = Rate[0].close + (Rate[0].spread * m_Infos.PointsPerTick);
100.                                    }                                       
101.                                    m_Infos.tick[0].time = Rate[0].time;
102.                                    m_Infos.tick[0].time_msc = Rate[0].time * 1000;
103.                            }else
104.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
105.                            CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
106.                            ChartRedraw(m_IdReplay);
107.                    }
108. //+------------------------------------------------------------------+
109.            void CreateGlobalVariable(const string szName, const double value)
110.                    {
111.                            GlobalVariableDel(szName);
112.                            GlobalVariableTemp(szName);     
113.                            GlobalVariableSet(szName, value);
114.                    }
115. //+------------------------------------------------------------------+
116.    public  :
117. //+------------------------------------------------------------------+
118.            C_Replay(const string szFileConfig)
119.                    {
120.                            m_ReplayCount = 0;
121.                            m_dtPrevLoading = 0;
122.                            m_Ticks.nTicks = 0;
123.                            m_Infos.bInit = false;
124.                            Print("************** Market Replay Service **************");
125.                            srand(GetTickCount());
126.                            GlobalVariableDel(def_GlobalVariableReplay);
127.                            SymbolSelect(def_SymbolReplay, false);
128.                            CustomSymbolDelete(def_SymbolReplay);
129.                            CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);
130.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
131.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
132.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0);
133.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0);
134.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0);
135.                            CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation");
136.                            CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, 8);
137.                            m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1);
138.                            SymbolSelect(def_SymbolReplay, true);
139.                    }
140. //+------------------------------------------------------------------+
141.            ~C_Replay()
142.                    {
143.                            ArrayFree(m_Ticks.Info);
144.                            ArrayFree(m_Ticks.Rate);
145.                            m_IdReplay = ChartFirst();
146.                            do
147.                            {
148.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
149.                                            ChartClose(m_IdReplay);
150.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
151.                            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
152.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
153.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
154.                            CustomSymbolDelete(def_SymbolReplay);
155.                            GlobalVariableDel(def_GlobalVariableReplay);
156.                            GlobalVariableDel(def_GlobalVariableServerTime);
157.                            Print("Finished replay service...");
158.                    }
159. //+------------------------------------------------------------------+
160.            bool ViewReplay(ENUM_TIMEFRAMES arg1)
161.                    {
162. #define macroError(A) { Print(A); return false; }
163.                            u_Interprocess info;
164.                            
165.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0)
166.                                    macroError("Asset configuration is not complete, it remains to declare the size of the ticket.");
167.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0)
168.                                    macroError("Asset configuration is not complete, need to declare the ticket value.");
169.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0)
170.                                    macroError("Asset configuration not complete, need to declare the minimum volume.");
171.                            if (m_IdReplay == -1) return false;
172.                            if ((m_IdReplay = ChartFirst()) > 0) do
173.                            {
174.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
175.                                    {
176.                                            ChartClose(m_IdReplay);
177.                                            ChartRedraw();
178.                                    }
179.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
180.                            Print("Waiting for [Market Replay] indicator permission to start replay ...");
181.                            info.ServerTime = ULONG_MAX;
182.                            CreateGlobalVariable(def_GlobalVariableServerTime, info.df_Value);
183.                            m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
184.                            ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
185.                            while ((!GlobalVariableGet(def_GlobalVariableReplay, info.df_Value)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750);
186.                            info.s_Infos.isHedging = TypeAccountIsHedging();
187.                            info.s_Infos.isSync = true;
188.                            GlobalVariableSet(def_GlobalVariableReplay, info.df_Value);
189. 
190.                            return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""));
191. #undef macroError
192.                    }
193. //+------------------------------------------------------------------+
194.            bool LoopEventOnTime(const bool bViewBuider)
195.                    {
196.                            u_Interprocess Info;
197.                            int iPos, iTest, iCount;
198.                            
199.                            if (!m_Infos.bInit) ViewInfos();
200.                            iTest = 0;
201.                            while ((iTest == 0) && (!_StopFlag))
202.                            {
203.                                    iTest = (ChartSymbol(m_IdReplay) != "" ? iTest : -1);
204.                                    iTest = (GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value) ? iTest : -1);
205.                                    iTest = (iTest == 0 ? (Info.s_Infos.isPlay ? 1 : iTest) : iTest);
206.                                    if (iTest == 0) Sleep(100);
207.                            }
208.                            if ((iTest < 0) || (_StopFlag)) return false;
209.                            AdjustPositionToReplay(bViewBuider);
210.                            Info.ServerTime = m_Ticks.Info[m_ReplayCount].time;
211.                            GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
212.                            iPos = iCount = 0;
213.                            while ((m_ReplayCount < m_Ticks.nTicks) && (!_StopFlag))
214.                            {
215.                                    iPos += (int)(m_ReplayCount < (m_Ticks.nTicks - 1) ? m_Ticks.Info[m_ReplayCount + 1].time_msc - m_Ticks.Info[m_ReplayCount].time_msc : 0);
216.                                    CreateBarInReplay(true);
217.                                    while ((iPos > 200) && (!_StopFlag))
218.                                    {
219.                                            if (ChartSymbol(m_IdReplay) == "") return false;
220.                                            GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value);
221.                                            if (!Info.s_Infos.isPlay) return true;
222.                                            Info.s_Infos.iPosShift = (ushort)((m_ReplayCount * def_MaxPosSlider) / m_Ticks.nTicks);
223.                                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
224.                                            Sleep(195);
225.                                            iPos -= 200;
226.                                            iCount++;
227.                                            if (iCount > 4)
228.                                            {
229.                                                    iCount = 0;
230.                                                    GlobalVariableGet(def_GlobalVariableServerTime, Info.df_Value);
231.                                                    if ((m_Ticks.Info[m_ReplayCount].time - m_Ticks.Info[m_ReplayCount - 1].time) > 60) Info.ServerTime = ULONG_MAX; else
232.                                                    {
233.                                                            Info.ServerTime += 1;
234.                                                            Info.ServerTime = ((Info.ServerTime + 1) < m_Ticks.Info[m_ReplayCount].time ? Info.ServerTime : m_Ticks.Info[m_ReplayCount].time);
235.                                                    };
236.                                                    GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
237.                                            }
238.                                    }
239.                            }                               
240.                            return (m_ReplayCount == m_Ticks.nTicks);
241.                    }                               
242. //+------------------------------------------------------------------+
243. };
244. //+------------------------------------------------------------------+
245. #undef macroRemoveSec
246. #undef def_SymbolReplay
247. //+------------------------------------------------------------------+

Código fonte do arquivo C_Replay.mqh

Este código visto acima já se encontra corrigido para o que vamos fazer neste artigo. Mas inda falta o código do indicador, mas se você compilar novamente o código do serviço já com as mudanças que foram feitas no código de cabeçalho C_Replay.mqh, irá ter como resultado o que é visto na imagem 07.

Figura 07

Imagem 07 - Falhas ainda apresentadas no Indicador.

Ou seja, agora temos que corrigir o código do indicador. Ao se tentar fazer isto, você irá obter como resultado a imagem 08.

Figura 08

Imagem 08 - Falhas oriundas da mesma causa.

Mais uma vez, siga o passo a passo. Comece sempre do primeiro erro indicado.

Então ao modificar de forma correta o arquivo do indicador, você terá como resultado o código que pode ser visto logo abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.49"
07. #property link "https://www.mql5.com/pt/articles/11820"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Terminal *terminal = NULL;
14. C_Controls *control = NULL;
15. //+------------------------------------------------------------------+
16. #define def_InfoTerminal (*terminal).GetInfoTerminal()
17. #define def_ShortName       "Market_" + def_SymbolReplay
18. //+------------------------------------------------------------------+
19. int OnInit()
20. {
21.     u_Interprocess Info;
22. 
23.     ResetLastError();
24.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED;
25.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
26.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
27.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
28.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
29.     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
30.     (*control).Init(Info.s_Infos.isPlay);
31.         
32.     return INIT_SUCCEEDED;
33. }
34. //+------------------------------------------------------------------+
35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
36. {
37.     static bool bWait = false;
38.     u_Interprocess Info;
39.     
40.     Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
41.     if (!bWait)
42.     {
43.             if (Info.s_Infos.isWait)
44.             {
45.                     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, "");
46.                     bWait = true;
47.             }
48.     }else if (!Info.s_Infos.isWait)
49.     {
50.             EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
51.             bWait = false;
52.     }
53.     
54.     return rates_total;
55. }
56. //+------------------------------------------------------------------+
57. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
58. {
59.     (*control).DispatchMessage(id, lparam, dparam, sparam);
60. }
61. //+------------------------------------------------------------------+
62. void OnDeinit(const int reason)
63. {
64.     switch (reason)
65.     {
66.             case REASON_REMOVE:
67.             case REASON_CHARTCLOSE:
68.                     if (def_InfoTerminal.szSymbol != def_SymbolReplay) break;
69.                     GlobalVariableDel(def_GlobalVariableReplay);
70.                     ChartClose(def_InfoTerminal.ID);
71.                     break;
72.     }
73.     delete control;
74.     delete terminal;
75. }
76. //+------------------------------------------------------------------+

Código fonte do Indicador de replay.

Este código já está totalmente corrigido, mas ao tentar compilar o mesmo, você irá receber ainda alguns erros do compilador, estes podem ser visto na imagem 09.

Figura 09

Imagem 09 - Tentativa de compilar o indicador de controle.

Então precisamos ir para o arquivo de cabeçalho C_Controls.mqh e efetuar as correções. Mas estas correções são bem simples de serem feitas. Já que tudo que será preciso será remover qualquer referência a u_Value. Sendo assim no final deste processo, você terá o arquivo visto no código logo abaixo:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\Interprocess.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PathBMP                "Images\\Market Replay\\Control\\"
007. #define def_ButtonPlay             def_PathBMP + "Play.bmp"
008. #define def_ButtonPause            def_PathBMP + "Pause.bmp"
009. #define def_ButtonLeft             def_PathBMP + "Left.bmp"
010. #define def_ButtonLeftBlock        def_PathBMP + "Left_Block.bmp"
011. #define def_ButtonRight            def_PathBMP + "Right.bmp"
012. #define def_ButtonRightBlock       def_PathBMP + "Right_Block.bmp"
013. #define def_ButtonPin              def_PathBMP + "Pin.bmp"
014. #define def_ButtonWait             def_PathBMP + "Wait.bmp"
015. #resource "\\" + def_ButtonPlay
016. #resource "\\" + def_ButtonPause
017. #resource "\\" + def_ButtonLeft
018. #resource "\\" + def_ButtonLeftBlock
019. #resource "\\" + def_ButtonRight
020. #resource "\\" + def_ButtonRightBlock
021. #resource "\\" + def_ButtonPin
022. #resource "\\" + def_ButtonWait
023. //+------------------------------------------------------------------+
024. #define def_PrefixObjectName       "Market Replay _ "
025. #define def_NameObjectsSlider      def_PrefixObjectName + "Slider"
026. #define def_PosXObjects            120
027. //+------------------------------------------------------------------+
028. #include "..\Auxiliar\C_Terminal.mqh"
029. #include "..\Auxiliar\C_Mouse.mqh"
030. //+------------------------------------------------------------------+
031. #define def_AcessTerminal (*Terminal)
032. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
033. //+------------------------------------------------------------------+
034. class C_Controls : protected C_Mouse
035. {
036.    protected:
037.            enum EventCustom {ev_WaitOn, ev_WaitOff};
038.    private :
039. //+------------------------------------------------------------------+
040.            string  m_szBtnPlay;
041.            bool            m_bWait;
042.            struct st_00
043.            {
044.                    string  szBtnLeft,
045.                            szBtnRight,
046.                            szBtnPin,
047.                            szBarSlider,
048.                            szBarSliderBlock;
049.                    int     posPinSlider,
050.                            posY,
051.                            Minimal;
052.            }m_Slider;
053.            C_Terminal *Terminal;
054. //+------------------------------------------------------------------+
055. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL)
056.                    {
057.                            ObjectCreate(def_InfoTerminal.ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0);
058.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x);
059.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YDISTANCE, y);
060.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1);
061.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2));
062.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_ZORDER, 1);
063.                    }
064. //+------------------------------------------------------------------+
065. inline void CreteBarSlider(int x, int size)
066.                    {
067.                            ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0);
068.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
069.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4);
070.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
071.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
072.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
073.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
074.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
075.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
076. //---
077.                            ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0);
078.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
079.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9);
080.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
081.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
082.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
083.                    }
084. //+------------------------------------------------------------------+
085.            void CreateBtnPlayPause(bool state)
086.                    {
087.                            m_szBtnPlay = def_PrefixObjectName + "Play";
088.                            CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay));
089.                            ObjectSetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE, state);
090.                    }
091. //+------------------------------------------------------------------+
092.            void CreteCtrlSlider(void)
093.                    {
094.                            u_Interprocess Info;
095.                            
096.                            m_Slider.szBarSlider      = def_NameObjectsSlider + " Bar";
097.                            m_Slider.szBarSliderBlock = def_NameObjectsSlider + " Bar Block";
098.                            m_Slider.szBtnLeft        = def_NameObjectsSlider + " BtnL";
099.                            m_Slider.szBtnRight       = def_NameObjectsSlider + " BtnR";
100.                            m_Slider.szBtnPin         = def_NameObjectsSlider + " BtnP";
101.                            m_Slider.posY = 40;
102.                            CreteBarSlider(77, 436);
103.                            CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock);
104.                            CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock);
105.                            CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin);
106.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER);
107.                            if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
108.                            m_Slider.Minimal = Info.s_Infos.iPosShift;
109.                            PositionPinSlider(Info.s_Infos.iPosShift);
110.                    }
111. //+------------------------------------------------------------------+
112. inline void RemoveCtrlSlider(void)
113.                    {                       
114.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false);
115.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_NameObjectsSlider);
116.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, true);
117.                    }
118. //+------------------------------------------------------------------+
119. inline void PositionPinSlider(int p, const int minimal = 0)
120.                    {
121.                            m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
122.                            m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
123.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95);
124.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal);
125.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal);
126.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider);
127.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2);
128.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
129.                            ChartRedraw();
130.                    }
131. //+------------------------------------------------------------------+
132.    public  :
133. //+------------------------------------------------------------------+
134.            C_Controls(C_Terminal *arg)
135.                    :C_Mouse(arg),
136.                     m_bWait(false)
137.                    {
138.                            if (CheckPointer(Terminal = arg) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
139.                            m_szBtnPlay          = NULL;
140.                            m_Slider.szBarSlider = NULL;
141.                            m_Slider.szBtnPin    = NULL;
142.                            m_Slider.szBtnLeft   = NULL;
143.                            m_Slider.szBtnRight  = NULL;
144.                    }
145. //+------------------------------------------------------------------+
146.            ~C_Controls()
147.                    {
148.                            if (CheckPointer(Terminal) == POINTER_INVALID) return;
149.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false);
150.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_PrefixObjectName);
151.                    }
152. //+------------------------------------------------------------------+
153.            void Init(const bool state)
154.                    {
155.                            CreateBtnPlayPause(state);
156.                            GlobalVariableTemp(def_GlobalVariableReplay);
157.                            if (!state) CreteCtrlSlider();
158.                            ChartRedraw();
159.                    }
160. //+------------------------------------------------------------------+
161.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
162.                    {
163.                            u_Interprocess Info;
164.                            static int six = -1, sps;
165.                            int x, y, px1, px2;
166.                            
167.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
168.                            switch (id)
169.                            {
170.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn):
171.                                            if (lparam == 0) break;
172.                                            m_bWait = true;
173.                                            CreateBtnPlayPause(true);
174.                                            break;
175.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff):
176.                                            if (lparam == 0) break;
177.                                            m_bWait = false;
178.                                            Info.df_Value = dparam;
179.                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
180.                                            break;
181.                                    case CHARTEVENT_OBJECT_DELETE:
182.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName)
183.                                            {
184.                                                    if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider)
185.                                                    {
186.                                                            RemoveCtrlSlider();
187.                                                            CreteCtrlSlider();
188.                                                    }else
189.                                                    {
190.                                                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
191.                                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
192.                                                    }
193.                                                    ChartRedraw();
194.                                            }
195.                                            break;
196.                                    case CHARTEVENT_OBJECT_CLICK:
197.                                            if (m_bWait) break;
198.                                            if (sparam == m_szBtnPlay)
199.                                            {
200.                                                    Info.s_Infos.isPlay = (bool) ObjectGetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE);
201.                                                    if (!Info.s_Infos.isPlay) CreteCtrlSlider(); else
202.                                                    {
203.                                                            RemoveCtrlSlider();
204.                                                            m_Slider.szBtnPin = NULL;
205.                                                    }
206.                                                    Info.s_Infos.iPosShift = (ushort) m_Slider.posPinSlider;
207.                                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
208.                                                    ChartRedraw();
209.                                            }else   if (sparam == m_Slider.szBtnLeft) PositionPinSlider(m_Slider.posPinSlider - 1);
210.                                            else if (sparam == m_Slider.szBtnRight) PositionPinSlider(m_Slider.posPinSlider + 1);
211.                                            break;
212.                                    case CHARTEVENT_MOUSE_MOVE:
213.                                            if (GetInfoMouse().ExecStudy) return;
214.                                            if ((CheckClick(C_Mouse::eClickLeft)) && (m_Slider.szBtnPin != NULL))
215.                                            {
216.                                                    x = GetInfoMouse().Position.X;
217.                                                    y = GetInfoMouse().Position.Y;
218.                                                    px1 = m_Slider.posPinSlider + def_PosXObjects + 86;
219.                                                    px2 = m_Slider.posPinSlider + def_PosXObjects + 114;
220.                                                    if ((y >= (m_Slider.posY - 14)) && (y <= (m_Slider.posY + 14)) && (x >= px1) && (x <= px2) && (six == -1))
221.                                                    {
222.                                                            six = x;
223.                                                            sps = m_Slider.posPinSlider;
224.                                                            ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, false);
225.                                                    }
226.                                                    if (six > 0) PositionPinSlider(sps + x - six);
227.                                            }else if (six > 0)
228.                                            {
229.                                                    six = -1;
230.                                                    ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, true);
231.                                            }
232.                                            break;
233.                            }
234.                    }
235. //+------------------------------------------------------------------+
236. };
237. //+------------------------------------------------------------------+
238. #undef def_InfoTerminal
239. #undef def_AcessTerminal
240. #undef def_PosXObjects
241. #undef def_ButtonPlay
242. #undef def_ButtonPause
243. #undef def_ButtonLeft
244. #undef def_ButtonRight
245. #undef def_ButtonPin
246. #undef def_NameObjectsSlider
247. #undef def_PrefixObjectName
248. #undef def_PathBMP
249. //+------------------------------------------------------------------+

Código fonte do arquivo C_Controls.mqh

Então feita estas correções, mudanças e ajustes, podemos tentar compilar novamente o serviço de replay / simulador. E como resultado você tem a seguinte imagem:

Figura 10

Imagem 10 : Compilação final.

Isto indica que o serviço foi compilado corretamente, e observem que o indicador também foi compilado. Isto indica que você já pode simplesmente remover o executável do indicador, usando o explorador de arquivos, já que ele agora será parte integrante do executável do serviço de replay / simulação. Porém, toda via e, entretanto, se você remover o executável do indicador e tentar lançar o sistema de replay / simulador no MetaTrader 5, irá perceber que o gráfico será aberto, mas o indicador de controle não estará ali. Por que ?!?!

O motivo, é que quem de fato ainda está lançando o indicador de controle, é o template e não o serviço. E no template, não existe nada que diz que o indicador de controle faz parte do executável de serviço. Você pode notar isto observando o conteúdo do arquivo de template. Mas como a ideia, não é usar o template, e sim o serviço, a fim de lançar o indicador de controle no gráfico. Não irei entrar em detalhes de como resolver esta questão. Mas vamos focar no que queremos de fato fazer.

Como o que precisamos fazer não é algo tão simples, e não quero me estender muito neste artigo. Irei deixar para fazer as mudanças no próximo artigo. Isto por conta que será necessário modificar muito algumas coisas a fim de que o Indicador de controle continue funcional, e ao mesmo tempo, possamos deixar o serviço de replay / simulador livre, leve e solto para que você como usuário possa usar indicadores, estratégias, ou modelos pessoais. A ideia em fazer tais mudanças é justamente para promover este tipo de coisa. Permitir você usar o sistema com o seus próprios conceitos e ideias.

Mas o principal motivo de deixar para mostrar no próximo artigo o que precisamos fazer a fim de que o indicador de controle apareça no gráfico, quando ele é um recurso do serviço, e sem que precisemos usar um template para isto, é o fato de que será preciso remover uma coisa que está duplicada no modo de funcionar do sistema. O que torna o indicador de controle extremamente instável, se ele for utilizado diretamente via serviço.


Conclusão

Apesar de termos modificado aqui neste artigo, o indicador de controle e o serviço, a ponto de começarmos a poder remover o executável do indicador da lista de indicadores acessíveis ao usuário. O sistema em si, se mostrou instável por conta de existe uma pendencia mal resolvida entre as formas de interação do indicador de controle e o usuário. Tal instabilidade se deve ao fato de que existe a necessidade de outras coisas estarem presentes no gráfico. Coisas estas que fazem parte do arquivo de template, mas não quero fazer uso deste arquivo quando o serviço de replay / simulação estiver em uso. Quero e vou mostrar como devemos proceder a fim de tornar o serviço auto sustentável, a fim de que o usuário possa utilizar seus próprios templates, ou configurações.

Ao mesmo tempo estaremos criando uma forma adequada de promover o serviço de replay / simulação, a fim de que você possa praticar a sua estratégia, ou modelo de análise.

Ainda temos muitas coisas a serem feitas, antes que este sistema de fato possa receber o tão sonhado conjunto para trabalhar com as ordens, e posições de replay e simulação. Espero que você, caro leitor, consiga compreender o nível e grau de complexidade que estamos desenvolvendo. E tudo isto fazendo uso unicamente do MQL5, sem programação externa, ou coisas do tipo. De fato, este sistema tem se mostrado bastante desafiador. Mas gosto de desafios e este está sendo bastante motivador.

Arquivos anexados |
Anexo.zip (420.65 KB)
Últimos Comentários | Ir para discussão (1)
Gladiator WXT
Gladiator WXT | 11 mai 2024 em 12:10

Thank you a lot for such interesting project! However reading all 49 articles to get the clue for every component is really hard work for any traders.

I kindly ask you to write next one #50 article under the name "User Guide" and lay out in it on each component of this project - Replay System, EA and indicators.

It will be great to add some practical examples of using it, like following:

- "Replaying and re-trading Brexit case on GBPUSD in June 2016 for educational purpose";

- "Replaying and re-trading Gold-rush case on XAUUSD in March 2020 for educational purpose";

It is expected that in these practical example should be used to retrieve historical data from real symbols  'GBPUSD'/'XAUUSD' (from any connected forex account), and to feed custom symbols like 'repGBPUSD'/'repXAUUSd' using extracted data in replay mode, to add some generic indicators (RSI(14), MA(50) etc) and to provide users with real-time experience of re-trading these historical events.

Such User Guide with practical examples of real time re-trading Brexit and Gold-rush will be really great finalization of this project!

Escrevemos o primeiro modelo de caixa de vidro (Glass Box) em Python e MQL5 Escrevemos o primeiro modelo de caixa de vidro (Glass Box) em Python e MQL5
Os modelos de aprendizado de máquina são difíceis de interpretar, e entender o motivo pelo qual os modelos não atendem às nossas expectativas pode ajudar muito a alcançar o resultado desejado ao usar esses métodos modernos. Sem um entendimento abrangente do funcionamento interno do modelo, pode ser difícil identificar erros que prejudicam o desempenho. Nesse processo, podemos dedicar tempo a criar funções que não impactam na qualidade da previsão. No final, por melhor que seja o modelo, perdemos todos os seus principais benefícios devido a nossos próprios erros. Felizmente, existe uma solução complexa, mas bem desenvolvida, que permite ver claramente o que está acontecendo sob o capô do modelo.
Algoritmos de otimização populacional: Método Nelder-Mead (NM) Algoritmos de otimização populacional: Método Nelder-Mead (NM)
O artigo apresenta um estudo completo do método Nelder-Mead explicando como o simplex — o espaço dos parâmetros da função — muda e se reestrutura a cada iteração para alcançar a solução ótima, e também descreve como melhorar este método.
Padrões de projeto no MQL5 (Parte 3): Padrões comportamentais 1 Padrões de projeto no MQL5 (Parte 3): Padrões comportamentais 1
Neste novo artigo da série dedicada a padrões de projeto, exploraremos os padrões comportamentais para entender como criar métodos eficazes de interação entre os objetos criados. Ao projetar esses padrões de comportamento, poderemos entender como desenvolver software reutilizável, expansível e testável.
Redes neurais de maneira fácil (Parte 66): Problemáticas da pesquisa em treinamento off-line Redes neurais de maneira fácil (Parte 66): Problemáticas da pesquisa em treinamento off-line
O treinamento de modelos em modo off-line é realizado com dados de uma amostra de treinamento previamente preparada. Isso nos oferece várias vantagens, mas também comprime significativamente as informações sobre o ambiente em relação às dimensões da amostra de treinamento. Isso, por sua vez, limita as possibilidades de pesquisa. Neste artigo, quero apresentar um método que permite enriquecer a amostra de treinamento com dados o mais diversificados possível.