English Русский 中文 Español Deutsch 日本語
Como criar um indicador de gráfico não padronizado no Mercado MetaTrader

Como criar um indicador de gráfico não padronizado no Mercado MetaTrader

MetaTrader 4Exemplos | 11 agosto 2016, 12:57
3 153 0
Vladimir Karputov
Vladimir Karputov

Índice

 

De candles japoneses para gráficos Renko

O tipo mais popular de gráficos entre os traders até agora são os candles Japoneses que ajudam a avaliar a atual situação do mercado com facilidade. Gráficos de Candles dão uma boa apresentação visual da evolução dos preços dentro de um período de tempo, refletido num único candles. Mas alguns traders acreditam que seja uma desvantagem os gráficos usarem um componente de tempo, então eles preferem lidar apenas com a alteração do preço. Assim são os gráficos "Point & Figure", "Renko", "Kagi", "Range bars", gráfico equivolume e outros que apareceram pela primeira vez. 

Todos estes gráficos podem ser obtidos no MetaTrader 4 por meio de gráficos off-line, programação em MQL4 e uma disposição razoável. Existe uma forma de criar gráficos com produtos sintéticos personalizados (que as corretoras não têm ou que não existem) e timeframes não padronizados que não estão disponíveis na plataforma. A maioria dos desenvolvedores tendem a usar chamadas DLL e esquemas complicados para tal fim. Neste artigo vamos descrever como criar diferentes indicadores de complexidade do tipo "dois por um" que não exigem apenas o conhecimento da DLL, mas pode facilmente serem publicados no mercado como um produto, uma vez que são aplicações totalmente independentes e completas.

Exemplos deste artigo podem ser baixados a partir do Mercado de graça:

  • USDx Chart MT4 - o indicador "USDx Chart MT4" cria um gráfico off-line, onde o índice do dólar é desenhado em vez de barras e candles comumente usados. 
  • Renko Chart MT4 - o indicador Renko Chart MT4 cria um gráfico Renko off-line onde todas as barras são mostradas como tijolos Renko. Tijolos não tem sombras e o tamanho de um tijolo é especificado nas definições.

Não existe a necessidade de um indicador criar um gráfico off-line com símbolos não padronizado e/ou período chamando um DLL, as coisas se resolvem automaticamente com o MQL4. Portanto, o método a seguir de funcionamento terá que ser implementado: o mesmo indicador pode operar gráficos on-line e off-line. Porém ele terá de ajustar a sua funcionalidade, dependendo do modo dos gráficos utilizados.

Para gráficos on-line, o indicador funciona no modo de manutenção: ele recolhe e reúne cotações, cria um gráfico off-line (com ambos os períodos padrão e não padronizados), e atualiza ele. Para gráficos off-line, o indicador opera do mesmo jeito que qualquer outro indicador - analisa cotações e constrói vários objetos e figuras. Por favor, note que todos os exemplos do artigo baseiam-se no nosso novo script PeriodConverter.mq4.


1. Indicador "IndCreateOffline" para a criação de um gráfico off-line

Nós vamos chamar o indicador IndCreateOffline. Ele terá apenas um parâmetro de entrada para alterar o período gráfico off-line. Nós falaremos sobre isso mais adiante. O indicador IndCreateOffline irá realizar uma única tarefa - criar o arquivo *.hst e abrir o gráfico off-line.

Duas funções básicas serão utilizados no indicador. A primeira função é utilizada apenas uma vez - para criar o arquivo *.hst e seu cabeçalho. A segunda função é necessária para escrever as cotações no arquivo *.hst.

1.1. Editando a linha cabeçalho do indicador

Mesmo que se você sinta preguiça de fazê-lo de imediato, ainda é obrigatóri adicionar a descrição do arquivo primeiramente. Depois de algum tempo ele vai nos ajudar a lembrar o propósito do indicador.

#property version   "1.00"
#property description "O indicador cria um gráfico off-line"
#property strict
#property indicator_chart_window

Teoricamente, o gráfico off-line pode ser criado com qualquer período. No entanto, para nossa segurança, vamos restringir a imaginação fértil de um trader que poderia estar interessado um período igual a 10000000, por exemplo. Vamos entrar na enumeração, onde todas as quatro opções podem ser selecionadas - dois, três, quatro ou seis minutos:

#property indicator_chart_window
//+------------------------------------------------------------------+
//| Enumerações de períodos do gráfico off-line                      |
//+------------------------------------------------------------------+
enum ENUM_OFF_TIMEFRAMES
  {
   M2=2,                      // período M2
   M3=3,                      // período M3
   M4=4,                      // período M4
   M6=6,                      // período M6
  };
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |

O próximo passo envolve a adição de variáveis globais ao cabeçalho (não deve ser confundido com variáveis globais no terminal):

   M6=6,                      // período M6
  };
//--- parâmetros de entrada
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     crash=false;         // falso -> erro no código
int      HandleHistory=-1;    // handle para abertura do arquivo "*.hst"
datetime time0;               //
ulong    last_fpos=0;         //
long     last_volume=0;       //
int      periodseconds;       //
int      i_period;            //
MqlRates rate;                //
long     ChartOffID=-1;       // ID do gráfico off-line
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |

1.2. Verificando o tipo de gráfico

Nosso indicador IndCreateOffline precisa ser iniciado no gráfico on-line, porque esta é a única maneira de garantir os dados corretos. A fim de identificar o tipo de gráfico que o indicador é configurado, a propriedade CHART_IS_OFFLINE é utilizada. Seguindo OnCalculate(), a função IsOffline retornará o tipo de gráfico serão adicionados:

//--- Valor de retorno para prev_calculated na próxima chamada
   return(rates_total);
  }
//+------------------------------------------------------------------+ 
//| A função verifica o modo off-line do gráfico                     | 
//+------------------------------------------------------------------+ 
bool IsOffline(const long chart_ID=0)
  {
   bool offline=ChartGetInteger(chart_ID,CHART_IS_OFFLINE);
   return(offline);
  }
//+------------------------------------------------------------------+

Chamar a função IsOffline que será realizada em OnInit():

int OnInit()
  {
   if(!IsOffline(ChartID()) && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//---
   return(INIT_SUCCEEDED);
  }

Por favor, perceba que se o indicador está no gráfico on-line (IsOffline(ChartID())==false) com um período que não é igual ao PERIOD_M1, então nesse caso o valor true é atribuído à variável crash . O resultado: quando crash==true, o indicador irá simplesmente permanecer no gráfico on-line e não haverá outra atividade no mesmo. Nesse caso, a seguinte mensagem aparecerá na aba "Experts":

IndCreateOffline EURUSD,H1: The period on the online chart must be "M1"! (o período no gráfico on-line dever ser M1)


O indicador permanecerá no gráfico on-line e esperar o usuário alterar o período para PERIOD_M1.

Por que o PERIOD_M1 é tão importante para nós? Há dois pontos importantes em jogo.

Ponto 1: Formando um período total do gráfico off-line. Vamos considerar o exemplo do script PeriodConverter.mq4, onde  o período total do gráfico off-line é calculado:

#property show_inputs
input int InpPeriodMultiplier=3; // Fator multiplicador do período
int       ExtHandle=-1;
//+------------------------------------------------------------------+
//| função inicial do programa tipo script                           |
//+------------------------------------------------------------------+
void OnStart()
  {
   datetime time0;
   ulong    last_fpos=0;
   long     last_volume=0;
   int      i,start_pos,periodseconds;
   int      cnt=0;
//---- Cabeçalho do histórico
   int      file_version=401;
   string   c_copyright;
   string   c_symbol=Symbol();
   int      i_period=Period()*InpPeriodMultiplier;
   int      i_digits=Digits;
   int      i_unused[13];
   MqlRates rate;
//---  

Com estes parâmetros de entrada, o período gráfico on-line que o script está anexado ao "PERIOD_M3" iguais. Com o valor InpPeriodMultiplier=3, nossa espectativa é que o gráfico off-line tenha um período igual a 3. Porém na realidade nós recebemos o período igual a 9:

   i_period=Period()*InpPeriodMultiplier=3*3=9

Por conseguinte, a fim de obter um período igual a três, o gráfico on-line com o período igual a 1 deve ser utilizado. 

Ponto 2: escrever o histórico de ofertas ao arquivo. Dados das timeseries do array  Open[], Low[], High[], Volume[], Time[] são utilizados na geração do arquivo de histórico. Todos eles usam os dados do gráfico corrente para o período corrente. E o que pode ser mais preciso do que formar qualquer período artificial com base em dados do gráfico com "PERIOD_M1"? A sua resposta está correta: apenas o gráfico com o período PERIOD_M1. 

O indicador transformado, descrito acima, está no arquivo IndCreateOfflineStep1.mq4.

1.3. Função para criar o arquivo cabeçalho do histórico

Vamos chamar a função responsável pela criação do arquivo cabeçalho do histórico de CreateHeader():

bool CreateHeader(
   const ENUM_OFF_TIMEFRAMES offline_period // período do gráfico off-line
   );

parâmetros

offline_period

[In]  Período do gráfico off-line. 

Valor retornado

verdadeiro, se o arquivo histórico foi criado com sucesso; falso - em caso de erros presentes.

Função completa da listagem:

//+------------------------------------------------------------------+ 
//| A função verifica o modo off-line do gráfico                     | 
//+------------------------------------------------------------------+ 
bool IsOffline(const long chart_ID=0)
  {
   bool offline=ChartGetInteger(chart_ID,CHART_IS_OFFLINE);
   return(offline);
  }
//+------------------------------------------------------------------+
//| Criar o cabeçalho do histórico                                   |
//+------------------------------------------------------------------+
bool CreateHeader(const ENUM_OFF_TIMEFRAMES offline_period)
  {
//---- Cabeçalho do histórico
   int      file_version=401;
   string   c_copyright;
   string   c_symbol=Symbol();
   i_period=Period()*offline_period;
   int      i_digits=Digits;
   int      i_unused[13];
//---  
   ResetLastError();
   HandleHistory=FileOpenHistory(c_symbol+(string)i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_ANSI);
   if(HandleHistory<0)
     {
      Print("Error open ",c_symbol+(string)i_period,".hst file ",GetLastError());
      return(false);
     }
   c_copyright="(C)opyright 2003, MetaQuotes Software Corp.";
   ArrayInitialize(i_unused,0);
//--- escreva o arquivo cabeçalho do histórico
   FileWriteInteger(HandleHistory,file_version,LONG_VALUE);
   FileWriteString(HandleHistory,c_copyright,64);
   FileWriteString(HandleHistory,c_symbol,12);
   FileWriteInteger(HandleHistory,i_period,LONG_VALUE);
   FileWriteInteger(HandleHistory,i_digits,LONG_VALUE);
   FileWriteInteger(HandleHistory,0,LONG_VALUE);
   FileWriteInteger(HandleHistory,0,LONG_VALUE);
   FileWriteArray(HandleHistory,i_unused,0,13);
   return(true);
  }
//+------------------------------------------------------------------+

Além de criar o arquivo do histórico "*.hst" e o arquivo cabeçalho (primeiras strings de serviços), o handle do arquivo criado é lembrado pela variável HandleHistory na função CreateHeader().

1.4. Primeiro registro das cotações no arquivo *.hst

Depois de criar o arquivo *.hst e preencher seu cabeçalho, temos que escrever o primeiro registro para o arquivo - ou seja, o arquivo deve ser preenchido com as cotações atuais. A função FirstWriteHistory() (você pode vê-la no indicador) está encarregada do primeiro registro no arquivo.

Quando a situação com o "primeiro registro" ocorre no nosso indicador? Seria lógico supor que acontece durante o primeiro carregamento do indicador.

O primeiro carregamento pode (e deve) ser controlado no indicador pela variável prev_calculated. O valor prev_calculated==0 indica o primeiro carregamento. Mas ao mesmo tempo, o prev_calculated==0 também pode implicar que este não é o primeiro carregamento do nosso indicador e que as cotações anteriores ou perdidas foram adicionados ao histórico. Vamos discutir o que precisa ser feito quando o histórico é atualizado durante a codificação do OnCalculate().

1.5. Escrevendo cotações on-line

Depois de criar e preencher o cabeçalho do *.hst e o primeiro carregamento, podemos continuar a escrever as cotações on-line. A função CollectTicks() deve ser responsável para este objetivo (você pode vê-la no indicador).

Alterações do indicador acima descrito está no arquivo IndCreateOfflineStep2.mq4

1.6. Editando a função OnCalculate()

Vamos introduzir a variável first_start. Ele irá armazenar o valor verdadeiro após o primeiro carregamento. Em outras palavras, com first_start==true saberemos que o nosso indicador ainda não criou o arquivo *.hst.

//--- parâmetros de entrada
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     first_start=true;    // verdadeiro -> primeiro carregamento
bool     crash=false;         // falso -> erro no código

O algoritmo da função OnCalculate() do nosso indicador:

algoritmo

Fig. 1. O algoritmo da função OnCalculate() 

A versão final do indicador pode ser encontrada no arquivo IndCreateOffline.mq4

 

2. Iniciando o indicador no gráfico off-line

2.1. Atualizando o gráfico off-line com ChartSetSymbolPeriod() 

Importante: para atualizar o gráfico off-line em vez do ChartRedraw(), deve ser chamado o ChartSetSymbolPeriod() com os parâmetros atuais. A chamada do ChartSetSymbolPeriod() é realizada na função CollectTicks() com um intervalo que não pode ser maior do que uma vez a cada três segundos.

Outra nuance que deve ser considerada: a cada atualização o indicador ligado ao gráfico off-line receberá prev_calculated==0 na sua função OnCalculate(). Isto deve ser lembrado. Um método considerando essas especificações é explicado abaixo. 

2.2. O modo de manutenção 

O que nós queremos obter: o mesmo indicador deve operar em ambos os gráficos: on-line e off-line. O comportamento do indicador muda de acordo com o gráfico usado. Quando o indicador está no gráfico on-line, então a sua funcionalidade se assemelha ao indicador IndCreateOffline.mq4, considerado acima. No entanto, no gráfico off-line, ele opera como um indicador padrão.

Então, podemos chamar nosso indicador IndMACDDoubleDuty — ele será criado baseado no IndCreateOffline.mq4 mencionado e no indicador padrão MACD.mq4. Por favor, prepare o projeto de um futuro indicador: abra o arquivo IndCreateOffline.mq4 no MetaEditor, selecione "Arquivo" -> "Salvar como..." e entre com o nome do indicador — IndMACDDoubleDuty.mq4

A descrição do indicador deve ser implementada de uma só vez - o nosso indicador agora cria gráficos off-line e tem um valor double:

//+------------------------------------------------------------------+
//|                                            IndMACDDoubleDuty.mq4 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "O indicador cria um gráfico off-line."
#property description "Pode trabalhar em gráfico on-line e off-line."
#property strict
#property indicator_chart_window

O indicador irá operar no modo de manutenção ao ser iniciado no gráfico on-line com o período PERIOD_M1. Neste modo, ele executa as seguintes funções:

  • cria e preenche o arquivo *.hst ;
  • abre o gráfico off-line baseado no arquivo *.hst ;
  • adiciona o histórico ao arquivo *.hst e atualiza o gráfico com a chegada das cotações.
A fim de lembrar o modo me que o indicador opera, nós vamos introduzir a variável mode_offline:
//--- parâmetros de entrada
input ENUM_OFF_TIMEFRAMES  ExtOffPeriod=M6;
//---
bool     first_start=true;    // verdadeiro -> primeiro carregamento
bool     crash=false;         // falso -> erro no código
bool     mode_offline=true;   // verdadeiro -> no gráfico off-line
int      HandleHistory=-1;    // handle para abertura do arquivo "*.hst"
datetime time0;               //

Portanto, o OnInit() irá mudar ligeiramente:

int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//---
   return(INIT_SUCCEEDED);
  }

Fazer alterações do OnCalculate():

//+------------------------------------------------------------------+
//| Funçao de iteração do Indicador personalizado                    |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(crash)
      return(rates_total);

   if(!mode_offline) // trabalha no gráfico on-line 
     {
      if(prev_calculated==0 && first_start) // primeiro carregamento
        {
         .
         .
         .
         first_start=false;
        }
      //---
      CollectTicks();
     }
//--- Valor de retorno para prev_calculated na próxima chamada
   return(rates_total);
  }

Nesta fase, quando o indicador está anexado ao gráfico on line com o período PERIOD_M1, ele alterna para o modo de manutenção e cria um gráfico off-line.

Alterações do indicador acima descrito estão no arquivo IndMACDDoubleDutyStep1.mq4

2.3. Copiando o indicador para gráfico off-line

Nós vamos adicionar novas funcionalidades ao indicador IndMACDDoubleDuty . Agora, ao operar no modo de manutenção, o indicador deve transferir a sua cópia ao gráfico off-line criado. As seguintes funções  ChartSaveTemplate e ChartApplyTemplate serão usadas para ajudar neste objetivo. O algoritmo OnCalcalculate() ficará da seguinte forma:

algorithm_2

Fig. 2. Algoritmo da função OnCalculate()  

Vamos colocar a funcionalidade adicional no código OnCalculate():

         else
            Print(__FUNCTION__,"Opening offline chart id=",ChartOffID);
         ResetLastError();
         if(!ChartSaveTemplate(0,"IndMACDDoubleDuty"))
           {
            Print("Error save template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
         ResetLastError();
         if(!ChartApplyTemplate(ChartOffID,"IndMACDDoubleDuty"))
           {
            Print("Error apply template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
        }
      //---
      if(prev_calculated==0 && !first_start) // uma histórico mais profundo é baixado ou os brancos do histórico são preenchidos

Depois de ser colocado no gráfico on-line com o período de PERIOD_M1, o nosso indicador muda para o modo de manutenção, criando um gráfico offline e sua própria cópia ali.

Alterações do indicador acima estão no arquivo IndMACDDoubleDutyStep2.mq4  

2.4. Integração no MACD.mq4

Nosso indicador já se posicionou no gráfico off-line, mas ainda não mostra ou conta nada. Vamos mudar isso: vamos integrar o nosso indicador ao padrão MACD.mq4

Em primeiro lugar, vamos colocar os parâmetros de entrada do indicador MACD.mq4 no nosso código:

#property strict

#include <MovingAverages.mqh>

//--- configurações do indicador MACD
#property  indicator_separate_window
#property  indicator_buffers 2
#property  indicator_color1  Silver
#property  indicator_color2  Red
#property  indicator_width1  2
//--- Parâmetros do indicador
input int InpFastEMA=12;   // Período EMA rápido
input int InpSlowEMA=26;   // Período EMA lento
input int InpSignalSMA=9;  // Sinal do período SMA
//--- Buffers de indicador
double    ExtMacdBuffer[];
double    ExtSignalBuffer[];
/ --- Parâmetros da flag de entrada
bool      ExtParameters=false;
//+------------------------------------------------------------------+
//| Enumerações de períodos do gráfico off-line                      |
//+------------------------------------------------------------------+

Em seguida, adicionamos o código para OnInit(). Note que a inicialização dos parâmetros do indicador MACD deve ser realizada sob qualquer condição - tanto em gráficos off-line e on-line, mesmo quando o gráfico on-line é diferente do PERIOD_M1:

int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
//--- Inicialização do indicador MACD
   IndicatorDigits(Digits+1);
//--- Configurações do desenho
   SetIndexStyle(0,DRAW_HISTOGRAM);
   SetIndexStyle(1,DRAW_LINE);
   SetIndexDrawBegin(1,InpSignalSMA);
//--- Mapeamento dos buffers do indicador
   SetIndexBuffer(0,ExtMacdBuffer);
   SetIndexBuffer(1,ExtSignalBuffer);
//--- Nome para DataWindow e etiquetas da subjanela do indicador
   IndicatorShortName("MACD("+IntegerToString(InpFastEMA)+","+IntegerToString(InpSlowEMA)+","+IntegerToString(InpSignalSMA)+")");
   SetIndexLabel(0,"MACD");
   SetIndexLabel(1,"Signal");
//--- verifica parâmetros de entrada
   if(InpFastEMA<=1 || InpSlowEMA<=1 || InpSignalSMA<=1 || InpFastEMA>=InpSlowEMA)
     {
      Print("Wrong input parameters");
      ExtParameters=false;
      return(INIT_FAILED);
     }
   else
      ExtParameters=true;
//---
   return(INIT_SUCCEEDED);
  }

O próximo passo envolve uma ligeira alteração do código no início de OnCalculate(). Se estamos no gráfico on-line e seu período não é igual ao PERIOD_M1, devemos permitir uma oportunidade para calcular os parâmetros MACD. O que era:

const long &volume[],
                const int &spread[])
  {
//---
   if(crash)
      return(rates_total);

   if(!mode_offline) // trabalha no gráfico on-line 
     {
      if(prev_calculated==0 && first_start) // primeiro carregamento
        {

 e o que será:

const long &volume[],
                const int &spread[])
  {
//---
   if(!mode_offline && !crash) // trabalha no gráfico on-line 
     {
      if(prev_calculated==0 && first_start) // primeiro carregamento
        {

Então, vamos adicionar o código ao cálculo dos parâmetros do indicador MACD no final de OnCalculate():

         FirstWriteHistory(ExtOffPeriod);
         first_start=false;
        }
      //---
      CollectTicks();
     }
//---
   int i,limit;
//---
   if(rates_total<=InpSignalSMA || !ExtParameters)
      return(0);
//--- Última barra será recontada
   limit=rates_total-prev_calculated;
   if(prev_calculated>0)
      limit++;
//--- macd contado no 1º buffer
   for(i=0; i<limit; i++)
      ExtMacdBuffer[i]=iMA(NULL,0,InpFastEMA,0,MODE_EMA,PRICE_CLOSE,i)-
                       iMA(NULL,0,InpSlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//--- linha de sinal contado no segundo buffer
   SimpleMAOnBuffer(rates_total,prev_calculated,0,InpSignalSMA,ExtMacdBuffer,ExtSignalBuffer);
//--- Valor de retorno para prev_calculated na próxima chamada
   return(rates_total);
  }

2.5. Recálculo do indicador econômico no gráfico off-line

Deixe-me lembrá-lo um ponto descrito anteriormente na seção 2:

Outra nuance que deve ser considerada: a cada atualização o indicador ligado ao gráfico off-line receberá prev_calculated==0 na sua função OnCalculate(). Isto deve ser lembrado. Um método considerando essas especificações é explicado abaixo. 

E mais um lembrete: o valor prev_calculated==0 no OnCalculate() pode indicar duas situações:

  1. Ou é a primeira vez que o indicador foi usado;
  2. ou o histórico foi baixado.

Em ambos os casos, o indicador deve recalcular todas as barras no gráfico . Quando é necessário ser feito apenas uma vez (enquanto carrega) - é normal. Mas no gráfico off-line, receberemos o prev_calculated==0 sobre cada atualização (aproximadamente uma vez a cada 2-3 segundos), e o indicador irá recalcular todas as barras. Isto requer alto consumo de recursos. Portanto, vamos usar um truque: quando o indicador estiver no gráfico off-line, ele irá armazenar e comparar o total de barras (taxas totais variáveis) e o tempo da barra mais à direita no gráfico.

Passo 1: No início do OnCalculate() vamos declarar duas variáveis estáticas e uma pseudo variável:

                const long &volume[],
                const int &spread[])
  {
//---
   static int static_rates_total=0;
   static datetime static_time_close=0;
   int pseudo_prev_calculated=prev_calculated;
//---
   if(!mode_offline && !crash) // trabalha com o gráfico on-line 
     {

Passo 2: Vamos mudar a variável prev_calculated para pseudo_prev_calculated no bloco de código dos cálculos dos valores do indicador:

      CollectTicks();
     }
//---
   int i,limit;
//---
   if(rates_total<=InpSignalSMA || !ExtParameters)
      return(0);
//--- Última barra será recontada
   limit=rates_total-pseudo_prev_calculated;
   if(pseudo_prev_calculated>0)
      limit++;
//--- macd contado no 1º buffer
   for(i=0; i<limit; i++)
      ExtMacdBuffer[i]=iMA(NULL,0,InpFastEMA,0,MODE_EMA,PRICE_CLOSE,i)-
                       iMA(NULL,0,InpSlowEMA,0,MODE_EMA,PRICE_CLOSE,i);
//--- linha de sinal contado no segundo buffer
   SimpleMAOnBuffer(rates_total,pseudo_prev_calculated,0,InpSignalSMA,ExtMacdBuffer,ExtSignalBuffer);
//---
   if(mode_offline) // trabalha com o gráfico off-line 

Passo 3: Vamos calcular o valor para a pseudo variável, se o indicador está operando no gráfico off-line. 

      CollectTicks();
     }
//---
   if(mode_offline) // trabalha com o gráfico off-line 
     {
      if(time[0]>static_time_close) // nova barra
        {
         if(static_time_close==0)
            pseudo_prev_calculated=0;
         else // barra de pesquisa no momento time[0]==static_time_close
           {
            for(int i=0;i<rates_total;i++)
              {
               if(time[i]==static_time_close)
                 {
                  pseudo_prev_calculated=rates_total-i;
                  break;
                 }
              }
           }
        }
      else
        {
         pseudo_prev_calculated=rates_total;
        }
      //---
      static_rates_total=rates_total;
      static_time_close=time[0];
     }
//---
   int i,limit;

Nosso indicador no gráfico off-line calcula agora seus valores economicamente. Além disso, ele manipula corretamente a desconexão neste modo: barras únicas recém adicionadas são calculadas após desconectado.

2.6. Carregando dados. Gráfico off-line

Agora temos somente que descobrir o que fazer quando o histórico for baixado no gráfico on-line com o nosso indicador (prev_calculated==0). Eu proponho que resolvamos a situação através das variáveis globais do terminal. O algoritmo de funcionamento é o seguinte: se recebermos o prev_calculated==0 no gráfico on-line (não importa se foi o primeiro lançamento ou através do download do histórico), nós acabamos de criar uma variável global. O indicador no gráfico off-line verifica a existência de uma variável global sobre cada atualização: se houver uma (isso significa que o gráfico on-line tinha prev_calculated==0), então o indicador será totalmente recalculado e vai eliminar a variável global.

Nós vamos adicionar a variável ao cabeçalho do indicador, onde o nome da futura variável global do terminal será armazenada e gera este nome em OnInit():

MqlRates rate;                //
long     ChartOffID=-1;       // ID do gráfico off-line
string   NameGlVariable="";   // nome da variável global
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()
  {
   mode_offline=IsOffline(ChartID());
   if(!mode_offline && Period()!=PERIOD_M1)
     {
      Print("The period on the online chart must be \"M1\"!");
      crash=true;
     }
   NameGlVariable=Symbol()+(string)ExtOffPeriod;
//--- Inicialização do indicador MACD
   IndicatorDigits(Digits+1);
//--- Configurações do desenho

Nós vamos adicionar o código de criação de uma variável global do terminal, se a condição prev_calculated==0 for cumprida e o indicador estiver no gráfico on-line:

         if(!ChartApplyTemplate(ChartOffID,"IndMACDDoubleDuty"))
           {
            Print("Error apply template: ",GetLastError());
            first_start=false;
            crash=true;
            return(rates_total);
           }
         //---
         ResetLastError();
         if(GlobalVariableSet(NameGlVariable,0.0)==0) // cria uma nova variável global
           {
            Print("Failed to creates a new global variable ",GetLastError());
           }
        }
      //---
      if(prev_calculated==0 && !first_start) // uma histórico mais profundo é baixado ou os brancos do histórico são preenchidos
        {
         Print("a deeper history downloaded or history blanks filled. first_start=",first_start);
         if(CreateHeader(ExtOffPeriod))
            first_start=false;
         else
           {
            crash=true;
            return(rates_total);
           }
         //---
         FirstWriteHistory(ExtOffPeriod);
         first_start=false;
         //---
         ResetLastError();
         if(GlobalVariableSet(NameGlVariable,0.0)==0) // cria uma nova variável global
           {
            Print("Failed to creates a new global variable ",GetLastError());
           }
        }
      //---
      CollectTicks();

E a última alteração: a verificação de uma variável global do indicador no gráfico off-line:

      else
        {
         pseudo_prev_calculated=rates_total;
        }
      //---
      if(GlobalVariableCheck(NameGlVariable))
        {
         pseudo_prev_calculated=0;
         GlobalVariableDel(NameGlVariable);
        }
      //---
      Print("rates_total=",rates_total,"; prev_calculated=",
            prev_calculated,"; pseudo_prev_calculated=",pseudo_prev_calculated);
      static_rates_total=rates_total;
      static_time_close=time[0];
     }
//---
   int i,limit;

Versão final do indicador com as últimas mudanças: IndMACDDoubleDuty.mq4.  

 

3. Indicador exibindo barras Renko

Barras Renko serão implementadas utilizando a mesma tecnologia usada nos pontos  1. O indicador "IndCreateOffline" que criará um gráfico off-line e 2. Iniciando o indicador no gráfico off-line. Eventualmente, obtemos um gráfico off-line, mas só neste caso, o arquivo de histórico *.hst conterá as barras do mesmo tamanho, também chamadas de tijolos. O tamanho dos tijolos é definido nas configurações dos indicadores e medido em pontos.

Barras Renko  

Fig. 3. Barras Renko 

Antes de começar, algumas regras de formação do arquivo de histórico  *.hst para plotar as barras Renko devem ser consideradas.

3.1. Regras de plotagem da variação das barras

Regra 1: A escrita OHLC no histórico do arquivo *.hst deve estar correta, especialmente quando se refere a valores das Máximas e Mínimas dos preços. Caso contrário, o terminal não irá exibir o registro (log) incorreto. Exemplo da escrita correta e incorreta da barra de alta no arquivo de histórico *.hst :

Exemplos de log correto e incorreto  

Fig. 4. Exemplos de log correto e incorreto 

Regra 2: mesmo que criamos barras de intervalo que não possuem uma âncora de tempo, o formato do arquivo de histórico *.hst requer o parâmetro de tempo - a hora de início do período. Por isso, é obrigatório escrever o parâmetro de tempo.

Regra 3: o parâmetro de tempo deve ser diferente para todas as barras Se você escrever o mesmo parâmetro para todas as barras de tempo, então este registro não será correto e o terminal não irá exibir o gráfico. Existe uma característica interessante aqui: o parâmetro de tempo pode ser escrito dentro de um segundo. Por exemplo, vamos escrever uma barra com os parâmetros time=2016.02.10 09:08:00 e a barra seguinte com parâmetros  time=2016.02.10 09:08:01.

3.2. Formando tijolos com dados do histórico 

Este método não é feito para ser um algoritmo impecável. Eu tenho usado uma abordagem simplificada porque o principal objetivo deste artigo é mostrar como formar o arquivo do histórico *.hst . No indicador " IndRange.mq4 ", os valores para High[i] e Low[i] do período corrente são analisados ao desenhar tijolos com base no histórico. Portanto, se o indicador "IndRange.mq4" está anexado ao gráfico com o período M5, então o indicador "InRange.mq4" irá analisar o histórico dentro do período M5 atual no primeiro lançamento.  

Certamente, se você quiser, você pode modernizar o algoritmo de desenho com base no histórico e considerar o movimento dos preços no menor período de tempo - М1. O regime geral de funcionamento é:

  • Se High[i] é maior do que o tijolo anterior, então um ou mais tijolos são desenhados por cima do anterior;
  • Se Low[i] é menor do que o tijolo anterior, então um ou mais tijolos são desenhados por cima do anterior.

O tijolo anterior é de alta

Fig. 5. O tijolo anterior é de alta   


 O tijolo anterior é de baixa

Fig. 6. O tijolo anterior é de baixa

Interessante: coordenadas de barras (Open, High, Low, Close, Time e Volumes) são armazenadas na estrutura "rate" que é declarada no cabeçalho do indicador

MqlRates rate;                   //

e esta estrutura não será zerada, mas será reescrita no arquivo do histórico *.hst a cada carregamento. Devido a isso, é fácil de implementar os algoritmos mostrado na fig. 5 e fig. 6. e a fórmula geral pode ser dada:

  • quando High[i] é maior do que o tijolo anterior (não faz diferença se era de alta ou de baixa), coordenadas de uma nova barra são calculadas em conformidade:  

Open = High; Low = Open; Close = Low + Renko size; High = Close

 

  • quando Low[i] é menor do que o tijolo anterior (e não faz diferença se era de alta ou de baixa), coordenadas de uma nova barra são calculadas em conformidade:  

Low = OpenHigh = OpenClose = High - Renko sizeLow = Close

E é assim que estas fórmulas aparecem no indicador "IndRange.mq4", na função FirstWriteHistory():

//+------------------------------------------------------------------+
//| Primeiro Escreve o Histórico                                     |
//+------------------------------------------------------------------+
bool FirstWriteHistory(const int offline_period)
  {
   int      i,start_pos;
   int      cnt=0;
//--- escreve o histórico do arquivo
   periodseconds=offline_period*60;
   start_pos=Bars-1;
   rate.open=Open[start_pos];
   rate.close=Close[start_pos];
   rate.low=Low[start_pos];
   rate.high=High[start_pos];
   rate.tick_volume=(long)Volume[start_pos];
   rate.spread=0;
   rate.real_volume=0;
//--- abertura de tempo normalizada
   rate.time=D'1980.07.19 12:30:27';
   for(i=start_pos-1; i>=0; i--)
     {
      if(IsStopped())
         break;
      while((High[i]-rate.high)>SizeRenko*Point())
        {
         rate.time+=1;
         rate.open=rate.high;
         rate.low=rate.open;
         rate.close=NormalizeDouble(rate.low+SizeRenko*Point(),Digits);
         rate.high=rate.close;
         last_fpos=FileTell(HandleHistory);
         uint byteswritten=FileWriteStruct(HandleHistory,rate);
         //--- verifica o número de bytes escritos 
         if(byteswritten==0)
            PrintFormat("Error read data. Error code=%d",GetLastError());
         else
            cnt++;
        }
      while((Low[i]-rate.low)<-SizeRenko*Point())
        {
         rate.time+=1;
         rate.open=rate.low;
         rate.high=rate.open;
         rate.close=NormalizeDouble(rate.high-SizeRenko*Point(),Digits);
         rate.low=rate.close;
         last_fpos=FileTell(HandleHistory);
         uint byteswritten=FileWriteStruct(HandleHistory,rate);
         //--- verifica o número de bytes escritos 
         if(byteswritten==0)
            PrintFormat("Error read data. Error code=%d",GetLastError());
         else
            cnt++;
        }
     }
   FileFlush(HandleHistory);
   PrintFormat("%d record(s) written",cnt);
   return(true);
  }

 

O indicador "IndRange.mq4" sempre forma o nome do arquivo de histórico *.hst de acordo com a seguinte regra: "nome do símbolo"+"7"+".hst". Por exemplo, para "EURUSD" o arquivo do histórico terá o nome de "EURUSD.hst".

3.3. Indicador operando on-line

Quando o indicador opera on-line, deve ser analisado o preço Close[0] para fechamento na barra zero (mais à direita) em vez do preço High[i]:

//+------------------------------------------------------------------+
//| Coletar Ticks                                                    |
//+------------------------------------------------------------------+
bool CollectTicks()
  {
   static datetime last_time;//=TimeLocal()-5;
   long     chart_id=0;
   datetime cur_time=TimeLocal();
//---
   while((Close[0]-rate.high)>SizeRenko*Point())
     {
      rate.time+=1;
      rate.open=rate.high;
      rate.low=rate.open;
      rate.close=NormalizeDouble(rate.low+SizeRenko*Point(),Digits);
      rate.high=rate.close;
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- verifica o número de bytes escritos  
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
     }
   while((Close[0]-rate.low)<-SizeRenko*Point())
     {
      rate.time+=1;
      rate.open=rate.low;
      rate.high=rate.open;
      rate.close=NormalizeDouble(rate.high-SizeRenko*Point(),Digits);
      rate.low=rate.close;
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- verifica o número de bytes escritos 
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
     }
//--- a janela atualiza não mais do que uma vez em 2 segundos
   if(cur_time-last_time>=3)
     {
      FileFlush(HandleHistory);
      ChartSetSymbolPeriod(ChartOffID,Symbol(),i_period);
      last_time=cur_time;
     }
   return(true);
  }


4. Indicador que cria um símbolo não padronizado - índice do dólar USDx

Observações importantes sobre o algoritmo do indicador "IndUSDx.mq4": o índice do indicador do dólar não implica na correção absoluta da fórmula de cálculo do índice. É mais importante para nós mostrar como formar o arquivo do histórico *.hst usando um símbolo não padronizado. Como resultado, teremos um gráfico off-line e suas barras exibirão o cálculo do índice do dólar. O indicador "IndUSDx.mq4" cria um gráfico off-line apenas uma vez nas seguintes situações: quando conecta pela primeira vez o indicador a um gráfico, ou depois de alterar o período do gráfico.

A fórmula para calcular o índice do dólar e o conjunto de símbolos é realizada com base no código: Indicador simples do índice do dólar

Dados de Time, Open e Close para "EURUSD", "GBPUSD", "USDCHF", "USDJPY", "AUDUSD", "USDCAD" e "NZDUSD" devem ser obtidos para a fórmula de cálculo do índice do dólar. Para facilitar o salvamento e referir-se aos dados, a estrutura OHLS foi introduzida (ela é declarada no cabeçalho do indicador):

//--- estruturas
struct   OHLS
  {
   datetime          ohls_time[];
   double            ohls_open[];
   double            ohls_close[];
  };

Na estrutura OHLS, os arrays para armazenar Time, Open e Close são usados como elementos. De acordo com descrição da estrutura, alguns objetos são instantaneamente declarados - a estrutura OHLS que será usada para o armazenamento de dados dos símbolos calculados:

//--- estruturas
struct   OHLS
  {
   datetime          ohls_time[];
   double            ohls_open[];
   double            ohls_close[];
  };
OHLS     OHLS_EURUSD,OHLS_GBPUSD,OHLS_USDCHF,OHLS_USDJPY,OHLS_AUDUSD,OHLS_USDCAD,OHLS_NZDUSD;
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()

A função SelectSymbols() é chamada em OnInit():

//+------------------------------------------------------------------+
//| selecionar Símbolos                                              |
//+------------------------------------------------------------------+
bool SelectSymbols()
  {
   bool rezult=true;
   string arr_symbols[7]={"EURUSD","GBPUSD","USDCHF","USDJPY","AUDUSD","USDCAD","NZDUSD"};
   for(int i=0;i<ArraySize(arr_symbols);i++)
      rezult+=SymbolSelect(arr_symbols[i],true);
//---
   return(rezult);
  }

A função SelectSymbols() com a ajuda do SymbolSelect, seleciona símbolos que participam na fórmula do índice do dólar na janela Observação do Mercado.

Em OnCalculate(), a função CopyCloseSymbols() é chamada no primeiro lançamento. Os dados para cálculos dos símbolos são solicitados e as estruturas do símbolo são preenchidas aqui:

//+------------------------------------------------------------------+
//| Símbolos CopyClose                                               |
//+------------------------------------------------------------------+
bool CopyCloseSymbols(const int rates)
  {
   int copied=0;
   int copy_time=0,copy_open=0,copy_close=0;
   copy_time=CopyTime("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_open);
   copy_close=CopyClose("EURUSD",Period(),0,rates,OHLS_EURUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"EURUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("GBPUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("GBPUSD",Period(),0,rates,OHLS_GBPUSD.ohls_open);
   copy_close=CopyClose("GBPUSD",Period(),0,rates,OHLS_GBPUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"GBPUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDCHF",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDCHF",Period(),0,rates,OHLS_USDCHF.ohls_open);
   copy_close=CopyClose("USDCHF",Period(),0,rates,OHLS_USDCHF.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDCHF\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDJPY",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDJPY",Period(),0,rates,OHLS_USDJPY.ohls_open);
   copy_close=CopyClose("USDJPY",Period(),0,rates,OHLS_USDJPY.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDJPY\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("AUDUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("AUDUSD",Period(),0,rates,OHLS_AUDUSD.ohls_open);
   copy_close=CopyClose("AUDUSD",Period(),0,rates,OHLS_AUDUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"AUDUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("USDCAD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("USDCAD",Period(),0,rates,OHLS_USDCAD.ohls_open);
   copy_close=CopyClose("USDCAD",Period(),0,rates,OHLS_USDCAD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"USDCAD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }

   copy_time=CopyTime("NZDUSD",Period(),0,rates,OHLS_EURUSD.ohls_time);
   copy_open=CopyOpen("NZDUSD",Period(),0,rates,OHLS_NZDUSD.ohls_open);
   copy_close=CopyClose("NZDUSD",Period(),0,rates,OHLS_NZDUSD.ohls_close);
   if(copy_open!=rates || copy_close!=rates || copy_time!=rates)
     {
      Print("Symbol \"NZDUSD\". Get time ",copy_time,", get open ",copy_open,", get close ",copy_close," of ",rates);
      return(false);
     }
//---
   return(true);
  }

Se o histórico do símbolo é baixado num valor menor do que o dado na função CopyCloseSymbols, então uma mensagem com o nome do símbolo e valor real do histórico do símbolo são exibidos.

No caso do sucesso do preenchimento das estruturas, a funcionalidade principal - FirstWriteHistory() que preenche o arquivo do histórico *.hst é chamado:

//+------------------------------------------------------------------+
//| Primeiro Escreve o Histórico                                     |
//+------------------------------------------------------------------+
bool FirstWriteHistory(const int rates)
  {
   int      i;
   int      cnt=0;
   rate.tick_volume=0;
   rate.spread=0;
   rate.real_volume=0;
   for(i=0;i<rates;i++)
     {
      rate.time=OHLS_EURUSD.ohls_time[i];
      rate.open=(100*MathPow(OHLS_EURUSD.ohls_open[i],0.125)+100*MathPow(OHLS_GBPUSD.ohls_open[i],0.125)+
                 100*MathPow(OHLS_USDCHF.ohls_open[i],0.125)+100*MathPow(OHLS_USDJPY.ohls_open[i],0.125)+
                 100*MathPow(OHLS_AUDUSD.ohls_open[i],0.125)+100*MathPow(OHLS_USDCAD.ohls_open[i],0.125)+
                 100*MathPow(OHLS_NZDUSD.ohls_open[i],0.125))/8.0;

      rate.close=(100*MathPow(OHLS_EURUSD.ohls_close[i],0.125)+100*MathPow(OHLS_GBPUSD.ohls_close[i],0.125)+
                  100*MathPow(OHLS_USDCHF.ohls_close[i],0.125)+100*MathPow(OHLS_USDJPY.ohls_close[i],0.125)+
                  100*MathPow(OHLS_AUDUSD.ohls_close[i],0.125)+100*MathPow(OHLS_USDCAD.ohls_close[i],0.125)+
                  100*MathPow(OHLS_NZDUSD.ohls_close[i],0.125))/8.0;

      if(rate.open>rate.close)
        {
         rate.high=rate.open;
         rate.low=rate.close;
        }
      else
        {
         rate.high=rate.close;
         rate.low=rate.open;
        }
      last_fpos=FileTell(HandleHistory);
      uint byteswritten=FileWriteStruct(HandleHistory,rate);
      //--- verifica o número de bytes escritos 
      if(byteswritten==0)
         PrintFormat("Error read data. Error code=%d",GetLastError());
      else
         cnt++;
     }
   FileFlush(HandleHistory);
   PrintFormat("%d record(s) written",cnt);
   return(true);
  }

Resultado da operação do indicador "IndUSDx.mq4": 

 "IndUSDx.mq4 indicator

Fig. 7. "IndUSDx.mq4 indicator 

  

Conclusão

Acontece que tanto gráficos off-line como on-line podem ser usados no novo cálculo econômico dos indicadores. No entanto, para este propósito, as alterações do código do indicador devem ser inseridas considerando as especificidades na atualização do gráfico off-line, ou seja, deve-se considerar que todos os indicadores receberam o valor prev_calculate==0 em OnCalculate() na atualização dos gráficos off-lines.

O artigo também mostra como formar o arquivo de histórico *.hst e como usá-lo para mudar completamente os parâmetros das barras exibidas nos gráficos. 


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

Interfaces Gráficas II: Configuração dos manipuladores de eventos da Biblioteca (Capítulo 3) Interfaces Gráficas II: Configuração dos manipuladores de eventos da Biblioteca (Capítulo 3)
Os artigos anteriores contêm a implementação das classes para criar os componentes do menu principal. Agora, está na hora de olharmos com mais atenção os manipuladores de eventos nas classes base principais e dos controles criados. Nós também prestaremos uma atenção especial na gestão do estado do gráfico, dependendo da localização do cursor do mouse.
Que testes deve passar o robô de negociação antes da publicação no Mercado Que testes deve passar o robô de negociação antes da publicação no Mercado
Todos os produtos do Mercado, antes de serem publicados, passam uma revisão preliminar obrigatória para garantir um único padrão de qualidade. Neste artigo, vamos falar sobre os erros mais comuns que os desenvolvedores cometem ao trabalhar com os seus indicadores técnicos e robôs de negociação. Além disso, mostraremos como testar por si mesmo o seu produto antes de enviá-lo para o Mercado.
Interfaces Gráficas II: O Elemento Menu Principal (Capítulo 4) Interfaces Gráficas II: O Elemento Menu Principal (Capítulo 4)
Este é o capítulo final da segunda parte da série sobre interfaces gráficas. Aqui, nós vamos estudar a criação do menu principal. Demonstraremos neste artigo o desenvolvimento deste controle e a configuração dos manipuladores das classes da biblioteca para que ele tenha uma reação correta para as ações do usuário. Nós também vamos discutir como anexar os menus de contexto para os elementos do menu principal. Além disso, nós vamos mencionar a questão do bloqueio dos elementos inativos atualmente.
Interfaces Gráficas II: Os Elementos Linha de Separação e o Menu de Contexto (Capítulo 2) Interfaces Gráficas II: Os Elementos Linha de Separação e o Menu de Contexto (Capítulo 2)
Neste artigo, nós vamos criar o elemento linha de separação. Será possível usá-lo não só como um elemento de interface independentes, mas também como uma parte de diversos outros elementos. Depois disso, nós teremos todos os recursos necessários para o desenvolvimento da classe do menu de contexto, que também serão considerados neste artigo em detalhe. Acrescentando, nós vamos apresentar todos os incrementos necessários à classe, que é a base para armazenar os ponteiros para todos os elementos da interface gráfica da aplicação.