
Simulação de mercado: Iniciando o SQL no MQL5 (IV)
Introdução
Olá pessoal, e sejam bem-vindos a mais um artigo da série sobre como construir um sistema de replay/simulação.
No artigo anterior Simulação de mercado: Iniciando o SQL no MQL5 (III), mostrei como você pode embutir o arquivo de script SQL em um executável criado dentro do MQL5. Mas não nos limitamos a fazer apenas isto. Também fizemos algumas mudanças, no código da classe, a fim de garantir que ele, fique bem mais amigável de ser utilizado. Além é claro, mudar um pouco a forma como o código estava sendo implementando.
Mas apesar de que naquele código da classe, que foi apresentado no artigo anterior, ser muito bom para nos ajudar a produzir um banco de dados. E isto usando o SQLite, que se encontra embutido no MetaTrader 5. E mais para frente falarei um pouco mais a este respeito. O código daquela classe, não nos permite de maneira alguma executar pesquisas usando para isto o MQL5.
Então o principal foco, neste artigo de agora, será explicar, da melhor maneira que for possível. Como você poderá modificar aquela classe, a fim de adicionar o código necessário para que pesquisas sejam feitas nele. Ou seja, o que iremos de fato fazer neste artigo, será a implementação de um sistema, a fim de pesquisar dentro do banco de dados, que faz uso do SQLite.
Mas antes de começarmos a ver como isto será feito. Precisamos entender uma coisa, que é muito, mas muito importante, quando você for usar o que estou mostrando nesta sequência. E é justamente por conta de coisas como a que será mostrada, é que não estamos ainda fazendo uso do SQLite, ainda junto com o código do sistema de replay/simulador. Isto por que, queremos explicar, e deixar bastante claro, como de fato o SQLite, que se encontra embutido no MetaTrader 5, funciona de fato.
Muitos podem imaginar que o SQLite, presente no MetaTrader 5, funciona igual ao SQLite que você pode conseguir usando uma DLL. Mas, apesar de ambos funcionarem basicamente da mesma forma. Existem alguma peculiaridade no uso, do SQLite, que está no MetaTrader 5. Talvez os desenvolvedores venham a mudar isto no futuro. Porém, você não deve de maneira alguma imaginar que algo estará ou não acontecendo. Então, na dúvida teste primeiro. E depois procure ver como resolver algum tipo de problema, que por ventura vier a surgir. Isto quando vier a fazer uso do SQLite, que estiver embutido em alguma aplicação em particular. Não estou aqui dizendo que o problema está nesta ou naquela aplicação. Estou dizendo para você testar as coisas antes de imaginar que o SQLite usado, não é o que você esperava.
Entendendo o problema
O problema em si, e este no momento em que escrevo este artigo, acontece. Isto na versão 5.00 Build 3815 de 22 de Junho de 2023. E sim meu caro leitor, este código que você está vendo neste artigo, já tem um bom tempo que foi criado. Nesta data que você, meu caro leitor, esteja lendo este artigo, posso lhe garantir que o MetaTrader 5, já foi atualizado. E tudo isto que estou mostrando já se encontra obsoleto. Mas nem por isto, você deve ignorar o conteúdo destes artigos. Já que, eles podem lhe ensinar algo que você não imagina ser possível de ser feito.
Ok, vamos então ao problema em questão. Quero que você observe atentamente o código de script em SQL, visto logo abaixo.
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Symbols; 04. DROP TABLE IF EXISTS tb_Quotes; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TABLE IF NOT EXISTS tb_Quotes 13. ( 14. of_day NOT NULL, 15. price NOT NULL, 16. fk_id NOT NULL, 17. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 18. );
Código em SQL
Este código, irá criar um sistema de tabelas, onde existirá uma relação entre a tabela tb_Symbols e a tabela tb_Quotes. Isto é garantido pela presença da chave estrangeira definida na linha 17. Se você não sabe do que estou falando, procure ler os artigos anteriores, desta mesma sequência de artigos. Para compreender o que será explicado daqui a pouco.
Pois bem, agora observe a linha três e a linha quatro, deste mesmo script mostrado acima. O que estas duas linhas fazem, é justamente remover as tabelas de dentro do arquivo de banco de dados SQL. Se você executar isto, seja no MetaTrader 5, ou usando uma DLL, do SQLite. Irá obter o mesmo resultado. Porém, nem sempre. E o motivo é justamente a bendita linha 17, que cria uma relação entre os valores presentes na tabela tb_Symbols e na tabela tb_Quotes.
Mas espere um pouco. Como assim, nem sempre teremos o mesmo resultado? O SQLite não é o mesmo encontrado tanto no MetaTrader 5, quanto o que viermos a usar via DLL? Por que você diz haver uma diferença no resultado da execução deste script acima? De fato, talvez eu tenha me expressado mal, ou no mínimo falei algo antes de explicar todos os por menores. De fato, a execução terá o mesmo resultado. Mas, porém, toda via e entretanto, quando existir um valor que cria uma relação entre as tabelas tb_Symbols e tb_Quotes. O SQLite, presente no MetaTrader 5, não irá executar com sucesso a remoção das tabelas. Sendo assim gerado um erro, que você pode constatar se fizer o seguinte teste.
Use o script que você pode ver logo abaixo, e o execute por duas vezes seguidas. Já que na primeira vez, ele será executado sem nenhum erro sendo reportado. Porém quando você o executar pela segunda vez irá ter como resultado a imagem que vem logo abaixo do código do script.
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Symbols; 04. DROP TABLE IF EXISTS tb_Quotes; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TABLE IF NOT EXISTS tb_Quotes 13. ( 14. of_day NOT NULL, 15. price NOT NULL, 16. fk_id NOT NULL, 17. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 18. ); 19. 20. INSERT INTO tb_Symbols (id, symbol) VALUES (1, 'PETR4'); 21. 22. INSERT INTO tb_Quotes (of_day, price, fk_id) VALUES ('2023-07-10', '22.00', 1);
Código em SQL
Observe, na imagem acima, que em menos de 3 segundos, entre uma execução e outra, tivemos resultados completamente diferentes. Isto por que na primeira tentativa, conseguimos obter sucesso na execução do script SQL. Porém ao tentarmos imediatamente após termos tido sucesso, executar o mesmo script, o mesmo obteve falha em sua execução. O observando o código de erro que está sendo reportado. Podemos ver nitidamente que o problema todo se deu justamente por conta que existe uma relação entre as tabelas. E tal relação nasceu justamente por conta da linha 17 vista no script acima.
Mas não foi justamente por conta da linha 17 que tivemos o erro. Observe novamente a imagem acima. Note que o nosso código da classe, que tem como objetivo nos permitir executar arquivos de script em SQL. Está reportando que o problema foi gerado no momento em que a linha três foi enviada para o SQL a executar.
No entanto, quero ressaltar uma coisa neste ponto. Se você enviar este mesmo arquivo de script SQL para ser executado pelo SQLite. Ele irá de fato executar o código. Não será reportado nenhum erro na execução. Já mostrei isto antes, quando foi feita a introdução de como programar em SQL. Na dúvida leia os artigos anteriores a este. Pois lá mostro claramente o que acabei de mencionar.
Então você pode imaginar que o problema está no SQLite, que se encontra no MetaTrader 5. Mas não quero colocar lenha na fogueira. A verdade é que talvez, o SQLite que se encontra embutido no MetaTrader 5, possa ter um mecanismo de segurança, que impeça que tabelas que possuam chaves relacionadas não sejam de fato destruídas. Não sei qual é o real motivo para esta aparente quebra de simetria entre o que é feito pelo SQLite e o SQLite embutido no MetaTrader 5. No entanto, não é somente isto que existe de diferenças entre ambos. Existem outros pontos onde também haverá tal quebra de simetria, como por exemplo, quando você tenta criar uma Trigger, ou gatilho usando o SQLite presente no MetaTrader 5.
Por estes motivos, eu não lhe jugaria pelo fato de você, caro leitor, venha a decidir fazer uso do SQLite, via DLL. No entanto, até onde pude experimentar o SQLite embutido no MetaTrader 5. Poderemos fazer muitas coisas sem ter que recorrer ao uso do SQLite via DLL. Mas se viermos a notar que a quebra de simetria se torne muito grande. Tentaremos um outro caminho. Mas por hora, podemos nos manter usando o SQLite embutido no MetaTrader 5.
Então, caso você esteja fazendo uso do SQLite, embutido no MetaTrader 5. E precisa remover as tabelas que estão presentes dentro do banco de dados. A melhor maneira, é de fato, deletar o arquivo de banco de dados. Assim, quando o script que estiver no executável, vier a ser novamente executado, ele não terá problemas em sua execução.
Muito bem. Se você entendeu claramente o que foi explicado acima. Podemos passar para a próxima etapa. Pois a coisa irá ficar bastante interessante. E neste caso sugiro a você, caro leitor e entusiasta. A prestar muita, mas muita atenção ao que explicarei.
Pois, no artigo Simulação de mercado (Parte 25): Iniciando o SQL no MQL5 (I), eu menciono que precisamos de poucas funções das disponíveis dentro do MQL5, para trabalhar com o SQL. Sendo necessárias seis funções. Já mostrei, nos artigos anteriores o uso de três destas seis funções. E as mesmas já se encontram implementadas no código da classe C_DB_SQL. Agora chegou a hora de conhecer e entender por que precisamos de apenas mais três funções. Porém, para separar adequadamente a explicação. Vamos a um novo tópico.
As últimas três funções necessárias
O que explicarei aqui, não é de fato, algo que você deva se sentir menosprezado por não ter compreendido antes. Isto se você já faz uso de banco de dados via MQL5. E tão pouco, você caro leitor, que esteja iniciando seu aprendizado, deve considerar como sendo uma verdade absoluta. O que mostrarei, é apenas e tão somente, a forma como eu faço as coisas. Não é a maneira mais correta, e tão pouco a maneira menos adequada. É somente a maneira que eu faço as coisas.
Quando eu digo, que precisamos apenas e somente de seis funções do MQL5, a fim de poder acessar o SQL. Não estou dizendo, que se você faz uso de mais funções ou procedimentos, disponíveis no MQL5, você esteja errado. Pelo contrário. Até admiro o fato de muitos conseguirem explorar, de uma forma mais ampla uma dada linguagem ou ferramenta. Mas a minha forma de agir, é sempre usando o mínimo do mínimo possível.
Não tenho paciência em aprender cada detalhe, de cada função presente, em cada uma das linguagens, ou ferramentas que utilizo. Isto por que, para realmente fazer isto, precisamos de tempo, a fim de aprender todos os detalhes. E muitas das vezes não tenho este tempo disponível. Tenho um prazo e gosto de cumprir o mesmo. Não importando o nível de dificuldade a ser assumida.
Assim, para que possamos fazer as devidas apresentações, e desta maneira, verificar se estou ou não correto na minha abordagem. Vamos modificar, novamente o script do banco de dados, porém sugiro que você faça diferente. Isto para evitar ficar viciado em fazer as coisas de uma forma que muitas das vezes não é adequada. Minha sugestão é que você crie um novo script, e o chame de script 02.sql. Mas se você desejar, pode colocar o conteúdo dele no final do script 01.sql. A escolha é sua. Porém o resultado obtido será exatamente igual.
O único detalhe, ao fazer uso desta minha sugestão, é que você precisará, fazer uma pequena mudança no código do arquivo principal. Como nem todos saberão como fazer isto. Focaremos justamente nesta solução. Assim mostro como você deverá proceder, para obter o resultado adequado.
A ideia principal, e o foco, neste ponto, é justamente lançar dados, ou registros dentro do banco de dados. Isto para que possamos fazer futuras pesquisas. Observe que os scripts SQL mostrados abaixo.
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. CREATE TABLE IF NOT EXISTS tb_Symbols 04. ( 05. id PRIMARY KEY, 06. symbol NOT NULL UNIQUE 07. ); 08. 09. CREATE TABLE IF NOT EXISTS tb_Quotes 10. ( 11. of_day NOT NULL, 12. price NOT NULL, 13. fk_id NOT NULL, 14. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 15. );
Código em SQL
01. INSERT INTO tb_Symbols (id, symbol) VALUES 02. (2, 'PETR4'), 03. (1, 'ITUB3'), 04. (3, 'VALE3'); 05. 06. INSERT INTO tb_Quotes (of_day, price, fk_id) VALUES 07. ('2023-07-10', '22.00', 1), 08. ('2023-07-11', '22.20', 1), 09. ('2023-07-12', '22.40', 1), 10. ('2023-07-13', '22.30', 1), 11. ('2023-07-14', '22.60', 1), 12. ('2023-07-10', '26.00', 2), 13. ('2023-07-11', '26.20', 2), 14. ('2023-07-12', '26.40', 2), 15. ('2023-07-13', '26.30', 2), 16. ('2023-07-14', '26.60', 2), 17. ('2023-07-10', '62.00', 3), 18. ('2023-07-11', '62.20', 3), 19. ('2023-07-12', '62.40', 3), 20. ('2023-07-13', '62.30', 3), 21. ('2023-07-14', '62.60', 3);
Código em SQL
Note que apesar de serem dois, você poderá unir o conteúdo de ambos. E fazer uso do arquivo principal escrito em MQL5, que já foi mostrado até neste momento. Mas caso queira usar, os scripts em SQL de forma separada, você precisará modificar o código principal em MQL5, conforme é visto abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Basic script for SQL database written in MQL5" 04. #property version "1.00" 05. #property script_show_inputs 06. //+------------------------------------------------------------------+ 07. #resource "\\Files\\Script 01.sql" as string SQL_Create 08. #resource "\\Files\\Script 02.sql" as string SQL_Insert 09. //+------------------------------------------------------------------+ 10. #include <Market Replay\SQL\C_DB_SQL.mqh> 11. //+------------------------------------------------------------------+ 12. input string user01 = "DataBase01"; //Database File Name 13. //+------------------------------------------------------------------+ 14. C_DB_SQL *SQL; 15. //+------------------------------------------------------------------+ 16. const string ExecScripts(void) 17. { 18. string szMsg = (*SQL).ExecResourceSQL(SQL_Create); 19. if (szMsg != NULL) return szMsg; 20. return (*SQL).ExecResourceSQL(SQL_Insert); 21. }; 22. //+------------------------------------------------------------------+ 23. void OnStart() 24. { 25. string szMsg; 26. 27. SQL = new C_DB_SQL(user01); 28. 29. szMsg = ExecScripts(); 30. Print(szMsg == NULL ? "Result of executing the SQL script: Success" : szMsg); 31. 32. delete SQL; 33. } 34. //+------------------------------------------------------------------+
Código em MQL5
Observe como é tudo muito simples, prático e direto de ser feito. O primeiro script SQL irá criar as tabelas que usaremos. O segundo script em SQL, irá preencher o banco de dados com alguns valores. Você pode notar que estes mesmos scripts em SQL foram mostrados anteriormente. Então sabiamente você, já deve saber como cada um deles irá trabalhar, e como eles trabalharão juntos para compor os registros que criaremos.
Pois bem, agora observe o código em MQL5. Note que ali adicionamos a linha oito, e mudamos o nome do recurso anteriormente existente para um nome mais adequado. Por conta de alguns detalhes na linha 29, chamamos a função presente na linha 16. Esta tem como objetivo, fazer com que os dois scripts SQL. Que na verdade serão recursos internos do executável final, criado pelo MQL5. Serão executados. Primeiro na linha 18, fazemos o pedido de execução do script SQL, que tem como objetivo, criar as tabelas para nós. Em seguida, na linha 19, testamos o resultado da execução. Caso tenhamos sucesso em executar o primeiro script SQL. Faremos o pedido de execução do segundo script. Isto na linha 20.
Este segundo script SQL, tem como objetivo lançar para dentro do banco de dados, os registros que ali constam. Algo simples e sem grandes dificuldades em sua execução. Porém, neste momento, já temos o que precisamos para a próxima etapa de implementação. Ou seja, já podemos desenvolver a parte onde faremos pesquisas dentro o banco de dados SQL. Isto via código escrito em MQL5. Mas primeiro, vamos ver o que existe dentro do arquivo de banco de dados. Para isto, podemos usar de forma bastante conveniente o MetaEditor conforme mostrado abaixo.
Note que o conteúdo esperado, de fato foi adicionado ao banco de dados. Então, tendo esta certeza, podemos passar para a próxima etapa. Assim sendo, vamos voltar ao arquivo de cabeçalho C_DB_SQL.mqh. Lembre-se de que, originalmente a classe presente neste arquivo, não consegue retornar nenhum dado de pesquisa. Apesar de que, você pode fazer pesquisas no SQL, usando o comando SELECT, como foi explicado, antes de começarmos a trabalhar no código MQL5. Para mais detalhes, veja os artigos anteriores desta mesma seria de artigos. Mas nada adianta podemos fazer as pesquisas em SQL, se no entanto, não pudermos de maneira alguma usar os dados diretamente no código em MQL5.
Mas de uma forma, bastante inteligente, os desenvolvedores da linguagem MQL5, nos propuseram usar algumas funções para que obtivéssemos os resultados de pesquisas em banco de dados SQL. Lembrando mais uma vez, que o conteúdo a ser mostrado aqui, visa o fato de você vir a fazer uso do SQLite presente no MetaTrader 5. Caso você esteja fazendo uso, mesmo do SQLite, mas usando para isto uma DLL, a forma de fazer as coisas será um pouco diferente. Talvez futuramente eu venha a mostrar como fazer isto. Mas por hora, vamos fazer uso do SQLite presente no MetaTrader 5. Assim como também, a forma de se obter os mesmos resultados, quando usamos um banco de dados SQL, via soquetes é diferente da que será mostrada aqui. Então não confunda as coisas. Cada problema envolve uma solução diferente, mesmo que os resultados possam ser os mesmos.
Muito bem, dito isto, podemos focar no código em si. Precisamos de alguma forma, fazer com que as pesquisas sejam executadas e que os resultados possam ser facilmente compreendidos quando necessário. Para fazer isto, precisamos usar duas novas funções. A primeira é, a função DatabasePrepare, cujo objetivo, é justamente executar algum tipo de pesquisa no banco de dados. Note que não devemos usar a função DatabaseExecute, como muitos poderiam esperar ou cogitar. O motivo, é que a função DatabaseExecute, apenas nos retorna se o comando SQL foi ou não devidamente executado. Ela não nos promove uma forma se observar o que o SQL estará nos retornando quando fizermos algum requerimento para fins de pesquisa.
Então em seu lugar, usamos a função DatabasePrepare, pois esta irá nos propiciar, uma forma de observar o que o SQL, estará retornando para a nossa aplicação criada em MQL5. Esta é a primeira parte, e a primeira função que precisamos. Além desta, precisaremos de uma outra função, cujo objetivo é de fato ler os dados retornados pelo SQL. Mas primeiro vamos ver como o código da classe foi modificado, para comportar a função DatabasePrepare. O código da classe pode ser visto logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Service Graphics\Support\C_Array.mqh" 005. //+------------------------------------------------------------------+ 006. class C_DB_SQL 007. { 008. private : 009. C_Array m_Arr; 010. int m_handleDB, 011. m_Request; 012. //+------------------------------------------------------------------+ 013. void Convert(const char &buff[], const int size) 014. { 015. string sz0 = ""; 016. bool b0, b1, bs1, bs2, bc0, bc1, bc; 017. int nLine = 1; 018. 019. b0 = b1 = bs1 = bs2 = bc0 = bc1 = bc = false; 020. for (int count = 0, nC0 = nLine; count < size; count++) 021. { 022. switch (buff[count]) 023. { 024. case '\t': 025. sz0 += (bs1 || bs2 ? "\t" : ""); 026. break; 027. case '\n': 028. nC0++; 029. case '\r': 030. bc0 = false; 031. break; 032. case ';': 033. b0 = (bs1 || bs2 || bc0 || bc1 ? b0 : true); 034. default: 035. switch (buff[count]) 036. { 037. case '"': 038. bs1 = (bs2 || bc0 || bc1 ? bs1 : !bs1); 039. break; 040. case '\'': 041. bs2 = (bs1 || bc0 || bc1 ? bs2 : !bs2); 042. break; 043. } 044. if (((count + 1) < size) && (!bs1) && (!bs2)) 045. { 046. if (bc = ((buff[count] == '-') && (buff[count + 1] == '-'))) bc0 = true; 047. if (bc = ((buff[count] == '/') && (buff[count + 1] == '*'))) bc1 = true; 048. if (bc = ((buff[count] == '*') && (buff[count + 1] == '/'))) bc1 = false; 049. if (bc) 050. { 051. count += 1; 052. bc = false; 053. continue; 054. } 055. } 056. if (!(bc0 || bc1)) 057. { 058. if ((!b1) && (buff[count] > ' ')) 059. { 060. b1 = true; 061. nLine = nC0; 062. } 063. sz0 += (b1 ? StringFormat("%c", buff[count]) : ""); 064. } 065. } 066. if (b0) 067. { 068. m_Arr.Add(sz0, nLine); 069. sz0 = ""; 070. b0 = b1 = false; 071. } 072. } 073. } 074. //+------------------------------------------------------------------+ 075. const string ExecSQL(void) 076. { 077. string szCmd; 078. 079. for (int count = 0, nLine; count >= 0; count++) 080. { 081. szCmd = m_Arr.At(count, nLine); 082. if (nLine < 0) break; 083. if (!ExecCommandSQL(szCmd)) 084. return StringFormat("Execution of line %d of the SQL script failed...", nLine); 085. } 086. 087. return NULL; 088. } 089. //+------------------------------------------------------------------+ 090. public : 091. //+------------------------------------------------------------------+ 092. C_DB_SQL(const string szFileName = ":memory:") 093. { 094. m_handleDB = DatabaseOpen(szFileName, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE); 095. m_Request = INVALID_HANDLE; 096. } 097. //+------------------------------------------------------------------+ 098. ~C_DB_SQL() 099. { 100. DatabaseClose(m_handleDB); 101. } 102. //+------------------------------------------------------------------+ 103. const string ExecResourceSQL(const string szResource) 104. { 105. char buff[]; 106. int size; 107. 108. ArrayResize(buff, size = StringLen(szResource)); 109. StringToCharArray(szResource, buff); 110. Convert(buff, size); 111. ArrayFree(buff); 112. 113. return ExecSQL(); 114. } 115. //+------------------------------------------------------------------+ 116. const string ExecScriptSQL(const string szFileName) 117. { 118. int file, size; 119. char buff[]; 120. 121. if ((file = FileOpen(szFileName, FILE_READ | FILE_BIN)) == INVALID_HANDLE) 122. return StringFormat("Unable to open script file: %s", szFileName); 123. ArrayResize(buff, size = (int) FileSize(file)); 124. FileReadArray(file, buff); 125. FileClose(file); 126. Convert(buff, size); 127. ArrayFree(buff); 128. 129. return ExecSQL(); 130. } 131. //+------------------------------------------------------------------+ 132. bool ExecCommandSQL(const string szCmd) 133. { 134. return (m_handleDB == INVALID_HANDLE ? false : DatabaseExecute(m_handleDB, szCmd)); 135. } 136. //+------------------------------------------------------------------+ 137. bool ExecRequestOfData(const string szCmd) 138. { 139. if (m_Request != INVALID_HANDLE) DatabaseFinalize(m_Request); 140. return ((m_Request = DatabasePrepare(m_handleDB, szCmd)) != INVALID_HANDLE); 141. } 142. //+------------------------------------------------------------------+ 143. }; 144. //+------------------------------------------------------------------+
Código de C_DB_SQL.mqh
Note que adicionamos uma nova variável na linha 11. Esta será a variável, a ser usada para o propósito de ler, o que o SQL estará nos reportando em um requerimento de pesquisa. Como toda variável, ela será inicializada, na linha 95. Observe bem o valor que estamos utilizando. Agora vamos para a linha 137, onde temos a função ExecRequestOfData, sendo implementada. Esta função é a que usaremos para enviar o comando de pesquisa ao SQL. Normalmente este comando envolve o uso de um SELECT FROM, mas como você verá depois. A coisa aqui pode ser algo bastante específico.
De qualquer maneira, observe atentamente o fato de que na linha 139, verificaremos se a variável m_Request tem um valor diferente de INVALID_HANDLE. Caso isto se confirme significa que temos uma pesquisa anterior em cache. Ou seja, queremos que os dados de uma pesquisa anterior, sejam descartados, para que uma nova pesquisa venha a ser feita. Assim fazemos uso de uma outra função, a DatabaseFinalize. Esta tem como objetivo, descartar os dados de uma pesquisa anterior. Sendo desta forma a terceira função da qual precisaremos usar nesta fase de incrementar a classe C_DB_SQL.
Uma vez que a antiga pesquisa, caso existisse uma, foi finalizada. Podemos na linha 140, enviar o pedido para uma nova pesquisa. Observe que não precisamos informar o nome do banco de dados. Isto por que, tal coisa foi feita no momento em que a classe foi criada. Tudo que precisamos é informar, qual o comando a ser utilizado. E este virá do chamador que veremos depois. Se a pesquisa for bem-sucedida, a função DatabasePrepare, irá retornar um valor numérico. Este será gravado na variável m_Request para uso posterior. Caso tenhamos uma falha na pesquisa, termos um valor INVALID_HANDLE sendo retornado. E por consequência a função ExecRequestOfDate, retornará um valor falso. Indicando ao chamador que ocorreu uma falha na pesquisa requisitada.
Muito bem, mas agora temos um detalhe: Por que não retornamos diretamente os dados aqui na função ExecRequestOfData? Por que retornamos apenas se a pesquisa foi bem, ou mal sucedida? O motivo disto é puramente simplicidade. Talvez por um motivo ou outro, você deseje fazer uma pesquisa que retorne certos valores, ou campos da tabela. E dependendo o que esteja em um campo ou outro, poderemos ter direções, ou decisões diferentes sendo tomadas. O que nos forçaria a fazer uma nova pesquisa no banco de dados. Tal coisa, apesar de não ser incorreta, faz com que o banco de dados, principalmente no caso de um banco cliente - servidor. Seja requisitado de forma desnecessária.
Podemos buscar todos os campos necessários em uma única pesquisa. Isto além de otimizar o trabalho do SQL, no que rege pesquisar o banco, também nos auxilia a agilizar um pouco o nosso código principal. Por conta disto, a leitura dos dados é colocada em uma outra função voltada apenas para este propósito.
Isto pode parecer uma tremenda de uma bobagem. E até pouco prático. Mas considere o fato de que você poderá adaptar muito facilmente, o código principal a fim de conseguir ler um determinado campo específico, apenas pelo fato de que a pesquisa estará separada da leitura do resultado obtido. Isto parece ser algo muito complicado de ser apresentado. Já que quando efetuamos uma pesquisa no SQL, podemos ter como resultado, diversos valores sendo retornados, isto usando apenas uma das colunas, de uma das tabelas. Isto conforme foi mostrado na animação que você pode ver um pouco acima neste mesmo artigo.
Mas se a pesquisa for bem dirigida e orientada de maneira correta, você muita das vezes obterá apenas um único resultado. Mas também poderá não obter resultado algum. Isto devido ao fato de que os critérios utilizados para a pesquisa, não foram encontrados dentro do banco de dados.
Aqui surge um fato, do qual poucos conseguem realmente compreender. E antes de mostrar a função de leitura dos dados. Gostaria de aproveitar a oportunidade e explicar tal coisa. Pois pode ser que você venha a se interessar pelo assunto.
Muitos costuma subutilizar o SQL, ou mesmo não fazer uso dele, devido a uma má compreensão de como ele realmente funciona. Quando pesquisamos dentro de um banco de dados SQL. Não queremos necessariamente saber de uma resposta genérica. Podemos em alguns casos, estar buscando uma resposta bastante objetiva e prática. Se você criar um banco de dados, com uma certa estruturação e modelagem. Poderá colocar, virtualmente qualquer tipo de informação dentro do banco de dados. Este fato é bastante curioso. Já que muitas das vezes, as pessoas costumam pensar em banco de dados, como sendo apenas um conjunto de números e carácteres alfanuméricos. Que objetivam armazenar dados de vendas, clientes, produtos ou coisas similares.
Mas se você entender adequadamente como o SQL funciona, e entender como fazer as coisas nele. Você poderá criar, um sistema, ou melhor dizendo, um pequeno robô, que será capaz de aprender a operar um dado ativo. Isto sem que você necessite de fato, criar um modelo operacional. Você simplesmente coloca uma série de cotações dentro do banco de dados e usando o SQL para pesquisar. E mais o próprio MQL5, a fim de executar os cálculos que o SQL não consegue fazer. Criar um mecanismo que consegue aprender de forma totalmente automática como operar no mercado. Tal mecanismo aprenderá por conta própria a criar uma maneira de prevê possíveis, futuros movimentos de um dado ativo.
Eu mencionei isto a algum tempo, nesta mesma sequência sobre o replay/simulador. Talvez você não tenha visto, ou notado até onde podemos chegar com este simples sistema que estou mostrando como criar. Mas se você o estudar de maneira adequada, conseguirá criar o tal mecanismo mencionado. É claro, que você precisará fazer uso de algumas técnicas matemáticas e de programação a fim de conseguir o resultado adequado. Mas daí, vem o motivo pelo qual, não retornamos diretamente o resultado de uma pesquisa no banco de dados SQL. Isto quando a executamos via MQL5, como mostrado no código acima.
Pode ser que tal pesquisa, tenha sido feita de tal maneira que podemos usar os resultados de uma forma completamente diferente do que muitos esperariam. Como os desenvolvedores do MQL5, permitiram que pudéssemos fazer uso de tal mecanismo. Não acho prudente mudar a forma como tal mecanismo funciona. E como a explicação da forma que tal leitura se dará é algo um tanto quando complicado de explicar em poucas palavras. Ficará para o próximo artigo, tal explicação. Assim como as mudanças que deverão ser feitas no código principal.
Considerações finais
Neste artigo, falei sobre como podemos fazer algumas coisas. Mencionei o fato de você poder criar um robô, capaz de aprender a operar, sem que você precise de fato criar um modelo operacional para tal coisa. Falei sobre o problema que existe quando tentamos usar algumas funções do SQL diretamente no SQLite presente no MetaTrader 5. E mostrei como você pode contornar tal coisa. Mas apesar de tudo, não mostrei como de fato a leitura dos resultados de uma pesquisa podem ser obtidos. Isto usando o próprio MQL5. O motivo de tal coisa, é que podemos fazer uma função que se auto adapte ao que é esperado como resultado de uma pesquisa. Isto para que você consiga compreender, o porquê de eu ter dito que precisamos de apenas seis funções, que o MQL5 nos permite fazer uso, quando estamos trabalhando com SQL.
Um último detalhe: O MQL5, já conta com mecanismos para poder usar modelagem ONNX. A modelagem ONNX é bem mais adequada que o SQL para fins de criar um robô capaz de aprender a operar no mercado sozinho. Mas, nada impede de você usar uma modelagem SQL para tal propósito. Tudo é uma questão de aprender e aplicar o conhecimento de maneira correta.
Arquivo | Descrição |
---|---|
Experts\Expert Advisor.mq5 | Demonstra a interação entre o Chart Trade e o Expert Advisor (É necessário o Mouse Study para interação) |
Indicators\Chart Trade.mq5 | Cria a janela para configuração da ordem a ser enviada (É necessário o Mouse Study para interação) |
Indicators\Market Replay.mq5 | Cria os controles para interação com o serviço de replay/simulador (É necessário o Mouse Study para interação) |
Indicators\Mouse Study.mq5 | Permite interação entre os controles gráficos e o usuário (Necessário tanto para operar o replay simulador, quanto no mercado real) |
Services\Market Replay.mq5 | Cria e mantém o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema) |
Code VS C++\Servidor.cpp | Cria e mantém um soquete servidor criado em C++ (Versão Mini Chat) |
Code in Python\Server.py | Cria e mantém um soquete em python para comunicação entre o MetaTrader 5 e o Excel |
Indicators\Mini Chat.mq5 | Permite implementar um mini chat via indicador (Necessário uso de um servidor para funcionar) |
Experts\Mini Chat.mq5 | Permite implementar um mini chat via Expert Advisor (Necessário uso de um servidor para funcionar) |
Scripts\SQLite.mq5 | Demonstra uso de script SQL por meio do MQL5 |
Files\Script 01.sql | Demonstra a criação de uma tabela simples, com chave estrangeira |
Files\Script 02.sql | Demonstra a adição de valores em uma tabela |
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso