Simulação de mercado: A união faz a força (II)
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: A união faz a força (I), iniciamos a fase final no desenvolvimento do sistema de replay/simulador. Apresentei a você, meu caro leitor, qual será a diretiva a ser usada para conseguir permitir que todas aplicações desenvolvidas, possam funcionar. Tanto em contato com o servidor real, que acontece quando estamos em uma conta demo ou conta real. Como também funcionar dentro do replay/simulação. Neste iremos fazer a simulação do que seria o comportamento de todo o processo de negociação.
Acredito que muitos, não venham a ter tanta esperança de que o sistema de fato venha a cumprir o que estou informando. Mas você verá que de fato, ele conseguirá cumprir com o objetivo. Se bem que a partir deste momento, a coisa pode realmente vir a se tornar bastante mais complicada. Isto devido ao que será feito. Então, caso você não tenha muita familiaridade, com programação envolvendo diversos arquivos, e uso de linguagens diferentes em um mesmo programa. Peço que, leia todo o conteúdo com bastante calma. Tanto deste artigo, quanto os que já foram postados e os que ainda virão. Pois de fato, a coisa aqui será extremamente densa. Não vou ficar entrando em muitos detalhes a respeito do que estará sendo programado. Isto para não tornar a escrita do artigo, e tão pouco a leitura algo maçante e cansativo.
Mas manterei a didática. Isto para que realmente seja possível compreender o que estará sendo feito. Assim peço desculpas aos que estão iniciando na programação. Mas aqui a coisa será bem mais dinâmica. Caso contrário, o sistema irá demorar muito para chegar ao seu estágio final.
Preparativos para simular o servidor de negociação
A primeira coisa que iremos fazer é preparar o banco de dados. Isto para que possamos lançar novos registros, atualizar o que já existem e também pesquisar algum específico. Esta é a parte inicial. Já que será a forma de permitir a comunicação entre todas as aplicações, que de fato irão fazer agora a simulação do servidor real. Preste atenção neste ponto. Não estou dizendo que montaremos um servidor simulado. Estou dizendo que iremos criar uma forma de simular o que aconteceria no servidor.
Isto para que possamos fazer uma simulação com uso de posições e ordens pendentes. Apesar de ainda não ter mostrado como o sistema de ordens pendentes funcionará. Ele praticamente já está no próprio código do indicador de posição. É tudo uma questão de adaptar o código. Mas isto será visto depois. Vamos começar criando o banco de dados. E basicamente, o que precisamos de fato colocar no banco de dados, é visto na imagem abaixo.

É claro que cada um destes dados representa uma coluna. E estas estarão em uma ou mais tabelas. Sem bem, que para efeitos didáticos, posso colocar tudo em uma única tabela. Mas antes de fazermos isto. Estive pensando se realmente seria uma boa colocar, montagem dos valores dentro do Expert Advisor. Isto por conta do fato de que, quero que o usuário, não efetue um pedido enquanto o replay/simulador estiver pausado. E o melhor local para se checar isto é no indicador de controle.
Porém, existe uma questão, que talvez torne aceitável manter a ideia original. Que seria simplesmente ler o buffer do indicador de controle, a fim se saber se o sistema está no modo pausado ou modo play. Mas de qualquer forma, vou deixar isto a cargo de cada um. Se o usuário pretende de fato assimilar como operar de maneira mais consistente. Ele não enviará pedidos quando o replay/simulador estiver pausado. Ele fará isto quando os dados de fato estão sendo emitidos e sendo montados no gráfico. Então iremos manter o plano original.
Assim sendo, já foi visto no artigo anterior, a classe que dará suporte ao banco de dados. Tudo que precisamos agora fazer é criar os scripts em SQL. Isto para que as colunas sejam criadas. Lembrando que usarei a montagem o mais simples quanto for possível ser feita. Ou seja, fazendo uso de uma única tabela e sem testes para checar a validade dos dados. Isto para não me prolongar desnecessariamente esta fase final de desenvolvimento. Irei assumir que você meu caro leitor, não tentara colocar dados inválidos dentro do banco de dados. Então vamos começar a ajeitar o código, do Expert Advisor, para construir o banco de dados. Isto quando estivermos usando o replay/simulador. Para fazer isto, o código original deverá ser modificado, para o fragmento de código visto logo abaixo.
030. //+------------------------------------------------------------------+ 031. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 032. { 033. int handle; 034. ulong ul; 035. 036. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 037. switch (id) 038. { 039. case CHARTEVENT_CHART_CHANGE: 040. if (Terminal != NULL) break; 041. else 042. { 043. Terminal = new C_Terminal(0, 0, user00); 044. if (_Symbol != def_SymbolReplay) 045. { 046. for (int count = PositionsTotal() - 1; count >= 0; count--) 047. { 048. ul = PositionGetTicket(count); 049. if (PositionGetString(POSITION_SYMBOL) != (*Terminal).GetInfoTerminal().szSymbol) 050. { 051. ChartIndicatorDelete(0, 0, IntegerToString(ul)); 052. continue; 053. } 054. handle = iCustom(NULL, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", ul); 055. ChartIndicatorAdd(0, 0, handle); 056. IndicatorRelease(handle); 057. } 058. } else 059. { 060. //... NEW CODE ... 061. } 062. } 063. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 064. EventChartCustom(0, evEA_At_ChartTrade, user00, 0, ""); 065. break; 066. } 067. } 068. //+------------------------------------------------------------------+ 069. void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) 070. { 071. if (Terminal == NULL) return; 072. static ulong ticket = 0; 073. switch (trans.type) 074. { 075. case TRADE_TRANSACTION_HISTORY_ADD: 076. EventChartCustom(0, evUpdate_Position, trans.position, 0, ""); 077. ticket = (trans.order != trans.position ? trans.position : 0); 078. break; 079. case TRADE_TRANSACTION_REQUEST: 080. if ((request.symbol == (*Terminal).GetInfoTerminal().szSymbol) && (result.retcode == TRADE_RETCODE_DONE)) switch (request.action) 081. { 082. case TRADE_ACTION_DEAL: 083. if (ticket > 0) EventChartCustom(0, evUpdate_Position, ticket, 0, ""); 084. else 085. { 086. int handle = iCustom(NULL, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", result.order); 087. ChartIndicatorAdd(0, 0, handle); 088. IndicatorRelease(handle); 089. } 090. ticket = 0; 091. break; 092. case TRADE_ACTION_SLTP: 093. EventChartCustom(0, evUpdate_Position, request.position, 0, ""); 094. break; 095. } 096. break; 097. }; 098. } 099. //+------------------------------------------------------------------+ 100. void OnDeinit(const int reason) 101. { 102. ulong ul; 103. 104. switch (reason) 105. { 106. case REASON_REMOVE: 107. case REASON_INITFAILED: 108. EventChartCustom(0, evEA_At_ChartTrade, -1, 0, ""); 109. break; 110. } 111. if (Terminal != NULL) 112. { 113. if (_Symbol != def_SymbolReplay) 114. { 115. for (int count = PositionsTotal() - 1; count >= 0; count--) 116. { 117. ul = PositionGetTicket(count); 118. if (PositionGetString(POSITION_SYMBOL) != (*Terminal).GetInfoTerminal().szSymbol) continue; 119. ChartIndicatorDelete(0, 0, IntegerToString(ul)); 120. } 121. } else 122. { 123. // ... NEW CODE ... 124. } 125. } 126. delete Orders; 127. delete Terminal; 128. delete DB; 129. } 130. //+------------------------------------------------------------------+
Fragmento do Expert Advisor
Observe que estamos mudando, levemente, o código visto no artigo anterior. Agora preste atenção. Existem dois pontos, onde um novo código irá ser injetado do Expert Advisor. Estes pontos podem ser vistos no fragmento, nas linhas 60 e 123. Para que você consiga entender. Na linha 60, iremos colocar um código cujo objetivo é colocar o indicador de posição no gráfico. Isto seria o equivalente ao que é feito entre as linhas 46 e 57. E na linha 123, faremos o equivalente ao código entre as linhas 115 e 119. Ou seja, o Expert Advisor sofrerá poucas mudanças. Já o código em OnTradeTransaction, não sofrerá nenhuma mudança, conforme foi explicado no artigo anterior. Porém ele será bastante utilizado aqui no Expert Advisor. Mesmo quando estivermos usando o Expert Advisor no replay/simulador. Mas não se preocupe. Irei explicar em detalhes como isto será de fato feito.
Muito bem, olhando as coisas assim, parece que iremos de fato implementar algo extremamente complicado. Mas você verá que é bem mais simples do você deve estar imaginando neste momento. Apenas será um pouco mais avançado. Isto se você já tem um bom conhecimento em MQL5 e em SQL. Mas não tem a mínima ideia de como simular o servidor de negociação. Usando para isto apenas MQL5 e SQL.
Agora se você prestou atenção, deve estar imaginando que está faltando código aqui. Ou que não estamos isolando a classe C_Orders. Como poderia estar imaginando, dado as explicações vistas no artigo anterior. Bem, inicialmente eu até cogitei isolar a classe C_Orders. Mas se isto fosse feito, seria necessário implementar uma classe equivalente. Ou seja, um trabalho completamente desnecessário. Visto que podemos simplesmente adaptar a classe C_Orders, para que ela consiga simular o servidor. Desta maneira, vamos para a classe C_Orders, para ver o que será modificado.
Basicamente, apenas para efeitos de teste, podemos fazer o que é visto no fragmento abaixo.
067. //+------------------------------------------------------------------+ 068. ulong SendToPhysicalServer(void) 069. { 070. MqlTradeCheckResult TradeCheck; 071. MqlTradeResult TradeResult; 072. MqlTradeTransaction TradeTrans; 073. 074. ZeroMemory(TradeCheck); 075. ZeroMemory(TradeResult); 076. if (_Symbol == def_SymbolReplay) 077. { 078. TradeTrans.type = TRADE_TRANSACTION_REQUEST; 079. m_Base.TradeRequest.symbol = _Symbol; 080. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 081. TradeResult.order = 2048; 082. TradeResult.retcode = TRADE_RETCODE_DONE; 083. OnTradeTransaction(TradeTrans, m_Base.TradeRequest, TradeResult); 084. TradeTrans.type = TRADE_TRANSACTION_HISTORY_ADD; 085. TradeTrans.order = 0; 086. TradeTrans.position = TradeResult.order; 087. OnTradeTransaction(TradeTrans, m_Base.TradeRequest, TradeResult); 088. } else { 089. if (!OrderCheck(m_Base.TradeRequest, TradeCheck)) 090. { 091. PrintFormat("Order System - Check Error: %d", GetLastError()); 092. return 0; 093. } 094. m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult); 095. } 096. if (TradeResult.retcode != TRADE_RETCODE_DONE) 097. { 098. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 099. return 0; 100. }; 101. 102. return TradeResult.order; 103. } 104. //+------------------------------------------------------------------+
Fragmento de C_Orders
Aqui é onde as coisas começam a se tornar cada vez mais interessantes. Lembrando que este código é apenas para testar o que de fato iremos fazer. Observe que foi adicionado a linha 72, uma nova estrutura que é definida pelo MQL5. Então na linha 76 checamos se estamos usando o replay/simulador ou não. Isto verificando o nome do ativo. Caso, estejamos usando um ativo, que provavelmente estará definido no servidor de negociação real. Iremos para a linha 89 e o código segue seu funcionamento já conhecido. Agora se estivermos usando o ativo para replay/simulador. Iremos simular uma resposta do servidor. Isto está sendo feito em duas etapas. A primeira é onde simulamos resposta para o pedido de abertura de posição. Quando os dados estiverem configurados, chamaremos OnTradeTransaction pela primeira vez, isto é feito na linha 83. Depois enviamos um outro pedido já na linha 87. Em ambos os casos, o procedimento OnTradeTransaction presente no Expert Advisor será executado. E neste teremos a tentativa de colocar o indicador de posição no gráfico.
E este tipo de coisa de fato funcionará. Porém, como o indicador de posição, não tem onde buscar as informações da posição ele será removido do gráfico. No entanto, você pode adicionar algumas mensagens no código de forma a rastrear este fluxo de mensagens. Deixo isto como uma tarefa para você fazer, meu caro leitor. Assim, você conseguirá de fato compreender todos os passos. Desde o momento em que usamos o indicador de mouse, interagindo com o botão de compra ou venda presentes no indicador Chart Trade. Vendo o indicador Chart Trade enviar uma mensagem para o Expert Advisor. E por último, ver o Expert Advisor, usando a classe C_Orders, simular uma abertura de posição.
Ao fazer isto, o Expert Advisor irá logo depois, pedir para o MetaTrader 5, carregar o indicador de posição. Quando o indicador de posição for colocado no gráfico, ele tentará encontrar a posição que a classe C_Orders, estará simulando. Mas como não encontrará os dados, já que ainda não estamos usando o SQL para isto. O indicador de posição será removido do gráfico. Isto sim é a beleza da programação. Que coisa maravilhosa.
Apesar do fragmento acima nos permitir efetuar uma e apenas uma única transação. Já é um bom ponto de partida. Isto dado o nível de simplicidade que estamos usando. Mas aí você, meu caro leitor pode ficar se perguntando: Por que dá apenas para simular uma única transação? Bem, esta é uma boa pergunta. Para entender, vamos ver o código do procedimento OnTradeTransaction. Este pode ser visto no fragmento anterior. Observe que no momento em que a linha 83 do código SendToPhysicalServer, for executada pela segunda vez. O teste feito na linha 83 do procedimento em OnTradeTransaction terá sucesso. Impedindo assim que a linha 86 seja executada, na tentativa de colocar o indicador de posição no gráfico.
Mas espere um pouco. Este sistema funciona em um conta HEDGING, quando está em contato com o servidor real. Mas por que não funciona aqui. Na tentativa se simular o servidor? O problema é a ordem em que colocamos os eventos de simulação. Observe na função SendToPhysicalServer, que primeiro estamos enviando um TRADE_TRANSACTION_REQUEST, para depois enviar um TRADE_TRANSACTION_HISTORY_ADD. Esta sequência está errada. Isto foi feito de propósito, para mostrar a você meu caro leitor e entusiasta, que agora você deverá tomar o máximo de cuidado ao fazer as coisas.
Isto por conta que a ordem em que elas irão acontecer. Com toda a certeza irá influenciar a forma como as coisas acontecerão no código. Mesmo que o código esteja completamente correto. Ele não funcionará como você possivelmente estaria imaginando. Tornando assim algo possível em algo impossível. Por isto que eu disse no artigo anterior, de que agora em diante, vem chumbo grosso, a cada novo passo dado. Bem, de qualquer forma esta mudança na classe C_Orders era apenas para demonstrar como as coisas funcionariam.
O Resultado pode ser visto na imagem abaixo.

Na região demarcada da imagem acima, você pode ver a tentativa de simular o servidor. Note que de fato as coisas funcionaram como o esperado. Sendo de fato possível efetivamente simular o servidor real. Apenas precisamos modelar a sua forma de agir. Mas antes de continuarmos vamos começar a usar o SQL. Já que precisamos dele para simular o servidor real. Para separar as coisas vamos a um novo tópico.
Modelando o banco de dados
Fazer a modelagem do banco de dados, é algo muito legal e pode ser até mesmo relaxante. Já que mudamos um pouco o foco, para algo que é bem divertido de ser feito. Programar em SQL. Como falei no começo, não irei mostrar como modelar algo elaborado no SQL. Irei fazer o modelo mais básico do básico em termos de SQL. Mas já será o suficiente para o que precisaremos. Então vamos começar fazendo as seguintes mudanças no código.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Defines.mqh" 005. #include "..\SQL\C_ReplayDataBase.mqh" 006. //+------------------------------------------------------------------+ 007. class C_Orders : public C_ReplayDataBase 008. { . . . 164. //+------------------------------------------------------------------+ 165. public : 166. //+------------------------------------------------------------------+ 167. C_Orders(const ulong magic) 168. :C_ReplayDataBase() 169. { 170. m_Base.MagicNumber = magic; 171. ExecCommandSQL("CREATE TABLE IF NOT EXISTS tb_Replay ( ticket, type, volume, price, sl, tp, history );"); 172. } 173. //+------------------------------------------------------------------+
Fragmento de C_Orders
Note que na linha cinco adicionei o arquivo de cabeçalho da classe C_ReplayDataBase. Na linha sete dizemos a classe C_Orders para herdar de forma pública a classe C_ReplayDataBase. Com isto a classe C_Orders passará a ter novas funcionalidades. Para que tudo fique correto, na linha 168, iniciamos a classe C_ReplayDataBase. Isto dentro do constructor de C_Orders. Com isto, precisaremos de menos código dentro do Expert Advisor. E ainda assim conseguiremos fazer o que será preciso, a fim de simular o servidor de negociação. Muito bem, mas precisamos de uma tabela dentro do banco de dados. Assim, usando o comando na linha 171, criamos a tabela que usaremos na simulação. Agora a coisa começa a tomar forma.
O próximo passo a ser dado, e começar a simular o que foi visto no tópico anterior. Isto já jogando as informações para dentro do banco de dados. Assim surge uma nova função dentro da classe C_Orders.
041. //+------------------------------------------------------------------+ 042. struct stChartTrade 043. { 044. struct stEvent 045. { 046. EnumEvents ev; 047. string szSymbol, 048. szContract; 049. bool IsDayTrade; 050. ushort Leverange; 051. double PointsTake, 052. PointsStop; 053. }Data; 054. //--- 055. bool Decode(const EnumEvents ev, const string sparam) 056. { 057. string Res[]; 058. 059. if (StringSplit(sparam, '?', Res) != 7) return false; 060. stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 061. if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc; 062. else return false; 063. 064. return true; 065. } 066. //--- 067. }m_ChartTrade; 068. //+------------------------------------------------------------------+ 069. ulong SimulateServer(void) 070. { 071. MqlTradeResult TradeResult; 072. MqlTradeTransaction TradeTrans; 073. 074. ZeroMemory(TradeResult); 075. ZeroMemory(TradeTrans); 076. TradeTrans.type = TRADE_TRANSACTION_HISTORY_ADD; 077. TradeTrans.order = 0; 078. TradeTrans.position = TradeResult.order; 079. OnTradeTransaction(TradeTrans, m_Base.TradeRequest, TradeResult); 080. TradeTrans.type = TRADE_TRANSACTION_REQUEST; 081. m_Base.TradeRequest.symbol = _Symbol; 082. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 083. TradeResult.order = 2048; 084. TradeResult.retcode = TRADE_RETCODE_DONE; 085. OnTradeTransaction(TradeTrans, m_Base.TradeRequest, TradeResult); 086. 087. return TradeResult.order; 088. } 089. //+------------------------------------------------------------------+ 090. ulong SendToPhysicalServer(void) 091. { 092. MqlTradeCheckResult TradeCheck; 093. MqlTradeResult TradeResult; 094. 095. ZeroMemory(TradeCheck); 096. ZeroMemory(TradeResult); 097. if (_Symbol == def_SymbolReplay) 098. return SimulateServer(); 099. if (!OrderCheck(m_Base.TradeRequest, TradeCheck)) 100. { 101. PrintFormat("Order System - Check Error: %d", GetLastError()); 102. return 0; 103. } 104. m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult); 105. if (TradeResult.retcode != TRADE_RETCODE_DONE) 106. { 107. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 108. return 0; 109. }; 110. 111. return TradeResult.order; 112. } 113. //+------------------------------------------------------------------+
Fragmento de C_Orders
Observe que agora, nesta função SimulateServer, onde faremos todo o trabalho de simulação. A ordem dos eventos, já está correta. Assim podemos emitir diversos pedidos de venda ou compra a mercado. Usando para isto o indicador Chart Trade. Note que é o mesmo código visto antes. Tanto que na linha 97, verificamos se estamos usando o replay ou não. Caso seja o replay, iremos na linha 98, efetuar a chamada ao simulador do servidor. De agora em diante iremos nos manter apenas focados nesta nova função. Porém não se preocupem por enquanto, em como as coisas funcionarão no Expert Advisor ou no indicador de posição. Primeiro precisamos dar alguma funcionalidade realmente prática para nosso servidor simulado.
Começando a simular o servidor
Antes de começarmos a de fato fazer o que é dito no título deste tópico. Será preciso que você, meu caro leitor, compreenda o que teremos de fato de fazer. Simular o servidor é uma das tarefas mais legais que faremos nesta sequência de artigos. Isto por que, você passará a entender diversas outras coisas. Mas todas ligadas ao servidor real. Primeiro você deve entender, que os requerimentos, que deveremos simular, já terão sido criados pelas outras funções e procedimentos dentro da classe C_Orders. E estarão dentro da estrutura m_Base.TradeRequest.
Assim tudo que precisaremos fazer será interpretar da forma correta o que esta estrutura contém. Lembrando novamente que não irei checar se os dados estão corretos. Isto seria o equivalente a executar OrderCheck, que é uma função do MQL5. Então tome cuidado, em não tentar jogar dados equivocados na estrutura m_Base.TradeRequest. Pois se isto ocorrer o sistema poderá lhe dar informações equivocadas.
Basicamente precisaremos simular dois tipos de requerimento: TRADE_ACTION_DEAL e TRADE_ACTION_SLTP. Que são os únicos que aparecem no código de C_Orders. Por isto foi importante antes desenvolver todo o sistema usando uma forma de ter contato com o servidor real. Isto para que neste ponto, não percamos muito tempo, implementando requerimentos desnecessários. Já que usando apenas os dois informados conseguiremos de fato, comprar e vender a mercado. Além é claro, fechar uma posição ou modificar o valor do take profit e stop loss.
Muitos de vocês, possivelmente iriam pular a etapa de desenvolver o sistema usando o servidor real. Querendo ir direto para a simulação do sistema de ordens e posição. Mas isto nos traria muito mais dificuldades do que agilidade. Assim como iremos agora implementar o sistema de compra e venda a mercado, antes de desenvolver o sistema de ordens pendentes. Pois uma vez que as operações a mercado tiverem sido completamente implementadas. Implementar o sistema de ordens pendentes será moleza. Uma verdadeira brincadeira de criança.
Para começar a simulação, vamos começar pelo processo mais simples. Este será implementar a simulação do requerimento TRADE_ACTION_SLTP. Isto por que este requerimento independe completamente do tipo de conta que estará sendo simulada. Ou melhor dizendo, do ativo que estaremos manipulando no replay/simulador. Basicamente, este requerimento TRADE_ACTION_SLTP, tem um único propósito. Mudar o valor do stop loss ou do take profit. Só isto. Como no próprio requerimento nos é informado quais são os dados a serem utilizados. Tudo que precisamos fazer é gerar um update no banco de dados. Não tem muito segredo. Porém aqui não vamos simular todas as mensagens disparadas pelo servidor. Vamos simplificar ao máximo as coisas. Se você desejar de fato simular todas as mensagens, poderá fazer isto. Mas como o propósito dos artigos é didático. Faremos a coisa da maneira mais simples possível. Então o novo código pode ser visto no fragmento a seguir.
068. //+------------------------------------------------------------------+ 069. ulong SimulateServer(void) 070. { 071. MqlTradeResult TradeResult; 072. MqlTradeTransaction TradeTrans; 073. bool bResult = false; 074. 075. ZeroMemory(TradeResult); 076. ZeroMemory(TradeTrans); 077. 078. switch (m_Base.TradeRequest.action) 079. { 080. case TRADE_ACTION_SLTP: 081. bResult = ExecCommandSQL(StringFormat("UPDATE tb_Replay SET sl = %f, tp = %f WHERE ticket = %d;", 082. m_Base.TradeRequest.sl, 083. m_Base.TradeRequest.tp, 084. m_Base.TradeRequest.position)); 085. TradeTrans.type = TRADE_TRANSACTION_REQUEST; 086. TradeResult.retcode = TRADE_RETCODE_DONE; 087. if (bResult) 088. { 089. OnTradeTransaction(TradeTrans, m_Base.TradeRequest, TradeResult); 090. return m_Base.TradeRequest.position; 091. } 092. break; 093. case TRADE_ACTION_DEAL: 094. break; 095. } 096. TradeResult.retcode = TRADE_RETCODE_INVALID; 097. OnTradeTransaction(TradeTrans, m_Base.TradeRequest, TradeResult); 098. return 0; 099. } 100. //+------------------------------------------------------------------+
Fragmento de C_Orders
Note que é tudo muito simples e direto. Na linha 80 está o tal evento que iremos capturar. Na linha 81, efetuamos a operação no banco de dados. Caso tenhamos sucesso na linha 89, simulamos o efeito que isto teria no MetaTrader 5. Como sendo a resposta do servidor. Assim como também, se não tivermos sucesso, devido a alguma falha no banco de dados. Na linha 97, simulamos o mesmo tipo de resposta vinda do servidor.
Porém é preciso lembrar uma coisa. Apesar de este código acima de fato simular o que seria feita pelo servidor. Isto de maneira bastante resumida. A classe C_Orders, não conseguirá de fato montar, neste momento o requerimento TRADE_ACTION_SLTP. Isto por que ela ainda está ligada ao servidor de negociação real. Lembre-se que os testes mostrados acima, fazem uma simulação de abertura ou fechamento de posição. Para que possamos resolver adequadamente o requerimento TRADE_ACTION_SLTP, precisaremos mudar o procedimento ModifyValueSLTP, que está presente na classe C_Orders. Ok, antes de continuarmos. Vamos dar uma olhada no código do procedimento ModifyValueSLTP. Este é visto no fragmento abaixo.
129. //+------------------------------------------------------------------+ 130. void ModifyValueSLTP(const ulong ticket, const string symbol, const double sl, const double tp) 131. { 132. ZeroMemory(m_Base.TradeRequest); 133. MqlTradeRequest TradeRequest[1]; 134. 135. if ((sl < 0) || (tp < 0)) if (!PositionSelectByTicket(ticket)) return; 136. m_Base.TradeRequest.magic = m_Base.MagicNumber; 137. m_Base.TradeRequest.action = TRADE_ACTION_SLTP; 138. m_Base.TradeRequest.symbol = symbol; 139. m_Base.TradeRequest.position = ticket; 140. m_Base.TradeRequest.sl = (sl < 0 ? PositionGetDouble(POSITION_SL) : sl); 141. m_Base.TradeRequest.tp = (tp < 0 ? PositionGetDouble(POSITION_TP) : tp); 142. 143. TradeRequest[0] = m_Base.TradeRequest; 144. ArrayPrint(TradeRequest); 145. 146. SendToPhysicalServer(); 147. } 148. //+------------------------------------------------------------------+
Fragmento de C_Orders
Veja que temos alguns problemas aqui. O mesmo tipo de problema que teremos de enfrentar no código do indicador de posição. Ou seja, as chamadas que fazem referência a biblioteca do MQL5, a fim de conseguir ler dados da posição. Então, vamos fazer uma abordagem um pouco diferente. Mesmo que tenhamos de mudar algumas coisas no código. Com isto nasce, um novo arquivo de cabeçalho. Este é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "..\Defines.mqh" 05. #include "..\SQL\C_ReplayDataBase.mqh" 06. //+------------------------------------------------------------------+ 07. class C_InServer : public C_ReplayDataBase 08. { 09. private : 10. bool m_IsReplay; 11. public : 12. //+------------------------------------------------------------------+ 13. C_InServer() 14. :C_ReplayDataBase(), 15. m_IsReplay(_Symbol == def_SymbolReplay) 16. { 17. ExecCommandSQL("CREATE TABLE IF NOT EXISTS tb_Replay ( ticket, type, volume, price, sl, tp, history );"); 18. } 19. //+------------------------------------------------------------------+ 20. inline const double _PositionGetDouble(ENUM_POSITION_PROPERTY_DOUBLE arg) 21. { 22. if (!m_IsReplay) 23. return PositionGetDouble(arg); 24. 25. return 0; 26. } 27. //+------------------------------------------------------------------+ 28. inline const long _PositionGetInteger(ENUM_POSITION_PROPERTY_INTEGER arg) 29. { 30. if (!m_IsReplay) 31. return PositionGetInteger(arg); 32. 33. return 0; 34. } 35. //+------------------------------------------------------------------+ 36. inline const bool _PositionSelectByTicket(ulong arg) 37. { 38. if (!m_IsReplay) 39. return PositionSelectByTicket(arg); 40. 41. return false; 42. } 43. //+------------------------------------------------------------------+ 44. inline const string _PositionGetString(ENUM_POSITION_PROPERTY_STRING arg) 45. { 46. if (!m_IsReplay) 47. return PositionGetString(arg); 48. 49. return ""; 50. } 51. //+------------------------------------------------------------------+ 52. inline const string _PositionGetSymbol(int arg) 53. { 54. return (m_IsReplay ? def_SymbolReplay : PositionGetSymbol(arg)); 55. } 56. //+------------------------------------------------------------------+ 57. inline const int _PositionsTotal(void) 58. { 59. if (!m_IsReplay) 60. return PositionsTotal(); 61. 62. return 0; 63. } 64. //+------------------------------------------------------------------+ 65. }; 66. //+------------------------------------------------------------------+
C_InServer
Agora vamos matar dois coelhos com uma cajadada. Isto por que, ao resolvermos as questões aqui na classe C_Orders, também resolveremos as mesmas questões no indicador de posição. Enfim, agora teremos que atualizar o código da classe C_Orders como mostrado abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_InServer.mqh" 005. //+------------------------------------------------------------------+ 006. class C_Orders : public C_InServer 007. { 008. protected: 009. //+------------------------------------------------------------------+ 010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; } 011. //+------------------------------------------------------------------+ 012. bool ClosePosition(const ulong ticket) 013. { 014. bool IsBuy; 015. string szContract; 016. 017. if (!_PositionSelectByTicket(ticket)) return false; 018. IsBuy = _PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY; 019. szContract = _PositionGetString(POSITION_SYMBOL); 020. ZeroMemory(m_Base.TradeRequest); 021. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 022. m_Base.TradeRequest.type = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); 023. m_Base.TradeRequest.price = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS)); 024. m_Base.TradeRequest.position = ticket; 025. m_Base.TradeRequest.symbol = szContract; 026. m_Base.TradeRequest.volume = _PositionGetDouble(POSITION_VOLUME); 027. m_Base.TradeRequest.deviation = 1000; 028. 029. return SendToPhysicalServer() != 0; 030. }; 031. //+------------------------------------------------------------------+ 032. private : 033. //+------------------------------------------------------------------+ 034. struct stBase 035. { 036. MqlTradeRequest TradeRequest; 037. ulong MagicNumber; 038. bool bTrash; 039. }m_Base; 040. //+------------------------------------------------------------------+ 041. struct stChartTrade 042. { 043. struct stEvent 044. { 045. EnumEvents ev; 046. string szSymbol, 047. szContract; 048. bool IsDayTrade; 049. ushort Leverange; 050. double PointsTake, 051. PointsStop; 052. }Data; 053. //--- 054. bool Decode(const EnumEvents ev, const string sparam) 055. { 056. string Res[]; 057. 058. if (StringSplit(sparam, '?', Res) != 7) return false; 059. stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 060. if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc; 061. else return false; 062. 063. return true; 064. } 065. //--- 066. }m_ChartTrade; 067. //+------------------------------------------------------------------+ 068. ulong SimulateServer(void) 069. { 070. MqlTradeResult TradeResult; 071. MqlTradeTransaction TradeTrans; 072. bool bResult = false; 073. 074. ZeroMemory(TradeResult); 075. ZeroMemory(TradeTrans); 076. 077. switch (m_Base.TradeRequest.action) 078. { 079. case TRADE_ACTION_SLTP: 080. bResult = ExecCommandSQL(StringFormat("UPDATE tb_Replay SET sl = %f, tp = %f WHERE ticket = %d;", 081. m_Base.TradeRequest.sl, 082. m_Base.TradeRequest.tp, 083. m_Base.TradeRequest.position)); 084. TradeTrans.type = TRADE_TRANSACTION_REQUEST; 085. TradeResult.retcode = TRADE_RETCODE_DONE; 086. if (bResult) 087. { 088. OnTradeTransaction(TradeTrans, m_Base.TradeRequest, TradeResult); 089. return m_Base.TradeRequest.position; 090. } 091. break; 092. case TRADE_ACTION_DEAL: 093. break; 094. } 095. TradeResult.retcode = TRADE_RETCODE_INVALID; 096. OnTradeTransaction(TradeTrans, m_Base.TradeRequest, TradeResult); 097. return 0; 098. } 099. //+------------------------------------------------------------------+ 100. ulong SendToPhysicalServer(void) 101. { 102. MqlTradeCheckResult TradeCheck; 103. MqlTradeResult TradeResult; 104. 105. ZeroMemory(TradeCheck); 106. ZeroMemory(TradeResult); 107. if (_Symbol == def_SymbolReplay) 108. return SimulateServer(); 109. if (!OrderCheck(m_Base.TradeRequest, TradeCheck)) 110. { 111. PrintFormat("Order System - Check Error: %d", GetLastError()); 112. return 0; 113. } 114. m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult); 115. if (TradeResult.retcode != TRADE_RETCODE_DONE) 116. { 117. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 118. return 0; 119. }; 120. 121. return TradeResult.order; 122. } 123. //+------------------------------------------------------------------+ 124. ulong ToMarket(const ENUM_ORDER_TYPE type) 125. { 126. double price = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)); 127. double vol = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP); 128. uchar nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS); 129. 130. ZeroMemory(m_Base.TradeRequest); 131. m_Base.TradeRequest.magic = m_Base.MagicNumber; 132. m_Base.TradeRequest.symbol = m_ChartTrade.Data.szContract; 133. m_Base.TradeRequest.price = NormalizeDouble(price, nDigit); 134. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 135. m_Base.TradeRequest.sl = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit); 136. m_Base.TradeRequest.tp = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit); 137. m_Base.TradeRequest.volume = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit); 138. m_Base.TradeRequest.type = type; 139. m_Base.TradeRequest.type_time = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC); 140. m_Base.TradeRequest.stoplimit = 0; 141. m_Base.TradeRequest.expiration = 0; 142. m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN; 143. m_Base.TradeRequest.deviation = 1000; 144. m_Base.TradeRequest.comment = "Order Generated by Experts Advisor."; 145. 146. MqlTradeRequest TradeRequest[1]; 147. 148. TradeRequest[0] = m_Base.TradeRequest; 149. ArrayPrint(TradeRequest); 150. 151. return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0); 152. }; 153. //+------------------------------------------------------------------+ 154. void CloseAllsPosition(void) 155. { 156. for (int count = _PositionsTotal() - 1; count >= 0; count--) 157. { 158. if (_PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue; 159. if (_PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue; 160. ClosePosition(_PositionGetInteger(POSITION_TICKET)); 161. } 162. }; 163. //+------------------------------------------------------------------+ 164. void ModifyValueSLTP(const ulong ticket, const string symbol, const double sl, const double tp) 165. { 166. ZeroMemory(m_Base.TradeRequest); 167. MqlTradeRequest TradeRequest[1]; 168. 169. if ((sl < 0) || (tp < 0)) if (!_PositionSelectByTicket(ticket)) return; 170. m_Base.TradeRequest.magic = m_Base.MagicNumber; 171. m_Base.TradeRequest.action = TRADE_ACTION_SLTP; 172. m_Base.TradeRequest.symbol = symbol; 173. m_Base.TradeRequest.position = ticket; 174. m_Base.TradeRequest.sl = (sl < 0 ? _PositionGetDouble(POSITION_SL) : sl); 175. m_Base.TradeRequest.tp = (tp < 0 ? _PositionGetDouble(POSITION_TP) : tp); 176. 177. TradeRequest[0] = m_Base.TradeRequest; 178. ArrayPrint(TradeRequest); 179. 180. SendToPhysicalServer(); 181. } 182. //+------------------------------------------------------------------+ 183. public : 184. //+------------------------------------------------------------------+ 185. C_Orders(const ulong magic) 186. :C_InServer() 187. { 188. m_Base.MagicNumber = magic; 189. } 190. //+------------------------------------------------------------------+ 191. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 192. { 193. switch (id) 194. { 195. case CHARTEVENT_CUSTOM + evChartTradeBuy : 196. case CHARTEVENT_CUSTOM + evChartTradeSell : 197. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 198. if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev) 199. { 200. case evChartTradeBuy: 201. ToMarket(ORDER_TYPE_BUY); 202. break; 203. case evChartTradeSell: 204. ToMarket(ORDER_TYPE_SELL); 205. break; 206. case evChartTradeCloseAll: 207. CloseAllsPosition(); 208. break; 209. } 210. break; 211. case CHARTEVENT_CUSTOM + evMsgClosePositionEA: 212. ClosePosition((ulong)(lparam)); 213. break; 214. case CHARTEVENT_CUSTOM + evMsgCloseTakeProfit: 215. ModifyValueSLTP((ulong)(lparam), sparam, dparam, 0); 216. break; 217. case CHARTEVENT_CUSTOM + evMsgCloseStopLoss: 218. ModifyValueSLTP((ulong)(lparam), sparam, 0, dparam); 219. break; 220. case CHARTEVENT_CUSTOM + evMsgNewTakeProfit: 221. ModifyValueSLTP((ulong)(lparam), sparam, -1, dparam); 222. break; 223. case CHARTEVENT_CUSTOM + evMsgNewStopLoss: 224. ModifyValueSLTP((ulong)(lparam), sparam, dparam, -1); 225. break; 226. } 227. } 228. //+------------------------------------------------------------------+ 229. }; 230. //+------------------------------------------------------------------+
C_Orders
Sei que pode parecer loucura e uma grande e imensa insanidade, o que estou fazendo e da forma como estou fazendo. Mas quero mostrar, que de fato não existe uma caixa mágica. Que podemos criar e implementar qualquer coisa que imaginarmos. Tudo é uma questão de entender o que se deseja. Programar é algo bastante divertido, justamente por conta disto. Sempre temos problemas para resolver. Então preste bastante atenção ao código, pois não vou entrar em detalhes sobre onde as mudanças aconteceram. Agora nossa classe C_Orders, está de fato atualizada. Vamos voltar a atenção para o código do arquivo de cabeçalho C_InServer.mqh. E implementar as partes onde estaremos no replay/simulador. Assim conseguiremos simular o servidor de negociação. Pelo menos para o sistema de ordens a mercado. Que é nosso objetivo atual.
Simulando o servidor de negociação
Aqui irei mostrar apenas só um pouco mais, dos pontos que serão vistos, de maneira mais intensa nos próximos artigos. O motivo, para isto, é que já existe bastante informação neste artigo, para que você, meu caro e estimado leitor, possa vir a conseguir deglutir e assimilar. Então vamos ver como será a cara da classe C_InServer. Vamos começar com o mais simples. Vou apresentar as coisas uma a uma para que você consiga acompanhar o que estará acontecendo. Começando pelo fragmento que é visto abaixo.
56. //+------------------------------------------------------------------+ 57. inline const int _PositionsTotal(void) 58. { 59. #define macro_ERROR { Print("Error in accessing the database..."); return 0; } 60. 61. struct stLocal 62. { 63. int value; 64. }Info; 65. 66. if (!m_IsReplay) 67. return PositionsTotal(); 68. 69. if (!ExecRequestOfData("SELECT COUNT(*) FROM tb_Replay AS tb WHERE tb.history = 0;")) macro_ERROR 70. if (!GetRegisterOfRequest(Info)) macro_ERROR 71. 72. return Info.value; 73. 74. #undef macro_ERROR 75. } 76. //+------------------------------------------------------------------+
Fragmento de C_InServer
Observe o que estou fazendo neste fragmento. Aqui na linha 59 defino uma macro que será usada apenas e somente neste fragmento. Cada uma das funções na classe C_InServer, terá algo bastante parecido. Com pequenas variações por conta do tipo de retorno que será usado. Mas a macro é apenas local. Então na linha 74 estaremos removendo-a do sistema. Agora observe a linha 61, onde criamos uma estrutura. Esta será usada para acessar o banco de dados e reconhecer quantas posições estão em aberto. Simulando assim a função PositionsTotal, presente na biblioteca do MQL5.
Para fazer a contagem, usamos a linha 69. Se tudo ocorrer sem problemas, na linha 70, iremos tentar obter o valor retornado pelo SQL. E se isto também ocorrer sem problemas, na linha 72 iremos devolver o valor ao chamador. Desta maneira, o chamador, poderá receber o valor adequado. E para ele, tanto o servidor real, quanto o simulado serão praticamente idênticos. Já que o banco de dados estará suprindo o fato de não termos contato com um servidor.
Este tipo de coisa vista neste fragmento irá se repetir por toda a classe C_InServer. Assim, para deleite de vocês, caros leitores. Abaixo, vemos o código na íntegra da classe C_InServer. Fica como tarefa de casa, para cada um, estudar como cada uma das funções trabalha.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Defines.mqh" 005. #include "..\SQL\C_ReplayDataBase.mqh" 006. //+------------------------------------------------------------------+ 007. class C_InServer : public C_ReplayDataBase 008. { 009. private : 010. bool m_IsReplay; 011. struct stLocal 012. { 013. ulong ticket; 014. int type; 015. double volume, 016. price, 017. sl, 018. tp; 019. }m_Info; 020. public : 021. //+------------------------------------------------------------------+ 022. C_InServer() 023. :C_ReplayDataBase(), 024. m_IsReplay(_Symbol == def_SymbolReplay) 025. { 026. ZeroMemory(m_Info); 027. ExecCommandSQL("CREATE TABLE IF NOT EXISTS tb_Replay ( ticket, type, volume, price, sl, tp, history );"); 028. } 029. //+------------------------------------------------------------------+ 030. inline const double _PositionGetDouble(ENUM_POSITION_PROPERTY_DOUBLE arg) 031. { 032. if (!m_IsReplay) 033. return PositionGetDouble(arg); 034. 035. switch (arg) 036. { 037. case POSITION_VOLUME : return m_Info.volume; 038. case POSITION_PRICE_OPEN : return m_Info.price; 039. case POSITION_SL : return m_Info.sl; 040. case POSITION_TP : return m_Info.tp; 041. case POSITION_PRICE_CURRENT : break; 042. case POSITION_SWAP : break; 043. case POSITION_PROFIT : break; 044. } 045. 046. return 0; 047. } 048. //+------------------------------------------------------------------+ 049. inline const long _PositionGetInteger(ENUM_POSITION_PROPERTY_INTEGER arg) 050. { 051. if (!m_IsReplay) 052. return PositionGetInteger(arg); 053. 054. switch (arg) 055. { 056. case POSITION_TICKET : return (long) m_Info.ticket; 057. case POSITION_TIME : break; 058. case POSITION_TIME_MSC : break; 059. case POSITION_TIME_UPDATE : break; 060. case POSITION_TIME_UPDATE_MSC : break; 061. case POSITION_TYPE : return (long) m_Info.type; 062. case POSITION_MAGIC : break; 063. case POSITION_IDENTIFIER : break; 064. } 065. 066. return 0; 067. } 068. //+------------------------------------------------------------------+ 069. inline const bool _PositionSelectByTicket(ulong arg) 070. { 071. bool ret; 072. 073. if (!m_IsReplay) 074. return PositionSelectByTicket(arg); 075. 076. ZeroMemory(m_Info); 077. if (!ExecRequestOfData(StringFormat("SELECT * FROM tb_Replay AS tb WHERE (tb.history = 0) AND (tb.ticket = %d);", arg))) 078. { 079. Print("Error in accessing the database..."); 080. return false; 081. } 082. 083. ret = GetRegisterOfRequest(m_Info); 084. 085. return ret; 086. } 087. //+------------------------------------------------------------------+ 088. inline const string _PositionGetString(ENUM_POSITION_PROPERTY_STRING arg) 089. { 090. if (!m_IsReplay) 091. return PositionGetString(arg); 092. 093. switch (arg) 094. { 095. case POSITION_SYMBOL : return def_SymbolReplay; 096. case POSITION_COMMENT : break; 097. case POSITION_EXTERNAL_ID: break; 098. } 099. 100. return ""; 101. } 102. //+------------------------------------------------------------------+ 103. inline const string _PositionGetSymbol(int arg) 104. { 105. return (!m_IsReplay ? PositionGetSymbol(arg) : (_PositionGetTicket(arg) > 0 ? def_SymbolReplay : "")); 106. } 107. //+------------------------------------------------------------------+ 108. inline const int _PositionsTotal(void) 109. { 110. #define macro_ERROR { Print("Error in accessing the database..."); return 0; } 111. 112. struct stLocal 113. { 114. int value; 115. }Info; 116. 117. if (!m_IsReplay) 118. return PositionsTotal(); 119. 120. if (!ExecRequestOfData("SELECT COUNT(*) FROM tb_Replay AS tb WHERE tb.history = 0;")) macro_ERROR 121. if (!GetRegisterOfRequest(Info)) macro_ERROR 122. 123. return Info.value; 124. 125. #undef macro_ERROR 126. } 127. //+------------------------------------------------------------------+ 128. }; 129. //+------------------------------------------------------------------+
C_InServer
Este código apesar de parecer bastante maluco e confuso. Faz exatamente o que você, que já cria alguns Expert Advisores já conhece e utiliza. Ou seja, usamos alguma função da biblioteca do MQL5, a fim de buscas as informações mais atuais de uma dada posição. Ao fazer esta busca, você consegue verificar se aquele bilhete que indica o número da posição é valido ou não. Quando ele é valido, todos os dados da posição são carregados para dentro de algum tipo de array. Assim o MetaTrader 5, pode nos fornecer os dados no menor tempo possível. Evitando ou no mínimo reduzindo o excesso de acessos ao servidor de negociação.
Já que é muito mais rápido buscar as informações uma única vez, e depois caso elas não tenham mudado, carregar elas da memória. Um típico exemplo de informação que muda muito pouco, é o volume negociado. Assim como também o take profit e stop loss. Mas este sistema visto acima armazena apenas um único bloco. Apesar de não ter certeza. Acredito que o MetaTrader 5, deva armazenar mais blocos. Já que podemos ter diversos ativos com posições em aberto. Mas como iremos usar apenas um. Que será o ativo definido para o replay/simulador. Não vejo problema no uso de um único bloco de memória. Mesmo por que até mesmo os Expert Advisores, antes de fazer qualquer coisa, deveriam verificar se o bilhete da posição anotada, ainda é de fato válido.
Considerações finais
Neste artigo, mostrei como podemos começar a simular o servidor de negociação. Até o momento, a aplicação que estava sendo desenvolvida nesta sequência de artigos. Visava apenas e tão somente simular a parte gráfica. Mas para um sistema mais completo, onde temos a possibilidade de experimentar um Expert Advisor dentro do serviço de replay/simulador. Precisamos também fazer a simulação do servidor de negociação. Você deve ter notado, que a simulação usará o mínimo do mínimo possível. Mas se você, meu caro leitor, desejar, poderá completar as partes que faltam. Mas como isto não fará diferença para o que estou disposto a mostrar. Já temos mais do que o suficiente para o próximo artigo. Então estude este com calma. Aprenda como ele de fato funciona. E nós vemos no próximo artigo desta série, que já está quase no seu "Gran Finale".
| 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) |
| Indicators\Order Indicator.mq5 | Responsável pela indicação de ordens de mercado, permitindo interação e controle das mesmas |
| Indicators\Position View.mq5 | Responsável pela indicação de posições de mercado, permitindo interação e controle das mesmas |
| Services\Market Replay.mq5 | Cria e mantém o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema) |
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.
Desenvolvendo um EA multimoeda (Parte 26): Informador para instrumentos de negociação
Do básico ao intermediário: Sobrecarga de operadores (III)
Redes neurais em trading: Otimização de LSTM para fins de previsão de séries temporais multivariadas (DA-CG-LSTM)
Previsão de barras Renko com a ajuda de IA CatBoost
- 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