English Русский Español Deutsch 日本語
preview
Desenvolvendo um sistema de Replay (Parte 32): Sistema de Ordens (I)

Desenvolvendo um sistema de Replay (Parte 32): Sistema de Ordens (I)

MetaTrader 5Exemplos | 25 outubro 2023, 12:53
586 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay (Parte 31): Projeto Expert Advisor - Classe C_Mouse (V), desenvolvemos a parte base para o uso do mouse no sistema de replay / simulador. Lá não adicionei a questão da roda do mouse, por não ver necessidade, neste primeiro momento de usar tal recurso. Com isto, já podemos começar a trabalhar na parte, que realmente, e sem sombra de dúvidas, é consideravelmente bastante complicada. E isto para dizer o mínimo sobre o que se fato teremos e precisaremos implementar. Tanto em questão de código, quando em questão de outras coisas relacionadas. Com toda a certeza e sem a menor sombra de dúvida, a mais desafiadora de todo o sistema de replay / simulador. Realmente não dá para conseguir efetuar qualquer tipo de estudo, de maneira prática e simples, sem contar com esta parte, que é: O Sistema de Ordens.

De todas as coisas desenvolvidas até aqui. Esta com toda a certeza, vocês também irão notar, e com o tempo irão concordar, que é a mais desafiadora de todas. O que temos de fazer é algo simples. Fazer com que o nosso sistema, simule o que um servidor de negociação efetua na prática. Isto de ter que implementar uma forma de simular, exatamente o que seria feito, pelo servidor de negociação, parece simples. Pelo menos nas palavras. Mas precisamos fazer isto de uma maneira, que para o usuário do sistema de replay / simulação, tudo venha a acontecer, de forma o mais invisível, ou transparente, possível. De fato, o usuário não pode conseguir, quando estiver utilizando o sistema, diferenciar entre um sistema real e um simulado. Mas não é esta a parte realmente mais complicada. A parte realmente mais complicada, e bem mais complicada, é de fato, permitir que o Expert Advisor seja o mesmo, em qualquer tipo de situação.

Dizer que o Expert Advisor deve ser o mesmo, implica não se fazer uma compilação, a fim de usa-lo no simulador, e depois compilar ele novamente, para usar no mercado real. Fazer isto, seria muito mais fácil e simples, em termos de programação. Mas iria gerar problemas, nas questões envolvendo o fato de que o usuário, precisaria ficar recompilando o Expert Advisor a todo o momento. Não queremos de modo algum, que isto aconteça, ou que venha a ser necessário. O que desejamos e iremos fazer, é de fato criar um Expert Advisor, e compilar ele apenas e somente uma vez. Feito isto poderemos usa-lo tanto no mercado real, quanto no simulador já construído.

A parte responsável por usar o Expert Advisor no mercado real, mesmo em uma conta demo, é a parte mais fácil de implementar. Então iremos começar por ela. Um detalhe, iremos começar com o modelo mais básico. Aos pouco iremos tornando o Expert Advisor, cada vez mais e mais complexo, a ponto de que ele consiga exercer, o que de fato queremos fazer, que será usar ele no junto com o replay / simulador, e em uma conta demo ou conta real. A primeira coisa que iremos fazer, é pegar emprestado grande parte do código que já expliquei em outros artigos que foram postados aqui na comunidade. Tais artigos são de minha autoria, então não vejo problemas em usar tais informações. Mas iremos fazer algumas mudanças, a fim de tornar o sistema o mais flexível, modular, robusto e estável quanto for possível de ser feito. Caso contrário poderemos entra em um labirinto, e acabar em algum momento em um beco sem saída. Impossibilitando de todas a formas, que o sistema possa avançar em seu desenvolvimento.

Para iniciar, e isto será apenas o começo, do começo, já que a tarefa é muito complicada. Será preciso primeiramente, você saber como o sistema de herança das classes, está atualmente sendo feito no Expert Advisor, que estamos montando já a alguns artigos, nesta mesma sequência. Este esquema pode ser visto na figura 01:

Figura 01

Figura 01 : Esquema atual de herança das classes do EA

Mas apesar deste esquema funcionar muito bem, para o que foi feito até o momento. Ele esta longe de ser adequado ao que realmente precisamos. Não pelo fato de que é complicado, de adicionar a classe C_Orders, neste esquema de herança. Isto de fato poderia ser feito, fazendo com que a classe C_Orders, herda-se a classe C_Study. Mas não quero que isto aconteça, e o motivo, se deve a uma questão muito prática, e as vezes bastante ignorada, por grande parte dos programadores, que trabalham com OOP ( programação orientada a objetos ). Tal questão é conhecida como: Encapsulamento. Ou seja, saiba apenas o que você precisa para executar o seu trabalho. Quando vamos criando uma hierarquia de classes, não devemos, e este é o termo, permitir que algumas classes, saibam mais do que elas realmente precisam saber. Devemos sempre dar preferencia, em programar a coisa toda, de maneira que cada uma das classes, saiba apenas, e somente, o que ela de fato precisa saber, para poder executar a sua tarefa. Assim manter o encapsulamento e ao mesmo tempo adicionar a classe C_Orders neste esquema mostrado na figura 01, é algo praticamente insustentável. Desta forma, a melhor alternativa para solucionar este problema, é remover a classe C_Terminal, do bloco de herança, visto na figura 01. E a enviar para este mesmo bloco, como sendo um argumento, ou parâmetro, que pode ser usado de uma maneira bem mais adequada. Assim quem irá passar a ter o controle, de quem irá receber, esta ou aquela informação, será o código do Expert Advisor, e isto será importante depois, já que assim iremos conseguir manter o encapsulamento da informações.

Então o novo esquema de classe, já incluindo a que iremos criar neste artigo. Ficará conforme mostrado na figura 02.

Figura 02

Figura 02 : Novo esquema de herança

Neste novo esquema, as classes soltas, na verdade serão, e poderão ser acessadas apenas, e somente, se código do Expert Advisor permitir isto. Como você deve já estar imaginando, será preciso fazer pequenas mudanças no código já existente. Mas estas mudanças não irão afetar muito todo o sistema. Desta forma passarei rapidamente por elas,  apenas para mostrar o que mudou.


Preparando o terreno

A primeira coisa de fato a se fazer, é criar uma enumeração dentro da classe C_Terminal. Esta pode ser vista no fragmento logo abaixo:

class C_Terminal
{

       protected:
      enum eErrUser {ERR_Unknown, ERR_PointerInvalid};

// ... Código interno ...

};

Esta enumeração, irá nos permitir indicar, via variável _LastError, quando um erro acontecer no sistema, seja ele por qual motivo for. No momento, estaremos definindo apenas estes dois tipos de erros.

Nesta fase, vamos modificar a classe C_Mouse, de maneira que agora ela terá algumas coisas, um pouco diferentes em seu código. Não irei entrar em muitos detalhes, já que as mudanças, não afetam de maneira alguma a forma como a classe trabalha. Isto irá apenas direciona o fluxo de mensagens, de uma maneira um pouco diferente, do que estava sendo visto ao usar os sistema de herança. Basicamente posso destacar as seguintes mudanças principais:

#define def_AcessTerminal (*Terminal)
#define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
//+------------------------------------------------------------------+
class C_Mouse : public C_Terminal
{
   protected:
//+------------------------------------------------------------------+
// ... Fragmento Interno ....
//+------------------------------------------------------------------+
   private :
//+------------------------------------------------------------------+
// ... Fragmento interno ...
      C_Terminal *Terminal;
//+------------------------------------------------------------------+

Foi feita a adição de duas novas definições, a fim de evitar ficar repetindo código a todo o momento, isto ajusta bastante. E a adição de uma variável global privativa, a fim de que podemos acessar a classe C_Terminal de maneira adequada. Isto além do fato, de que agora não mais usaremos a herança da classe C_Terminal. Conforme pode ser visto pelo código riscado acima.

Bem, já que não estamos mais utilizando a herança, precisamos fazer outra duas mudanças que merecem destaque. A primeira é vista no constructor da classe C_Mouse:

C_Mouse(C_Terminal *arg, color corH, color corP, color corN)
	:C_Terminal()
   {
      Terminal = arg;
      if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
      if (_LastError != ERR_SUCCESS) return;
      ZeroMemory(m_Info);
      m_Info.corLineH  = corH;
      m_Info.corTrendP = corP;
      m_Info.corTrendN = corN;
      m_Info.Study = eStudyNull;
      m_Mem.CrossHair = (bool)ChartGetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL);
      ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_MOUSE_MOVE, true);
      ChartSetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL, false);
      def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
   }

Removemos a chamada do constructor de classe C_Terminal, do constructor da classe C_Mouse.  Agora temos que receber uma novo parâmetro, a fim de inicializar o ponteiro para a classe. Por questões de segurança, já que não queremos que o nosso código quebre em uma situação não muito adequada. Iremos fazer um teste, a fim de verificar se o ponteiro que permite usar a classe C_Terminal, foi de fato inicializado de forma adequada. 

Isto é feito ao usar esta função CheckPointer, mas como o constructor, não nos permite retornar nenhuma informação de erro. Iremos indicar uma condição de erro, usando um valor predefinido na enumeração presente na classe C_Terminal. Mas como não podemos modificar diretamente o valor da variável _LastError, precisamos fazer uso da chamada SetUserError. Desta maneira podemos testar depois a variável _LastError a fim de saber o que aconteceu.

Mas atenção: Caso a classe C_Terminal, não tenha sido corretamente inicializada, este constructor na classe C_Mouse, irá retornar, não fazendo absolutamente nada. Já que ele não poderá usar a classe C_Terminal, por ela não ter sido inicializada.

A outra modificação que merece algum destaque, é com relação a seguinte função mostrada abaixo:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      int w = 0;
      static double memPrice = 0;
                                
      C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
      switch (id)
      {
//....

O código riscado, deverá ser adicionado no Expert Advisor, a fim de conseguir tratar dos eventos informados pela plataforma MetaTrader 5. Como você pode notar, se isto não for feito, teremos problemas com relação a alguns tipos de eventos, o que pode acabar comprometendo o posicionamento de algum elemento no gráfico. Mas isto será visto depois, por enquanto, apenas removemos o código, desta localização. Poderíamos até permitir que a classe C_Mouse, fizesse a chamada ao sistema de mensagens da classe C_Terminal. Mas como não estamos usando a herança aqui, isto deixaria o código com uma dependência, meio que estranha.

Da mesma forma, como foi feito na classe C_Mouse, faremos na classe C_Study. Mas isto será feito de tal maneira, que a única função que realmente merece algum destaque, é o constructor da classe, que pode ser visto logo abaixo:

C_Study(C_Terminal *arg, color corH, color corP, color corN)
        :C_Mouse(arg, corH, corP, corN)
   {
      Terminal = arg;
      if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
      if (_LastError != ERR_SUCCESS) return;
      ZeroMemory(m_Info);
      m_Info.Status = eCloseMarket;
      m_Info.Rate.close = iClose(def_InfoTerminal.szSymbol, PERIOD_D1, ((def_InfoTerminal.szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(def_InfoTerminal.szSymbol, PERIOD_D1, 0))) ? 0 : 1));
      m_Info.corP = corP;
      m_Info.corN = corN;
      CreateObjectInfo(2, 110, def_ExpansionBtn1, clrPaleTurquoise);
      CreateObjectInfo(2, 53, def_ExpansionBtn2);
      CreateObjectInfo(58, 53, def_ExpansionBtn3);
   }

Observe que recebemos o parâmetro, que nos diz o ponteiro para a classe C_Terminal, e repassamos ele para a classe C_Mouse. Já que estamos herdando ela, e precisamos inicializa-la da forma adequada. Mas de uma forma ou de outra, iremos fazer os mesmos testes, que fizemos no constructor da classe C_Mouse, a fim de garantir que estaremos usando um ponteiro adequado. Agora precisamos prestar atenção a uma coisa: Se você notou, em ambos constructores, tanto em C_Mouse, quanto em C_Study, estamos verificando o valor de _LastError, a fim de saber se alguma coisa, esta fora no esperado. No entanto, dependendo do ativo que estiver sendo usado, a classe C_Terminal, pode ter que inicializar o nome do mesmo, a fim de que o Expert Advisor possa de fato, saber qual é o ativo corrente que está no gráfico.

Se por um acaso isto ocorrer, a variável _LastError, irá conter o valor 4301 ( ERR_MARKET_UNKNOWN_SYMBOL ), indicando que o ativo não foi corretamente detectado. Mas isto não será verdade, já que a classe C_Terminal consegue, dentro do que já esta programado, acessar o ativo correto. Para evitar que este erro, impeça do Expert Advisor permanecer no gráfico, precisamos fazer uma pequena mudança no constructor da classe C_Terminal. Esta pode ser vista abaixo:

C_Terminal()
   {
      m_Infos.ID = ChartID();
      CurrentSymbol();
      m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
      m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
      m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
      m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
      m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
      m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
      m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
      m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
      m_Infos.AdjustToTrade = m_Infos.PointPerTick / m_Infos.ValuePerPoint;
      ResetLastError();
   }

Com a adição do código destacado, iremos dizer, que não existe nenhum erro inicial, assim ao fazer uso do sistema de constructores, a fim de inicializar as classe no código do Expert Advisor. Não precisaremos de fato adicionar esta linha nele, mesmo porque, em alguns casos, você pode acabar por esquecer de fazer esta adição. Ou pior, fazer ela no ponto errado, tornando o código completamente instável e inseguro para ser utilizado.


A classe C_Orders

Pois bem, o que foi visto até o momento, serve para nos ajudar a preparar o terreno para a próxima etapa. Se bem que iremos precisar fazer mais algumas mudanças na classe C_Terminal, e algumas serão feitas ainda neste artigo. Mas passemos a criar a classe C_Orders, a fim de poder nos comunicar com o servidor de negociação. Este no caso será o servidor REAL, que irá ser acessado via corretora. Mas você poderá fazer uso de uma conta DEMO para testar o sistema. Não é aconselhável usar o sistema direto na conta Real.

O código desta classe começará da seguinte forma:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "..\C_Terminal.mqh"
//+------------------------------------------------------------------+
#define def_AcessTerminal (*Terminal)
#define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
//+------------------------------------------------------------------+
class C_Orders
{

Aqui definimos, para facilitar a codificação, algumas coisas para acessar a classe C_Terminal. Tais definições irão deixar de existir no final do arquivo da classe, mas durante todo o restante do tempo, dentro do código da classe. Esta será a forma de acessar a classe C_Terminal, isto para caso venhamos a fazer alguma mudança futura. Não precisemos modificar, ponto a ponto o código da classe, tudo que precisaremos fazer será modificar apenas estas definições. Notem que a classe não está herdando coisa alguma, é importante você notar isto, para não se confundir durante a codificação da mesma, ou das demais classes que irão vim depois.

A próxima coisa que temos, são as declarações, das primeiras variáveis globais, internas da classe. Isto pode ser visto no fragmento abaixo:

   private :
//+------------------------------------------------------------------+
      MqlTradeRequest m_TradeRequest;
      ulong           m_MagicNumber;
      C_Terminal      *Terminal;

Observem o fato, de que tais variáveis globais, são declaradas como sendo privativas, ou seja, elas não poderão ser acessadas fora do código da classe. Um detalhe me merece atenção, é como esta sendo declarada a variável que dará acesso a classe C_Terminal. Notem que ela é de fato declarada como sendo um ponteiro, apesar do uso de ponteiros, não ser feito da mesma forma em MQL5, do que é em C / C++.

ulong ToServer(void)
   {
      MqlTradeCheckResult     TradeCheck;
      MqlTradeResult          TradeResult;
      bool bTmp;
                                
      ResetLastError();
      ZeroMemory(TradeCheck);
      ZeroMemory(TradeResult);
      bTmp = OrderCheck(m_TradeRequest, TradeCheck);
      if (_LastError == ERR_SUCCESS) bTmp = OrderSend(m_TradeRequest, TradeResult);
      if (_LastError != ERR_SUCCESS) PrintFormat("Order System - Error Number: %d", _LastError);
      return (_LastError == ERR_SUCCESS ? TradeResult.order : 0);
   }

Esta função acima, que será uma função privativa, serve para centralizar as chamadas. A decisão de centralizar as chamadas, se deve ao fato, de que desta maneira, será mais simples executarmos a adaptação do sistema depois. Isto a fim de conseguir usar o mesmo esquema, tanto para trabalhar com um servidor real, quanto um servidor simulado. Esta função acima foi retirada, assim como outras que também veremos aqui, da serie de artigos, onde o último artigo neste momento é :  Aprendendo a construindo um Expert Advisor que opera de forma automática ( Parte 15 ): Automação ( VII ). Lá foi explicado como você faz para desenvolver um Expert Advisor automatizado a partir de um Expert Advisor manual. Iremos pegar diversas rotinas presentes naquela serie, a fim de agilizar um pouco o trabalho que será feito aqui. Desta forma você poderá, caso queira usar aqueles mesmos conceitos, para testar, usando o sistema de replay / simulador, um Expert Advisor automático, isto sem de fato usar o Testador de Estratégias presente no MetaTrader 5.

Basicamente a função acima, irá checar algumas coisas junto ao servidor da corretora. Se tudo estiver de acordo, irá enviar um pedido para o servidor de negociação, a fim de executar uma solicitação feita pelo usuário ou pelo Expert Advisor. Já que este pode está sendo operado de maneira automática.

inline void CommonData(const ENUM_ORDER_TYPE type, const double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
   {
      double Desloc;
                                
      ZeroMemory(m_TradeRequest);
      m_TradeRequest.magic        = m_MagicNumber;
      m_TradeRequest.symbol       = def_InfoTerminal.szSymbol;
      m_TradeRequest.volume       = NormalizeDouble(def_InfoTerminal.VolumeMinimal + (def_InfoTerminal.VolumeMinimal * (Leverage - 1)), def_InfoTerminal.nDigits);
      m_TradeRequest.price        = NormalizeDouble(Price, def_InfoTerminal.nDigits);
      Desloc = def_AcessTerminal.FinanceToPoints(FinanceStop, Leverage);
      m_TradeRequest.sl           = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? -1 : 1)), def_InfoTerminal.nDigits);
      Desloc = def_AcessTerminal.FinanceToPoints(FinanceTake, Leverage);
      m_TradeRequest.tp           = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? 1 : -1)), def_InfoTerminal.nDigits);
      m_TradeRequest.type_time    = (IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
      m_TradeRequest.stoplimit    = 0;
      m_TradeRequest.expiration   = 0;
      m_TradeRequest.type_filling = ORDER_FILLING_RETURN;
      m_TradeRequest.deviation    = 1000;
      m_TradeRequest.comment      = "Order Generated by Experts Advisor.";
   }

De maneira análoga, a função acima também está sendo importada da mesma serie de artigos, mas no entanto aqui, tivemos que adapta-la ao novo sistema que está sendo implementado. No entanto, a forma de funcionar da mesma é basicamente a mesma que foi vista na serie sobre automação. Mas para quem não viu, ou leu os artigos da serie, vamos dar uma rápida passada pelo código diferente presente nesta função, este pode ser visto nos pontos em destaque. O que este código faz, é traduzir o valor financeiro, para um valor em termos de pontos. Isto a fim de que você, como usuário, não precise se preocupar em ajustar o numero de pontos frente a uma dada alavancagem. Desta maneira não terá um financeiro muito maior do que o desejado. Fazer isto manualmente é algo muito sujeito a erros e falhas. Mas usando esta função fica bastante simples de fazer a conversão. Ela funciona independentemente do ativo que esta sendo negociado, não importa o ativo, a conversão irá sempre se dar da maneira correta e adequada.

Vamos dar uma olhada nesta função então. Ela está presente na classe C_Terminal. Seu código pode ser visto logo abaixo:

inline double FinanceToPoints(const double Finance, const uint Leverage)
   {
      double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1));
                                
      return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
   };

O grande segredo desta função acima, é justamente o valor que está sendo destacado no código, se você procurar ele irá ver que ele é calculado conforme mostrado no fragmento abaixo:

m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;

Notem que todos os valores presentes acima, e são usados em FinanceToPoints, são dependentes do ativo que está sendo gerenciado e usado para operar. Desta forma, quando FinanceToPoints, executa a conversão, ela de fato estará se adaptando ao ativo que estaremos utilizando no gráfico. Então pouco importa para o Expert Advisor, qual ativo ou qual mercado, ele foi posto para operar. Ele irá conseguir trabalhar em qualquer um, de maneira totalmente análoga, que precisemos nos adaptar a fim de poder usar ele em um mercado ou outro. Agora que já vimos a parte privativa da classe, vamos ver a parte publica da mesma. Começaremos com o constructor, que é visto logo abaixo:

C_Orders(C_Terminal *arg, const ulong magic)
         :m_MagicNumber(magic)
   {
      if (CheckPointer(Terminal = arg) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
   }

De maneira simples, e eficaz, fazemos com que o constructor da classe, tenha acesso a classe C_Terminal. Mas observem como isto se dá de fato. Quando o Expert Advisor cria o objeto C_Terminal, a fim de poder usar a classe C_Terminal, ele também cria um objeto, que irá ser repassado para todas as outras classes, que precisarem de tal objeto. Isto é feito desta forma, onde a classe recebe o ponteiro criado pelo Expert Advisor, a fim de poder ter acesso também a classe já inicializada. Então guardamos o valor na nossa variável global privativa, a fim de poder acessar quando necessário, algum dado, ou função da classe C_Terminal. O fato é, se tal objeto, no caso classe, não estiver apontando para algo útil, isto será indicado como sendo um erro. Já que o constructor não pode retornar nenhum valor, usamos este método, que irá colocar um valor na variável _LastError. Assim poderemos testar isto depois.

Com isto podemos ver as duas últimas funções, presentes na classe neste momento do desenvolvimento. A primeira delas pode ser vista logo abaixo:

ulong ToMarket(const ENUM_ORDER_TYPE type, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
   {
      CommonData(type, SymbolInfoDouble(def_InfoTerminal.szSymbol, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)), FinanceStop, FinanceTake, Leverage, IsDayTrade);
      m_TradeRequest.action   = TRADE_ACTION_DEAL;
      m_TradeRequest.type     = type;

      return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0);
   };

Esta função, é responsável por enviar um pedido de execução a mercado. Aqui praticamente fazemos o uso de todo o código visto anteriormente. Assim podemos fazer uma melhor reutilização do mesmo, promovendo uma maior segurança e performance do mesmo ao longo do tempo. Já que conforme qualquer parte do sistema que esta sendo reutilizado vai sendo melhorado, todo o código irá se beneficiar disto. Alguns detalhes que precisam de alguma atenção no uso deste código acima.

  • Primeiro, estaremos indicando os limites ( take profit e stop loss), como sendo valores financeiros e não atravez de uso de pontuação.
  • Segundo, estamos dizendo ao servidor para executar a ordem de forma imediata, no melhor preço possível e disponível no momento que a ordem for se executada. 
  • Terceiro, apesar de termos acesso a mais tipos de ordens, aqui obrigatoriamente, poderemos usar apenas estes dois tipos, a fim de informa se estaremos comprando ou vendendo. Se isto não for feito assim, a ordem não será enviada.

Estes detalhes são importantes, para que você consiga de fato usar este sistema. Não conhecer, ou ignorar tais detalhes irá lhe dar muitas dores de cabeça, e gerar uma quantidade muito grande de dúvidas na próxima fase de desenvolvimento.

Com isto, chegamos a última rotina desta classe C_Orders. Para o atual estágio de desenvolvimento, esta pode ser vista logo abaixo:

ulong CreateOrder(const ENUM_ORDER_TYPE type, const double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
   {
      double  bid, ask;
                                
      bid = SymbolInfoDouble(def_InfoTerminal.szSymbol, (def_InfoTerminal.ChartMode == SYMBOL_CHART_MODE_LAST ? SYMBOL_LAST : SYMBOL_BID));
      ask = (def_InfoTerminal.ChartMode == SYMBOL_CHART_MODE_LAST ? bid : SymbolInfoDouble(def_InfoTerminal.szSymbol, SYMBOL_ASK));
      CommonData(type, def_AcessTerminal.AdjustPrice(Price), FinanceStop, FinanceTake, Leverage, IsDayTrade);
      m_TradeRequest.action   = TRADE_ACTION_PENDING;
      m_TradeRequest.type     = (type == ORDER_TYPE_BUY ? (ask >= Price ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_BUY_STOP) : 
							  (bid < Price ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_SELL_STOP));                                
                                
      return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0);
   };

Aqui temos algumas coisas muito parecidas com a função de execução a mercado. Como o fato de que os limites são em termos financeiros, e que temos que indicar se estaremos comprando ou vendendo usando apenas um destes dois valores. Apesar disto, se você observar irá notar que fazendo uma coisa um pouco diferente da grande maioria dos códigos presentes em Expert Advisor. Normalmente, quando estamos criando um Expert Advisor, ele irá ser voltado para ser utilizado em um tipo de mercado bastante especifico, seja o mercado de Forex, seja o de Bolsa. Já que no MetaTrader 5, podemos fazer uso de ambos tipos de mercado, precisamos de fazer algum tipo de padronização para facilitar a nossa vida. Mas por que isto ?!?! Não seria a mesma coisa trabalhar no Forex e na Bolsa ?!?! Pelo ponto de vista do usuário SIM, mas pelo ponto de vista da programação NÃO. Se você observar, irá ver que estamos testando qual o tipo de plotagem que esta sendo utilizada no momento. Ao fazer este teste, podemos saber se estamos trabalhando com um sistema que trabalha com o último preço, ou se é baseado em valores BID e ASK. Saber disto é importante, não para posicionar a ordem, mas sim para saber qual o tipo de ordem a ser utilizada. No futuro, iremos precisar implementar estes tipos de ordens no sistema, a fim de simular o que seria feito pelo servidor de negociação. Mas no momento atual, tudo que você precisa saber, é que o tipo de ordem, tem tanta importância, quanto o preço na qual ela será executada. Se você colocar o preço no local correto. Mas erra o tipo de ordem, irá ter problema, já que a ordem será executada, em um momento diferente do que você esperava, ou imaginava que ela seria de fato executada pelo servidor.

É muito comum, o usuário iniciante no MetaTrader 5, errar no que diz respeito ao preenchimento do tipo de ordens pendentes. Não em qualquer tipo de mercado, já que com o tempo, ele acaba se acostumando com o mercado, e não irá errar tão facilmente. Mas quando você fica pulando, de mercado para mercado, a coisa se torna complicada. Se o sistema de plotagem for do tipo BID - ASK, a forma de ajustar o tipo de ordem, é diferente de um sistema de plotagem LAST. As diferenças são sutis mas elas existem, e fazem com que a ordem não fique pendente, mas sim que ela seja executa a mercado, mesmo quando a intenção seria posicionar uma ordem pendente.


Considerações Finais

Apesar de tudo, não haverá nenhum código presente no anexo deste artigo, e o motivo, é o fato de que não implementamos, realmente um sistema de ordens, apenas criamos a classe ( BÁSICA ) para implementar tal sistema. Você pode ter notado, que está faltando diversas rotinas, e procedimentos, ao comparar este código mostrado aqui, na classe C_Orders. Isto frente aos códigos presentes em artigos anteriores, onde expliquei e falei sobre o sistema de ordens.

O fato de isto esta acontecendo, de deve a minha decisão de dividir e repartir, este sistema de ordens, em diversas partes, algumas maiores e outras menores. Isto para que seja possível, explicar de maneira clara e simples, como o sistema irá se integrar ao serviço de replay / simulador. Acredite, fazer isto, não é uma das tarefas mais simples, pelo contrário, é algo bastante complexo, e que envolve muitos conceitos que não sei se você de fato tem. Então será preciso nivelar as coisas aos poucos, para que os artigos seja bem compreendidos e o seu conteúdo não se torne um caos completo.

No próximo artigo, iremos ver como fazer com que este sistema de ordens, possa começar a se comunicar com o servidor de negociação. Pelo menos o físico, a fim que que você possa usar o Expert Advisor, em uma conta DEMO ou REAL. Assim já comece a entender como os tipos de ordens funcionam, para somente assim podermos entrar no sistema simulado. Se for feito o contrário, ou se for colocado tanto o sistema simulado, quanto o real, juntos, a confusão que isto irá causar, não irá permitir que todos consigam acompanhar as explicações. Então nos vemos no próximo artigo.


Arquivos anexados |
Anexo.zip (130.63 KB)
Desenvolvendo um sistema de Replay (Parte 33): Sistema de Ordens (II) Desenvolvendo um sistema de Replay (Parte 33): Sistema de Ordens (II)
Vamos continuar o desenvolvimento do sistema de ordens. Mas você irá ver que iremos fazer uma reutilização massiva de coisas já vistas em outros artigos. Mesmo assim teremos um bônus neste artigo. Iremos desenvolver, primeiramente um sistema que consiga ser operado junto ao servidor de negociação real, seja usando uma conta demo, seja usando uma conta real. Vamos fazer uso massivo e extensivo da plataforma MetaTrader 5, para nos fornecer todo o suporte do qual precisaremos neste inicio de jornada
Desenvolvendo um sistema de Replay (Parte 31): Projeto Expert Advisor - Classe C_Mouse (V) Desenvolvendo um sistema de Replay (Parte 31): Projeto Expert Advisor - Classe C_Mouse (V)
Desenvolver uma forma de colocar o cronometro, de modo que durante um replay / simulação, ele consiga nos dizer quanto tempo falta, pode parecer a principio uma tarefa simples e de rápida solução. Muitos iriam simplesmente tentar adaptar e usar o mesmo sistema que é usado quando temos o servidor de negociação ao nosso lado. Mas aqui mora um ponto que muitos talvez não se atentem ao pensar em tal solução. Quando você está fazendo um replay, e isto para não falar do fato da simulação, o relógio não funciona da mesma forma. Este tipo de coisa torna complexo construir tal sistema.
Teoria das Categorias em MQL5 (Parte 11): Grafos Teoria das Categorias em MQL5 (Parte 11): Grafos
Esse artigo é uma continuação da série sobre como implementar a teoria das categorias no MQL5. Aqui consideramos como a teoria dos grafos pode ser integrada com monoides e outras estruturas de dados ao desenvolver uma estratégia para fechar um sistema de negociação.
Iniciando o VPS MetaTrader pela primeira vez - Instruções passo a passo Iniciando o VPS MetaTrader pela primeira vez - Instruções passo a passo
Para todos que usam Expert Advisors ou assinaturas de sinais, mais cedo ou mais tarde, será necessário um serviço de hospedagem confiável 24 horas por dia para a plataforma de negociação. Recomendamos o uso do VPS MetaTrader por vários motivos. Você pode pagar e gerenciar o serviço através da sua conta na MQL5.community.