preview
Desenvolvendo um sistema de Replay (Parte 58): Voltando a trabalhar no serviço

Desenvolvendo um sistema de Replay (Parte 58): Voltando a trabalhar no serviço

MetaTrader 5Exemplos | 25 julho 2024, 12:33
13 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay (Parte 57): Dissecando o serviço de testagem, expliquei em detalhes, o código fonte que foi utilizado para demonstrar, uma possível forma de comunicação entre os módulos, que faremos uso no nosso sistema de replay / simulador.

Mas apesar de que aquele código, nos dê uma ideia do que realmente precisaremos implementar. Ele ainda não conta com um detalhe importante para ser de fato útil para nosso sistema. Tal detalhe é a possibilidade de fazer uso de templates. Talvez você que não opere de fato, ou não compreenda bem, o poder que os templates trazem para nós, tanto na questão da codificação, quanto na questão de configuração do MetaTrader 5, imagine que isto não é de fato importante.

Mas saber, entender, e compreender os templates, torna todo o nosso trabalho muito menor. Tanto que existem coisas que são super simples de serem feitas ao usar templates, mas quando vamos programar as mesmas a coisa se torna extremamente complexa e difícil de ser de fato implementada. Talvez em um futuro, eu venha a mostrar como de fato fazer algumas coisas usando apenas templates para consegui-las. Já que existem possibilidades muito grandes e pouco exploradas no que tange os templates. Mas por hora temos e faremos outras coisas, mais urgentes.

Eu sinceramente acreditava, ter conseguido fazer com que não precisássemos modificar, ou mudar algo nos módulos de controle e de mouse. No entanto, por conta de alguns detalhes que veremos no decorrer dos próximos artigos, será preciso fazer algumas pequenas mudanças em ambos os módulos. Mas isto será visto depois, neste artigo aqui, vamos entender como tornar aquele conhecimento adquirido no artigo anterior em algo plausível e funcional. Mas para separar as coisas, vamos para um novo tópico.


Modificando o antigo serviço de replay / simulação

Apesar de já ter um bom tempo, que não fazemos nada, relacionado em termos de modificação ou melhorias no código do replay / simulador. Alguns cabeçalhos envolvidos, na construção do executável do replay / simulador, passou por modificações e mudanças. Talvez a mais evidente, seja a remoção do arquivo de cabeçalho InterProcess.mqh. Este foi substituído por um arquivo, cujo propósito é bem mais amplo, o arquivo de cabeçalho Defines.mqh.

O fato de já termos feito alguma mudança nos módulos de controle e mouse, a fim de fazer uso deste novo arquivo de cabeçalho, nos força a fazer a mesma coisa aqui no serviço de replay / simulador. Então por conta disto, ao tentarmos compilar o serviço de replay / simulador, já usando a nova composição de arquivos de cabeçalho, teremos como resultado a indicação de falhas na compilação. Tais falhas podem ser vistas na figura 01.

Fig 01

Figura 01 - Tentativa de compilar o serviço de Replay / Simulador

De todos os erros que poderão aparecer, você primeiro deve resolver estes dois, que estão sendo indicados em destaque. Para fazer isto, será preciso abrir o arquivo de cabeçalho C_Simulation.mqh, e modificar o código, conforme é mostrado no fragmento logo abaixo. Note que tudo que foi preciso de fato fazer, foi remover a linha 04, e no lugar dela adicionar o que pode ser visto na linha 05. Desta forma o arquivo C_Simulation.mqh, ficará adequado ao novo modelo que estamos usando.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "..\..\Auxiliar\Interprocess.mqh"
05. #include "..\..\Defines.mqh"
06. //+------------------------------------------------------------------+
07. #define def_MaxSizeArray    16777216 // 16 Mbytes de posições
08. //+------------------------------------------------------------------+
09. class C_Simulation
10. {
11.    private   :
12. //+------------------------------------------------------------------+
13.       int       m_NDigits;
14.       bool       m_IsPriceBID;

Fragmento do código fonte do arquivo C_Simulation.mqh

Assim como foi feito no arquivo de cabeçalho C_Simulation.mqh. Teremos que fazer algo parecido no arquivo C_FilesBars.mqh. Então abra o arquivo de cabeçalho C_FilesBars.mqh, e modifique o código como mostrado no fragmento a seguir.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "..\..\Auxiliar\Interprocess.mqh"
05. #include "..\..\Defines.mqh"
06. //+------------------------------------------------------------------+
07. #define def_BarsDiary   1440
08. //+------------------------------------------------------------------+
09. class C_FileBars
10. {
11.    private   :
12.       int      m_file;

Fragmento do código fonte do arquivo C_FilesBars.mqh

Observe que em ambos fragmentos, removemos o arquivo de cabeçalho InterProcess.mqh, e no lugar dele passamos a fazer uso do arquivo de cabeçalho Defines.mqh. Feitas estas duas modificações, grande parte do código já estará dentro do que seria esperado pelo serviço de replay / simulador. Mas existe um problema aqui. Se você comparar o conteúdo dos arquivos de cabeçalhos InterProcess.mqh e Defines.mqh, notará que Defines.mqh não faz nenhuma menção a variáveis globais de terminal. Mas, no entanto, o sistema de replay / simulação ainda faz menção a tais variáveis.

Para ser mais preciso, tais variáveis são usadas justamente no código presente no arquivo C_Replay.mqh. Mas não é somente este o nosso principal problema aqui. Temos um outro problema. Talvez futuramente, eu venha a decidir, organizar o código de uma outra maneira, a fim de separar ainda mais as coisas, e desta forma ter uma melhor estabilidade e flexibilidade em todo o sistema. Mas por hora, irei apenas adaptar o que já está sendo feito. Não quero ter que precisar fazer mudanças radicais em todo o sistema, apenas para ter uma pequena melhoria em termos de flexibilidade e estabilidade. Apesar de sempre ser bom incrementar ambas.

Então para que as coisas não fiquem muito confusas. Vamos separar a explicação e tópicos. E a primeira coisa que faremos, será corrigir uma falha, que nem é assim tão grave, mas que perturba uma das premissas da programação orientada em objetos. O encapsulamento.


Revendo o encapsulamento do código

Um dos problemas mais sérios que podemos criar para qualquer código, é não respeitar algumas coisas que torna a programação orientada em objetos algo tão seguro. Durante um bom tempo, tenho negligenciado e feito um mal-uso, em uma parte bem específica do código. Isto para ter um fácil acesso a determinados dados, a fim de conseguir promover o replay / simulação.

Só que deste momento em diante, tal prática não será mais de fato utilizada. Estou falando da quebra do encapsulamento, que está presente no código da classe C_ConfigService.

Se você observar o código, presente no arquivo de cabeçalho desta classe. O arquivo de cabeçalho é o C_ConfigService.mqh. Irá notar que existe uma cláusula protected sendo declarada, e dentro desta cláusula, existem variáveis. E é justamente a existência destas variáveis aqui que fazem o encapsulamento ser quebrado. Mesmo que tais variáveis sejam de fato usadas apenas na classe C_ConfigService e na classe que a herda, que no caso é a C_Replay. Não é adequado permitir que estas mesmas variáveis sejam de fato visíveis fora da classe C_ConfigService. Isto da forma como elas estão sendo visíveis. Pois se você observar o código da classe C_Replay, notará que tais variáveis são modificadas por aquela classe, e é justamente isto que não é adequado. Em C++, existem formas de tornar variáveis privativas das classes, algo que pode ser visto e manipulado fora da classe base. No entanto, tal prática muitas das vezes nos leva a tornar o código algo extremamente complexo e de difícil manutenção. Isto sem falar a imensa dificuldade em fazer melhorias no mesmo.

Já que o MQL5, deriva do C++, tentando evitar trazer para dentro de si, praticas potencialmente perigosas, que podem ser feitas em C++, acho mais que adequado, mostrar e fazer uso correto das três premissas básicas da programação orientada em objetos.

Ao fazer tal modificação, no código presente no arquivo de cabeçalho C_ConfigService.mqh, conseguiremos que o encapsulamento volte a ser de fato usado em nosso sistema. Mas isto irá nos forçar a fazer algumas outras mudanças nas camadas mais altas. Ou seja, o código presente na classe C_Replay, que se encontra no arquivo de cabeçalho C_Replay.mqh, irá sofre drásticas mudanças. Mas também vamos melhorar um pouco mais o nosso código, a fim de tornar o código fonte do serviço de replay / simulador, um pouco menos aninhado. Desta forma, tentaremos fazer com que as coisas aconteçam em passos menores, tornando assim a manutenção mais simples. Já que ao fazer passos mais curtos, podemos controlar melhor o que de fato está acontecendo. E isto será muito benéfico para os próximos artigos, já que precisaremos implementar algo, ainda mais complicado, e que envolve uma série de coisas a serem desenvolvidas.

Então vamos ver o que começaremos a fazer, a fim de tornar as coisas mais adequadas. Para isto será preciso abrir o arquivo de cabeçalho C_ConfigService.mqh, e modificar o código conforme pode ser visto no fragmento abaixo. Estou mostrando o fragmento, já que o restante do código irá se manter igual era antes. Mas as mudanças mostradas no fragmento, já nos dão a garantia de que o encapsulamento será alcançado.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Support\C_FileBars.mqh"
05. #include "Support\C_FileTicks.mqh"
06. #include "Support\C_Array.mqh"
07. //+------------------------------------------------------------------+
08. class C_ConfigService : protected C_FileTicks
09. {
10.    protected:
11.         datetime m_dtPrevLoading;
12.         int      m_ReplayCount,
13.                  m_ModelLoading;
14. //+------------------------------------------------------------------+
15. inline void FirstBarNULL(void)
16.          {
17.             MqlRates rate[1];
18.             int c0 = 0;
19.             
20.             for(; (m_Ticks.ModePlot == PRICE_EXCHANGE) && (m_Ticks.Info[c0].volume_real == 0); c0++);
21.             rate[0].close = (m_Ticks.ModePlot == PRICE_EXCHANGE ? m_Ticks.Info[c0].last : m_Ticks.Info[c0].bid);
22.             rate[0].open = rate[0].high = rate[0].low = rate[0].close;
23.             rate[0].tick_volume = 0;
24.             rate[0].real_volume = 0;
25.             rate[0].time = macroRemoveSec(m_Ticks.Info[c0].time) - 86400;
26.             CustomRatesUpdate(def_SymbolReplay, rate);
27.             m_ReplayCount = 0;
28.          }
29. //+------------------------------------------------------------------+
30.    private   :
31.       enum eWhatExec {eTickReplay, eBarToTick, eTickToBar, eBarPrev};
32.       enum eTranscriptionDefine {Transcription_INFO, Transcription_DEFINE};
33.       struct st001
34.       {
35.          C_Array *pTicksToReplay, *pBarsToTicks, *pTicksToBars, *pBarsToPrev;
36.          int      Line;
37.       }m_GlPrivate;
38.       string    m_szPath;
39.       bool      m_AccountHedging;
40.       datetime  m_dtPrevLoading;
41.       int       m_ReplayCount,
42.                 m_ModelLoading;
43. //+------------------------------------------------------------------+
44. inline void FirstBarNULL(void)
45.          {
46.             MqlRates rate[1];
47.             int c0 = 0;
48.             
49.             for(; (m_Ticks.ModePlot == PRICE_EXCHANGE) && (m_Ticks.Info[c0].volume_real == 0); c0++);
50.             rate[0].close = (m_Ticks.ModePlot == PRICE_EXCHANGE ? m_Ticks.Info[c0].last : m_Ticks.Info[c0].bid);
51.             rate[0].open = rate[0].high = rate[0].low = rate[0].close;
52.             rate[0].tick_volume = 0;
53.             rate[0].real_volume = 0;
54.             rate[0].time = macroRemoveSec(m_Ticks.Info[c0].time) - 86400;
55.             CustomRatesUpdate(def_SymbolReplay, rate);
56.             m_ReplayCount = 0;
57.          }
58. //+------------------------------------------------------------------+
59. inline eTranscriptionDefine GetDefinition(const string &In, string &Out)

Fragmento do código fonte do arquivo C_ConfigService.mqh

Note que o conteúdo presente entre as linhas 11 e 13, foram transferidos para as linhas 40 a 42. Ou seja, agora não será mais possível acessar tais variáveis fora do corpo da classe C_ConfigService. Mas além disto também foi feita uma outra mudança. Está na verdade poderia ser sido ignorada, mas como algumas coisas não serão usadas fora da classe, decidi tornar o procedimento FirstBarNULL algo privativo. Sendo assim o conteúdo presente entre as linhas 15 e 28, foi transferido, ficando agora presente entre as linhas 44 e 57.

É claro que ao fazer tais modificações, isto no arquivo real, o número das linhas ficará diferente. Já que o código removido, deixará de fazer parte, realmente do código da classe. Mas decidi deixar assim, no fragmento, para que você conseguisse de fato notar o que foi feito, isto em termos de mudanças. Assim acredito ficar mais claro e simples de entender o que foi modificado.

Muito bem. Ao fazer tais mudanças, iremos agora precisar fazer uma radical modificação no código presente no arquivo C_Replay.mqh. Mas para separar adequadamente as coisas, vamos ver isto no próximo tópico.


Reiniciando a codificação da classe C_Replay

Apesar do nome desde tópico, ser algo um pouco desmotivador. Visto que isto denota que tentaremos reinventar a roda, que já havia sido construída. Não quero que você se sinta desmotivado ao ver tal título. Muito pelo contrário. Quero que você compreenda que de fato temos que refazer grande parte do código referente a classe C_Replay. Mas tudo que aprendemos e vimos ao longo desta série de artigos, não será de fato perdido. O que faremos é nos adequar a um novo cenário, ou uma nova forma de fazer as coisas, já que existem coisas das quais não serão possíveis de serem feitas, com eram feitas anteriormente.

Todo o código, presente e pertencente a classe C_Replay, para este artigo, pode ser visto logo abaixo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_ConfigService.mqh"
005. //+------------------------------------------------------------------+
006. #define def_IndicatorControl   "Indicators\\Market Replay.ex5"
007. #resource "\\" + def_IndicatorControl
008. //+------------------------------------------------------------------+
009. #define def_CheckLoopService ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""))
010. //+------------------------------------------------------------------+
011. #define def_ShortNameIndControl "Market Replay Control"
012. //+------------------------------------------------------------------+
013. class C_Replay : public C_ConfigService
014. {
015.    private   :
016.       long      m_IdReplay;
017.       struct st00
018.       {
019.          ushort Position;
020.          short  Mode;
021.       }m_IndControl;
022. //+------------------------------------------------------------------+
023. inline bool MsgError(string sz0) { Print(sz0); return false; }
024. //+------------------------------------------------------------------+
025. inline void UpdateIndicatorControl(void)
026.          {
027.             uCast_Double info;
028.             int handle;
029.             double Buff[];
030.             
031.             if ((handle = ChartIndicatorGet(m_IdReplay, 0, def_ShortNameIndControl)) == INVALID_HANDLE) return;
032.             info.dValue = 0;
033.             if (CopyBuffer(handle, 0, 0, 1, Buff) == 1)
034.                info.dValue = Buff[0];
035.             IndicatorRelease(handle);
036.             if ((short)(info._16b[0]) != SHORT_MIN)
037.                m_IndControl.Mode = (short)info._16b[1];
038.             if (info._16b[0] != m_IndControl.Position)
039.             {
040.                if (((short)(info._16b[0]) != SHORT_MIN) && ((short)(info._16b[1]) == SHORT_MAX))
041.                   m_IndControl.Position = info._16b[0];
042.                info._16b[0] = m_IndControl.Position;
043.                info._16b[1] = (ushort)m_IndControl.Mode;
044.                EventChartCustom(m_IdReplay, evCtrlReplayInit, 0, info.dValue, "");
045.             }
046.          }
047. //+------------------------------------------------------------------+
048.       void SweepAndCloseChart(void)
049.          {
050.             long id;
051.             
052.             if ((id = ChartFirst()) > 0) do
053.             {
054.                if (ChartSymbol(id) == def_SymbolReplay)
055.                   ChartClose(id);
056.             }while ((id = ChartNext(id)) > 0);
057.          }
058. //+------------------------------------------------------------------+
059.    public   :
060. //+------------------------------------------------------------------+
061.       C_Replay()
062.          :C_ConfigService()
063.          {
064.             Print("************** Market Replay Service **************");
065.             srand(GetTickCount());
066.             SymbolSelect(def_SymbolReplay, false);
067.             CustomSymbolDelete(def_SymbolReplay);
068.             CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay));
069.             CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0);
070.             CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0);
071.             CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0);
072.             CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation");
073.             CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, 8);
074.             SymbolSelect(def_SymbolReplay, true);
075.          }
076. //+------------------------------------------------------------------+
077.       bool OpenChartReplay(const ENUM_TIMEFRAMES arg1, const string szNameTemplate)
078.          {
079.             if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0)
080.                return MsgError("Asset configuration is not complete, it remains to declare the size of the ticket.");
081.             if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0)
082.                return MsgError("Asset configuration is not complete, need to declare the ticket value.");
083.             if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0)
084.                return MsgError("Asset configuration not complete, need to declare the minimum volume.");
085.             SweepAndCloseChart();
086.             m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
087.             if (!ChartApplyTemplate(m_IdReplay, szNameTemplate + ".tpl"))
088.                Print("Failed apply template: ", szNameTemplate, ".tpl Using template default.tpl");
089.             else
090.                Print("Apply template: ", szNameTemplate, ".tpl");
091. 
092.             return true;
093.          }
094. //+------------------------------------------------------------------+
095.       bool InitBaseControl(const ushort wait = 1000)
096.          {
097.             int handle;
098.             
099.             Print("Waiting for Mouse Indicator...");
100.             Sleep(wait);
101.             while ((def_CheckLoopService) && (ChartIndicatorGet(m_IdReplay, 0, "Indicator Mouse Study") == INVALID_HANDLE)) Sleep(200);
102.             if (def_CheckLoopService)
103.             {
104.                Print("Waiting for Control Indicator...");
105.                if ((handle = iCustom(ChartSymbol(m_IdReplay), ChartPeriod(m_IdReplay), "::" + def_IndicatorControl, m_IdReplay)) == INVALID_HANDLE) return false;
106.                ChartIndicatorAdd(m_IdReplay, 0, handle);
107.                IndicatorRelease(handle);
108.                m_IndControl.Position = 0;
109.                m_IndControl.Mode = SHORT_MIN;
110.                UpdateIndicatorControl();
111.             }
112.             
113.             return def_CheckLoopService;
114.          }
115. //+------------------------------------------------------------------+
116.       bool LoopEventOnTime(void)
117.          {         
118.             
119.             while (def_CheckLoopService)
120.             {
121.                UpdateIndicatorControl();
122.                Sleep(250);
123.             }
124.             
125.             return false;
126.          }
127. //+------------------------------------------------------------------+
128.       ~C_Replay()
129.          {
130.             SweepAndCloseChart();
131.             SymbolSelect(def_SymbolReplay, false);
132.             CustomSymbolDelete(def_SymbolReplay);
133.             Print("Finished replay service...");
134.          }
135. //+------------------------------------------------------------------+

Código fonte do arquivo C_Replay.mqh

Apesar deste código não fazer de fato o replay / simulação, como era feito antes. Visto que ele não conta com determinadas coisas, ainda. O mesmo tem como finalidade, fazer com o serviço de replay / simulação, faça uso de algumas coisas que não foram vistas no artigo anterior. Entre estas coisas está o fato de ser possível carregar as barras previas, assim como era feito, e também as barras a serem usadas tanto como forma de fazer o replay, quanto quando uma forma de se fazer uma simulação. Apesar de que não poderemos ainda, e neste artigo, fazer com que as barras de replay ou simulação sejam de fato usadas. As mesmas serão carregadas e estarão disponíveis, para quando o sistema de fato conseguir fazer o correto lançamento delas no gráfico do ativo customizado.

Existem muitas questões aqui, neste código que pode ser visto acima, que merecem algum tipo de explicação. Isto por conta, que muitas das coisas presentes nele podem não ser tão claras. Mesmo para quem já tem uma boa experiência em MQL5. Mas a explicação será mais focada para quem esteja querendo de fato entender por que este código está sendo feito desta maneira.

No início do código, e isto entre as linhas 5 e 11, temos além de algumas definições, a declaração e inclusão do arquivo de compilado do indicador de controle, para dentro do executável do serviço. O motivo disto já foi falado, e explicado intensamente, em outros artigos desta mesma série sobre o replay / simulador. Sendo assim, apenas estou chamando a sua atenção a tais fatos, isto para que você compreenda que não é de forma alguma, necessário transportar o arquivo do indicador de controle.

Logo depois, note que na linha 13, fazemos a herança pública da classe C_ConfigService. O motivo disto, é que não concentraremos todo o trabalho aqui, na classe C_Replay. Partilharemos parte da carga com a classe C_ConfigService. Isto reforça ainda mais o que foi visto no tópico anterior, onde expliquei as mudanças necessárias a fim de garantir o encapsulamento adequado das informações e variáveis.

Assim entramos na parte privativa da classe C_Replay. Isto é feito a partir da linha 15 e perdurará até a linha 58, quando finalmente entraremos na parte pública. Mas primeiramente vamos ver como a parte privativa funciona. Já que a mesma conta com um pequeno conjunto de variáveis globais, que estão sendo declaradas nas linhas 16 e 21. Mas atenção a linha 21 declara uma variável, que na verdade é uma estrutura. Assim sendo temos mais coisas dentro desta variável declarada na linha 21.

Já na linha 23, temos uma pequena função, cujo único propósito é imprimir uma mensagem no terminal e retornar um valor falso. Mas por que estamos retornando um valor falso aqui ?!?! O motivo disto, é quase não fizéssemos um retorno falso, iriamos precisar de uma segunda linha quando formos imprimir um erro no terminal. Para ficar mais claro, esta ideia, observe a linha 79, onde verificamos uma dada condição. Se tão condição indicar que se trata de um erro, teríamos que ter na linha 80, um código um pouco diferente. Onde primeiro iriamos imprimir a mensagem de erro, e logo depois usar a chamada return a fim de devolver ao chamador uma indicação de falha. Este tipo de coisa iria apenas aumentar o trabalho. No entanto, ao usamos a função declarada na linha 23, e retornarmos um valor falso nela, poderemos imprimir a mensagem desejada e ao mesmo tempo retornar uma indicação de falha. Assim tudo que será de fato necessário, é que usemos algo igual ao que pode ser visto na linha 80. Combinamos as coisas de forma a reduzir o trabalho de programação.

No entanto, o código que de fato lhe deve ser mais curioso, é o que podemos visualizar entre as linhas 25 e 46. Este código fará um trabalho, que é de grande importância para nós. Ele manterá e ajustar os dados do indicador de controle. Então é importante, e necessário que você entenda bem como todas as partes funcionam, antes de tentar entender o que está sendo feito aqui. Na dúvida, veja os artigos anteriores, a fim de compreender como o indicador de controle estará se comunicando com o mundo exterior.

Na linha 31, verificamos e tentamos capturar um manipulador, a fim de poder acessar o indicador de controle. Note que se esta captura falhar, não será de fato um desastre completo. Tudo que fazemos é retornar ao chamador, abandonando o que iriamos fazer no restante do procedimento. No entanto, se o manipulador for de fato capturado. Iremos primeiramente zerar o valor de testagem, isto é feito na linha 32. É extremamente importante que tal zeramento seja de fato executada, a forma de se fazer isto pode variar um pouco, mas ela precisa ser feita. Para que na linha 33, possamos verificar se o buffer do indicador pode ser lido, caso seja lido, iremos na linha 34 colocar o valor na variável de testagem e ajuste. Possivelmente, você irá ver um código ligeiramente diferente sendo executado aqui, nos próximos artigos. Mas não precisa de fato estranhar ou se assustar. Apenas poderá ser implementando algumas pequenas melhorias no mesmo. Mas elas não aparecerão na explicação do artigo. Assim como está sendo feito agora.

Já que o manipulador não nos é mais necessário, usamos a linha 35, de forma a nos desfazer dele. E entramos na fase de testagem e ajuste das informações a serem passadas e utilizadas. Então na linha 36, verificamos se estamos ou não com algum dado válido no indicador de controle. Se o indicador já contiver dados válidos, armazenaremos se estamos pausados ou em modo de execução, ou melhor dizendo: Se estamos ou não em play na simulação / replay. Isto é armazenado usando a linha 37. Agora atenção, precisamos fazer isto, antes de qualquer outra coisa. Se você for fazer isto depois, poderá e inevitavelmente acessará dados já modificados, o que comprometerá a memória da informação. Já que a intenção aqui é fazer com que o serviço assegure a última forma do indicador de controle. Coisa que antes era feita por uma variável global de terminal.

Agora uma atenção, ainda mais especial, deve ser dada a linha 38. Aqui testaremos se o conteúdo do buffer é diferente do nosso sistema de posicionamento global. Caso seja diferente, faremos um novo teste na linha 40. Este visa verificar se o indicador de controle já foi inicializado, e se estamos em modo de play. Se ambas condições, forem de fato, satisfeitas, iremos na linha 41 armazenar o valor que se encontra no buffer do indicador. Bastante atenção a isto. Pois durante a fase que o indicador de controle estiver em modo pausado, não queremos de fato atualizar os dados aqui. Queremos e devemos deixar que o usuário ajuste, e use o indicador de controle da maneira como ele desejar.

De quer forma, nas linhas 42 e 43, fazemos a montagem da informação que será repassada ao indicador. Tal informação irá ser repassada para ele durante um evento customizado, que é disparado na linha 44. Assim que este evento for disparado, passamos o controle para que o MetaTrader 5, faça o seu trabalho, no entanto o serviço irá continuar a ser executado em paralelo.

Este código presente neste procedimento, é algo que você deve analisar com muita atenção, até que de fato consiga compreender o que está acontecendo. Pois diferente do que foi visto no artigo anterior, este código daqui é um pouco mais complexo, apesar de fazer exatamente a mesma coisa. Ou seja, assim que o MetaTrader 5, coloca o indicador de controle no gráfico, este código o iniciará. Depois disto, ficará verificando o que está acontecendo no indicador. Quando o usuário trocar o tempo gráfico, o serviço servirá como uma memória da última forma do indicador, fazendo com que o indicador seja novamente inicializado, só que com o último status antes da troca de tempo gráfico.

Pois bem. Agora vamos ver um código, que surgiu por questões de reutilização. Este é visto na linha 48. O que este procedimento faz, é simplesmente fechar todas as janelas gráficas que estiverem presentes no MetaTrader 5, que contenha o ativo de replay. Não é algo assim tão complicado de ser compreendido. Mas por conta que temos que fazer isto, no mínimo, duas vezes. Decidi criar este procedimento apenas para evitar duplicar o código.

Assim sendo. A partir de agora, entramos nos procedimentos que são públicos da classe C_Replay. Basicamente, você pode notar que o código não difere muito do que era antes. Pelo menos no que diz respeito ao constructor e destructor da classe. Então estes dois não farei nenhum comentário extra, já que os mesmos já foram devidamente explicados em artigos anteriores, onde expliquei o funcionamento da classe C_Replay. No entanto, aqui existem três funções que merecem, pelo menos algum tipo de explicação. Então vamos a elas, pela ordem que as mesmas aparecem no código.

A primeira é OpenChartReplay, que se inicia na linha 77 e vai até a linha 93. Esta função tem como princípio, verificar a integridade das informações que foram coletadas pelo sistema de carregamento. Isto para que o replay / simulação possam de fato serem possíveis de serem executados. No entanto, é justamente nesta função que temos algo bastante complexo, que juntamente com a função InitBaseControl, que será vista depois, permite fazermos uso de um template.

Esta questão do template, é de suma importância para nós. Precisamos que ele, seja de fato, corretamente utilizado, e colocado em funcionamento de uma maneira adequada. Mas fazer isto, não é algo assim tão simples. Diferente do que muitos, inclusive eu, imaginava no começo. Então na linha 87, depois de ter aberto o gráfico do ativo, na linha 86, tentamos adicionar o template no gráfico. O template a ser usado, é indicado como um dos argumentos da função. De qualquer forma, um template será de fato colocado no gráfico. Seja um indicado pelo usuário, seja o template default do MetaTrader 5. Mas existe uma coisa aqui, um detalhe que é pouco explorado, ou se quer falado. O template não é colocado imediatamente. Esta função ChartApplyTemplate, ela é assíncrona, ou seja, ela poderá ser de fato executada, alguns milissegundos depois de ter sido de fato chamada. E isto é um problema para nós.

Para entender o tamanho da encrenca. Vamos dar uma breve pausa, na classe C_Replay, para poder olhar o código do serviço. Este pode ser visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property service
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property copyright "Daniel Jose"
05. #property version   "1.58"
06. #property description "Replay-Simulator service for MetaTrade 5 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/"
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Replay.mqh>
12. //+------------------------------------------------------------------+
13. input string            user00 = "Mini Dolar.txt";   //Replay Configuration File.
14. input ENUM_TIMEFRAMES   user01 = PERIOD_M5;          //Initial Graphic Time.
15. input string            user02 = "Default";          //Template File Name
16. //+------------------------------------------------------------------+
17. C_Replay *pReplay;
18. //+------------------------------------------------------------------+
19. void OnStart()
20. {
21.    pReplay = new C_Replay();
22. 
23.    UsingReplay();   
24.    
25.    delete pReplay;
26. }
27. //+------------------------------------------------------------------+
28. void UsingReplay(void)
29. {
30.    if (!(*pReplay).SetSymbolReplay(user00)) return;
31.    if (!(*pReplay).OpenChartReplay(user01, user02)) return;
32.    if (!(*pReplay).InitBaseControl()) return;
33.    Print("Permission granted. Replay service can now be used...");
34.    while ((*pReplay).LoopEventOnTime());
35. }
36. //+------------------------------------------------------------------+

Código fonte do Serviço de replay / simulador

Observe que executamos as coisas, em uma dada sequência. E isto entre as linhas 30 e 34. Note que depois que fazemos a inicialização via constructor, na linha 21, vamos para a linha 30, onde verificamos se está tudo certo com o carregamento. Depois na linha 31, tentamos abrir o gráfico, para somente depois, na linha 32, carregarmos os elementos que precisamos para controlar o serviço. Se tudo ocorrer bem, na linha 33 imprimiremos uma mensagem no terminal, e na linha 34 entraremos no loop de execução.

Olhando este código, não conseguimos de fato notar que entre abrir o gráfico, na linha 31 e adicionar os controles na linha 32, coisas estranhas podem acontecer. Isto devido ao uso do template que é carregado na classe C_Replay. Então vamos voltar a classe para entender, a real encrenca presente no fato de usarmos um template.

Depois que pedimos para que o MetaTrader 5, use um template, na linha 87 da classe C_Replay. O código pode executar muito mais rapidamente do que realmente deveria. Por conta disto na linha 99, informamos ao usuário que o serviço espera a presença do indicador de mouse. Se este indicador estiver no template, ele será carregado automaticamente, caso contrário, o usuário deverá adicionar o mesmo de forma manual.

Neste ponto mora um problema, e este problema se deve ao fato de que a função responsável por adicionar o template, ser executada de forma assíncrona. Para minimizar os danos, fazemos uso da linha 100, onde, fazemos com que o serviço fique por algum tempo, sem executar nada. Parado esperando que o gráfico se estabilize e a função de colocação de template, seja de fato aplicada no gráfico. Apenas e somente depois disto é que de fato verificamos na linha 101, se o indicador de mouse, está ou não presente. Este laço ficará executando, até que o indicador apareça no gráfico, ou que o gráfico seja fechado pelo usuário.

Assim que o indicador de mouse, seja de fato detectado, ou o gráfico seja fechado, o código continuará. Se tudo estiver conforme esperado, iremos na linha 105, tentar adicionar o indicado de controle no gráfico. Tudo isto é muito lindo e belo. Mas o indicador de controle não pode e não será aceito caso esteja no template. Esta é uma das modificações que mostrarei depois como fazer, na qual impede que o indicador apareça no template. Assim como também será preciso fazer uma pequena modificação no indicador de mouse, mas isto fica para depois. De qualquer maneira, sem a linha 100, o gráfico iria ser fechado pouco tempo depois de ter sido aberto.


Conclusão

Apesar de ter ficado um gostinho de ainda não terminou. O fato é que, explicar por que do gráfico fechar assim que o template for aplicado, e isto em detalhes, é algo bastante complicado, sendo necessário mostrar outras coisas também, a fim de que você de fato venha a entender como isto é possível, e por que o simples fato de existir a linha 100, impede isto de acontecer. Desta forma, deixarei esta explicação para o próximo artigo. Onde detalharei com calma esta questão do template, e as modificações que foram necessárias nos módulos dos indicadores, a fim de que eles possam funcionar de maneira adequada, neste serviço de replay / simulação.

Pois como você pode ver ele é diferente do serviço de teste, que foi visto no artigo anterior. Mas antes deixo vocês com um vídeo mostrando o resultado da execução deste sistema. E como ele não está da forma como é mostrado no vídeo, não teremos anexo neste artigo.


Vídeo de demonstração


Arquivos anexados |
Anexo.zip (420.65 KB)
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Redes neurais de maneira fácil (Parte 78): Detecção de objetos baseada em Transformador (DFFT) Redes neurais de maneira fácil (Parte 78): Detecção de objetos baseada em Transformador (DFFT)
Neste artigo, proponho olhar a questão da construção de uma estratégia de trading de outra perspectiva. Em vez de prever o movimento futuro dos preços, tentaremos construir um sistema de trading baseado na análise de dados históricos.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Ciência de dados e aprendizado de máquina (Parte 20): Escolha entre LDA e PCA em tarefas de algotrading no MQL5 Ciência de dados e aprendizado de máquina (Parte 20): Escolha entre LDA e PCA em tarefas de algotrading no MQL5
Neste artigo, vamos considerar métodos de redução de dimensionalidade e sua aplicação no ambiente de trading MQL5. Especificamente, vamos estudar as nuances da Análise Discriminante Linear (LDA) e da Análise de Componentes Principais (PCA), bem como analisar sua influência no desenvolvimento de estratégias e na análise de mercado.