Como Proteger Seu Expert Advisor Investindo na Bolsa de Moscou

Vasiliy Sokolov | 3 dezembro, 2015

Índice


Introdução

Qualquer investidor nos mercados financeiros está sujeito aos riscos de perdas. A natureza desses riscos é diferente, mas os resultados são os mesmos - dinheiro perdido, tempo perdido e uma sensação duradoura de frustração. Para evitar essas situações desagradáveis, devemos seguir algumas regras simples: gerenciarmos nossos riscos (Money Management), desenvolvermos algoritmos confiantes para os investimentos e usarmos sistemas de negociação rentáveis. Essas regras dizem respeito a diferentes áreas da negociação e devemos combiná-las, de modo que podemos esperar por resultados positivos e confiáveis nos investimentos.

Atualmente, você pode encontrar livros e artigos que cobrem abundantemente as questões sobre como gerenciar o dinheiro, bem como sistemas de negociação que podem ser utilizados nas operações diárias. Infelizmente, o mesmo não é verdade referente as regras básicas de segurança quanto aos investimentos no mercado.

O artigo tem como objetivo mudar isso, descrevendo as regras de segurança que devem ser seguidas para entrar nos mercados. As regras consistem em métodos e práticas de negociação que evitam perdas financeiras consideráveis causadas por picos de preços, falta de liquidez e outras forças maiores. O artigo foca o risco técnico, deixando de lado os temas de desenvolvimento de estratégias de negociação e gestão dos riscos.

Traz uma abordagem prática para a teoria de negociação descrita no artigo "Princípios da Precificação da Bolsa Tomando Como Exemplo o Mercado de Derivativos da Bolsa de Moscou". Enquanto o artigo mencionado estabelece a teoria de precificação da bolsa, o presente artigo descreve os mecanismos de proteção do colapso financeiro acidental a você e a seu Expert Advisor, causado por alguns elementos perigosos desta mesma precificação da bolsa.


Capítulo 1. Natureza Discreta de um Fluxo de Preço e Como Enfrentá-la


1.1. Natureza discreta de um Fluxo de Preço. Lacunas de Preço

A liquidez é um dos principais conceitos no mercado de ações. É a capacidade de compra e venda de bens a preços próximos aos de mercado. Quanto maior a liquidez do mercado, mais próximos os preços acompanham um ao outro no mercado. O preço é um processo discreto. Isso significa que os preço consistem de várias operações seguindo umas as outras em alta velocidade. O fluxo de transações é representado pelas cotações ou gráficos de ticks que são re-organizados em candles ou gráficos de barras em qualquer timeframe. Da perspectiva de um trader, tais gráficos são contínuos. Em qualquer dado momento, uma barra ou uma candle tem um certo preço. Isto pode ser demonstrado da seguinte maneira:


Fig. 1. Barra de preço e sua função contínua do preço

Independentemente de qual ponto o preço estará na barra, ele tem seu próprio nível exibido como uma linha vermelha. Isto é exatamente como as barras estão representadas no modo "Cada tick" do testador de estratégia do MetaTrader. Nesse modo, os preços são gerados continuamente e sequencialmente. Por exemplo, se o passo é 1 e o preço passou de 10 a 15, os preços nos ticks 11, 12, 13 e 14 também estão disponíveis durante o movimento dos preços. Na realidade, o preço é discreto e as mudanças são realizadas em pequenos saltos. Além disso, essas mudanças nem sempre são consistentes e regulares. Às vezes, o preço pode saltar vários níveis ao mesmo tempo. Vamos examinar a mesma barra usando um caráter mais realista (discreto) da variação de preços:

Fig. 2. Barra de preço e sua função discreta de preço

Como podemos ver, realmente não existe algo como um preço contínuo (exibido aqui como uma linha tracejada vermelha). Isto significa que suas ordens a mercado (especialmente as tipos stop) podem ser desencadeadas por um preço não intencional! Esta é uma característica muito perigosa das ordens a mercado. Vamos examinar como uma ordem pendente Buy Stop pode ser desencadeada nesta barra. Suponha que enviemos uma solicitação a mercado no nível 64 203 ou superior (linha azul sendo transposta pela barra de preço tracejada). Este preço simplesmente não existe no interior da barra. Nesse caso, a ordem é ativada no preço 64 220 que é significativamente mais elevado do que 64 203:

Fig. 3. Ativando ordens pendentes a preços discretos

No nosso exemplo, a execução real da ordem ocorre com uma diferença de 13 pontos do preço solicitado, a diferença entre esses preços forma um desvio (slippage). Se o mercado for bastante líquido, os preços discretos chegam a um fluxo denso, movendo-se de um nível para outro sem problemas. No entanto pode haver diferenças com o preço mudando rapidamente, mesmo em mercados de alta liquidez. É impossível ver as lacunas através gráficos comuns de preços, mas devemos estar conscientes da sua existência.


1.2. Picos de Preço

Devido à falta de liquidez, diferenças de preços podem atingir valores muito elevados, transformando-se em picos de preços (ofertas realizadas a preços que se desviam muito do mercado). São muito perigosos tanto para os traders manuais como aos sistemas de negociação automatizados. Esses picos acionam ordens stops, executando-as a preços muito desfavoráveis.

Vamos considerar um caso simples: suponha que nós negociamos um contrato futuro RUB/USD e colocamos uma ordem Buy Stop para comprar a 64 200. O stop loss é colocado a 64 100. Esperamos que o preço se mova, no entanto se isso não acontecer, o nosso stop loss em 64 100 é limitado a uma perda de 100 pontos. Nosso risco é aparentemente limitado, mas isto não é verdade. Vamos observar um caso quando a alta do preço ocorre ativando a nossa ordem stop a um preço bastante diferente:


Fig. 4. Representação do Tick de um pico e a execução da ordem Buy Stop

Neste gráfico de tick, podemos verificar que um dos ticks está localizado muito longe dos outros, obtendo a forma de um pico. Este tick desencadeia a nossa ordem Buy Stop no nível 64 440. No próximo tick, o preço retorna à sua variação atual, acionando a nossa ordem stop em 64 100. Em menos de um segundo, a nossa ordem pendente pode ser acionada e fechada por um stop loss, deixando-nos com uma enorme perda. Em vez da nossa perda estar calculada em 100 pontos, a nossa ordem perde 340 pontos.

Na realidade, os picos podem ser ainda maiores. Assim, um único pico gigante é o suficiente para arruinar a nossa conta, não importando o quão grande ela é! A fim de evitar tais desastres, é preciso respeitar as simples regras de proteção descritas abaixo.

Por favor note que no modo "Cada tick" do testador de estratégia, esses picos ocorrem a preços simulados, podendo serem melhores do que no mercado real. Se nós testamos nossa estratégia no intervalo de preço apresentado na figura, a nossa ordem pendente iria experimentar um desvio mínimo (se houver). Como sabemos, o fluxo de preço dentro de uma barra no testador estratégia é bastante contínuo, significando que o testador executa nossa ordem no preço próximo ao estabelecido, sem desvio. Estas situações podem ser adequadamente consideradas no testador estratégia. Para fazer isso, você deve selecionar um modo de teste especial, falaremos mais sobre o assunto na seção especial do Capítulo 3. 


1.3. Gerenciando o Desvio Máximo Usando as Ordens Limitadas

Descobrimos que o mercado e as ordens stops não têm nenhuma proteção contra um desvio de preço (slippage). Pode haver liquidez insuficiente para entrar o nosso pedido ou o mercado pode perder a sua liquidez por um breve tempo, causando picos de preços. Também esses pontos ocorrem com frequência nos mercados de baixa liquidez, como no mercado de derivativos FORTS. No entanto, você pode evitá-los usando ordens limitadas em vez de ordens a mercado ou stops.

A ordem limitada é sempre executada a um preço melhor do que o especificado. Uma característica interessante de ordens limitadas no modo de execução nas bolsas é a sua capacidade de ser executada a preços em tempo real, mesmo que seu nível esteja acima ou abaixo do preço da ordem especificada.

Por exemplo, se o preço atual do contrato futuro RUB/USD é de 64 200, podemos definir uma ordem Buy Limit em 64 220. Isso significa que estamos de acordo em comprar caso o preço não seja maior do que 64 220. Uma vez que o preço em tempo real de 64 200 é melhor do que o definido na ordem, nossa operação é executada imediatamente após ser colocada. Desta forma gerenciamos o valor máximo do desvio. Se por algum motivo não existe liquidez suficiente no nível 64 220, uma parte da nossa ordem simplesmente não será executada.

Observe que você pode gerenciar o desvio somente usando ordens limitadas. Ordens de mercado comuns no modo execução na bolsa não permitem que você defina o nível máximo do desvio. Portanto uma ordem limitada é a única maneira de manter a segurança da negociação nos mercados de baixa liquidez.

Logo é razoável usar ordens limitadas para as entradas e saídas do mercado. Você pode colocá-las, mesmo se sua estratégia requer entradas ou saídas a preços em tempo real no mercado. Substitua as ordens de Comprar e Vender por ordens Buy Limit e Sell Limit respectivamente. Por exemplo, se você for comprar a preços em tempo real, coloque uma ordem limitada com um preço máximo de execução ligeiramente maior do que o solicitado no mercado. O mesmo é verdadeiro para a venda. Nesse caso, coloque a sua ordem Sell limit com o nível de preço ligeiramente abaixo do solicitado. A diferença entre o preço definido numa ordem limitada e o solicitado em tempo real é um valor máximo de desvio (slippage) que você pode aceitar.

Vamos ver o seguinte exemplo: Suponha que compramos um grande volume contratos futuros de ED-3.15 EUR/USD no preço 1.1356. A liquidez que está em vigor no momento é baixa. Esta situação foi escolhida deliberadamente para mostrar os benefícios da entrada no mercado com ordens limitadas. Entramos no mercado coincidentemente com um pico de preço que pode ser visto no gráfico M1:

Fig. 5. Entrada no mercado no momento da ocorrência de um pico do preço, ED-3.15

Está bastante óbvio que o ponto de entrada no mercado é completamente desfavorável. Vamos analisar o gráfico de tick para esse momento:


Fig. 6. O gráfico de tick e a execução da ordem limitada durante a ruptura da liquidez

Execução da nossa ordem limitada é mostrada em grandes círculos brancos (ticks): . Os ticks são descritos como pontos azuis redondos. Se compramos a mercado no preço 1.1356, nossa ordem no mercado seria executado em diversas operações que começam a partir de 1.1356 e terminam no preço 1.1398. Isso poderia causar um forte desvio e nosso preço médio de entrada no mercado seria significativamente pior do que 1.1356. Quanto mais transações são necessárias para executar nossa ordem, pior o preço médio de entrada.

Neste caso, uma enorme diferença de preços é causada pela liquidez baixa quando os pedidos desaparecem por vários motivos e o preço oscila caoticamente sobre uma grande variação. Mas uma ordem limitada tem uma proteção embutida. Simplesmente não é executada se o preço em tempo real ultrapassa o nível 1.1356. Por exemplo, a nossa ordem limitada foi executada em sete transações - elas são mostrados em grandes círculos brancos no gráfico. Havia outros preços entre estas transações, mas todos eles eram piores do que 1.1356, então eles foram simplesmente ignorados. Depois de um tempo, o preço estabiliza e a nossa ordem foi finalmente executado integralmente.


1.4. Definição Manual de uma Ordem Limitada Gerenciando o Desvio Máximo

Agora, que nós enendemos o princípio da ativação de uma ordem limitada, é hora de colocarmos um pouco de prática e usar o nosso conhecimento em condições reais do mercado. Suponha que a nossa conta está conectada à Bolsa de Moscou. Vamos colocar uma ordem limitada no preço um pouco pior do que o atual. Também vamos escolher os contratos futuros EUR/USD (ED-6,15) como nosso ativo de trabalho. Chamamos uma janela para abertura da posição e definimos uma ordem Buy Limit um preço ligeiramente superior ao atual preço Ask:

Fig. 7. Colocamos uma ordem limitada manualmente no modo execução da bolsa

Fig. 7. Colocamos uma ordem limitada manualmente no modo execução da bolsa

Como podemos ver na imagem, o preço ASK em tempo real é 1.1242, então vamos definir um preço pendente no nível de 1.1245. A diferença entre o nosso preço e a melhor oferta é 0.0003 pontos (1.1245 - 1.1242 = 0,0003). Este valor é o máximo desvio (slippage) que estamos nos expondo. No modo execução da bolsa, essa ordem limitada é equivalente ao envio de uma ordem ordinária de Compra ou Venda com o desvio máximo:

Fig. 8. Executando a ordem a mercado com o desvio especificado

Fig. 8. Executando a ordem a mercado com o desvio especificado

Uma vez que um desvio máximo não está disponível no modo execução da bolsa, a única maneira de especificar esta operação é estabelecendo uma ordem limitada da maneira mostrada na Figura 7.


1.5. Definir o Desvio Máximo no Modo Execução da Bolsa Usando um Expert Advisor

Agora vamos colocar uma ordem limitada usando um programa. Para fazer isso, devemos codificar um painel simples que consiste nos seguintes elementos:

A imagem abaixo mostra a primeira versão do painel:

Fig. 9. Definindo o desvio máximo no DeviationPanel

O painel é construído como uma classe CDevPanel. Seu código fonte é o seguinte:

//+--------------------------------------------------------------------+
//|                                                       Panel.mqh    |
//|                                 Copyright 2015, Vasiliy Sokolov.   |
//|                                              http://www.mql5.com   |
//+--------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Trade\Trade.mqh>
#define OP_BUY 0
#define OP_SELL 1
//+--------------------------------------------------------------------+
//|                                                                    |
//+--------------------------------------------------------------------+
class CDevPanel
  {
private:
   CTrade            Trade;
   string            m_descr_dev;
   string            m_buy_button_name;
   string            m_sell_button_name;
   string            m_deviation_name;
   string            m_volume_name;
   string            m_bg_fon;
   int               m_deviation;
   void              OnObjClick(string sparam);
   void              OnEndEdit(string sparam);
   double            CalcCurrentPrice(int op_type);

public:
                     CDevPanel();
                    ~CDevPanel();
   void              OnChartEvent(const int id,
                                  const long &lparam,
                                  const double &dparam,
                                  const string &sparam);
  };
//+--------------------------------------------------------------------+
  //| Classe CDevPanel                                                 |
//+--------------------------------------------------------------------+
CDevPanel::CDevPanel(): m_buy_button_name("buy_button"),
                        m_sell_button_name("sell_button"),
                        m_deviation_name("deviation"),
                        m_volume_name("volume"),
                        m_bg_fon("bg_fon"),
                        m_descr_dev("descr_dev"),
                        m_deviation(3)
  {
//--- plano de fundo
   ObjectCreate(0,m_bg_fon,OBJ_RECTANGLE_LABEL,0,0,0);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_YSIZE,80);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_XSIZE,190);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_BGCOLOR,clrWhiteSmoke);

//--- botão buy
   ObjectCreate(0,m_buy_button_name,OBJ_BUTTON,0,0,0);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_XDISTANCE,100);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_YDISTANCE,50);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_BGCOLOR,clrAliceBlue);
   ObjectSetString(0,m_buy_button_name,OBJPROP_TEXT,"BUY");

//--- botão sell
   ObjectCreate(0,m_sell_button_name,OBJ_BUTTON,0,0,0);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_XDISTANCE,10);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_YDISTANCE,50);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_BGCOLOR,clrPink);
   ObjectSetString(0,m_sell_button_name,OBJPROP_TEXT,"SELL");

//--- desvio
   ObjectCreate(0,m_deviation_name,OBJ_EDIT,0,0,0);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_XDISTANCE,120);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_YDISTANCE,20);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_XSIZE,60);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_BGCOLOR,clrWhite);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_COLOR,clrBlack);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_ALIGN,ALIGN_RIGHT);
   ObjectSetString(0,m_deviation_name,OBJPROP_TEXT,(string)m_deviation);

//--- descrição
   ObjectCreate(0,m_descr_dev,OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_XDISTANCE,12);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_YDISTANCE,20);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_BGCOLOR,clrWhite);
   ObjectSetString(0,m_descr_dev,OBJPROP_TEXT,"Deviation (pips):");
   ObjectSetInteger(0,m_descr_dev,OBJPROP_COLOR,clrBlack);
   ChartRedraw();
  }
//+--------------------------------------------------------------------+
//|                                                                    |
//+--------------------------------------------------------------------+
CDevPanel::~CDevPanel(void)
  {
   ObjectDelete(0,m_buy_button_name);
   ObjectDelete(0,m_sell_button_name);
   ObjectDelete(0,m_bg_fon);
   ObjectDelete(0,m_deviation_name);
   ObjectDelete(0,m_descr_dev);
  }
//+--------------------------------------------------------------------+
//| Função do evento                                                   |
//+--------------------------------------------------------------------+
void CDevPanel::OnChartEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   switch(id)
     {
      case CHARTEVENT_OBJECT_CLICK:
         OnObjClick(sparam);
         break;
      case CHARTEVENT_OBJECT_ENDEDIT:
         OnEndEdit(sparam);
     }
  }
//+--------------------------------------------------------------------+
//| Detectar End Edit                                                  |
//+--------------------------------------------------------------------+
void CDevPanel::OnEndEdit(string sparam)
  {
   if(sparam != m_deviation_name)return;
   int value = (int)ObjectGetString(0, m_deviation_name, OBJPROP_TEXT);
   if(value <= 0)
      ObjectSetString(0,m_deviation_name,OBJPROP_TEXT,(string)m_deviation);
   else
      m_deviation=value;
   ChartRedraw();
  }
//+--------------------------------------------------------------------+
//| Finalizar Obj Click                                                |
//+--------------------------------------------------------------------+
void CDevPanel::OnObjClick(string sparam)
  {
   if(sparam==m_buy_button_name)
      Trade.BuyLimit(1,CalcCurrentPrice(OP_BUY));
   if(sparam==m_sell_button_name)
      Trade.SellLimit(1,CalcCurrentPrice(OP_SELL));
   ObjectSetInteger(0,sparam,OBJPROP_STATE,false);
   Sleep(100);
   ChartRedraw();
  }
//+--------------------------------------------------------------------+
//| Cálculo do nível de preço                                          |
//+--------------------------------------------------------------------+
double CDevPanel::CalcCurrentPrice(int op_type)
  {
   if(op_type==OP_BUY)
     {
      double ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      return ask + (m_deviation * Point());
     }
   else if(op_type==OP_SELL)
     {
      double bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
      return bid - (m_deviation * Point());
     }
   return 0.0;
  }
//+--------------------------------------------------------------------+

O painel permite definir um desvio máximo em pontos nas operações com a execução de ordens a mercado. Entradas/saídas reais a mercado são realizadas utilizando ordens limitadas.

O painel somente funciona com corretoras que permitem execução de ordens a mercado. Caso contrário, o código emite uma mensgem de erro padrão ao entrar numa ordem limitada:

2015.04.15 14:08:39.709 Trades  '58406864': failed buy limit 0.10 EURUSD at 1.05927 [Invalid price]


1.6. Ordens Buy Stop Limit e Sell Stop Limit como uma Alternativa para as Ordens Stop Tipo Buy Stop e Sell Stop

Ordens Limitadas possibilitam uma defesa conveniente e natural contra os desvios. Mas algumas vezes se faz necessário o uso de ordens pendentes que devem acionar o momento onde um determinado nível está sendo rompido. A ordem tipo stop loss é o exemplo mais óbvio. Também algumas estratégias devem responder ao preço deixando um determinado canal, onde precisam de ordens stops para entrar no mercado. No entanto, já sabemos que ordens stops estão sujeitas a desvios e não estão protegidas contra problemas de liquidez. Além disso, não é possível definir um valor máximo de desvio para elas.

Neste caso, devemos usar as ordens Buy Stop Limit e Sell Stop Limit. Estas são ordens algorítmicas para MetaTrader 5. Elas não são ordens a mercado, mas são colocadas como alternativas no servidor MetaTrader. Vamos examinar a documentação oficial:

  • Buy Stop Limit - este tipo combina com os dois primeiros, [Buy Limit e Buy Stop] , sendo uma ordem stop para colocar uma ordem limitada de compra (Buy Limit). Assim que o futuro preço Ask atinge o nível stop especificado na ordem (campo Preço), uma ordem Buy Limit será colocada no nível especificado no campo Preço Stop Limit.
  • Sell Stop Limit - este tipo é uma ordem stop para colocar uma ordem limitada de venda (Sell Limit). Assim que o futuro preço Bid atinge o nível stop especificado na ordem (campo Preço), uma ordem Sell Limit será colocada no nível especificado no campo Preço Stop Limit.

A documentação também fornece as imagens (Fig. 10) descrevendo o princípio de funcionamento das ordens no terminal MetaTrader 5. O quadro amarelo marca os dois tipos de ordens que nós estamos interessados:


Fig. 10. Tipos de ordens no terminal MetaTrader 5

Assim, estas ordens são colocadas no mercado quando o preço atinge um determinado nível stop. Para uma ordem Buy Limit Stop, um nível stop é colocado acima do atual nível Ask, enquanto que para a Sell Stop Limit, é colocado abaixo do atual nível Bid. Um preço de ordem limitada no modo execução pode ser acima ou abaixo do preço stop dessas ordens. Esse recurso permite configurar as ordens stop especiais com gestão de desvio. A imagem abaixo mostra como funciona:

Fig. 11. Definindo um desvio máximo, colocando uma ordem Buy Stop Limit

Podemos colocar uma ordem Buy Stop Limit tendo um Preço Limitado superior a um preço Stop. Assim que o Preço Stop é atingido, uma ordem Buy Limit é enviada e executada imediatamente, uma vez que o Preço Limitado é pior do que o atual Preço Stop. A diferença entre o Preço Stop e o Preço Limitado forma um desvio máximo que decidimos criar para as nossas ordens. Ordens Sell Stop Limit funcionam da maneira similar, porém com um Preço Limitado inferior ao Preço Stop.

Agora, vamos voltar a prática e colocar uma ordem Buy Stop Limit manualmente.


1.7. Definição Manual das Ordens Buy Stop Limit e Sell Stop Limit em vez dos Stop Loss

Consideremos a proteção da nossa posição aberta com uma ordem de stop, porém os mercados de baixa liquidez são muito perigosos e imprevisíveis para usar ordens stops ou a mercado. Uma ordem stop (por exemplo, um stop loss) não tem nenhuma proteção contra um desvio ilimitado. Assim, as grandes diferenças de preços ou picos podem arruinar completamente a nossa conta. Para evitar isso, nós devemos substituir uma ordem stop com aquela ordem stop limitada.

Vamos ver o seguinte exemplo: Considerando que temos uma posição comprada em Si-6.15. O stop loss é no preço 56 960. Devemos definir um desvio máximo de cinco pontos, de modo que o preço Stop Limit seja 56 960 - 5 = 56 955 pontos.

Fig. 12. Colocando uma ordem SellStopLimit como um nível stop para uma posição comprada

Fig. 12. Colocando uma ordem SellStopLimit como um nível stop para uma posição comprada

Como podemos ver, tal configuração da ordem Sell Stop Limit torna-se possível no modo execução da bolsa. Quando o preço atual chega a 56 960, uma ordem limitada de venda é colocada a 56 955. Uma vez que o preço atual de 56 960 é melhor do que o especificado na ordem limitada, ela é imediatamente executada a 56 960. Se não houver liquidez suficiente nesse nível, a execução seria realizada a preços subsequentes até 56 955. A ordem limitada não é executada a preços piores do que 56 955, garantindo assim o desvio máximo de cinco pontos: 56 960-56 955 = 5.

Agora vamos proteger a nossa posição vendida da mesma forma. Para fechar uma posição vendida pelo stop loss, precisamos executar uma operação oposta - comprar usando uma ordem Buy Stop Limit. Considerando que o stop loss atual de nossa posição vendida é 56 920, então devemos utilizar a seguinte configuração: ordem Buy Stop Limit com desvio máximo de cinco pontos.

Fig. 13. Colocando uma ordem BuyStopLimit como um nível stop para uma posição vendida

Fig. 13. Colocando uma ordem BuyStopLimit como um nível stop para uma posição vendida

Desta vez, o campo do preço Stop Limit excede ao preço de cinco pontos e definido pelo valor 56 925.


1.8. Substituindo os Níveis de Stop Loss com as Ordens Buy Stop Limit e Sell Stop Limit no Expert Advisor

Vamos voltar ao nosso painel descrito na seção 1.5, nós devemos modificá-lo para ele permitir a colocação de stops de proteção usando as ordens Buy Stop Limit e Sell Stop Limit. Para fazer isso, vamos adicionar um novo campo chamado Stop-Loss. Agora, o nosso painel é o seguinte:

Fig. 14. Colocando um nível de stop loss no DevaitionPanel

Há duas mudanças significativas no código: a classe CDevPanel agora apresenta um novo método responsável pela colocação das ordens Buy Stop Limit e Sell Stop Limit, também o método OnObjClick para a abertura de uma nova posição foi modificado. O código fonte dos métodos é o seguinte:

//+--------------------------------------------------------------------+
//| Finalizar Obj Click                                                |
//+--------------------------------------------------------------------+
void CDevPanel::OnObjClick(string sparam)
  {
   if(sparam==m_buy_button_name)
     {
      if(Trade.BuyLimit(1,CalcCurrentPrice(OP_BUY)))
         SendStopLoss(OP_BUY);
     }
   if(sparam==m_sell_button_name)
     {
      if(Trade.SellLimit(1,CalcCurrentPrice(OP_SELL)))
         SendStopLoss(OP_SELL);
     }
   ObjectSetInteger(0,sparam,OBJPROP_STATE,false);
   Sleep(100);
   ChartRedraw();
  }
//+--------------------------------------------------------------------+
//| Enviar Ordem de SL                                                 |
//+--------------------------------------------------------------------+
bool CDevPanel::SendStopLoss(int op_type)
  {
   if(op_type==OP_BUY)
     {
      double bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
      if(m_sl_level>=0.0 && m_sl_level<bid)
        {
         MqlTradeRequest request={0};
         request.action = TRADE_ACTION_PENDING;
         request.symbol = Symbol();
         request.volume = 1.0;
         request.price=m_sl_level;
         request.stoplimit=m_sl_level -(m_deviation*Point());
         request.type=ORDER_TYPE_SELL_STOP_LIMIT;
         request.type_filling=ORDER_FILLING_RETURN;
         request.type_time=ORDER_TIME_DAY;
         MqlTradeResult result;
         bool res=OrderSend(request,result);
         if(!res)
            Print("Error set S/L. Reason: "+(string)GetLastError());
         return res;
        }
     }
   else if(op_type==OP_SELL)
     {
      double ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      if(m_sl_level>=0.0 && m_sl_level>ask)
        {
         MqlTradeRequest request={0};
         request.action = TRADE_ACTION_PENDING;
         request.symbol = Symbol();
         request.volume = 1.0;
         request.price=m_sl_level;
         request.stoplimit=m_sl_level+(m_deviation*Point());
         request.type=ORDER_TYPE_BUY_STOP_LIMIT;
         request.type_filling=ORDER_FILLING_RETURN;
         request.type_time=ORDER_TIME_DAY;
         MqlTradeResult result;
         bool res=OrderSend(request,result);
         if(!res)
            Print("Error set S/L. Reason: "+(string)GetLastError());
         return res;
        }
      if(CharToStr(StringGetChar(data,strlen-1))=='.')
         StringSetChar(data,strlen-1,'');
     }
   return false;
  }

Além desses métodos, o código da classe painel agora inclui a inicialização e um campo importante para colocar o stop loss. Agora, se preencher o campo Stop-Loss antes de clicar em COMPRAR ou VENDER, uma nova ordem de mercado é acompanhada com proteção especial de uma ordem Buy Stop Limit ou Sell Stop Limit (dependendo da direção da posição).


Cápitulo 2. Análise da Liquidez do Mercado


2.1 Cálculo do Desvio Antes de Entrar no Mercado

Um mercado de ações é caracterizado por negociações centralizadas. Assim, todas as ordens limitadas de compra/venda estão disponíveis a partir do Livro de Ofertas (Profundidade do Mercado). Se voltarmos às definições descritas no artigo "Princípios da Precificação da Bolsa Tomando Como Exemplo o Mercado de Derivativos da Bolsa de Moscou", veremos que as ordens limitadas localizadas no Livro de Ofertas mostram-nos a liquidez do mercado (a capacidade de comprar e vender de certos volumes próximos ao Último preço da oferta).

Quanto maior o volume que desejamos vender ou comprar, mais ordens no Livro de Ofertas são colocadas, aumentando o desvio, dessa forma temos de atrair fornecedores de liquidez a partir de níveis cada vez mais remotos em relação ao preço atual. Você pode encontrar mais sobre como funciona os desvios de preço no artigo "Princípios da Precificação da Bolsa" acima mencionado. Vamos considerar o seguinte exemplo para tornar a questão mais clara.

A qualquer momento específico, nós temos o Livro de Ofertas descrevendo os volumes de compra/venda. Vamos analisar o Livro de Ofertas dos contratos futuros Si-6.15 USD/RUB:


Fig. 15. O Livro de Ofertas nos contratos futuros Si-6.15

Se nós compramos 2 contratos, não sofremos nenhum desvio com a oferta sendo realizada no melhor preço Ask: 51 931. Mas se compramos 4 contratos, o nosso preço médio será diferente de 51 931, compreendendo: (2*51 931+2*51 932)/4 = 51 931,5. Nós compramos dois contratos a 51 931 e os dois restantes a 51 932, ou seja, temos uma média ponderada do preço de entrada no mercado no valor 51 931,5. A diferença entre este valor e os melhores preços Ask formam nosso valor de desvio.

Agora, podemos organizar a tabela de liquidez que define o valor de desvio de acordo com o nosso volume da oferta. No volume de 1 ou 2 contratos, a nossa oferta é executada com o melhor preço Ask (51 931), sem desvio. No caso de 4 contratos, o desvio é de 0,5 pontos (51 931,5 - 51 931,0). A equação é simples: o melhor preço Ask ou Bid (dependendo da direção da operaçao) é subtraído da média ponderada do preço de entrada .

A tabela de liquidez é mostrado abaixo:

Volume Preço Oferta
volume
Média ponderada
preço de entrada
Desvio
2  51 938 25 51 934.5 3.5
9  51 936  23 51 934.2 3.2
3  51 935  14 51 933.0 2.0
7  51 933  11 51 932.5 1.5
2  51 932  4 51 931.5 0.5
2  51 931  2 51 931.0 0.0

Tabela 1. Cálculando a média ponderada do preço de entrada e o desvio apropriado

Esta tabela deve ser examinada de baixo para cima, similar ao preço Ask do Livro de Ofertas. Como podemos ver, o volume de ofertas de dois contratos não tem qualquer desvio. O volume de ofertas de quatro contratos tem um desvio de 0,5 pontos. O volume de negócio de 25 contratos tem um desvio de 3,5 pontos e seu preço médio ponderado é 51934,5.

O mercado centralizado e o Livro de Ofertas nos permitem fazer a seguinte conclusão:

Sabendo o estado do Livro de Ofertas, podemos calcular o potencial desvio antes de conduzir uma operação.

Assim, podemos gerenciar nosso risco. Independentemente de negociação manual ou usando robôs, podemos definir a profundidade do mercado pelo Livro de Ofertas antes de entrar nele. Neste caso, podemos comparar um trader a um mergulhador na piscina. Antes de saltar para a água, o mergulhador deve saber a profundidade da piscina. Quanto mais alto o mergulhador, mais profunda a piscina deve ser. De igual modo, quanto maior o volume de negócio, maior liquidez deve ter o mercado. É claro que a profundidade do mercado, verificada no Livro de Ofertas, pode mudar um pouco antes de entrar no mercado, mas mesmo um cálculo ligeiramente ultrapassado permanece preciso o suficiente para realizar uma operação.


2.2. Cálculo de Desvio Potencial em Tempo Real

Agora, é hora de colocar a teoria em prática. É impossível calcular manualmente o valor do potencial desvio, uma vez que os valores do Livro de Ofertas muda muito rapidamente, enquanto que o próprio cálculo é muito complicado. Assim, precisamos automatizar. A fim de facilitar o cálculo, nós implementamos uma classe especial, a CMarketBook, para trabalhar com o Livro de Ofertas. O desenvolvimento desta classe é uma tarefa difícil, digna de um artigo separado. Não há necessidade de descrever os seus princípios de operação aqui. Em vez disso, vamos usar um de seus métodos, o GetDeviationByVol. Vamos ver como ele funciona:

//+--------------------------------------------------------------------+
//| Obter valor de desvio pelo volume. Retorna -1.0 se o desvio for    |
//| infinito (liquidez insuficiente)                                   |
//+--------------------------------------------------------------------+
double CMarketBook::GetDeviationByVol(long vol,ENUM_MBOOK_SIDE side)
  {
   int best_ask = InfoGetInteger(MBOOK_BEST_ASK_INDEX);
   int last_ask = InfoGetInteger(MBOOK_LAST_ASK_INDEX);
   int best_bid = InfoGetInteger(MBOOK_BEST_BID_INDEX);
   int last_bid = InfoGetInteger(MBOOK_LAST_BID_INDEX);
   double avrg_price=0.0;
   long volume_exe=vol;
   if(side==MBOOK_ASK)
     {
      for(int i=best_ask; i>=last_ask; i--)
        {
         long currVol=MarketBook[i].volume<volume_exe ?
                      MarketBook[i].volume : volume_exe;
         avrg_price += currVol * MarketBook[i].price;
         volume_exe -= MarketBook[i].volume;
         if(volume_exe<=0)break;
        }
     }
   else
     {
      for(int i=best_bid; i<=last_bid; i++)
        {
         long currVol=MarketBook[i].volume<volume_exe ?
                      MarketBook[i].volume : volume_exe;
         avrg_price += currVol * MarketBook[i].price;
         volume_exe -= MarketBook[i].volume;
         if(volume_exe<=0)break;
        }
     }
   if(volume_exe>0)
      return -1.0;
   avrg_price/=(double)vol;
   double deviation=0.0;
   if(side==MBOOK_ASK)
      deviation=avrg_price-MarketBook[best_ask].price;
   else
      deviation=MarketBook[best_bid].price-avrg_price;
   return deviation;
  }

Quando o método é chamado, refere-se ao Livro de Ofertas (Profundidade Do Mercado). No Livro de Ofertas, a partir do melhor preço, calcula o volume disponível. Assim que o volume disponível é igual ou excede ao requerido, o método finaliza a busca e calcula um preço médio ponderado correspondente a um volume previamente determinado. A diferença entre o preço médio ponderado e a melhor oferta, forma o nosso potencial valor de desvio.

Se por alguma razão, a profundidade de liquidez do mercado (no Livro de Ofertas) for insuficiente ao volume especificado, o método retorna -1.0, indicando que o potencial desvio não pode ser calculado.

Agora que temos o método de cálculo do potencial desvio, precisamos visualizar os resultados obtidos. Obviamente, o valor de desvio correlaciona-se com o volume comprado ou vendido no mercado. Quanto maior for o volume, maior é o desvio. Assim, precisamos adicionar uma nova linha e um campo de entrada no nosso painel, oVolume:

Fig. 16. O painel com o campo Volume

Agora, o nosso painel poderá comprar e vender um volume arbitrário. Por exemplo, se queremos comprar 5 contratos no preço a mercado, devemos simplesmente digitar 5 no campo de volume e clicar em BUY. Esta não é a única inovação, como já mencionado, gerenciamos um desvio ao entrar num mercado graças ao método GetDeviationVol.

Para obter mais visibilidade, vamos apresentar o valor calculado diretamente sobre os botões BUY e SELL. Vamos especificar o nosso desvio em pontos. Este valor será calculado de novo em cada alteração no Livro de Ofertas, aumentando a liquidez, o valor de desvio cai e vice-versa. Se queremos comprar ou vender apenas um contrato, não haverá desvio, uma vez que o volume de 1 nunca excede o melhor volume do preço Bid/Ask.

Eu recomendo que você visualize a atualização do painel de operação em tempo real. O vídeo abaixo mostra o cálculo do potencial desvio em tempo real para o contrato do mercado futuro RTS-6.15 :



No início, um contrato é inserido no campo Volume. Como esperado, os botões BUY e SELL exibem 0. Isto significa que a nossa entrada no mercado não irá sofrer um desvio. Aumentado o volume para 100 contratos, o desvio médio para compra e venda é aumentado em até 10-20 pontos. Quando o volume é aumentado em até 500 contratos, o desvio médio fica entre 60-80 pontos. Finalmente, depois de definir o volume para 1 500 contratos, experimentamos uma liquidez insuficiente exibido pelo valor -1,0 no botão BUY (o desvio não pode ser definido). A liquidez da demanda ainda é suficiente, embora a venda de uma grande quantidade de tais contratos terá um desvio de 100-130 pontos.

A classe para trabalhar com o Livro de Ofertas, bem como a versão final do DeviationPanel estão disponíveis nos códigos fonte anexados neste artigo.


2.3. Usando o Indicador SpreadRecord como um Filtro de Entrada no Mercado

Fazer uma análise da liquidez em andamento antes de entrar no mercado é um hábito útil e razoável. Um robô de negociação bem desenvolvido, realiza cálculos complexos que pode salvá-lo de um desvio perigoso, mas isso nem sempre é suficiente.

Outra questão que o trader tem de ficar atento é com um spread confiável com tamanho determinado. Spread é a diferença entre os melhores preços Ask e Bid. Funciona principalmente como um parâmetro relativo para ofertas de grande volume, que são mais afetadas pela liquidez comum do Livro de Oferta, ao invés do tamanho em si do spread. Porém um trader normalmente não tem acesso ao histórico do Livro de Ofertas no Mercado, por isso é muito difícil avaliar o passado da liquidez de um contrato de negociação. Por outro lado, o spread está inversamente correlacionado com liquidez do ativo. Quanto menor for o spread, maior é a liquidez, e vice-versa.

Com esta funcionalidade em mente, podemos desenvolver um indicador de spread para exibir o histórico dos valores. Este indicador será extremamente útil na negociação, uma vez que nos permite avaliar visualmente os valores históricos da liquidez e o tamanho do spread de um ativo. Sabendo o valor médio spread, quando um spread expande significativamente, podemos limitar a nossa negociação durante esta mudança acentuada da liquidez.

Então vamos criar um indicador desse tipo. Ele irá exibir seus valores como barras na sub janela do gráfico. O nível médio do valor do spread é exibido como um ponto verde no nível apropriado dentro das barras. O indicador calcula os seguintes valores do spread:

O indicador não desenha e salva os valores do spread na última barra após o terminal ser reiniciado. O código fonte do indicador é descrito abaixo:

//+--------------------------------------------------------------------+
//|                                                Spread Record.mq4   |
//|                        Copyright 2015, MetaQuotes Software Corp.   |
//|                                              http://www.mql5.com   |
//+--------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com/ru/users/c-4"
#property version   "1.00"
#property description "Recording spread and show it."
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   5
#property indicator_type1   DRAW_BARS
#property indicator_type2   DRAW_ARROW
#property indicator_color1   clrBlack
#property indicator_color2   clrBlack
double spread_open[];
double spread_high[];
double spread_low[];
double spread_close[];
double spread_avrg[];
int elements;
double avrg_current;
int count;
//+--------------------------------------------------------------------+
//| Função de Inicialização do Indicador personalizado                 |
//+--------------------------------------------------------------------+
int OnInit()
  {
//--- mapeamento de buffers do indicador 
   SetIndexBuffer(0,spread_open,INDICATOR_DATA);
   SetIndexBuffer(1,spread_high,INDICATOR_DATA);
   SetIndexBuffer(2,spread_low,INDICATOR_DATA);
   SetIndexBuffer(3,spread_close,INDICATOR_DATA);
   SetIndexBuffer(4,spread_avrg,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,1);
   PlotIndexSetInteger(1,PLOT_ARROW,0x9f);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrRed);
   PlotIndexSetInteger(1,PLOT_LINE_COLOR,clrGreen);
//---
   return(INIT_SUCCEEDED);
  }
//+--------------------------------------------------------------------+
//|                                                                    |
//+--------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   printf("DEINIT");
  }
//+--------------------------------------------------------------------+
//| Função de iteração do indicador personalizado                      |
//+--------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(prev_calculated==0)
     {
      printf("INITIALIZE INDICATORS "+TimeToString(TimeCurrent()));
      double init_value=EMPTY_VALUE;
      ArrayInitialize(spread_high,init_value);
      ArrayInitialize(spread_low,init_value);
      ArrayInitialize(spread_open,init_value);
      ArrayInitialize(spread_close,init_value);
      ArrayInitialize(spread_avrg,init_value);
      elements=ArraySize(spread_high);
      InitNewBar(elements-1);
     }
//--- nova barra de inicialização
   for(; elements<ArraySize(spread_high); elements++)
      InitNewBar(elements);
   double d=GetSpread();
   for(int i=rates_total-1; i<rates_total; i++)
     {
      if(d>spread_high[i])
         spread_high[i]=d;
      if(d<spread_low[i])
         spread_low[i]= d;
      spread_close[i] = d;
      avrg_current+=d;
      count++;
      spread_avrg[i]=avrg_current/count;
     }
//--- valor de retorno do prev_calculated para próxima chamada
   return(rates_total-1);
  }
//+--------------------------------------------------------------------+
//|                                                                    |
//+--------------------------------------------------------------------+
double GetSpread()
  {
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   return NormalizeDouble((ask-bid)/Point(), 0);
  }
//+--------------------------------------------------------------------+
//| Inicialização nova barra                                           |
//+--------------------------------------------------------------------+
void InitNewBar(int index)
  {
   spread_open[index] = GetSpread();
   spread_high[index] = 0.0;
   spread_low[index]=DBL_MAX;
   avrg_current=0.0;
   count=0;
  }

Vamos executar este indicador no gráfico M1 Si-6,15. Pouco depois do início, ele mostra os seguintes resultados:

Fig. 17. Indicador SpreadRecord lançado no gráfico M1 Si-6,15

Podemos ver que o spread oscilou entre 1 e 21 pontos no período analisado. Em cada minuto, houve pelo menos um momento em que o spread correspondia ao valor mínimo de 1 ponto. O valor médio compreendido foi de 3 pontos, como foi mencionado, ele é mostrado como um ponto verde na janela do indicador.


2.4. Negociação Manual e Automática Limitadas Durante a Expansão de Spreads Fortes

Agora temos de aprender a usar o indicador para gerenciar nossos riscos. A coisa mais simples que podemos fazer é limitar a nossa atividade de negociação quando os valores do indicador são elevados demais. No período de tempo selecionado, os valores de indicador variaram principalmente entre 1 a 9 pontos. Esta área pode ser chamado de "verde". A negociação neste intervalo é habilitada. Se o spread sobe acima de 9 pontos, nós vamos para zona vermelha, então a negociação neste intervalo é desabilitada. Isto pode ser demonstrado da seguinte maneira:


Fig. 18. Zonas de Negociação habilitadas e desabilitadas definidas pelo indicador

Além de limitarmos a negociação manual, também precisamos ensinar nosso Expert Advisor a aceitar os valores do indicador e limitar suas ações de negociação quando o spread em andamento ultrapassa os limites especificados. Você pode fazer isso chamando o indicador a partir do EA, utilizando a função iCustom. Esta função permite que você chame qualquer indicador diretamente do EA, obtendo os seus valores. Abaixo está o modelo do EA para gerenciar o spread usando o indicador:

//+--------------------------------------------------------------------+
//|                                          SpreadRecordControl.mq5   |
//|                        Copyright 2015, MetaQuotes Software Corp.   |
//|                                              http://www.mql5.com   |
//+--------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#define OPEN  0
#define HIGH  1
#define LOW   2
#define CLOSE 3
#define AVRG  4

input int MaxSpread=9;

int h_spread_record=INVALID_HANDLE;       // Handle do indicador SpreadRecord
bool print_disable = false;
//+--------------------------------------------------------------------+
//| Função de inicialização do Expert                                  |
//+--------------------------------------------------------------------+
int OnInit()
  {
   h_spread_record=iCustom(Symbol(),Period(),"Spread Record");
   return(INIT_SUCCEEDED);
  }
//+--------------------------------------------------------------------+
//| Função de desinicialização do Expert                               |
//+--------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(h_spread_record);
  }
//+--------------------------------------------------------------------+
//| Função tick do Expert                                              |
//+--------------------------------------------------------------------+
void OnTick()
  {
   if(IsTradeDisable(MaxSpread))return;
   //
   // LÓGICA DA NEGOCIAÇÃO...
   //
  }
//+--------------------------------------------------------------------+
//| Retorna verdadeiro se desativar a negociação, se não retorna falso |
//+--------------------------------------------------------------------+
bool IsTradeDisable(int max_spread)
  {
   if(h_spread_record==INVALID_HANDLE)
      return false;
   double close[];
   if(CopyBuffer(h_spread_record, CLOSE, 0, 1, close) < 1)return false;
   if(close[0]>MaxSpread)
     {
      if(!print_disable)
         printf("trade disable");
      print_disable=true;
      return true;
     }
   if(print_disable)
      printf("trade enable");
   print_disable=false;
   return false;
  }

A função IsTradeDisable é responsável principalmente para definir se a negociação está habilitada ou não. Ela retorna verdadeiro se o spread é muito alto e a negociação deve ser desativada. Se o spread é normal, a função retorna falso. A função é baseada na chamada do indicador SpreadRecord ao copiar o seu valor atual, utilizando a função CopyBuffer. O EA apresenta o parâmetro MaxSpread como o valor limiar. Se o valor for ultrapassado, o EA bloqueia a sua atividade de negociação. Se o spread ficar abaixo do limiar especificado, o EA retoma o seu trabalho. A função IsTradeDisable indica a transição de um estado para outro, retornará uma mensagem correspondente: "habilitar negociação" (trade enable) e "desabilitar negociação" (trade disable):

2015.05.27 16:57:08.238 SpreadRecordControl (Si-6.15,H1)        trade enable
2015.05.27 16:57:08.218 SpreadRecordControl (Si-6.15,H1)        trade disable
2015.05.27 16:56:49.411 SpreadRecordControl (Si-6.15,H1)        trade enable
2015.05.27 16:56:49.401 SpreadRecordControl (Si-6.15,H1)        trade disable
2015.05.27 16:56:36.478 SpreadRecordControl (Si-6.15,H1)        trade enable
2015.05.27 16:56:36.452 SpreadRecordControl (Si-6.15,H1)        trade disable

Você pode usar este protótipo de EA nos seus sistemas de negociação, evitando assim uma entrada no mercado durante a baixa liquidez e grande desvio.

Os códigos fonte do Expert Advisor e do indicador SpreadRecord estão anexados neste artigo.


Capítulo 3. Negociação Protegida e Modos de Testes do Expert Advisor


3.1. Usando o "Modo Sleep" como uma alternativa de controle ao Tick a Tick

Tal como mencionado na seção 1.1 "Natureza discreta de um Fluxo de Preço. Lacunas de Preço ", a cotação do mercado pode ser comparado a um fluxo contínuo de preços. Assim, se o preço das ações muda de $10 para $15, isso significa que houve momentos onde a composição do preço foi respectivamente 11, 12, 13 e 14 dólares. No entanto, já descobrimos que nem sempre é o caso.

Os preços movem-se frequentemente e acentuadamente, enquanto os nossos métodos de negociação são comumente baseados na suposição de que as cotações mudam gradualmente e de forma consistente. Quando definimos um stop loss, assumimos que a nossa posição será fechada num nível antes de sofrer uma perda devastadora, porém a base de qualquer ordem stop é uma permissão para comprar ou vender a qualquer preço disponível num determinado nível. Em caso de preços distintos, uma ordem stop se torna uma potencial máquina de perda. Se o preço atual é várias vezes pior do que o especificado na nossa ordem stop, um stop loss é executado, porém deixando-nos com uma perda muito maior.

Por outro lado, se um Expert Advisor verifica a situação do mercado a cada tick de entrada, também corre o risco de fechar uma posição a um preço muito desfavorável: no caso de baixa liquidez, o último negócio gerando um tick pode ser executado em partes, atingindo a maioria dos preços ao longo do seu caminho e causando um pico de preços.

Portanto, em vez de rastrear cada tick do mercado, é mais razoável "dessensibilizar" a estratégia, de modo que a lógica do negociação de um EA seja executada por um período determinado de tempo (por exemplo, uma vez por minuto) e não por cada tick. Uso de ordens stop está fora de questão também. Em vez disso, seria mais sábio usar um algorítmo (virtual) com as suas condições de ativação verificadas uma vez por um determinado período de tempo.

Pode parecer que essa dessensibilização lógica de negociação irá distorcer significativamente os resultados das operações, mas não é o caso. Claro, o preço pode mover um longo caminho a partir de uma saída em potencial do mercado ou numa entrada dentro de um minuto, mas também haverá condições mais favoráveis quando as operações são realizadas longe dos pontos de reversão dos preços.

Vamos considerar um caso real no mercado observado nos contratos futuros do Si-6.15 RUB/USD em 28 de maio. Um aumento considerável de preço ocorreu às 10:03 (horário de Moscou). Considerando que nós tivéssemos uma posição comprada em 53 040 e stop loss de 52 740 (300 pontos), neste horário, então o nosso stop loss teria sido acionado no preço muito menor do que o especificado no nível do stop.

Como mostra a prática, níveis de stop são geralmente desencadeados próximos dos piores preços durante os picos de preços. Nesse caso, teria sido no nível 52 493, nos trazendo uma perda de 53 040-52 493 = 547 rublos por contrato (em vez de 300 rublos especificados no nosso nível stop). O caso é exibido abaixo, no gráfico A. Se verificássemos o nosso stop loss uma vez por minuto, o pico de preço teria sido ignorado pela nossa estratégia, o stop loss não teria sido acionado e, finalmente, nós teriamos terminado a nosssa operação no lucro (quadro B):

Fig. 19. A estratégia se comporta de maneira diferente, dependendo se é usada sobre uma ordem stop real ou virtual

Fig. 19. A estratégia se comporta de maneira diferente, dependendo se é usada sobre uma ordem stop real ou virtual

O pico de preço mostrado aqui é relativamente pequeno. No entanto, algumas vezes pode até chegar no limite do preço de um contrato futuro. Limites de preços geralmente são colocados a uma distância de 5% do preço atual. Assim, se usarmos uma alavancagem de 1:1, corremos o risco de perder 5% do nosso depósito durante a execução de um nível stop. Se usarmos a alavancagem de 1:10, as perdas compreenderão 50% do nosso depósito!


3.2. Amostra de Expert Advisor Baseado em Médias Móveis e Verificando a Sua Lógica de Negociação Por Período

Um Expert Advisor operando no cruzamento de duas médias móveis é um bom exemplo de como você pode fazer um EA para verificar as condições do mercado por período de tempo. O último valor do preço de uma média móvel (MA) está constantemente sendo alterado no fechamento da última barra.

A estratégia clássica para duas MAs é bem conhecida por muitos traders. O EA compra quando uma MA rápida cruza para cima uma MA lenta e vende quando uma MA rápida cruza para baixo uma MA lenta. A imagem seguinte nos mostra os sinais de entrada da estratégia para compra e venda no mercado:

Fig. 20. Os sinais de entrada da estratégia MovingAverage para compra e venda no mercado

Fig. 20. Os sinais de entrada da estratégia MovingAverage para compra e venda no mercado

Como já mencionado, as MAs estão mudando constantemente na última barra . Neste caso, uma MA rápida pode cruzar uma lenta várias vezes dentro de uma única barra, fazendo um EA executar múltiplas reversões, enquanto o preço está quase parado. Também já sabemos que não é razoável verificar as condições de cada tick recém-chegados nos mercados futuros. Assim, devemos fazer um EA para verificar as condições de negociação uma vez por minuto. Nesse caso, o EA verifica a barra anterior (já concluída), em vez da barra atual, de modo que o redesenho da MA na última barra não afeta o comportamento do EA.

O código do EA MovingAverage é mostrado abaixo:

//+--------------------------------------------------------------------+
//|                                                MovingAverage.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 <Trade\Trade.mqh>

input int FastMAPeriod = 10;     // MA periodo rápido
input int SlowMAPeriod = 20;     // MA periodo Lento 
input double Volume = 1.0;       // Volume para Negociação
int FastMA = INVALID_HANDLE;     // Handle do indicador MA rápida.
int SlowMA = INVALID_HANDLE;     // Handle do indicador MA lenta.
datetime TimeLastBar;
CTrade Trade;
//+--------------------------------------------------------------------+
//| Função de inicialização do Expert                                  |
//+--------------------------------------------------------------------+
int OnInit()
  {
   FastMA = iMA(Symbol(), Period(), FastMAPeriod, MODE_SMA, 1, PRICE_CLOSE);
   SlowMA = iMA(Symbol(), Period(), SlowMAPeriod, MODE_SMA, 1, PRICE_CLOSE);
   if(FastMA==POINTER_INVALID || SlowMA==POINTER_INVALID)
     {
      printf("handle of indicator has not been created");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+--------------------------------------------------------------------+
//| Função de desinicializaçao do Expert                               |
//+--------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(FastMA);
   IndicatorRelease(SlowMA);
  }
//+--------------------------------------------------------------------+
//| Função tick do Expert                                              |
//+--------------------------------------------------------------------+
void OnTick()
  {
   if(!NewBarDetect())return;
   if(CrossOver())
      Trade.Buy(GetVolume());
   else if(CrossUnder())
      Trade.Sell(GetVolume());
  }
//+--------------------------------------------------------------------+
//| Retorna verdadeiro se a MA rápida cruza com a MA lenta para baixo  |
//| Caso contrário, retorna falso.                                     |
//+--------------------------------------------------------------------+
bool CrossOver()
  {
   double fast_ma[];
   double slow_ma[];
   if(CopyBuffer(FastMA, 0, 1, 2, fast_ma) < 1)return false;
   if(CopyBuffer(SlowMA, 0, 1, 2, slow_ma) < 1)return false;
   bool is_over=fast_ma[1]>slow_ma[1] && fast_ma[0]<slow_ma[0];
   return is_over;
  }
//+--------------------------------------------------------------------+
//| Retorna verdadeiro se o MA rápida cruza sobre a MA lenta           |
//| Caso contrário, retorna falso.                                     |
//+--------------------------------------------------------------------+
bool CrossUnder()
  {
   double fast_ma[];
   double slow_ma[];
   if(CopyBuffer(FastMA, 0, 1, 2, fast_ma) < 1)return false;
   if(CopyBuffer(SlowMA, 0, 1, 2, slow_ma) < 1)return false;
   bool is_under=fast_ma[0]>slow_ma[0] && fast_ma[1]<slow_ma[1];
   return is_under;
  }
//+--------------------------------------------------------------------+
//| Retorno do volume de contagem para a negociação                    |
//+--------------------------------------------------------------------+
double GetVolume()
  {
   if(PositionSelect(Symbol()))return Volume*2.0;
   return Volume;
  }
//+--------------------------------------------------------------------+
//| Retorna verdadeiro detectando nova barra, do contrário será falso  |
//+--------------------------------------------------------------------+
bool NewBarDetect()
  {
   datetime times[];
   if(CopyTime(Symbol(),Period(),0,1,times)<1)
      return false;
   if(times[0] == TimeLastBar)return false;
   TimeLastBar = times[0];
   return true;
  }
//+--------------------------------------------------------------------+

 A principal característica do EA é a condição na verificação de chegada da nova barra:

void OnTick()
{
   if(!NewBarDetect())return;
   ...
}

A versão atual do EA não tem stop loss. Contudo, se fosse utilizado, uma saída de posição através do stop loss teria de ser feita após a abertura de uma nova barra e verificando o funcionamento, assim um Stop Loss seria desencadeado após o surgimento da nova barra.

Isso nos permitiria analisar as condições de mercado apenas quando uma nova barra abrisse, evitando assim possíveis picos de preços. Claro, a alta do preço pode ocorrer logo na barra recém aberta, mas é centenas de vezes menos provável em relação à verificação das condições de mercado a cada tick.


3.3. Usando o Modo Barra Completa Como Uma Alternativa ao Teste Tick a Tick do Expert Advisor

Finalmente, vamos considerar um dos mais interessantes modos de testes de EAs e indicadores disponíveis no testador de estratégia do MetaTrader 5. Este modo é chamado "Somente preços de abertura". Abrir o testador de estratégia (Veja --> Testador de Estratégia) e selecione-o na seção Execução, na janela do testador.

Fig. 21. Selecionar o modo "Somente preços de abertura"

Fig. 21. Selecionar o modo "Somente preços de abertura"

Os traders muitas vezes subestimam este modo, pois pensam que é muito impreciso, porém poucos EAs podem usar este modo eficientemente. No entanto poucas pessoas sabem que este é, de fato, o modo de teste mais preciso e rápido, especialmente quando comparado com o "Cada tick".

A alta precisão é conseguida apenas através das aberturas de preços. Todos os novos preços das barras tornam-se disponíveis somente quando elas estiverem completas, transformando assim a barra anterior no histórico da cotação.

Em contraste, o modo "Cada tick" forma cada barra num modo especial de conseguir dados a partir do menor timeframe disponível e usando o gerador próprio de tick. Desde que o terminal MetaTrader 5 não armazena o histórico de tick, o gerador não pode simular diferenças de preços dentro da barra de minuto. Portanto, é possível desenvolver uma "Graal" que mostra excelentes resultados no testador, mas falha no mercado real.

O modo de teste tick a tick é a maior ameaça para estratégias baseadas em níveis avançados. Além disso, as ordens pendentes também podem distorcer os resultados reais. Vamos considerar uma estratégia que coloca uma ordem pendente Buy Stop e aguarda um forte movimento ascendente. Às 19:00, 25 de maio de 2015 (logo após a câmara de compensação noturna), o contrato de futuros SBRF-6.15 move-se de 7 473 até 7 530 rublos durante um minuto. Se tivéssemos ordens pendentes em 7 485, teria sido aberta pelo seu preço especificado no testador estratégia, trazendo lucro após o seu encerramento algumas barras mais tarde:

Fig. 22. Ativação de ordem pendente

No entanto, a realidade pode ser muito diferente. Não sabemos nada sobre os preços dentro da barra de minuto. Em outras palavras, a ordem pode ser executada a um preço muito pior. O vídeo abaixo mostra como a ordem é executada no modo "Cada tick":


Como podemos ver, o testador de estratégia não tinha problemas durante o processamento do preço definido. Agora vamos olhar para o gráfico de tick do candle de minuto:

Fig. 23. Gráfico de tick do candle de minuto

O preço tem experimentado mudanças bastante dramáticas dentro desse minuto. O gráfico de tick apresenta grandes diferenças de preços e movimentos bruscos. Assim, a nossa ordem stop dificilmente teria sido executada ao preço desejado em condições reais do mercado. O preço de execução real seria provavelmente entre 7 510 e 7 520.


Analisando cada tick e usando os preços de mercado em vez de ordens pendentes não teria mudado nada. O gerador de tick do testador de estratégia gera ticks sequencialmente, a nossa ordem teria sido acionado tão logo o preço Ask tivesse tocado o nosso nível especificado. Na realidade, teria sido impossível cumprir a nossa ordem aos preços fixados.

Portanto, use o modo "Cada tick" com cautela. Você deve estar ciente o quanto sua estratégia é especialmente sensível aos picos de preços.

O modo de teste de barra completa é muito mais seguro. Não devemos usar ordens pendentes neste modo para garantir alta precisão. Se nossa estratégia requeresse a entrada no mercado ao preço 7 495, teria que verificar cada Abertura da barra de preço e esperar até que o nível necessário fosse ultrapassado para comprar ao preço real, assim que a barra fosse aberta. No modo barra completa, teriamos descoberto que o preço estaria acima do nível desejado somente quando uma nova barra fosse aberta às 19:01, pois a Abertura de preço às 19:00 ainda estava abaixo dos 7 495 rublos. Assim, a nossa negociação teria sido a seguinte no modo barra completa:

 

Fig. 24. Negociação real no modo barra completa

Embora o resultado final ainda seja negativo, ele tem uma enorme vantagem:

Testes com barras completas garante que todas as negociações sejam realizadas a preços reais. Logo este modo pode ser usado para testar estratégias em mercados de baixa liquidez.

Se a sua estratégia funciona num período de tempo maior do que M1 e não pode se dar ao luxo de verificar as condições de negociação por período, experimente o modo de teste "OHLC por minuto". Neste modo, cada barra é gerada somente com base nos preços do gráfico M1. Uma vez que todos os preços do gráfico M1 são históricos, este modo também tem uma precisão absoluta e pode ser recomendado como um modo de teste razoável para estratégias de médio prazo.

Eu não recomendo aplicar o modo "Cada tick" ao testar estratégias para mercados de baixa liquidez. Além disso, tais estratégias não devem usar ordens Stop nas suas atividades.

Você pode argumentar que a precisão de entrada no mercado é de importância crítica para os sistemas de negociação e até mesmo alguns pontos podem afetar significativamente os resultados finais. No entanto, se recorrer à lei dos grandes números, veremos que a diferença entre um cálculo e uma entrada real a mercado é um mero componente de ruído. Na maioria dos casos, a entrada no mercado é realizada a preços muito piores do que os calculados, embora às vezes as quebras extremas de preço através do nível calculado retornam na mesma barra.

Se usarmos o modo "Cada tick", teremos prejuízos durante esses momentos, mas se usarmos barras completas, não teremos entradas no mercado em tais condições. Em outras palavras, a entrada no mercado a preços piores seriam compensadas por outros efeitos (positivos). No geral, a diferença seria completamente eliminada, o resultado seria completamente dependente da ideia implementada numa estratégia, do que dependente de um nível de entrada momentâneo.


Conclusão

Agora é o momento de resumirmos as ideias principais: