EA com interface gráfica: Criação do painel (Parte I)

Anatoli Kazharski | 16 julho, 2018


Sumário

Introdução

Apesar do desenvolvimento ativo do algo-trading, muitos traders ainda preferem negociar manualmente, porém, há poucas hipóteses de fazer o trabalho sem automatizar as operações de rotina.

Este artigo mostra a criação de um EA multissímbolo de sinal para negociação manual. Como exemplo, vamos examinar os sinais do indicador Stochastic fornecido com o terminal. Este código pode ser usado para criar seus próprios EAs com uma interface gráfica, pois você pode conectar a ele qualquer outro indicador ou usar os resultados de certos cálculos para tomar decisões.

Para aqueles que atendem encomendas no serviço Freelance, este artigo pode exemplificar uns termos de referência para mostrar aos clientes. É possível que este exemplo ajude a reduzir o tempo que leva redigir os termos de referência de um programa com interface gráfica.

Listemos os assuntos que serão discutidos em detalhes neste artigo:

  • Criação de uma interface gráfica.
  • Obtenção de uma lista de símbolos com as propriedades especificadas. 
  • Controles de operações de negociação. 
  • Alternância rápida de símbolos e timeframes em gráficos sem reinicialização do EA.
  • Gerenciamento das propriedades dos gráficos através da interface do usuário.
  • Obtenção dos sinais do indicador a partir de um conjunto de símbolos com indicação de cor.
  • Trabalho com posições abertas. 
  • Atualizações da biblioteca EasyAndFast.

O artigo será publicado em duas partes. Neste artigo, será considerada a criação de um painel; no próximo, será descrita a parte de sua funcionalidade.

Elementos da interface gráfica

O desenvolvimento do EA começará com a construção de uma interface gráfica, através da qual será implementada a interação com o usuário e com a visualização dos dados. Pode-se criar uma interface gráfica, usando os recursos da biblioteca padrão, mas, no meu exemplo, ela será implementada com base na biblioteca EasyAndFast. Seus ricos recursos permitem concentrar-se na funcionalidade do próprio programa, sem se distrair com a revisão e melhoramento de sua parte gráfica.

Primeiro, vamos delinear o esquema geral da interface gráfica. O esquema abaixo mostra que há um grupo de duas guias na janela da interface gráfica do usuário. As listas mostram as funções que precisam ser colocadas nelas. Este é um exemplo simplificado, mas o cliente e o desenvolvedor podem trabalhá-lo com mais detalhes durante a discussão da tarefa.


Fig. 1. Esquema geral da interface gráfica com esclarecimentos.

Pode haver muitos controles de interface gráfica do usuário, por isso, primeiro, simplesmente os listamos de forma hierárquica:

  • Formulário para controles
    • Barra de status para mostrar informações adicionais finais
    • Grupo de guias:
      • Trade:
        • Campo inserido com caixa de verificação para filtrar a lista de símbolos
        • Botão de solicitação para começar a coletar a lista de símbolos
        • Botão para a operação de negociação SELL
        • Botão para a operação de negociação BUY
        • Campo inserido para definir o volume do trade
        • Botão para fechar todas as posições abertas
        • Campo inserido para definir o nível do sinal de venda
        • Campo inserido para definir o nível do sinal de compra
        • Tabela de símbolos de negociação
        • Gráfico para visualizar estes símbolos. Por conveniência, vamos fazer com que algumas propriedades do gráfico possam ser controladas com a ajuda do seguinte grupo de elementos:
          • Caixa de combinação com lista suspensa para selecionar a alternância de timeframe 
          • Caixa de verificação para habilitar/desabilitar a escala de tempo 
          • Caixa de verificação para habilitar/desabilitar a escala de preços 
          • Campo inserido para controlar a escala 
          • Botão para ativar o recuo 
          • Caixa de verificação para exibição do indicador 
      • Positions:
        • Tabela de posições
    • Indicador para o processo de reprodução de quadros

Na classe principal do programa (CProgram), declare os métodos e instâncias das classes dos elementos acima. O código dos métodos para criação destes elementos é colocado num arquivo separado e é conectado a um arquivo com a classe do programa MQL:

//+----------´--------------------------------------------------------+
//| Classe para criar o aplicativo                                    |
//+----------´--------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Janela
   CWindow           m_window1;
   //--- Barra de Status
   CStatusBar        m_status_bar;
   //--- Guias
   CTabs             m_tabs1;
   //--- Campo inserido
   CTextEdit         m_symb_filter;
   CTextEdit         m_lot;
   CTextEdit         m_up_level;
   CTextEdit         m_down_level;
   CTextEdit         m_chart_scale;
   //--- Botões
   CButton           m_request;
   CButton           m_chart_shift;
   CButton           m_buy;
   CButton           m_sell;
   CButton           m_close_all;
   //--- Caixas de combinação
   CComboBox         m_timeframes;
   //--- Caixas de verificação
   CCheckBox         m_date_scale;
   CCheckBox         m_price_scale;
   CCheckBox         m_show_indicator;
   //--- Tabelas
   CTable            m_table_positions;
   CTable            m_table_symb;
   //--- Gráfico padrão
   CStandardChart    m_sub_chart1;
   //--- Barra de progresso
   CProgressBar      m_progress_bar;
   //---
public:
   //--- Cria uma interface gráfica
   bool              CreateGUI(void);
   //---
private:
   //--- Formulário
   bool              CreateWindow(const string text);
   //--- Barra de Status
   bool              CreateStatusBar(const int x_gap,const int y_gap);
   //--- Guias
   bool              CreateTabs1(const int x_gap,const int y_gap);
   //--- Campo inserido
   bool              CreateSymbFilter(const int x_gap,const int y_gap,const string text);
   bool              CreateLot(const int x_gap,const int y_gap,const string text);
   bool              CreateUpLevel(const int x_gap,const int y_gap,const string text);
   bool              CreateDownLevel(const int x_gap,const int y_gap,const string text);
   bool              CreateChartScale(const int x_gap,const int y_gap,const string text);
   //--- Botões
   bool              CreateRequest(const int x_gap,const int y_gap,const string text);
   bool              CreateChartShift(const int x_gap,const int y_gap,const string text);
   bool              CreateBuy(const int x_gap,const int y_gap,const string text);
   bool              CreateSell(const int x_gap,const int y_gap,const string text);
   bool              CreateCloseAll(const int x_gap,const int y_gap,const string text);
   //--- Caixa de combinação
   bool              CreateComboBoxTF(const int x_gap,const int y_gap,const string text);
   //--- Caixas de verificação
   bool              CreateDateScale(const int x_gap,const int y_gap,const string text);
   bool              CreatePriceScale(const int x_gap,const int y_gap,const string text);
   bool              CreateShowIndicator(const int x_gap,const int y_gap,const string text);
   //--- Tabelas
   bool              CreatePositionsTable(const int x_gap,const int y_gap);
   bool              CreateSymbolsTable(const int x_gap,const int y_gap);
   //--- Gráfico padrão
   bool              CreateSubChart1(const int x_gap,const int y_gap);
   //--- Barra de progresso
   bool              CreateProgressBar(const int x_gap,const int y_gap,const string text);
  };
//+----------´--------------------------------------------------------+
//| Métodos para criar controles                                      |
//+----------´--------------------------------------------------------+
#include "CreateGUI.mqh"
//+----------´--------------------------------------------------------+

Em seguida, damos uma olhada mais de perto à montagem da interface gráfica do usuário, os métodos para criar seus elementos e suas propriedades.

Montagem da interface gráfica do usuário

A interface gráfica do aplicativo a ser desenvolvido utilizará elementos de dez tipos diferentes:

  • Formulário para controles (CWindow)
  • Barra de Status (CStatusBar)
  • Grupo de guias (CTabs)
  • Campo inserido (CTextEdit)
  • Botão (CButton)
  • Caixa de combinação com lista suspensa (CComboBox)
  • Caixa de verificação (CCheckBox)
  • Tabela (CTable)
  • Gráfico padrão (CStandardChart)
  • Barra de progresso (CProgressBar)

Nesta lista, haverá vários elementos, portanto considere apenas um de cada grupo. Na mesma sequência, considere os métodos para criá-los. 

Formulário para controles

Abaixo está o código do método para criar um formulário no qual serão localizados todos os outros elementos. No começo, você precisa adicionar o formulário à lista de elementos da interface gráfica do usuário do programa. Para fazer isso, você precisa chamar o método CWndContainer::AddWindow(), passando para ele o objeto de um elemento do tipo CWindow. Logo, antes de criar o formulário, defina suas propriedades. Defina as seguintes propriedades (na mesma ordem que na listagem abaixo):

  • Dimensões do formulário (largura e altura)
  • Tamanho da fonte no cabeçalho
  • Modo de deslocamento do formulário (dentro do gráfico)
  • Modo de redimensionamento manual do formulário (arrastando bordas)
  • Botões do formulário. A visibilidade de cada botão é ativada separadamente. Nesse caso, estão envolvidos os seguintes:
    • Encerramento do formulário. No formulário principal do programa, quando você clica neste botão, aparece uma caixa de diálogo perguntando sobre o encerramento do programa.
    • Recolher e expandir o formulário. 
    • Dica de balão de elementos. Este botão também tem dois estados. Se for pressionado, serão exibidas dicas de balão (desde que tenham sido definidas).
    • Expandir o formulário em toda a área do gráfico do terminal. Expandido o formulário, ele poderá ser retornado às dimensões anteriores clicando neste botão novamente.
  • Para cada botão de formulário, você pode definir uma dica de balão.

Depois que as propriedades estiverem definidas, você precisará chamar o método de criação de formulário CWindow::CreateWindow() e transferir para ele:

  • o identificador do gráfico,
  • o número da subjanela do gráfico,
  • o texto do cabeçalho,
  • as coordenadas iniciais de localização para o formulário.
//+----------´--------------------------------------------------------+
//| Criando o formulário para os controles                            |
//+----------´--------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Adicione o ponteiro da janela para a matriz de janelas
   CWndContainer::AddWindow(m_window1);
//--- Propriedades
   m_window1.XSize(750);
   m_window1.YSize(450);
   m_window1.FontSize(9);
   m_window1.IsMovable(true);
   m_window1.ResizeMode(true);
   m_window1.CloseButtonIsUsed(true);
   m_window1.CollapseButtonIsUsed(true);
   m_window1.TooltipsButtonIsUsed(true);
   m_window1.FullscreenButtonIsUsed(true);
//--- Defina as dicas de balão
   m_window1.GetCloseButtonPointer().Tooltip("Close");
   m_window1.GetTooltipButtonPointer().Tooltip("Tooltips");
   m_window1.GetFullscreenButtonPointer().Tooltip("Fullscreen");
   m_window1.GetCollapseButtonPointer().Tooltip("Collapse/Expand");
//--- Criação do formulário
   if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,1,1))
      return(false);
//---
   return(true);
  }

Sempre que adicionando ao código um novo elemento da interface gráfica do usuário, é desejável compilar o programa e ver qual é o resultado: 

Fig. 2 - Formulário para controles.

Fig. 2. Formulário para controles.

Abaixo estão as capturas de tela de todos os resultados intermediários.

Barra de Status

O código do método para criar a barra de status começa com a definição do elemento principal. A partir dele serão calculados o posicionamento e o alinhamento de acordo com o tamanho dos elementos associados a ele. Isso ajuda a poupar tempo, pois todo um grupo de elementos relacionados pode ser movido, alterando as coordenadas apenas para o elemento principal. Para associar um elemento, seu ponteiro é passado para o método CElement::MainPointer(). Neste exemplo, ligamos a barra de status a um formulário, por isso, transfira o objeto do formulário para o método.

Em seguida, defina as propriedades da barra de status. Nela, você fará três itens em que serão exibidas as informações para o usuário. 

  • Para não especificar dimensões relativas à forma, indique que a largura deve mudar automaticamente quando a largura do formulário for alterada.
  • O recuo da borda direita do elemento a partir da borda direita do formulário será de 1 pixel.
  • Associe a barra de status à parte inferior do formulário para que, quando a altura mudar, ele se ajuste automaticamente à borda inferior.
  • Em seguida, ao adicionar itens, especifique suas larguras.

Depois que as propriedades estiverem definidas, crie o elemento. Agora ele está pronto para trabalhar e você pode alterar o texto em seus itens quando o programa estiver em execução. No nosso exemplo, defina no primeiro item o texto «For Help, press F1»

No final do método, você sempre deve salvar o ponteiro do elemento criado na lista geral de elementos da interface gráfica do usuário. Para fazer isso, chame o método CWndContainer::AddToElementsArray(), transfira para ele o índice do formulário e o objeto do elemento. Como só temos um formulário, seu índice será 0.

//+----------´--------------------------------------------------------+
//| Criando a barra de status                                         |
//+----------´--------------------------------------------------------+
bool CProgram::CreateStatusBar(const int x_gap,const int y_gap)
  {
#define STATUS_LABELS_TOTAL 3
//--- Salve o ponteiro para a janela
   m_status_bar.MainPointer(m_window1);
//--- Propriedades
   m_status_bar.AutoXResizeMode(true);
   m_status_bar.AutoXResizeRightOffset(1);
   m_status_bar.AnchorBottomWindowSide(true);
//--- Especifique quantas partes devem ser e defina suas propriedades
   int width[STATUS_LABELS_TOTAL]={0,200,110};
   for(int i=0; i<STATUS_LABELS_TOTAL; i++)
      m_status_bar.AddItem(width[i]);
//--- Crie o elemento de controle
   if(!m_status_bar.CreateStatusBar(x_gap,y_gap))
      return(false);
//--- Definição do texto para os itens da barra de status
   m_status_bar.SetValue(0,"For Help, press F1");
//--- Adicione um objeto à matriz comum de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_status_bar);
   return(true);
  }

Outros elementos da biblioteca EasyAndFast são criados seguindo o mesmo princípio. Portanto, consideraremos apenas as propriedades que serão definidas para serem usadas em nosso EA.

Fig. 3 - Adição da barra de status.

Fig. 3. Adição da barra de status.

Grupo de guias

No método para criar um grupo de guias, defina as seguintes propriedades para o elemento:

  • Texto nas guias centralizado.
  • Guias na parte superior do espaço de trabalho.
  • Dimensões que se ajustam automaticamente à área do elemento principal (formulário). Nesse caso, não é necessário especificar o tamanho da área do grupo de guias.
  • Recuo a partir das bordas direita e inferior do elemento principal. Ao redimensionar o formulário, esses recuos persistem.
  • Ao adicionar as seguintes guias, o nome e a largura da guia também são transferidos para o método.

Veja o código do método:

//+----------´--------------------------------------------------------+
//| Criando um grupo com guias 1                                      |
//+----------´--------------------------------------------------------+
bool CProgram::CreateTabs1(const int x_gap,const int y_gap)
  {
#define TABS1_TOTAL 2
//--- Salve o ponteiro para o elemento principal
   m_tabs1.MainPointer(m_window1);
//--- Propriedades
   m_tabs1.IsCenterText(true);
   m_tabs1.PositionMode(TABS_TOP);
   m_tabs1.AutoXResizeMode(true);
   m_tabs1.AutoYResizeMode(true);
   m_tabs1.AutoXResizeRightOffset(3);
   m_tabs1.AutoYResizeBottomOffset(25);
//--- Adicione guias com as propriedades especificadas
   string tabs_names[TABS1_TOTAL]={"Trade","Positions"};
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs1.AddTab(tabs_names[i],100);
//--- Crie o elemento de controle
   if(!m_tabs1.CreateTabs(x_gap,y_gap))
      return(false);
//--- Adicione um objeto à matriz comum de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_tabs1);
   return(true);
  }

Fig. 4 - Adicionando um grupo de guias

Fig. 4. Adicionando um grupo de guias.

Campo inserido

Por exemplo, consideremos um campo inserido em que o usuário pode especificar moedas e/ou pares de moedas para formar uma lista de símbolos numa tabela. Seu elemento principal será um grupo de guias. Aqui é preciso especificar em qual guia exibir este campo inserido. Para fazer isso, chame o método CTabs::AddToElementsArray(), transfira para ele o índice da guia e o objeto do elemento anexado.

Em seguida, consideramos as propriedades definidas para esse campo inserido.

  • Por padrão, no campo inserido será inserido o texto «USD»: o programa coletará na tabela símbolos em que há USD. A moeda e/ou símbolos neste campo inserido devem ser separados por vírgulas. Abaixo, mostrarei o método que permite formar uma lista de símbolos - de acordo com linhas separadas por vírgulas - neste campo inserido.
  • O campo inserido será acompanhado por uma caixa de verificação. Se você desativar a caixa de verificação, o texto no campo inserido será ignorado e todos os pares de moedas encontrados serão incluídos na lista de símbolos.
  • A largura do campo inserido será toda a largura do elemento principal. Ela também será corrigida se você alterar a largura da área das guias.
  • À direita do campo inserido estará o botão Request. Enquanto o programa está em execução, você pode especificar outras moedas e/ou símbolos no campo inserido, e, para que a lista seja formada, você precisará clicar neste botão. Como estão implícitas a alteração automática da largura do campo inserido e a localização do botão Request sempre à direita, é necessário que o lado direito do campo inserido sempre tenha um recuo a partir da borda direita do elemento principal. 

O elemento de tipo CTextEdit consiste em vários outros elementos. Portanto, se você precisar alterar suas propriedades, você pode obter ponteiros para eles. Nós precisávamos mudar algumas propriedades do campo inserido (CTextBox). Vamos considerá-las na mesma sequência, conforme implementado na listagem de código abaixo.

  • Recuo do campo inserido a partir da borda esquerda do elemento principal (CTextEdit). 
  • Alteração automática de largura em relação ao elemento principal.
  • Ao ativar o campo inserido (clique no botão esquerdo do mouse no campo inserido), o texto é marcado automaticamente como selecionado para uma possível substituição rápida.
  • Se não houver texto no campo inserido, será exibida a seguinte infodica: «Example: EURUSD, GBP, NOK».  

Por padrão, a caixa de verificação do campo inserido está ativada. Para fazer isso, é preciso ativá-la logo após o elemento ser criado.

//+----------´------------------------------------------------------------------+
//| Criando uma caixa de verificação com o campo inserido "Symbols filter"      |
//+----------´------------------------------------------------------------------+
bool CProgram::CreateSymbolsFilter(const int x_gap,const int y_gap,const string text)
  {
//--- Salve o ponteiro para o elemento principal
   m_symb_filter.MainPointer(m_tabs1);
//--- Fixe na guia
   m_tabs1.AddToElementsArray(0,m_symb_filter);
//--- Propriedades
   m_symb_filter.SetValue("USD"); // "EUR,USD" "EURUSD,GBPUSD" "EURUSD,GBPUSD,AUDUSD,NZDUSD,USDCHF"
   m_symb_filter.CheckBoxMode(true);
   m_symb_filter.AutoXResizeMode(true);
   m_symb_filter.AutoXResizeRightOffset(90);
   m_symb_filter.GetTextBoxPointer().XGap(100);
   m_symb_filter.GetTextBoxPointer().AutoXResizeMode(true);
   m_symb_filter.GetTextBoxPointer().AutoSelectionMode(true);
   m_symb_filter.GetTextBoxPointer().DefaultText("Example: EURUSD,GBP,NOK");
//--- Crie o elemento de controle
   if(!m_symb_filter.CreateTextEdit(text,x_gap,y_gap))
      return(false);
//--- Ative a caixa de verificação
   m_symb_filter.IsPressed(true);
//--- Adicione um objeto à matriz comum de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_symb_filter);
   return(true);
  }

Além do campo inserido, haverá numerais na interface gráfica. Por exemplo, o campo inserido Lot (volume para abertura de posições). Para campos inseridos desse tipo, você precisa especificar outras propriedades.

  • Largura total do elemento.
  • Valor máximo a ser inserido.
  • Valor mínimo a ser inserido.
  • Incremento ao alternar com os botões de incremento e decremento.
  • Número de casas decimais.
  • Para que o campo inserido seja numérico, você precisa especificar isso usando o método CTextEdit::SpinEditMode().
  • Valor padrão após o carregamento do programa no gráfico do terminal.
  • Largura do campo inserido.
  • Seleção automática de texto no campo inserido ao clicar nele.
  • Acoplagem do campo inserido à borda direita do elemento.

Veja o código para este método:

//+----------´--------------------------------------------------------+
//| Criando o campo inserido "Lot"                                    |
//+----------´--------------------------------------------------------+
bool CProgram::CreateLot(const int x_gap,const int y_gap,const string text)
  {
//--- Salve o ponteiro para o elemento principal
   m_lot.MainPointer(m_tabs1);
//--- Fixe na guia
   m_tabs1.AddToElementsArray(0,m_lot);
//--- Propriedades
   m_lot.XSize(80);
   m_lot.MaxValue(1000);
   m_lot.MinValue(0.01);
   m_lot.StepValue(0.01);
   m_lot.SetDigits(2);
   m_lot.SpinEditMode(true);
   m_lot.SetValue((string)0.1);
   m_lot.GetTextBoxPointer().XSize(50);
   m_lot.GetTextBoxPointer().AutoSelectionMode(true);
   m_lot.GetTextBoxPointer().AnchorRightWindowSide(true);
//--- Crie o elemento de controle
   if(!m_lot.CreateTextEdit(text,x_gap,y_gap))
      return(false);
//--- Adicione um objeto à matriz comum de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_lot);
   return(true);
  }

Fig. 5 - Adicionando campos inseridos.

Fig. 5.  Adicionando campos inseridos.

A imagem não parece muito lógica, mas quando você adiciona os outros elementos, tudo fica certo.

Botão

Adicione alguns botões à interface gráfica do nosso EA. Considere o que tem mais propriedades, isto é, o botão para abrir as posições SELL.

  • Largura do botão.
  • Texto do botão centrado, tanto na vertical como na horizontal.
  • Cor de fundo do botão.
  • Cor de fundo ao passar o cursor.
  • Cor de fundo ao clicar com o botão esquerdo.
  • Cor do texto do botão.
  • Cor do texto ao passar o cursor.
  • Cor do texto ao clicar com o botão esquerdo.
  • Cor da borda do botão.
  • Cor da borda ao passar o cursor.
  • Cor da borda ao clicar com o botão esquerdo.

No botão BUY são alteradas as mesmas propriedades, porém, existe uma diferença apenas nas cores de plano de fundo especificadas.

//+----------´--------------------------------------------------------+
//| Criando o botão 'Sell'                                            |
//+----------´--------------------------------------------------------+
bool CProgram::CreateSell(const int x_gap,const int y_gap,const string text)
  {
//--- Salve o ponteiro para o elemento principal
   m_sell.MainPointer(m_tabs1);
//--- Fixe na guia
   m_tabs1.AddToElementsArray(0,m_sell);
//--- Propriedades
   m_sell.XSize(80);
   m_sell.IsCenterText(true);
   m_sell.BackColor(C'255,51,51');
   m_sell.BackColorHover(C'255,100,100');
   m_sell.BackColorPressed(C'195,0,0');
   m_sell.LabelColor(clrWhite);
   m_sell.LabelColorHover(clrWhite);
   m_sell.LabelColorPressed(clrWhite);
   m_sell.BorderColor(clrBlack);
   m_sell.BorderColorHover(clrBlack);
   m_sell.BorderColorPressed(clrBlack);
//--- Crie o elemento de controle
   if(!m_sell.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Adicione o ponteiro para o elemento na base
   CWndContainer::AddToElementsArray(0,m_sell);
   return(true);
  }

Fig. 6 - Adicionando botões.

Fig. 6 Adicionando botões.

Caixa de combinação com lista suspensa

Para alterar o timeframe, crie uma caixa de combinação com uma lista suspensa. Liste as propriedades para configurá-la.

  • Largura total do elemento. 
  • Número de itens na lista (no nosso caso 21, conforme o número de timeframes no terminal).
  • Elemento ancorado ao lado direito da área das guias.
  • Largura do botão da caixa de combinação.
  • Botão ancorado ao lado direito do elemento.

Logo, cada item da lista recebe valores e, em seguida, para a lista são definidas algumas propriedades através do ponteiro.

  • Realce de itens ao passar o cursor.
  • Item realçado. Neste caso, será o item com o índice 18 (timeframe D1).

Veja o código do método para criar esta caixa de combinação:

//+----------´--------------------------------------------------------+
//| Criando uma caixa de combinação para selecionar timeframes        |
//+----------´--------------------------------------------------------+
bool CProgram::CreateComboBoxTF(const int x_gap,const int y_gap,const string text)
  {
//--- Número total de itens na lista
#define ITEMS_TOTAL2 21
//--- Transfira o objeto do painel
   m_timeframes.MainPointer(m_tabs1);
//--- Fixe na guia
   m_tabs1.AddToElementsArray(0,m_timeframes);
//--- Propriedades
   m_timeframes.XSize(115);
   m_timeframes.ItemsTotal(ITEMS_TOTAL2);
   m_timeframes.AnchorRightWindowSide(true);
   m_timeframes.GetButtonPointer().XSize(50);
   m_timeframes.GetButtonPointer().AnchorRightWindowSide(true);
//--- Salve os valores dos itens na lista da caixa de combinação
   string items_text[ITEMS_TOTAL2]={"M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30","H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"};
   for(int i=0; i<ITEMS_TOTAL2; i++)
      m_timeframes.SetValue(i,items_text[i]);
//--- Obtenha o ponteiro da lista
   CListView *lv=m_timeframes.GetListViewPointer();
//--- Definia as propriedades da lista
   lv.LightsHover(true);
   lv.SelectItem(18);
//--- Crie o elemento de controle
   if(!m_timeframes.CreateComboBox(text,x_gap,y_gap))
      return(false);
//--- Adicione o ponteiro para o elemento na base
   CWndContainer::AddToElementsArray(0,m_timeframes);
   return(true);
  }

Fig. 7 - Adicionando caixa de combinação.

Fig. 7. Adicionando caixa de combinação.

Caixa de verificação

A caixa de verificação é o elemento mais simples. Para ele, basta especificar duas propriedades.

  • Largura
  • Localização no lado direito do elemento principal.

Após criar um elemento, você pode ativar programaticamente a caixa de verificação.

//+----------´--------------------------------------------------------+
//| Criando a caixa de verificação "Date scale"                       |
//+----------´--------------------------------------------------------+
bool CProgram::CreateDateScale(const int x_gap,const int y_gap,const string text)
  {
//--- Salve o ponteiro para a janela
   m_date_scale.MainPointer(m_tabs1);
//--- Fixe na guia
   m_tabs1.AddToElementsArray(0,m_date_scale);
//--- Propriedades
   m_date_scale.XSize(70);
   m_date_scale.AnchorRightWindowSide(true);
//--- Crie o elemento de controle
   if(!m_date_scale.CreateCheckBox(text,x_gap,y_gap))
      return(false);
//--- Ative a caixa de verificação
   m_date_scale.IsPressed(true);
//--- Adicione um objeto à matriz comum de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_date_scale);
   return(true);
  }

Fig. 8 - Adicionando caixas de verificação

Fig. 8. Adicionando caixas de verificação.

Tabela

A interface gráfica do usuário terá duas tabelas. Considere a que torna visível a lista gerada de símbolos e de sinais para a abertura de posições. Ela é colocada na primeira guia. Primeiro, declare e inicialize as matrizes para definir as propriedades da tabela. Configure as seguintes propriedades.

  • Largura do elemento
  • Dimensão da tabela (o número de colunas e de linhas).
  • Largura da coluna (os valores são transferidos numa matriz).
  • Alinhamento do texto (os valores são transferidos numa matriz).
  • Recuo do texto a partir das bordas das células.
  • Exibição de cabeçalhos.
  • Capacidade de selecionar linhas.
  • Capacidade de redimensionar as colunas manualmente, arrastando a borda do cabeçalho.
  • Exibir com formatação no estilo Zebra.
  • Redimensionar automaticamente verticalmente em relação ao elemento principal.
  • Recuo a partir da borda inferior do elemento principal.

O texto dos cabeçalhos pode ser especificado após a criação da tabela:

//+----------´--------------------------------------------------------+
//| Criando a tabela de símbolos                                      |
//+----------´--------------------------------------------------------+
bool CProgram::CreateSymbolsTable(const int x_gap,const int y_gap)
  {
#define COLUMNS1_TOTAL 2
#define ROWS1_TOTAL    1
//--- Salve o ponteiro para o elemento principal
   m_table_symb.MainPointer(m_tabs1);
//--- Fixe na guia
   m_tabs1.AddToElementsArray(0,m_table_symb);
//--- Matriz de largura de colunas
   int width[COLUMNS1_TOTAL]={95,58};
//--- Matriz de alinhamento do texto, nas colunas
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL]={ALIGN_LEFT,ALIGN_RIGHT};
//--- Matriz de recuo do texto, nas colunas, ao longo do eixo X
   int text_x_offset[COLUMNS1_TOTAL]={5,5};
//--- Propriedades
   m_table_symb.XSize(168);
   m_table_symb.TableSize(COLUMNS1_TOTAL,ROWS1_TOTAL);
   m_table_symb.ColumnsWidth(width);
   m_table_symb.TextAlign(align);
   m_table_symb.TextXOffset(text_x_offset);
   m_table_symb.ShowHeaders(true);
   m_table_symb.SelectableRow(true);
   m_table_symb.ColumnResizeMode(true);
   m_table_symb.IsZebraFormatRows(clrWhiteSmoke);
   m_table_symb.AutoYResizeMode(true);
   m_table_symb.AutoYResizeBottomOffset(2);
//--- Crie o elemento de controle
   if(!m_table_symb.CreateTable(x_gap,y_gap))
      return(false);
//--- Definimos o título do cabeçalho
   m_table_symb.SetHeaderText(0,"Symbol");
   m_table_symb.SetHeaderText(1,"Values");
//--- Adicione um objeto à matriz comum de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_table_symb);
   return(true);
  }

A segunda tabela exibe algumas propriedades das posições abertas. Suas dez colunas exibirão os seguintes dados:

  • Símbolo da posição.
  • Número de posições.
  • Volume total de todas as posições abertas.
  • Volume das posições BUY.
  • Volume das posições SELL.
  • Total atual para todas as posições abertas.
  • Total atual para todas as posições BUY abertas.
  • Total atual para todas as posições SELL abertas.
  • Carga do depósito para cada símbolo separadamente.
  • Preço médio.

Na segunda tabela, você também deve configurar as seguintes propriedades:

  • Recuo de imagens a partir das bordas direita e superior das células.
  • Capacidade de classificar valores.
  • Redimensionar automaticamente horizontalmente em relação ao elemento principal.
  • Recuo a partir da borda direita do elemento principal.

As imagens nas células da primeira coluna simbolizarão os botões que, ao ser clicados, fecham a posição ou todas as posições, se esta é uma conta de hedge, no símbolo especificado. 

//+----------´--------------------------------------------------------+
//| Criando uma tabela de posições                                    |
//+----------´--------------------------------------------------------+
bool CProgram::CreatePositionsTable(const int x_gap,const int y_gap)
  {
...
//--- Propriedades
   m_table_positions.TableSize(COLUMNS2_TOTAL,ROWS2_TOTAL);
   m_table_positions.ColumnsWidth(width);
   m_table_positions.TextAlign(align);
   m_table_positions.TextXOffset(text_x_offset);
   m_table_positions.ImageXOffset(image_x_offset);
   m_table_positions.ImageYOffset(image_y_offset);
   m_table_positions.ShowHeaders(true);
   m_table_positions.IsSortMode(true);
   m_table_positions.SelectableRow(true);
   m_table_positions.ColumnResizeMode(true);
   m_table_positions.IsZebraFormatRows(clrWhiteSmoke);
   m_table_positions.AutoXResizeMode(true);
   m_table_positions.AutoYResizeMode(true);
   m_table_positions.AutoXResizeRightOffset(2);
   m_table_positions.AutoYResizeBottomOffset(2);
...
   return(true);
  }

Fig. 9 - Adicionando uma tabela na primeira guia.

Fig. 9. Adicionando uma tabela na primeira guia.

Fig. 10 - Adicionando uma tabela na segunda guia.

Fig. 10. Adicionando uma tabela na segunda guia.

Numa das próximas seções do artigo, falarei sobre como trabalhar com tabelas no arquivo principal do programa (CProgram).

Gráfico padrão

Para visualizar dados por símbolos, é fornecido um elemento do tipo CStandardChart. Por padrão, será exibido o gráfico EURUSD no timeframe diário. Ele tem as seguintes propriedades.

  • Rolagem horizontal.
  • Ajuste automático de largura
  • Ajuste automático de altura.
  • Recuo a partir da borda direita do elemento principal.
  • Recuo a partir da borda inferior do elemento principal.

Se necessário, você pode criar uma matriz de gráficos alinhados horizontalmente. Para fazer isso, use o método CStandardChart::AddSubChart(), transferindo para ele o símbolo e o timeframe do gráfico como argumentos. Mas, neste caso, você precisa apenas de um gráfico, enquanto os símbolos e timeframes serão alternados usando outros controles.

//+----------´--------------------------------------------------------+
//| Criando o gráfico padrão 1                                        |
//+----------´--------------------------------------------------------+
bool CProgram::CreateSubChart1(const int x_gap,const int y_gap)
  {
//--- Salve o ponteiro para a janela
   m_sub_chart1.MainPointer(m_tabs1);
//--- Fixe na primeira guia
   m_tabs1.AddToElementsArray(0,m_sub_chart1);
//--- Propriedades
   m_sub_chart1.XScrollMode(true);
   m_sub_chart1.AutoXResizeMode(true);
   m_sub_chart1.AutoYResizeMode(true);
   m_sub_chart1.AutoXResizeRightOffset(125);
   m_sub_chart1.AutoYResizeBottomOffset(2);
//--- Adicione os gráficos
   m_sub_chart1.AddSubChart("EURUSD",PERIOD_D1);
//--- Crie o elemento de controle
   if(!m_sub_chart1.CreateStandardChart(x_gap,y_gap))
      return(false);
//--- Adicione um objeto à matriz comum de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_sub_chart1);
   return(true);
  }

Fig. 11 - Adicionando o gráfico.

Fig. 11.  Adicionando o gráfico.

Barra de progresso

Para que o usuário entenda o que o programa está fazendo agora, adicione uma barra de progresso à interface gráfica. Veja as propriedades do nosso exemplo (na mesma ordem que no código):

  • Altura total do elemento.
  • Altura do indicador (barra de progresso).
  • Recuo do indicador ao longo do eixo X.
  • Recuo do indicador ao longo do eixo Y.
  • Recuo da etiqueta principal ao longo do eixo X.
  • Recuo da etiqueta principal ao longo do eixo Y.
  • Recuo da etiqueta de porcentagem ao longo do eixo X.
  • Recuo da etiqueta de porcentagem ao longo do eixo Y.
  • Atributo do item suspenso (para ocultamento automático). 
  • Fonte. 
  • Cor da borda do indicador.
  • Cor de fundo do indicador.
  • Cor da barra de progresso do indicador.
  • Ajuste automático de largura
  • Recuo a partir da borda direita do elemento principal.

Abaixo, mostrarei exemplos de uso da barra de progresso

//+----------´--------------------------------------------------------+
//| Criando uma barra de progresso                                    |
//+----------´--------------------------------------------------------+
bool CProgram::CreateProgressBar(const int x_gap,const int y_gap,const string text)
  {
//--- Salve o ponteiro para o elemento principal
   m_progress_bar.MainPointer(m_status_bar);
//--- Propriedades
   m_progress_bar.YSize(17);
   m_progress_bar.BarYSize(14);
   m_progress_bar.BarXGap(0);
   m_progress_bar.BarYGap(1);
   m_progress_bar.LabelXGap(5);
   m_progress_bar.LabelYGap(2);
   m_progress_bar.PercentXGap(5);
   m_progress_bar.PercentYGap(2);
   m_progress_bar.IsDropdown(true);
   m_progress_bar.Font("Consolas");
   m_progress_bar.BorderColor(clrSilver);
   m_progress_bar.IndicatorBackColor(clrWhiteSmoke);
   m_progress_bar.IndicatorColor(clrLightGreen);
   m_progress_bar.AutoXResizeMode(true);
   m_progress_bar.AutoXResizeRightOffset(2);
//--- Criação do elemento
   if(!m_progress_bar.CreateProgressBar(text,x_gap,y_gap))
      return(false);
//--- Adicione o ponteiro para o elemento na base
   CWndContainer::AddToElementsArray(0,m_progress_bar);
   return(true);
  }

Revimos todos os controles que serão utilizados na interface gráfica do nosso EA. Apesar de, no momento, ser apenas uma interface gráfica, escreveremos todos os métodos necessários para fazer todo funcione de acordo com a idéia original.

Atualizações da biblioteca EasyAndFast

Na biblioteca EasyAndFastna classe CTablefoi desenvolvido totalmente o método público CTable::SortData(). Agora, como segundo argumento, você pode especificar a direção de classificação de tabela (opcional). Anteriormente, uma nova chamada do método CTable::SortData() iniciava a classificação na direção oposta à atual. Além disso, foram adicionados os métodos para obter a direção da classificação atual e o índice da coluna classificada. Agora, se a tabela for manualmente classificada pelo usuário (clicando no cabeçalho) e, em seguida, os dados na tabela não forem atualizados na mesma sequência, então após descobrir a direção de classificação atual, você poderá restaurá-la. 

//+----------´--------------------------------------------------------+
//| Classe para criar uma tabela                                      |
//+----------´--------------------------------------------------------+
class CTable : public CElement
  {
public:
...
   //--- Classificando dados segundo a coluna especificada
   void              SortData(const uint column_index=0,const int direction=WRONG_VALUE);
   //--- (1) Direção atual da classificação, (2) índice da matriz classificada
   int               IsSortDirection(void)             const { return(m_last_sort_direction);    }
   int               IsSortedColumnIndex(void)         const { return(m_is_sorted_column_index); }
...
  };
//+----------´--------------------------------------------------------+
//| Classificando dados segundo a coluna especificada                 |
//+----------´--------------------------------------------------------+
void CTable::SortData(const uint column_index=0,const int direction=WRONG_VALUE)
  {
//--- Sair, se você sair das bordas da tabela
   if(column_index>=m_columns_total)
      return;
//--- Índice a partir do qual é necessário começar a classificação
   uint first_index=0;
//--- Último índice
   uint last_index=m_rows_total-1;
//--- Sem controle de direção do usuário
   if(direction==WRONG_VALUE)
     {
      //--- A primeira vez será classificada em ordem crescente e, em seguida, toda vez na direção oposta
      if(m_is_sorted_column_index==WRONG_VALUE || column_index!=m_is_sorted_column_index || m_last_sort_direction==SORT_DESCEND)
         m_last_sort_direction=SORT_ASCEND;
      else
         m_last_sort_direction=SORT_DESCEND;
     }
   else
     {
      m_last_sort_direction=(ENUM_SORT_MODE)direction;
     }
//--- Lembre-se do índice da última coluna classificada de dados
   m_is_sorted_column_index=(int)column_index;
//--- Classificação
   QuickSort(first_index,last_index,column_index,m_last_sort_direction);
  }

Outro pequeno complemento adicionado à classe CKeys ao método CKeys::KeySymbol(). Anteriormente, o teclado numérico (o bloco separado de teclas no lado direito do teclado) não era processado. Agora você pode inserir números, assim como caracteres especiais, usando essa parte do teclado.

//+----------´--------------------------------------------------------+
//| Retornando o símbolo da tecla pressionada                         |
//+----------´--------------------------------------------------------+
string CKeys::KeySymbol(const long key_code)
  {
   string key_symbol="";
//--- Se você precisar inserir um espaço (a tecla "Space")
   if(key_code==KEY_SPACE)
     {
      key_symbol=" ";
     }
//--- Se você precisar inserir (1) um caractere alfabético ou (2) um caractere de tecla numérica ou (3) um caractere especial
   else if((key_code>=KEY_A && key_code<=KEY_Z) ||
           (key_code>=KEY_0 && key_code<=KEY_9) ||
           (key_code>=KEY_NUMLOCK_0 && key_code<=KEY_NUMLOCK_SLASH) ||
           (key_code>=KEY_SEMICOLON && key_code<=KEY_SINGLE_QUOTE))
     {
      key_symbol=::ShortToString(::TranslateKey((int)key_code));
     }
//--- Retornando o caractere
   return(key_symbol);
  }

As novas versões das classes  CTable e CKeys podem ser baixadas no final do artigo.

Fim do artigo

Você leu a primeira parte do artigo. Nele, analisamos como, sem muito esforço, você pode criar interfaces gráficas de qualquer complexidade para seus programas. Você pode continuar desenvolvendo este programa e usá-lo para seus próprios propósitos. Na segunda parte do artigo, mostrarei como trabalhar com a interface gráfica e, o mais importante, como preenchê-la com funcionalidades. 

Abaixo você pode baixar para o seu computador os arquivos de teste e um estudo mais detalhado do código apresentado no artigo.

Nome do arquivo Comentário
MQL5\Experts\TradePanel\TradePanel.mq5 EA - para negociação manual - com interface gráfica
MQL5\Experts\TradePanel\Program.mqh Arquivo com a classe do programa
MQL5\Experts\TradePanel\CreateGUI.mqh Arquivo com a implementação dos métodos para criar uma interface gráfica a partir da classe de programa no arquivo Program.mqh
MQL5\Include\EasyAndFastGUI\Controls\Table.mqh Classe atualizada CTable
MQL5\Include\EasyAndFastGUI\Keys.mqh Classe atualizada CKeys