preview
Simulação de mercado: Iniciando o SQL no MQL5 (III)

Simulação de mercado: Iniciando o SQL no MQL5 (III)

MetaTrader 5Testador |
134 0
Daniel Jose
Daniel Jose

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 (II), vimos como poderíamos desenvolver uma classe em MQL5, que seria capaz de nos dar algum suporte. Cuja finalidade, se dá justamente para que possamos colocar o código SQL dentro de um arquivo de script. Isto de forma que não precisaríamos, ter que digitar o mesmo código em uma string, no código MQL5. Mas apesar de daquela solução, ser funcional. Ela contém alguns detalhes, que podemos melhorar e devemos melhorar. Isto para que as coisas fiquem mais agradáveis, ou no mínimo mais amigáveis, quando formos fazer um uso mais massivo do SQL.

Porém, aqui neste artigo, não faremos apenas tais melhorias. Visto que tal tarefa se dará de forma bastante rápida, e sem grandes transtornos. Vamos, além disto, ver também como podemos embutir os arquivos de script SQL, em um executável feito em MQL5. Isto por que, muitos podem desejar, que o código SQL, faça parte do executável final. E não seja algo, do qual um usuário possa vir a modificar ou mesmo deletar, sem saber realmente do que se trata.

Além do fato, de que quando embutimos o arquivo, que no caso será um arquivo de script em SQL, dentro do executável. Passamos a ter menos uma preocupação, quando formos fornecer o programa para um outro usuário. Isto por que, o próprio executável, já conterá os dados esperados e necessários para que a tarefa proposta seja de fato cumprida. Não vou, e não serei eu que irá lhe dizer para não fazer tal coisa. A decisão de colocar ou não de forma embutida, o arquivo, que no caso será o script em SQL, no executável. Depende única e exclusivamente de você, isto como programador. Já que cada caso, é um caso e exige uma solução específica para que seja corretamente solucionado.

Mas saber como embutir e utilizar scripts SQL, em um executável criado em MQL5, não é uma tarefa complexa. Mas envolve saber alguns detalhes para que tudo funcione perfeitamente bem. Muito bem, então não vamos nos alongar muito, em explicações e preparações. Vamos direto ao que interessa, pois teremos muito trabalho neste artigo, a fim de preparar as coisas para os próximos.


Modificando o esquema de classes

Apesar de à primeira vista, o esquema feito no artigo anterior, ser algo interessante. Ele contém alguns problemas, que dificulta o uso mais intensivo de arquivos de script em SQL. Mesmo que você não venha de fato fazer uso de arquivos de script. Mantendo-se usando apenas linhas de comando em SQL. A forma como as coisas foram esquematizadas no artigo anterior. Dificulta você fazer certas coisas. Uma delas é mudar de forma simples, a direção que está sendo tomada durante a implementação do código principal. Para deixar isto mais claro. Pense por um instante no seguinte: O esquema das classes e a forma de elas trabalharem, não nos permite, usar alguns recursos de programação. Já que tudo estará direcionado para o terminal. Ou para ser usado no SQLite, que faz parte do MetaTrader 5.

Porém, se você desejar criar uma versão particular do SQLite, usando para isto o código fonte, disponível na WEB. E fazer com que este SQLite particular, seja usado junto ao MetaTrader 5. Terá algumas dificuldades, da forma como o código foi apresentado. Mas também temos um outro problema. Se você desejar usar, um sistema de caixa de mensagens, estilo MessageBox, presente no Windows, a fim de informar algo ao usuário. Terá muitos problemas ao usar o esquema apresentado no artigo anterior. Mas solucionar isto, neste momento, é uma tarefa bastante simples. E como neste ponto, ainda estamos esquematizando o que será de fato usado, quando formos usar o SQL no replay/simulador. Precisamos providenciar as modificações neste ponto do desenvolvimento. Isto para não sofremos muito, durante a portabilidade para dentro do replay/simulador.

Assim, a primeira coisa a ser feita, será matar o arquivo de cabeçalho C_DB_SQLite.mqh. Isto por que, faremos a esquematização das coisas de maneira a podermos no futuro usar uma outra implementação do SQL. Uma que seja do tipo cliente-servidor. Mas isto é uma questão a ser feita no futuro. No momento, iremos nos manter dentro do SQLite.

Muito bem. Pensando nisto, foi feito a mudança no nome da classe C_ScriptSQL, assim como o nome do arquivo, para C_DB_SQL. O nome do arquivo sempre irá acompanhar o nome da classe, então o arquivo, passou a ser chamado de C_DB_SQL.mqh, e estará na mesma localização vista no artigo anterior. Porém o código passou por algumas mudanças, como você pode ver 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. //+------------------------------------------------------------------+
012.         void Convert(const char &buff[], const int size)
013.         {
014.             string sz0 = "";
015.             bool b0, b1, bs1, bs2, bc0, bc1, bc;
016.             int nLine = 1;
017.             
018.             b0 = b1 = bs1 = bs2 = bc0 = bc1 = bc = false;
019.             for (int count = 0, nC0 = nLine; count < size; count++)
020.             {
021.                 switch (buff[count])
022.                 {
023.                     case '\t':
024.                         sz0 += (bs1 || bs2 ? "\t" : "");
025.                         break;
026.                     case '\n':
027.                         nC0++;
028.                     case '\r':
029.                         bc0 = false;
030.                         break;
031.                     case ';':
032.                         b0 = (bs1 || bs2 || bc0 || bc1 ? b0 : true);
033.                     default:
034.                         switch (buff[count])
035.                         {
036.                             case '"':
037.                                 bs1 = (bs2 || bc0 || bc1 ? bs1 : !bs1);
038.                                 break;
039.                             case '\'':
040.                                 bs2 = (bs1 || bc0 || bc1 ? bs2 : !bs2);
041.                                 break;
042.                         }
043.                         if (((count + 1) < size) && (!bs1) && (!bs2))
044.                         {
045.                             if (bc = ((buff[count] == '-') && (buff[count + 1] == '-'))) bc0 = true;
046.                             if (bc = ((buff[count] == '/') && (buff[count + 1] == '*'))) bc1 = true;
047.                             if (bc = ((buff[count] == '*') && (buff[count + 1] == '/'))) bc1 = false;
048.                             if (bc)
049.                             {
050.                                 count += 1;
051.                                 bc = false;
052.                                 continue;
053.                             }
054.                         }
055.                         if (!(bc0 || bc1))
056.                         {
057.                             if ((!b1) && (buff[count] > ' '))
058.                             {
059.                                 b1 = true;
060.                                 nLine = nC0;
061.                             }
062.                             sz0 += (b1 ? StringFormat("%c", buff[count]) : "");
063.                         }
064.                 }
065.                 if (b0)
066.                 {
067.                     m_Arr.Add(sz0, nLine);
068.                     sz0 = "";
069.                     b0 = b1 = false;
070.                 }
071.             }
072.         }
073. //+------------------------------------------------------------------+
074.     public    :
075. //+------------------------------------------------------------------+
076.         C_DB_SQL(const string szFileName = ":memory:")
077.         {
078.             m_handleDB = DatabaseOpen(szFileName, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE);
079.         }
080. //+------------------------------------------------------------------+
081.         ~C_DB_SQL()
082.         {
083.             DatabaseClose(m_handleDB);
084.         }
085. //+------------------------------------------------------------------+
086.         const string ExecScriptSQL(const string szFileName)
087.         {
088.             int file, size;
089.             char buff[];
090.             string szCmd;
091.             
092.             if ((file = FileOpen(szFileName, FILE_READ | FILE_BIN)) == INVALID_HANDLE)
093.                 return StringFormat("Unable to open script file: %s", szFileName);
094.             ArrayResize(buff, size = (int) FileSize(file));
095.             FileReadArray(file, buff);                
096.             FileClose(file);
097.             Convert(buff, size);
098.             ArrayFree(buff);
099.             for (int count = 0, nLine; count >= 0; count++)
100.             {
101.                 szCmd = m_Arr.At(count, nLine);
102.                 if (nLine < 0) break;
103.                 if (!ExecCommandSQL(szCmd))
104.                     return StringFormat("Execution of line %d of the SQL script failed...", nLine);
105.             }
106.             
107.             return NULL;
108.         }
109. //+------------------------------------------------------------------+
110.         bool ExecCommandSQL(const string szCmd)
111.         {
112.             return (m_handleDB == INVALID_HANDLE ? false : DatabaseExecute(m_handleDB, szCmd));
113.         }
114. //+------------------------------------------------------------------+
115. };
116. //+------------------------------------------------------------------+

Código fonte de C_DB_SQL.mqh

Estou mostrando estas mudanças, antes de implementamos as próximas coisas, para que você compreenda adequadamente o que está sendo feito. As mudanças não foram tantas assim. Mas algumas merecem destaque, começando pelo contructor da classe. Observe que na linha 76, onde declaramos o constructor, temos a possibilidade do uso de um nome default. Este nome que você vê no código, nos diz, e isto pode ser visto na documentação, que o banco de dados será aberto e criado na memória RAM. Este tipo de coisa é bastante interessante, quando queremos testar algumas coisas.

Para mais detalhes sobre qual deverá ser o valor passado para o constructor, veja a documentação da função DatabaseOpen. Lá temos a correta explicação do valor a ser utilizado, a fim de criar ou abrir um banco de dados em disco. De qualquer maneira, aqui iremos criar ou abrir um banco de dados. Então o constructor estará fazendo o seu trabalho.

Isto nos remete, ao destructor da classe, que vem logo em seguida, sendo visto na linha 81. Observe que aqui, tudo que estaremos de fato fazendo será fechar o banco de dados. Então enquanto a classe estiver em uso, ou podendo ser utilizada, o banco de dados estará aberto. Lembre-se disto, se for tentar acessar o banco de dados, se alguma aplicação o estiver utilizando dentro do MetaTrader 5. Pois isto poderá gerar erros de acesso.

Já que sabemos, que o banco de dados, estará aberto durante todo o tempo. Podemos simplesmente trabalhar de uma maneira diferente aqui na classe. Por isto, foi feita uma mudança na forma de executar o arquivo de script SQL. Veja que na linha 86, agora temos uma função que irá retornar uma string. Então, não iremos mais forçar a impressão de alguma coisa no terminal. Iremos retornar uma mensagem de texto ao chamador. Este tipo de coisa, facilita a implementação de algo que possa usar uma janela particular, ou especialmente desenhada, para informar algo ao usuário.

Note que, apenas mudamos alguns detalhes aqui. Onde iremos abrir, traduzir e enviar os comandos presentes no script, para que o SQL os execute. Isto é feito na linha 103. Esta chamará justamente a função presente na linha 110. Apesar desta função ter apenas uma linha, que é a linha 112. Aqui estamos preparando a classe, assim como todo restante da implementação, para que consigamos futuramente usar uma implementação cliente-servidor do SQL. Se isto vier a acontecer no futuro, precisaremos mexer o mínimo possível no código. Mas qualquer alteração será feita apenas e somente neste código da classe.

Voltando a linha 103. Caso a execução do comando SQL, venha a falhar, você pode ver que na linha 104, formatamos uma string. Esta tem como objetivo, retornar ao chamador uma informação dizendo em que linha do script SQL, não obtivemos uma execução bem-sucedida. Porém, se tudo ocorrer de forma perfeita, teremos como retorno um valor NULL, este é visto na linha 107. Desta maneira, tomando como base, o script SQL visto no artigo anterior, poderemos testar toda a classe de uma só vez. Para isto, usaremos a nova versão do script em MQL5, que pode ser vista logo 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. #include <Market Replay\SQL\C_DB_SQL.mqh>
08. //+------------------------------------------------------------------+
09. input string user01 = "DataBase01";       //Database File Name
10. input string user02 = "Script 01.sql";    //SQL Script File Name
11. //+------------------------------------------------------------------+
12. void OnStart()
13. {
14.     C_DB_SQL *SQL;
15.     string szMsg;
16.     
17.     SQL = new C_DB_SQL(user01);
18.     
19.     szMsg = (*SQL).ExecScriptSQL(user02);
20.     Print(szMsg == NULL ? "Result of executing the SQL script: Success" : szMsg);
21.     
22.     delete SQL;
23. }
24. //+------------------------------------------------------------------+

Código fonte do script MQL5

Acredito que ninguém terá problemas em entender este código acima. Dada a sua extrema simplicidade. Como resultado, em caso de sucesso, você terá sendo impresso no terminal do MetaTrader 5, a imagem abaixo.

Caso exista alguma falha. Você pode testar isto, modificando o script SQL, a fim de provocar uma falha nele. Você terá como resultado a imagem abaixo.

Note como tudo está funcionando. Usando apenas um único script SQL, acabamos de testar integralmente toda a classe C_DB_SQL. A fim de garantir que poderemos tanto, enviar comandos via chamada ExecCommandSQL. Como também poderemos rodar um script SQL, usando a chamada ExecScriptSQL. E tudo isto testado de uma só vez. Este tipo de coisa que acabamos de fazer, é o que todo programador, tenta de fato fazer. Ou seja, criamos uma cadeia de rotinas. Onde cada uma, individualmente é relativamente curta e bem objetiva. E tentamos fazer com que elas executem uma tarefa, bem específica. Mas que de alguma forma fará com que todas sejam executadas e ao mesmo tempo sendo testadas. Quando você consegue de fato fazer tal coisa, irá aumentar exponencialmente a velocidade de programação. Já que você passará a precisar testar menos rotinas ou as testar de forma menos frequente. Uma vez que todas serão executadas de forma bastante frequente. Eliminado assim possíveis falhas de forma muito mais rápida.

Muito bem. Acabamos a parte inicial. Mas muitos de vocês, podem estar achando esta coisa de usar um arquivo externo um verdadeiro tormento. Já que sempre precisaremos cuidar para que o usuário não o mude de diretório. Ou no mínimo, que o usuário não venha a editar, ou modificar o script de SQL que será preciso ser executado.

De fato, devo concordar em tese com você, meu caro leitor. Porém, discordo com relação a uma questão. Você não precisa necessariamente usar um arquivo externo. Você pode embutir este arquivo, junto ao executável criado pelo MQL5. E desta forma, ter a garantia de que o usuário, não irá modificar ou manipular o script SQL que deverá ser executado. Assim como também terá a garantia que a sintaxe do script SQL estará correta. Como falei no artigo anterior, colocar os comandos SQL em uma string dentro do executável é muito arriscado. Já que você pode acabar, sem notar, digitando algo errado. E só irá de fato perceber a falha, muito tempo depois. Onde muitas das vezes o banco de dados, já se encontra em um estágio de uso massivo.

Desta forma, a melhor solução de fato é, criar o script SQL em um editor, que a sintaxe, possa ser analisada facilmente. Salvar o script em um arquivo. Pegar este arquivo e o embutir dentro do executável a ser utilizado posteriormente por algum usuário. Existem diversas formas de se fazer este tipo de coisa. Algumas mais simples, outras um pouco mais complexas. Mas aqui, usando o MQL5, existe uma maneira bastante simples e eficaz de embutir o script SQL, ou qualquer outro tipo de arquivo dentro do executável final.

A forma de se fazer isto é vista no próximo tópico. Farei assim, para separar as coisas. Desta forma, não haverá confusão entre o que vimos até aqui, e o que será visto deste momento em diante.


Embutindo o script SQL no executável

A primeira coisa a ser feita, é garantir que o script SQL, que será embutido no executável de fato funcione. Teste isto, usando o próprio SQL. Não use o código que irá executar o script dentro do executável final. Apesar de parecer bobagem, você deve de fato, ter a garantia que o script de fato funciona. E para fazer isto, você deverá executar ele dentro do ambiente SQL, antes de mais nada. Neste ponto, existe algo do qual você deve se atentar. Não sei se você leu os artigos anteriores nesta série sobre como construir um replay/simulador. Isto por que, durante alguns artigos nesta sequência, expliquei um pouco sobre como trabalhar no SQL. Isto para que você, caro leitor, não tivesse o devido conhecimento sobre o assunto. Pudesse de alguma forma aprender um pouco sobre o ambiente.

Como eu havia dito, durante aqueles momentos. Você de fato não deve achar que usar o SQLite será diferente de usar uma outra implementação do SQL. Existem sim algumas poucas diferenças. Mas no geral, você se entender SQL, conseguirá desenvolver o que será feito. Ou deverá ser feito, usando o SQLite que se encontra no MetaTrader 5. Usando um código que foi testado no MySQL ou no SQL Server por exemplo. Não existem grandes diferenças, desde é claro você entenda de fato o que está sendo feito.

Uma vez garantido que o script funcione. Salve ele e o coloque em algum local dentro do diretório MQL5. Para exemplificar, iremos utilizar, o script que estamos usando desde o artigo passado. Então ele estará na mesma localização indicada no artigo anterior. Assim sendo, vamos modificar primeiramente o arquivo de cabeçalho C_DB_SQL.mqh a fim de conseguir executar o script. Pois diferente, de fazer a leitura de um arquivo externo. Quando usamos o script como sendo um recurso dentro do executável. As coisas serão, e muitas das vezes são feitas de uma maneira diferente. Desta forma o novo código do arquivo de cabeçalho, 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. //+------------------------------------------------------------------+
012.         void Convert(const char &buff[], const int size)
013.         {
014.             string sz0 = "";
015.             bool b0, b1, bs1, bs2, bc0, bc1, bc;
016.             int nLine = 1;
017.             
018.             b0 = b1 = bs1 = bs2 = bc0 = bc1 = bc = false;
019.             for (int count = 0, nC0 = nLine; count < size; count++)
020.             {
021.                 switch (buff[count])
022.                 {
023.                     case '\t':
024.                         sz0 += (bs1 || bs2 ? "\t" : "");
025.                         break;
026.                     case '\n':
027.                         nC0++;
028.                     case '\r':
029.                         bc0 = false;
030.                         break;
031.                     case ';':
032.                         b0 = (bs1 || bs2 || bc0 || bc1 ? b0 : true);
033.                     default:
034.                         switch (buff[count])
035.                         {
036.                             case '"':
037.                                 bs1 = (bs2 || bc0 || bc1 ? bs1 : !bs1);
038.                                 break;
039.                             case '\'':
040.                                 bs2 = (bs1 || bc0 || bc1 ? bs2 : !bs2);
041.                                 break;
042.                         }
043.                         if (((count + 1) < size) && (!bs1) && (!bs2))
044.                         {
045.                             if (bc = ((buff[count] == '-') && (buff[count + 1] == '-'))) bc0 = true;
046.                             if (bc = ((buff[count] == '/') && (buff[count + 1] == '*'))) bc1 = true;
047.                             if (bc = ((buff[count] == '*') && (buff[count + 1] == '/'))) bc1 = false;
048.                             if (bc)
049.                             {
050.                                 count += 1;
051.                                 bc = false;
052.                                 continue;
053.                             }
054.                         }
055.                         if (!(bc0 || bc1))
056.                         {
057.                             if ((!b1) && (buff[count] > ' '))
058.                             {
059.                                 b1 = true;
060.                                 nLine = nC0;
061.                             }
062.                             sz0 += (b1 ? StringFormat("%c", buff[count]) : "");
063.                         }
064.                 }
065.                 if (b0)
066.                 {
067.                     m_Arr.Add(sz0, nLine);
068.                     sz0 = "";
069.                     b0 = b1 = false;
070.                 }
071.             }
072.         }
073. //+------------------------------------------------------------------+
074.         const string ExecSQL(void)
075.         {
076.             string szCmd;
077.             
078.             for (int count = 0, nLine; count >= 0; count++)
079.             {
080.                 szCmd = m_Arr.At(count, nLine);
081.                 if (nLine < 0) break;
082.                 if (!ExecCommandSQL(szCmd))
083.                     return StringFormat("Execution of line %d of the SQL script failed...", nLine);
084.             }
085.             
086.             return NULL;
087.         }
088. //+------------------------------------------------------------------+
089.     public    :
090. //+------------------------------------------------------------------+
091.         C_DB_SQL(const string szFileName = ":memory:")
092.         {
093.             m_handleDB = DatabaseOpen(szFileName, DATABASE_OPEN_CREATE | DATABASE_OPEN_READWRITE);
094.         }
095. //+------------------------------------------------------------------+
096.         ~C_DB_SQL()
097.         {
098.             DatabaseClose(m_handleDB);
099.         }
100. //+------------------------------------------------------------------+
101.         const string ExecResourceSQL(const string szResource)
102.         {
103.             char buff[];
104.             int size;
105.             
106.             ArrayResize(buff, size = StringLen(szResource));
107.             StringToCharArray(szResource, buff);
108.             Convert(buff, size);
109.             ArrayFree(buff);
110.             
111.             return ExecSQL();
112.         }
113. //+------------------------------------------------------------------+
114.         const string ExecScriptSQL(const string szFileName)
115.         {
116.             int file, size;
117.             char buff[];
118.             
119.             if ((file = FileOpen(szFileName, FILE_READ | FILE_BIN)) == INVALID_HANDLE)
120.                 return StringFormat("Unable to open script file: %s", szFileName);
121.             ArrayResize(buff, size = (int) FileSize(file));
122.             FileReadArray(file, buff);                
123.             FileClose(file);
124.             Convert(buff, size);
125.             ArrayFree(buff);
126.             
127.             return ExecSQL();
128.         }
129. //+------------------------------------------------------------------+
130.         bool ExecCommandSQL(const string szCmd)
131.         {
132.             return (m_handleDB == INVALID_HANDLE ? false : DatabaseExecute(m_handleDB, szCmd));
133.         }
134. //+------------------------------------------------------------------+
135. };
136. //+------------------------------------------------------------------+

Código fonte de C_DB_SQL.mqh

Note que fizemos uma pequena mudança. Esta se encontra em primeiro lugar, na função ExecScriptSQL, onde na linha 127, removemos o código que era executado aqui. E o transferimos para um outro local. Você pode perceber isto, olhando a versão anterior, que é vista no tópico anterior. Note que o mesmo código que havia aqui na função ExecScriptSQL. Agora está na função ExecSQL. Esta se encontra na linha 74, sendo uma função privativa da classe. Ou seja, você não conseguirá chamar este procedimento, fora do corpo da classe. Isto por que, fora do corpo da classe C_DB_SQL, esta função ExecSQL, não faz nenhum sentido. Mas aqui ela faz todo o sentido de existir.

Como o código presente nesta função já havia sido testado anteriormente. Sabemos que a função ExecScriptSQL, continuará funcionando da mesma maneira. Esta é uma coisa que no mundo da programação orientada em objetos, é conhecida como encapsulamento. Ou seja, mudamos a forma interna de implementar a função ExecScriptSQL, mas para qualquer código externo, que faça uso da classe C_DB_SQL, nada terá sido alterado. Permanecendo tudo como era antes.

Agora vem a parte interessante. Como separamos a parte responsável, por de fato executar os comandos em SQL, como se eles estivessem vindo de um arquivo externo. Contendo em seu interior um script SQL. Podemos usar este mesmo procedimento ExecSQL, para executar algo que teremos colocado, diretamente dentro do executável final. No caso, será o arquivo de script em SQL. Mas, as coisas não se limitam a isto. Podemos fazer bem mais do que isto.

Porém para não complicar demais a explicação. Já que se você pensar um pouco, irá acabar entendendo que podemos bem mais do que irei explicar. Não vejo de fato necessidade de dar muitos detalhes, ou por menores sobre o alcance do que acabamos de fazer. No entanto, observe na linha 101. Ali temos uma função, que se parece muito com a função ExecScriptSQL. Mas aqui, decidi chama-la de ExecResourceSQL. Isto para que ela não entrasse em conflito com a antiga versão ExecScriptSQL.

Agora vamos olhar dentro desta função da linha 101. Note que começamos declarando duas variáveis. E na linha 106, alocamos espaço na memória. Este espaço, será do tamanho da string que estaremos por fim recebendo. Preste atenção a isto. O código estará em uma string. Assim como era no caso do arquivo. Só que aqui, a string que será fornecida não estará vindo de um arquivo. Pelo menos não diretamente. Depois você entenderá melhor isto. Então como a string, não vem de um arquivo, na forma binária, precisamos tornar a string em um array binário. Assim sendo, fazemos uso da linha 107, que converte a string em sua forma de representação, como sendo um grande array de carácteres.

Neste ponto estamos em algo equivalente ao que aconteceria se fizéssemos a leitura via arquivo. O que representa exatamente o que acontece na linha 122. Pois bem, uma vez que já temos o array de carácteres, a próxima coisa a ser feita, é justamente converter o array em linhas de comando em SQL. Isto é feito na linha 108. Depois devolvemos a memória para o sistema operacional. Isto na linha 109, e na linha 111, executamos exatamente o mesmo que era executado quando a informação do script em SQL, via de um arquivo externo. Ou seja, chamamos a função ExecSQL, que se encontra na linha 74.

Por isto, que eu disse, que você precisa garantir que o script funcione. Pois diferente do arquivo externo, onde você pode ficar mexendo nele até fazer as coisas funcionarem. Aqui, quando usamos esta função ExecResourceSQL, não haverá tal possibilidade. Isto por que os dados já estarão de alguma forma dentro do executável em forma de uma grande string. Que seria o equivalente ao arquivo externo.

Ok. Mas como faremos para usar esta função ExecResourceSQL. Iremos precisar copiar e colar todo o conteúdo presente no arquivo de script SQL? Na verdade você até poderia fazer isto. Mas seria um trabalho enfadonho e completamente desnecessário. Podemos fazer melhor. Vamos pedir para o MQL5, fazer a cópia do arquivo para nós. Assim, se precisamos modificar algo. Modificaremos e no momento que formos compilar novamente o executável final. O próprio MQL5, garantirá que tudo funcionem adequadamente. E que o arquivo correto, venha a ser embutido no executável da melhor maneira possível. E sempre com o conteúdo mais atual. Independentemente de quanto tempo tenha se passado. O MQL5, fará o trabalho pesado para nós.

Assim para obter esta facilidade em se usar o MQL5. Você deverá modificar o arquivo principal, para o que é mostrado logo 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_01
08. //+------------------------------------------------------------------+
09. #include <Market Replay\SQL\C_DB_SQL.mqh>
10. //+------------------------------------------------------------------+
11. input string user01 = "DataBase01";        //Database File Name
12. //+------------------------------------------------------------------+
13. void OnStart()
14. {
15.     C_DB_SQL *SQL;
16.     string szMsg;
17.     
18.     SQL = new C_DB_SQL(user01);
19.     
20.     szMsg = (*SQL).ExecResourceSQL(SQL_01);
21.     Print(szMsg == NULL ? "Result of executing the SQL script: Success" : szMsg);
22.     
23.     delete SQL;
24. }
25. //+------------------------------------------------------------------+

Código fonte do Script MQL5

Observe a linha sete no código acima. Note que nela estamos dizendo que desejamos adicionar um recurso, no código do executável final.

Agora a parte que realmente, você caro leitor, deve se atentar é no que vem depois do nome entre aspas duplas ( " ). Ali é que se encontra um dos macetes, ou maneiras de fazer uso de recursos no MQL5. Além é claro de nos permitir fazer outras coisas.

Você pode até imaginar que esta linha sete, é uma tremenda de uma bobagem. Mas para o compilador, e consequentemente para todo o código. Esta linha sete, está definindo uma constante, cujo conteúdo será uma string com o nome SQL_01. E cujo conteúdo é o exatamente, o que se encontra dentro do arquivo indicado entre aspas duplas. Resumindo: Seria como se você, manualmente, fizesse a captura de todo o código presente dentro do arquivo, que no caso é o Script 01.sql e o colocasse dentro deste código. Mas não o colocaria em uma variável, e sim em uma constante do tipo string, cujo nome seria SQL_01.

Não sei se você de fato conseguiu entender, o que acabei de dizer. Mas se você, e é aqui que o compilador do MQL5, irá de fato nos ajudar. Modificar, o conteúdo do arquivo Script 01.sql, mas o mantiver no mesmo diretório esperado por este código principal. No momento que você for compilar o código novamente. O compilador do MQL5, irá buscar o conteúdo presente no arquivo Script 01.sql, e o disponibilizará como sendo uma string constante cujo nome será SQL_01.

Este tipo de coisa facilita bastante a nossa vida. E caso, o arquivo Script 01.sql, não existirá na localização indicada. O compilador do MQL5, emitirá um erro durante a compilação, dizendo que o arquivo Script 01.sql, não existe no local indicado. Porém uma vez que o código tenha sido compilado. Você não precisará se preocupar mais com o arquivo Script 01.sql. Isto por que, ele estará embutido dentro de executável.

Então no momento que a linha 20, do código acima, for executada. O programa irá pegar e transferir a string para dentro da função ExecResourceSQL. Fazendo com que ela seja interpretada como se fosse um código de um arquivo binário, que acabou de ser lido. E é desta forma que a mágica acontece.

Não faz sentido, você ter que digitar comandos e mais comandos a fim de fazer as coisas que estariam em um arquivo de script. Pegue e use diretamente o arquivo de script. Se algo der errado, você conseguirá facilmente encontrar a falha. Não precisando ter que recorrer a todo um trabalho, em analisar strings e mais strings. Dentro de um código, a procura de falhas, seja de digitação, seja por outro motivo. Mas que estejam fazendo o SQL se comportar, diferente do que você esperava que de fato fosse executado pelo SQL.

Talvez este seja um dos motivos, pelo qual muitos não se interessam em aprender SQL. Justamente por conta que se você fosse fazer as coisas, como todos normalmente fazem, teria um trabalho enorme em mãos. Em códigos pequenos e simples, até que o trabalho não seria assim tão grande. Mas conforme o código vai se tornando mais e mais complexo, a coisa pode se tornar um pesadelo. Ou no mínimo um baita de um trabalhão, que acaba por nos desanimar de prosseguir e fazer as coisas de forma mais eficaz possível.


Considerações finais

Olhando o código da classe C_DB_SQL, você pode estar se perguntando uma coisa: Por que precisamos de três funções diferentes, e aparentemente iguais, para executar um código em SQL? Será que não faria mais sentido, ter apenas uma função? Eu, até cheguei a me questionar a este respeito. Será mesmo que não teria uma forma de tornar esta classe C_DB_SQL, um pouco mais simples? Mas quando, levamos o código para outros analisarem, acabamos por perceber uma coisa. Apesar de termos três funções, aparentemente iguais, ou muito parecidas, voltadas para executar o código SQL. Elas de fato não são assim tão iguais.

Isto por que, cada uma delas é voltada a cobrir uma determinada situação. Mas de qualquer maneira, o código está disponível de forma aberta e gratuita. Você como programador é que irá decidir, o que deve ou não ser feito. Aqui a intenção é sempre mostrar uma de tantas soluções possíveis de serem criadas e implementadas. Então sinta-se à vontade para promover as devidas modificações a fim de cobrir o seu caso específico.

Porém, neste artigo, ainda não cobrimos uma outra parte envolvida com o SQL. Tal parte a qual estou me referindo, é justamente a obtenção das respostas vindas do SQL, durante uma pesquisa feita pelo nosso código escrito em MQL5. Esta parte, de fato será bastante interessante de ser explicada e abordada. Já que temos forma diferentes de tratar a mesma questão. No entanto, como aqui, neste momento, estaremos focados em fazer uso do SQLite. E visto que este estará embutido no MetaTrader 5. A forma como faremos tais referências aos resultados de pesquisas no banco de dados, será abordada no próximo artigo.

Mas o que irei mostrar lá não é de maneira alguma, a única e derradeira forma, de fazer as coisas. Podemos fazer as coisas de muitas maneiras diferentes. Assim como o que foi feito aqui neste artigo. Então procure sempre estudar e aprender coisas novas.

ArquivoDescriçã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.mq5Cria a janela para configuração da ordem a ser enviada   (É necessário o Mouse Study para interação)
 Indicators\Market Replay.mq5Cria os controles para interação com o serviço de replay/simulador   (É necessário o Mouse Study para interação)
 Indicators\Mouse Study.mq5Permite 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.mq5Cria e mantém o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema)
 Code VS C++\Servidor.cppCria e mantém um soquete servidor criado em C++ (Versão Mini Chat)
 Code in Python\Server.pyCria e mantém um soquete em python para comunicação entre o MetaTrader 5 e o Excel
 Indicators\Mini Chat.mq5Permite implementar um mini chat via indicador (Necessário uso de um servidor para funcionar)
 Experts\Mini Chat.mq5Permite implementar um mini chat via Expert Advisor (Necessário uso de um servidor para funcionar)
 Scripts\SQLite.mq5Demonstra uso de script SQL por meio do MQL5
 Files\Script 01.sqlDemonstra a criação de uma tabela simples, com chave estrangeira
 Files\Script 02.sqlDemonstra a adição de valores em uma tabela
Arquivos anexados |
Anexo.zip (571.71 KB)
Redes neurais em trading: Sistema multiagente com confirmação conceitual (Conclusão) Redes neurais em trading: Sistema multiagente com confirmação conceitual (Conclusão)
Continuamos a implementação das abordagens propostas pelos autores do framework FinCon. O FinCon é um sistema multiagente baseado em grandes modelos de linguagem (LLM). Hoje vamos implementar os módulos necessários e realizar testes abrangentes do modelo com dados históricos reais.
Redes neurais em trading: Sistema multiagente com validação conceitual (FinCon) Redes neurais em trading: Sistema multiagente com validação conceitual (FinCon)
Apresentamos o framework FinCon, que é um sistema multiagente baseado em grandes modelos de linguagem (LLM). O framework utiliza reforço verbal conceitual para melhorar a tomada de decisões e o gerenciamento de riscos, permitindo realizar diversas tarefas financeiras de forma eficiente.
Sistemas neurossimbólicos no algotrading: Unindo regras simbólicas e redes neurais Sistemas neurossimbólicos no algotrading: Unindo regras simbólicas e redes neurais
Este artigo fala sobre a experiência de desenvolver um sistema de negociação híbrido que combina análise técnica clássica com redes neurais. O autor destrincha a arquitetura do sistema, desde a análise básica de padrões e estrutura da rede neural até os mecanismos de tomada de decisão, compartilhando código real e observações práticas.
Funções de ativação de neurônios durante o aprendizado: chave para uma convergência rápida? Funções de ativação de neurônios durante o aprendizado: chave para uma convergência rápida?
Este trabalho apresenta uma análise da interação entre diferentes funções de ativação e algoritmos de otimização no contexto do treinamento de redes neurais. A atenção principal está voltada para a comparação entre o ADAM clássico e sua versão populacional ao lidar com uma ampla gama de funções de ativação, incluindo as funções oscilatórias ACON e Snake. Mediante uma arquitetura MLP minimalista (1-1-1) e um único exemplo de treino, isola-se a influência das funções de ativação no processo de otimização, eliminando interferências de outros fatores. Propomos um método de controle dos pesos da rede por meio dos limites das funções de ativação e um mecanismo de reflexão de pesos, permitindo evitar problemas de saturação e estagnação no aprendizado.