English Русский 中文 Español Deutsch 日本語
Expert Advisor Universal: O Modelo de Evento e o Protótipo da Estratégia de Negociação (Parte 2)

Expert Advisor Universal: O Modelo de Evento e o Protótipo da Estratégia de Negociação (Parte 2)

MetaTrader 5Exemplos | 20 julho 2016, 14:01
2 061 1
Vasiliy Sokolov
Vasiliy Sokolov

Conteúdo

 

Introdução

O artigo contém uma descrição mais detalhada da negociação universal do motor CStrategy. No primeiro artigo Expert Advisor Universal: Modos de Negociação das Estratégias (Parte 1), discutimos em detalhes os modos de negociação e funções que permitem a sua aplicação. Analisamos um esquema de Expert Advisor Universal que consiste em quatro métodos, no qual dois métodos abrem novas posições e outros dois as fecham. Diferentes combinações de chamadas de método definem um modo particular de negociação. Por exemplo, um Expert Advisor que permite apenas vender ou comprar, pode gerir posições abertas anteriores ou esperar. Usando esses modos, uma operação do Expert Advisor pode ser flexivelmente configurada, dependendo do tempo de negociação ou do dia da semana.

No entanto, os modos de negociação não são a única coisa que um Expert Advisor precisa. Na segunda parte, vamos discutir o modelo de evento do modo de negociação CStrategy, baseado na manipulação de eventos centralizado. O esquema de manipulaçao do evento proposto difere dos eventos do sistema onde todos os eventos são reunidos num só lugar. As vantagens de tal implementação serão consideradas posteriormente.

Além disso, este artigo descreve duas classes importantes do motor de negociação -CStrategy e CPosition. O primeiro é o núcleo de toda a lógica de negociação do EA, que une eventos e modos numa única estrutura flexível que é herdado diretamente pelo EA customizado. A segunda classe é a base das operações universais de negociação. Ele contém ações aplicadas para a abertura de uma posição (como fechamento de uma posição ou modificação de seu Stop Loss ou Take Profit). Isso permite formalizar todas as ações de negociação e torná-las independente de plataforma.

Por favor, note que todas as características específicas para trabalhar com o motor, o qual você terá de cumprir, são criadas para o seu próprio benefício. Por exemplo, a pesquisa usual através de posições e o acesso a eventos do sistema não estão disponíveis para a estratégia. Portanto, não há necessidade de se preocupar com a sequência de ações e qual manipulador usar no evento. Em vez disso, o CStrategy oferece um EA costumizado para focar na lógica de negociação, empreendendo a implementação destas e muitas outras operações.

Motores de negociação semelhantes ao descrito são concebidos para utilizadores normais, permitindo-lhes facilmente utilizar a funcionalidade desejada. Você não precisa analisar os detalhes dos algoritmos descritos. Você só deve compreender os princípios gerais e a funcionalidade do CStrategy. Portanto, se você encontrar algumas partes difíceis de entender neste artigo, sinta-se livre para ignorá-las.

 

Modelo de Evento Baseado no Processamento Centralizado, ENUM_MARKET_EVENT_TYPE

MetaTrader 5 fornece uma série de eventos. Estes eventos incluem notificações de alterações dos preços do mercado ( NewTick , BookEvent ) e eventos de sistema como Timer ou Trade Transaction. A função do sistema de mesmo nome, com o prefixo On* está disponível em cada evento. Esta função é o manipulador (handler) deste evento. Por exemplo, se você deseja manipular a chegada de um novo tick, adicione um conjunto apropriado de procedimentos para a função OnTick:

//+------------------------------------------------------------------+
//| Função tick do Expert                                            |
//+------------------------------------------------------------------+
void OnTick()
  {
   // Manipulando a chegada de um novo tick
   // ...
  }

Quando o OnBookEvent ocorre, uma outra unidade do código que lida com mudanças no preços do Livro de Ofertas (Profundidade do Mercado) deve ser chamada:

void OnBookEvent (const string& symbol)
  {
   // Aqui nós manipulamos mudanças nos preços do volume de ordens
   // ...
  }

Com esta abordagem, quanto mais eventos são manipulados pelo nosso Expert Advisor, mais fragmentada se torna sua lógica. Condições para abertura e fechamento das posições podem ser separadas em unidades diferentes. Do ponto de vista da eficácia do programa, a separação em diferentes unidades pode ser uma boa solução, mas esta abordagem é indesejável no nosso caso. Pelo contrário, uma solução melhor é reuniar a lógica de negociação num local específico.

Além disso, alguns eventos não são suportados no MetaTrader 5. Neste caso, o Expert Advisor deve detectar a ocorrência de condições por conta própria. Por exemplo, o MetaTrader 5 não possui nenhum evento que manipula a abertura de uma nova barra. Entretanto, é a verificação mais comumente usada pelos Expert Advisors. Portanto, o modelo de eventos do motor de negociação descrito suporta não apenas os eventos do sistema, mas também eventos personalizados (não confunda com os eventos de usuário no gráfico), o que facilita muito o desenvolvimento dos Expert Advisors. Por exemplo, um desses eventos é a criação de uma nova barra no gráfico.

Para entender o modelo de evento proposto, vamos descrever os eventos relacionados aos preços ou tempo das alterações usando a enumeração especial ENUM_MARKET_EVENT_TYPE:

//+------------------------------------------------------------------+
//| Determina o tipo do evento do mercado.                           |
//+------------------------------------------------------------------+
enum ENUM_MARKET_EVENT_TYPE
  {
   MARKET_EVENT_TICK,               // Chegada de um novo tick
   MARKET_EVENT_BAR_OPEN,           // Abertura de uma nova barra
   MARKET_EVENT_TIMER,              // Temporizador acionado
   MARKET_EVENT_BOOK_EVENT          // Mudanca d o Livro de Ofertas (incluindo chegada de um novo tick).
  };

Como você pode ver, a enumeração inclui a descrição de ambos os eventos do sistema e o evento que não é suportado diretamente no MetaTrader 5 (MARKET_EVENT_BAR_OPEN - abertura de uma nova barra no EA).

Suponha que o nosso Expert Advisor universal possua quatro métodos da lógica de negociação: InitBuy, InitSell, SupportBuy, SupportSell. Nós descrevemos esses métodos na primeira parte do artigo "Expert Advisor Universal". Se um dos valores de enumeração é utilizado como parâmetro nestes métodos, a lógica de processamento do EA a qualquer momento pode descobrir o evento, baseado no método chamado. Vamos descrever um regime simplificado do Expert Advisor:

//+------------------------------------------------------------------+
//|                                                     ExampExp.mq5 |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Strategy\Series.mqh>
ulong ExpertMagic=12345; // o número mágico do Expert Advisor
//+------------------------------------------------------------------+
//| Determina o tipo do evento do mercado.                           |
//+------------------------------------------------------------------+
enum ENUM_MARKET_EVENT_TYPE
  {
   MARKET_EVENT_TICK,               //A chegada de um novo tick do símbolo atual 
   MARKET_EVENT_BAR_OPEN,           // Abertura de uma nova barra do instrumento atual
   MARKET_EVENT_TIMER,              // Temporizador acionado
   MARKET_EVENT_BOOK_EVENT          // Mudanças do Livro de Ofertas (incluindo a chegada do tick).
  };
//+------------------------------------------------------------------+
//| Protótipo do Expert Advisor Universal.                           |
//+------------------------------------------------------------------+
class CExpert
  {
public:
   void              InitBuy(ENUM_MARKET_EVENT_TYPE &event_id);
   void              InitSell(ENUM_MARKET_EVENT_TYPE &event_id);
   void              SupportBuy(ENUM_MARKET_EVENT_TYPE &event_id);
   void              SupportSell(ENUM_MARKET_EVENT_TYPE &event_id);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpert::InitBuy(ENUM_MARKET_EVENT_TYPE &event_id)
  {
   printf(__FUNCTION__+" EventID: "+EnumToString(event_id));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpert::InitSell(ENUM_MARKET_EVENT_TYPE &event_id)
  {
   printf(__FUNCTION__+" EventID: "+EnumToString(event_id));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpert::SupportBuy(ENUM_MARKET_EVENT_TYPE &event_id)
  {
   printf(__FUNCTION__+" EventID: "+EnumToString(event_id));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CExpert::SupportSell(ENUM_MARKET_EVENT_TYPE &event_id)
  {
   printf(__FUNCTION__+" EventID: "+EnumToString(event_id));
  }

ENUM_MARKET_EVENT_TYPE event_type;
CExpert Expert;
datetime last_time;
CTime Time;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   EventSetTimer(1);
   return INIT_SUCCEEDED;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Função tick do Expert                                            |
//+------------------------------------------------------------------+
void OnTick()
  {
   event_type=MARKET_EVENT_TICK;
   CallExpertLogic(event_type);
   if(last_time!=Time[0])
     {
      event_type=MARKET_EVENT_BAR_OPEN;
      CallExpertLogic(event_type);
      last_time=Time[0];
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnTimer()
  {
   event_type=MARKET_EVENT_TIMER;
   CallExpertLogic(event_type);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
   event_type=MARKET_EVENT_TIMER;
   CallExpertLogic(event_type);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CallExpertLogic(ENUM_MARKET_EVENT_TYPE &event)
  {
   Expert.InitBuy(event);
   Expert.InitSell(event);
   Expert.SupportBuy(event);
   Expert.SupportSell(event);
  }
//+------------------------------------------------------------------+

Os eventos que indicam as alterações dos preços de mercado chamam a função CallExpertMagic, onde o evento atual é colocado como um parâmetro. Esta função, por sua vez chama os quatro métodos do CExpert. Quando chamados, cada um desses métodos imprime seu nome e o identificador do evento. Se você executar este Expert Advisor num gráfico, registros apropriados indicam que as IDs dos eventos processados serão exibidas depois de algum tempo:

2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportSell EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportBuy EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitSell EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitBuy EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:40.015 ExampExp2 (Si-12.15,M1) CExpert::InitSell EventID: MARKET_EVENT_TICK
2015.11.30 14:13:40.015 ExampExp2 (Si-12.15,M1) CExpert::InitBuy EventID: MARKET_EVENT_TICK
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportSell EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportBuy EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitSell EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitBuy EventID: MARKET_EVENT_BAR_OPEN
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportSell EventID: MARKET_EVENT_TICK
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::SupportBuy EventID: MARKET_EVENT_TICK
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitSell EventID: MARKET_EVENT_TICK
2015.11.30 14:13:39.981 ExampExp2 (Si-12.15,M1) CExpert::InitBuy EventID: MARKET_EVENT_TICK

 

Acesso a Eventos que Ocorrem em Outros Instrumentos, a Estrutura do MarketEvent

Ao projetar um sistema de negociação que analisa vários símbolos, é necessário criar um mecanismo para controlar as alterações nos preços dos vários instrumentos. No entanto, a função padrão OnTick somente é chamada na execução do Expert Advisor para um novo tick do instrumento. Por outro lado, os desenvolvedores do sistema de negociação podem usar a função OnBookEvent que responde a mudanças no Livro de Ofertas (Profundidade do Mercado). Ao contrário da OnTick, a OnBookEvent é chamada para qualquer mudança no livro de ofertas do instrumento, a qual você inscreveu usando a função MarketBookAdd .

Mudanças no livro de ofertas acontecem muito frequentemente, logo para acompanhar estes eventos é necessário um processo intensivo de recursos. Como regra geral, o monitoramento da alteração do tick atual do símbolo requerido é suficiente para o Expert Advisor. Por outro lado, no caso de alteração do livro de oferta também inclui a chegada de um novo tick. Além do OnBookEvent, você pode configurar as chamadas OnTimer em intervalos específicos e analisar as alterações dos preços de vários símbolos nessa função.

Assim você pode adicionar uma chamada de algum módulo intermédio (vamos chamá-lo de EventProcessor) nas funções do sistema que reagem aos eventos NewTick, BookEvent e Timer, analisando simultaneamente as mudanças dos preços nos vários instrumentos, gerando um evento apropriado. Cada evento teria uma descrição unificada sob a forma de uma estrutura e seria enviado pelos métodos de controle da estratégia. Tendo recebido um evento apropriado como uma estrutura, a estratégia reagiria a ela ou a ignoraria. Neste caso, a função do sistema que realmente iniciou o evento para o Expert Advisor seria desconhecida.

De fato, se um Expert Advisor recebe uma notificação de um novo tick, não tem importância se a informação é recebida através do OnTick, OnTimer ou do OnBookEvent. A única coisa que importa é que existe um novo tick no símbolo determinado. Um manipulador de eventos pode ser usado para muitas estratégias. Por exemplo, se cada estratégia é representada como uma classe customizada, múltiplas instâncias destes tipos podem ser armazenadas na lista especial das estratégias. Neste caso, qualquer estratégia da lista poderá receber um novo evento gerado pelo EventProcessor. O diagrama a seguir mostra como os eventos são gerados e enviados:

Fig. 1. Diagrama de geração e envio do evento

Agora, vamos considerar a estrutura real que será colocada no Expert Advisors como um evento. A estrutura é chamada de MarketEvent e a sua definição é a seguinte:

//+------------------------------------------------------------------+
//| A estrutura define o tipo de evento, o instrumento onde          |
//| ele ocorreu e o seu timeframe (para o evento BarOpen)            |
//+------------------------------------------------------------------+
struct MarketEvent
  {
   ENUM_MARKET_EVENT_TYPE type;     // Tipo de evento.
   ENUM_TIMEFRAMES   period;        // O timeframe do gráfico aplica-se ao evento (apenas para MARKET_EVENT_BAR_OPEN).
   string            symbol;        // O nome do símbolo em que o evento ocorreu.
  };

Após receber uma instância da estrutura pela referência, o método decisório da negociação poderá analisá-la e definir corretamente, com base nas seguintes informações:

  • o tipo de evento
  • o timeframe do gráfico, no qual ocorreu o evento
  • o nome do instrumento, no qual ocorreu o evento

Se analisarmos que o timeframe do gráfico é inútil para um evento (por exemplo NewTick ou Timer), o campo period da estrutura MarketEvent é sempre preenchida com o valor PERIOD_CURRENT.

 

O Evento "New Bar". Novo Tick e Algoritmos de Detecção da Barra

A fim de acompanhar novos ticks e a formação de novas barras em vários instrumentos, vamos precisar escrever classes de módulos apropriadas para a execução dessa tarefa. Estes módulos são partes internas da classe CStrategy e o usuário não interage diretamente com eles. O primeiro módulo a considerar é a classe CLickDetector. O código-fonte da classe está disponível a seguir:

//+------------------------------------------------------------------+
//|                                              NewTickDetector.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <Object.mqh>
//+------------------------------------------------------------------+
//| Detector de novo tick                                            |
//+------------------------------------------------------------------+
class CTickDetector : public CObject
  {
private:
   string            m_symbol;         // O símbolo para rastrear novos ticks.
   MqlTick           m_last_tick;      // Último tick utilizado.
public:
                     CTickDetector(void);
                     CTickDetector(string symbol);
   string            Symbol(void);
   void              Symbol(string symbol);
   bool              IsNewTick(void);
  };
//+------------------------------------------------------------------+
//| O construtor define por padrão o timeframe atual e               |
//| o simbolo.                                                       |
//+------------------------------------------------------------------+
CTickDetector::CTickDetector(void)
  {
   m_symbol=_Symbol;
  }
//+------------------------------------------------------------------+
//| Cria um objeto com um símbolo e timeframe predefinido.           |
//+------------------------------------------------------------------+
CTickDetector::CTickDetector(string symbol)
  {
   m_symbol=symbol;
  }
//+------------------------------------------------------------------+
//| Define o nome do símbolo, no qual a formação de um novo tick     |
//| deve ser rastreada.                                              |
//+------------------------------------------------------------------+
void CTickDetector::Symbol(string symbol)
  {
   m_symbol=symbol;
  }
//+------------------------------------------------------------------+
//| Retorna o nome do símbolo após o surgimento de um novo tick      |
//| ser rastreada.                                                   |
//+------------------------------------------------------------------+
string CTickDetector::Symbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//| Retorna verdadeiro se no símbolo escolhido e timeframe existe    |
//| um novo tick.                                                     |
//+------------------------------------------------------------------+
bool CTickDetector::IsNewTick(void)
  {
   MqlTick tick;
   SymbolInfoTick(m_symbol,tick);
   if(tick.last!=m_last_tick.last || 
      tick.time!=m_last_tick.time)
     {
      m_last_tick=tick;
      return true;
     }
   return false;
  }

Seu principal método utilizado é o IsNewTick. Ele retorna verdadeiro se um novo tick do símbolo monitorado foi recebido. O instrumento financeiro usado para monitorar é configurado usando o método Symbol. A classe CLickDetector é derivada da CObject. Por isso, pode ser adicionada como um elemento da colecção CArrayObj. É isso que precisamos. Por exemplo, podemos criar dez cópias da CTickDetect, cada uma delas irá monitorar seu próprio símbolo. Pela referência consistente ao conjunto de classes do tipo CArrayObj, você pode descobrir rapidamente o símbolo, em que o novo tick foi formado e então gerar o evento correspondente que colocaria essa informação na coleção dos Expert Advisors.

Como já foi mencionado, em adição aos novos ticks, é frequentemente necessário determinar o surgimento de novas barras. É melhor entregar essa tarefa para a classe especial CBarDetector. Ele funciona semelhante a CLickDetector. O principal método da CBarDetector é o IsNewBar - o método retorna verdadeiro, se existe uma nova barra do instrumento, com o nome especificado anteriormente através do método Symbol. Seu código fonte é o seguinte:

//+------------------------------------------------------------------+
//|                                               NewBarDetecter.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
//+------------------------------------------------------------------+
//| A classe detecta o surgimento de uma nova barra no símbolo e     |
//| período específico                                               |
//+------------------------------------------------------------------+
class CBarDetector : public CObject
  {
private:
   ENUM_TIMEFRAMES   m_timeframe;      // O timeframe para controlar a formação de uma nova barra
   string            m_symbol;         // O símbolo para controlar a formação de uma nova barra
   datetime          m_last_time;      // O tempo da última barra conhecida
public:
                     CBarDetector(void);
                     CBarDetector(string symbol,ENUM_TIMEFRAMES timeframe);
   void              Timeframe(ENUM_TIMEFRAMES tf);
   ENUM_TIMEFRAMES   Timeframe(void);
   void              Symbol(string symbol);
   string            Symbol(void);
   bool              IsNewBar(void);
  };
//+------------------------------------------------------------------+
//| O construtor define por padrão o timeframe atual e               |
//| o simbolo.                                                       |
//+------------------------------------------------------------------+
CBarDetector::CBarDetector(void)
  {
   m_symbol=_Symbol;
   m_timeframe=Period();
  }
//+------------------------------------------------------------------+
//| Cria um objeto com um símbolo e timeframe predefinido.           |
//+------------------------------------------------------------------+
CBarDetector::CBarDetector(string symbol,ENUM_TIMEFRAMES tf)
  {
   m_symbol=symbol;
   m_timeframe=tf;
  }
//+------------------------------------------------------------------+
//| Define o timeframe para rastrear o surgimento de uma             |
//| nova barra.                                                      |
//+------------------------------------------------------------------+
void CBarDetector::Timeframe(ENUM_TIMEFRAMES tf)
  {
   m_timeframe=tf;
  }
//+------------------------------------------------------------------+
//| Retorna o timeframe para controlar o surgimento de uma           |
//| nova barra.                                                      |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CBarDetector::Timeframe(void)
  {
   return m_timeframe;
  }
//+------------------------------------------------------------------+
//| Define o nome do símbolo, onde a formação de uma nova barra      |
//| deve ser rastreada.                                              |
//+------------------------------------------------------------------+
void CBarDetector::Symbol(string symbol)
  {
   m_symbol=symbol;
  }
//+------------------------------------------------------------------+
//| Retorna o nome do símbolo para a formação de uma nova barra a    |
//| ser rastreada.                                                   |
//+------------------------------------------------------------------+
string CBarDetector::Symbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//| Retorna verdadeiro se no símbolo escolhido e timeframe existe    |
//| nova barra.                                                      |
//+------------------------------------------------------------------+
bool CBarDetector::IsNewBar(void)
  {
   datetime time[];
   if(CopyTime(m_symbol, m_timeframe, 0, 1, time) < 1)return false;
   if(time[0] == m_last_time)return false;
   return m_last_time = time[0];
  }

 

A classe CPositionMT5 - A Base do Algoritmo da Independência de Plataforma

Agora é hora de analisar uma das classes mais importantes na operação do Expert Advisor universal. Esta categoria inclui os métodos de trabalho com posições no terminal MetaTrader 5. De um ponto de vista técnico, é muito simples. É uma classe "wrapper" que atua como intermediária entre um Expert Advisor e as funções do sistema relativas as operações com posições no MetaTrader 5 - PositionSelect e PositionGet. No entanto, ele implementa uma função importante em relação as classes oferecidas - independência da plataforma.

Ao analisar cuidadosamente todos os módulos descritos acima, podemos concluir que eles não usam funções específicas para uma plataforma de negociação (MetaTrader 4 ou MetaTrader 5). Isto é verdade, pois as versões modernas MQL4 e MQL5 são, na verdade, uma única e mesma linguagem de programação com diferentes conjuntos de funções. Certos conjuntos específicos de funções para as duas plataformas estão interligadas com o gerenciamento das posições de negociação.

Naturalmente, todos os Expert Advisors usam funções de gerenciamento de posição. No entanto, se em vez destas funções, um Expert Advisor utiliza uma única interface abstrata sob a forma de uma classe de posição, seria possível (pelo menos teoricamente) desenvolver um robô de negociação que poderia ser compilado ao mesmo tempo para MetaTrader 4 e MetaTrader 5, sem quaisquer mudanças no seu código-fonte. Tudo o que precisamos fazer é desenvolver algumas classes alternativas que implementam o trabalho com posições. Uma classe vai usar as funções MetaTrader 4; o outra, as funções do MetaTrader 5. No entanto, ambas as classes fornecem o mesmo conjunto de métodos para o Expert Advisor final, proporcionando assim a "independência da plataforma".

Na prática, no entanto, o problema da criação de um Expert Advisor com independência de plataforma é um pouco mais complicado e requer um artigo separado. Este artigo seria um material bastante extenso, por isso não vamos discuti-lo agora, mas será considerado em outras partes.

O segundo argumento a favor da classe especial que representa a abertura de uma posição na estratégia é a admistração individual de cada posição. O motor de negociação transmite a lista de posições e coloca cada uma delas na lógica do Expert Advisor. Desta forma, nós conseguimos a máxima flexibilidade: o Expert Advisor gere cada posição individual e, portanto, torna-se possível descrever regras para as estratégias que gerem mais de uma posição de negociação. Claro, você pode ter apenas uma posição no MetaTrader 5. Mas, se conectamos o motor de negociação na plataforma MetaTrader 4, ele apresentará uma função muito importante.

Aqui está o código-fonte desta classe. Ele é simples e direto:

//+------------------------------------------------------------------+
//|                                                  PositionMT5.mqh |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
#include "Logs.mqh"
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//| Ativa a classe de posição nas estratégias clássicas              |
//+------------------------------------------------------------------+
class CPositionMT5 : public CObject
  {
private:
   ulong             m_id;                // Um identificador de posição única
   uint              m_magic;             // Um identificador exclusivo do Expert Advisor para a posição que pertence.
   ENUM_POSITION_TYPE m_direction;         // Direção da posição
   double            m_entry_price;       // Preço de entrada na posição
   string            m_symbol;            // O símbolo da posição está aberta
   datetime          m_time_open;         // Tempo de abertura
   string            m_entry_comment;     // Comentário de entrada
   bool              m_is_closed;         // Verdadeiro se a posição foi fechada
   CLog*             Log;                 // Carregamento
   CTrade            m_trade;             // Módulo de negociação
public:
                     CPositionMT5(void);
   uint              ExpertMagic(void);
   ulong             ID(void);
   ENUM_POSITION_TYPE Direction(void);
   double            EntryPrice(void);
   string            EntryComment(void);
   double            Profit(void);
   double            Volume(void);
   string            Symbol(void);
   datetime          TimeOpen(void);
   bool              CloseAtMarket(string comment="");
   double            StopLossValue(void);
   bool              StopLossValue(double sl);
   double            TakeProfitValue(void);
   bool              TakeProfitValue(double tp);
  };
//+------------------------------------------------------------------+
//| A inicialização das propriedades básicas de uma posição          |
//+------------------------------------------------------------------+
void CPositionMT5::CPositionMT5(void) : m_id(0),
                                        m_entry_price(0.0),
                                        m_symbol(""),
                                        m_time_open(0)
  {
   m_id=PositionGetInteger(POSITION_IDENTIFIER);
   m_magic=(uint)PositionGetInteger(POSITION_MAGIC);
   m_direction=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
   m_entry_price=PositionGetDouble(POSITION_PRICE_OPEN);
   m_symbol=PositionGetString(POSITION_SYMBOL);
   m_time_open=(datetime)PositionGetInteger(POSITION_TIME);
   m_entry_comment=PositionGetString(POSITION_COMMENT);
   m_trade.SetExpertMagicNumber(m_magic);
  }
//+------------------------------------------------------------------+
//| Retorna a direção da posição.                                    |
//+------------------------------------------------------------------+
ENUM_POSITION_TYPE CPositionMT5::Direction(void)
  {
   return m_direction;
  }
//+------------------------------------------------------------------+
//| Retorna a ID única do Expert Advisor                             |
//| na posição atual que pertence.                                   |
//+------------------------------------------------------------------+
uint CPositionMT5::ExpertMagic(void)
  {
   retornar m_magic;
  }
//+------------------------------------------------------------------+
//| Retorna o identificador de posição única.                        |
//+------------------------------------------------------------------+
ulong CPositionMT5::ID(void)
  {
   return m_id;
  }
//+------------------------------------------------------------------+
//| Retorna o preço de entrada na posição.                           |
//+------------------------------------------------------------------+
double CPositionMT5::EntryPrice(void)
  {
   return m_entry_price;
  }
//+------------------------------------------------------------------+
//| Retorna o comentário de entrada de uma posição ativa.            |
//+------------------------------------------------------------------+
string CPositionMT5::EntryComment(void)
  {
   return m_entry_comment;
  }
//+------------------------------------------------------------------+
//| Retorna o nome do símbolo para a posição que está atualmente     |
//| aberta.                                                          |
//+------------------------------------------------------------------+
string CPositionMT5::Symbol(void)
  {
   return m_symbol;
  }
//+------------------------------------------------------------------+
//| Retorna o tempo de abertura da posição.                          |
//+------------------------------------------------------------------+
datetime CPositionMT5::TimeOpen(void)
  {
   return m_time_open;
  }
//+------------------------------------------------------------------+
//| 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 CPositionMT5::StopLossValue(void)
  {
   if(!PositionSelect(m_symbol))
      return 0.0;
   return PositionGetDouble(POSITION_SL);
  }
//+------------------------------------------------------------------+
//| Define um nível de stop loss absoluto                            |
//+------------------------------------------------------------------+
bool CPositionMT5::StopLossValue(double sl)
  {
   if(!PositionSelect(m_symbol))
      return false;
   return m_trade.Buy(0.0, m_symbol, 0.0, sl, TakeProfitValue(), NULL);
  }
//+------------------------------------------------------------------+
//| 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 CPositionMT5::TakeProfitValue(void)
  {
   if(!PositionSelect(m_symbol))
      return 0.0;
   return PositionGetDouble(POSITION_TP);
  }
//+------------------------------------------------------------------+
//| Define um nível de stop loss absoluto                            |
//+------------------------------------------------------------------+
bool CPositionMT5::TakeProfitValue(double tp)
  {
   if(!PositionSelect(m_symbol))
      return false;
   return m_trade.Buy(0.0, m_symbol, 0.0, StopLossValue(), tp, NULL);
  }
//+------------------------------------------------------------------+
//| Fecha a posição atual pelo mercado e define um comentário de     |
//| fechamento igual a 'comment'                                     |
//+------------------------------------------------------------------+
bool CPositionMT5::CloseAtMarket(string comment="")
  {
   if(!PositionSelect(m_symbol))
      return false;
   return m_trade.PositionClose(m_symbol);
  }
//+------------------------------------------------------------------+
//| Retorna volume de posição atual.                                 |
//+------------------------------------------------------------------+
double CPositionMT5::Volume(void)
  {
   if(!PositionSelect(m_symbol))
      return false;
   return PositionGetDouble(POSITION_VOLUME);
  }
//+------------------------------------------------------------------+
//| Retorna lucro corrente de uma posição em moeda do depósito.      |
//+------------------------------------------------------------------+
double CPositionMT5::Profit(void)
  {
   if(!PositionSelect(m_symbol))
      return false;
   return PositionGetDouble(POSITION_PROFIT);
  }

Como você pode ver, a principal tarefa da classe é o retorno de uma propriedade particular para abertura de posições. Além disso, a classe fornece vários métodos para gerenciar a posição atual: fechamento, mudança do Take Profit e Stop Loss.

 

Protótipo de Estratégia da Negociação - a classe CStrategy

Nós consideramos um número de módulos que executam tarefas comuns. Analisamos também o modelo de evento, o modelo de tomada de decisão e o algoritmo de ação de um Expert Advisor típico. Agora precisamos combinar as informações num único módulo do Expert Advisor - a classe CStrategy. Cujo objetivo é a execução de várias tarefas. Aqui estão algumas delas:

  • Arranjar uma seqüência única de ações de negociação a todas as estratégias que são baseadas no CStrategy.
  • Identificação dos eventos Timer, NewTek, BookEvent, NewBar e o uso destes a uma estratégia personalizada.
  • Implementação de fácil acesso às cotações dos símbolos baseados no modelo MetaTrader 4.
  • Gerenciamento dos modos de negociação.
  • Acesso as informações sobre as aberturas de posições e o recebimento das estatísticas sobre as mesmas.

A estratégia é uma classe bastante extensa, por isso não vamos publicar o seu código-fonte completo aqui. Em vez disso, vamos focar nos algoritmos mais importantes da classe. A primeira coisa que vamos considerar é o seu modelo de evento. Nós já sabemos sobre este modelo. Agora somente precisamos considerar um processo de recepção de eventos e entregá-los a uma determinada estratégia. A CStrategy é uma classe familiar da nossa futura estratégia. Consequentemente, estamos familiarizados com seus métodos que notificam a recepção de eventos apropriados:

//+------------------------------------------------------------------+
//| A classe básica da disposição em camadas da estratégia.          |
//+------------------------------------------------------------------+
class CStrategy : public CObject
  {
   //...
   void              OnTick(void);
   void              OnTimer(void);
   void              OnBookEvent(string symbol);
   virtual void      OnTradeTransaction(const MqlTradeTransaction &trans,
                                        const MqlTradeRequest &request,
                                        const MqlTradeResult &result);
   //...
  };

Por favor, note que todos os manipuladores de eventos, exceto o OnTradeTransaction não são virtuais. Isto significa que, Tick, Timer ou BookEvent não podem ser manipulados diretamente no nível da estratégia. Em vez disso, eles são tratados pela classe CStrategy. O evento TradeTransaction não é usado nesta classe, por isso, é virtual.

Abaixo estão conteúdo OnTick, OnTimer e OnBookEvent:

//+------------------------------------------------------------------+
//| Chamada pelo gerente da estratégia sobre o evento do sistema     |
//| 'novo tick'.                                                     |
//+------------------------------------------------------------------+
void CStrategy::OnTick(void)
  {
   NewTickDetect();
   NewBarsDetect();
  }
//+------------------------------------------------------------------+
//| Chamada pelo gerente da estratégia sobre o evento do sistema     |
//| 'OnTimer'.                                                       |
//+------------------------------------------------------------------+
void CStrategy::OnTimer(void)
  {
   m_event.symbol=Symbol();
   m_event.type=MARKET_EVENT_TIMER;
   m_event.period=(ENUM_TIMEFRAMES)Period();
   CallSupport(m_event);
   CallInit(m_event);
   NewTickDetect();
   NewBarsDetect();
  }
//+------------------------------------------------------------------+
//| Chamada pelo gerente da estratégia sobre o evento do sistema     |
//| 'OnBookEvent'.                                                   |
//+------------------------------------------------------------------+
void CStrategy::OnBookEvent(string symbol)
  {
   m_event.symbol=symbol;
   m_event.type=MARKET_EVENT_BOOK_EVENT;
   m_event.period=PERIOD_CURRENT;
   CallSupport(m_event);
   CallInit(m_event);
   NewTickDetect();
   NewBarsDetect();
  }

Como mencionado acima, o evento do sistema NewTick somente se aplica ao instrumento no qual o Expert Advisor atual está em execução. Se queremos o NewTick seja um evento de múltiplos símbolos, devemos usar um manipulador especial dos novos ticks e acompanhar o surgimento de novas barras. Esta tarefa é designada apropriando-se dos métodos fechados: NewBarDetect e NewTickDetect. Abaixo está o seu código fonte:

//+------------------------------------------------------------------+
//| Detecta o surgimento de uma nova barra e gera um evento          |
//| adequado ao Expert Advisor.                                      |
//+------------------------------------------------------------------+
void CStrategy::NewBarsDetect(void)
  {
   if(m_bars_detecors.Total()==0)
      AddBarOpenEvent(ExpertSymbol(),Timeframe());
   for(int i=0; i<m_bars_detecors.Total(); i++)
     {
      CBarDetector *bar=m_bars_detecors.At(i);
      if(bar.IsNewBar())
        {
         m_event.period = bar.Timeframe();
         m_event.symbol = bar.Symbol();
         m_event.type=MARKET_EVENT_BAR_OPEN;
         CallSupport(m_event);
         CallInit(m_event);
        }
     }
  }
//+------------------------------------------------------------------+
//| Detecta a chegada de novos ticks de multi-instrumentos.          |
//+------------------------------------------------------------------+
void CStrategy::NewTickDetect(void)
  {
   if(m_ticks_detectors.Total()==0)
      AddTickEvent(ExpertSymbol());
   for(int i=0; i<m_ticks_detectors.Total(); i++)
     {
      CTickDetector *tick=m_ticks_detectors.At(i);
      if(tick.IsNewTick())
        {
         m_event.period=PERIOD_CURRENT;
         m_event.type=MARKET_EVENT_TICK;
         m_event.symbol=tick.Symbol();
         CallSupport(m_event);
         CallInit(m_event);
        }
     }
  }

Os métodos realmente verificam os detectores de novos ticks e de barras (as classes CLickDetector e CBarDetector descritas acima). Cada um destes detectores são os primeiros configurados ao instrumento financeiro do Expert Advisor que realmente negocia. Se um novo tick ou barra aparece, os métodos especiais CallSupport e CallInit são chamados, em seguida chama os métodos de negociação da estratégia específica.

O algoritmo geral dos manipuladores de eventos, incluindo NewBarsDetect e NewTickDetect, é o seguinte:

  1. detecta a ocorrência de um novo evento;
  2. preenche a estrutura MarketEvent com uma especificação da ID de eventos apropriada e seus atributos;
  3. chama o método CallSupport, então chama o SupportBuy e SupportSell virtuais com o evento transmitido ao método;
  4. da mesma forma, chama o método CallInit, em seguida chama o InitBuy e InitSell virtual com o evento transmitido ao método.

Para entender como o controle é transmitido a uma certa estratégia, é preciso considerar os métodos CallInit e CallSupport. Aqui está o código fonte de CallInit:

//+------------------------------------------------------------------+
//| Chama a posição de abertura lógica desde que o estado da         |
//| negociação não a restrinja explicitamente.                       |
//+------------------------------------------------------------------+
void CStrategy::CallInit(const MarketEvent &event)
  {
   m_trade_state=m_state.GetTradeState();
   if(m_trade_state == TRADE_STOP)return;
   if(m_trade_state == TRADE_WAIT)return;
   if(m_trade_state == TRADE_NO_NEW_ENTRY)return;
   SpyEnvironment();
   if(m_trade_state==TRADE_BUY_AND_SELL || m_trade_state==TRADE_BUY_ONLY)
      InitBuy(event);
   if(m_trade_state==TRADE_BUY_AND_SELL || m_trade_state==TRADE_SELL_ONLY)
      InitSell(event);
  }

O método recebe o estado de negociação do módulo m_state (da classe CTradeState descrito no primeiro artigo) e decide se é possível chamar novos métodos de inicialização da posição, colocando o estado de negociação. Se o estado de negociação proíbe a negociação, esses métodos não são chamados. Caso contrário, eles são chamados com a indicação do evento, como resultado chama o método CallInit.

O CallSupport funciona de forma semelhante, mas sua lógica é um pouco diferente. Aqui está o código fonte:

//+------------------------------------------------------------------+
//| Chama a lógica de manutenção da posição desde que o estado da    |
//| negociação não seja igual a TRADE_WAIT.                          |
//+------------------------------------------------------------------+
void CStrategy::CallSupport(const MarketEvent &event)
  {
   m_trade_state=m_state.GetTradeState();
   if(m_trade_state == TRADE_WAIT)return;
   SpyEnvironment();
   for(int i=ActivePositions.Total()-1; i>=0; i--)
     {
      CPosition *pos=ActivePositions.At(i);
      if(pos.ExpertMagic()!=m_expert_magic)continue;
      if(pos.Direction()==POSITION_TYPE_BUY)
         SupportBuy(event,pos);
      else
         SupportSell(event,pos);
      if(m_trade_state==TRADE_STOP && pos.IsActive())
         ExitByStopRegim(pos);
     }
  }

De um modo semelhante, o método recebe o estado atual da negociação do Expert Advisor. Se ele permite a administração das posições, o método vai começar a pesquisa através de todas as posições abertas em andamento. Estas posições são representados pela classe CPosition. Apos ter recebidos acesso a cada posição, o método compara o seu número mágico com o número mágico do Expert Advisor atual. Uma vez que uma correspondência foi encontrada, os métodos apropriados de gerenciamento de posição são chamados: SupportBuy para uma posição comprada e SupportSell para uma posição de vendida.

 

Conclusão

Examinamos todos os principais módulos da classe CStrategy, através do qual ela fornece ampla funcionalidade para uma estratégia personalizada. Uma estratégia derivada dessa classe pode operar com cotações de preços e eventos de negociação. A estratégia começa uma sequência unificada de ações de negociação herdada da classe CStrategy. Assim a estratégia pode ser desenvolvida como um módulo separado, descrevendo apenas operações de negociação e regras. Esta solução ajuda a reduzir significativamente o esforço aplicado no desenvolvimento do algoritmo de negociação.

No próximo artigo "Expert Advisor Universal: Estratégias Personalizadas e Classes Auxiliares de Negociação (Parte 3)", vamos discutir o processo de desenvolvimento da estratégia com base nos algoritmos descritos.

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

Arquivos anexados |
strategyarticle.zip (100.23 KB)
Últimos Comentários | Ir para discussão (1)
zemo
zemo | 29 nov 2018 em 14:26

Mr.Vasily,

very nice code.. and useful to me..

in the mt5 news builds (1952), we got a "message" in the compiler, 


bool CBarDetector::IsNewBar(void)

  {

   datetime time[];

   if(CopyTime(m_symbol, m_timeframe, 0, 1, time) < 1)return false;

   if(time[0] == m_last_time)return false;

   return (m_last_time = time[0]);    //<=============HERE

  }

//+------------------------------------------------------------------+

///////////MESSAGE in THE METAEDITOR compiler///////////////
expression not boolean NewBarDetector.mqh 87 24


the correct should be this? please confirm

//+------------------------------------------------------------------+
//| Returns true if for the given symbol and timeframe there is      |
//| a new bar.                                                       |
//+------------------------------------------------------------------+
bool CBarDetector::IsNewBar(void)
  {
   datetime time[];
   if(CopyTime(m_symbol, m_timeframe, 0, 1, time) < 1)return (false);
   if(time[0] == m_last_time)return (false);
   return (m_last_time == time[0]);
  }
//+------------------------------------------------------------------+



Interfaces Gráficas I: Formulário para os Controles (Capítulo 2) Interfaces Gráficas I: Formulário para os Controles (Capítulo 2)
Neste artigo, nós vamos criar o primeiro e o principal elemento da interface gráfica - o formulário para os controles. Vários controles podem ser anexados a este formulário, podendo ser de qualquer lugar e qualquer combinação.
Interfaces Gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) Interfaces Gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1)
Este artigo é o começo de uma outra série sobre o desenvolvimento de interfaces gráficas. Atualmente, não há uma única biblioteca de código que permite a criação rápida e fácil de interfaces gráficas de alta qualidade dentro das aplicações em MQL. Me refiro as interfaces gráficas que estamos acostumados nos sistemas operacionais comuns.
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.
Expert Advisor Universal: Modos de Negociação das Estratégias (Parte 1) Expert Advisor Universal: Modos de Negociação das Estratégias (Parte 1)
Qualquer desenvolvedor de Expert Advisor, independentemente de suas habilidades de programação, diariamente é confrontado com as mesmas tarefas de negociação e problemas algorítmicos, que devem ser resolvidos para organizar um processo de negociação confiável. O artigo descreve as possibilidades do motor de negociação CStrategy que possibilita a solução destas tarefas e fornece ao usuário um mecanismo eficaz para descrever uma idéia de negociação personalizada.