English Русский 中文 Español Deutsch 日本語
Expert Advisor Universal: Ordens Pendentes e Suporte para Cobertura de Risco (Parte 5)

Expert Advisor Universal: Ordens Pendentes e Suporte para Cobertura de Risco (Parte 5)

MetaTrader 5Exemplos | 25 julho 2016, 10:22
4 598 0
Vasiliy Sokolov
Vasiliy Sokolov

Tabela de Conteúdos


Introdução

O novo artigo da série dedicada ao desenvolvimento de um Expert Advisor Universal fornece uma descrição mais detalhada da funcionalidade do motor de negociação especial, com o qual você pode facilmente desenvolver um robô de negociação que utiliza uma lógica complexa. O conjunto de classes de estratégia continua melhorando ativamente. Novos algoritmos estão sendo adicionados ao motor para fazer as negociações ainda mais eficiente e simples. Além disso desenvolvimento de algoritmos decorre principalmente a partir do feedback dos usuários que lêem esta série de artigos e fazer perguntas nos tópicos de discussão do artigo ou através do envio de mensagens pessoais. Uma das perguntas mais frequentes era sobre o uso de ordens pendentes. Bem, ordens pendentes não foram discutidas nos artigos anteriores, por isto o motor CStrategy não fornece uma ferramenta adequada para trabalhar com ordens pendentes. Este artigo apresenta adições significativas ao motor de negociação CStrategy. Agora, neste artigo, você conhecerá as novas ferramentas que o CStrategy oferece para trabalhar com ordens pendentes. Vamos discutir os detalhes das alterações mais tarde.

A nova versão da plataforma MetaTrader 5 agora oferece suporte na negociação bi-direcional em contas com a opção de cobertura (para mais detalhes, veja o artigo "Agora a plataforma Metatrader 5 possui um sistema de cobertura de registro de posições"). O código do CStrategy foi modificado para suprir estas inovações e operar corretamente nos novos tipos de conta. Apenas algumas mudanças foram feitas para adicionar o suporte de cobertura (hedge) ao código, indicando que a abordagem inicialmente escolhida está correta: alterações e adições, não importa o quão global elas são, não impedem o funcionamento do motor de negociação. Além disso, as novas ferramentas MetaTrader 5, tais como suporte de cobertura (hedge), oferecem muitas oportunidades interessantes que podem ser implementadas nas novas versões do CStrategy.

Além disso, o CStrategy agora suporta métodos intuitivos e populares para obter os preços Ask, Bid, e Last. Estes métodos são descritos no primeiro capítulo deste artigo.

O artigo contém uma série de informações, a fim de cobrir todas as inovações e mudanças, tanto no CStrategy quanto no MetaTrader 5. Este artigo aborda muitos dos temas que não foram discutidos nas partes anteriores. Espero que desperte o interesse dos leitores.


Acesso aos preços atuais através dos métodos Ask, Bid e Last. Substituindo a função Digits

Os traders muitas vezes precisam do acesso aos preços atuais. As versões anteriores do CStrategy não incluem métodos especiais do acesso aos dados. Ou seja, assume-se que o usuário irá utilizar as funções padrões para solicitar os preços correntes. Por exemplo, a fim de descobrir o preço Ask, o usuário precisava escrever o seguinte código:

double ask = SymbolInfoDouble(ExpertSymbol(), SYMBOL_ASK);
int digits = SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
ask = NormalizeDouble(ask, digits);

Embora você apenas precisa de uma função na ordem para receber o preço Ask - SymbolInfoDouble - o valor recebido deve ser normalizado a luz da precisão do instrumento atual. Assim, a recepção real dos preços Ask requer mais ações a serem executadas. O mesmo refere-se aos algoritmos Bid e Last.

A fim de tornar o uso da estratégia mais fácil, três métodos foram adicionados à Estratégia: Ask(), Bid() e Last(). Cada um deles recebe um preço correspondente e normaliza conforme o símbolo atual: 

//+------------------------------------------------------------------+
//| Retorna o preço Ask.                                             |
//+------------------------------------------------------------------+
double CStrategy::Ask(void)
  {
   double ask = SymbolInfoDouble(ExpertSymbol(), SYMBOL_ASK);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   ask = NormalizeDouble(ask, digits);
   return ask;
  }
//+------------------------------------------------------------------+
//| Retorna o preço Bid.                                             |
//+------------------------------------------------------------------+
double CStrategy::Bid(void)
  {
   double bid = SymbolInfoDouble(ExpertSymbol(), SYMBOL_BID);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   bid = NormalizeDouble(bid, digits);
   return bid;
  }
//+------------------------------------------------------------------+
//| Retorna o preço Last.                                            |
//+------------------------------------------------------------------+
double CStrategy::Last(void)
  {
   double last = SymbolInfoDouble(ExpertSymbol(), SYMBOL_LAST);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   last = NormalizeDouble(last, digits);
   return last;
  }

Estes métodos são definidos na nova versão da classe CStrategy e agora estão disponíveis diretamente nas classes de estratégias derivadas. Vamos utilizá-los nos exemplos de desenvolvimento de estratégia.

Além da organização do acesso aos preços Ask, Bid e Last através dos métodos com nomes apropriados, o CStrategy substitui a função do sistema Digits. A função retorna o número de casas decimais junto ao instrumento atual. Pode parecer que tais função primordiais sejam sem sentido, mas não é verdade. O símbolo de trabalho de um Expert Advisor pode ser diferente do símbolodo módulo de execução que possui as estratégias em execução. Neste caso, a chamada da função do sistema Digits pode ser enganosa. Ela irá retornar o número de dígitos do símbolo onde o EA está em execução, não do símbolo de trabalho. A fim de evitar esta confusão, a função Digits na classe CStartegy é substituída pelo método com o mesmo nome. Qualquer referência a essa função vai chamar esse método. Retorna o número de casas decimais de símbolo no qual o EA está trabalhando. Aqui está o código fonte deste método:

//+------------------------------------------------------------------+
//| Retorna o número de casas decimais ao instrumento de             |
//| trabalho                                                         |
//+------------------------------------------------------------------+
int CStrategy::Digits(void)
  {
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   return digits;
  }

Você deve estar ciente e entender o significado deste recurso.

 

Suporte para contas com a opção de cobertura (hedge)

Suporte para contas com a opção de cobertura foi recentemente adicionado ao MetaTrader 5. Em tais contas, um trader pode ter várias posições abertas ao mesmo tempo, tanto em oposição (Compra e Venda) como na mesma direção. Na classe CStrategy, todas as operações com posições são processadas em manipuladores especiais SupportBuy e SupportSell. Posições listadas para a atual estratégia são transmitidas uma a uma para estes métodos. Não importa se existe apenas uma posição ou muitas posições. Posições podem ser abertas no mesmo símbolo ou em símbolos diferentes. O mesmo mecanismo é utilizado para processar e transmitir estas posições. Portanto haverá poucas mudanças, a fim de adicionar um suporte de cobertura (hedge). Antes de mais nada, haverá poucas mudanças no método RebuildPosition. Quando o ambiente muda a negociação (novas negociações são executadas), o método vai reorganizar a lista de posições. O rearranjo difere dependendo do modo utilizado. Para as contas de compensação, será usado um algoritmo para selecionar a posição de um símbolo. Para contas com cobertura (hedge), será utilizado o índice de uma posição na lista.

Aqui está a versão anterior do método RebuildPosition:

//+------------------------------------------------------------------+
//| Reorganiza a lista de posições.                                  |
//+------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
{
   ActivePositions.Clear();
   for(int i = 0; i < PositionsTotal(); i++)
   {
      string symbol = PositionGetSymbol(i);
      PositionSelect(symbol);
      CPosition* pos = new CPosition();
      ActivePositions.Add(pos);
   }
}

Na nova versão do RebuildPosition, dois algoritmos para a seleção da posição são utilizados, dependendo do tipo de conta:

//+------------------------------------------------------------------+
//| Reorganiza a lista de posições.                                  |
//+------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
  {
   ActivePositions.Clear();
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         string symbol=PositionGetSymbol(i);
         PositionSelect(symbol);
         CPosition *pos=new CPosition();
         ActivePositions.Add(pos);
        }
     }
   else
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         ulong ticket=PositionGetTicket(i);
         PositionSelectByTicket(ticket);
         CPosition *pos=new CPosition();
         ActivePositions.Add(pos);
        }
     }
  }

Observe que a mesma classe de posição CPosition é usado tanto para cobertura (hedge) quanto para as contas de compensação. Uma vez que a posição for selecionada, suas propriedades podem ser acessadas através dos métodos PositionGetInteger, PositionGetDouble e PositionGetString. Não importa se uma posição de cobertura ou posição normal seja selecionada. Posição de acesso é o mesmo em ambos os casos. É por isso que é possível usar a mesma composição de classe CPosition sobre diferentes tipos de conta. 

Não existem outros métodos que devem ser substituídas dentro da classe CStrategy. CStrategy é projetada de modo que a operação das estratégias baseadas neste mecanismo depende do contexto. Isto significa que se uma estratégia funciona em contas com a opção de cobertura (hedge) e abre várias posições numa direção, ela vai gerenciar essas posições em paralelo, tratando cada posição como uma classe CPosition separada. Se, ao contrário, apenas uma posição pode ser aberta na conta, a estratégia somente irá gerir esta posição na forma de um mesmo objeto CPosition.  

Além de mudanças no método RebuildPosition, nós também precisaremos modificar o conteúdo interno de alguns métodos CPosition. A classe está localizado no arquivo PositionMT5.mqh, contém métodos que são baseados nas chamadas de função do sistema. Além disso, o CPosition usa ativamente a classe de negociação padrão CTrade. A versão mais recente da CTrade foi modificada de modo a permitir o uso de propriedades das posições de cobertura (hedge). Por exemplo, uma posição de cobertura pode ser fechada por uma posição oposta, onde o novo método CTrade::PositionCloseBy é chamado. A seguir estão os métodos conta, cujos conteúdos foram alterados:

//+------------------------------------------------------------------+
//|                                                  PositionMT5.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
#include "Logs.mqh"
#include <Trade\Trade.mqh>
#include "Trailing.mqh"
//+------------------------------------------------------------------+
//| Ativa a classe de posição para as estratégias clássicas          |
//+------------------------------------------------------------------+
class CPosition : public CObject
  {
   ...
  };
...
//+------------------------------------------------------------------+
//| Retorna um nível de Stop Loss absoluto para a posição atual.     |
//| Se o nível de Stop Loss não está definido, retorna 0.0           |
//+------------------------------------------------------------------+
double CPosition::StopLossValue(void)
{
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_SL);
}
//+------------------------------------------------------------------+
//| Define um nível de stop loss absoluto                            |
//+------------------------------------------------------------------+
bool CPosition::StopLossValue(double sl)
{
   if(!IsActive())
      return false;
   return m_trade.PositionModify(m_id, sl, TakeProfitValue());
}
//+------------------------------------------------------------------+
//| Retorna um nível de Stop Loss absoluto para a posição atual.     |
//| Se o nível de Stop Loss não está definido, retorna 0.0           |
//+------------------------------------------------------------------+
double CPosition::TakeProfitValue(void)
{
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_TP);
}
//+------------------------------------------------------------------+
//| Define um nível de stop loss absoluto                            |
//+------------------------------------------------------------------+
bool CPosition::TakeProfitValue(double tp)
  {
   if(!IsActive())
      return false;
   return m_trade.PositionModify(m_id, StopLossValue(), tp);
  }
//+------------------------------------------------------------------+
//| Fecha a posição atual pelo mercado e define um fechamento        |
//| comentar igual a 'comment'                                       |
//+------------------------------------------------------------------+
bool CPosition::CloseAtMarket(string comment="")
  {
   if(!IsActive())
      return false;
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
      return m_trade.PositionClose(m_symbol);
   return m_trade.PositionClose(m_id);
  }
//+------------------------------------------------------------------+
//| Retorna volume de posição atual.                                 |
//+------------------------------------------------------------------+
double CPosition::Volume(void)
  {
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_VOLUME);
  }
//+------------------------------------------------------------------+
//| Retorna lucro atual de uma posição do depósito da moeda.         |
//+------------------------------------------------------------------+
double CPosition::Profit(void)
  {
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_PROFIT);
  }
//+------------------------------------------------------------------+
//| Retorna verdadeiro se a posição está ativa.  Retorna falso           |
//| do contrário.                                                    |
//+------------------------------------------------------------------+
bool CPosition::IsActive(void)
{
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
      return PositionSelect(m_symbol);
   else
      return PositionSelectByTicket(m_id);
}
//+------------------------------------------------------------------+


Como você pode ver, a base desses métodos é a chamada do método IsActive. O método retorna verdadeiro se a posição ativa representada pelo objeto CPosition realmente existe no sistema. O método realmente elege uma posição usando um dos dois métodos, dependendo do tipo de conta. Para contas clássicas com a opção de compensação, uma posição é selecionada usando a função PositionSelect, onde é necessário especificar o símbolo. Para as contas com a opção de cobertura (hedge), uma posição é selecionada com base no seu tick, utilizando a nova função PositionSelectByTicket. O resultado da seleção (verdadeiro ou falso), é devolvido oo procedimento que foi chamado pelo método. Este método define o contexto para outras operações com a posição. Não há necessidade de modificar o algoritmo de negociação, porque todas as funções de negociação CPosition baseiam-se na classe CTrade. CTrade pode modificar, abrir e fechar ambas as posições tradicionais e bi-direcionais.  

 

O uso de ordens pendentes nas versões anteriores da classe CStrategy

O uso de ordens pendentes é uma parte importante de muitos algoritmos de negociação. Após o lançamento da primeira versão do mecanismo de negociação CStrategy, recebi um monte de perguntas sobre o uso de ordens pendentes. Nesta e nas seções a seguir, discutiremos as negociações com ordens pendentes.

O motor CStrategy foi originalmente criado sem suporte para as ordens pendentes. No entanto, isso não significa que a estratégia desenvolvida com base na classe CStrategy não possa trabalhar com ordens pendentes. Na primeira versão do CSTrategy, isso poderia ser feito usando as funções padrão do MetaTrader 5, tais como OrdersTotal() e OrderSelect().

Por exemplo, um Expert Advisor define ou modifica um ordem stop pendente previamente definida em cada nova barra, de modo que o seu preço de desencadeamento é 0,25% maior do que o preço atual (para comprar) ou abaixo dela (para vender). A idéia é que, se o preço faz um movimento brusco (impulso) dentro de uma barra, então esta ordem será preenchida e o Expert Advisor vai entrar durante o movimento do preço forte. Se o movimento não é forte o suficiente, então a ordem não será preenchida dentro da barra atual. Neste caso, é necessário mover a ordem para um novo nível. A plena implementação desta estratégia está disponível abaixo em seções que descrevem o algoritmo, que chamei de CImpulse. Como é evidente a partir do nome do sistema, é baseado no algoritmo de impulso. Ela exige uma única entrada para o desencadeamento de uma ordem pendente. Como já sabemos, o CStrategy contém os métodos especiais de substitução, BuyInit e SellInit, para colocarf uma posição. Portanto, o algoritmo de ordem pendente usado deve ser adicionado a estes métodos. Sem o apoio direto da classe CStrategy, o código para operações Compradas será o seguinte:

//+------------------------------------------------------------------+
//| A lógica das operações com ordens pendentes Buy.                 |
//+------------------------------------------------------------------+
void CMovingAverage::InitBuy(const MarketEvent &event)
  {
   if(!IsTrackEvents(event))return;                      // Manipular apenas se necessário!
   if(positions.open_buy > 0) return;                    // Se houver pelo menos uma posição aberta, não há necessidade de comprar, pois já compramos!
   int buy_stop_total = 0;
   for(int i = OrdersTotal()-1; i >= 0; i--)
   {
      ulong ticket = OrderGetTicket(i);
      if(!OrderSelect(ticket))continue;
      ulong magic = OrderGetInteger(ORDER_MAGIC);
      if(magic != ExpertMagic())continue;
      string symbol = OrderGetString(ORDER_SYMBOL);
      if(symbol != ExpertSymbol())continue;
      ENUM_ORDER_TYPE order_type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
      if(order_type == ORDER_TYPE_BUY_STOP)
      {
         buy_stop_total++;
         Trade.OrderModify(ticket, Ask()*0.0025, 0, 0, 0);
      }
   }
   if(buy_stop_total == 0)
      Trade.BuyStop(MM.GetLotFixed(), Ask() + Ask()*0.0025, ExpertSymbol(), 0, 0, NULL);
  }

O método IsTrackEvents identifica que o evento recebido corresponde à abertura de uma nova barrra no símbolo atual. Em seguida, o Expert Advisor verifica o número de posições abertas na direção de compra. Se houver pelo menos uma posição de compra, o EA não deve comprar e a lógica está concluída. Depois, a estratégia verifica todos as ordens pendentes atuais. Percorrendo os índices das ordens pendentes. Cada uma das ordens é selecionada pelo índice, então seu número mágico e símbolo são analisados. Se estes dois parâmetros correspondem aos parâmetros do Expert Advisor, considera-se que a ordem pertence ao Expert Advisor atual, então o contador de ordem é aumentado em mais uma. A ordem é modificada, o seu preço de entrada é substituído pelo preço atual com + 0,25% acima dele. Se não há ordens pendentes, quando o contador buy_stop_order for igual a zero, uma nova ordem é colocada a uma distância de 0,25% mais longe do preço atual.

Observe que não existe nenhuma posição aberta pelo InitBuy. O CStrategy não impõe tal restrição no posicionamento dos métodos de abertura. Assim, formalmente, uma lógica Expert Advisor pode ser adicionada aqui. No entanto, para a manipulação de eventos apropriados, a lógica deve ser ligado exatamente com a abertura de uma posição, seja através das ordens pendente ou de mercado. 

Como pode ser visto a partir deste exemplo, operações com as ordens pendentes são realizadas de forma semelhante às posições convencionais. A principal exigência é a mesma: é necessário dividir a lógica da Compra e Venda, descrevendo-as nos métodos separados BuyInit e SellInit. Apenas ordens pendentes de compra devem ser tratados no BuyInit. Apenas ordens pendentes de venda devem ser tratados no SellInit. O resto da lógica das operações com ordens pendentes é semelhante ao esquema clássico adotado no MetaTrader 4: pesquisando através das ordens, a seleção das pertencem ao Expert Advisor atual, analisando o ambiente de negociação e tomando a decisão de abrir ou modificar uma ordem existente. 

 

CPendingOrders e COrdersEnvironment para operações com ordens pendentes

Funções do MetaTrader 5 padrão para trabalhar com ordens pendentes fornecem um sistema conveniente a um acompanhamento abrangente e fácil das mesmas. No entanto, a CStrategy é um conjunto de classes orientadas a objetos que permite a criação de um sistema de negociação. Todas as ações realizadas na classe CStrategy são orientadas a objeto, ou seja elas são realizadas por objetos que, por sua vez, executam operações de negociação. Esta abordagem oferece várias vantagens. Aqui estão alguns delass.

  • Tamanho reduzido do código-fonte personalizado. Muitas ações, como a normalização ou preparação de preços de arrays receptores para as funções de classe CopyBuffer são realizadas "nos bastidores", assim o método proporciona um resultado pronto. Você não precisa escrever procedimentos adicionais para verificação de resultados e outros passos intermediários, que não podem ser evitados se você trabalha diretamente com as funções do sistema MetaTrader 5.
  • Independência de Plataforma. Uma vez que todas as classes e métodos fornecidos são escritos na linguagem formal MQL, é possível obter qualquer propriedade usando um método universal. A implementação real deste método será diferente em certas plataformas. No entanto, o nível do usuário não importa. Isso significa que um Expert Advisor desenvolvido para uma plataforma, em teoria, pode ser compilado para ser executado em outra plataforma. No entanto, na prática, existem muitas nuances, e não vamos discutir a independência de plataforma neste artigo.
  • funcionalidade. Um conjunto de funções MQL proporciona a funcionalidade básica, através da combinação onde você pode criar algoritmos complexos e funções úteis. Quando estes algoritmos são incluídos numa única biblioteca de classes, como a CStartegy, esta funcionalidade torna-se muito mais fácil para acessar, enquanto a adição de novas funções não complica o funcionamento, porque nesses casos são adicionados apenas novos módulos, onde o desenvolvedor de EA opta por utilizar ou não.

A fim de manter esta abordagem, decidimos expandir a funcionalidade da CStartegy de modo a proporcionar um mecanismo orientado a objeto conveniente para operações com ordens pendentes. Este mecanismo é representado por duas classes: CPendingOrderCOrdersEnvironment. COrder fornece um objeto que contém todas as propriedades de uma ordem pendente, tendo o acesso através das OrderGetInteger, OrderGetDouble e OrderGetString. O objetivo da COrdersEnvironment será explicado mais tarde.

Suponha-se que o objeto CPendingOrder representa uma ordem pendente, a qual já existe no sistema. Se eliminar esta ordem pendente, então o que deve acontecer com o objeto CPendingOrder que representa a ordem? Se o objeto permanece após a eliminação da ordem pendente real, isto irá causar erros graves. O Expert Advisor vai encontrar a ordem pendente e erroneamente "pensar" que ela ainda existe no sistema. Para evitar tais erros, temos de garantir a sincronização do ambiente de negociação com o ambiente da classe CStrategy. Em outras palavras, precisamos criar um mecanismo que garanta o acesso a apenas aos objetos que realmente existem no sistema. Isto é o que a classe COrdersEnvironment está fazendo. A sua aplicação é bastante simples, permite que o acesso apenas aos objetos CPendingOrders, que representam as ordens pendentes reais.

A base da classe COrdersEnvironment é composta pelos métodos GetOrder e Total. A primeira ordem retorna o objeto CPendingOrders que corresponde a uma ordem pendente com um índice específico no sistema de ordens pendentes MetaTrader 5. O segundo método retorna o número total de ordens pendentes. Agora é hora de examinar esta classe em detalhes. Aqui está o código-fonte da classe:

//+------------------------------------------------------------------+
//| Uma classe para operações com ordens pendentes                   |
//+------------------------------------------------------------------+
class COrdersEnvironment
{
private:
   CDictionary    m_orders;         // O número total de todas as ordens pendentes
public:
                  COrdersEnvironment(void);
   int            Total(void);
   CPendingOrder* GetOrder(int index);
};
//+------------------------------------------------------------------+
//| Precisamos saber o símbolo atual e número mágico do EA           |
//+------------------------------------------------------------------+
COrdersEnvironment::COrdersEnvironment(void)
{
}
//+------------------------------------------------------------------+
//| Retorna uma ordem pendente                                       |
//+------------------------------------------------------------------+
CPendingOrder* COrdersEnvironment::GetOrder(int index)
{
   ulong ticket = OrderGetTicket(index);
   if(ticket == 0)
      return NULL;
   if(!m_orders.ContainsKey(ticket))
      return m_orders.GetObjectByKey(ticket);
   if(OrderSelect(ticket))
      return NULL;
   CPendingOrder* order = new CPendingOrder(ticket);
   m_orders.AddObject(ticket, order);
   return order;
}
//+------------------------------------------------------------------+
//| Retorna o número de ordens pendentes                             |
//+------------------------------------------------------------------+
int COrdersEnvironment::Total(void)
{
   return OrdersTotal();   
}

O método Total retorna o número de ordens pendentes que existem atualmente no sistema. O método nunca está errado, pois ele retorna o valor do sistema recebido da função OrdersTotal().

Para o funcionamento do método GetOrder, é necessário especificar o índice da ordem pendente no sistema. Nós sempre saberemos o número total de ordens através do método Total, o índice de uma ordem necessária também sempre é conhecido e corresponde exatamente ao índice real de uma ordem pendente no sistema. Além disso, o método GetOrder recebe um identificador de uma ordem pendente pelo seu índice. Se uma ordem foi removida por algum motivo, a ID do pedido será igual a zero, e portanto, a constante NULL será devolvida ao Expert Advisor, informando que a ordem com o índice solicitado não pode ser encontrada.

Cada objeto é criado dinamicamente, necessitando ser totalmente removido por um operador de exclusão especial. Sendo que o GetOrder cria objetos CPendingOrders dinamicamente usando o novo operador, esses objetos também precisam ser excluídos. A fim de eliminar a necessidade de excluir um objeto por um usuário após a criação, nós usaremos uma técnica especial - o objeto está localizado num dicionário especial tipo container dentro do objeto COrdersEnvironment. Um elemento no dicionário pode ser acessado pela sua chave única, que por acaso é uma ID de ordem. Neste caso, se a ordem ainda existir, o objeto anterior que representa esta ordem foi provavelmente criado e colocado no container. Então, esse objeto criado anteriormente tem seu retorno pela função GetOrder. Se for a primeira chamada da ordem com a ID especificada, um novo objeto CPendingOrder é criado, depois ele é adicionado ao dicionário e uma referência a ela é então devolvida ao usuário.

O que temos a partir da abordagem proposta? Em primeiro lugar, o método GetOrder assegura devolver apenas o objeto que representa uma ordem pendente realmente existente. A função do sistema OrderGetTicket é responsável por isso. Segundo, o objeto será criado apenas se ele ainda não existe. Isso economiza recursos informáticos. E, finalmente em terceiro lugar, o algoritmo libera o usuário da necessidade de remover o objeto resultante. Uma vez que todos os objetos são armazenados no dicionário, eles serão excluídos automaticamente após a desinicialização do COrdersEnvironment. 

 

Referência a ordens pendentes no código do Expert Advisor

Agora é hora de reescrever a lógica das operações com ordens pendentes apresentadas na seção anterior "Negociação através de ordens pendentes em versões anteriores do CStrategy". Aqui está o código com as classes CPendingOrder e COrdersEnvironment:

//+------------------------------------------------------------------+
//| Comprar quando a MM rápida é superior a lenta.                   |
//+------------------------------------------------------------------+
void CMovingAverage::InitBuy(const MarketEvent &event)
  {
   if(!IsTrackEvents(event))return;                      // Manipular apenas se necessário!
   if(positions.open_buy > 0) return;                    // Se houver pelo menos uma posição aberta, não há necessidade de comprar, pois já compramos!
   int buy_stop_total = 0;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
     {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_BUY_STOP)
       {
         buy_stop_total++;
         Order.Modify(Ask() + Ask()*0.0025);
       }
       //deletar Ordem; Não há necessidade de excluir o objeto Order!
     }
   if(buy_stop_total == 0)
      Trade.BuyStop(MM.GetLotFixed(), Ask() + Ask()*0.0025, ExpertSymbol(), 0, 0, NULL);
  }

O objeto PendingsOrders é uma classe COrdersEnvironment. A pesquisa através das ordens pendentes começa da mesma forma como o uso das funções do sistema. Então é feita uma tentativa de obter um objeto de acordo com o índice da ordem pendente igual à variável i. Se a ordem não foi recebida por algum motivo ou pertencer a outro Expert Advisor, pesquisa através de ordens continuas com uma nova ordem. Nestes casos, o método IsMain do objeto CPendingorder é usado, que retorna verdadeiro se o símbolo da ordem e o número mágico coincidem com o símbolo e Número Mágico do EA, o que significa que a ordem pertence a este Expert Advisor.

Se o tipo de ordem corresponde a ORDER_TYPE_BUY_STOP, significa que a ordem não foi acionada e o seu nível deve ser modificado pela fórmula: preço atual + 0.25%. A modificação da ordem pendente é feita usando o método Modify e sua versão substituída. Ela permite a definação do preço e outros parâmetros que você deseja mudar.

Note que após completar as operações com uma ordem pendente, não há necessidade de excluir o objeto da ordem. A referência à ordem deve ser deixada como está. O container PendingOrders gerencia objetos do tipo CPendingOrdem e não requer a remoção dos objetos retornados em funções do usuário. 

Se não existirem ordens pendentes e a contagem total de buy_stop é igual a zero, uma nova ordem pendente será colocada. Uma nova ordem é colocada através da utilização do módulo Trade, que já foi descrito neste artigo. 

Na abordagem orientada a objetos, as propriedades de uma ordem pendente são acessadas através dos métodos apropriados do objeto CPendingOrders. Este tipo de acesso reduz o volume do código, preservando sua confiabilidade, uma vez que o PendingOrders garante que apenas uma ordem do objeto existente seja recebida, ou seja, a ordem pendente realmente existe no sistema.

 

A lógica de negociação do Expert Advisor CImpulse, que usa ordens pendentes 


Analisamos o uso de ordens pendentes e agora podemos criar um Expert Advisor completo que utiliza as possibilidades do motor de negociação. Nossa estratégia será baseada em entradas durante fortes movimentos do mercado na direção do movimento, por isso vai ser chamado de CImpulse. Na abertura de cada nova barra, o EA vai medir uma distância predefinida a partir da barra atual, expresso em porcentagem. O EA vai colocar ordens pendentes BuyStop e SellStop a uma distância igual a partir do preço atual. A distância é definida por uma porcentagem. Se uma das ordens for acionada dentro da barra, isso significa que o preço se moveu uma distância bastante longa, indicando o impulso do mercado. A ordem será executada e a posição estará aberta.

Posições abertas serão geridas por uma média móvel simples. Se o preço retorna para a Média Móvel, a posição será fechada. A imagem abaixo mostra uma entrada típica de posição comprada em cima do desencadeamento de uma ordem pendente BuyStop:


Fig. 1. Uma posição comprada da estratégia CImpulse.
 

Na figura acima, o início de uma barra está marcado com um triângulo preto. No início da barra, duas ordens pendentes são colocados a uma distância de 0,1% em relação ao preço de abertura da barra. Um deles - a ordem BuyStop - é acionada. Uma nova posição comprada foi aberta. Assim que uma das barras fechar abaixo da média móvel, exibida como uma linha vermelha, o Expert Advisor fecha a posição. Na Fig. 1, o fechamento é exibido como um triângulo azul.

Se uma ordem pendente não for acionada dentro de uma barra, ela é movida para um novo nível calculado com base no novo preço vigente.

A estratégia descrita tem uma característica específica de processamento de ordens pendentes. O nível de uma ordem BuyStop pode ser menor do que a Média Móvel atual. Neste caso, a posição será fechada imediatamente após a abertura, porque o preço atual está abaixo da Média Móvel. O mesmo é verdadeiro para as posições vendida: o preço acionado de uma ordem SellStop pode ser maior do que o nível da Média Móvel. A fim de evitar o fechamento imediato, precisamos adicionar uma verificação extra do nível da Média Móvel nos métodos BuyInit e SellInit. Assim, o algoritmo somente irá colocar ordens pendentes BuyStop, se o preço for superior ao valor de Média Móvel. O mesmo é verdadeiro para a ordem SellStop: eles só vão ser colocados se o seu preço é abaixo da Média Móvel.

Também vamos usar o novo recurso da plataforma MetaTrader 5 - operação em contas de cobertura (hedge). Esta característica significa que tanto uma posição comprada como vendida podem ser abertas dentro de uma barra. No entanto, a lógica apresentada originalmente do Expert Advisor, implica na separação de posições compradas e vendidas, fazendo com que seja possível deixar inalterado o código da estratégia de negociação. Independentemente do número de posições, todas as posições vendidas serão fechadas se o preço de abertura da barra se move acima da Média Móvel, as posições compradas serão fechadas quando o preço ficar abaixo da Média Móvel. Neste caso, não importa se estamos negociando em contas de cobertura (hedge) ou não. 

Primeiro de tudo temos de abrir uma conta com a opção de cobertura (hedge) ativada, confirmando na janela "abertura de Conta":

 

Fig. 2. Abrertura de uma conta com a opção de cobertura (hedge) 

Após a abertura da conta, podemos prosseguir para a negociação a partir dela. Em primeiro lugar, precisamos escrever a classe CImpulse que implementa a lógica do algoritmo de negociação discutido. 

 

A classe da estratégia CImpulse

Existe uma lista da classe CImpulse que descreve a lógica da nossa nova negociação do Expert Advisor. Para uma descrição mais clara das operações com ordens pendentes, esta classe é a mais simples possível, não contém procedimentos relacionados ao registo, bem como métodos especiais que analisam os parâmetros de estratégia de um arquivo XML:

//+------------------------------------------------------------------+
//|                                                      Impulse.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Strategy\Strategy.mqh>
#include <Strategy\Indicators\MovingAverage.mqh>

input double StopPercent = 0.05;
//+------------------------------------------------------------------+
//| Define as ações que precisam ser executadas com uma ordem        |
//| pendente.                                                        |
//+------------------------------------------------------------------+
enum ENUM_ORDER_TASK
{
   ORDER_TASK_DELETE,   // Elimina uma ordem pendente
   ORDER_TASK_MODIFY    // Modifica uma ordem pendente
};
//+------------------------------------------------------------------+
//| A estratégia CImpulse                                            |
//+------------------------------------------------------------------+
class CImpulse : public CStrategy
{
private:
   double            m_percent;        // Valor percentual para o nível de uma ordem pendente
   bool              IsTrackEvents(const MarketEvent &event);
protected:
   virtual void      InitBuy(const MarketEvent &event);
   virtual void      InitSell(const MarketEvent &event);
   virtual void      SupportBuy(const MarketEvent &event,CPosition *pos);
   virtual void      SupportSell(const MarketEvent &event,CPosition *pos);
   virtual void      OnSymbolChanged(string new_symbol);
   virtual void      OnTimeframeChanged(ENUM_TIMEFRAMES new_tf);
public:
   double            GetPercent(void);
   void              SetPercent(double percent);
   CIndMovingAverage Moving;
};
//+------------------------------------------------------------------+
//| Trabalhando com as ordens pendentes BuyStop para a abertura de   |
//| posição                                                          |
//+------------------------------------------------------------------+
void CImpulse::InitBuy(const MarketEvent &event)
{
   if(!IsTrackEvents(event))return;                      
   if(positions.open_buy > 0) return;                    
   int buy_stop_total = 0;
   ENUM_ORDER_TASK task;
   double target = Ask() + Ask()*(m_percent/100.0);
   if(target < Moving.OutValue(0))                    // A ordem de acionamento do preço deve ser acima da Média Móvel
      task = ORDER_TASK_DELETE;
   else
      task = ORDER_TASK_MODIFY;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
   {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_BUY_STOP)
      {
         if(task == ORDER_TASK_MODIFY)
         {
            buy_stop_total++;
            Order.Modify(target);
         }
         else
            Order.Delete();
      }
   }
   if(buy_stop_total == 0 && task == ORDER_TASK_MODIFY)
      Trade.BuyStop(MM.GetLotFixed(), target, ExpertSymbol(), 0, 0, NULL);
}
//+------------------------------------------------------------------+
//| Trabalhando com as ordens pendentes SellStop para a abertura da  |
//| posição                                                          |
//+------------------------------------------------------------------+
void CImpulse::InitSell(const MarketEvent &event)
{
   if(!IsTrackEvents(event))return;                      
   if(positions.open_sell > 0) return;                    
   int sell_stop_total = 0;
   ENUM_ORDER_TASK task;
   double target = Bid() - Bid()*(m_percent/100.0);
   if(target > Moving.OutValue(0))                    // A ordem de acionamento do preço deve ser abaixo da Média Móvel
      task = ORDER_TASK_DELETE;
   else
      task = ORDER_TASK_MODIFY;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
   {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_SELL_STOP)
      {
         if(task == ORDER_TASK_MODIFY)
         {
            sell_stop_total++;
            Order.Modify(target);
         }
         else
            Order.Delete();
      }
   }
   if(sell_stop_total == 0 && task == ORDER_TASK_MODIFY)
      Trade.SellStop(MM.GetLotFixed(), target, ExpertSymbol(), 0, 0, NULL);
}
//+------------------------------------------------------------------+
//| Gerenciando uma posição comprada de acordo com a Média Móvel     |
//+------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   if(Bid() < Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//| Gerenciando uma posição vendida de acordo com a Média Móvel      |
//+------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   if(Ask() > Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//| Filtros de eventos de entrada. Se o evento transmitido não é     |
//| processado pela estratégia, ela retorna falso; Se processada     |
//| retorna verdadeiro.                                              |
//+------------------------------------------------------------------+
bool CImpulse::IsTrackEvents(const MarketEvent &event)
  {
//Nós manipulamos a abertura de uma nova barra apenas no símbolo e timeframe trabalhado
   if(event.type != MARKET_EVENT_BAR_OPEN)return false;
   if(event.period != Timeframe())return false;
   if(event.symbol != ExpertSymbol())return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Responder à alteração do símbolo                                 |
//+------------------------------------------------------------------+
void CImpulse::OnSymbolChanged(string new_symbol)
  {
   Moving.Symbol(new_symbol);
  }
//+------------------------------------------------------------------+
//| Responder à alteração do timeframe                               |
//+------------------------------------------------------------------+
void CImpulse::OnTimeframeChanged(ENUM_TIMEFRAMES new_tf)
  {
   Moving.Timeframe(new_tf);
  }
//+------------------------------------------------------------------+
//| Retorna a porcentagem do nível de rompimento do preço            |
//+------------------------------------------------------------------+  
double CImpulse::GetPercent(void)
{
   return m_percent;
}
//+------------------------------------------------------------------+
//| Define a porcentagem do nível de rompimento do preço             |
//+------------------------------------------------------------------+  
void CImpulse::SetPercent(double percent)
{
   m_percent = percent;
}

O arquivo mq5 padrão do Expert Advisor que configura e inicia esta estratégia está disponível abaixo:

//+------------------------------------------------------------------+
//|                                                ImpulseExpert.mq5 |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Strategy\StrategiesList.mqh>
#include <Strategy\Samples\Impulse.mqh>

CStrategyList Manager;
//+------------------------------------------------------------------+
//| função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   CImpulse* impulse = new CImpulse();
   impulse.ExpertMagic(1218);
   impulse.Timeframe(Period());
   impulse.ExpertSymbol(Symbol());
   impulse.ExpertName("Impulse");
   impulse.Moving.MaPeriod(28);                      
   impulse.SetPercent(StopPercent);
   if(!Manager.AddStrategy(impulse))
      delete impulse;
//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Função tick do Expert                                            |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   Manager.OnTick();
  }
//+------------------------------------------------------------------+


 

Analisando a estratégia CImpulse

Por favor, veja o vídeo anexado, demonstra o funcionamento do EA usando ordens pendentes durante o teste de estratégia. Ordens BuyStop e SellStop são colocadas quando uma nova barra abre. As ordens são colocados a uma certa distância a partir do preço atual, e assim, formam um canal dinâmico. Uma vez que o nível de uma ordem pendente fica abaixo da linha da Média Móvel, ela é completamente removida. No entanto, quando o preço de acionamento torna-se maior do que a Média Móvel, a ordem pendente aparece novamente. A mesma regra aplica-se a ordens SellStop:


A imagem abaixo mostra claramente o momento em que a situação da cobertura (hedge) foi formada, ou seja, houve a abertura de uma posição Comprada e o encerramento com o acionamento de uma posição vendida. Depois disso, a posição comprada continua existindo e foi fechada de acordo com a sua lógica, quando a abertura do preço da barra se move abaixo da Média Móvel:

 

Fig. 3. Gerenciamento de posição sobre uma conta com suporte de cobertura (hedge).

A mesma lógica do Expert Advisor implementado em conta clássica apareceria algo semelhante, embora a situação seja um pouco diferente:

 

Fig. 4. Adminsitração de posições em conta de compensação. 

A negociação de Compra foi executada durante um movimento forte e esta negociação fechou uma posição vendida aberta anteriormente. Assim, todas as posições abertas foram fechadas. Portanto, na próxima barra o EA começa a colocar novas ordens de Compra, uma das quais estava preecnhida e se transformou em nova posição comprada, que por sua vez, fechou uma posição na conta com a opção de cobertura (hedge).

Nós podemos mudar a lógica do EA e torná-lo polimórfico, ou seja, o algoritmo correspondente será executado dependendo do tipo de conta. É óbvio que apenas uma posição pode existir em conta de compensação. Para evitar as situações em que a abertura de uma nova posição fecha a oposta, devemos acrescentar o Stop Loss para todas as posições compensadas. O valor do Stop Loss deve ser igual ao nível de rompimento pelas ordens opostas. Assim, o acionando uma das ordens stop significaria o acionamento do Stop Loss de uma posição contrária, se tal posição estiver aberta no momento. Esta lógica somente seria ativado em contas de compensação. Deve ser conjunta com os métodos BuySupport e SellSupport. Aqui está o código fonte modificado dos métodos de suporte atualizados:

//+------------------------------------------------------------------+
//| Gerenciando uma posição comprada de acordo com a Média Móvel     |
//+------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Bid() - Bid()*(m_percent/100.0);
      if(target < Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Bid() < Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//| Gerenciando uma posição vendida de acordo com a Média Móvel      |
//+------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Ask() + Ask()*(m_percent/100.0);
      if(target > Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Ask() > Moving.OutValue(0))
      pos.CloseAtMarket();
}

Os resultados do teste da estratégia com a funcionalidade atualizada diferem ligeiramente. Em contas de compesação, o EA vai se comportar como um Expert Advisor clássico que funciona somente com uma posição:


Fig. 5. Uma posição gerenciada por um Expert Advisor polimórfico em conta clássica de compesação.

O anexo a este artigo inclui a versão mais recente da estratégia CImpulse, implementando lógicas para diferentes tipos de conta.   

Verifica-se que todas as versões da lógica de negociação estão corretas. No entanto, o funcionamento exato de uma estratégia de negociação depende da própria estratégia. O CStrategy só fornece uma interface unificada para trabalhar com posições dependendo do seu tipo. 

 

Conclusão 

Revisamos as novas características do motor de negociação CStrategy. As novas funções incluem suporte para novos tipos de conta, operações com objetos para ordens pendentes e um vasto conjunto de funções para trabalhar com os preços correntes.

Os novos métodos da classe CStrategy permitem um acesso rápido e fácil aos preços correntes, como Ask, Bid, e Last. O método Digits substituído nem sempre retorna o número correto de casas decimais no preço do símbolo. 

Trabalhar com ordens pendentes usando as classes especiais CPendingOrders e COrdersEnvironment, simplifica a lógica de negociação. O Expert Advisor pode acessar uma ordem pendente através de um objeto especial do tipo CPendingOrder. Alterando as propriedades do objeto, por exemplo a ordem do nível de acionamento, o EA altera a propriedade da ordem atual correspondente ao objeto. O modelo de objetos proporciona um elevado nível de comfiabilidade. É impossível acessar um objeto se não houver uma ordem pendente real no sistema correspondente a esse objeto. Operações com ordens pendentes são realizadas nos métodos BuyInit e SellInit que devem ser substituídos na estratégia. O BuyInit é projetado para trabalhar com as ordens pendentes BuyStop e BuyLimit. O SellInit é projetado para trabalhar com as ordens pendentes SellStop e SellLimit.  

Dentro do motor do CStrategy proposto, trabalhar em contas com suporte de cobertura (hedge) praticamente não difere das contas clássicas. As operações com posições não dependem do tipo de posição e estão disponíveis através de uma classe especial, a CPosition. A única diferença em operações com esses tipos de contas é a lógica da estratégia. Se a estratégia funciona com uma única posição, será implementado a lógica apropriada para uma gestão adequada à posição. Se a estratégia funciona com múltiplas posições ao mesmo tempo, a lógica leverá em conta os métodos adequados BuySupport e SellSupport, podendo serem transferidas para várias posições sucessivas. O motor de negociação em si não implementa qualquer lógica de negociação. Apenas fornece o tipo de posição que corresponde ao tipo de conta.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2404

Arquivos anexados |
Interfaces gráficas I: Funções para os Botões do Formulário e Remoção dos Elementos da Interface (Capítulo 4) Interfaces gráficas I: Funções para os Botões do Formulário e Remoção dos Elementos da Interface (Capítulo 4)
Neste artigo, nós vamos continuar desenvolvendo esta classe, adicionando os métodos que permitem gerir o formulário mediante os cliques em seus controles. Nós habilitaremos o fechamento do programa por um botão do formulário, bem como implementar a funcionalidade de minimização e maximização do mesmo.
Expert Advisor Universal: Negociação em Grupo e Gestão de uma Carteira de Estratégias (Parte 4) Expert Advisor Universal: Negociação em Grupo e Gestão de uma Carteira de Estratégias (Parte 4)
Na última parte da série de artigos sobre o mecanismo de negociação CStrategy, vamos considerar a operação simultânea de vários algoritmos de negociação, aprenderemos a carregar estratégias de arquivos XML, e apresentaremos um painel simples para selecionar Expert Advisors partir de um único módulo executável e gerenciar os seus modos de negociação.
Interfaces gráficas I: Biblioteca de Testes em Programas de Diferentes Tipos e no Terminal MetaTrader 4 (Capítulo 5) Interfaces gráficas I: Biblioteca de Testes em Programas de Diferentes Tipos e no Terminal MetaTrader 4 (Capítulo 5)
No capítulo anterior da primeira parte da série sobre interfaces gráficas, a classe de formulário foi enriquecida por métodos que permitiram gerir o formulário através dos cliques em seus controles. Neste artigo, nós vamos testar nosso trabalho em diferentes tipos de programa MQL, como indicadores e scripts. Já que a biblioteca foi concebida para ser multi-plataforma para que ela pudesse ser utilizada em todas as plataformas MetaTrader, nós também vamos testá-la no MetaTrader 4.
Interfaces Gráficas I: Animação na Interface Gráfica (Capítulo 3) Interfaces Gráficas I: Animação na Interface Gráfica (Capítulo 3)
No artigo anterior, nós começamos a desenvolver uma classe de formulário para os controles. Neste artigo, nós vamos continuar a desenvolver esta classe e preenchê-la com os métodos para mover um formulário sobre a área do gráfico. Em seguida, integraremos este componente da interface para o núcleo da biblioteca. Além disso, nós vamos garantir que os controles do formulário mudem de cor quando o cursor do mouse estiver pairando sobre eles.