Desenvolvendo um sistema de Replay (Parte 49): Complicando as coisas (I)
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
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.
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
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!