preview
Desenvolvendo um sistema de Replay (Parte 39): Pavimentando o Terreno (III)

Desenvolvendo um sistema de Replay (Parte 39): Pavimentando o Terreno (III)

MetaTrader 5Exemplos | 11 janeiro 2024, 09:39
210 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay (Parte 38): Pavimentando o Terreno (II), expliquei e demonstrei como você faz para enviar informações, no nosso caso o EA, para o indicador. A fim de configurar o indicador para que ele consiga nos retornar algum tipo de informação plausível.

Pois bem, agora chegou a hora de ir no sentido contrário. Ou seja fazer com que o indicador diga para o requerente, que no nosso caso é o EA. Algo que tenha algum tipo de sentido. Isto para que possamos saber como, ou de que forma deveremos proceder.

Existem diversas coisas aqui. Algumas mais simples se serem explicadas, outras nem tanto. Algumas demandam tempo, enquanto outras são relativamente rápidas de serem demonstradas. Mas de uma forma ou de outra. Aqui, neste artigo, iremos criar as bases necessárias para poder construir o nosso Chart Trader. O Chart Trader que será desenvolvido aqui, no sistema de replay / simulação, se parecerá muito com o que foi visto no artigo passado.

Porém diferente daquele visto no artigo que você pode ver ao olhar este link:  Desenvolvendo um EA de negociação do zero (Parte 30): CHART TRADE agora como indicador ?!. O que iremos fazer aqui, faz com que aquele seja uma brincadeira de criança. Isto por conta da forma como iremos fazer aquele mesmo Chart Trader. Mas não vou estragar a surpresa. Caso contrário não irá ter graça. Mas para simplificar a explicação do que irá ser feito. Primeiro é preciso que você entenda mais algumas coisas. E tais coisas serão vista aqui, neste artigo.


Construção inicial do modelo

Apesar do que foi visto nos dois últimos artigos ser algo válido. E tudo aquilo está funcionando, conforme me propus mostrar. No entanto, temos uma questão aqui, que é o fato de já estarmos estamos apenas enviando dados do EA para o indicador. Bem, então existe algum tipo de comunicação, mas não estamos ainda lendo os dados do indicador. Neste caso estamos precisando então nos organizar de modo a ler os dados do indicador. 

Para facilitar a nossa vida, vamos fazer algumas coisinhas extras no código visto até o momento. 

A primeira coisa que faremos, isto para facilitar imensamente a nossa vida. É criar um arquivo de cabeçalho. Este pode ser visto abaixo:

1. #property copyright "Daniel Jose"
2. #property link      ""
3. //+------------------------------------------------------------------+
4. #define def_ShortName        "SWAP MSG"
5. //+------------------------------------------------------------------+

Arquivo de cabeçalho

Este arquivo de cabeçalho, terá como nome Defines.mqh. Ele deverá ser salvo no diretório Includes, na sub pasta Mode Swap. A única linha de fato que tem algum valor para nos, é a linha 4. Nela definimos um nome para ser usado, tanto no EA, como no indicador. A fim de facilitar a portabilidade. Já que você poderá colocar um nome no indicador, e se esquecer de colocar o mesmo nome no EA. E não estou falando do nome do arquivo. Estou me referindo ao nome no qual o indicador será reconhecido pelo MetaTrader 5.

Para isto ficar mais claro vamos ver o código do indicador. Este pode ser visto logo abaixo:

01. #property copyright "Daniel Jose"
02. #property link      ""
03. #property version   "1.00"
04. #property indicator_chart_window
05. #property indicator_plots 0
06. #property indicator_buffers 0
07. //+------------------------------------------------------------------+
08. #include <Mode Swap\Defines.mqh>
09. //+------------------------------------------------------------------+
10. #define def_ShortNameTmp    def_ShortName + "_Tmp"
11. //+------------------------------------------------------------------+
12. input double user00 = 0.0;
13. //+------------------------------------------------------------------+
14. long m_id;
15. //+------------------------------------------------------------------+
16. int OnInit()
17. {
18.     m_id = ChartID();
19.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNameTmp);
20.     if (ChartWindowFind(m_id, def_ShortName) != -1)
21.     {
22.             ChartIndicatorDelete(m_id, 0, def_ShortNameTmp);
23.             Print("Only one instance is allowed...");
24.             return INIT_FAILED;
25.     }
26.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
27.     Print("Indicator configured with the following value:", user00);
28.     
29.     return INIT_SUCCEEDED;
30. }
31. //+------------------------------------------------------------------+
32. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
33. {
34.     return rates_total;
35. }
36. //+------------------------------------------------------------------+

Código fonte do Indicador

Atenção ao código presente na linha 08. Nela incluímos, ou melhor dizendo. Pedimos para o compilador incluir o código de arquivo de cabeçalho que vemos a pouco. Não foi feita nenhuma modificação no código. Salvo o fato de que agora não temos mais aquela definição, sendo feita aqui no código do indicador.

Desta forma toda e qualquer explicação feita antes continua valendo. Mas agora vamos ver o código do EA. Esta pode ser visto logo abaixo:

01. #property copyright "Daniel Jose"
02. #property link      ""
03. #property version   "1.00"
04. //+------------------------------------------------------------------+
05. #include <Mode Swap\Defines.mqh>
06. //+------------------------------------------------------------------+
07. #define def_SWAP "Mode Swap\\Swap MSG.ex5"
08. #resource "\\Indicators\\" + def_SWAP
09. //+------------------------------------------------------------------+
10. input double user00 = 2.2;
11. //+------------------------------------------------------------------+
12. int m_handle;
13. long m_id;
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {   
17.     m_id = ChartID();
18. 
19.     EraseIndicator();
20.     m_handle = iCustom(NULL, PERIOD_CURRENT, "::" + def_SWAP, user00);
21.     ChartIndicatorAdd(m_id, 0, m_handle);
22.     
23.     Print("Indicator loading result:", m_handle != INVALID_HANDLE ? "Success" : "Failed");
24.     
25.     return INIT_SUCCEEDED;
26. }
27. //+------------------------------------------------------------------+
28. void OnDeinit(const int reason)
29. {
30.     EraseIndicator();
31. }
32. //+------------------------------------------------------------------+
33. void OnTick()
34. {
35. }
36. //+------------------------------------------------------------------+
37. void EraseIndicator(void)
38. {
39.     if ((m_handle = ChartIndicatorGet(m_id, 0, def_ShortName)) == INVALID_HANDLE) return;
40.     ChartIndicatorDelete(m_id, 0, def_ShortName);
41.     IndicatorRelease(m_handle);
42. }
43. //+------------------------------------------------------------------+

Código fonte do EA

Aqui temos algo BEM DIFERENTE. Mas nem tanto assim, se você for um bom observador. Talvez a única e real diferença seja de fato no fato de existir um procedimento chamado EraseIndicator. Este pode ser visto na linha 37. Este procedimento é chamado em dois momentos. O primeiro é na linha 19 e o segundo é na linha 30. O fato de ele ser chamado na linha 30, acredito que ninguém irá de fato fica na dúvida, do por que da chamada. Mas e quanto a chamada da linha 19. Você sabe o motivo desta chamada ?!?!

O motivo da chamada na linha 19, é justamente promover a sincronização entre o valor informado no EA, com o valor a ser enviado para o indicador. Mas existe algo a ser observado com calma. Veja a linha 39 do código do EA. Notem o fato de que quando o procedimento for chamado, seja na linha 19, seja na linha 30. Iremos verificar se o indicador se encontra no gráfico. Caso ele não esteja, o código irá retornar e tanto a linha 40, que remove o indicador do gráfico. Quanto a linha 41 que libera o handle, não serão executadas.

Mas por que precisamos remover o indicador do gráfico, usando a linha 40 ?!?! O motivo, não está no código do EA, mas sim no código do indicador.

Ao observar o código do indicador, você irá notar que se ele estiver no gráfico, no momento que o EA, tentar mudar o valor do mesmo. A linha 20 irá bloquear o indicador. Evitando que ele seja atualizado, já que o sistema irá detectar que seria o lançamento de um novo indicador no gráfico. Apesar deste não ser de fato o que estará acontecendo, a linha 20 irá ver desta forma. Por conta disto precisamos remover o indicador do gráfico.

Mas voltando ao código do EA. Você tem noção do que representa as linhas 07 e 08. E por que a linha 20 está diferente do que foi visto no artigo anterior ?!?!

É neste ponto que começamos a sair do básico e começamos a entrar em um outro terreno. A linha 07 criar uma definição, mas esta definição indica onde se encontra o código do indicador. Desta forma quando o compilador for criar o executável do EA, e ele encontrar a linha 08, ele irá procurar o executável indicado na linha 07. Caso este não exista, o compilador irá tratar de compilar o código responsável por gerar tal executável. Ou seja na linha 08, tornamos o indicador como sendo um recurso interno do EA.

Isto é algo bastante interessante. Tem seus prós e contras. Uma das vantagens que posso citar, é a de que: Uma vez compilado o EA, você pode apagar o executável do indicador. Mas calma ai. Não faça isto ainda. Existe um detalhe para que você possa de fato fazer isto. Uma desvantagem que também posso citar, é a de que: Uma vez compilado o EA, se você tiver problemas no indicador, terá que recompilar novamente todo o código do EA. Mesmo que o problema seja apenas no indicador. Mas novamente calma. Existe um detalhe que deve ser levado em conta.

E qual seria o detalhe ?!?! Bem, ele é algo bastante sutil. Quase passa batido. Por conta disto, todo bom programador, é também um bom observador. Então vamos ao detalhe. Observe na linha 20 do código do EA. Reparem que na declaração do terceiro parâmetro aparece uma pequena string ( :: ). Esta pequena string que vem antes da definição é um operador de resolução de escopo.

O fato deste operador esta ali, indica que iremos usar algo, não exatamente como podemos imaginar. Toda a vez que este operador aparece, estamos de alguma forma, informando de forma explicita ao compilador algo a ser feito. Normalmente irá se tratar de algum tipo de decisão que o compilador deverá tomar, mas queremos que ele saiba explicitamente qual a decisão a ser tomada.

No caso especifico, visto no código, estamos informando ao compilador, para usar o indicador que se encontra como sendo um recurso no executável do EA. Neste caso, poderemos simplesmente remover o executável do indicador, logo que o EA tiver sido compilado com sucesso. E mesmo assim o EA irá conseguir usar o indicador correto.

Caso este operador, o tal de resolução de escopo, não seja adicionado, como mostrado no fragmento abaixo. Mesmo que o compilador inclua o indicador como sendo um recurso do EA. O EA irá de fato usar o executável, que se encontra no diretório informado na definição. Ou seja o indicador terá que ser transportado junto com o EA, sendo assim um arquivo separado.

20.     m_handle = iCustom(NULL, PERIOD_CURRENT, def_SWAP, user00);

Este simples detalhe, faz toda a diferença. No caso como está na linha 20, no código completo, uma vez que o EA tenha sido compilado. Você poderá deletar o executável do indicador. Tem mais um detalhe: Caso o indicador seja adicionado e usado como recurso do EA, você antes de compilar o EA, deve preferencialmente, deletar o executável do indicador. Isto para garantir, que o último código presente no indicador seja de fato compilado. Normalmente em sistema onde usamos compilações múltiplas, contamos com uma ajuda. Um arquivo normalmente chamado de MAKE.

Ao usar este tipo de ajuda, toda a vez que você for compilar um código. O MAKE irá remover, de forma adequada os executáveis. Isto de forma totalmente automática. Ele compara o momento da última compilação, com o tempo da última modificação de algum dos arquivos, usados no fonte do executável. Caso tenha havido alguma edição, o MAKE irá remover o executável, fazendo assim que o compilador refaça um novo executável. No atual momento, o MQL5 ainda não conta com este recurso. Mas quem sabe no futuro os mantedores decidam adicionar tal recurso para nos desenvolvedores.

Esta explicação dada acima, mostra o que podemos fazer, e como deveremos proceder e iremos proceder no futuro. Mas ainda não temos a leitura de nenhum tipo de dado do indicador.

Vamos então implementar uma versão bem, mas bem simples de comunicação. Mas para explicar isto com mais detalhes e com mais calma vamos para u novo tópico.


Lendo os dados do indicador.

Basicamente existe somente uma forma de ler qualquer tipo de informação presente no indicador. Fazendo a leitura do buffer do mesmo. Mas aqui temos um pequeno detalhe: A função CopyBuffer, não nos permite trabalhar com qualquer tipo de dado presente no buffer. Não de forma natural, e observando a declaração desta função você entenderá o que estou querendo dizer.

int  CopyBuffer(
   int       indicator_handle,     // handle do indicador
   int       buffer_num,           // número do buffer do indicador
   int       start_pos,            // posição de início
   int       count,                // quantidade para copiar
   double    buffer[]              // array destino para copiar
   );

Definição da função CopyBuffer

Para melhor explicar. Você pode ver acima como é declarada uma das variantes da função CopyBuffer. Digo uma, pois existem 3, mas o que nos importa de fato, é a última variável presente na declaração. Notem que ela é do tipo double, ou seja, somente poderemos retornar valores do tipo double. Isto a principio, pois na verdade podemos retornar qualquer coisa. No entanto, no artigo Desenvolvendo um EA de negociação do zero (Parte 17): Acessando dados na WEB (III), mostrei uma forma de você poder burlar isto.

Iremos usar algo muito parecido aqui nesta sequencia também, porém em um nível diferente. Talvez um pouco maior e mais complicado, isto para quem está começando a programar. Mas a ideia será a mesma usada no artigo acima mencionado. Se você deseja saber mais detalhes, veja o artigo para entender o que iremos de fato fazer. Pois aqui iremos simplesmente fazer, e não irei explicar os detalhes que estão sendo mostrados naquele artigo.

Então tudo bem, já sabemos então com o que iremos lidar. Vamos então modificar o código fonte do indicador para algo visto abaixo. Detalhe: Irei fazer as coisas aos pouco, para que você consiga acompanhar e assim compreender o que estará sendo feito.

01. #property copyright "Daniel Jose"
02. #property link      ""
03. #property version   "1.00"
04. #property indicator_chart_window
05. #property indicator_plots 0
06. #property indicator_buffers 1
07. //+------------------------------------------------------------------+
08. #include <Mode Swap\Defines.mqh>
09. //+------------------------------------------------------------------+
10. #define def_ShortNameTmp    def_ShortName + "_Tmp"
11. //+------------------------------------------------------------------+
12. input double user00 = 0.0;
13. //+------------------------------------------------------------------+
14. long m_id;
15. double m_Buff[];
16. //+------------------------------------------------------------------+
17. int OnInit()
18. {
19.     m_id = ChartID();
20.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNameTmp);
21.     if (ChartWindowFind(m_id, def_ShortName) != -1)
22.     {
23.             ChartIndicatorDelete(m_id, 0, def_ShortNameTmp);
24.             Print("Only one instance is allowed...");
25.             return INIT_FAILED;
26.     }
27.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
28.     Print("Indicator configured with the following value:", user00);
29.     
30.     SetIndexBuffer(0, m_Buff, INDICATOR_CALCULATIONS);
31.     ArrayInitialize(m_Buff, EMPTY_VALUE);
32. 
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
37. {
38.     m_Buff[rates_total - 1] = user00 * 2.0;
39.     
40.     return rates_total;
41. }
42. //+------------------------------------------------------------------+

Código fonte do indicador

Se você comparar o código fonte visto acima, com o código fonte visto no inicio deste artigo. Irá notar que temos apenas algumas poucas diferenças. Como foi dito, irei devagar. Basicamente temos a mudança na linha 06, onde dizemos que existirá um buffer dentro do indicador. Este buffer será visível fora do indicador. Mas depois iremos ver isto. Vamos primeiro entender o que está acontecendo no indicador.

Depois disto, temos na linha 15 a declaração de uma variável global. Mas esta será vista apenas e somente pelo código do indicador, ou pelas partes que pertencerem ao indicador. Esta variável, que é um buffer, precisa ser inicializada. Isto é feito nas linhas 30 e 31. Agora já temos o nosso buffer inicializado e acessível com algum tipo de informação.

No entanto, se você o ler, poderá obter apenas lixo. Então precisamos de fato de alguma forma útil de lançar dados no buffer. Podemos fazer isto de diversas maneiras, mas tecnicamente a melhor e mais segura maneira é como esta sendo feito. Por conta disto temos a linha 38.

Observem com bastante atenção esta linha. Vejam que não aponto para qualquer região no buffer. Mas por que ?!?!

O motivo é que precisamos padronizar de alguma forma as coisas. Se você coloca-se a informação, no caso o resultado da multiplicação do valor informado pelo EA por 2.0; Poderia ser difícil saber, onde buscar a informação realmente útil. Lembre-se estou indo com calma. A coisa vai ficar bem mais complicada depois.

Então usamos o próprio sistema de calculo do indicador, para colocar a informação sempre em uma mesma posição.

Para compreender melhor as coisas, vamos ver o código fonte do EA. Este pode ser visto agora logo abaixo:

01. #property copyright "Daniel Jose"
02. #property link      ""
03. #property version   "1.00"
04. //+------------------------------------------------------------------+
05. #include <Mode Swap\Defines.mqh>
06. //+------------------------------------------------------------------+
07. #define def_SWAP "Indicators\\Mode Swap\\Swap MSG.ex5"
08. #resource "\\" + def_SWAP
09. //+------------------------------------------------------------------+
10. input double user00 = 2.2;
11. //+------------------------------------------------------------------+
12. int m_handle;
13. long m_id;
14. //+------------------------------------------------------------------+
15. int OnInit()
16. {   
17.     double Buff[];
18.     
19.     m_id = ChartID();
20. 
21.     if ((m_handle = ChartIndicatorGet(m_id, 0, def_ShortName)) == INVALID_HANDLE)
22.     {
23.             m_handle = iCustom(NULL, PERIOD_CURRENT, "::" + def_SWAP, user00);
24.             ChartIndicatorAdd(m_id, 0, m_handle);
25.     }
26.             
27.     Print ("Buffer reading:", (m_handle == INVALID_HANDLE ? "Error..." : CopyBuffer(m_handle, 0, 0, 1, Buff) > 0 ?  (string)Buff[0] : " Failed."));
28.     
29.     return INIT_SUCCEEDED;
30. }
31. //+------------------------------------------------------------------+
32. void OnDeinit(const int reason)
33. {
34.     ChartIndicatorDelete(m_id, 0, def_ShortName);
35.     IndicatorRelease(m_handle);
36. }
37. //+------------------------------------------------------------------+
38. void OnTick()
39. {
40. }
41. //+------------------------------------------------------------------+

Código fonte do EA

Notem que o código não mudou muito frente ao que foi visto antes. Lembrando que aqui o Indicador será usado como recurso. Então você pode apagar o executável do indicador depois de compilar o EA. Mas vamos ver o que de fato foi adicionado ao código.

Primeiro temos agora a declaração de uma variável na linha 17. Este será o nosso buffer de retorno, ou de leitura do indicador. Além desta linha 17, temos uma nova linha também, a linha 27.

Muitos vão achar esta linha 27 uma tremenda confusão. Mas ela é basicamente dois operadores ternários aninhados. Primeiro testamos se o handle do indicador é válido, se não for, uma mensagem será impressa na caixa de mensagens do terminal. Caso o handle seja válido, iremos ler 1 posição do buffer do indicador. E qual posição será lida ?!?! A primeira. Não entendi. Calma, relaxa, vamos continuar e depois explico melhor isto. Caso tenhamos sucesso na leitura do buffer, iremos imprimir o valor que ele contém. Caso não tenhamos sucesso na leitura, uma outra mensagem de erro será impressa. Mas esta será diferente da primeira, para indicar que a origem da falha foi outra.

Agora vamos entender, por que quando lemos o buffer da forma como é mostrada na linha 27. Estaremos lendo a primeira e não a última posição.


Entendendo a escrita e leitura do buffer.

Existe uma imagem, que irei pegar emprestada da documentação do MQL5 que expressa claramente o que irei explicar. Esta pode ser vista logo abaixo:

Figura 01

Figura 01 - Escrita e leitura de buffer de indicadores

Esta imagem acima, quase fala por si só. Mas mesmo assim, vamos entender como o buffer é escrito no indicador, e lido fora dele.

Quando você escreve no buffer, isto no indicador, você deve tomar o cuidado, de sempre escrever em um determinada posição. Conforme novas barras vão surgindo, novas posições vão aparecendo automaticamente no buffer. O problema é que em cada um do tempos gráfico, você terá um buffer de tamanho diferente.

E onde está o problema nisto ?!?! Não é apenas um único problema. Temos mais de um. Para começar, suponhamos que você, no indicador resolva escrever na posição Zero usando algo parecido com o código abaixo:

35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
37. {
38.     m_Buff[0] = user00 * 3.0;
39.     
40.     return rates_total;
41. }
42. //+------------------------------------------------------------------+

Fragmento de código - Modelo 01

Se você modificar as linhas, no código do indicador, visto no tópico anterior, por este fragmento de código - Modelo 01. Irá literalmente escrever na posição Zero do buffer, conforme pode ser notado na linha 38. Mas existe um problema ao se fazer isto.

O problema não estará  em escrever, mas sim quando você tentar ler o buffer, fora do código do indicador. Por exemplo no EA.

Quando você executa uma leitura de buffer, normalmente, e frequentemente o MetaTrader 5 irá retornar apenas e somente no máximo 2000 posições. Ou seja caímos na questão da figura 01. Se por um acaso, a quantidade de dados presentes no indicador, for maior que estas 2000 posições, você terá problemas. Pois lembre-se que a escrita se deu na posição zero, mas a posição zero, NÃO É, a mesma posição zero que CopyBuffer estará se referindo. Para CopyBuffer, esta posição zero, que está no código do indicador, pode ser de fato a posição 2001, e se este for o caso você não conseguirá ler a posição zero, do buffer do indicador. Aquele valor estará preso no indicador.

Por conta desta fato abrimos mão de escrever em um  posição iniciada em zero, e começamos a escrever a partir de uma posição, que irá se dar no final do buffer. Ainda não compreendeu ?!?!

A posição zero no buffer do indicador, deverá sempre ser pensada como sendo a posição rates_total - 1. Por conta disto escrevemos nesta posição no código do indicador visto no tópico anterior. E justamente por este fato quando lemos o buffer do indicador, via CopyBuffer. Ao imprimirmos o valor, usamos de fato o index zero.

Talvez isto ainda não tenha ficado de todo claro em sua mente. Para compreender isto, vamos ver um outro exemplo de código, onde iremos passar um dado para o indicador. Mas diferente do que foi feito no tópico anterior, vamos desta vez retornar não um, mas uma quantidade maior de valores. Sendo um deles uma string simples.

Um detalhe: Você pode escrever ao contrário no buffer, ou pode escrever de maneira convencional. Ou melhor dizendo, seguindo a ordem: Primeira informação, primeiro valor. Segunda informação, segundo valor. Se fosse feito ao contrário, seria: Primeira informação, último valor. Segunda informação, último valor menos uma posição, e assim por diante. Mas para facilitar a interpretação, não na escrita, e sim na leitura, vamos fazer as coisas de forma convencional.

Para simplificar a nossa vida, vamos modificar o arquivo de cabeçalho, para o que pode ser visto logo abaixo:

01. #property copyright "Daniel Jose"
02. //+------------------------------------------------------------------+
03. #define def_ShortName       "SWAP MSG"
04. //+------------------------------------------------------------------+
05. union uCharDouble
06. {
07.     double  dValue;
08.     char    cInfo[sizeof(double)];
09. };
10. //+------------------------------------------------------------------+

Código fonte do arquivo Defines.mqh

O que temos aqui, entre as linhas 05 e 09 é uma união. Esta união irá fazer com que possamos passar dados do tipo texto, usando um valor do tipo double. Se você está vendo isto pela primeira vez, pode lhe parecer estranho. Mas já fizemos isto em outros momentos. Um exemplo disto é visto no artigo: Desenvolvendo um EA de negociação do zero (Parte 17): Acessando dados na WEB (III). Mas voltemos a nossa questão. Agora temos uma forma de lançar um pequena string do indicador para o EA. E o motivo de usar um valor double, é por conta que não podemos enviar via CopyBuffer, outro tipo de valor. Temos obrigatoriamente de usar o tipo double.

Tendo feito esta mudança no arquivo Defines.mqh, podemos passar para o código fonte do indicador.

01. #property copyright "Daniel Jose"
02. #property version   "1.00"
03. #property indicator_chart_window
04. #property indicator_plots 0
05. #property indicator_buffers 1
06. //+------------------------------------------------------------------+
07. #include <Mode Swap\Defines.mqh>
08. //+------------------------------------------------------------------+
09. #define def_ShortNameTmp    def_ShortName + "_Tmp"
10. //+------------------------------------------------------------------+
11. input double user00 = 0.0;
12. //+------------------------------------------------------------------+
13. long m_id;
14. double m_Buff[];
15. //+------------------------------------------------------------------+
16. int OnInit()
17. {
18.     m_id = ChartID();
19.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNameTmp);
20.     if (ChartWindowFind(m_id, def_ShortName) != -1)
21.     {
22.             ChartIndicatorDelete(m_id, 0, def_ShortNameTmp);
23.             Print("Only one instance is allowed...");
24.             return INIT_FAILED;
25.     }
26.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
27.     Print("Indicator configured with the following value:", user00);
28.     
29.     SetIndexBuffer(0, m_Buff, INDICATOR_CALCULATIONS);
30.     ArrayInitialize(m_Buff, EMPTY_VALUE);
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.     uCharDouble info;
38.     int pos = rates_total - 3;
39.     
40.     StringToCharArray("Config", info.cInfo);
41.     m_Buff[pos + 0] = info.dValue;
42.     m_Buff[pos + 1] = user00 * 2.0;
43.     m_Buff[pos + 2] = user00 * 2.5;
44.             
45.     return rates_total;
46. }
47. //+------------------------------------------------------------------+

Código fonte do indicador - Atualizado para escrever em mais de uma posição

Observem com atenção este código acima. Notem que apenas a partir da linha 37 é que temos algum tipo de mudança frente ao código visto no inicio do artigo. E por que disto ?!?! O motivo, é que agora iremos de fato montar uma informação mais ampla a ser retornada via CopyBuffer. Nenhuma outra parte do código precisou ser modificada. Apenas o código presente a partir da  linha 37.

Então vamos entender o que está acontecendo. Na linha 37 declaramos uma variável que será usada para transformar uma string em um valor double. Detalhe: A string é limitada a 8 caracteres, caso a informação tenha mais caracteres, você deverá providenciar um array para isto, mas sempre contabilizando a coisa de 8 em 8.

Na linha 38, declaramos uma variável que será utilizada para que a informação possa ser postada de forma convencional. Ou seja seria como se você estivesse escrevendo um texto, da esquerda para a direita. No caso do árabe, você estaria escrevendo da direita para a esquerda. Mas acredito que deu para entender a ideia. Nesta mesma linha 38, indicamos a quantidade de valores double que iremos postar. No caso serão 3 valores.

Na linha 40 iremos converter a string em um array de caracteres. Ao fazer isto, teremos o valor a ser usado na nossa primeira posição. Então na linha 41, lançamos este valor para o buffer. 

Nas linhas 42 e 43, efetuamos um cálculo simples, apenas para constar algum tipo de dado para ser lido. Assim será possível mostrar como a leitura irá se dar depois, quando for preciso acessar mais posições no buffer.

Basicamente a questão sobre o indicador é esta. Não existe, neste momento especifico mais nada a se falar sobre a forma de fazermos a comunicação. Então vamos ver agora como é que iremos agir, a ponto de saber e ler o buffer que foi criado no indicador. Para isto vamos recorrer ao código do EA. Este pode ser visto, agora já atualizado logo abaixo:

01. #property copyright "Daniel Jose"
02. #property version   "1.00"
03. //+------------------------------------------------------------------+
04. #include <Mode Swap\Defines.mqh>
05. //+------------------------------------------------------------------+
06. #define def_SWAP "Indicators\\Mode Swap\\Swap MSG.ex5"
07. #resource "\\" + def_SWAP
08. //+------------------------------------------------------------------+
09. input double user00 = 2.2;
10. //+------------------------------------------------------------------+
11. int m_handle;
12. long m_id;
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {   
16.     double Buff[];
17.     uCharDouble Info;
18.     int iRet;
19.     string szInfo;
20.     
21.     m_id = ChartID();
22.     if ((m_handle = ChartIndicatorGet(m_id, 0, def_ShortName)) == INVALID_HANDLE)
23.     {
24.             m_handle = iCustom(NULL, PERIOD_CURRENT, "::" + def_SWAP, user00);
25.             ChartIndicatorAdd(m_id, 0, m_handle);
26.     }
27.     ArraySetAsSeries(Buff, false);
28.     if (m_handle == INVALID_HANDLE) szInfo = "Invalid handler to read the buffer.";
29.     else
30.     {
31.             if ((iRet = CopyBuffer(m_handle, 0, 0, 3, Buff)) < 3) szInfo = "Buffer reading failed.";
32.             else
33.             {
34.                     Info.dValue = Buff[0];
35.                     szInfo = CharArrayToString(Info.cInfo) + " [ " + (string)Buff[1] + " ] [ " + (string)Buff[2] + " ]";
36.             }
37.     }
38.     Print("Return => ", szInfo);
39.             
40.     return INIT_SUCCEEDED;
41. }
42. //+------------------------------------------------------------------+
43. void OnDeinit(const int reason)
44. {
45.     ChartIndicatorDelete(m_id, 0, def_ShortName);
46.     IndicatorRelease(m_handle);
47. }
48. //+------------------------------------------------------------------+
49. void OnTick()
50. {
51. }
52. //+------------------------------------------------------------------+

Código fonte do EA - Atualizado para ler mais de uma posição.

Neste código atualizado do EA, que pode ser visto acima. Não é muito diferente do que estamos vendo deste o inicio. Mas aqui temos algumas coisas novas, e que merecem algum tipo de explicação.

Entre as linhas 17 a 19 temos a declaração de novas variáveis que iremos usar, a fim de conseguir decodificar a informação que se encontra no buffer. Pode parecer estranho dizer isto, mas de fato a informação presente no buffer está codificada. Já que estamos repassando uma string em uma das posições.

Mas nosso interesse está de fato entre as linhas 27 e  38. Neste ponto é que estamos efetuando a leitura do buffer. Então vamos começar por partes.

Na linha 27, temos um código que em alguns momentos pode não ser visto de fato. Mesmo quando estamos fazendo uma leitura de diversas posições do buffer. O motivo é que por padrão, a leitura irá ser feita de forma direta. Ou seja olhe a figura 01. Veja que o array está na sequencia direta, Porém, toda via, e entretanto. Existem momentos quem que a leitura deverá ser feita de forma invertida. Neste caso, o invés de fazer a indexação contrária no acesso ao array. Usamos a função definida na linha 27, para dizer que a leitura será invertida. Neste caso, e ao contrário do que esta sendo visto. O valor repassado a função será um false. Assim podemos usar uma indexação de acesso, como se fosse um acesso direto.

Então apesar da linha 27, não fazer nenhum tipo de sentido, no nosso código atual. Dado o fato de estarmos fazendo uma escrita direta. Ela foi adicionada para mencionar e explicar justamente o que acabei de falar acima.

Não temos muito o que falar em quase nenhuma das linhas, já que grande parte das mesmas se auto explicam. Mas vamos pular para a linha 31. Aqui temos algo a ser compreendido.

Quando escrevemos no buffer, isto lá no indicador. Sabemos quantas posições de informação estaremos enviando. Esta linha 31, faz justamente isto. Ela lê o numero de posições esperadas. O mais adequado, seria fazer uma combinação melhor, usando o arquivo de cabeçalho para isto. Então se o numero de posições contendo informações viesse a aumentar, tanto o EA, quanto o indicador, estariam sempre cientes disto. Mas como aqui estamos apenas exemplificando as coisas para explicar um conceito que será usado depois. Isto pode ser ignorado por hora.

Se a quantidade de posições lidas forem menores do que o esperado, isto será um erro e será informado na terminal. Agora caso o numero seja o esperado, iremos na linha 34 traduzir a informação que está sendo dada como um double, para uma string. Assim recuperamos de volta a informação colocada no buffer pelo indicador.

Observem o fato do index neste caso ser zero. Da mesma forma como é expressa no indicador. Se fosse feita a escrita contrária no indicador, ainda assim poderíamos usar a mesma indexação no EA. Bastaria para isto, mudar o valor na linha 27, e o EA iria conseguir entender a informação usando a mesma forma de indexar.

Experimente fazer isto para entender com isto de fato acontece. Pois é importante entender estes detalhes para, de fato compreender os próximos artigos desta serie.

Uma vez feita a conversão usamos a linha 35 para montar a mensagem a ser impressa no terminal. Simples assim.


Conclusão

Nesta curta parada que foi dada no projeto do replay / simulador. Mostrei as bases do que iremos ver nos próximos artigos.

Aqui foi visto e mostrado apenas e somente a parte mais simples, de tudo que ainda está por vim. Já que não adicionamos algumas coisa e implementamos outras que irão de fato fazer parte do sistema de replay / simulação. Porém o conteúdo mostrado aqui. Será de grande importância para os próximos artigos. Já que sem entender como transferir dados entre indicadores e outros processos, você ficará meio que perdido. Isto por que ainda não mostrei como ocultar o indicador na janela de indicadores, a fim de proibir de o usuário o remover. E não adicionamos alguns tipos de coisas que complicam muito o entendimento, em diversas combinações possíveis.

Espero que você de fato consiga compreender, os conceitos e o real interesse por de traz destes 3 últimos artigos. Pois de agora em diante, a coisa vai complicar bastante.

Como quero que você venha a desenvolver um tipo de memória mecânica. Não haverá nenhum anexo aqui. Caso queira experimentar e aprender as bases mostradas aqui. Será preciso digitar os código. Mas como eles são super curtos. Isto não será problema.

Então nos vemos no próximo artigo. Onde iremos iniciar a colocação do Chart Trader, para ser usado no sistema de replay / simulador.



Arquivos anexados |
EA.mq5 (1.81 KB)
swap.mq5 (1.72 KB)
Defines.mqh (0.24 KB)
Tudo o que você precisa saber sobre a estrutura de um programa MQL5 Tudo o que você precisa saber sobre a estrutura de um programa MQL5
Qualquer programa em qualquer linguagem de programação possui uma estrutura específica. Neste artigo, você aprenderá os componentes básicos da estrutura de um programa na linguagem MQL5, o que pode ser extremamente útil ao criar um sistema de negociação ou uma ferramenta de negociação para o MetaTrader 5.
Desenvolvendo um agente de Aprendizado por Reforço em MQL5 com Integração RestAPI (Parte 3): Criando jogadas automáticas e Scripts de Teste em MQL5 Desenvolvendo um agente de Aprendizado por Reforço em MQL5 com Integração RestAPI (Parte 3): Criando jogadas automáticas e Scripts de Teste em MQL5
Este artigo explora a implementação de jogadas automáticas no jogo da velha Python, integrado com funções MQL5 e testes unitários. O objetivo é aprimorar a interatividade do jogo e garantir a robustez do sistema através de testes MQL5. Ele aborda desde o desenvolvimento da lógica de jogo até a integração e testes práticos, culminando na criação de um ambiente de jogo dinâmico e um sistema integrado confiável.
Desenvolvendo um sistema de Replay (Parte 40): Iniciando a segunda fase (I) Desenvolvendo um sistema de Replay (Parte 40): Iniciando a segunda fase (I)
Esta é a nova fase do sistema de replay / simulação. Nesta fase a conversa de fato irá ser seria. E o conteúdo irá ser tornar bastante denso. Peço que você leia com calma o artigo e sempre procure usar as referencias que possivelmente estarão sendo indicadas nos artigos. Isto para lhe ajudar a compreender melhor o que estará sendo explicado.
Redes neurais de maneira fácil (Parte 51): ator-crítico comportamental (BAC) Redes neurais de maneira fácil (Parte 51): ator-crítico comportamental (BAC)
Nos últimos dois artigos, discutimos o algoritmo Soft Actor-Critic, que incorpora regularização de entropia na função de recompensa. Essa abordagem permite equilibrar a exploração do ambiente e a exploração do modelo, mas é aplicável apenas a modelos estocásticos. Neste artigo, exploraremos uma abordagem alternativa que é aplicável tanto a modelos estocásticos quanto determinísticos.