preview
Do básico ao intermediário: Ponteiro para função

Do básico ao intermediário: Ponteiro para função

MetaTrader 5Exemplos | 19 junho 2025, 08:43
52 0
CODE X
CODE X

Introdução

No artigo anterior Do básico ao intermediário: Objetos (II), começamos a trabalhar com o que seria o primeiro tipo de evento, que podemos utilizar para manipular um objeto que esteja presente no gráfico.

No entanto, ali foi utilizado um tipo de evento, que por padrão o MetaTrader 5, sempre irá disparar, quando o usuário interagir com o gráfico. No caso o evento em questão é o de pressionar uma tecla. Como se trata de um evento simples de capturar utilizando o tratador OnChartEvent. Você talvez possa estar pensando que não existe uma outra forma de se fazer uso do teclado sem que seja via captura daquele evento. Pois bem meu caro leitor, não é bem assim que as coisas funcionam. Apesar de scripts, não utilizar ou permitir o uso do tratador OnChartEvent. Podemos sim, adicionar meios de controlar certas propriedades de um objeto, fazendo uso do teclado e de um script. Apesar de ser algo pouco convencional.

Então, antes de podermos ver como tratar eventos vindos do mouse. Vamos ver como lidar com eventos de teclado, quando estamos utilizando scripts. Apesar do MetaTrader 5, e por consequência o MQL5, não ser voltado para este tipo de atividade. Já que foram pensados para se trabalhar com gráfico de cotações. É importante que você, meu caro leitor, saiba o que dá ou não para ser feito. Pois existem limitações que precisam ser entendidas.


Script com eventos?

Para começar, você primeiro precisa entender, que NÃO. NÃO PODEMOS TER UM SCRIPT COM EVENTOS. Mas isto não nos impede de criar um script, que possa tratar eventos vindos do teclado. Porém, e é aqui onde o bicho realmente pega. É preciso que você, meu caro leitor, entenda que o MQL5, NÃO É VOLTADO para fazer uso de determinados tipos de implementação. Por conta disto, diversas limitações e dificuldade surgem, quando você deseja programar algo, utilizando única e exclusivamente o MQL5.

Mas como podemos capturar e tratar eventos de teclado, dentro de um script? A verdade é que NÃO PODEMOS. O que podemos fazer, é capturar determinadas teclas e assim fazendo uso de algum tipo de filtragem, gerar algo que seria similar ao que seria o tratador de eventos de teclado. Isto quanto estaríamos de fato fazendo uso do tratador OnChartEvent.

Para que você consiga entender. Vamos fazer uso de um código visto no artigo anterior. Assim será bem mais simples para que você compreenda o tipo de buraco no qual podemos entrar. O código em questão é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_KEY_UP          38
05. #define def_KEY_DOWN        40
06. //+----------------+
07. #define macro_NameObject  "Demo" + (string)ObjectsTotal(0)
08. //+------------------------------------------------------------------+
09. string  gl_Objs[2];
10. //+------------------------------------------------------------------+
11. int OnInit()
12. {
13.     ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0);
14.     ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrRoyalBlue);
15.     ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0);
16.     ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrPurple);
17. 
18.     return INIT_SUCCEEDED;
19. };
20. //+------------------------------------------------------------------+
21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
22. {
23.     return rates_total;
24. };
25. //+------------------------------------------------------------------+
26. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
27. {
28.     static int p = 0;
29.     MqlRates rate[1];
30. 
31.     switch(id)
32.     {
33.         case CHARTEVENT_KEYDOWN:
34.             switch ((int)lparam)
35.             {
36.                 case def_KEY_DOWN:
37.                     p = (p < Bars(_Symbol, _Period) ? p + 1 : p);
38.                     break;
39.                 case def_KEY_UP:
40.                     p = (p > 0 ? p - 1 : p);
41.                     break;
42.                 default:
43.                     return;
44.             }
45.             Comment(StringFormat("Current bar analyzed: %d", p));
46.             CopyRates(_Symbol, _Period, p, rate.Size(), rate);
47.             ObjectMove(0, gl_Objs[0], 0, rate[0].time, rate[0].close);
48.             ObjectMove(0, gl_Objs[1], 0, rate[0].time, rate[0].close);
49.             break;
50.     }
51.     ChartRedraw();
52. };
53. //+------------------------------------------------------------------+
54. void OnDeinit(const int reason)
55. {
56.     Comment("");
57.     for (uint c = 0; c < gl_Objs.Size(); c++)
58.         ObjectDelete(0, gl_Objs[c]);
59.     ChartRedraw();
60. };
61. //+------------------------------------------------------------------+

Código 01

Este código 01, quando executado irá produzir como resultado o que pode ser visto na animação logo abaixo.

Animação 01

Agora a pergunta: Como poderíamos criar algo parecido com este código 01, com um resultado idêntico ao que pode ser visto na animação 01. Mas utilizando para isto um script? Bem, esta é a questão que iremos ver como responder.

Para conseguir fazer isto, iremos precisar utilizar diversas chamadas presentes na biblioteca do MQL5. No entanto, como quero focar no MQL5 puro, não iremos adotar nenhuma outra metodologia aqui. Mesmo que o uso de uma metodologia, onde faríamos uso de um código escrito em C ou C++, pudesse vir a tornar as coisas bem mais simples. Não faremos isto, aqui nestes artigos voltados a material que vai do básico ao intermediário.

Talvez futuramente, se eu me decidir em demonstrar como desenvolver código mais avançados, em que seja necessário uso de código escrito e compilado em outras linguagens de programação. Venhamos a ver como lidar com este mesmo problema, que será visto aqui. De uma outra maneira. Que ao meu ver é bem mais simples. Porém, muito mais complicada, devido ao fato de que você precisa dominar bem o MQL5, assim como também dominar outra linguagem de programação. Isto a fim de conseguir que ambas se comuniquem a fim de resolver um determinado problema.

Ok, então vamos voltar ao nosso problema central. Para começar, não iremos utilizar um código estrutural aqui. Isto por que, a ideia aqui, não é gerar uma aplicação. Mas sim mostrar como resolver um problema. Por conta disto, precisamos elaborar um plano de ataque. E para fazer isto, o primeiro passo é definir, como seria o script inicial.

Como pode ser visto, tanto no código, quanto na animação, temos dois objetos. Sendo um deles uma linha horizontal e o outro uma linha vertical. Ambos estarão ligados ao tempo que a barra foi criada, assim como também ao preço de fechamento. E quando o script, for finalizado, e isto de maneira correta, temos que remover ambos objetos criados pelo script.

Muito bem, já temos o que seria o nosso plano inicial de ataque. Podemos então começar a implementar o que seria o código inicial. Este é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_KEY_UP          38
05. #define def_KEY_DOWN        40
06. //+----------------+
07. #define macro_NameObject  "Demo" + (string)ObjectsTotal(0)
08. //+------------------------------------------------------------------+
09. string gl_Objs[2];
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13.     int p = 0;
14.     MqlRates rate[1];
15. 
16.     ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0);
17.     ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrRoyalBlue);
18.     ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0);
19.     ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrPurple);
20. 
21.     Comment(StringFormat("Current bar analyzed: %d", p));
22.     CopyRates(_Symbol, _Period, p, rate.Size(), rate);
23.     ObjectMove(0, gl_Objs[0], 0, rate[0].time, rate[0].close);
24.     ObjectMove(0, gl_Objs[1], 0, rate[0].time, rate[0].close);
25. 
26.     Comment("");
27.     for (uint c = 0; c < gl_Objs.Size(); c++)
28.         ObjectDelete(0, gl_Objs[c]);
29.     ChartRedraw();
30. }
31. //+------------------------------------------------------------------+

Código 02

Este código 02, inicia o que é nosso código de ataque. Porém se você tentar executar ele, não irá conseguir ver de fato nenhum tipo de resultado. O motivo para isto é que os objetos são criados, posicionados e removidos antes que consigamos perceber que eles estavam de fato ali.

Mas, aqui neste código 02, quero chamar a sua atenção para uma coisa, meu caro leitor. Observe atentamente cada linha deste código 02, e veja que as mesmas linhas estão também presentes no código 01. Porém, toda via e, entretanto, aqui não conseguimos observar nada. E o motivo é que o trecho entre as linhas 21 até a linha 24, que fazem parte do tratador de evento OnChartEvent, visto no código 01. Aqui é executado de maneira muito rápida, não nos dando a oportunidade de interação. E é justamente este trecho, no qual precisamos mexer. Isto a fim de conseguir gerar o que seria o tratamento de eventos do teclado.

Perfeito. Já sabemos como seria o código inicial. Hora de separar as coisas em blocos menores. Pois criar códigos monolíticos é muito chato, além de ser muito cansativo. Assim o código 02, é modificado para o que será o código 03.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_KEY_UP          38
05. #define def_KEY_DOWN        40
06. //+----------------+
07. #define macro_NameObject  "Demo" + (string)ObjectsTotal(0)
08. //+------------------------------------------------------------------+
09. string gl_Objs[2];
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13.     Init();
14.     KeyEvent(def_KEY_DOWN);
15.     Deinit();
16. }
17. //+------------------------------------------------------------------+
18. void Init(void)
19. {
20.     ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0);
21.     ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrRoyalBlue);
22.     ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0);
23.     ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrPurple);
24. }
25. //+------------------------------------------------------------------+
26. void KeyEvent(int lparam)
27. {
28.     static int p = 0;
29.     MqlRates rate[1];
30. 
31.     switch (lparam)
32.     {
33.         case def_KEY_DOWN:
34.             p = (p < Bars(_Symbol, _Period) ? p + 1 : p);
35.             break;
36.         case def_KEY_UP:
37.             p = (p > 0 ? p - 1 : p);
38.             break;
39.         default:
40.             return;
41.     }
42.     Comment(StringFormat("Current bar analyzed: %d", p));
43.     CopyRates(_Symbol, _Period, p, rate.Size(), rate);
44.     ObjectMove(0, gl_Objs[0], 0, rate[0].time, rate[0].close);
45.     ObjectMove(0, gl_Objs[1], 0, rate[0].time, rate[0].close);
46. }
47. //+------------------------------------------------------------------+
48. void Deinit(void)
49. {
50.     Comment("");
51.     for (uint c = 0; c < gl_Objs.Size(); c++)
52.         ObjectDelete(0, gl_Objs[c]);
53.     ChartRedraw();
54. }
55. //+------------------------------------------------------------------+

Código 03

Agora sim, temos um código muito mais prazeroso. Um verdadeiro tesão. Isto por que, observando agora este código 03, você consegue notar o que precisamos de fato fazer. Precisamos que a linha 14 execute um tipo de loop, a fim de que possamos interagir controlar os objetos, feito o que pode ser visto na animação 01. Pergunta: Como podemos fazer isto? Bem, meu caro leitor, está realmente é a parte divertida. Mas antes de fazermos isto, quero lembrar a você, que loops são coisas perigosas. Já expliquei sobre isto, e forma de sair de loops de modo seguro.

E como todo e qualquer script é removido de modo permanente de um gráfico, assim que o MetaTrader 5, precisa reconstruir o gráfico do zero. Tipo, quando o usuário, pede para o MetaTrader 5 mudar o tempo gráfico. O script será removido, neste momento. Porém, e é importante você entender isto. Não basta o script ser removido. Ele precisa levar junto, os objetos que ele criou. Caso contrário, os mesmos serão recolocados no gráfico, assim que o MetaTrader 5, começar a reconstruir o gráfico.

Sei que isto parece bem desmotivador. Mas estou dizendo isto, para que você venha a prestar atenção, quando utilizar objetos dentro de um script. Se você não quer que os objetos criados pelo script, continuem no gráfico, quando o script for encerrado. Deve remover estes objetos. Caso contrário, basta ignorar esta fase da remoção, e estamos resolvidos.

Beleza, então agora vamos iniciar a questão do loop. O problema aqui, não é exatamente criar o loop. Mas sim não deixar ele muito agressivo, tomando assim muito recurso da CPU. Como eventos de teclado, por mais maluco que um usuário possa ser, não ocorrem a todo instante. Podemos adicionar algumas coisas ao corpo do loop. Assim teremos um script bem menos agressivo, em termos de consumo de recurso de CPU. Então depois de ter analisado estas possibilidades, temos o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_KEY_UP          38
05. #define def_KEY_DOWN        40
06. //+----------------+
07. #define macro_NameObject  "Demo" + (string)ObjectsTotal(0)
08. //+------------------------------------------------------------------+
09. string gl_Objs[2];
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13.     Init();
14.     while (!IsStopped())
15.     {
16.         if (TerminalInfoInteger(TERMINAL_KEYSTATE_UP))
17.             KeyEvent(def_KEY_UP);
18.         if (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN))
19.             KeyEvent(def_KEY_DOWN);
20.         Sleep(100);
21.     }
22.     Deinit();
23. }
24. //+------------------------------------------------------------------+
25. void Init(void)
26. {
27.     ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0);
28.     ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrRoyalBlue);
29.     ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0);
30.     ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrPurple);
31. }
32. //+------------------------------------------------------------------+
33. void KeyEvent(int lparam)
34. {
35.     static int p = 0;
36.     MqlRates rate[1];
37. 
38.     switch (lparam)
39.     {
40.         case def_KEY_DOWN:
41.             p = (p < Bars(_Symbol, _Period) ? p + 1 : p);
42.             break;
43.         case def_KEY_UP:
44.             p = (p > 0 ? p - 1 : p);
45.             break;
46.         default:
47.             return;
48.     }
49.     Comment(StringFormat("Current bar analyzed: %d", p));
50.     CopyRates(_Symbol, _Period, p, rate.Size(), rate);
51.     ObjectMove(0, gl_Objs[0], 0, rate[0].time, rate[0].close);
52.     ObjectMove(0, gl_Objs[1], 0, rate[0].time, rate[0].close);
53. }
54. //+------------------------------------------------------------------+
55. void Deinit(void)
56. {
57.     Comment("");
58.     for (uint c = 0; c < gl_Objs.Size(); c++)
59.         ObjectDelete(0, gl_Objs[c]);
60.     ChartRedraw();
61. }
62. //+------------------------------------------------------------------+

Código 04

Agora observe o laço na linha 14. Com isto agora temos o comportamento que pode ser visto na animação logo abaixo.

Animação 02

Legal, não é mesmo? Mas não é somente isto. Ainda tem uma outra coisa que também precisa ser mostrada. Esta pode ser vista na animação logo a seguir.

Animação 03

Perceba o seguinte, meu caro leitor, quando pedimos para modificar o tempo gráfico. O MetaTrader 5, irá destruir o gráfico e o recriar logo em seguida. Como scripts, não são recarregados por padrão, pelo MetaTrader 5, ele será encerrado, e os objetos que ele a via criado, serão removidos do gráfico. Sendo exatamente o que almejávamos fazer com base no código 04, que pode ser visto logo acima.

Mas você talvez esteja se perguntando: Por que você utilizou estas funções que podemos ver nas linhas 16 e 18? Bem, meu caro leitor, o motivo para isto, é justamente o de não usar absolutamente nada que não esteja presente na biblioteca padrão do MQL5. Como o MQL5, não fornece, por não haver necessidade, nenhuma função ou procedimento, para podermos ler diretamente as teclas pressionadas. Não temos como fazer a leitura de maneira mais genérica. Isto a fim de conseguir uma modelagem que seria feita se estivéssemos interceptando o evento ChartEvent e capturando CHARTEVENT_KEYDOWN. Como pode ser visto no código 01.

Porém, isto não significa que não poderíamos fazer este tipo de leitura do teclado. Apenas seria necessário, utilizar meios que não fazem parte do MQL5. E por consequência não irei abordar nestes artigos que focam do básico ao intermediário.

Ok, mas você pode notar que neste código 04, estamos utilizando recursos, ou uma enumeração que torna bem mais simples entender que tipo de tecla é esperada. Mas ao mesmo tempo podemos ver que não precisamos colocar as coisas necessariamente como visto no código 04. Podemos fazer algo um pouco melhor. E neste caso, como não quero mostrar uma modificação muito simples, e sem graça. Vou aproveitar e explicar um outro recurso presente e passivo de ser utilizado no MQL5. Algo que tem um objetivo muito específico e são poucas as oportunidades que temos em mostrar como tal recurso funciona. Sendo está uma delas. Então para separar de maneira adequada os assuntos. Vamos a um novo tópico.


Ponteiro para função

Esta é uma daquelas raras oportunidades que temos para explicar sobre algum recurso, com objetivo bem específico no MQL5. Como no tópico anterior implementamos uma versão alternativa, por assim dizer, do que a via sido anteriormente implementado como indicador. Temos neste momento, uma oportunidade de explicar ponteiros para funções. Um recurso muito divertido, porém que gera uma confusão sem igual na cabeça de todo iniciante em programação. Ainda mais quando a pessoa não tem certos conceitos muito bem fundamentados e esclarecidos em sua mente.

Para começar, já faz um bom tempo que não menciono a necessidade de nenhum pré-requisito para se entender um assunto. Isto porque, os assuntos estavam sempre ligados a algum artigo próximo. Porém este é um daqueles em que os conceitos a serem adotados estão em artigos relativamente distantes. No caso é Do básico ao intermediário: Variáveis (III), este artigo mencionado fecha a questão sobre conceitos envolvendo variáveis e constantes. Entender de maneira clara o conceito explicado ali, será de suma importância para entender o que iremos fazer aqui.

Para iniciarmos da forma correta, vamos ver um exemplo bem simples, a aparentemente tolo. Porém que irá nos ajudar a entender como trabalhar com este recurso do qual iremos explorar. O código inicial pode ser visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. string Msg_01(const int value)
05. {
06.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
07. }
08. //+------------------------------------------------------------------+
09. string Msg_02(const int value)
10. {
11.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
12. }
13. //+------------------------------------------------------------------+
14. void OnStart(void)
15. {
16.     Print(Msg_01(171));
17.     Print(Msg_02(-375));
18. }
19. //+------------------------------------------------------------------+

Código 05

Ok, este código 05 é muito simples, sendo assim não irei explicar o mesmo. Mas o resultado da execução é visto logo abaixo.

Imagem 01

Apesar deste código 05 ser muito simples. Ele contém elementos que nos interessa. Isto antes de vermos algo mais elaborado. A grande questão aqui é: Que tipo mecanismo podemos vir a criar, a fim de unir a função definida na linha quatro com a função definida na linha nove? Isto a fim de podermos selecionar qual função será utilizada. Mas sem usar diretamente o nome da mesma.

Bem, talvez você possa estar pensando na sobrecarga, que foi explicada no artigo Do básico ao intermediário: Sobrecarga. Porém a sobrecarga não se aplica aqui. Não da forma como queremos utilizar as coisas. Na verdade o mecanismo mais próximo do ideal, seria arrays. Nos artigos Do básico ao intermediário: Array (IV), expliquei um pouco sobre como trabalhar com arrays, de maneira básica e simples. Ali o foco era armazenar valores discretos, como inteiros e ponto flutuante. No entanto, podemos armazenar um outro tipo de informação dentro de arrays. No caso funções, ou procedimentos. Parou. Como assim? Você está maluco? Como podemos armazenar funções e procedimentos dentro de um array? Isto não faz o menor sentido.

Por conta disto que mencionei o fato de que seria necessário que você tivesse o conceito de variável bem esclarecido em sua mente, meu caro leitor. Existe um tipo de variável especial, chamada de ponteiro. Como por motivos de segurança e simplicidade, o MQL5, não implementa, ou melhor, não permite ao programador, implementar um código que utilize ponteiros. Você dificilmente irá ter contato de fato com eles. Porém, existem alguns tipos de situação, onde, mesmo aqui no MQL5, temos ponteiros sendo criados e utilizados. E como na prática, ponteiros em MQL5, não são iguais a ponteiros em C e C++. Praticamente quase não se ouve falar deles aqui.

Assim antes de você entender o que iremos fazer, preciso que você entenda que qualquer função ou procedimento, está na memória em uma dada localização. Esta localização, é armazenar em um tipo especial de variável conhecida como ponteiro. Ponteiros, são de longe a coisa mais poderosa que existe na programação. Mas ao mesmo tempo é a coisa mais confusa e complicada de se dominar. Tendo em vista que, quando manipulamos ponteiros, estamos de fato manipulando o conteúdo da memória do computador. Independentemente do tipo de dado que estamos acessando.

Ok, não vamos nos aprofundar muito nesta questão, justamente devido ao fato de que não precisamos fazer isto. Apenas preciso que você entenda isto:

Um ponteiro, é uma variável. Esta variável aponta para uma região da memória. Se esta região for um código executável. Podemos fazer com que o programa mude seu fluxo de execução para aquela região.

Entendendo isto, já é o suficiente, para o que precisamos e iremos fazer. Perfeito, agora já sabemos o que seria um ponteiro. Mas então como podemos criar, ou melhor dizendo, definir um ponteiro? Isto aqui no MQL5. Bem, esta é a parte onde precisarei que você tenha entendido, como templates funcionam. Nos artigos Do básico ao intermediário: Template e Typename (V), procurei explicar como templates funcionariam e como trabalhar com eles a fim de criar funções e procedimentos sobrecarregados. Mas, e é aqui onde a coisa fica confusa, para iniciantes. Podemos também definir templates de tipos de dados. Um ponteiro, seria justamente isto. Um template de um tipo de dado. Só que neste caso, um dado que representa um template de função ou procedimento. Xi, agora complicou, pois para mim isto não está fazendo o menor sentido.

Calma meu caro leitor. Vamos com calma. Eu sei que isto de fato é complicado inicialmente. Por isto somente agora estou mencionando e irei explicar este recurso. É preciso que você tenha conhecimento sobre diversos conceitos diferentes, para conseguir entender de fato como trabalhar com este recurso, conhecido como ponteiro.

No código 05, as funções das linhas quatro e nove, foram declaradas daquela maneira de propósito. Isto para simplificar entender o próprio conceito de template a ser usado na definição do ponteiro. Ok, se você notar irá perceber que, basicamente apenas o nome da função está mudando. O conteúdo, ou tipo de procedimento efetuado pela função não importa, o que importa é a declaração. Como a declaração, ali é muito parecida. Tanto em termos de tipo de retorno, quanto em termos de número e tipo de elementos a serem repassados para as funções. Temos um ambiente propicio para definir um ponteiro. E para fazer isto, adicionaremos a seguinte linha mostrada no código abaixo. Irei mostrar aos poucos como o código 05 irá mudar, para que você de fato consiga compreender o que estará acontecendo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. typedef string (*FnPtr)(const int);
05. //+------------------------------------------------------------------+
06. string Msg_01(const int value)
07. {
08.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
09. }
10. //+------------------------------------------------------------------+
11. string Msg_02(const int value)
12. {
13.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
14. }
15. //+------------------------------------------------------------------+
16. void OnStart(void)
17. {
18.     Print(Msg_01(171));
19.     Print(Msg_02(-375));
20. }
21. //+------------------------------------------------------------------+

Código 06

Agora largue tudo que você esteja fazendo, e que possa lhe distrair, e preste muita atenção ao que irei explicar. Isto porque, se você não entender este começo, irá ficar igual cachorro quando cai do caminhão de mudança. Note que neste código 06, adicionamos a linha quatro, que não existia no código 05. Esta linha quatro, é justamente o que cria a definição do ponteiro que iremos usar.

Observe que a declaração se parece em muito com o que existe na linha seis e onze. Sendo que foi suprimido no nome da variável, e também o nome das funções, foi substituído por um (*FnPtr). Esta parte, o tal (*FnPtr) pode ser qualquer tipo de coisa que você queira utilizar. Porém, tome cuidado ao fazer isto. Já que é justamente esta parte que precisaremos utilizar em breve.

Ok, primeira parte explicada, agora vamos declarar o array, como sendo do tipo definido na linha quatro do código 06. Lembre-se de que o que foi definido ali, foi um ponteiro. Esta segunda parte é vista no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. typedef string (*FnPtr)(const int);
05. //+------------------------------------------------------------------+
06. string Msg_01(const int value)
07. {
08.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
09. }
10. //+------------------------------------------------------------------+
11. string Msg_02(const int value)
12. {
13.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
14. }
15. //+------------------------------------------------------------------+
16. void OnStart(void)
17. {
18.     FnPtr FnMsg[2];
19. 
20.     Print(Msg_01(171));
21.     Print(Msg_02(-375));
22. }
23. //+------------------------------------------------------------------+

Código 07

Agora na linha 18, vista neste código 07, temos a definição de um array, para utilizar ponteiros do tipo definido na linha quatro. Para tornar simples as coisas, estamos definidos um array estático com dois elementos. Se você estiver na dúvida do que esteja acontecendo aqui, procure estudar os artigos anteriores. Pois não vou entrar mais em detalhes, sobre coisas já explicadas anteriormente. Perfeito, até aqui acredito que você esteja conseguindo acompanha. O próximo passo é visto no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. typedef string (*FnPtr)(const int);
05. //+------------------------------------------------------------------+
06. string Msg_01(const int value)
07. {
08.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
09. }
10. //+------------------------------------------------------------------+
11. string Msg_02(const int value)
12. {
13.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
14. }
15. //+------------------------------------------------------------------+
16. void OnStart(void)
17. {
18.     FnPtr FnMsg[2];
19. 
20.     FnMsg[0] = Msg_01;
21.     FnMsg[1] = Msg_02;
22. 
23.     Print(Msg_01(171));
24.     Print(Msg_02(-375));
25. }
26. //+------------------------------------------------------------------+

Código 08

Cara, que doideira é está? Você está maluco ou perdeu completamente o juízo? Este código 08 não irá compilar.

Mas, é claro que este código 08 irá compilar, meu caro leitor. Como assim? Você não entendeu o que foi feito aqui? Aqui estamos definindo elementos do array. Se você achou isto completamente maluco, volte ao início deste tópico, e tente entender o que foi dito. Na linha quatro estamos definindo um tipo de dado. Já na linha 18 estamos definindo uma variável que irá utilizar aquele mesmo tipo de dado definido na linha quatro. Enquanto as linhas 20 e 21, estamos atribuindo um valor aos elementos da variável declarada na linha 18. Nada além disto. Tudo muito simples e fácil de entender. A parte complicada, é justamente o que é mostrado logo no próximo passo. Veja isto sendo feito no código abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. typedef string (*FnPtr)(const int);
05. //+------------------------------------------------------------------+
06. string Msg_01(const int value)
07. {
08.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
09. }
10. //+------------------------------------------------------------------+
11. string Msg_02(const int value)
12. {
13.     return "Example message made in function " + __FUNCTION__ + " with the value " + (string)value;
14. }
15. //+------------------------------------------------------------------+
16. void OnStart(void)
17. {
18.     FnPtr FnMsg[2];
19. 
20.     FnMsg[0] = Msg_01;
21.     FnMsg[1] = Msg_02;
22. 
23.     Print(Msg_01(171));
24.     Print(Msg_02(-375));
25. 
26.     for (uchar c = 0, i = 15; c < FnMsg.Size(); c++, i += 30)
27.         Print(FnMsg[c](i));
28. }
29. //+------------------------------------------------------------------+

Código 09

Quando você executar este código 09 irá ver no terminal as seguintes mensagens sendo mostradas.

Imagem 02

Virgem santa Maria. Que coisa mais insana e maluca é está? Bem, meu caro leitor, neste ponto realmente chegamos no que é a parte realmente complicada. De certa maneira, fui um tanto quanto maldoso neste código. Já que se você, pular as etapas anteriores, e tentar entender logo de cara este código 09. Irá com toda certeza ficar mais perdido do que qualquer um já esteve nesta vida. Isto por que, você iria conseguir entender as duas primeiras linhas vista nesta imagem 02. Porém, de onde está vindo as duas outras que estou destacando? E é este o ponto. E olha que aqui no MQL5, ponteiros são entidades realmente muito simples de explicar e de entender. Mas mesmo assim, podem ser muito confusos, para quem observa um código que os utilize, sem de fato entender, que tipo de coisa está sendo implementada ali.

Note que as duas linhas destacadas na imagem 02, aparecem justamente definido a linha 27 do código 09. Mas como? Esta é a parte confusa. Como a declaração da linha 18, está fazendo uso de um tipo, que é um ponteiro. Você ao ver a linha 27, esperava obter um dado tipo de informação. Porém, o compilador entendeu que ali temos é uma chamada para uma função. Por isto a declaração está sendo feita daquela maneira. E é por isto que o resultado apresentado é visto como pode ser observado na imagem 02. Note que não importa o nome da função. A linha 27 irá conseguir saber qual função chamar, justamente por conta que nas linhas 20 e 21 atribuímos a função ao elemento do array.

Você pode estar pensando: Mas para que criar este tipo de complicação? A vida já não estava suficientemente complicada? Precisávamos de mais este tipo de coisa, podendo ser feita? Por DEUS, programadores não são pessoas normais. São todos uns loucos de pedra.

Talvez, grande parte dos programadores, em algum momento, irão de fato perder a sanidade, se tornando pessoas muito estranhas. (RISOS). Mas no geral, somos todos gente muito boa, apenas um tanto quanto excêntricos. Mas neste ponto, podemos pensar em um tipo de coisa, que antes não era possível de ser feita. Isto devido justamente ao fato de que você, meu caro leitor, ainda não havia visto o que foi mostrado neste tópico. Mas vamos agora atualizar o código 04, visto no tópico anterior, para poder utilizar o que foi visto neste tópico de agora. Com isto, teremos um código que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_KEY_UP          38
05. #define def_KEY_DOWN        40
06. //+----------------+
07. #define macro_NameObject  "Demo" + (string)ObjectsTotal(0)
08. //+------------------------------------------------------------------+
09. string gl_Objs[2];
10. //+------------------------------------------------------------------+
11. typedef void (*ProcPtr)(void);
12. typedef void (*KeyEvent)(int &);
13. //+------------------------------------------------------------------+
14. void OnStart(void)
15. {
16.     ProcPtr proc[3];
17. 
18.     proc[0] = Init;
19.     proc[1] = Loop;
20.     proc[2] = Deinit;
21. 
22.     for (uint c = 0; c < proc.Size(); c++)
23.         proc[c]();
24. }
25. //+------------------------------------------------------------------+
26. void Init(void)
27. {
28.     ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0);
29.     ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrRoyalBlue);
30.     ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0);
31.     ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrPurple);
32. }
33. //+------------------------------------------------------------------+
34. void Loop(void)
35. {
36.     KeyEvent    key[3];
37.     int         pos = 0;
38. 
39.     key[0] = Bar_NONE;
40.     key[1] = Bar_Next;
41.     key[2] = Bar_Prev;
42. 
43.     while (!IsStopped())
44.     {
45.         key[TerminalInfoInteger(TERMINAL_KEYSTATE_UP) ? 1 : (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN) ? 2 : 0)](pos);
46.         Sleep(100);
47.     }
48. }
49. //+------------------------------------------------------------------+
50. void Event(const int arg)
51. {
52.     MqlRates rate[1];
53. 
54.     Comment(StringFormat("Current bar analyzed: %d", arg));
55.     CopyRates(_Symbol, _Period, arg, rate.Size(), rate);
56.     for (uint c = 0; c < gl_Objs.Size(); c++)
57.         ObjectMove(0, gl_Objs[c], 0, rate[0].time, rate[0].close);
58. }
59. //+------------------------------------------------------------------+
60. void Deinit(void)
61. {
62.     Comment("");
63.     for (uint c = 0; c < gl_Objs.Size(); c++)
64.         ObjectDelete(0, gl_Objs[c]);
65.     ChartRedraw();
66. }
67. //+------------------------------------------------------------------+
68. void Bar_Prev(int &arg)
69. {
70.     arg = (arg < Bars(_Symbol, _Period) ? arg + 1 : arg);
71.     Event(arg);
72. }
73. //+------------------------------------------------------------------+
74. void Bar_Next(int &arg)
75. {
76.     arg = (arg > 0 ? arg - 1 : arg);
77.     Event(arg);
78. }
79. //+------------------------------------------------------------------+
80. void Bar_NONE(int &arg)
81. {}
82. //+------------------------------------------------------------------+

Código 10

O resultado da execução deste código 10, será exatamente igual ao que pode ser visto na animação 01. Portanto não irei repetir ela aqui novamente. Porém, aqui neste código 10, estou fazendo um pouco de graça. Isto para que você meu caro leitor, possa entender que nem sempre precisamos fazer as coisas de uma dada maneira. Existem uma infinidade de formas de se obter o mesmo tipo de resultado. Algumas mais simples, porém que torna incrementar o código uma tarefa um pouco mais lenta. Enquanto outras, um pouco mais complexas, que permitem incremento no código de forma muito rápida.

De qualquer maneira, ao meu ver este código 10 não contém elementos assim tão complicados de serem entendido. Ainda mais que o material neste momento, está ainda bem fresco na sua mente. No entanto, aqui neste código 10, existem alguns pontos que podem não parecer fazer muito sentido terem sido construídos da maneira como se apresentam. Então deixe-me lhe dar uma rápida explicação sobre tais pontos.

O primeiro se encontra dentro do procedimento OnStart. Aqui podemos ver que temos um array de procedimentos sendo declarados na linha 16. Logo depois nas linhas 18, 19 e 20, definimos os procedimentos que estarão em cada elemento do array. Para logo em seguida entramos em um laço a fim de executar cada um daqueles procedimentos em uma dada ordem. Sei que isto aparentemente não faz nenhum tipo de sentido em ser feito aqui. No entanto, você precisa entender que o intuito dos artigos é o de explicar e lhe fazer pensar como um programador.

Agora pense em uma série de pequenos passos, muito repetitivos e que precisam ser executados em uma certa ordem. Se você fosse digitar linha por linha, cada um dos procedimentos que precisaram ser feitos. Isto além de tomar muito tempo, tornaria a mudança e correção da ordem de execução, algo cansativo e chato. Mas se você puder utilizar uma expressão matemática, a fim de calcular índices em um array. Poderia muito bem, definir os procedimentos como sendo elementos deste mesmo array. Ao fazer isto, usaria um laço a fim de que o array fosse executado em uma determinada ordem. Coisa que de outra maneira seria impossível de ser feito.

Já no procedimento Loop, visto na linha 34, temos algo com um propósito, muito parecido. Porém, ali a questão funciona de uma maneira um pouco diferente. Neste caso, estamos definindo entre as linhas 39 e 41, alguns procedimentos que serão utilizados conforme a tecla que venha a ser pressionada. E na linha 45, usando um operador ternário, fazemos a chamada para o procedimento adequado.

Este tipo de abordagem, ou forma de pensar em como abordar um determinado tipo de problema, pode as vezes tornar uma tarefa que seria muito cansativa, em algo bem mais simples e rápido de ser feito. Já que se for necessário mudar algo, precisaremos mexer no menor número de linhas. Tornando assim, qualquer solução, algo muito rápido de ser implementado.


Considerações finais

Apesar de podermos fazer muito mais coisas do que o mostrado aqui. Tenho certeza que para muitos, o que foi visto aqui, é de fato algo completamente novo. Precisando assim ser devidamente estudado. Isto a ponto de que você, de fato venha a conseguir compreender cada ponto mostrado aqui neste artigo.

No anexo, você terá acesso aos principais códigos vistos neste artigo. Assim aproveite para estudar e praticar o que foi mostrado aqui. Além disto, procure pensar no que foi explicado nos artigos anteriores, e como aqueles mesmos fatos e conceitos, podem vir a ser utilizados em conjunto com o que foi visto aqui. Tente implementar algo, que faça uso de todo o conhecimento até aqui adquirido. E no próximo artigo, iremos voltar a falar de eventos com objetos. Só que desta vez, o tema principal será o uso do mouse, já que a parte referente ao teclado, já foi devidamente explorada.

Arquivos anexados |
Anexo.zip (3.35 KB)
Simulação de mercado (Parte 24): Iniciando o SQL (VII) Simulação de mercado (Parte 24): Iniciando o SQL (VII)
No artigo anterior terminamos de fazer as devidas apresentações sobre o SQL. Então o que eu havia me proposto a mostrar e explicar, sobre SQL, ao meu ver, foi devidamente explicado. Isto para que todos, que vierem a ver o sistema de replay / simulador, sendo construído. Consigam no mínimo terem alguma noção do que pode estar se passando ali. Devido ao fato, de que não faz sentido, programar diversas coisas, que podem ser perfeitamente cobertas pelo SQL.
Redes neurais em trading: Modelos com uso de wavelet transform e atenção multitarefa (Conclusão) Redes neurais em trading: Modelos com uso de wavelet transform e atenção multitarefa (Conclusão)
No artigo anterior, exploramos os fundamentos teóricos e começamos a implementar as abordagens do framework Multitask-Stockformer, que combina wavelet transform e o modelo multitarefa Self-Attention. Damos continuidade à implementação dos algoritmos desse framework e avaliamos sua eficácia com dados históricos reais.
Aplicando Seleção de Recursos Localizada em Python e MQL5 Aplicando Seleção de Recursos Localizada em Python e MQL5
Este artigo explora um algoritmo de seleção de recursos introduzido no artigo 'Local Feature Selection for Data Classification' de Narges Armanfard et al. O algoritmo é implementado em Python para construir modelos de classificação binária que podem ser integrados com aplicativos MetaTrader 5 para inferência.
Indicador de força e direção da tendência em barras 3D Indicador de força e direção da tendência em barras 3D
Vamos considerar uma nova abordagem para analisar tendências de mercado, baseada em visualização tridimensional e análise tensora da microestrutura do mercado.