English Русский 中文 Español Deutsch 日本語
preview
Aprendendo a construindo um EA que opera de forma automática (Parte 02): Iniciando a programação

Aprendendo a construindo um EA que opera de forma automática (Parte 02): Iniciando a programação

MetaTrader 5Negociação | 20 outubro 2022, 14:45
2 030 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior,  Aprendendo a construindo um EA que opera de forma automática ( Parte 01 ) - Conceitos e estruturas. Apresentei as primeiras etapas das quais você precisa compreender, antes mesmo de iniciar a construção de um EA, que opere de forma automática. Ali mostrei, quais são os conceitos que você deve levar em conta, e qual deverá ser a estrutura que você deverá criar.

Pois bem, não entramos na parte de codificação. Pois lá, estava de fato interessado em tentar nivelar as pessoas. Tentando trazer o iniciante ou entusiasta, que não tem nenhum conhecimento prático em programação, para o mundo do MQL5, a fim de mostrar, como criar um EA capaz de operar de forma totalmente automatizada. Se você não leu o artigo anterior, sugiro que o faça, pois é muito importante, que você saiba onde estará se metendo, quando for trabalhar com um EA automático.

Mas chega de enrolação, vamos começar a desenvolver a coisa. A partir do sistema base, o sistema de ordens, já que este, irá fazer parte de todo e qual quer EA que você for de fato criar.


Planejamento

É bem verdade, que a biblioteca padrão do MetaTrader 5, nos fornece uma forma bastante conveniente e adequada, para trabalhar com um sistema de ordens. Mas aqui irei um pouco além, não quero desmotivar a você de forma alguma, a fazer uso da biblioteca TRADE do MetaTrader 5. Pelo contrário, quero trazer a luz, o que existe por dentro da caixa preta da biblioteca. Fazendo com que todos entendam o que se passa por traz das cortinas, e para isto, precisaremos e iremos desenvolver a nossa própria biblioteca para envio de ordens. Ela não irá contar com todos aqueles recursos, presentes na biblioteca padrão do MetaTrader 5. Na verdade ela terá apenas e somente o básico do básico, para que o sistema de ordens seja criado e se mantenha funcional, robusto e confiável.

Dito estas palavras, precisaremos utilizar um tipo especifico de construção. Me desculpe os que estão iniciando na programação, mas não tem como, você irá ter que se esforçar um pouco para conseguir acompanhar a explicação. Mas prometo tentar deixá-la o mais simples quanto for possível, para que você consiga acompanhar e entender as ideias e conceitos. No entanto, não devo deixar de expressar o fato, de que sem esforço você irá ficar apenas admirando a coisa sendo construída, e não irá de fato conseguir se desenvolver no MQL5, ficando sempre a ver navios. Então vamos começar.


Criando a classe C_Orders

Para criar a classe, precisaremos primeiro criar uma arquivo que irá conter o código da nossa classe, para fazer isto, na janela do navegador, entro do MetaEditor, vá a pasta Include, e clique com o botão direito nesta pasta include, e selecione novo arquivo. Agora siga as figura abaixo:

Figura 01

Figura 01 - Vamos adicionar um tipo de arquivo de inclusão

Figura 02

Figura 02 - Desta forma será criado o arquivo que precisamos


Ao fazer o que é mostrado na figura 01 e figura 02, será criado um arquivo, e ele já será aberto pelo MetaEditor, seu conteúdo pode ser visto logo abaixo. Mas antes devo dar uma rápida explicação sobre algo. Observem a figura 01, vejam que nela podemos criar diretamente uma classe, a pergunta é porque não faço assim ?!?! Esta de fato é uma pergunta válida. O motivo é que se você criar a classe, usando o ponto presente na figura 01, a classe não será criada totalmente do zero. Ela já irá começar contendo algum tipo de informação, ou formatação, e queremos que ela seja criada do zero. Mas voltemos ao código.

#property copyright "Daniel Jose"
#property link      ""
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Todas as linhas em cinza claro são comentários (no caso um exemplo de código), você pode apagar tais linhas se desejar, mas o que nos interessa começa a partir de agora. Uma vez que este arquivo esteja aberto, vamos começar a colocar código dentro dele, e isto não será feito de qualquer maneira, aqui vamos adicionar o código, que irá nos ajudar a trabalhar com o nosso sistema de ordens.

Mas para que ele seja seguro, confiável e robusto, vamos fazer uso de uma coisa da qual o MQL5 trouxe do C++. As classes. Quem não sabe o que são classes, deve dar uma estudada sobre o assunto, não precisa ir direto na documentação sobre classes presentes no C++. Na verdade você pode começar lendo sobre as classes na documentação do MQL5, isto já lhe dará uma bom ponto de partida. Além do que será bem mais simples de entender, do que toda aquela confusão que C++ faz, para quem nunca ouviu falar de classes, o conteúdo da documentação do MQL5, já será mais que o suficiente.

Mas de uma forma geral, uma classe é com toda a certeza, a forma mais segura e eficiente de você isolar o código de outras partes do mesmo. Isto faz com que a classe não seja vista como um código, mas sim como um tipo de dado especial, onde você pode fazer bem mais do que apenas usando as primitivas, como integer, double, boolean, entre outras. Ou seja para o programa, a classe seria uma ferramenta multiuso, ele não precisa de fato saber nada a respeito de como a classe foi criada, e o que ela contém. Ele apenas precisa saber como usá-la. Pense em uma classe, como se fosse uma ferramenta elétrica. Você não precisa saber como ela foi construída, ou quais os componentes que estão nesta ferramenta. Tudo que você precisa saber é como ligar e usar a ferramenta, a forma como ela funciona, não vai fazer diferença na forma de você a utilizar. Esta seria uma definição bem simples de classe.

Bem, mas vamos continuar. A primeira coisa que fazemos, é gerar as seguintes linhas:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
class C_Orders
{
        private :
        public  :
};
//+------------------------------------------------------------------+

Estas linhas, são a parte mais básica de nossa classe. Normalmente usamos o mesmo nome da classe, como sendo o nome do arquivo na qual ela está. Isto não é obrigatório, mas ajuda a você encontrar a classe depois. Dentro da classe, indico duas palavras reservadas, estas indicam o nível de compartilhamento das informações presentes entre elas. Caso você não adicione tais palavras, todos os elementos da classe serão tratados como sendo públicos, ou seja, qualquer um poderá ler, escrever ou acessar eles. Isto quebra algumas das características marcantes que nos faz usar classe. A segurança e robustez, já que qualquer parte do código poderá acessar e mudar o que estará dentro da classe.

Então resumindo: Tudo que estiver entre a declaração da palavra private, e a palavra public, será e poderá ser acessado, apenas e somente dentro da classe. Você pode usar variáveis globais aqui, e elas não poderão ser acessadas fora do código da classe. Tudo declarado depois da palavra public, poderá ser acessado por qualquer parte do código, não interessa se faz parte ou não da classe. Qualquer um poderá acessar o que estiver ali.

Uma vez que criamos esta arquivo, como mostrado acima, podemos adicionar ele ao nosso EA. Então nosso EA irá ficar com o seguinte código:

#property copyright "Daniel Jose"
#property version   "1.00"
#property link      "https://www.mql5.com/pt/articles/11223"
//+------------------------------------------------------------------+
#include <Generic Auto Trader\C_Orders.mqh>
//+------------------------------------------------------------------+
int OnInit()
{
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+

Neste momento, a nossa classe foi incluída no EA ao usar a diretiva de compilação Include. Então ao fazer uso desta diretiva, o compilador irá entender, que a partir daquele momento em diante, o arquivo de cabeçalho C_Orders.mqh presente no diretório include na pasta Generic Auto Trader, deverá ser incluído no sistema e compilado. Existem alguns macetes nisto, mas não vou entrar nestes detalhes, pelo menos aqui, já que isto iria deixar muitas pessoas menos experientes, completamente perdidas, tentando entender alguns destes detalhes. Mas o que esta acontecendo é justamente o que acabei de descrever.


Definindo as primeiras funções da classe C_Orders

Diferentemente do que muitos pensa, ou imaginam, sobre o que de fato é programar as coisas. Um programador, não vai logo digitando códigos e mais códigos, muito pelo contrário, ele pensa, e estuda o que ele de fato precisa fazer, antes de realmente começar a fazer qualquer coisa.

Então você que esta começando, também deve fazer o mesmo tipo de coisa. Pensar. Isto antes mesmo de procurar adicionar qualquer linha de código. Vamos pensar um pouco. O que precisamos de fato ter na classe C_Orders ?!?! Isto para ter o mínimo de trabalho possível, e utilizar o máximo que a plataforma MetaTrader 5 pode nos oferecer ?!?!

A única coisa que realmente vem a mente, é uma forma de enviar ordens, não precisamos realmente de algum tipo de meios para mover as ordens, remover alguma ordem, ou mesmo fechar uma posição. Não, para isto temos a plataforma MetaTrader 5, que nos oferece este tipo de recurso, já que ao lançar uma ordem no gráfico, a mesma irá aparecer na caixa de ferramentas, na aba Negociação, então não precisamos fazer muita coisa com relação a isto.

Uma outra coisa é que não precisamos de fato, isto no primeiro momento, de algum mecanismo para mostrar as ordens, ou posições no gráfico, a própria plataforma faz isto para nos. Então de fato a única coisa que precisamos, é de algum meio para enviar ordens diretamente do EA, sem realmente precisar passar pelo sistema de boleta, da plataforma MetaTrader 5.

Com base nesta ideia, e neste pensamento, podemos começar a programar, o que realmente iremos precisar, pelo menos neste primeiro momento, ou seja uma função ou procedimento, que permita ao EA enviar ordens. E é neste ponto, que muita gente começa a fazer bobagens, principalmente aqueles que não tem tanto conhecimento, ou experiência em programação.

Já que definimos que iremos utilizar o MetaTrader 5, neste primeiro momento, como sendo o gerenciador de ordens e posições, utilizando a caixa de ferramentas, presente na plataforma. Precisamos começar a desenvolver o sistema de envio de ordens, para o servidor, pois esta é a parte mais básica de todas. Mas para fazer isto, precisaremos saber de algumas coisas sobre o ativo, no qual o EA estará observando, isto para evitar ficar a todo momento buscando estas informações, via chamadas de procedimento da biblioteca padrão do MQL5. Então vamos começar, definindo alguma variáveis globais, dentro da nossa classe.

class C_Orders
{
        private :
//+------------------------------------------------------------------+
                MqlTradeRequest m_TradeRequest;
                struct st00
                {
                        int     nDigits;
                        double  VolMinimal,
                                VolStep,
                                PointPerTick,
                                ValuePerPoint,
                                AdjustToTrade;
                        bool    PlotLast;
                }m_Infos;

Estas variáveis, e não se preocupe por enquanto com elas, irão manter os dados que precisamos, visíveis por toda a classe, mas não tente acessar elas fora da classe, pois isto não será possível, dado o fato de que elas estão sendo declaradas depois da clausula private, ou seja, elas serão privativas da classe, não podendo ser acessadas ou visualizadas fora da classe.

Muito bem, mas precisamos inicializar estas variáveis de alguma forma. Existem alguns métodos para fazer isto, mas por experiência, sei que muitas das vezes acabando por esquecer de fazer isto, então vamos fazer uso, de uma forma, onde nunca iremos nos esquecer, e isto é feito utilizando um contructor de classe, conforme é visto abaixo:

                C_Orders()
                        {
                                m_Infos.nDigits         = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
                                m_Infos.VolMinimal      = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
                                m_Infos.VolStep         = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
                                m_Infos.PointPerTick    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
                                m_Infos.ValuePerPoint   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
                                m_Infos.AdjustToTrade   = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
                                m_Infos.PlotLast        = (SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_LAST);
                        };

Os contructores de classe, são uma das formas mais seguras de inicializarmos uma classe, para depois trabalhar com ela. Mas observem, que aqui estou utilizando um constructor default, ou seja ele não recebe nenhum parâmetro, existem casos em que o constructor, irá e poderá receber parâmetros, para inicializar alguma coisa dentro da classe, mas aqui não precisamos fazer isto.

Agora observem, o fato de que cada uma das variáveis é inicializada, de uma forma bem especifica, de maneira que ao acessarmos qualquer um dos valores, seja para um teste, seja para um cálculo especifico, teremos ele devidamente inicializado. Por isto, você deve sempre dar preferência em utilizar os constructores de classe, para inicializar as suas variáveis globais e interna da classe, antes de qualquer outra coisa ser feita, em termos de código.

Mas já que estaremos utilizando um constructor, precisamos, e muitas vezes será necessário, declarar um destructor da classe, este pode ser tão simples quanto visto logo abaixo:

                ~C_Orders() { }

Reparem no detalhe da sintaxe. Todo o contructor irá ter o mesmo nome da classe, e todo destructor terá também o mesmo nome da classe, mas diferente do constructor, a declaração do destructor, é antecedida por um til ( ~ ) e um outro detalhe também importante, tanto o constructor, quanto o destructor, NÃO retornam nenhum tipo de valor. Fazer, ou tentar fazer isto, é considerando um erro, e irá impedir de seu código venha a ser compilado.

Então caso você precise, retornar algum valor, durante a inicialização, ou o encerramento da classe, você deverá utilizar uma chamada de procedimento convencional. Não poderá utilizar os constructores ou destructores para isto.

Muito bem, mas como de fato este tipo de codificação, usando constructores e destructores irá me ajudar ?!?! Como disse acima, é muito comum você as vezes acessar, ou tentar utilizar alguma variável global da classe, sem que esta esteja de fato inicializada. Acredite, isto é muito comum, mesmo para programadores experientes, vira e mexe ele se vê, tendo seu programa gerando dados estranhos, e quando vai observar o por que disto, é por conta que as variáveis globais da classe não foram inicializadas.

Então não crie o mal hábito de programar, utilizando programação orientada a objetos, sem de fato fazer uso de constructores e destructores. Pois isto vai lhe dar muitas dores de cabeça, e horas e horas perdidas, tentando entender por que o programa não esta funcionando corretamente.

Agora antes de continuarmos, quero chamar a sua atenção para um fato, presente no constructor da classe, este é visto em destaque logo abaixo:

        m_Infos.PlotLast = (SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_LAST);

Diferente do que muitos pensam, existem diferenças entre mercados, não do tipo de diferença que muitos, e até mesmo você pode estar acostumado, ou imaginando. A diferença é na forma de plotagem utilizada pelo ativo, e esta linha acima, faz justamente isto, ela verifica qual o tipo de plotagem das barras, que o ativo estará utilizando.

E por que isto é importante ?!?! Bem, se você deseja criar um EA, que possa funcionar tanto em um mercado, quanto em outro, deverá saber que mercados diferentes, e até mesmo ativos diferentes, podem ter sistemas de plotagem diferentes. Basicamente existem 2 principais:

  • Plotagem BID, que pode ser vista no mercado de FOREX;
  • Plotagem LAST, que é vista normalmente no mercado de bolsa.

Estes dois tipos apesar de parecerem iguais, não são pelo ponto de vista do EA, e consequentemente pelo ponto de vista do servidor de negociação. Esta diferença fica mais evidente, e tem mais peso, no momento que o EA começar a enviar ordens, não ordens a mercado, que neste caso seriam executadas no melhor preço, mas sim ordens pendentes, e aqui a ideia é gerar uma classe, que consiga enviar justamente este tipo de ordem, ou seja ordens pendentes, justamente as que ficarão no book de oferta.

Você pode pensar: Mas ainda não entendi, por que motivo o EA precisa saber qual o sistema de plotagem utilizado ?!?! Calma, o motivo é simples: Quando o ativo utiliza o sistema de plotagem BID, o valor do preço LAST será sempre zero, e neste caso, o EA não conseguirá enviar alguns tipos de ordens, para dentro do servidor, já que ele não conseguirá saber qual o tipo de ordem correta, e se mesmo assim ele conseguir enviar tal ordem, e o servidor aceitar a ordem, ela será executada de forma incorreta, já que o seu preenchimento foi feito de forma errada, ao indicar o tipo de ordem.

Este problema, acontece caso você tente utilizar um EA criado para mercado de BOLSA, em um mercado de FOREX. Agora vamos ver o contrário, se o sistema de plotagem do tipo LAST, mas o EA foi compilado e construído, para trabalhar no mercado de FOREX, ou seja onde o sistema de plotagem é do tipo BID, neste caso o problema será que o EA, nunca irá lançar as ordens em um ponto adequado, já que o preço LAST, pode ficar variando enquanto o BID e o ASK, ficam ali, muitas vezes parado.

Mas neste caso, e diferente do FOREX, a distancia entre o BID e o ASK, sempre será maior do que ZERO, ou seja, irá sempre existir um spread entre eles, o problema é que ao fazer a montagem da ordem, que vai ser enviada para o servidor, o EA pode criar a ordem de uma forma errada, apesar de ser menos comum, isto pode acontecer, ainda mais se você não observar ambos os valores ( o valor BID e ASK ) ao criar a ordem. E para um EA, que busque operar dentro do spread existente, este tipo de falha pode ser fatal, e custar uma bela quantia de dinheiro.

Por conta disto, o EA estará verificando, em qual tipo de mercado, ou melhor, qual o sistema de plotagem, em que ele estará de fato trabalhando, assim ele sempre poderá ser utilizado, ou portado de um mercado do tipo FOREX, para um de BOLSA, ou vice versa, sem precisar sofrer nenhuma modificação, ou necessitar ser compilado novamente.

Notaram como as coisas são bem sutis. Um simples detalhe pode por tudo a perder. Ok, mas vamos agora ver como iremos fazer, para enviar uma ordem pendente, para dentro do servidor de negociação.


Enviando uma ordem pendente para o servidor

Ao observarmos a documentação do MQL5, a procura de como o sistema de negociação funciona, nos deparamos a seguinte função OrderSend, lembrando que você não precisa fazer uso direto desta função, você pode usar a biblioteca padrão do MetaTrader 5 para enviar ordens para o servidor, neste caso bastaria fazer uso da classe CTrade, mas aqui quero abrir a caixa preta, mostrando como a coisa funciona por dentro.

Então tudo que precisamos é utilizar a função OrderSend, para assim poder enviar requisições de negociação para o servidor, mas você deve tomar cuidado, ao fazer uso direto desta função, então para garantir que ela será utilizada apenas, e somente dentro da classe, iremos colocar ela dentro da clausula privativa, desta forma poderemos separar toda a complexidade, no preenchimento do requerimento, dentro das funções publicas da classe, que poderão ser acessadas pelo EA.

Mas devemos tomar todo o cuidado, sempre fazendo os teste necessários, a fim de que possamos saber quando a coisa deu errado, e quando deu certo. Já que todos os requerimentos, terão obrigatoriamente, que passar pela função OrderSend, então é bem adequado, criamos um procedimento apenas para ela, desta forma nasce o código abaixo:

                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) MessageBox(StringFormat("Error Number: %d", GetLastError()), "Order System", MB_OK);
                
                                return (_LastError == ERR_SUCCESS ? TradeResult.order : 0);
                        }

Não precisa ficar assustado com este código acima, o que ele faz, resumidamente, é garantir que o envio seja feito, e quando for feito, saibamos se ele foi bem sucedido ou não, e caso não tenha sido, somos alertados imediatamente da falha. Então vamos ver com calma, como ele faz este trabalho. A primeira coisa que fazemos é zerar a variável interna de erro, desta forma qualquer erro gerado, poderá ser analisado em qualquer ponto do EA.

Mas lembre-se do seguinte: Nunca deixe de testar o retorno dos procedimentos mais críticos. Feito isto, zeramos as estruturas internas da função, para depois enviar um pedido de checagem da ordem. Agora atenção: Eu não estou verificando se esta checagem, gerou ou não alguma falha, de forma a saber qual a falha que ocorreu, ao invés disto, verificamos o valor da variável de erro, caso ela indique um valor diferente do que esperamos, o envio ao servidor não será feito, mas se a checagem não gerou nenhum erro, teremos o envio do pedido ao servidor de negociação.

Um detalhe que faz toda a diferença. Independentemente de quem gerou o erro, caso tenha sido gerando um erro, teremos a apresentação de uma mensagem, no gráfico informando o numero do erro que aconteceu, se tudo ocorreu perfeitamente, a mensagem não será mostrada, ou seja, desta forma não interessa qual tipo de requerimento estejamos fazendo o servidor, se fizermos uso somente e apenas do procedimento visto acima, teremos sempre o mesmo tipo de tratamento, caso seja necessário mais informações ou testes, bastará adiciona-los neste procedimento acima, fazendo assim você sempre irá garantir, que o sistema seja o mais estável e confiável possível.

Como retorno da função, poderemos ter dois valores: O ticket da ordem, que será sempre retornado pelo servido em caso de sucesso, ou o valor zero, que neste caso indicaria erro, e para que o EA saiba qual foi o erro, bastará que ele olhe o valor da variável _LastError, neste caso, você poderia dispensar a mensagem que se encontra neste procedimento. Em um EA automático, de fato esta mensagem não estaria aqui, ela estaria em outro local, ou seria apenas uma mensagem de log, não aparecendo em nenhum outro lugar, mas tudo isto depende do proposito do EA, já que a ideia aqui é mostrar como criar um, precisamos que você saiba, de onde esta sendo originada as mensagens. Então decidi deixa-la ali.

Agora tem um outro problema, que também precisamos solucionar, é o seguinte: Você não pode enviar para o servidor qualquer preço, caso você faça isto a ordem poderá ser negada, você precisará informa o preço correto, já vi algumas pessoas dizerem que bastará normalizar o valor, que ele já ficará correto, mas não, isto não irá de fato funcionar, salvo em casos bem específicos, você de fato precisará fazer um pequeno cálculo, a fim de informar ao servidor o preço correto, que será usado na ordem, para fazer isto usaremos a seguinte função, vista logo abaixo:

inline double AdjustPrice(const double value)
                        {
                                return MathRound(value / m_Infos.PointPerTick) * m_Infos.PointPerTick;
                        }

Este simples cálculo, irá corrigir o preço de forma que independentemente do valor que for digitado, ele será corrigido para um valor adequado, assim o servidor irá aceita o preço indicado.

Muito bem, menos uma preocupação. Mas apenas e somente criar a função acima, não faz de fato a nossa classe criar, ou enviar uma ordem para dentro do servidor, para fazer isto precisaremos preencher uma estrutura, que você deve ter notado que aparece no código acima, esta estrutura é a MqlTradeRequest, e ela é acessa via variável global privativa, chamada m_TradeRequest, precisamos preencher esta estrutura de forma adequada.

Mas cada um dos requerimentos, é exigido um tipo especifico de preenchimento, no entanto a nossa ideia aqui, e agora é simplesmente enviar um pedido, para que o servidor possa adicionar uma ordem pendente no ativo, e quando isto acontecer, a plataforma MetaTrader 5, irá mostrar uma ordem pendente no gráfico e a caixa de ferramentas irá mostrar a ordem pendente.

Preencher esta estrutura, costuma ser algo extremamente sujeito a erros e falhas, sendo uma das principais causas de problemas encontrados em EA, seja ele manual ou automático. Por conta disto, vamos utilizar a nossa classe de forma a gerar um tipo de abstração, e assim facilitar muito o preenchimento correto, e adequado dos valores, de maneira que a nossa requisição, possa ser aceita pelo servidor de negociação.

Mas antes de criar de fato a função, precisamos pensar um pouco. Teremos que responder as seguintes perguntas:

  • Que tipo de negócio iremos fazer ( Compra ou Venda ) ?
  • Qual o preço que desejo efetuar o negócio ?
  • Qual o volume que irei negociar ?
  • Qual o prazo desejado para a operação ?
  • Que tipo de ordem devo usar: Compra Limite, Compra Stop, Venda Limite ou Venda Stop ( não irei usar Venda Stop Limite ou Compra Stop Limite ) ?
  • Qual o mercado que estarei operando: Forex ou Bolsa ?
  • Qual o Stop financeiro que aceito tomar ?
  • E qual Take Profit financeiro que pretendo alcançar ?
  • Veja que são diversas perguntas, e algumas delas, não podem ser utilizadas diretamente na estrutura requerida pelo servidor. Então fazemos uma abstração, de forma a conseguir gerar uma linguagem, ou modelagem mais pratica, assim nosso EA poderá trabalhar tranquilo, deixamos toda a complexidade de fazer os ajustes e correções, para a classe executar, desta forma nasce a seguinte função:

                    ulong CreateOrder(const ENUM_ORDER_TYPE type, double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
                            {
                                            double  bid, ask, Desloc;                      
    					
                                    	Price = AdjustPrice(Price);
                                            bid = SymbolInfoDouble(_Symbol, (m_Infos.PlotLast ? SYMBOL_LAST : SYMBOL_BID));
                                            ask = (m_Infos.PlotLast ? bid : SymbolInfoDouble(_Symbol, SYMBOL_ASK));
                                            ZeroMemory(m_TradeRequest);
                                            m_TradeRequest.action           = TRADE_ACTION_PENDING;
                                            m_TradeRequest.symbol           = _Symbol;
                                            m_TradeRequest.volume           = NormalizeDouble(m_Infos.VolMinimal + (m_Infos.VolStep * (Leverage - 1)), m_Infos.nDigits);
                                            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));
                                            m_TradeRequest.price            = NormalizeDouble(Price, m_Infos.nDigits);
                                            Desloc = FinanceToPoints(FinanceStop, Leverage);
                                            m_TradeRequest.sl               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? -1 : 1)), m_Infos.nDigits);
                                            Desloc = FinanceToPoints(FinanceTake, Leverage);
                                            m_TradeRequest.tp               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? 1 : -1)), m_Infos.nDigits);
                                            m_TradeRequest.type_time        = (IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
                                            m_TradeRequest.type_filling     = ORDER_FILLING_RETURN;
                                            m_TradeRequest.deviation        = 1000;
                                            m_TradeRequest.comment          = "Order Generated by Experts Advisor.";
                                    
                                            return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0);
                    };
    

    O que esta simples função acima faz, é algo maravilhoso. Isto por que ela pega os dados, que queremos negociar, e os transforma em algo que o servidor de negociação espera receber, e faz isto de forma que pouco importa, se você esta trabalhando com um ativo do mercado de FOREX, ou um ativo do mercado de BOLSA.

    Então tudo que você precisa fazer é dizer a esta função: Se você vai comprar ou vender. Qual o preço que você deseja. Quanto você aceita perder em termos financeiros. Quando deseja ganhar financeiramente. Qual o nível de alavancagem que você quer usar. E se a operação é do tipo Day Trade ou mais longa. Somente isto, e todo o resto, a função mesmo irá calcular, e preencher de forma que a ordem, seja criada de maneira correta. Mas para isto, iremos precisar fazer diversos cálculos e ajustes, de maneira que os valores sejam adequados aos esperados pelo servidor.

    Porém, temos dois pontos que você deverá observar, e que muitas vezes faz com que a ordem seja recusada, pelo servidor, mas aqui não teremos este problema. Estes pontos são o Take Profit e o Stop Loss. Caso você informe o valor zero, em qualquer como valor financeiro, a função não irá gerar o valor de Take Profit ou Stop Loss, então você deve tomar o cuidado, de apenas informa o valor de zero, quando desejar ter em carteira um ativo, ou se a estratégia assim exigir, pois assim os limites de ganho e perda não serão criados ( apesar de depois você poder faze-lo eles não serão criados aqui neste momento ), um outro detalhe, é que valores negativos para o Stop Loss, não fazem sentido para a função no caso de você estar comprando, e valores negativos para o Take Profit, também não fazem sentido no caso de você esteja vendendo. A função irá ignorar este tipo de coisa, então você pode simplesmente trabalhar com valores positivos, que tudo vai da certo.

    Apenas olhando esta função você pode estar confuso, achando que usa-la é muito complicado e difícil, mas assim que você a vir funcionando, irá mudar de ideia. Mas antes de ver a coisa funcionando, vamos ver e entender uma função presente no procedimento acima, que é a FinanceToPoints, esta tem o seu código visto logo abaixo:

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

    Vamos entender o que esta acontecendo aqui, pois é um pouco mais complicado do que pode parecer, e se você não entender esta função, não irá de fato entender o que estará acontecendo, quando a classe enviar a ordem para o servidor, e este colocar os limites da ordem ( take profit e stop loss ) em determinadas posições, deslocado do preço de entrada.

    Todo e qualquer ativo, tem algumas informações que podemos usar para conseguir converter um dado valor financeiro em um valor de pontos. Para entender vejam as figuras abaixo:

    Figura 05  Figura 06  Figura 07


    Todas estas figura acima, tem pontos destacados, e estes são usados no cálculo responsável por converter um valor financeiro em pontos, no caso do FOREX, o tamanho do ticket e o valor do mesmo não aparecem, mas eles são 1.0 para o valor do ticket e 0,00001 para o tamanho ( numero de pontos ) por ticket. Não se esqueça destes valores.

    Agora vamos pensar no seguinte: O valor financeiro é o resultado da fatoração, entre o volume negociado, pela quantidade de pontos, que se faz multiplicado pelo valor de cada ponto, ou seja: Suponhamos que você esteja operando um ativo qualquer, e este tenha como volume mínimo igual a 100, o passo do volume, normalmente, será igual a este volume mínimo, e você venha a alavancar em 2x, ou seja duas vezes o volume mínimo. Desta forma, o volume que você estará negociando é de 100 x 2 o que dá 200. Por enquanto não se preocupe com este valor, vamos continuar. Precisamos saber quando vale cada ponto. Para fazer isto executamos o seguinte cálculo:

    Valor de 1 ponto = Valor por ponto / Numero de pontos. Muita gente assume que o numero de pontos é 1, e isto é um erro, você pode ver que nas figuras acima, o numero de pontos pode variar, mas no Forex ele é de 0,00001, ou seja, não caia nesta de assumir que um valor, é o que parece mais adequado, faça o programa captura o valor correto, e use este valor, então vamos assumir, para exemplificar, que o numero de pontos é de 0,01, e o valor de cada ponto é de 0,01, desta forma dividindo um pelo outro, teremos como resultado o valor de 1. Novamente este valor pode mudar, vamos ver no forex por exemplo, ele seria de 0,00001, e no Dólar negociado na B3 ( Bolsa do Brasil ) ele é de 10.

    Agora vamos entender outra coisa. Para facilitar iremos assumir que o usuário, deseje fazer uma operação em um ativo cujo volume mínimo é de 100, e que ele irá se alavancar em 3 vezes, o tamanho do ticket é de 0,01, e o valor do ticket é de 0,01, no entanto ele deseja assumir um risco financeiro de 250. Quantos pontos ele precisa colocar deslocados do preço de entrada, para que o valor corresponda a estes 250 ?!?! Pois bem, é justamente isto que o procedimento acima faz. Ele calcula o valor, e ajusta ele de maneira que teremos um deslocamento positivo ou negativo, dependendo do caso, de forma que o financeiro irá ser de 250. O que daria no caso 2,5 ou 250 pontos.

    Neste exemplo, parece ser simples, mas tente fazer isto rapidamente, enquanto estiver operando no mercado, com a volatilidade deixando as coisas tensas, e você tem que decidir ali, na hora, qual o tamanho da mão a ser usada, sobre um dado valor financeiro, a fim de não colocar em risco muito mais do que aceita perder, e não se arrepender de ter colocado menos do que poderia. Ai você percebe o quanto é importante, ter um EA bem programado ali, ao seu lado.

    Desta forma, a nossa classe já conseguirá permitir que o EA envie uma ordem para o servidor, de forma que seja configurada corretamente, mas para ver como isto é feito, vamos criar uma maneira de testar o nosso sistema de ordens.


    Criando uma forma de testar o sistema de ordens ...

    Quero ressaltar, que o intuito aqui, desta sequencia não será criar um EA, que possa ser operado manualmente, a ideia aqui, é demonstrar como você, aspirante a programador, pode conseguir criar um EA, capaz de operar de forma automática, com o mínimo esforço possível. Caso queira saber, de fato, como criar um EA, para operar de forma manual, veja a serie de artigos Desenvolvendo um EA de negociação do zero, lá mostro como você pode desenvolver um EA, próprio para operar de forma manual, se bem que aquele EA, já está obsoleto, e você logo irá entender o por que. Mas isto fica para depois; é assunto para outro momento.

    Então a maneira mais simples, e ao mesmo tempo adequada, para você testar o sistema de ordens, e assim ver o EA enviando ordens para o servidor de negociação, é fazendo uso de algumas artimanhas. Então vamos ver como fazer isto. Normalmente, eu iria adicionar uma linha horizontal no gráfico, mas aqui queremos apenas testar se o sistema de ordens, está ou não funcionado, assim para fazer o teste de uma maneira mais simples, iremos utilizar o seguinte código:

    #property copyright "Daniel Jose"
    #property description "This one is an automatic Expert Advisor"
    #property description "for demonstration. To understand how to"
    #property description "develop yours in order to use a particular"
    #property description "operational, see the articles where there"
    #property description "is an explanation of how to proceed."
    #property version   "1.03"
    #property link      "https://www.mql5.com/pt/articles/11223"
    //+------------------------------------------------------------------+
    #include <Generic Auto Trader\C_Orders.mqh>
    //+------------------------------------------------------------------+
    C_Orders *orders;
    ulong m_ticket;
    //+------------------------------------------------------------------+
    input int       user01   = 1;           //Fator de alavancagem
    input int       user02   = 100;         //Take Profit ( FINANCEIRO )
    input int       user03   = 75;          //Stop Loss ( FINANCEIRO )
    input bool      user04   = true;        //Day Trade ?
    input double    user05   = 84.00;       //Preço de entrada...
    //+------------------------------------------------------------------+
    int OnInit()
    {
            orders = new C_Orders();
            
            return INIT_SUCCEEDED;
    }
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
            delete orders;
    }
    //+------------------------------------------------------------------+
    void OnTick()
    {
    }
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
    {
    #define KEY_UP          38
    #define KEY_DOWN        40
    
            switch (id)
            {
                    case CHARTEVENT_KEYDOWN:
                            switch ((int)lparam)
                            {
                                    case KEY_UP:
                                            m_ticket = orders.CreateOrder(ORDER_TYPE_BUY, user05, user03, user02, user01, user04);
                                            break;
                                    case KEY_DOWN:
                                            m_ticket = orders.CreateOrder(ORDER_TYPE_SELL, user05, user03, user02, user01, user04);
                                            break;
                            }
                            break;
            }
    #undef KEY_DOWN
    #undef KEY_UP
    }
    //+------------------------------------------------------------------+
    

    Este código, apesar de sua incrível simplicidade, irá conseguir fazer com que testemos o sistema de ordens, para isto bastará fazer o seguinte:

    1. Diga qual será o preço na qual a ordem deverá ser colocado. Lembre-se de não indicar o preço atual;
    2. Use a seta para cima, se acha que o preço irá subir ou use a seta para baixo, se acha que o preço irá cair;
    3. Ao fazer isto, observe a aba Negociação na caixa de ferramentas, uma ordem irá surgir, indicando exatamente as condições expressas no EA.

    Os dados serão preenchidos conforme é indicado nesta região, onde o EA irá poder interagir com o usuário. Não esqueçam de notar, como foi feito a inicialização da classe via constructor e a chamada ao destructor. Desta forma, estaremos sempre garantido, que a classe sempre, e sempre será inicializada, antes de que venhamos a fazer qualquer tipo de chamada a um de seus procedimentos internos.


    Conclusão

    Apesar deste EA ser super singelo e simples. Você conseguirá enviar e testar o sistema de ordens. Mas novamente, evite utilizar o preço atual. Caso contrário, você não verá, se de fato o sistema estará pendurando as ordens a posição correta, e com dos valores adequados.

    No video, a abaixo você poderá ver uma demonstração, de como se deve fazer, e como o sistema está operando. Já no anexo, você terá o código completo visto neste artigo. Assim poderá experimentar e estudar como ele funciona.

    Mas aqui estamos apenas começando. No próximo artigo iremos tornar as coisa um pouco mais interessantes. No entanto, quero que você entenda bem, esta parte, de como fazer para pendurar ordens no book.


    Video de demonstração


Arquivos anexados |
Redes neurais de maneira fácil (Parte 19): Regras de associação usando MQL5 Redes neurais de maneira fácil (Parte 19): Regras de associação usando MQL5
Continuamos o tópico de busca de regras de associação. No artigo anterior, consideramos os aspectos teóricos desse tipo de problema. No artigo de hoje, ensinarei a implementação do método FP-Growth usando MQL5. Também vamos testá-la com dados reais.
DoEasy. Controles (Parte 11): Objetos WinForms - grupos, objeto WinForms CheckedListBox DoEasy. Controles (Parte 11): Objetos WinForms - grupos, objeto WinForms CheckedListBox
Neste artigo, consideraremos como agrupar objetos WinForms e criar um objeto-lista de objetos CheckBox.
Aprendendo a construindo um EA que opera de forma automática (Parte 03): Novas funções Aprendendo a construindo um EA que opera de forma automática (Parte 03): Novas funções
Aprenda como criar um EA que opera de forma automática, isto de forma simples e o mais seguro possível. No artigo anterior começamos o desenvolvimento do sistema de ordens, para ser utilizado no EA automático, no entanto, ali montamos apenas e somente uma das funções.
DoEasy. Controles (Parte 10): Objetos WinForms, dando vida à interface DoEasy. Controles (Parte 10): Objetos WinForms, dando vida à interface
Chegou a hora de dar vida à interface gráfica e criar funcionalidades para a interação de objetos com o usuário e outros objetos. E para que objetos mais complexos funcionem corretamente, já precisamos que os objetos interajam entre si e interajam com o usuário.