English Русский 中文 Español Deutsch 日本語
Sistema de negociação '80-20'

Sistema de negociação '80-20'

MetaTrader 5Sistemas de negociação | 3 novembro 2016, 08:31
2 410 0
Alexander Puzanov
Alexander Puzanov

Introdução

'80-20' é o nome de uma das estratégias de negociação (EN) descritas no livro Street Smarts: High Probability Short-Term Trading Strategies de Linda Raschke e Laurence Connors. Como o par de estratégias discutidas no meu artigo anterior, as autoras atribuíram-lo para a fase de teste de limites de preço do intervalo. Ele também foca na obtenção de lucro a partir de fuga falsa ou reversão de limites. Mas, desta vez, para detectar o sinal, é analisado o movimento dos preços, no período significativamente mais curto do histórico, quer dizer, apenas o dia anterior. O tempo de vida do sinal recebido também é relativamente pequeno, uma vez que o sistema é projetado para negociação intradia.

O primeiro dos objetivos do presente artigo é descrever como criar, usando a linguagem MQL5, um módulo de sinal que implemente as regras da estratégia de negociação '80-20'. Para, em seguida, conectar esse módulo ao Expert Advisor, criado no artigo anterior desta série, após editá-lo um pouco. Nós usamos este módulo inalterado ao criar um indicador para negociação manual.

Lembre-se que, nesta série de artigos, o código criado está focado principalmente na categoria dos programadores que pode ser definida como "iniciantes ligeiramente avançados". Portanto, além da tarefa principal, o ccódigo é projetado para ajudar a mudança de programação procedural para programação de alto nível orientada a objetos. Nele, não serão geradas classes, porém serão usados na totalidade análogos mais simples para desenvolver, nomeadamente, estruturas.

Outro objetivo do artigo consiste em criar instrumentos que permitem verificar quão atual é esta estratégia hoje em dia, já que quando foi criada, Raschke e Connors levavam em conta o comportamento do mercado no final do século passado. No final do artigo, serão apresentados vários testes - com base no histórico de dados atual - do Expert Advisor criado.


Sistema de negociação '80-20'

Como justificação teórica, os autores referem-se ao livro The Taylor Trading Technique do George Taylor, bem como ao trabalho na análise de computador dos mercados de futuros do Steve Moore e experiência prática do trader Derek Gipson. Formada a base da hipótese da Estratégia de Negociação, pode ser resumidamente afirmado como se segue: se os preços de abertura e fechamento de ontem estiverem distribuídos na região oposta do intervalo diário, hoje haverá uma grande probabilidade de que você possa esperar uma inversão na direção de abertura de ontem. Assim, é importante que, por um lado, os preços de abertura e fechamento de ontem estejam bastante perto dos limites do intervalo, por outro, a inversão comece hoje, mas não antes do fechamento da barra de ontem. O conjunto de regras da Estratégia de Negociação '80-20' adicional alterado pelos autores para entrada no mercado pode ser formulado da seguinte forma:

1. Certifique-se de que ontem o mercado abriu acima de 20% do intervalo diário e fechou abaixo de 20% do intervalo

2. Espere até que o atual preço mínimo atinja o mínimo de ontem pelo menos em 5 ticks

3. Coloque uma ordem pendente de comprar no limite inferior do intervalo de ontem

4. Imediatamente após a ativação da ordem pendente, defina seu StopLoss inicial no mínimo do dia

5. Use Trailing Stop para proteger os lucros obtidos

As regras são semelhantes para vender, no entanto a barra de ontem debe ser de alta, é necessário colocar, por um lado, a ordem de venda no limite superior desta barra e, por outro, o StopLoss no nível do máximo atual.

Outro detalhe importante aparece no livro, ao discutir as ilustrações para a Estratégia de Negociação nos gráficos a partir do histórico, aqui os autores chamar a atenção para o tamanho da barra diária que foi fechada. De acordo com Linda Raschke, ela deve ser superior ao tamanho médio das barras diárias. Na verdade, ele não especifica o número de dias de histórico deve ser tomado em consideração ao calcular o intervalo médio diário.

Não se esqueça que a Estratégia de Negociação é projetada exclusivamente para negociação intradia, os exemplos mostrados no livro usam gráficos do timeframe de 15 minutos.

Abaixo vou descrever o bloco de sinal e o indicador que faz a layout para essa Estratégia de Negociação. Abaixo você pode ver algumas capturas de tela com o resultado do indicador. Nelas são claramente visíveis os padrões, que correspondem às regras do sistema, e os níveis de negociação vinculados a esses padrões.

Timeframe de 5 minutos:


Como consequência da análise deste padrão, é preciso colocar uma ordem pendente para compra. Os respetivos níveis de negociação podem ser apreciados melhor no timeframe de minuto:


Um padrão semelhante, com negociação no sentido oposto no timeframe de cinco minutos:

Padrão com base na Estratégia de Negociação 80-20

Seus níveis de negociação (timeframe de minuto):


 

Módulo de sinal

Para exibir um exemplo de como adicionar opções adicionais à Estratégia de Negociação do autor, adicionamos o cálculo do nível Take Profit. Na versão original este nível não existe, para fechar uma posição, usa-se apenas o trailing de nível Stop Loss. Vamos fazer com que o Take Profit dependa do tamanho mínimo de fuga definido pelo usuário (TS_8020_Extremum_Break) — vamos multiplica-lo pelo coeficiente personalizado TS_8020_Take_Profit_Ratio.

A partir da função base do sinal de módulo fe_Get_Entry_Signal vamos precisar: o estado atual do sinal, os níveis atuais de entrada e saída (Stop Loss e Take Profit), bem como os limites do intervalo de ontem. Obtemos todos os níveis das referências - da função - transferidas para as variáveis, enquanto o estado retornado irá usar a lista de opções a partir do artigo anterior:

enum ENUM_ENTRY_SIGNAL {  // Lista de sinais de entrada
  ENTRY_BUY,              // sinal para compra
  ENTRY_SELL,             // sinal para venda
  ENTRY_NONE,             // sem sinal
  ENTRY_UNKNOWN            // estado não definido
};

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal( // Análise de padrão de dois velas D1
   datetime  t_Time,          // tempo atual
  double&    d_Entry_Level,   // nível de entrada (referência para a variável)
  double&    d_SL,           // nível StopLoss (referência para a variável)
  double&    d_TP,           // nível TakeProfit (referência para a variável)
  double&    d_Range_High,   // máximo do intervalo da primeira barra do padrão(referência para a variável)
   double&    d_Range_Low     // mínimo do intervalo da primeira barra do padrão (referência para a variável)
) {}

Para identificar o sinal, é necessário examinar as duas últimas barras do timeframe diário. Vamos começar com a primeira delas, se ela não satisfazer os critérios da Estratégia de Negociação, não fará sentido verificar a segunda barra. Dois critérios:

1. A magnitude da barra (diferença entre os preços High e Low) deve ser superior à média para os últimos XX dias (é definida pela configuração de usuário TS_8020_D1_Average_Period)

2. Os níveis de apertura e fechamento da barra devem estar relacionados com o oposto 20% do intervalo da barra

Se essas condições forem atendidas, para uso futuro, será necessário lembrar os preços High e Low. Como os parâmetros da primeira barra do padrão não são alterados durante todo o dia, não vamos verificá-los a cada chamada da função, em vez disso levamos em conta as variáveis estatísticas:

// configurações personalizadas
input uint   TS_8020_D1_Average_Period = 20;  // 80-20: Número de dias para cálculo da intervalo médio diário
input uint   TS_8020_Extremum_Break = 50;     // 80-20: fuga mínimo do extremo de ontem (em pontos)


static ENUM_ENTRY_SIGNAL se_Possible_Signal = ENTRY_UNKNOWN; // direção do sinal na primeira barra do padrão
static double
  // variáveis para armazenamento dos níveis calculados entre os ticks
  sd_Entry_Level = 0,
  sd_SL = 0, sd_TP = 0,
  sd_Range_High = 0, sd_Range_Low = 0
;


// verificação da primeira barra do padrão em D1:
if(se_Possible_Signal == ENTRY_UNKNOWN) { // hoje ainda não foi levada a cabo
  st_Last_D1_Bar = t_Curr_D1_Bar; // neste dia a primeira barra já não irá mudar
  
  // intervalo médio diário
  double d_Average_Bar_Range = fd_Average_Bar_Range(TS_8020_D1_Average_Period, PERIOD_D1, t_Time);
  
  if (ma_Rates[0].high — ma_Rates[0].low <= d_Average_Bar_Range) {
    // a primeira barra não grande o suficiente
     se_Possible_Signal = ENTRY_NONE; // significa que hoje não haverá sinal
    return(se_Possible_Signal);
  }
  
  double d_20_Percents = 0.2 * (ma_Rates[0].high — ma_Rates[0].low); // 20% do intervalo de ontem
  if((
      // barra de baixa:
      ma_Rates[0].open > ma_Rates[0].high — d_20_Percents // a barra abriu em máximos do 20% do intervalo
      &&
      ma_Rates[0].close < ma_Rates[0].low + d_20_Percents // a barra fechou em mínimos do 20% do intervalo
    ) || (
      // de alta:
      ma_Rates[0].close > ma_Rates[0].high — d_20_Percents // a barra fechou em máximos do 20% do intervalo
      &&
      ma_Rates[0].open < ma_Rates[0].low + d_20_Percents // a barra abriu em mínimos do 20% do intervalo
  )) {
    // a primeira barra atende as regras
    // definição da direção da negociação para hoje de acordo com a primeira barra do padrão:
    se_Possible_Signal = ma_Rates[0].open > ma_Rates[0].close ? ENTRY_BUY : ENTRY_SELL;
    // nível de entrada no mercado:
    sd_Entry_Level = d_Entry_Level = se_Possible_Signal == ENTRY_BUY ? ma_Rates[0].low : ma_Rates[0].high;
    // limite do intervalo da primeira barra do padrão:
    sd_Range_High = d_Range_High = ma_Rates[0].high;
    sd_Range_Low = d_Range_Low = ma_Rates[0].low;
  } else {
    // ps níveis de apertura/fechamento da primeira barra não cumprem as condições
     se_Possible_Signal = ENTRY_NONE; // significa que hoje não haverá sinal
    return(se_Possible_Signal);
  }
}

Listagem de função de definição do intervalo médio da barra para o número de barras estabelecido do timeframe especificado, começando com as funções de tempo indicadas:

double fd_Average_Bar_Range(     // Cálculo do tamanho médio da barra
  int i_Bars_Limit,             // quantas barras levar em conta
  ENUM_TIMEFRAMES e_TF = PERIOD_CURRENT,  // timeframe de barras
  datetime t_Time = WRONG_VALUE  // a partir de que momento começar o cálculo
) {
  double d_Average_Range = 0; // variável para soma de valores
  if(i_Bars_Limit < 1) return(d_Average_Range);
  
  MqlRates ma_Rates[]; // matriz para informação sobre as barras
  
  // obtenção de informação sobre as barras a partir da parte especificada do histórico:
  if(t_Time == WRONG_VALUE) t_Time = TimeCurrent();
  int i_Price_Bars = CopyRates(_Symbol, e_TF, t_Time, i_Bars_Limit, ma_Rates);
  
  if(i_Price_Bars == WRONG_VALUE) { // tratamento de erros da função CopyRates
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyRates: erro #%u", __FUNCTION__, _LastError);
    return(d_Average_Range);
  }
  
  if(i_Price_Bars < i_Bars_Limit) { // a função CopyRates removeu dados parcialmente
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyRates: copiado %u de barras de %u" , __FUNCTION__, i_Price_Bars, i_Bars_Limit);
  }
  
  // soma de intervalos:
  int i_Bar = i_Price_Bars;
  while(i_Bar-- > 0)
    d_Average_Range += ma_Rates[i_Bar].high — ma_Rates[i_Bar].low;
  
  // valor médio:
  return(d_Average_Range / double(i_Price_Bars));
}

Para a segunda barra (atual) do padrão, o critério é apenas um, isto é, a fuga através do limite do intervalo de ontem deve ser superior à fuga definida nas configurações (TS_8020_Extremum_Break). Uma vez que este nível é atingido, aparece o sinal para colocar uma ordem pendente:

// verificação da segunda barra (atual) do padrão em D1:
if(se_Possible_Signal == ENTRY_BUY) {
  sd_SL = d_SL = ma_Rates[1].low; // StopLoss — para o máximo do preço de hoje
  if(TS_8020_Take_Profit_Ratio > 0) sd_TP = d_TP = d_Entry_Level + _Point * TS_8020_Extremum_Break * TS_8020_Take_Profit_Ratio; // TakeProfit
  return(
    // será que a fuga é o suficientemente pronunciada para abaixo?
    ma_Rates[1].close < ma_Rates[0].low — _Point * TS_8020_Extremum_Break ?
    ENTRY_BUY : ENTRY_NONE
  );
}

if(se_Possible_Signal == ENTRY_SELL) {
  sd_SL = d_SL = ma_Rates[1].high; // StopLoss — para o mínimo do preço de hoje
   if(TS_8020_Take_Profit_Ratio > 0) sd_TP = d_TP = d_Entry_Level — _Point * TS_8020_Extremum_Break * TS_8020_Take_Profit_Ratio; // TakeProfit
  return(
    // será que a fuga é o suficientemente pronunciada para cima?
    ma_Rates[1].close > ma_Rates[0].high + _Point * TS_8020_Extremum_Break ?
    ENTRY_SELL : ENTRY_NONE
  );
}

No arquivo da biblioteca mqh, armazenamos as funções descritas acima (fe_Get_Entry_Signal e fd_Average_Bar_Range) e as configurações personalizadas relacionadas com a obtenção do sinal. Uma lista completa está no anexo a este artigo. Vamos chamar o arquivo Signal_80-20.mqh e colocá-lo na pasta apropriada (MQL5\Include\Expert\Signal) do diretório de dados do terminal. 


Indicador para negociação manual

Tanto o indicador quanto o Expert Advisor vão usar o módulo de sinal descrito acima. Ele (o indicador) deve informar o trader sobre a chegada do sinal para colocação da ordem pendente e notificar sobre níveis calculados, ou seja, os níveis de colocação da ordem, os níveis Take Profit e Stop Loss. O usuário pode selecionar por si mesmo os métodos de notificação: uma janela pop-up, uma mensagem no e-mail ou notificação no dispositivo móvel. É possível escolher tudo de uma só vez ou qualquer combinação destas convenientes opções.

Outra finalidade do indicador consiste no layout no histórico de negociação com base na Estratégia de Negociação '80-20'. Ele vai destacar as barras diárias, conforme os critérios do sistema, e desenhar os níveis de negociação e cálculo. Com base nas linhas dos níveis, será possível avaliar a forma como a situação evoluiu ao longo do tempo. Para maior clareza, fazemos o seguinte: após o preço tocar a linha de sinal, ela se acaba, começa a linha da ordem pendente; após a ativação da ordem pendente, acaba-se sua linha e começam as linhas Take Profit e Stop Loss. Estas linhas são interrompidas, após o preço tocar uma delas (a ordem é fechada). Usando esse layout (ou esboço), será mais fácil avaliar a eficácia das regras do sistema de negociação e entender o que pode ser melhorado.

Vamos começar com a declaração de buffers e seus parâmetros de exibição. Em primeiro lugar, precisamos de declarar dois buffers com preenchimento da região vertical (DRAW_FILLING). Um deles vai destacar todo o intervalo da barra do dia anterior, o outro, apenas a região interna, isto com o fim de separá-la dos máximos e mínimos - do 20% do intervalo - envolvidos na Estratégia de Negociação. Em seguida, declaramos dois buffers para as linhas de sinal multi-coloridas e linhas de ordem pendente(DRAW_COLOR_LINE). Sua cor vai depender da direção da negociação. Haverá também dois linhas (Take Proft e Stop Loss) que não mudarão de cor (DRAW_LINE), uma vez que elas vão utilizar as mesmas cores padrão que lhes foram atribuídas no terminal. À exceção da linha simples, todos os tipos de exibição selecionados requerem dois buffers, por isso o código vai ter a seguinte aparência:

#property indicator_chart_window
#property indicator_buffers  10
#property indicator_plots    6

#property indicator_label1  "primeira barra do padrão"
#property indicator_type1   DRAW_FILLING
#property indicator_color1  clrDeepPink, clrDodgerBlue
#property indicator_width1   1

#property indicator_label2  "primeira barra do padrão"
#property indicator_type2   DRAW_FILLING
#property indicator_color2  clrDeepPink, clrDodgerBlue
#property indicator_width2   1

#property indicator_label3  "Nível de sinal"
#property indicator_type3   DRAW_COLOR_LINE
#property indicator_style3  STYLE_SOLID
#property indicator_color3  clrDeepPink , clrDodgerBlue
#property indicator_width3  2

#property indicator_label4  "Nível de entrada"
#property indicator_type4   DRAW_COLOR_LINE
#property indicator_style4   STYLE_DASHDOT
#property indicator_color4  clrDeepPink, clrDodgerBlue
#property indicator_width4   2

#property indicator_label5  "Stop Loss"
#property indicator_type5   DRAW_LINE
#property indicator_style5  STYLE_DASHDOTDOT
#property indicator_color5  clrCrimson
#property indicator_width5  1

#property indicator_label6  "Take Profit"
#property indicator_type6   DRAW_LINE
#property indicator_style6  STYLE_DASHDOTDOT
#property indicator_color6  clrLime
#property indicator_width6  1

Permitimos que o usuário: desative o preenchimento da primeira barra do padrão diário, selecione as opções de notificação sobre o sinal e limite a profundidade de layout do histórico. Aqui, a partir do módulo de sinal, habilitamos todas as configurações do sistema de negociação. Para fazer isso, será preciso calcular antecipadamente as variáveis envolvidas no módulo, mesmo se apenas algumas delas são utilizadas no Expert Advisor e não são necessárias no indicador:

#include <Expert\Signal\Signal_80-20 .mqh> // módulo de sinal da Estratégia de Negociação '80-20'

input bool    Show_Outer = true;       // primeira barra do padrão: Mostrar todo o intervalo?
input bool    Show_Inner = true ;      // primeira barra do padrão: Exibir a região interna?
input bool    Alert_Popup = true;     // Alerta: Exibir a janela pop-up?
input bool    Alert_Email = false;     // Alerta: Enviar e-mail?
input string  Alert_Email_Subj = "";  // Alerta: Tema mensagem e-mail
input bool    Alert_Push = true;      // Alerta: Enviar notificação push?

input uint  Bars_Limit = 2000;  // Profundidade do layout do histórico (nas barras do timeframe atual)



ENUM_LOG_LEVEL  Log_Level = LOG_LEVEL_NONE;  // Modo detalhado
double
  buff_1st_Bar_Inner[], buff_1st_Bar_Inner_Zero[], // buffers para traçado dos internos 60% da primeira barra do padrão
  buff_1st_Bar_Inner[], buff_1st_Bar_Inner_Zero[], // buffers para traçado dos internos 60% da primeira barra do padrão
  buff_Signal[], buff_Signal_Color[], // buffers da linha de sinal
  buff_Entry[], buff_Entry_Color[], // buffers da linha da ordem pendente
  buff_SL[], buff_TP[], // buffers das linhas de StopLoss e TakeProfit
  gd_Extremum_Break = 0 // TS_8020_Extremum_Break nos preços do instrumento
;
int
  gi_D1_Average_Period = 1, // valor correto para TS_8020_D1_Average_Period
  gi_Min_Bars = WRONG_VALUE // número mínimo obrigatório de barras para cálculo
;



int OnInit() {
  // verificação do parâmetro introduzido TS_8020_D1_Average_Period:
  gi_D1_Average_Period = int(fmin(1, TS_8020_D1_Average_Period));
   // transferência de pontos para os preços do instrumento:
  gd_Extremum_Break = TS_8020_Extremum_Break * _Point;
  // número mínimo obrigatório de barras para cálculo = número de barras do timeframe atual em dias
  gi_Min_Bars = int(86400 / PeriodSeconds());
  
  // finalidade dos buffers do indicador:
  
  // re(c)tângulo de todo o intervalo da primeira barra
  SetIndexBuffer (0, buff_1st_Bar_Outer, INDICATOR_DATA);
    PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0);
  SetIndexBuffer(1, buff_1st_Bar_Outer_Zero, INDICATOR_DATA);
  
  // re(c)tângulo da região interna da primeira barra
  SetIndexBuffer(2, buff_1st_Bar_Inner, INDICATOR_DATA);
    PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0);
  SetIndexBuffer(3, buff_1st_Bar_Inner_Zero, INDICATOR_DATA);
  
  // linha de sinal
  SetIndexBuffer(4, buff_Signal, INDICATOR_DATA);
    PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0);
  SetIndexBuffer(5, buff_Signal_Color, INDICATOR_COLOR_INDEX);
  
  // Linha de colocação da ordem pendente
  SetIndexBuffer(6, buff_Entry, INDICATOR_DATA);
    PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0);
  SetIndexBuffer(7, buff_Entry_Color, INDICATOR_COLOR_INDEX);
  
  // Linha SL
  SetIndexBuffer(8, buff_SL, INDICATOR_DATA);
    PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, 0);
  
  // Linha TP
   SetIndexBuffer(9, buff_TP, INDICATOR_DATA);
    PlotIndexSetDouble(5, PLOT_EMPTY_VALUE , 0);
  
  IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
   IndicatorSetString(INDICATOR_SHORTNAME, "EN 80-20");
  
  return(INIT_SUCCEEDED);
}

Na função padrão OnCalculate, colocamos o código principal do programa, em outras palavras, organizamos o ciclo que irá iterar - do passado para o futuro - as barras do timeframe atual e verificar a presença de sinal nelas usando uma função a partir do módulo de sinal. Declaramos e inicializamos previamente as variáveis necessárias usando os valores iniciais. Tendo em conta o limite - definido pelo usuário - de profundidade de histórico (Bars_Limit), estabelecemos a barra mais antiga do ciclo para o primeiro cálculo. Para as chamadas subsequentes, nós vamos recalcular todas as barras do dia atual, uma vez que o padrão de dois barras, de fato, pertence ao gráfico D1, independentemente do timeframe atual.

Além disso, vamos ter que tomar medidas: se, após a inicialização, não termos limpado previamente os buffers de indicador, então, ao alternar gráficos ou mudar de símbolo, no novo gráfico irão permanecer áreas pintadas não atualizadas. É por isso que, após a inicialização do indicador, será necessário vincular a limpeza de buffers à primeira chamada da função OnCalculate. Como a variável padrão prev_calculated contém zero tanto ao chamar pela primeira vez a função quanto "ao alterar a soma de verificação", será impossível determinar com ajuda dela que se trata da primeira chamada. Para resolver este problema criamos uma estrutura - independente do zeramento da variável prev_calculated - que vai armazenar e processar os dados úteis usados frequentemente nos indicadores:

- sinalizador da primeira execução da função OnCalculate;

- contador de barras contadas não-zerável, ao alterar a soma de verificação;

- sinalizador de alteração da soma de verificação;

- sinalizador do início da nova barra;

- tempo de início da barra atual.

A estrutura que junta todos estes dados será declarada e poderá coletar ou fornecer informações em/de quaisquer funções padrão ou personalizadas. Um nome bastante adequado para a essência desse programa seria "Duende amigo" (Brownie). Podemos colocá-lo no final do código do indicador. Assim, declaramos um objeto-estrutura global chamado go_Brownie:

struct BROWNIE {                // Duende amigo: estrutura para armazenar e desenvolver dados globalmente
  datetime  t_Last_Bar_Time;    // tempo da última barra processada
  int         i_Prew_Calculated; // número de barras contadas
  bool      b_First_Run;        // sinalizador da primeira execução
  bool      b_History_Updated;  // sinalizador da atualização do histórico
  bool      b_Is_New_Bar;       // sinalizador da abertura da nova barra
  
  BROWNIE() { // construtor
    // valores por padrão:
    t_Last_Bar_Time = 0;
    i_Prew_Calculated = WRONG_VALUE ;
    b_First_Run = b_Is_New_Bar = true;
    b_History_Updated = false;
  }
  
  void f_Reset(bool b_Reset_First_Run = true) { // zeramento das variáveis
    // valores por padrão:
    t_Last_Bar_Time = 0;
    i_Prew_Calculated = WRONG_VALUE;
    if(b_Reset_First_Run) b_First_Run = true; // zeramento, se houver permissão
    b_Is_New_Bar = true;
    b_History_Updated = false;
  }
  
  void f_Update(int i_New_Prew_Calculated = WRONG_VALUE) { // zeramento das variáveis
    // sinalizador da primeira chamada da função padrão OnCalculate
    if(b_First_Run && i_Prew_Calculated > 0) b_First_Run = false;
    
    // nova barra?
    datetime t_This_Bar_Time = TimeCurrent() - TimeCurrent() % PeriodSeconds();
    b_Is_New_Bar = t_Last_Bar_Time == t_This_Bar_Time;
    
    // atualizar o tempo da barra atual?
    if(b_Is_New_Bar) t_Last_Bar_Time = t_This_Bar_Time;
    
    if(i_New_Prew_Calculated > -1) {
      // existem algumas alterações no histórico?
      b_History_Updated = i_New_Prew_Calculated == 0 && i_Prew_Calculated > WRONG_VALUE;
      
      // usar prew_calculated, se for a primeira chamada de OnCalculate
      if(i_Prew_Calculated == WRONG_VALUE ) i_Prew_Calculated = i_New_Prew_Calculated;
      // ou se não houver atualização no histórico
      else if(i_New_Prew_Calculated > 0) i_Prew_Calculated = i_New_Prew_Calculated;
    }
  }
};
BROWNIE go_Brownie;

Prevemos o informamento do 'Duende amigo' sobre o evento de anulação da inicialização do indicador:

void OnDeinit(const int reason) {
   go_Brownie.f_Reset(); // notificar o Duente amigo
}

É possível estender a coleta de informações salvadas pelo 'Duende amigo', caso funções personalizadas ou classes forem precisadas, por exemplo, nos preços, volumes ou tamanho de spread da barra atual (Open, High, Low, Close, tick_volume, volume, spread). É mais fácil pegar os dados prontos a partir da função OnCalculate e transferi-los através do 'Duende amigo' do que utilizar as funções de cópia do Timeseries (CopyOpen, CopyHigh ou CopyRates) — isso vai poupar recursos da CPU e eliminar a necessidade de organizar o tratamento de erros destas funções da linguagem.

Voltemos às principais funções de visualização. A declaração de variáveis e preparação de matrizes usando a estrutura go_Brownie ficará assim:

go_Brownie.f_Update(prev_calculated); // alimentar com informações o Duende amigo

int
  i_Period_Bar = 0, // contador auxiliar
  i_Current_TF_Bar = rates_total - int(Bars_Limit) // índice da barra de início de ciclo do timeframe atual
;
static datetime st_Last_D1_Bar = 0; // tempo da última barra D1 a partir do par processado (segunda barra do padrão)
static int si_1st_Bar_of_Day = 0; // índice da primeira barra do dia atual

if(go_Brownie.b_First_Run) { // se este for a primeira execução
  // limpar o buffer ao reinicializar:
   ArrayInitialize(buff_1st_Bar_Inner, 0); ArrayInitialize(buff_1st_Bar_Inner_Zero, 0);
  ArrayInitialize (buff_1st_Bar_Outer, 0); ArrayInitialize(buff_1st_Bar_Outer_Zero, 0);
  ArrayInitialize(buff_Entry, 0 ); ArrayInitialize(buff_Entry_Color, 0);
  ArrayInitialize(buff_Signal, 0); ArrayInitialize (buff_Signal_Color, 0);
  ArrayInitialize(buff_TP, 0);
  ArrayInitialize(buff_SL, 0);
  st_Last_D1_Bar = 0;
  si_1st_Bar_of_Day = 0;
} else { // este não é a primeira execução
  datetime t_Time = TimeCurrent();
  // profundidade mínimo de recálculo - a partir do dia anterior:
  i_Current_TF_Bar = rates_total - Bars(_Symbol, PERIOD_CURRENT, t_Time - t_Time % 86400, t_Time) - 1;
}
ENUM_ENTRY_SIGNAL e_Signal = ENTRY_UNKNOWN; // sinal
double
  d_SL = WRONG_VALUE, // nível SL
  d_TP = WRONG_VALUE, // nível TP
  d_Entry_Level = WRONG_VALUE, // nível de entrada
  d_Range_High = WRONG_VALUE, d_Range_Low = WRONG_VALUE // limites do intervalo da primeira barra do padrão
;
datetime
  t_Curr_D1_Bar = 0, // tempo da barra atual D1 (segunda barra do padrão)
  t_D1_Bar_To_Fill = 0 // tempo da barra D1 que é preciso pintar (primeira barra do padrão)
;

// verificamos que o índice da barra inicial de recálculo esteja dentro do limite aceitável:
i_Current_TF_Bar = int(fmax(0 , fmin(i_Current_TF_Bar, rates_total - gi_Min_Bars)));

while(++i_Current_TF_Bar < rates_total && !IsStopped()) { // iteração do timeframe atual
  // aqui vai estar o ciclo principal do programa
}

Ao iterar as barras do timeframe atual, vamos verificar a presença do sinal:

e_Signal = fe_Get_Entry_Signal(Time[i_Current_TF_Bar], d_Entry_Level, d_SL, d_TP, d_Range_High, d_Range_Low);
if(e_Signal > 1) continue; // no dia a que pertence a esta barra, não exites sinal

Se, por um lado, existir o sinal e, por outro, ele ser a primeira barra do novo dia, será necessário organizar o preenchimento do intervalo da barra diária anterior. O valor da variável t_D1_Bar_To_Fill do tipo datetime será o sinalizador; se ela não tiver definido o valor WRONG_VALUE, nesta barra o preenchimento não será´necessário. Pressupõe-se que a partir desta primeira barra deve começar a linha de sinal, mas para melhor compreensão do layout, vamos estendê-lo até a última barra do dia anterior. Como os cálculos do nível de sinal, cores das linhas e as áreas sombreadas para barras de alta e baixa são diferentes, vamos fazer mais dois blocos semelhantes entre si:

t_Curr_D1_Bar = Time[i_Current_TF_Bar] — Time[i_Current_TF_Bar] % 86400; // inicio do dia ao qual pertence esta barra
if(st_Last_D1_Bar < t_Curr_D1_Bar) { // esta é a barra do novo dia
  t_D1_Bar_To_Fill = Time[i_Current_TF_Bar — 1] — Time[i_Current_TF_Bar — 1] % 86400;
  si_1st_Bar_of_Day = i_Current_TF_Bar;
}
else t_D1_Bar_To_Fill = WRONG_VALUE; // barra do dia anterior, não é necessário um novo preenchimento
st_Last_D1_Bar = t_Curr_D1_Bar; // lembramos

if(t_D1_Bar_To_Fill != WRONG_VALUE) { // nova barra D1
  // Preenchimento da barra D1 do dia anterior:
  i_Period_Bar = i_Current_TF_Bar;
  if(d_Entry_Level < d_Range_High) { // barra de baixa D1
    if(Show_Outer) while(--i_Period_Bar > 0) { // todo o intervalo
      if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
      buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_Low;
      buff_1st_Bar_Outer[i_Period_Bar] = d_Range_High;
    }
    if(Show_Inner) { // área interna
      i_Period_Bar = i_Current_TF_Bar;
      while(--i_Period_Bar > 0) {
        if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
        buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_Low + 0.2 * (d_Range_High — d_Range_Low);
        buff_1st_Bar_Inner[i_Period_Bar] = d_Range_High — 0.2 * (d_Range_High — d_Range_Low);
      }
    }
    // início da linha de sinal — a partir da última barra do dia anterior
    buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar — 1] = d_Range_Low — gd_Extremum_Break;
    buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar — 1] = 0;
  } else { // barra de alta D1
    if(Show_Outer) while(--i_Period_Bar > 0) { // todo o intervalo
      if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
      buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_High;
      buff_1st_Bar_Outer[i_Period_Bar] = d_Range_Low;
    }
    if(Show_Inner) { // área interna
      i_Period_Bar = i_Current_TF_Bar;
      while(--i_Period_Bar > 0) {
        if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
        buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_High — 0.2 * (d_Range_High — d_Range_Low);
        buff_1st_Bar_Inner[i_Period_Bar] = d_Range_Low + 0.2 * (d_Range_High — d_Range_Low);
      }
    }
    // início da linha de sinal — a partir da última barra do dia anterior
    buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar — 1] = d_Range_High + gd_Extremum_Break;
    buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar — 1] = 1;
  }
} else continue;

Aqui (dentro do ciclo de iteração de barras do timeframe atual) vamos organizar o desenho do resto de linhas de layout. Deixe-me lembrá-lo, a linha de sinal deve terminar na barra, onde ela foi tocada pelo preço. Nesta barra deve começar a linha da ordem pendente. Ela deve terminar na barra de contato com o preço, e nesta mesma barra devem começar as linhas Take Profit e Stop Loss. Na barra de contato de uma delas com o preço, o layout deste padrão será finalizado:

// Linha de sinal até à barra pela qual foi atravessada:
i_Period_Bar = i_Current_TF_Bar;
if(d_Entry_Level < d_Range_High) { // barra de baixa D1
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Signal[i_Period_Bar] = d_Range_Low — gd_Extremum_Break;
    buff_Signal_Color[i_Period_Bar] = 0;
    if(d_Range_Low — gd_Extremum_Break >= Low[i_Period_Bar]) break;
  }
} else { // barra de alta D1
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Signal[i_Period_Bar] = d_Range_High + gd_Extremum_Break;
    buff_Signal_Color[i_Period_Bar] = 1;
    if(d_Range_High + gd_Extremum_Break <= High[i_Period_Bar]) break;
  }
}

// Linha de entrada até à barra pela qual foi atravessada:
if(d_Entry_Level < d_Range_High) { // barra de baixa D1
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Entry[i_Period_Bar] = d_Range_Low;
    buff_Entry_Color[i_Period_Bar] = 0;
    if(d_Range_Low <= High[i_Period_Bar]) {
      if(buff_Entry[i_Period_Bar — 1] == 0.) {
        // início e final na mesma barra, estendemos 1 barra no passado
        buff_Entry[i_Period_Bar — 1] = d_Range_Low;
        buff_Entry_Color[i_Period_Bar — 1] = 0;
      }
      break;
    }
  }
} else { // barra de baixa D1
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Entry[i_Period_Bar] = d_Range_High;
    buff_Entry_Color[i_Period_Bar] = 1;
    if(d_Range_High >= Low[i_Period_Bar]) {
      if(buff_Entry[i_Period_Bar — 1] == 0.) {
        // início e final na mesma barra, estendemos 1 barra no passado
        buff_Entry[i_Period_Bar — 1] = d_Range_High;
        buff_Entry_Color[i_Period_Bar — 1] = 1;
      }
      break;
    }
  }
}

// Linhas TP e SL até à barra pela qual foi atravessada uma delas:
if(d_Entry_Level < d_Range_High) { // barra de baixa D1
  // SL igual ao mínimo desde o início do dia:
  d_SL = Low[ArrayMinimum(Low, si_1st_Bar_of_Day, i_Period_Bar — si_1st_Bar_of_Day)];
  
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_SL[i_Period_Bar] = d_SL;
    buff_TP[i_Period_Bar] = d_TP;
    if(d_TP <= High[i_Period_Bar] || d_SL >= Low[i_Period_Bar]) {
      if(buff_SL[i_Period_Bar — 1] == 0.) {
        // início e final na mesma barra, estendemos 1 barra no passado
        buff_SL[i_Period_Bar — 1] = d_SL;
        buff_TP[i_Period_Bar — 1] = d_TP;
      }
      break;
    }
  }
} else { // barra de alta D1
  // SL igual ao máximo desde o início do dia:
  d_SL = High[ArrayMaximum(High, si_1st_Bar_of_Day, i_Period_Bar — si_1st_Bar_of_Day)];
  
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_SL[i_Period_Bar] = d_SL;
    buff_TP[i_Period_Bar] = d_TP;
    if(d_SL <= High[i_Period_Bar] || d_TP >= Low[i_Period_Bar]) {
      if(buff_SL[i_Period_Bar — 1] == 0.) {
        // início e final na mesma barra, estendemos 1 barra no passado
        buff_SL[i_Period_Bar — 1] = d_SL;
        buff_TP[i_Period_Bar — 1] = d_TP;
      }
      break;
    }
  }
}

Fora do ciclo, colocamos o código de chamada da função de alerta sobre o sinal f_Do_Alert. A função pode trabalhar com arquivos de áudio, quer dizer, para sinais de compra e venda, é possível adicionar, nas configurações personalizadas, a ativação desta opção e a escolha de arquivos separados. Listagem da função:

void f_Do_Alert(                  // Função de distribuição de sinais e alertas
  string  s_Message,              // texto para o alerta
  bool    b_Alert = true,         // exibir janela pop-up?
  bool    b_Sound = false,        // reproduzir o arquivo de som?
  bool    b_Email = false,        // enviar e-mail?
  bool    b_Notification = false, // enviar notificação push?
  string  s_Email_Subject = "",   // tema para a mensagem do e-mail
  string  s_Sound = "alert.wav"   // arquivo de som
) {
  static string ss_Prev_Message = "houve silêncio"; // texto do alerta anterior
  static datetime st_Prev_Time; // tempo da barra do alerta anterior
  datetime t_This_Bar_Time = TimeCurrent() — PeriodSeconds() % PeriodSeconds(); // tempo da barra anterior
  
  if(ss_Prev_Message != s_Message || st_Prev_Time != t_This_Bar_Time) {
    // alerta de outra e/ou da primeira nesta barra
    
    // lembre-se:
    ss_Prev_Message = s_Message;
    st_Prev_Time = t_This_Bar_Time;
    
    // gerar cadeia de caracteres da mensagem:
    s_Message = StringFormat("%s | %s | %s | %s",
      TimeToString(TimeLocal(), TIME_SECONDS), // tempo local
      _Symbol, // símbolo
      StringSubstr(EnumToString(ENUM_TIMEFRAMES(_Period)), 7), // timeframe
      s_Message // mensagem
    );
    
    // enviar sinal de alerta:
    if(b_Alert) Alert(s_Message);
    if(b_Email) SendMail(s_Email_Subject + " " + _Symbol, s_Message);
    if(b_Notification) SendNotification(s_Message);
    if(b_Sound) PlaySound(s_Sound);
  }
}

Este são o código de autenticação da chamada desta função e a formação do texto de mensagem para ela, localizados no corpo do programa, antes de o manipulador de eventos OnCalculate finalizar:

// alerta
i_Period_Bar = rates_total — 1; // barra atual

if(Alert_Popup + Alert_Email + Alert_Push == 0) return(rates_total); // tudo desativado
if(buff_Signal[i_Period_Bar] == 0) return(rates_total); // pronto ou não há nada que apanhar
if(
  buff_Signal[i_Period_Bar] > High[i_Period_Bar]
  ||
  buff_Signal[i_Period_Bar] < Low[i_Period_Bar]
) return(rates_total); // não existe contato da linha de sinal

// texto da mensagem:
string s_Message = StringFormat("TS 80-20: precisa %s @ %s, TP: %s, SL: %s",
  buff_Signal_Color[i_Period_Bar] > 0 ? "BuyStop" : "SellStop",
  DoubleToString(d_Entry_Level, _Digits),
  DoubleToString(d_TP, _Digits),
  DoubleToString(d_SL, _Digits)
);
// alerta:
f_Do_Alert(s_Message, Alert_Popup, false, Alert_Email, Alert_Push, Alert_Email_Subj);

return(rates_total); // finalização do trabalho de OnCalculate

Todo o código-fonte do indicador está no arquivo anexado com o nome TS_80-20.mq5. Quanto ao seu uso, podemos dizer que seu layout tem uma melhor apresentação nos gráficos de minutos.

É importante levar em conta, ao usar este layout, que o indicador utiliza os dados das barras, em vez de uma sequência de ticks dentro das barras. Ou seja, se, na mesma barra, o preço atravessar várias linhas do layout (por exemplo, as linas Take Profit e Stop Loss), não sempre será possível determinar qual delas foi atravessada primeiro. Outro ponto desfavorável está relacionado com o fato de as barras de início e final de linha não coincidirem, caso contrário as linhas a partir buffer de tipo DRAW_LINE e DRAW_COLOR_LINE simplesmente não seriam visíveis para o usuário. Embora estas características tornam o layout não totalmente preciso, ele continua sendo ainda muito convincente.


Expert Advisor para teste da Estratégia de Negociação '80-20'

Expert Advisor para teste de estratégias do livro Street Smarts: High Probability Short-Term Trading Strategies descrito em detalhe no primeiro artigo. Vamos fazer nele duas alterações significativas. O primeiro tem a ver com o fato de que o módulo de sinal será usado também no indicador e, portanto, ele fará nele um cálculo racional de níveis de negociação. Nós já temos feito isto acima: a função fe_Get_Entry_Signal,, além do estado do sinal, retorna os níveis de colocação das ordens Stop Loss e Take Profit. Por isso, removemos, a partir da versão anterior do Expert Advisor, a parte relevante do código, adicionamos as variáveis para recepção do código, a partir da função, e editamos a chamada desta função. Eu não vou fazer a listagem dos blocos novos e velhos do código, você pode vê-los no arquivo anexado (linhas de 128 a 141).

Como a Estratégia de Negociação tem de lidar com a tendência de curto prazo, ao contrário dos dois anteriores, justifica-se uma segunda adição significativa ao código base do Expert Advisor. Ela assume que o recuo vai acontecer uma vez durante o dia e é improvável que seja repetido. Isso significa que o robô deve fazer apenas uma entrada e ignorar o sinal existente todo o tempo restante até o dia seguinte. A maneira mais simples de implementar isso consiste em usar um sinalizador especial, por exemplo, uma variável estática o globar do tipo bool na memória do programa. Mas se o trabalho do Expert Advisor for interrompido por qualquer motivo, em seguida, perde-se o valor do sinalizador. Portanto, após reiniciado o Expert Advisor, dever haver alguma chance de verificar se o sinal de hoje foi modificado anteriormente. Para este efeito, é possível analisar o histórico de negociação de hoje, armazenar a data da última entrada nas variáveis globais do terminal. Nós vamos usar a segunda opção, pois é muito mais fácil de implementar.

Nós damos ao usuário a capacidade de controlar a opção 'uma entrada por dia', bem como definir o identificador de cada versão do robô em execução, uma vez que ele é necessário para a utilização de variáveis globais de nível de terminal:

input bool  One_Trade = false;    // Uma posição por dia
input uint  Magic_Number = 2016;  // Identificado do Expert Advisor(Magic Number)

Para implementação da opção 'uma entrada por dia', adicionamos a declaração das variáveis necessárias ao bloco de determinação de variáveis globais do programa. Na função OnInit, vamos inicializá-las:

string
  gs_Prefix // identificador de nomes (super)de variáveis globais
;
bool
  gb_Position_Today = false,
  gb_Pending_Today = false
;

int OnInit() {

...

  // Criação do prefixo de nomes (super)de variáveis globais:
  gs_Prefix = StringFormat("SSB %s %u %s", _Symbol, Magic_Number, MQLInfoInteger(MQL_TESTER) ? "t " : "");
  
  // Hoje o robô trabalhou com ordens pendentes ou de mercado?
  gb_Position_Today = int(GlobalVariableGet(gs_Prefix + "Last_Position_Date")) == TimeCurrent() — TimeCurrent() % 86400;
  gb_Pending_Today = int(GlobalVariableGet(gs_Prefix + "Last_Pending_Date")) == TimeCurrent() — TimeCurrent() % 86400;

...
}

Aqui o robô considera os valores das variáveis globais e compara o tempo registrado nelas com o tempo de início do dia, desse modo determina se o sinal de hoje já foi trabalhado. Organizamos o registro de tempo nessas variáveis em dois lugares; no código de colocação de ordem pendente adicionamos o bloco apropriado:

if(i_Try != -10) { // falhou a colocação da ordem pendente
  if(Log_Level > LOG_LEVEL_NONE) Print("Erro de colocação da ordem pendente");
  // a distância a partir do preço atual é insuficiente:(
  if(Log_Level > LOG_LEVEL_ERR)
    PrintFormat("É impossível colocar a ordem pendente para o nível %s. Bid: %s Ask: %s StopLevel: %s",
      DoubleToString(d_Entry_Level, _Digits),
      DoubleToString(go_Tick.bid, _Digits),
      DoubleToString(go_Tick.ask, _Digits),
      DoubleToString(gd_Stop_Level, _Digits)
    );
} else { // conseguiu
  // atualizar o sinalizador:
  GlobalVariableSet( // nas variáveis globais do terminal
    gs_Prefix + "Last_Pending_Date",
    TimeCurrent() — TimeCurrent() % 86400
  );
  gb_Pending_Today = true; // nas variáveis globais do programa
}

  Colocamos o segundo bloque depois do código que define a posição recentemente aberta:

if(PositionSelect(_Symbol)) { // existe posição aberta
        if(PositionGetDouble(POSITION_SL) == 0.) { // nova posição
                
                if(!gb_Position_Today) { // esta é a primeira posição de hoje
                        // atualizar o sinalizador:
                        GlobalVariableSet( // nas variáveis globais do terminal
                                gs_Prefix + "Last_Position_Date",
                                TimeCurrent() — TimeCurrent() % 86400
                        );
                        gb_Position_Today = true; // nas variáveis globais do programa
                }
...

Não exitem outras alterações significativas no código da versão anterior do Expert Advisor. No anexo ao artigo, você pode encontrar o código fonte da nova versão em sua forma final.

 

Teste de estratégia com base no histórico de dados

Os autores do sistema de negociação como prova de sua viabilidade citam padrões em gráficos de final do século passado, no entanto nós precisamos verificar sua relevância no mercado de hoje. Para os testes, eu tomei o par de moedas mais popular no mercado Forex EURUSD, bem como o mais volátil USDJPY e o par de metais XAUUSD. Eu aumentei 10 vezes os recuos especificados por Raschke e Connors, porque naquela época eram usadas cotações de quatro dígitos, e ou testei o Expert Advisor nas de cinco dígitos. Como não havia nenhuma orientação feita pelos autores quanto aos parâmetros de trail, eu escolhi os que pareciam mais adequados para o timeframe diário e volatilidade do instrumento. O mesmo se aplica ao algoritmo de cálculo de Take Profit adicionado às suas regras originais, quer dizer, o coeficiente para seu cálculo foi selecionado arbitrariamente, sem uma otimização profunda.

Gráfico de mudanças do balanço durante o teste no histórico de cinco anos EURUSD com regras originais (sem Take Profit):

EURUSD D1 5 anos

Com as mesmas configurações adicionamos o Take Profit:

EURUSD D1 5 anos

Gráfico de mudanças do balanço durante o teste no histórico de cinco anos USDJPY:

USDJPY D1 5 anos

O mesmo instrumento e timeframe com as mesmas configurações, mas com adição de Take Profit:

USDJPY D1 5 anos

As regras originais em cotações diárias de ouro nos últimos 4 anos mostra o seguinte gráfico de mudança de balanço:

XAUUSD D1 4 anos

Informações completas sobre cada teste das configurações do robô podem ser encontradas no arquivo anexado ao artigo, nele encontrará relatórios completos de cada teste. 


Conclusão

As regras programadas no módulo de sinal correspondem ao sistema de negociação 80-20' descrito a partir do livro de Linda Raschke e Laurence Connors Street Smarts: High Probability Short-Term Trading Strategies. Há uma pequena extensão das regras de direitos autorais. Estas ferramentas (robô e indicador) devem ajudar a fazer suas próprias conclusões sobre a relevância da Estratégia de Negociação no mercado de hoje. Na minha humilde opinião, ela precisa de uma atualização séria. Neste artigo eu tentei falar em detalhe sobre a criação do código do módulo de sinal e seu robô e indicador, e pois espero que isto ajude aqueles que decidam empreender tal modernização. Além da atualização das regras, você pode tentar escolher os instrumentos que melhor encaixem no sistema de negociação, as melhores configurações do sinal e o melhor acompanhamento de posições. 


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

Arquivos anexados |
Reports.zip (597.76 KB)
MQL5.zip (124.99 KB)
Interfaces Gráficas VIII: O Controle Navegador de Arquivos (Capítulo 3) Interfaces Gráficas VIII: O Controle Navegador de Arquivos (Capítulo 3)
Nos capítulos anteriores da oitava parte da série, nossa biblioteca foi reforçada por várias classes para o desenvolvimento de ponteiros para o cursor do mouse, calendários e as listas hierárquicas. O presente artigo lida com o controle navegador de arquivos que também pode ser utilizado como parte de uma interface gráfica na aplicação MQL.
Interfaces Gráficas VIII: O Controle Lista Hierárquica (Capítulo 2) Interfaces Gráficas VIII: O Controle Lista Hierárquica (Capítulo 2)
O capítulo anterior da parte VIII da série Interfaces Gráficas, nós focamos sobre os elementos do calendário estático e suspenso. O segundo capítulo será dedicado a um elemento igualmente complexo - uma lista hierárquica, que está incluída em cada biblioteca multifuncional, usada para a criação de interfaces gráficas. A lista hierárquica implementada neste artigo contém várias configurações flexíveis e modos, permitindo assim ajustar este elemento de controle às suas necessidades.
Interfaces Gráficas IX: O Controle Seletor de Cores (Capítulo 1) Interfaces Gráficas IX: O Controle Seletor de Cores (Capítulo 1)
Com este artigo, nós começamos o capítulo nove da série de artigos dedicados à criação das interfaces gráficas nos terminais de negociação MetaTrader. Ele consiste de dois capítulos onde são apresentados os novos elementos do controle da interface, tais como o seletor de cores, o botão do seletor de cores, a barra de progresso e o gráfico de linha.
Carteira de Investimentos no MetaTrader 4 Carteira de Investimentos no MetaTrader 4
O artigo revela a origem da Carteira de Investimentos e sua aplicação no mercado Forex. São considerados alguns modelos de carteiras de acordo com a matemática simples. O artigo contém exemplos da implementaçao prática da Carteira de Investimentos no MetaTrader 4: indicador de carteiras e um Expert Advisor para negociação semi-automatizada. São descritos tanto os elementos de estratégia de negociação, quanto as suas vantagens e desvantagens.