English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Teste rápido das ideias de negociação no gráfico

Teste rápido das ideias de negociação no gráfico

MetaTrader 5Sistemas de negociação | 17 março 2014, 14:40
2 270 0
Vladimir Kustikov
Vladimir Kustikov

Introdução

O sexto Campeonato de Negociação Automatizada (ATC) finalmente começou. A agitação inicial já passou e finalmente podemos relaxar um pouco e examinar os robôs de negociação enviados. Decidi fazer uma pequena pesquisa para encontrar os recursos mais notáveis dos robôs de negociação modernos e definir o que podemos esperar de suas atividades de negociação.

Isso provou ser uma tarefa bem difícil. Por essa razão, meus cálculos não podem ser considerados perfeitamente exatos ou completos, visto que apenas tive acesso às descrições de Expert Advisors e a raros comentários dos desenvolvedores. Entretanto, ainda podemos chegar a algumas conclusões e abaixo estão os resultados dos meus cálculos: 451 Expert Advisors participaram do Campeonato, mas apenas 316 deles contêm alguma descrição significativa. Os desenvolvedores dos EAs restantes preencheram suas descrições com saudações a seus amigos e familiares, mensagens a civilizações extraterrestres ou aplausos para si próprios.

Estratégias mais populares no ATC 2012:

  • negociação utilizando diversas construções gráficas (níveis de preços importantes, níveis de suporte e resistência, canais) – 55;
  • análise de movimento de preço (para vários períodos de tempo) – 33;
  • sistemas de monitoramento de tendência (penso que essas palavras escondem alguma combinação excessivamente otimizada de médias móveis, mas posso estar enganado) :) ) – 31;
  • padrões de preços estatísticos – 10:
  • arbitragem, análise de correlação de símbolos – 8;
  • análise de volatilidade – 8;
  • redes neurais – 7;
  • análise de candlestick – 5;
  • averagers – 5;
  • pacotes de estratégias – 5;
  • tempo da sessão de negociação – 4;
  • gerador de número aleatório – 4;
  • negociando as novidades – 3,
  • Ondas de Elliott – 2.

É claro que as estratégias de indicador são tradicionalmente as mais populares. É difícil definir o papel de cada indicador específico em um Expert Advisor em particular, mas é possível estimar o número absoluto de seu uso:

  • Média Móvel – 75;
  • MACD – 54;
  • Oscilador Estocástico – 25;
  • RSI – 23;
  • Bandas de Bollinger – 19;
  • Fractais – 8;
  • CCI, ATR – 7 indicadores de cada;
  • Zigzag, SAR Parabólico – 6 indicadores de cada;
  • ADX – 5;
  • Impulso – 4;
  • indicadores personalizados (isso é intrigante :) ) – 4;
  • Ichimoku, AO – 3 indicadores de cada;
  • ROC, WPR, StdDev, Volumes – 2 indicadores de cada.

Os dados sugerem as seguintes conclusões - a maioria dos participantes utiliza estratégias de acompanhamento de negociação com indicadores. É possível que eu tenha perdido algo ao coletar os dados e nós veremos o surgimento de algumas personalidades extraordinárias no ramo da negociação automatizada, mas isso parece improvável no momento. Penso que o principal problema é que os iniciantes atraídos pelo mercado, na maioria dos casos, recebem regras em vez de conhecimento.

Por exemplo, aqui estão as regras de uso de MACD, aqui estão os sinais - agora otimize os parâmetros e ganhe dinheiro. E se eles utilizassem os cérebros um pouco? Besteira! Os padrões já foram desenvolvidos! Por que reinventar a roda? Entretanto, frequentemente nos esquecemos de que os indicadores que são tão populares no momento também foram inventados por negociadores como você e eu. Eles também tinham seus padrões e especialidades. Talvez um indicador com o seu nome se tornará padrão em cerca de dez anos.

Eu gostaria de compartilhar o meu método de busca de ideias de negociação, bem como o método que uso para testá-las rapidamente.


Descrição do método

Toda a análise técnica é baseada em um axioma simples - os preços consideram tudo. Mas há um problema - esta afirmação não tem dinâmica. Olhamos para o gráfico e observamos uma imagem estática: o preço realmente considerou tudo. Entretanto, queremos saber o que o preço considerará em um determinado período de tempo futuro e aonde ele irá, para que possamos ter lucro. Os indicadores derivados do preço foram projetados exatamente para prever possíveis movimentos futuros.

Como aprendemos com a física, o derivativo de primeira ordem de magnitude é velocidade. Assim, os indicadores calculam a velocidade de alteração do preço atual. Também sabemos que magnitudes significativas têm inércia que evita mudanças bruscas no valor da velocidade sem a intervenção de forças externas consideráveis. É assim que abordamos gradativamente o conceito de uma tendência - o estado de preço quando o seu derivativo de primeira ordem (velocidade) mantém o seu valor durante o período de tempo em que forças externas (notícias, políticas de bancos centrais, etc.) não afetam o mercado.

Agora vamos voltar ao início - os preços consideram tudo. Para desenvolver novas ideias, devemos examinar o comportamento do preço e seus derivativos no mesmo intervalo de tempo. Apenas uma análise cuidadosa dos gráficos de preço elevará a sua negociação de fé cega ao nível de entendimento verdadeiro.

Isso pode não levar a mudanças imediatas nos resultados de negociação, mas a capacidade de responder a inúmeros porquês terá um papel positivo mais cedo ou mais tarde. Além disso, a análise visual de gráficos e indicadores permitirá que você encontre correlações novas entre os preços e indicadores, as quais passaram totalmente despercebidas pelos desenvolvedores.

Imagine que você encontrou uma nova correlação que aparentemente trabalha a seu favor. Qual o próximo passo? A forma mais fácil é escrever um Expert Advisor e testá-lo em relação a dados históricos, certificando-se de que a sua hipótese esteja correta. Se esse não for o caso, temos que selecionar uma forma comum de otimização de parâmetros. A pior coisa é que não fomos capazes de responder ao porquê. Por que o nosso Expert Advisor causa perdas/lucros? Por que houve uma redução tão grande? Sem as respostas, você não será capaz de implementar sua ideia com eficiência.

Eu realizo as seguintes ações para visualizar os resultados de uma correlação obtida no gráfico:

  1. Eu crio o indicador necessário, para que ele gere um sinal: -1 para venda e 1 para compra.
  2. Eu conecto o indicador de balanço que exibe pontos de entrada e saída para o gráfico. O indicador também mostra as mudanças de balanço e participação (em pontos) ao processar o sinal.
  3. Eu analiso em quais casos e circunstâncias minhas hipóteses são corretas.

O método tem algumas vantagens.

  • Primeiramente, o indicador de balanço é totalmente calculado utilizando o método OnCalculate, que proporciona velocidade máxima de cálculo e disponibilidade automática de dados históricos nas séries de cálculo de entrada.
  • Em segundo lugar, a adição do sinal ao indicador existente é um passo intermediário entre a criação de um Expert Advisor via Assistente e desenvolvê-lo por conta própria.
  • Em terceiro lugar, uma ideia e um resultado final podem ser visualizados em um único gráfico. É evidente que o método apresenta algumas limitações: um sinal está amarrado ao preço de fechamento da barra, o balanço é calculado para o lote constante, não há opções para negociar com ordens pendentes. Entretanto, todas essas limitações podem ser facilmente corrigidas/melhoradas.


Implementação

Vamos desenvolver um indicador de sinal simples para compreender como ele funciona e avaliar a conveniência do método. Já ouvi falar sobre os padrões de candlestick há muito tempo. Então por que não verificar o funcionamento prático deles? Eu selecionei os padrões reversos "martelo" e "estrela cadente" como sinais de compra e venda, respectivamente. As imagens abaixo apresentam o aspecto esquemático deles:

Figura 1. Padrões de candlestick

Figura 1. Padrões de candlestick "martelo" (hammer) e "estrela cadente" (shooting star)

Agora, vamos definir as regras de entrada no mercado quando o padrão "martelo" aparece.

  1. O valor mais baixo do candle deve ser inferior que os valores dos últimos cinco candles;
  2. O corpo do candle não deve exceder 50% de sua altura total;
  3. A sombra superior do candle não deve exceder 0% de sua altura total;
  4. A altura do candle não deve ser inferior a 100% a altura média dos cinco candles anteriores;
  5. O preço de fechamento do padrão deve ser inferior à Média Móvel de 10 períodos.

Se essas condições estiverem presentes, devemos abrir uma posição longa. As regras são as mesmas para o padrão "estrela cadente". A única diferença é que devemos abrir uma posição curta:

  1. O valor mais alto do candle deve ser superior aos valores dos últimos cinco candles;
  2. O corpo do candle não deve exceder 50% de sua altura total;
  3. A sombra inferior do candle não deve exceder 0% de sua altura total;
  4. A altura do candle não deve ser inferior a 100% a altura média dos cinco candles anteriores;
  5. O preço de fechamento do padrão deve ser superior à Média Móvel de 10 períodos.

Eu utilizei negrito para os parâmetros que usei com base em esboços que podem ser otimizados no futuro (se o padrão apresentar resultados aceitáveis). Os limites que desejo implementar permitem limparmos os padrões dos que apresentam aparência inapropriada (pp. 1-3) bem como dos conhecidamente fracos que não podem ser aceitos como sinais.

Ademais, devemos determinar os momentos de saída. Visto que os padrões mencionados aparecem como sinais de inversão de tendência, a tendência existe no momento em que o candle apropriado aparece. Dessa forma, a média móvel que segue o preço também estará presente. O sinal de saída é formado pelo cruzamento do preço e de sua média móvel de 10 períodos.

Agora é hora de programar um pouco. Vamos desenvolver um novo indicador personalizado no Assistente MQL5, nomeá-lo PivotCandles e descrever o seu comportamento. Vamos definir os valores retornados para conectar o indicador de balanço:

  • -1 – abrir uma posição de venda;
  • -2 – fechar uma posição de compra;
  • 0 – sem sinal;
  • 1 – abrir posição de compra;
  • 2 – fechar posição de venda.

Como você sabe, os verdadeiros programadores não procuram caminhos fáceis. Eles procuram os mais fáceis de todos. :) Eu não sou uma exceção. Enquanto ouvia música com meus fones de ouvido e bebia café aromático, criei o arquivo com a classe a ser implementada em um indicador e em um Expert Advisor (caso eu decida desenvolvê-lo com base no indicador). É possível que ele até mesmo possa ser modificado para outros padrões de candlestick. O código não tem nada absolutamente novo. Acredito que os comentários implementados no código cobrem todas as perguntas possíveis.

//+------------------------------------------------------------------+
//|                                            PivotCandlesClass.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input int      iMaxBodySize            = 50;  // Maximum candle body, %
input int      iMaxShadowSize          = 0;   // Maximum allowed candle shadow, %
input int      iVolatilityCandlesCount = 5;   // Number of previous bars for calculation of an average volatility
input int      iPrevCandlesCount       = 5;   // Number of previous bars, for which the current bar should be an extremum
input int      iVolatilityPercent      = 100; // Correlation of a signal candle with a previous volatility, %
input int      iMAPeriod               = 10;  // Period of a simple signal moving average
//+------------------------------------------------------------------+
//| Class definition                                                 |
//+------------------------------------------------------------------+
class CPivotCandlesClass
  {
private:
   MqlRates          m_candles[];              // Array for storing the history necessary for calculations
   int               m_history_depth;          // Array length for storing the history
   int               m_handled_candles_count;  // Number of the already processed candles
   
   double            m_ma_value;               // Current calculated moving average value
   double            m_prev_ma_value;          // Previous calculated moving average value
   bool              m_is_highest;             // Check if the current candle is the highest one
   bool              m_is_lowest;              // Check if the current candle is the lowest one
   double            m_volatility;             // Average volatility
   int               m_candle_pattern;         // Current recognized pattern
   
   void              PrepareArrayForNewCandle();        // Prepare the array for accepting the new candle
   int               CheckCandleSize(MqlRates &candle); // Check the candle for conformity with patterns
   void              PrepareCalculation();
protected:
   int               DoAnalizeNewCandle();              // Calculation function
public:
   void              CPivotCandlesClass(); 
   
   void              CleanupHistory();                  // Clean up all calculation variables  
   double            MAValue() {return m_ma_value;}     // Current value of the moving average
   int               AnalizeNewCandle(MqlRates& candle);
   int               AnalizeNewCandle( 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 );
  };
//+------------------------------------------------------------------+
//| CPivotCandlesClass                                               |
//+------------------------------------------------------------------+
//| Class initialization                                             |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::CPivotCandlesClass()
  {
   // History depth should be enough for all calculations
   m_history_depth = (int)MathMax(MathMax(
      iVolatilityCandlesCount + 1, iPrevCandlesCount + 1), iMAPeriod);
   m_handled_candles_count = 0;
   m_prev_ma_value = 0;
   m_ma_value = 0;
   
   ArrayResize(m_candles, m_history_depth);
  }  
//+------------------------------------------------------------------+
//| CleanupHistory                                                   |
//+------------------------------------------------------------------+
//| Clean up the candle buffer for recalculation                     |
//+------------------------------------------------------------------+
void CPivotCandlesClass::CleanupHistory()
  {
   // Clean up the array
   ArrayFree(m_candles);
   ArrayResize(m_candles, m_history_depth);
   
   // Null calculation variables
   m_handled_candles_count = 0;
   m_prev_ma_value = 0;
   m_ma_value = 0;   
  }
//+-------------------------------------------------------------------+
//| AnalizeNewCandle                                                  |
//+-------------------------------------------------------------------+
//| Preparations for analyzing the new candle and the analysis itself |
//| based on candle's separate parameter values                       |
//+-------------------------------------------------------------------+
int CPivotCandlesClass::AnalizeNewCandle( 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 )
  {
   // Prepare the array for the new candle
   PrepareArrayForNewCandle();

   // Fill out the current value of the candle
   m_candles[0].time          = time;
   m_candles[0].open          = open;
   m_candles[0].high          = high;
   m_candles[0].low           = low;
   m_candles[0].close         = close;
   m_candles[0].tick_volume   = tick_volume;
   m_candles[0].real_volume   = volume;
   m_candles[0].spread        = spread;

   // Check if there is enough data for calculation
   if (m_handled_candles_count < m_history_depth)
      return 0;
   else
      return DoAnalizeNewCandle();
  }  
//+-------------------------------------------------------------------+
//| AnalizeNewCandle                                                  |
//+-------------------------------------------------------------------+
//| Preparations for analyzing the new candle and the analysis itself |
//| based on the received candle                                      |
//+-------------------------------------------------------------------+
int CPivotCandlesClass::AnalizeNewCandle(MqlRates& candle)
  {
   // Prepare the array for the new candle   
   PrepareArrayForNewCandle();

   // Add the candle 
   m_candles[0] = candle;

   // Check if there is enough data for calculation
   if (m_handled_candles_count < m_history_depth)
      return 0;
   else
      return DoAnalizeNewCandle();
  }
//+------------------------------------------------------------------+
//| PrepareArrayForNewCandle                                         |
//+------------------------------------------------------------------+ 
//| Prepare the array for the new candle                             |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::PrepareArrayForNewCandle()
  {
   // Shift the array by one position to write the new value there
   ArrayCopy(m_candles, m_candles, 1, 0, m_history_depth-1);
   
   // Increase the counter of added candles
   m_handled_candles_count++;
  }
//+------------------------------------------------------------------+
//| CalcMAValue                                                      |
//+------------------------------------------------------------------+ 
//| Calculate the current values of the Moving Average, volatility   |
//|   and the value extremality                                      |
//+------------------------------------------------------------------+ 
void CPivotCandlesClass::PrepareCalculation()
  {
   // Store the previous value
   m_prev_ma_value = m_ma_value;
   m_ma_value = 0;
   
   m_is_highest = true;         // check if the current candle is the highest one
   m_is_lowest = true;          // check if the current candle is the lowest one
   m_volatility = 0;    // average volatility
   
   double price_sum = 0; // Variable for storing the sum
   for (int i=0; i<m_history_depth; i++)
     {
      if (i<iMAPeriod)
         price_sum += m_candles[i].close;
      if (i>0 && i<=iVolatilityCandlesCount)
         m_volatility += m_candles[i].high - m_candles[i].low;
      if (i>0 && i<=iPrevCandlesCount)
        {
         m_is_highest = m_is_highest && (m_candles[0].high > m_candles[i].high);
         m_is_lowest = m_is_lowest && (m_candles[0].low < m_candles[i].low);
        }
     }
   m_ma_value = price_sum / iMAPeriod;
   m_volatility /= iVolatilityCandlesCount;
   
   m_candle_pattern = CheckCandleSize(m_candles[0]);
  }
//+------------------------------------------------------------------+
//| CheckCandleSize                                                  |
//+------------------------------------------------------------------+
//| Check if the candle sizes comply with the patterns               |
//| The function returns:                                            |
//|   0 - if the candle does not comply with the patterns            |
//|   1 - if "hammer" pattern is detected                            |
//|   -1 - if "shooting star" pattern is detected                    |
//+------------------------------------------------------------------+ 
int CPivotCandlesClass::CheckCandleSize(MqlRates &candle)
  {
   double candle_height=candle.high-candle.low;          // candle's full height
   double candle_body=MathAbs(candle.close-candle.open); // candle's body height

   // Check if the candle has a small body
   if(candle_body/candle_height*100.0>iMaxBodySize)
      return 0;

   double candle_top_shadow=candle.high-MathMax(candle.open,candle.close);   // candle upper shadow height
   double candle_bottom_shadow=MathMin(candle.open,candle.close)-candle.low; // candle bottom shadow height

   // If the upper shadow is very small, that indicates the "hammer" pattern
   if(candle_top_shadow/candle_height*100.0<=iMaxShadowSize)
      return 1;
   // If the bottom shadow is very small, that indicates the "shooting star" pattern
   else if(candle_bottom_shadow/candle_height*100.0<=iMaxShadowSize)
      return -1;
   else
      return 0;
  }
//+------------------------------------------------------------------+
//| DoAnalizeNewCandle                                               |
//+------------------------------------------------------------------+
//| Real analysis of compliance with the patterns                    |
//+------------------------------------------------------------------+ 
int CPivotCandlesClass::DoAnalizeNewCandle()
  {
   // Prepare data for analyzing the current situation
   PrepareCalculation();
   
   // Process prepared data and set the exit signal
   int signal = 0;
   
   ///////////////////////////////////////////////////////////////////
   // EXIT SIGNALS                                                  //
   ///////////////////////////////////////////////////////////////////
   // If price crosses the moving average downwards, short position is closed
   if(m_candles[1].close > m_prev_ma_value && m_candles[0].close < m_ma_value)
      signal = 2;
   // If price crosses the moving average upwards, long position is closed 
   else if (m_candles[1].close < m_prev_ma_value && m_candles[0].close > m_ma_value)
      signal = -2;
      
   ///////////////////////////////////////////////////////////////////
   // ENTRY SIGNALS                                                 //
   ///////////////////////////////////////////////////////////////////
   // Check if the minimum volatility condition is met
   if (m_candles[0].high - m_candles[0].low >= iVolatilityPercent / 100.0 * m_volatility)
     {
      // Checks for "shooting star" pattern
      if (m_candle_pattern < 0 && m_is_highest && m_candles[0].close > m_ma_value)
         signal = -1;
      // Checks for "hammer" pattern
      else if (m_candle_pattern > 0 && m_is_lowest && m_candles[0].close < m_ma_value)
         signal = 1;
     }
     
   return signal;
  }
//+------------------------------------------------------------------+

Podemos perceber que toda a parte de cálculo é realizada pela classe CPivotCandlesClass. Considera-se boa prática de programação a separação da parte de cálculo da parte visual e eu faço o máximo esforço para seguir essa recomendação. Os benefícios não demoram a chegar - segue abaixo o código do próprio indicador:

//+------------------------------------------------------------------+
//|                                                 PivotCandles.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

// Use four buffers, while drawing two
#property indicator_buffers 4
#property indicator_plots   2
//--- plot SlowMA
#property indicator_label1  "SlowMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAliceBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot ChartSignal
#property indicator_label2  "ChartSignal"
#property indicator_type2   DRAW_COLOR_ARROW
#property indicator_color2  clrLightSalmon,clrOrangeRed,clrBlack,clrSteelBlue,clrLightBlue
#property indicator_style2  STYLE_SOLID
#property indicator_width2  3

#include <PivotCandlesClass.mqh>
//+------------------------------------------------------------------+
//| Common arrays and structures                                     |
//+------------------------------------------------------------------+
//--- Indicator buffers                                                
double   SMA[];            // Values of the Moving Average
double   Signal[];         // Signal values
double   ChartSignal[];    // Location of signals on the chart
double   SignalColor[];    // Signal color array
//--- Calculation class
CPivotCandlesClass PivotCandlesClass;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,SMA,INDICATOR_DATA);
   SetIndexBuffer(1,ChartSignal,INDICATOR_DATA);
   SetIndexBuffer(2,SignalColor,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(3,Signal,INDICATOR_CALCULATIONS);

//--- set 0 as an empty value
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);

   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 there have not been calculations yet or (!) the new history is uploaded, clean up the calculation object
   if (prev_calculated == 0)
      PivotCandlesClass.CleanupHistory();
   
   int end_calc_edge = rates_total-1;   
   if (prev_calculated >= end_calc_edge)
      return end_calc_edge;
   
   for(int i=prev_calculated; i<end_calc_edge; i++)
     {
      int signal = PivotCandlesClass.AnalizeNewCandle(time[i],open[i],high[i],low[i],close[i],tick_volume[i],volume[i],spread[i]);
      Signal[i] = signal;
      SMA[i] = PivotCandlesClass.MAValue();
      
      // Signals are processed, display them on the chart
      // Set the location of our signals...
      if (signal < 0)
         ChartSignal[i]=high[i];
      else if (signal > 0)
         ChartSignal[i]=low[i];
      else
         ChartSignal[i]=0;
      // .. as well as their color
      // Signals have a range of [-2..2], while color indices - [0..4]. Align them 
      SignalColor[i]=signal+2;
     }
   
   // Set the Moving Average value similar to the previous one to prevent it from sharp fall
   SMA[end_calc_edge] = SMA[end_calc_edge-1];

//--- return value of prev_calculated for next call
   return(end_calc_edge);
  }
//+------------------------------------------------------------------+

O indicador está pronto. Agora, vamos testá-lo em qualquer um dos gráficos. Para fazer isso, instale o indicador compilado no gráfico. Após isso, veremos algo similar ao apresentado na imagem abaixo.

Figura 2. Indicador dos padrões de candlestick

Figura 2. Indicador dos padrões de candlestick "martelo" e "estrela cadente"

Os pontos coloridos indicam possíveis entradas e saídas de mercado. As cores são selecionadas da seguinte forma:

  • vermelho escuro – vender;
  • azul escuro – comprar;
  • vermelho claro – fechar posição longa;
  • vermelho claro – fechar posição curta;

Sinais de fechamento são formados sempre que o preço atinge a sua média móvel. O sinal é ignorado caso não houvesse posições naquele momento.

Agora, vamos ao tópico principal deste artigo. Nós temos um indicador com o buffer de sinal gerando apenas alguns sinais determinados. Vamos exibir em uma janela separada do mesmo gráfico o quanto esses sinais podem ser lucrativos ou causar prejuízos se realmente forem seguidos. O indicador foi desenvolvido especialmente para esse caso. Ele pode se conectar a outro indicador e abrir/fechar posições virtuais dependendo dos sinais que estão sendo recebidos.

Da mesma forma que fizemos com o indicador anterior, devemos separar o código em duas partes - cálculo e visual. Segue abaixo o resultado de uma noite sem dormir, mas espero que valha a pena. :)

//+------------------------------------------------------------------+
//|                                                 BalanceClass.mqh |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//+------------------------------------------------------------------+
//| Common structures                                                |
//+------------------------------------------------------------------+
// Structure for returning calculation results 
// using only return command;
struct BalanceResults
  {
   double balance;
   double equity;
  };
//+------------------------------------------------------------------+
//| Common function                                                  |
//+------------------------------------------------------------------+
//  Function for searching for the indicator handle by its name
int FindIndicatorHandle(string _name)
  {
   // Receive the number of open charts
   int windowsCount = (int)ChartGetInteger(0,CHART_WINDOWS_TOTAL);
   
   // Search all of them
   for(int w=windowsCount-1; w>=0; w--)
     {
      // How many indicators are attached to the current chart
      int indicatorsCount = ChartIndicatorsTotal(0,w);

      // Search by all chart indicators
      for(int i=0;i<indicatorsCount;i++)
        {
         string name = ChartIndicatorName(0,w,i);
         // If such an indicator is found, return its handle
         if (name == _name)
            return ChartIndicatorGet(0,w,name);
        }
     }  
     
   // If there is no such an indicator, return the incorrect handle 
   return -1;
  }
//+------------------------------------------------------------------+
//| Base calculation class                                           |
//+------------------------------------------------------------------+
class CBaseBalanceCalculator
  {
private:
   double            m_position_volume; // Current open position volume
   double            m_position_price;  // Position opening price
   double            m_symbol_points;   // Value of one point for the current symbol
   BalanceResults    m_results;         // Calculation results
public:
   void              CBaseBalanceCalculator(string symbol_name = "");
   void              Cleanup();
   BalanceResults    Calculate( const double _prev_balance, 
                                const int    _signal,
                                const double _next_open,
                                const double _next_spread );
  };
//+------------------------------------------------------------------+
//| CBaseBalanceCalculator                                           |
//+------------------------------------------------------------------+
void CBaseBalanceCalculator::CBaseBalanceCalculator(string symbol_name = "")
  {
   // Clean up state variables
   Cleanup();
   
   // Define point size (because we will calculate the profit in points)
   if (symbol_name == "")
      m_symbol_points = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
   else 
      m_symbol_points = SymbolInfoDouble(symbol_name, SYMBOL_POINT);
  }
//+------------------------------------------------------------------+
//| Cleanup                                                          |
//+------------------------------------------------------------------+
//| Clean up data on positions and prices                            |
//+------------------------------------------------------------------+
void CBaseBalanceCalculator::Cleanup()
  {
   m_position_volume = 0;
   m_position_price = 0;  
  }
//+------------------------------------------------------------------+
//| Calculate                                                        |
//+------------------------------------------------------------------+
//| Main calculation block                                           |
//+------------------------------------------------------------------+
BalanceResults CBaseBalanceCalculator::Calculate(
                                       const double _prev_balance,
                                       const int _signal,
                                       const double _next_open,
                                       const double _next_spread )
  {
   // Clean up the output structure from the previous values
   ZeroMemory(m_results);
   
   // Initialize additional variables
   double current_price = 0; // current price (bid or ask depending on position direction)
   double profit = 0;        // profit calculated value
   
   // If there was no signal, the balance remains the same 
   if (_signal == 0)
      m_results.balance = _prev_balance;
   // the signal coincides with the direction or no positions are opened yet
   else if (_signal * m_position_volume >= 0)
     {
      // Position already exists, the signal is ignored
      if (m_position_volume != 0)
         // Balance is not changed
         m_results.balance = _prev_balance;
      // No positions yet, buy signal

      else if (_signal == 1)
        {
         // Calculate current ASK price, recalculate price, volume and balance
         current_price = _next_open + _next_spread * m_symbol_points;
         m_position_price = (m_position_volume * m_position_price + current_price) / (m_position_volume + 1);
         m_position_volume = m_position_volume + 1;
         m_results.balance = _prev_balance;
        }
      // No positions yet, sell signal
      else if (_signal == -1) 
        {
         // Calculate current BID price, recalculate price, volume and balance
         current_price = _next_open;
         m_position_price = (-m_position_volume * m_position_price + current_price) / (-m_position_volume + 1);
         m_position_volume = m_position_volume - 1; 
         m_results.balance = _prev_balance;      
        }
      else
         m_results.balance = _prev_balance;
     }
   // Position is set already, the opposite direction signal is received
   else 
     {
      // buy signal/close sell position
      if (_signal > 0)
        {
         // Close position by ASK price, recalculate profit and balance
         current_price = _next_open + _next_spread * m_symbol_points;
         profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
         m_results.balance = _prev_balance + profit;
          
         // If there is a signal for opening a new position, open it at once
         if (_signal == 1)
           {
            m_position_price = current_price;
            m_position_volume = 1;
           }
         else
            m_position_volume = 0;
        }
      // sell signal/close buy position
      else 
        {
         // Close position by BID price, recalculate profit and balance
         current_price = _next_open;
         profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
         m_results.balance = _prev_balance + profit;
         
         // If there is a signal for opening a new position, open it at once
         if (_signal == -1)
           {
            m_position_price = current_price;
            m_position_volume = -1;
           }
         else 
           m_position_volume = 0;
        }
     }
    
   // Calculate the current equity
   if (m_position_volume > 0)
     {
      current_price = _next_open;
      profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
      m_results.equity = m_results.balance + profit;
     }
   else if (m_position_volume < 0)
     {
      current_price = _next_open + _next_spread * m_symbol_points;
      profit = (current_price - m_position_price) / m_symbol_points * m_position_volume;
      m_results.equity = m_results.balance + profit;
     }
   else
      m_results.equity = m_results.balance;    
   
   return m_results;
  }
//+------------------------------------------------------------------+

A classe de cálculo está pronta. Agora, devemos implementar a exibição do indicador para ver como ele funciona.

//+------------------------------------------------------------------+
//|                                                      Balance.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window

#property indicator_buffers 4
#property indicator_plots   3
#property indicator_level1  0.0 
#property indicator_levelcolor Silver 
#property indicator_levelstyle STYLE_DOT
#property indicator_levelwidth 1  
//--- plot Balance
#property indicator_label1  "Balance"
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  clrBlue,clrRed
#property indicator_style1  STYLE_DOT
#property indicator_width1  1
//--- plot Equity
#property indicator_label2  "Equity"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrLime
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot Zero
#property indicator_label3  "Zero"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrGray
#property indicator_style3  STYLE_DOT
#property indicator_width3  1

#include <BalanceClass.mqh>
//+------------------------------------------------------------------+
//| Input and global variables                                       |
//+------------------------------------------------------------------+
input string   iParentName        = "";             // Indicator name for balance calculation
input int      iSignalBufferIndex = -1;            // Signal buffer's index number
input datetime iStartTime         = D'01.01.2012';  // Calculation start date
input datetime iEndTime           = 0;             // Calculation end date
//--- Indicator buffers 
double   Balance[];       // Balance values
double   BalanceColor[];  // Color index for drawing the balance
double   Equity[];        // Equity values
double   Zero[];          // Zero value for histogram's correct display
//--- Global variables
double   Signal[1];       // Array for receiving the current signal
int      parent_handle;   // Indicator handle, the signals of which are to be used 

CBaseBalanceCalculator calculator; // Object for calculating balance and equity
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {     
   // Binding indicator buffers
   SetIndexBuffer(0,Balance,INDICATOR_DATA);
   SetIndexBuffer(1,BalanceColor,INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2,Equity,INDICATOR_DATA);
   SetIndexBuffer(3,Zero,INDICATOR_DATA);
  
   // Search for indicator handle by its name
   parent_handle = FindIndicatorHandle(iParentName);
   if (parent_handle < 0)
     {
      Print("Error! Parent indicator not found");
      return -1;
     } 
   
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {   
   // Set the borders for calculating the indicator
   int start_index = prev_calculated;
   int end_index = rates_total-1;
   
   // Calculate balance and equity values
   for(int i=start_index; i<end_index; i++)
     {
      // Check if the balance calculation corresponds the interval
      if (time[i] < iStartTime)
        {
         Balance[i] = 0;
         Equity[i] = 0; 
         continue;
        }
      if (time[i] > iEndTime && iEndTime != 0)
        {
         Equity[i] = (i==0) ? 0 : Equity[i-1];
         Balance[i] = Equity[i]; 
         continue;
        }
      
      // Request a signal from the parent indicator
      if(CopyBuffer(parent_handle,iSignalBufferIndex,time[i],1,Signal)==-1) // Copy the indicator main line data
        {
         Print("Data copy error: " + IntegerToString(GetLastError()));
         return(0);  // Finish the function operation and send indicator for the full recalculation
        }
      
      // Initialize balance and equity calculation
      // Since the signal is formed when the candle is closing, we will be able 
      //   to perform any operation only at the next candle's opening price
      BalanceResults results = calculator.Calculate(i==0?0:Balance[i-1], (int)Signal[0], open[i+1], spread[1+1]);
      
      // Fill out all indicator buffers
      Balance[i] = results.balance;
      Equity[i] = results.equity; 
      Zero[i] = 0;
      if (Balance[i] >= 0)
         BalanceColor[i] = 0;
      else
         BalanceColor[i] = 1;
     }
     
   // Fill out buffers for the last candle 
   Balance[end_index] = Balance[end_index-1];
   Equity[end_index] = Equity[end_index-1]; 
   BalanceColor[end_index] = BalanceColor[end_index-1];
   Zero[end_index] = 0;
     
   return rates_total;
  }
//+------------------------------------------------------------------+

Finalmente terminou! Vamos compilá-lo e examinar os resultados.


Instruções de uso

Para avaliar a operação de nosso indicador recém-desenvolvido, ele deve ser anexado ao gráfico que contém pelo menos um indicador de sinal. Se você seguiu todos os passos, já temos um indicador – PivotCandles. Agora temos que configurar os parâmetros de entrada. Vamos ver o que devemos especificar:

  • Nome do indicador para cálculo de balanço (string) – devemos ter em mente que a ligação do indicador de balanço é realizada pelo nome. Assim, este campo é obrigatório.
  • Número do índice do buffer de sinal (inteiro) – outro parâmetro crítico. O indicador de sinal poderá gerar diversos sinais de acordo com um algoritmo previamente definido. Dessa forma, o indicador de balanço deve ter os dados relativos ao sinal do buffer que ele irá calcular.
  • Data de início do cálculo (data/hora) – data inicial do cálculo do balanço.
  • Data de término do cálculo (data/hora) – data final do cálculo do balanço. Se a data não for selecionada (igual a zero), o cálculo será realizado até a última barra.

A Figura 3 mostra a configuração dos primeiros dois parâmetros para anexar o indicador de balanço ao terceiro buffer do indicador PivotCandles. Os dois parâmetros restantes podem ser configurados de acordo com a sua preferência.

Figura 3. Parâmetros do indicador de balanço

Figura 3. Parâmetros do indicador de balanço

Se todos os passos anteriores foram executados corretamente, você deve observar uma imagem muito similar à que é exibida abaixo:

Figura 4. Curvas de balanço e participação geradas com o uso de sinais de indicador PivotCandles

Figura 4. Curvas de balanço e participação geradas com o uso de sinais de indicador PivotCandles

Agora, podemos experimentar diferentes períodos de tempo e símbolos e encontrar as entradas de mercado mais lucrativas e as que causam mais prejuízos. Deve-se adicionar que essa abordagem ajuda a encontrar as correlações de mercado que afetam os seus resultados de negociação.

Originalmente, eu queria comparar o tempo gasto no teste do Expert Advisor com base nos mesmos sinais com o tempo gasto utilizando o método descrito acima. Mas abandonei a ideia, visto que o recálculo do indicador demora cerca de um segundo. Um tempo tão curto certamente ainda não é alcançado pelo Expert Advisor com seus algoritmos de upload de histórico e de geração de ticks.


Conclusão

O método descrito acima é muito rápido. Além disso, ele proporciona clareza no teste de indicadores que geram sinais de posição aberta/fechada. Ele permite que os negociadores analisem os sinais e as respostas do depósito a eles em uma única janela de gráfico. Mas precisamos estar cientes de algumas limitações que ele ainda possui:

  • O buffer de sinal do indicador analisado deve ser preparado preliminarmente;
  • Os sinais estão ligados à hora de abertura da nova barra;
  • Nenhum MM ao calcular o balanço;

Entretanto, apesar dessas fraquezas, espero que os benefícios sejam mais significativos e que este método de teste obtenha o seu lugar entre as outras ferramentas projetadas para análise de comportamento de mercado e processamento de sinais gerados pelo mercado.

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

Arquivos anexados |
balanceclass.mqh (8.07 KB)
balance.mq5 (5.57 KB)
pivotcandles.mq5 (4.14 KB)
Sinais de negociação no MetaTrader 5: uma melhor alternativa às contas PAMM! Sinais de negociação no MetaTrader 5: uma melhor alternativa às contas PAMM!
Temos o prazer de anunciar que o MetaTrader 5 agora dispõe de Sinais de negociação, proporcionando assim uma ferramenta poderosa aos investidores e gerentes. Enquanto você estiver seguindo as negociações de um trader bem sucedido, o terminal irá reproduzi-las automaticamente em sua conta!
Resultados da MetaTrader AppStore para o terceiro trimestre de 2013 Resultados da MetaTrader AppStore para o terceiro trimestre de 2013
Outro trimestre do ano se passou e nós decidimos resumir seus resultados para a MetaTrader AppStore - a maior loja de robôs comerciais e indicadores técnicos para plataformas MetaTrader. Mais de 500 desenvolvedores colocaram mais de 200 produtos no mercado até o final do trimestre reportado.
Fundamentos básicos da programação MQL5: arrays Fundamentos básicos da programação MQL5: arrays
Juntamente com as variáveis e funções, os arrays são partes integrais de quase todas as linguagens de programação. O artigo deve ser de interesse principalmente para programadores MQL5 novatos, enquanto os desenvolvedores mais experientes terão uma boa oportunidade para resumir e sistematizar seu conhecimento.
Como ganhar dinheiro com o AppStore MetaTrader e Serviços de Sinal de Comércio se você não é um vendedor ou fornecedor Como ganhar dinheiro com o AppStore MetaTrader e Serviços de Sinal de Comércio se você não é um vendedor ou fornecedor
É possível começar a ganhar dinheiro com MQL.com agora mesmo sem ter que ser um vendedor de aplicativos Market ou um fornecedor de sinais lucrativo. Selecione os produtos que você deseja e poste links para eles em diversos recursos web. Atraia clientes potenciais e o lucro é seu!