Criando Painéis de Controle Ativo no MQL5 para Negociação

9 janeiro 2014, 13:59
Евгений
0
2 283

Introdução

A eficiência é bastante essencial em um ambiente de trabalho, especialmente no trabalho de negociadores onde a rapidez e a precisão desempenham um importante papel. Enquanto preparam o terminal para o trabalho, cada um torna seu local de trabalho o mais confortável possível para si de modo a realizar as análises e entrar no mercado assim que possível. Mas a realidade da questão é que os desenvolvedores nem sempre podem agradar a todos e é impossível ajustar certas funções de acordo com o desejo de cada um.

Por exemplo, para um escalpelador (pessoa que faz pequenas transações na bolsa), cada fração de segundo e cada aperto na tecla "Novo Pedido" é importante e a configuração seguinte de todos os parâmetros pode ser decisiva quanto ao tempo.

Então como encontramos uma solução? A solução está na personalização dos elementos já que o MetaTrader 5 oferece componentes tão fabulosos como o "Botão", o "Editar" e o "Rótulo". Vamos fazer isso.


2. Opções do painel

Em primeiro lugar, vamos decidir que tipo de funções são essenciais para um painel. Colocaremos a ênfase principal nos negócios utilizando o painel, e, portanto, incluiremos as seguintes funções:

  • Posição de abertura
  • Colocação de um pedido pendente
  • Modificação da posição/pedido
  • Fechamento da posição
  • Exclusão de um pedido pendente

Além disso, nenhum prejuízo será causado ao adicionar a capacidade de personalizar o painel do esquema de cores, tamanhos de fonte e configurações para salvar. Vamos dar uma descrição mais detalhada de todos os elementos do futuro painel. Vamos especificar o nome do objeto, seu tipo e a descrição do seu objetivo para cada uma das funções do painel. O nome de cada objeto começará com "ActP" - isto será um tipo de legenda indicando que o objeto pertence ao painel.

2,1. Posições abertas

Abaixo vamos apresentar todos os parâmetros necessários para a abertura da posição e iremos executá-los clicando em um botão. As linhas auxiliares as quais são ativadas ao marcar uma caixa, irão nos auxiliar na configuração dos níveis Stop Loss e Take Profit. A escolha do tipo de execução será feita utilizando os botões de seleção.

Nome
Tipo
Descrição
ActP_buy_button1 Botão
Botão para um negócio de compra
ActP_sell_button1
Botão
Botão para um negócio de venda
ActP_DealLines_check1
Sinalizador
Ajustar/reajustar o sinalizador das linhas auxiliares
ActP_Exe_radio1
Botão de seleção
Grupo de botões de seleção para selecionar o tipo de negócio
ActP_SL_edit1
Campo de entrada
Campo para inserção de um Stop Loss
ActP_TP_edit1
Campo de entrada
Campo para inserção de um Take Profit
ActP_Lots_edit1
Campo de entrada
Campo para inserir a quantidade
ActP_dev_edit1
Campo de entrada
Campo para inserir um desvio tolerável durante a abertura
ActP_mag_edit1
Campo de entrada
Campo para inserir um número
ActP_comm_edit1 Campo de entrada Arquivado para inserir comentários

Tabela 1 Lista dos elementos do painel, "Abertura de negociação"

2.2 Colocando um pedido pendente

Abaixo vamos apresentar todos os parâmetros necessários para a colocação de um pedido pendente e colocá-los clicando em um botão. Linhas de apoio, as quais são ativadas marcando um sinalizador, ajudarão a ajustar o Stop Loss, o Take Profit, os níveis de ordem de limite e tempos de validade. A seleção do tipo de execução e do tipo de tempo de validade serão feitos com o auxílio de uns grupos de botões de seleção

Nome
Tipo
Descrição
ActP_buy_button2 Botão
Botão para configurar um pedido de compra
ActP_sell_button2
Botão
Botão para configurar um pedido de negócio
ActP_DealLines_check2
Sinalizador
As linhas auxiliares ajustam/reajustam o sinalizador
ActP_lim_check2 Sinalizador Sinalizador de ajuste/reajuste de ordem de parada-limite
ActP_Exe_radio2
Botão de seleção
Grupo de botões de seleção para selecionar o tipo de execução do pedido
ActP_exp_radio2 Botão de seleção Grupo de botões de seleção para selecionar o tipo de validade de pedido
ActP_SL_edit2
Campo de entrada
Campo para inserção de um Stop Loss
ActP_TP_edit2
Campo de entrada
Campo para inserção de um Take Profit
ActP_Lots_edit2
Campo de entrada
Campo para inserir a quantidade
ActP_limpr_edit2
Campo de entrada Campo para inserir o preço de um pedido de ordem de limite
ActP_mag_edit2
Campo de entrada
Campo para inserir o número mágico
ActP_comm_edit2 Campo de entrada Campo para comentários
ActP_exp_edit2 Campo de entrada Campo para inserir o tempo de validade
ActP_Pr_edit2 Campo de entrada Campo para inserir o preço da execução do pedido

Tabela 2 Lista dos elementos do painel "Colocando ordens pendentes"

2,3. Modificação / fechamento de negócios

Abaixo apresentamos todos os parâmetros necessários para a modificação e o fechamento de um negócio. As linhas auxiliares as quais são ativadas ao marcar uma caixa, irão nos auxiliar na instalação dos níveis Stop Loss e Take Profit. A seleção de negócios será gerada de uma lista suspensa.

Nome
Tipo
Descrição
ActP_ord_button5 Lista suspensa Lista de seleções para um negócio
ActP_mod_button4 Botão
Botão de modificação do negócio
ActP_del_button4
Botão
Botão de fechamento do negócio
ActP_DealLines_check4
Sinalizador
Linhas auxiliares ajustam/reajustam o sinalizador.
ActP_SL_edit4
Campo de entrada
Campo para inserção de um Stop Loss
ActP_TP_edit4
Campo de entrada
Campo para inserção de um Take Profit
ActP_Lots_edit4
Campo de entrada
Campo para inserir a quantidade
ActP_dev_edit4
Campo de entrada
Campo para inserir um desvio tolerável
ActP_mag_edit4
Campo de entrada
Campo para mostrar o número mágico (somente leitura)
ActP_Pr_edit4 Campo de entrada Campo para mostrar o preço de entrada (somente leitura)

Tabela 3. Lista dos elementos do painel "Modificação do negócio / fechamento"

2,4. Modificação / exclusão de pedidos

Abaixo apresentamos todos os parâmetros necessários para a modificação e a exclusão de pedidos pendentes. Linhas de apoio, que são ativadas marcando um sinalizador, ajudarão na instalação de stops, takes, níveis de ordem de limite e tempos de validade. A seleção do tipo do tempo de validade será gerada com o auxílio de uns grupos de botões de seleção. A seleção de pedidos será gerada de uma lista suspensa.

Nome
Tipo
Descrição
ActP_ord_button5 Lista suspensa Lista para selecionar o pedido
ActP_mod_button3 Botão
Botão de modificação do pedido
ActP_del_button3
Botão
Botão de exclusão do pedido
ActP_DealLines_check3
Sinalizador
Linhas auxiliares ajustam/reajustam o sinalizador.
ActP_exp_radio3 Botão de seleção Grupo de botões de seleção para selecionar o tipo de validade de um pedido
ActP_SL_edit3
Campo de entrada
Campo para inserção de um Stop Loss
ActP_TP_edit3
Campo de entrada
Campo para inserção de um take
ActP_Lots_edit3
Campo de entrada
Campo que mostra o volume (somente leitura)
ActP_limpr_edit3
Campo de entrada Campo para inserir o preço de um pedido de ordem de limite.
ActP_mag_edit3
Campo de entrada
Campo que mostra números mágicos (somente leitura)
ActP_comm_edit3 Campo de entrada Campo para comentários
ActP_exp_edit3 Campo de entrada Campo para inserir o tempo de validade
ActP_Pr_edit3 Campo de entrada Campo para inserir o preço da execução do pedido
ActP_ticket_edit3 Campo de entrada Campo que mostra o tíquete do pedido (somente leitura)

Tabela 4. Lista dos elementos do painel "Modificação / exclusão de pedidos"

2.5 Configurações

Abaixo iremos escolher a cor dos botões, rótulos e textos da lista suspensa além de montar vários tamanhos de fonte.

Nome
Tipo
Descrição
ActP_col1_button6 Lista suspensa
Lista de seleções de cor para os botões
ActP_col2_button6
Lista suspensa
Lista de seleção de cor para abas
ActP_col3_button6
Lista suspensa
Lista de seleção de cor de texto
ActP_font_edit6
Campo de entrada
Campo para especificar o tamanho do texto

Tabela 5. Lista de elementos do painel de "Configurações"

Um botão também é adicionado para criar a possibilidade de minimização do painel se o mesmo não estiver sendo usado. Você pode ter notado a presença de um tal instrumento chamado "linhas de apoio". O que são elas e porque precisamos delas? Por meio da utilização dessas linhas, seremos capazes de montar um Stop Loss, um Take Profit, o preço de acionamento de um pedido pendente, o preço de um pedido de ordem de limite (linhas horizontais), além do tempo de validade de um pedido adiado (linha vertical), bastando apenas usar o mouse para arrastar estas linhas para preço/tempo desejados.

Afinal, uma instalação visual é mais conveniente do que uma textual (manualmente inserindo preços / tempo dentro do campo apropriado) Além disso, estas linhas nos servirão como "destaques" do parâmetro de um pedido selecionado. Já que podem haver vários pedidos, as linhas sombreadas do terminal padrão, que geralmente mostram preços, podem se tornar muito confusas.


3. A abordagem geral para a criação da interface

Então apresentamos nosso objetivo com sucesso - criar uma forma de assistente gráfico dentro dos negócios. Para esse objetivo, precisamos da interface mais amigável. Em primeiro lugar, deve ficar claro que todos os elementos de controle (e haverão muitos) deverão ser criados utilizando software, e portanto a posição e tamanho dos objetos precisam ser pré-calculadas.

Agora, imagine que passamos por um período longo, tedioso e difícil calculando as coordenadas dos objetos, certificando que eles não iriam se sobrepôr uns aos outros e que fossem claramente visíveis, e então surge a necessidade de acrescentar um novo objeto, e todo o nosso plano precisa agora ser reconstruído!

Aqueles familiarizados com o ambiente do Rapid Application Development (Delphi, C + + Builder, etc.) sabem com que rapidez a interface do usuário mais complicada pode ser criada.

Vamos tentar implementá-la usando o MQL5. Em primeiro lugar, utilizando um mouse, localizamos os objetos de controle da maneira mais apropriada e ajustamos seus tamanhos. Depois, escrevemos um simples script que lê as propriedades de todos os objetos no gráfico e os grava em um arquivo e, quando necessário, seremos facilmente capazes de recuperar aquelas propriedades e reconstruir completamente os objetos em qualquer gráfico.

O código do script pode se parecer com este:

//+------------------------------------------------------------------+
//|                                  Component properties writer.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property script_show_inputs

input int interfaceID=1; //input parameter - the identifier of the stored interface
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //Open file for writing  
   int handle=FileOpen("Active_Panel_scheme_"+IntegerToString(interfaceID)+".bin", FILE_WRITE|FILE_BIN);
   if(handle!=INVALID_HANDLE)
     {
      //We will go all the objects on the chart
      for(int i=0;i<ObjectsTotal(0);i++)
        {
         string name=ObjectName(0,i);
         //And write their properties in the file
         FileWriteString(handle,name,100);
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_TYPE));

         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_COLOR));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STYLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_WIDTH));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BACK));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTED));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTABLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_READONLY));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_FONTSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STATE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BGCOLOR));

         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_TEXT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_FONT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,0),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,1),100);

         FileWriteDouble(handle,ObjectGetDouble(0,name,OBJPROP_PRICE));
        }
      //Close file
      FileClose(handle);
      Alert("Done!");
     }
  }
//+------------------------------------------------------------------+

Como você pode ver, o código é extremamente simples, ele escreve a um arquivo binário algumas propriedades de todos os objetos do gráfico. O ponto mais importante é não esquecer a ordem da sequência das propriedades gravadas ao ler o arquivo.

O script está pronto, então vamos nos voltar para a criação da interface.

E a primeira coisa que faremos é organizar o menu principal pelo tipo de suas abas. Por que precisamos de abas? Porque existem vários objetos e fazê-los caber na tela seria problemático. E já que os objetos são agrupados de acordo (veja tabela acima), é mais fácil colocar cada grupo em uma aba separada.

Assim, utilizando o menu do terminal Inserir -> Objetos -> Botão, criaremos cinco botões no topo do gráfico que servirão como nosso menu principal.

Figura 1. Abas do painel

Fig. 1 Abas do painel

Não esqueçamos que objetos podem ser facilmente duplicados ao selecionar um deles e então arrastá-lo com o mouse enquanto pressionamos a tecla "Ctrl". Ao fazer isso, criaremos uma cópia do objeto ao invés de realocar o original.

Deve ser dada uma atenção especial aos nomes dos objetos, sem esquecer que todos devem começar com "ActP". Além disso, adicionamos "principal" ao nome da cadeia o que indica que o objeto pertence a barra do menu principal.

Figura 2. Lista de objetos (abas do painel)

Figura 2. Lista de objetos (abas do painel)

De modo semelhante, vamos aplicar os conteúdos da aba ao novo gráfico. Os conteúdos de cada aba devem ser colocados em um gráfico separado!

Aba "Mercado"

Figura 3. Elementos da aba

Figura 3. Elementos da aba "Mercado"

Aba "Pendente":

Figura 4. Elementos da aba

Figura 4. Elementos da aba "Pendente"

Aba de configurações:

Figura 5. Elementos da aba

Figura 5. Elementos da aba "Configurações"

A última aba "Modificar / fechar é diferente, servirá para modificar / apagar pedidos pendentes, além de modificar e fechar transações de negócios. Será sensato dividir o trabalho com negócios e o trabalho com pedidos em duas sub abas separadas. Em primeiro lugar, vamos criar um botão o qual vai ativar a lista suspensa da qual escolheremos um pedido ou um negócio para trabalharmos.

Figura 6. Elementos da aba

Figura6. Elementos da aba "Modificar/Fechar"

Posteriormente criamos as sub abas. Para trabalhar com negócios:

Figura 7. Elementos para trabalhar com posições

Figura 7. Elementos para trabalhar com posições

E para trabalhar com pedido:

Figura 8. Sub aba para trabalhar com pedidos

Figura 8. Sub aba para trabalhar com pedidos

Concluído, a interface está criada

Aplicamos o script a cada um dos gráficos para salvar cada aba em um arquivo separado. O parâmetro de entrada "interfaceID"deve ser diferente para cada aba:

  • 0 - Página inicial
  • 1 - Mercado
  • 2 - Pendente
  • 3 - Botão para ativação da lista de seleção de negócio / pedido
  • 4 - Configurações
  • 6 - Sub aba para trabalhar com negócios
  • 7 - Sub aba para trabalhar com pedidos

A aba de número 5 corresponde ao botão "Minimizar janela" no menu principal, então não existem objetos nele e podemos pulá-lo.

Após todas essas manipulações, os seguintes arquivos aparecerão na pasta do diretório do terminal -> MQL5 ->:

Figura 8. Lista de arquivos dos esquemas dos painéis

Figura 9. Lista de arquivos com esquemas dos painéis


4. Baixando elementos da interface

Agora os elementos da interface estão armazenados e estão prontos para serem colocados para funcionar. Para iniciar, vamos determinar o local onde nosso painel ficará localizado. Se o colocarmos diretamente no gráfico principal, ele bloqueará o gráfico de preços, o que é muito inoportuno. Portanto, será mais sensato colocar o painel na sub janela do gráfico principal. Um indicador pode criar este painel.

Vamos criá-lo:

#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window //place the indicator in a separate window

int OnInit()
  {
//--- indicator buffers mapping
   //Set the short name of the indicator
   IndicatorSetString(INDICATOR_SHORTNAME, "AP");
//---
   return(0);
  }

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[])
  {
//---
//--- return value of prev_calculated for next call
   return(rates_total);
  }

O código é muito simples porque a função principal deste indicador é a criação de sub janelas, ao invés de realizar vários cálculos. A única coisa que faremos será instalar um nome "curto" do indicador pelo qual podemos encontrar sua sub janela. Vamos compilar e aplicar um gráfico para o indicador, e uma janela aparecerá.

Agora, vamos focar no painel do Consultor Especialista. Vamos criar um novo Consultor Especialista.

A função OnInit () vai conter os seguintes operadores:

double Bid,Ask;         //variables for current prices
datetime time_current;  //time of last tick
int wnd=-1;             //index of the window with an indicator
bool last_loaded=false; //flag indicating whether it's a first initialization or not
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   //Start the timer at intervals of 1 second
   EventSetTimer(1); 
   //Get the latest prices
   get_prices();
   //Define the window with an indicator
   wnd=ChartWindowFind(0,"AP");
   //If the first initialization - create interface
   if(!last_loaded) create_interface();
//---
   return(0);
  }

Aqui inicializamos um temporizador (o porquê de se fazer isso será explicado abaixo), obtemos os preços mais recentes do mercado, utilizando o ChartWindowFind, localizamos a janela do indicador e o salvamos como varável. Sinalizador last_loaded - indica se é ou não a primeira vez na qual o Consultor Especialista é inicializado. Esta informação será necessária de modo a evitar o recarregamento da interface durante a reinicialização.

A função create_interface () se apresenta da seguinte forma:

//+------------------------------------------------------------------+
//| Function of the interface creation                               |
//+------------------------------------------------------------------+
void create_interface()
  {
   //if reset settings is selected
   if(Reset_Expert_Settings)
     {
     //Reset
      GlobalVariableDel("ActP_buttons_color");
      GlobalVariableDel("ActP_label_color");
      GlobalVariableDel("ActP_text_color");
      GlobalVariableDel("ActP_font_size");
     }

   //Create the main menu interface
   ApplyScheme(0);
   //Create the interface tab "Market"
   ApplyScheme(1);
   //Set all objects as unmarked
   Objects_Selectable("ActP",false);
   //redraw the chart
   ChartRedraw();
  }

O primeiro passo é verificar o parâmetro de entrada "configurações de reajuste" e, se estiver instalado, apague as variáveis globais responsáveis pelas configurações. Como esta ação afeta o painel será descrito abaixo. Além disso, a função ApplyScheme () criará uma interface de um arquivo.

//+------------------------------------------------------------------+
//| The function for the interface loading                           |
//| ID - ID of the saved interface                                   |
//+------------------------------------------------------------------+
bool ApplyScheme(int ID)
  {
   string fname="Active_Panel_scheme_custom_"+IntegerToString(ID)+".bin";
   //download the standard scheme if there isn't saved scheme 
   if(!FileIsExist(fname)) fname="Active_Panel_scheme_"+IntegerToString(ID)+".bin";
   //open file for reading
   int handle=FileOpen(fname,FILE_READ|FILE_BIN);
   //file opened
   if(handle!=INVALID_HANDLE)
     {
      //Loading all objects
      while(!FileIsEnding(handle))
        {
         string obj_name=FileReadString(handle,100);
         int _wnd=wnd;
         //the auxiliary lines are in the main window
         if(StringFind(obj_name,"line")>=0) _wnd=0;
         ENUM_OBJECT obj_type=FileReadInteger(handle);
         //creating object
         ObjectCreate(0, obj_name, obj_type, _wnd, 0, 0);   
         //and apply the properties 
         ObjectSetInteger(0,obj_name,OBJPROP_XDISTANCE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_YDISTANCE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_XSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_YSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_COLOR,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_STYLE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_WIDTH,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_BACK,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_SELECTED,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_SELECTABLE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_READONLY,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_FONTSIZE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_STATE,FileReadInteger(handle));
         ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,FileReadInteger(handle));

         ObjectSetString(0,obj_name,OBJPROP_TEXT,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_FONT,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_BMPFILE,0,FileReadString(handle,100));
         ObjectSetString(0,obj_name,OBJPROP_BMPFILE,1,FileReadString(handle,100));

         ObjectSetDouble(0,obj_name,OBJPROP_PRICE,FileReadDouble(handle));

         //Set color for the objects
         if(GlobalVariableCheck("ActP_buttons_color") && obj_type==OBJ_BUTTON)
            ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,GlobalVariableGet("ActP_buttons_color"));
         if(GlobalVariableCheck("ActP_label_color") && obj_type==OBJ_LABEL)
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,GlobalVariableGet("ActP_label_color"));
         if(GlobalVariableCheck("ActP_text_color") && (obj_type==OBJ_EDIT || obj_type==OBJ_BUTTON))
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,GlobalVariableGet("ActP_text_color"));
         if(GlobalVariableCheck("ActP_font_size") && (obj_type==OBJ_EDIT || obj_type==OBJ_LABEL))
            ObjectSetInteger(0,obj_name,OBJPROP_FONTSIZE,GlobalVariableGet("ActP_font_size"));
         //Set global variable font size
         if(obj_name=="ActP_font_edit6" && GlobalVariableCheck("ActP_font_size"))
            ObjectSetString(0,obj_name,OBJPROP_TEXT,IntegerToString(GlobalVariableGet("ActP_font_size")));
        }
      //Close file
      FileClose(handle);
      return(true);
     }
   return(false);
  }

Mais uma vez, não há nada complicado sobre isso. A função abrirá o arquivo desejado com um esquema de interface pré-salva e o criará na janela a qual identificamos previamente (janela do indicador). Além disso, selecionamos as cores dos objetos e os tamanhos das fontes das variáveis globais do terminal.

A função Objects_Selectable () torna todos os objetos, exceto as linhas auxiliares, desmarcados de modo a ligar a animação dos botões e evitar a exclusão acidental de um objeto necessário.

//+------------------------------------------------------------------+
//| Function of setting objects as unselectable                      |
//+------------------------------------------------------------------+
void  Objects_Selectable(string IDstr,bool flag)
  {
   //Check all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //If the object belongs to the panel
      if(StringFind(n,IDstr)>=0)
        {
         //Lines remain untouched
         if(!flag)
            if(StringFind(n,"line")>-1) continue; 
         //Set everything unselectable except the lines
         ObjectSetInteger(0,n,OBJPROP_SELECTABLE,flag); 
        }
     }
  }

Agora vamos dar uma olhada na função OnTick(). Ela nos servirá para obtermos os preços mais recentes no mercado.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //Get the latest prices
   get_prices();
  }

A função get_prices() tem a forma:

//+------------------------------------------------------------------+
//| Function obtain information on tick                              |
//+------------------------------------------------------------------+
void get_prices()
  {
   MqlTick tick;
   //if the tick was
   if(SymbolInfoTick(Symbol(),tick))
     {
      //obtain information
      Bid=tick.bid;
      Ask=tick.ask;
      time_current=tick.time;
     }
  }

E não se esqueça do OnDeinit ():

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   //if the deinitialisation reason isn't the timeframe or symbol change
   if(reason!=REASON_CHARTCHANGE)
     {
      //reset initialization flag
      last_loaded=false;  
      //Delete all panel objects
      ObjectsDeleteAll_my("ActP");
      //Delete files with the saved state of the tabs
      FileDelete("Active_Panel_scheme_custom_1.bin");
      FileDelete("Active_Panel_scheme_custom_2.bin");
      FileDelete("Active_Panel_scheme_custom_3.bin");
      FileDelete("Active_Panel_scheme_custom_4.bin");
      FileDelete("Active_Panel_scheme_custom_5.bin");
     }
   //otherwise set a flag
   else last_loaded=true;
   //stop the timer
   EventKillTimer();
  }

Em primeiro lugar verifique a causa da deinicialização: se for devido a uma mudança de um prazo e / ou símbolos, não excluiremos o item do painel. Em todos os outros casos, exclua todos os itens utilizando a função ObjectsDeleteAll_my ().

//+------------------------------------------------------------------+
//| The function deletes all panel objects                           |
//| IDstr - object identifier                                        |
//+------------------------------------------------------------------+
void  ObjectsDeleteAll_my(string IDstr)
  {
   //check all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //if the name contains the identifier - remove the object
      if(StringFind(n,IDstr)>=0) ObjectDelete(0,n);
     }
  }

Após compilar e executar o Consultor Especialista, obtemos o seguinte resultado.

Figura 10. Exemplo do trabalho do Consultor Especialista

Figura 10. Exemplo do trabalho do Consultor Especialista

No entanto há uma pequena utilização de tudo isso até sermos capazes de fazer esses objetos responderem a nossas manipulações.


5. Gerenciamento de evento

A interface está criada agora temos que colocá-la para funcionar. Todas as nossas ações com objetos geram eventos específicos. A função OnChartEvent OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) é o mecanismo que lida com eventos ChartEvent . De todos os eventos estamos interessados nos seguintes:

  • CHARTEVENT_CLICK - clique no gráfico
  • CHARTEVENT_OBJECT_ENDEDIT - edição finalizada do campo de entrada
  • CHARTEVENT_OBJECT_CLICK - clique no objeto do gráfico

Em nosso caso, o parâmetro da função id indica que a ID do evento, sparam - indica o nome do objeto o qual gera esse evento e todos os demais parâmetros não são de interesse para nós.

O primeiro evento que exploraremos é o - clique no botão do menu principal.

5,1. Gerenciamento dos eventos do menu principal

Lembre-se que o menu principal consiste em cinco botões. Quando um deles estiver apertado, ele deve ficar em um estado pressionado, direcionar-nos a interface direita e carregar as abas apropriadas. Depois todos os outros botões do menu devem ir para o modo não pressionado.

//+------------------------------------------------------------------+
//| Event handlers                                                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //main menu button click
      if(sparam=="ActP_main_1") {Main_controls_click(1); ChartRedraw(); return;}
       //Here we execute the corresponding operators
      if(sparam=="ActP_main_2") {Main_controls_click(2); ChartRedraw(); return;}
      if(sparam=="ActP_main_3") {Main_controls_click(3); ChartRedraw(); return;}
      if(sparam=="ActP_main_4") {Main_controls_click(4); ChartRedraw(); return;}
      if(sparam=="ActP_main_5") {Main_controls_click(5); ChartRedraw(); return;}
   ...   
   }
...
}

Se houve um clique no botão do menu, então fizemos a função Main_controls_click(). Vamos redesenhar o gráfico usando o ChartRedraw(), e completar as funções. Devemos completar a execução porque somente um objeto pode ser apertado por vez e, portanto, todas as demais implementações nos levarão a um desperdício de tempo do CPU.

//+------------------------------------------------------------------+
//| Tab processor                                                    |
//| ID - index of clicked tab                                        |
//+------------------------------------------------------------------+
void Main_controls_click(int ID)
  {
   int loaded=ID;
   //we will go all tabs
   for(int i=1;i<6;i++)
     {
      //for all except the selected set inactive
      if(i!=ID) 
        {
         //also remember the last active tab
         if(ObjectGetInteger(0,"ActP_main_"+IntegerToString(i),OBJPROP_STATE)==1) loaded=i;
         ObjectSetInteger(0,"ActP_main_"+IntegerToString(i),OBJPROP_STATE,0);
        }
     }
//if(loaded==ID) return;
   //set an active state for the selected
   ObjectSetInteger(0,"ActP_main_"+IntegerToString(ID),OBJPROP_STATE,1);
   //delete the drop-down lists
   DeleteLists("ActP_orders_list_"); 
   DeleteLists("ActP_color_list_");
   //and set the list buttons to the unpressed state
   ObjectSetInteger(0,"ActP_ord_button5",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col1_button6",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col2_button6",OBJPROP_STATE,0);
   ObjectSetInteger(0,"ActP_col3_button6",OBJPROP_STATE,0);
   //save state of the last active tab
   SaveScheme(loaded);
   //remove old tab
   DeleteScheme("ActP");
   //and load a new
   ApplyScheme(ID);
   //Set all objects as unselected
   Objects_Selectable("ActP",false);
  }

Fomos apresentados as funções Objects_Selectable() and ApplyScheme() e depois vamos nos voltar para a função DeleteLists().

A função SaveScheme() salva um arquivo de interface, de modo que os objetos guardam todas as suas propriedades durante um recarregamento:

//+------------------------------------------------------------------+
//| Interface saving function                                        |
//+------------------------------------------------------------------+
void SaveScheme(int interfaceID)
  {
   //open file for writing
   int handle=FileOpen("Active_Panel_scheme_custom_"+IntegerToString(interfaceID)+".bin",FILE_WRITE|FILE_BIN);
   //if file opened
   if(handle!=INVALID_HANDLE)
     {
      //go all the chart objects
      for(int i=0;i<ObjectsTotal(0);i++)
        {
         string name=ObjectName(0,i);
         //if the object belongs to the panel
         if(StringFind(name,"ActP")<0) continue;
         //and it isn't a tab
         if(StringFind(name,"main")>=0) continue; 
         //write the object properties to a file
         FileWriteString(handle,name,100);
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_TYPE));

         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YDISTANCE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_XSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_YSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_COLOR));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STYLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_WIDTH));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BACK));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTED));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_SELECTABLE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_READONLY));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_FONTSIZE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_STATE));
         FileWriteInteger(handle,ObjectGetInteger(0,name,OBJPROP_BGCOLOR));

         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_TEXT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_FONT),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,0),100);
         FileWriteString(handle,ObjectGetString(0,name,OBJPROP_BMPFILE,1),100);

         FileWriteDouble(handle,ObjectGetDouble(0,name,OBJPROP_PRICE));
        }
      //Close file
      FileClose(handle);
     }
  }

A função DeleteScheme() elimina os objetos da aba.

//+------------------------------------------------------------------+
//| Function to delete all the panel objects, except tabs            |
//+------------------------------------------------------------------+
void  DeleteScheme(string IDstr)
  {
   //we will go through all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //remove everything but the tab
      if(StringFind(n,IDstr)>=0 && StringFind(n,"main")<0) ObjectDelete(0,n);
     }
  }

Assim, ao executarmos a função Main_controls_click() vamos eliminar a aba antiga, salvando a previamente e carregaremos uma nova.

Ao compilar o Consultor Especialista, veremos os resultados.

Agora clicaremos no botão do menu principal, carregaremos as novas abas, mantendo as no estado das abas originais.

Figura 11. Itens de uma aba

Figura 11. Itens de uma aba "Pendente"

Figura 12. Elementos de uma aba

Figura 12. Elementos de uma aba "Modificar/Fechar"


Figura 12. Elementos da aba

Figura 13. Elementos da aba "Configurações"

Com isto podemos finalizar a manipulação do menu principal já que agora ele cumpre suas funções completamente.

5,2. Gerenciamento do evento do componente "Sinalizador"

A configuração das linhas auxiliares e de pedidos de ordem de limite é feita utilizando os componentes "sinalizador", mas ela está na lista de objetos gráficos do MT5. Então vamos criá-la: Existe um objeto "rótulo gráfico" o qual é realmente uma imagem que tem um estado de "ligado"e um estado de "desligado". O estado pode ser variado clicando no objeto. Uma imagem separada pode ser ajustada para cada estado. Escolha uma imagem para cada um dos estados:

  • Habilitado
  • Desabilitado

Vamos ajustar as imagens nas propriedades do objeto:

Figura 13. Configurando as propriedades do elemento

Figura 13. Configurando as propriedades do elemento "sinalizador"

Deve ser lembrado que para que as imagens estejam disponíveis na lista, elas precisão estar localizadas na pasta "Terminal folder-> MQL5-> Images" e ter a extensão "bmp".

Vamos nos voltar para o eventos de processamento os quais ocorrem quando você clica em um objeto. Usaremos o exemplo do sinalizador o qual é responsável pelo posicionamento das linhas auxiliares na abertura do negócio.

      //click on the flag of the setting of auxiliary lines during transaction opening
      if(sparam=="ActP_DealLines_check1")
      {
         //Check the flag state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //If the flag is set
         if(selected)
         {
            //Retrieve the value of the stop loss and take profit from the input fields
            string SL_txt=ObjectGetString(0, "ActP_SL_edit1", OBJPROP_TEXT);
            string TP_txt=ObjectGetString(0, "ActP_TP_edit1", OBJPROP_TEXT);
            double val_SL, val_TP;
            
            //If the Stop field is not empty
            //save the value
            if(SL_txt!="")
               val_SL=StringToDouble(SL_txt);

            //if empty
            else
            {
               //Take the max. and min. prices of chart
               double pr_max=ChartGetDouble(0, CHART_PRICE_MAX);
               double pr_min=ChartGetDouble(0, CHART_PRICE_MIN);
               //Set the stop at the 1/3 of the chart price range
               val_SL=pr_min+(pr_max-pr_min)*0.33;
            }   
            
            //Similarly processes the Take
            if(TP_txt!="")
               val_TP=StringToDouble(TP_txt);
            else
            {
               double pr_max=ChartGetDouble(0, CHART_PRICE_MAX);
               double pr_min=ChartGetDouble(0, CHART_PRICE_MIN);
               val_TP=pr_max-(pr_max-pr_min)*0.33;
            }      
            //Move the line to new positions
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, val_SL);  
            ObjectSetDouble(0, "ActP_TP_line1", OBJPROP_PRICE, val_TP);  
         }
          //If the flag is unset
         else
         {
             //remove the lines
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, 0);
            ObjectSetDouble(0, "ActP_TP_line1", OBJPROP_PRICE, 0);
         }
          //redraw the chart
         ChartRedraw();
          //and finish the function 
         return;
      }

O mesmo método é utilizado para os sinalizadores os quais são responsáveis pelo processamento e instalação de linhas auxiliares no fechamento / modificação da aba de pedidos pendentes. Portanto, não entraremos nos detalhes sobre eles neste artigo. Aqueles que desejam se familiarizar com eles pode usar o código do Consultor Especialista.

A configuração do sinalizador de pedidos de ordem de limite na aba "Pendente" tem o seguinte gerenciador:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...

   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //Click on the orders stoplimit check box
      if(sparam=="ActP_limit_check2")
      {
         //Check the flag state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected) //if flag is set
         {
            //set the new color for the price edit
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_BGCOLOR, White);
            //enable it for the edit
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_READONLY, false);
            //установим в поле значение текущей цены
            //Set the current price as the field value
            ObjectSetString(0, "ActP_limpr_edit2", OBJPROP_TEXT, DoubleToString(Bid, _Digits));
            //if the auxiliary lines are allowed
            //move them
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetDouble(0, "ActP_lim_line2", OBJPROP_PRICE, Bid);
         }  
          //if flag is unset
         else
         {
            //set the field unavailable for editing
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_BGCOLOR, LavenderBlush);
            //set the field color
            ObjectSetInteger(0, "ActP_limpr_edit2", OBJPROP_READONLY, true);
            //and "empty" text
            ObjectSetString(0, "ActP_limpr_edit2", OBJPROP_TEXT, "");
            //if the auxiliary lines are allowed
            //move them to the zero point
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetDouble(0, "ActP_lim_line2", OBJPROP_PRICE, 0);
         }
      }       
   ...   
   }
...
}

Agora concluímos o trabalho com sinalizadores. Vamos considerar o seguinte objeto de nossa própria produção - o "grupo dos botões de seleção".

5,3. Gerenciamento do evento do componente "grupo dos botões de seleção"

Utilizando este componente, selecionaremos o tipo de um negócio e o tipo de validade do pedido. Assim como no caso dos sinalizadores, usaremos abas gráficas mas, dessa vez, com novas imagens.

  • Habilitado
  • Desabilitado

Mas aqui o problema é complicado pela necessidade de reajuste de todos os botões de seleção, exceto para aquele em que você clique, para um estado inativo. Considere o exemplo do botão de seleção do tipo de execução do pedido :

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on radion button 1 - order execution type
      if(sparam=="ActP_Exe1_radio2")
      {
         //check the radio button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //set the appropriate state
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         //if it selected
         if(selected)
         {
            //reset the other radio buttons
            ObjectSetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE, false);
            //redraw the chart
            ChartRedraw();
            //finish the execution of function
            return;
         }
         //redraw the chart
         ChartRedraw();
         //finish the execution of function
         return;
      }
 
       //Similarly for the radio button 2
      if(sparam=="ActP_Exe2_radio2") 
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         if(selected)
         {
            ObjectSetInteger(0, "ActP_Exe1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE, false);
            ChartRedraw();
            return;
         }
         ChartRedraw();
         return;
      }   

       //Similarly for the radio button 3
      if(sparam=="ActP_Exe3_radio2") 
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         if(selected)
         {
            ObjectSetInteger(0, "ActP_Exe1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE, false);
            ChartRedraw();
            return;
         }
         ChartRedraw();
         return;
      }     
   ...   
   }
...
}

Os botões de seleção do tipo de validade do pedido somente diferem pelo fato de quando você clica no terceiro, você deve realizar um passo adicional - você precisa ajustar uma nova data no registro de tempo da validade de um pedido:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click on the 3rd radio button - order expiration date
      if(sparam=="ActP_exp3_radio2")
      {
         //checking it state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         ObjectSetInteger(0,sparam,OBJPROP_STATE, 1);
         //if it selected
         if(selected)
         {
            //reset the remained radio buttons
            ObjectSetInteger(0, "ActP_exp1_radio2", OBJPROP_STATE, false);
            ObjectSetInteger(0, "ActP_exp2_radio2", OBJPROP_STATE, false);
            //set the new date to the date edit field
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_BGCOLOR, White);
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_READONLY, false);
            ObjectSetString(0, "ActP_exp_edit2", OBJPROP_TEXT, TimeToString(time_current));
            //if auxiliary lines are allowed 
            //set the new time line
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetInteger(0, "ActP_exp_line2", OBJPROP_TIME, time_current);
            ChartRedraw();
            return;
         }

          //if it isn't selected
         else
         {
            //set the edit field as not available for editing
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_BGCOLOR, LavenderBlush);
            ObjectSetInteger(0, "ActP_exp_edit2", OBJPROP_READONLY, true);
            //remove the auxiliary line
            if(ObjectGetInteger(0, "ActP_DealLines_check2", OBJPROP_STATE)==1)
               ObjectSetInteger(0, "ActP_exp_line2", OBJPROP_TIME, 0);
         }      
         ChartRedraw();
         return;
   ...   
   }
...
}

Agora concluímos o trabalho com os botões de seleção.

5,4. Criando e gerenciando eventos de listas suspensas

Vamos utilizar a lista suspensa para escolher pedidos / negócios para modificação / fechamento / exclusão e para o painel de seleções de cor. Vamos começar com a lista de negócios / pedidos.

A primeira coisa que aparece na aba "Modificação / fechamento" é um botão chamado "Selecione um pedido -->". Este será o botão que ativa a lista. Quando você clicar nele, a lista suspensa deve se desdobrar e, após fazermos nossa seleção, ela deve se dobrar novamente. Vamos dar uma olhada no gerenciador CHARTEVENT_OBJECT_CLICK deste botão:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the drop-down list activate button (order select)

      if(sparam=="ActP_ord_button5")
      {
         //check status
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //the list is activated
         if(selected)// the list is selected
         {
            //delete interface
            DeleteScheme("ActP", true);
            //arrays for serving the information about the orders
            string info[100];
            //array for the tickets
            int tickets[100];
            //initialize it
            ArrayInitialize(tickets, -1);
            //get orders info
            get_ord_info(info, tickets);
            //create the list
            create_list(info, tickets);
         }
          //the list isn't active
         else
         {
            //delete it
            DeleteLists("ActP_orders_list_");
         }
          //redraw the chart
         ChartRedraw();
          //finish the function
         return;
      }      
   ...   
   }
...
}

Nosso principal objetivo é determinar se os negócios/pedidos estão no mercado e, se estiverem, extrair informações deles e mostrá-los na lista. A função get_ord_info() cumpre esse papel:

//+------------------------------------------------------------------+
//| The function for obtaining the information about orders          |
//+------------------------------------------------------------------+
void get_ord_info(string &info[],int &tickets[])
  {
   //initialize the counter
   int cnt=0;
   string inf;
   //if there is an open position
   if(PositionSelect(Symbol()))
     {
     //combine all order infomation in a single line
      double vol=PositionGetDouble(POSITION_VOLUME);
      int typ=PositionGetInteger(POSITION_TYPE);
      if(typ==POSITION_TYPE_BUY) inf+="BUY ";
      if(typ==POSITION_TYPE_SELL) inf+="SELL ";
      inf+=DoubleToString(vol, MathCeil(MathAbs(MathLog(vol)/MathLog(10))))+" lots";
      inf+=" at "+DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN), Digits());
      //write the results
      info[cnt]=inf;
      tickets[cnt]=0;
      //increment the counter
      cnt++;
     }

   //all orders
   for(int i=0;i<OrdersTotal();i++)
     {
      //get ticket
      int ticket=OrderGetTicket(i);
      //if order symbol is equal to chart symbol
      if(OrderGetString(ORDER_SYMBOL)==Symbol())
        {
         //combine all order infomation in a single line
         inf="#"+IntegerToString(ticket)+" ";
         int typ=OrderGetInteger(ORDER_TYPE);
         double vol=OrderGetDouble(ORDER_VOLUME_CURRENT);
         if(typ==ORDER_TYPE_BUY_LIMIT) inf+="BUY LIMIT ";
         if(typ==ORDER_TYPE_SELL_LIMIT) inf+="SELL LIMIT ";
         if(typ==ORDER_TYPE_BUY_STOP) inf+="BUY STOP ";
         if(typ==ORDER_TYPE_SELL_STOP) inf+="SELL STOP ";
         if(typ==ORDER_TYPE_BUY_STOP_LIMIT) inf+="BUY STOP LIMIT ";
         if(typ==ORDER_TYPE_SELL_STOP_LIMIT) inf+="SELL STOP LIMIT ";
         inf+=DoubleToString(vol, MathCeil(MathAbs(MathLog(vol)/MathLog(10))))+" lots";
         inf+=" at "+DoubleToString(OrderGetDouble(ORDER_PRICE_OPEN), Digits());
         //write the results
         info[cnt]=inf;
         tickets[cnt]=ticket;
         //increment the counter
         cnt++;
        }
     }
  }

Ela combinará em um bloco informações e tíquetes de pedido e negócios.

Além disso, a função create_list() criará uma lista baseada nesta informação.

//+------------------------------------------------------------------+
//| The function creates list of positions                           |
//| info - array for the positions                                   |
//| tickets - array for the tickets                                  |
//+------------------------------------------------------------------+
void create_list(string &info[],int &tickets[])
  {
   //get the coordinates of the list activation button
   int x=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_XDISTANCE);
   int y=ObjectGetInteger(0, "ActP_ord_button5", OBJPROP_YDISTANCE)+ObjectGetInteger(0, "ActP_ord_button5", OBJPROP_YSIZE);
   //get colors
   color col=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_COLOR);
   color bgcol=ObjectGetInteger(0,"ActP_ord_button5",OBJPROP_BGCOLOR);
   //get window height
   int wnd_height=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,wnd);
   int y_cnt=0;
   //proceed arrays
   for(int i=0;i<100;i++)
     {
      //break if end reached
      if(tickets[i]==-1) break;
      //calculate the list item coordinates
      int y_pos=y+y_cnt*20;
      //if the windiow limits are reachedl, start a new column
      if(y_pos+20>wnd_height) {x+=300; y_cnt=0;}
      y_pos=y+y_cnt*20;
      y_cnt++;
      string name="ActP_orders_list_"+IntegerToString(i)+" $"+IntegerToString(tickets[i]);
      //create element
      create_button(name,info[i],x,y_pos,300,20);
      //and set its properties
      ObjectSetInteger(0,name,OBJPROP_COLOR,col);
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
      ObjectSetInteger(0,name,OBJPROP_STATE,0);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
      ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgcol);
     }
  }

E finalmente, as funções DeleteLists () apagam os elementos da lista:

//+------------------------------------------------------------------+
//| The function for the list deletion                               |
//+------------------------------------------------------------------+
void  DeleteLists(string IDstr)
  {
   //proceed all objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //delete lists
      if(StringFind(n,IDstr)>=0 && StringFind(n,"main")<0) ObjectDelete(0,n);
     }
  }

Então agora, quando você clicar no botão de ativação, uma lista é criada. Precisamos fazer isto funcionar já que, a cada clique em qualquer elemento da lista, alguma ação especifica deve acontecer. Especificamente: o carregamento de uma interface para operar com um pedido, e o preenchimento desta interface com informações sobre o pedido/negócio.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   // Event - click on a graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click not on an item of order selection list
      if(StringFind(sparam, "ActP_orders_list_")<0)
      {
          //Remove it
         DeleteLists("ActP_orders_list_");
          //Set the activation button to "unpressed"
         ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
          //redraw chart
         ChartRedraw();
      }     
       //Click on the order selection list item
      else
      {
          //Set a new name for the activation button
         ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, ObjectGetString(0, sparam, OBJPROP_TEXT));
          //Set the activation button to "unpressed"
         ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
          //get ticket from the list item description
         int ticket=StringToInteger(StringSubstr(sparam, StringFind(sparam, "$")+1));
          //Load the interface
         SetScheme(ticket);
          //and delete the list
         DeleteLists("ActP_orders_list_");
          //chart redraw
         ChartRedraw();
      }
   ...   
   }
...
}

é aqui que as coisas ficam complicadas. Já que não sabemos antecipadamente o tamanho da lista e os nomes de seus objetos, teremos que recuperar informações dela recuperando o nome do elemento da lista. A função SetScheme() irá montar a interface apropriada - para operar com um negócio ou com um pedido pendente:

//+------------------------------------------------------------------+
//| The function sets the interface depending on type:               |
//| position or pending order                                        |
//| t - ticket                                                       |
//+------------------------------------------------------------------+
void SetScheme(int t)
  {
   //if position
   if(t==0)
     {
      //check for its presence
      if(PositionSelect(Symbol()))
        {
         //delete old scheme
         DeleteScheme("ActP",true);
         //and apply new
         ApplyScheme(6);
         //set position parameters
         SetPositionParams();
         //the objects are unavailable for the selection
         Objects_Selectable("ActP",false);
        }
     }
   //if order
   if(t>0)
     {
      //check for its presence
      if(OrderSelect(t))
        {
         //delete old scheme
         DeleteScheme("ActP",true);
         //and apply new
         ApplyScheme(7);
         //set order parameters
         SetOrderParams(t);
         //the objects are unavailable for the selection
         Objects_Selectable("ActP",false);
        }
     }
  }

As funções SetPositionParams() e SetOrderParams() instalam as propriedades necessárias da interface carregada:

//+------------------------------------------------------------------+
//| Set position parameters for the objects                          |
//+------------------------------------------------------------------+
void SetPositionParams()
  {
   //if position is exists
   if(PositionSelect(Symbol()))
     {
      //get its parameters
      double pr=PositionGetDouble(POSITION_PRICE_OPEN);
      double lots=PositionGetDouble(POSITION_VOLUME);
      double sl=PositionGetDouble(POSITION_SL);
      double tp=PositionGetDouble(POSITION_TP);
      double mag=PositionGetInteger(POSITION_MAGIC);
      //and set new values to the objects
      ObjectSetString(0,"ActP_Pr_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(pr)));
      ObjectSetString(0,"ActP_lots_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(lots)));
      ObjectSetString(0,"ActP_SL_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(sl)));
      ObjectSetString(0,"ActP_TP_edit4",OBJPROP_TEXT,str_del_zero(DoubleToString(tp)));
      if(mag!=0) ObjectSetString(0,"ActP_mag_edit4",OBJPROP_TEXT,IntegerToString(mag));
      //redraw chart
      ChartRedraw();
     }
   //if there isn't position, show message 
   else MessageBox("There isn't open position for "+Symbol());
  }
//+------------------------------------------------------------------+
//| Set pending order parameters for the objects                     |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
void SetOrderParams(int ticket)
  {
   //if order exists
   if(OrderSelect(ticket) && OrderGetString(ORDER_SYMBOL)==Symbol())
     {
      //get its parameters
      double pr=OrderGetDouble(ORDER_PRICE_OPEN);
      double lots=OrderGetDouble(ORDER_VOLUME_CURRENT);
      double sl=OrderGetDouble(ORDER_SL);
      double tp=OrderGetDouble(ORDER_TP);
      double mag=OrderGetInteger(ORDER_MAGIC);
      double lim=OrderGetDouble(ORDER_PRICE_STOPLIMIT);
      datetime expir=OrderGetInteger(ORDER_TIME_EXPIRATION);
      ENUM_ORDER_TYPE type=OrderGetInteger(ORDER_TYPE);
      ENUM_ORDER_TYPE_TIME expir_type=OrderGetInteger(ORDER_TYPE_TIME);
      
      //of order type is stoplimit, modify the interface
      if(type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT)
        {
         //set new value to the order price edit
         ObjectSetString(0,"ActP_limpr_edit3",OBJPROP_TEXT,DoubleToString(lim,_Digits));
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_BGCOLOR,White);
         //set order price available for edit
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_READONLY,false);
        }
      //if order type isn't stoplimit, modify the interface
      else
        {
         ObjectSetString(0,"ActP_limpr_edit3",OBJPROP_TEXT,"");
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_BGCOLOR,LavenderBlush);
         ObjectSetInteger(0,"ActP_limpr_edit3",OBJPROP_READONLY,true);
        }

      //check expiration type
      //and set interface elements
      switch(expir_type)
        {
         case ORDER_TIME_GTC:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,1);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,0);
            break;
           }
         case ORDER_TIME_DAY:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,1);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,0);
            break;
           }
         case ORDER_TIME_SPECIFIED:
           {
            ObjectSetInteger(0,"ActP_exp1_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp2_radio3",OBJPROP_STATE,0);
            ObjectSetInteger(0,"ActP_exp3_radio3",OBJPROP_STATE,1);
            //in addition, set new value to the edit
            ObjectSetString(0,"ActP_exp_edit3",OBJPROP_TEXT,TimeToString(expir));
            break;
           }
        }
      //set new values for the objects
      ObjectSetString(0,"ActP_Pr_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(pr)));
      ObjectSetString(0,"ActP_lots_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(lots)));
      ObjectSetString(0,"ActP_SL_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(sl)));
      ObjectSetString(0,"ActP_TP_edit3",OBJPROP_TEXT,str_del_zero(DoubleToString(tp)));
      ObjectSetString(0,"ActP_ticket_edit3",OBJPROP_TEXT,IntegerToString(ticket));
      if(mag!=0) ObjectSetString(0,"ActP_mag_edit3",OBJPROP_TEXT,IntegerToString(mag));
      ChartRedraw();
     }
   //if there isn't such order, show message
   else MessageBox("There isn't an order with ticket "+IntegerToString(ticket)+" for "+Symbol());
  }

E o toque final - a lista deve ser apagada quando você clicar no gráfico, utilizando CHARTEVENT_CLICK para esse evento:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - is click on the chart
   if(id==CHARTEVENT_CLICK)
   {
       //delete all lists
      DeleteLists("ActP_orders_list_");
      DeleteLists("ActP_color_list_"); 
       //Set the activate buttons to the unpressed state
      ObjectSetInteger(0, "ActP_ord_button5", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col2_button6", OBJPROP_STATE, 0);
      ObjectSetInteger(0, "ActP_col3_button6", OBJPROP_STATE, 0);
      ChartRedraw(); 
      return;
   }
...
}

Como resultado, temos uma boa lista suspensa:

Figura 14. Um exemplo de

Figura14. Um exemplo de "Modificar/Fechar" do painel da lista suspensa

Agora precisamos criar uma lista de seleção de cor na aba Configurações.

Considere os gerenciadores dos botões de ativação:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //Click on the button to activate the colors drop-down list
      if(sparam=="ActP_col1_button6")
      {
          //check state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //the list is active
         if(selected)//the list is active
         {
             //creat list
            create_color_list(100, "ActP_col1_button6", 1);
             //Set the position of the remaining buttons to "unpressed"
            ObjectSetInteger(0, "ActP_col2_button6", OBJPROP_STATE, 0);
            ObjectSetInteger(0, "ActP_col3_button6", OBJPROP_STATE, 0);
             //delete other lists
            DeleteLists("ActP_color_list_2");
            DeleteLists("ActP_color_list_3");                 
         }
          //the list isn't selected
         else
         {
             //delete it
            DeleteLists("ActP_color_list_");
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }
   ...   
   }
...
}

Aqui seguimos o mesmo método da lista de seleção do pedido.

A função de criar uma lista se difere:

//+------------------------------------------------------------------+
//| Function for creating the colors list                            |
//| y_max - maximal list widthа                                      |
//| ID - list ID                                                     |
//| num - interface number                                           |
//+------------------------------------------------------------------+
void create_color_list(int y_max,string ID,int num)
  {
  //Get the coordinates of the list activation button
   int x=ObjectGetInteger(0,ID,OBJPROP_XDISTANCE);
   int y=ObjectGetInteger(0, ID, OBJPROP_YDISTANCE)+ObjectGetInteger(0, ID, OBJPROP_YSIZE);
   //get color
   color col=ObjectGetInteger(0,ID,OBJPROP_COLOR);
   //and window width
   int wnd_height=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,wnd);
   y_max+=y;
   int y_cnt=0;
   //We will go through the colors array
   for(int i=0;i<132;i++)
     {
      color bgcol=colors[i];
      //calculate list item coordinates
      int y_pos=y+y_cnt*20;
      //if we reached the boundaries of the window, start new column
      if(y_pos+20>wnd_height || y_pos+20>y_max) {x+=20; y_cnt=0;}
      y_pos=y+y_cnt*20;
      y_cnt++;
      //create new element
      string name="ActP_color_list_"+IntegerToString(num)+ID+IntegerToString(i);
      create_button(name,"",x,y_pos,20,20);
      //and set its properties
      ObjectSetInteger(0,name,OBJPROP_COLOR,col);
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
      ObjectSetInteger(0,name,OBJPROP_STATE,0);
      ObjectSetInteger(0,name,OBJPROP_BGCOLOR,bgcol);
     }
  }

Mais a frente, vamos desenvolver o processo de clique para o elemento da lista:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click isn't on the color list button
      if(StringFind(sparam, "ActP_color_list_1")<0)
      {
          //delete list
         DeleteLists("ActP_color_list_1");
          //set color list activation button to "unpressed"
         ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
          //redraw chart
         ChartRedraw();
      }     
       //click on the color list
      else
      {
          //get color from the list
         color col=ObjectGetInteger(0, sparam, OBJPROP_BGCOLOR);
          //set it for all the buttons
         SetButtonsColor(col);
          //set button to unpressed
         ObjectSetInteger(0, "ActP_col1_button6", OBJPROP_STATE, 0);
          //delete list
         DeleteLists("ActP_color_list_1");
          //redraw chart
         ChartRedraw();
      }
   ...   
   }
...
}

A função SetButtonsColor() ajusta a cor dos botões:

//+------------------------------------------------------------------+
//| The function sets color for all buttons                          |
//| col - color                                                      |
//+------------------------------------------------------------------+
void SetButtonsColor(color col)
  {
   //We will go through all the objects
   for(int i=ObjectsTotal(0);i>=0;i--)
     {
      string n=ObjectName(0,i);
      //If the object belongs to the panel and its has a button type
      //set color
      if(StringFind(n,"ActP")>=0 && ObjectGetInteger(0,n,OBJPROP_TYPE)==OBJ_BUTTON) 
         ObjectSetInteger(0,n,OBJPROP_BGCOLOR,col); 
     }
   //set global variable
   GlobalVariableSet("ActP_buttons_color",col);
  }

Vejamos os resultados abaixo:

Figura 15. Configurando as cores dos botões

Figura15. Configurando as cores dos botões

As listas de seleção de cor e de rótulos de texto são parecidas. Como resultado, podemos fazer o painel bem colorido em poucos cliques:

Figura 16. Cores modificadas dos painéis, botões, e texto

Figura 16. Cores modificadas dos painéis, botões, e texto

Agora terminamos quanto as listas. Vamos prosseguir para os campos de entrada.

5,5. Gerenciamento do evento do campo de registro

Um campo de registro irá gerar um evento CHARTEVENT_OBJECT_ENDEDIT, o qual ocorre na conclusão da edição do texto no campo. A única razão pela qual precisamos gerir este evento é devido a configuração nas linhas auxiliares para os preços, relevante aos preços nos campos de registro.

Vamos considerar o exemplo de uma linha de retenção:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //End edit event
   if(id==CHARTEVENT_OBJECT_ENDEDIT)//end edit event
   {
   ...
      //if edit field is SL field
      if(sparam=="ActP_SL_edit1")
      {
        //and auxiliary lines are enabled
         if(ObjectGetInteger(0,"ActP_DealLines_check1",OBJPROP_STATE)==1)
         {
            //get text from the field
            double sl_val=StringToDouble(ObjectGetString(0, "ActP_SL_edit1", OBJPROP_TEXT));
            //move lines at new position
            ObjectSetDouble(0, "ActP_SL_line1", OBJPROP_PRICE, sl_val);
         }
         //redraw chart
         ChartRedraw();
         //it ins't necessary to proceed the other objects, because the event from the one
         return;
      }
   ...   
   }
...
}

Outros campos de registro são processados de modo semelhante.

5.6 Gerenciando eventos do temporizador

O temporizador é utilizado para monitorar as linhas auxiliares. Dessa forma, quando você desloca as linhas, os valores dos preços aos quais elas estão vinculadas se deslocam automaticamente para o campo de entrada. Com cada ponto do temporizador, a função OnTimer() é executada.

Considere o exemplo da colocação das linhas Stop Loss e Take Profit acompanhando a aba ativa "Mercado".

void OnTimer()// Timer handler
{
   //panel 1 is active
   if(ObjectGetInteger(0, "ActP_main_1", OBJPROP_STATE)==1)
   {  
      //if auxiliary lines are allowed
      if(ObjectGetInteger(0,"ActP_DealLines_check1",OBJPROP_STATE)==1)
      {
         //set new values to the edit fields
         double sl_pr=NormalizeDouble(ObjectGetDouble(0, "ActP_SL_line1", OBJPROP_PRICE), _Digits);
         //stop loss
         ObjectSetString(0, "ActP_SL_edit1", OBJPROP_TEXT, DoubleToString(sl_pr, _Digits));
         //take profit
         double tp_pr=NormalizeDouble(ObjectGetDouble(0, "ActP_TP_line1", OBJPROP_PRICE), _Digits);
         ObjectSetString(0, "ActP_TP_edit1", OBJPROP_TEXT, DoubleToString(tp_pr, _Digits));
      }   
   }
   ...
   //redraw chart
   ChartRedraw();
}
//+------------------------------------------------------------------+

O acompanhamento de outras linhas é implementado da mesma forma.


6. Realizando operações de negócios

Então nesse momento preenchemos todos os campos de registro necessários, caixas de seleção, linhas e botões de seleção. Agora chegou o momento de tentar realizar alguns negócios baseado em todos os dados que temos.

6,1. Negociação de abertura

A aba "Do mercado" contém os botões "Comprar"e "Vender". Se os campos estiverem preenchidos corretamente, um negócio deve ser implementado quando clicarmos em qualquer um dos botões.

Vamos dar uma olhada nos gerenciadores desse botões:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the Buy button
      if(sparam=="ActP_buy_button1")
      {
          //check its state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if it "pressed"
         if(selected)
         {
             //try to perform a deal
            deal(ORDER_TYPE_BUY);
             //and set the button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }

          //redraw chart
         ChartRedraw();
          //and finish the function execution
         return;
      }
      //******************************************
       //the similar for the sell button
      if(sparam=="ActP_sell_button1")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {
            deal(ORDER_TYPE_SELL);
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
         ChartRedraw();
         return;
      }     
   ...   
   }
...
}

Você nota que a função deal() está funcionando.

//+------------------------------------------------------------------+
//| Deal function                                                    |
//+------------------------------------------------------------------+
int deal(ENUM_ORDER_TYPE typ)
  {
   //get the data from the objects
   double SL=StringToDouble(ObjectGetString(0,"ActP_SL_edit1",OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit1", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit1",OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0, "ActP_Magic_edit1", OBJPROP_TEXT));
   int dev=StringToInteger(ObjectGetString(0, "ActP_Dev_edit1", OBJPROP_TEXT));
   string comm=ObjectGetString(0,"ActP_Comm_edit1",OBJPROP_TEXT);
   ENUM_ORDER_TYPE_FILLING filling=ORDER_FILLING_FOK;
   if(ObjectGetInteger(0,"ActP_Exe2_radio1",OBJPROP_STATE)==1) filling=ORDER_FILLING_IOC;
   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_DEAL;
   req.symbol=Symbol();
   req.volume=lots;
   req.price=Ask;
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.deviation=dev;
   req.type=typ;
   req.type_filling=filling;
   req.magic=mag;
   req.comment=comm;
   //send order
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Nada de sobrenatural. Em primeiro lugar lemos as informações necessárias dos objetos e, baseado nelas, criamos um pedido de negócios.

Vamos verificar nosso trabalho:

Figura 17. Operações de negócios - o resultado da execução de negócio de compra

Figura 17. Operações de negócios - o resultado da execução de negócio de compra

Como você pode ver, o negócio de compra foi concretizado com sucesso.

6,2. Configurando um pedido pendente

Os botões "Comprar"e "Vender" na aba "Pendente" são responsáveis pela colocação de ofertas pendentes.

Vamos considerar os gerenciadores:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the pending order set button
      if(sparam=="ActP_buy_button2")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         //if it pressed
         if(selected)
         {
            ENUM_ORDER_TYPE typ; 
            //get the pending order from the edit
            double pr=NormalizeDouble(StringToDouble(ObjectGetString(0, "ActP_Pr_edit2", OBJPROP_TEXT)), Digits());
            //if it isn't stoplimit order
            if(ObjectGetInteger(0, "ActP_limit_check2", OBJPROP_STATE)==0)
            {
               //if the order price is below the current price, set limit order
               if(Ask>pr) typ=ORDER_TYPE_BUY_LIMIT; 
               //overwise - stop order
               else typ=ORDER_TYPE_BUY_STOP;
            }
              //if stoplimit order is specified
            else
            {
               //set operation type
               typ=ORDER_TYPE_BUY_STOP_LIMIT;
            }   
              //try to place order
            order(typ);
              //set button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
      //******************************************  

       //similar for the sell pending order
      if(sparam=="ActP_sell_button2")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {
            ENUM_ORDER_TYPE typ;
            double pr=NormalizeDouble(StringToDouble(ObjectGetString(0, "ActP_Pr_edit2", OBJPROP_TEXT)), Digits());
            if(ObjectGetInteger(0, "ActP_limit_check2", OBJPROP_STATE)==0)
            {
               if(Bid<pr) typ="ORDER_TYPE_SELL_LIMIT;
               else typ=ORDER_TYPE_SELL_STOP;
            }
            else
            {
               typ=ORDER_TYPE_SELL_STOP_LIMIT;
            }   
            order(typ);
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
         ChartRedraw();
         return;
      }        
   ...   
   }
...
}

Aqui determinamos o tipo de pedidos futuros baseados na relação do preço de mercado atual para o preço fixo, após o qual a função order() determinar o pedido.

//+------------------------------------------------------------------+
//| The function places an order                                     |
//+------------------------------------------------------------------+
int order(ENUM_ORDER_TYPE typ)
  {
   //get the order details from the objects
   double pr=StringToDouble(ObjectGetString(0,"ActP_Pr_edit2",OBJPROP_TEXT));
   double stoplim=StringToDouble(ObjectGetString(0,"ActP_limpr_edit2",OBJPROP_TEXT));
   double SL=StringToDouble(ObjectGetString(0, "ActP_SL_edit2", OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit2", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit2",OBJPROP_TEXT));
   datetime expir=StringToTime(ObjectGetString(0,"ActP_exp_edit2",OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0,"ActP_Magic_edit2",OBJPROP_TEXT));
   string comm=ObjectGetString(0,"ActP_Comm_edit2",OBJPROP_TEXT);
   ENUM_ORDER_TYPE_FILLING filling=ORDER_FILLING_FOK;
   if(ObjectGetInteger(0, "ActP_Exe2_radio2", OBJPROP_STATE)==1) filling=ORDER_FILLING_IOC;
   if(ObjectGetInteger(0, "ActP_Exe3_radio2", OBJPROP_STATE)==1) filling=ORDER_FILLING_RETURN;
   ENUM_ORDER_TYPE_TIME expir_type=ORDER_TIME_GTC;
   if(ObjectGetInteger(0, "ActP_exp2_radio2", OBJPROP_STATE)==1) expir_type=ORDER_TIME_DAY;
   if(ObjectGetInteger(0, "ActP_exp3_radio2", OBJPROP_STATE)==1) expir_type=ORDER_TIME_SPECIFIED;

   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res; 
   req.action=TRADE_ACTION_PENDING;
   req.symbol=Symbol();
   req.volume=lots;
   req.price=NormalizeDouble(pr,Digits());
   req.stoplimit=NormalizeDouble(stoplim,Digits());
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.type=typ;
   req.type_filling=filling;
   req.type_time=expir_type;
   req.expiration=expir;
   req.comment=comm;
   req.magic=mag;
   //place order
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Vamos verificar nosso trabalho:

Figura 18. Operações de negócios - a colocação do pedido pendente resultante

Figura 18. Operações de negócios - a colocação do pedido pendente resultante

A ordem de limite de compra está configurada com sucesso.

6,3. Modificação de Posição

O botão Editar na aba "Modificar/Fechar" é responsável pela modificação da posição selecionada:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the graphic object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the modify position button
      if(sparam=="ActP_mod_button4")
      {
          //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if it pressed
         if(selected)//if pressed
         {
            //modify position
            modify_pos();
            //delete the elements of the scheme
            DeleteScheme("ActP" ,true);
            //and reset it (update the interface)
            SetScheme(0);
            //set the button to the unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }     
   ...   
   }
...
}

A função Modify_pos() é diretamente responsável pela modificação:

//+------------------------------------------------------------------+
//| The function modifies the position parameters                    |
//+------------------------------------------------------------------+
int modify_pos()
  {
   if(!PositionSelect(Symbol())) MessageBox("There isn't open position for symbol "+Symbol(),"Message");
   //get the details from the edit objects
   double SL=StringToDouble(ObjectGetString(0,"ActP_SL_edit4",OBJPROP_TEXT));  
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit4", OBJPROP_TEXT));
   int dev=StringToInteger(ObjectGetString(0,"ActP_dev_edit4",OBJPROP_TEXT));
   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;  
   req.action=TRADE_ACTION_SLTP;
   req.symbol=Symbol();
   req.sl=NormalizeDouble(SL, _Digits);
   req.tp=NormalizeDouble(TP, _Digits);
   req.deviation=dev;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Resultados:

Figura 19. Operações de negócios - o resultado da modificação das propriedades dos negócios (conjunto TP e SL)

Figura19. Operações de negócios - o resultado da modificação das propriedades dos negócios (conjunto TP e SL)


Os níveis do Stop Loss e do Take Profit estão alterados com sucesso.

6,4. Fechamento de posição

O botão Fechar na aba "Modificar/Fechar" é responsável pelo fechamento (possivelmente parcial) da posição:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the close button
      if(sparam=="ActP_del_button4")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if pressed
         if(selected)
         {
            //try to close position
            int retcode=close_pos();
            //if successful
            if(retcode==10009)
            {
               //delete scheme elements
               DeleteScheme("ActP" ,true);
               //set the new text for the list activisation
               ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, "Select order -->");
            }
            //set button state to unpressed
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //finish the execution of function
         return;
      }     
   ...   
   }
...
}

A função close_pos() é responsável pelo fechamento:

//+------------------------------------------------------------------+
//| Closes the position                                              |
//+------------------------------------------------------------------+
int close_pos()
  {
   if(!PositionSelect(Symbol())) MessageBox("There isn't open position for symbol "+Symbol(),"Message");
   //get the position details from the objects
   double lots=StringToDouble(ObjectGetString(0,"ActP_lots_edit4",OBJPROP_TEXT));
   if(lots>PositionGetDouble(POSITION_VOLUME)) lots=PositionGetDouble(POSITION_VOLUME);
   int dev=StringToInteger(ObjectGetString(0, "ActP_dev_edit4", OBJPROP_TEXT));
   int mag=StringToInteger(ObjectGetString(0, "ActP_mag_edit4", OBJPROP_TEXT));

   //prepare request
   MqlTradeRequest req;
   MqlTradeResult res;

   //the opposite deal is dependent on position type
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
     {
      req.price=Bid;
      req.type=ORDER_TYPE_SELL;
     }
   else
     {
      req.price=Ask;
      req.type=ORDER_TYPE_BUY;
     }

   req.action=TRADE_ACTION_DEAL;
   req.symbol=Symbol();
   req.volume=lots;
   req.sl=0;
   req.tp=0;
   req.deviation=dev;
   req.type_filling=ORDER_FILLING_FOK;
   req.magic=mag;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

O resultado - lotes 1.5 fechados de três das transações selecionadas:

Figura 20. Negócios - fechamento parcial da posição

Figura20. Negócios - fechamento parcial da posição

6,5. Modificação de um pedido pendente

O botão Editar na aba "Modificação/fechamento" é responsável pela modificação da posição selecionada:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the chart graphic object
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
      //click on the order modify button
      if(sparam=="ActP_mod_button3")
      {
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
         if(selected)
         {     
            //get the order ticket from the edit
            string button_name=ObjectGetString(0, "ActP_ord_button5", OBJPROP_TEXT);
            long ticket=StringToInteger(StringSubstr(button_name, 1, StringFind(button_name, " ")-1));
            //modifying an order
            modify_order(ticket);
            //update interface
            DeleteScheme("ActP" ,true);
            SetScheme(ticket);
            //set button to unpressed state
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
   ...   
   }
...
}

A função Modify_order () é responsável pela modificação:

//+------------------------------------------------------------------+
//| The function modifies an order                                   |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
int modify_order(int ticket)
  {
   //get the order details from the corresponding chart objects
   double pr=StringToDouble(ObjectGetString(0,"ActP_Pr_edit3",OBJPROP_TEXT)); 
   double stoplim=StringToDouble(ObjectGetString(0,"ActP_limpr_edit3",OBJPROP_TEXT));
   double SL=StringToDouble(ObjectGetString(0, "ActP_SL_edit3", OBJPROP_TEXT));
   double TP=StringToDouble(ObjectGetString(0, "ActP_TP_edit3", OBJPROP_TEXT));
   double lots=StringToDouble(ObjectGetString(0,"ActP_Lots_edit3",OBJPROP_TEXT));
   datetime expir=StringToTime(ObjectGetString(0,"ActP_exp_edit3",OBJPROP_TEXT));
   ENUM_ORDER_TYPE_TIME expir_type=ORDER_TIME_GTC;
   if(ObjectGetInteger(0, "ActP_exp2_radio3", OBJPROP_STATE)==1) expir_type=ORDER_TIME_DAY;
   if(ObjectGetInteger(0, "ActP_exp3_radio3", OBJPROP_STATE)==1) expir_type=ORDER_TIME_SPECIFIED;

   //prepare request to modify
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_MODIFY;
   req.order=ticket;
   req.volume=lots;
   req.price=NormalizeDouble(pr,Digits());
   req.stoplimit=NormalizeDouble(stoplim,Digits());
   req.sl=NormalizeDouble(SL, Digits());
   req.tp=NormalizeDouble(TP, Digits());
   req.type_time=expir_type;
   req.expiration=expir;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Vejamos o resultado - um pedido está modificado com sucesso:

Figura 21. Modificação de pedido pendente

Figura 21. Modificação de pedido pendente

6,6. Excluindo um pedido pendente

O botão Excluir na aba "Modificação/fechamento" é responsável pela exclusão do pedido selecionado:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
...
   //Event - click on the graphic object on the chart
   if(id==CHARTEVENT_OBJECT_CLICK)
   {
   ...
       //click on the order delete button
      if(sparam=="ActP_del_button3")
      {
         //check the button state
         bool selected=ObjectGetInteger(0,sparam,OBJPROP_STATE);
          //if pressed
         if(selected)
         {
            //get the ticket from the list
            string button_name=ObjectGetString(0, "ActP_ord_button5", OBJPROP_TEXT);
            long ticket=StringToInteger(StringSubstr(button_name, 1, StringFind(button_name, " ")-1));
            //try to delete order
            int retcode=del_order(ticket);
            //if successful
            if(retcode==10009)
            {
               //delete all objects of the scheme
               DeleteScheme("ActP" ,true);
               //set new text for the list activation button
               ObjectSetString(0, "ActP_ord_button5", OBJPROP_TEXT, "Select an order -->");
            }
             //set button state to unpressed
            ObjectSetInteger(0, sparam, OBJPROP_STATE, 0);
         }
          //redraw chart
         ChartRedraw();
          //and finish the execution of function
         return;
      }     
   ...   
   }
...
}

A função del_order() é responsável pela exclusão de pedidos:

//+------------------------------------------------------------------+
//| The function for pending order deletion                          |
//| ticket - order ticket                                            |
//+------------------------------------------------------------------+
int del_order(int ticket)
  {
   //prepare request for deletion
   MqlTradeRequest req;
   MqlTradeResult res;
   req.action=TRADE_ACTION_REMOVE;
   req.order=ticket;
   //send request
   OrderSend(req,res);
   //show message with the result
   MessageBox(RetcodeDescription(res.retcode),"Message");
   //return retcode
   return(res.retcode);
  }

Vejamos o resultado - o pedido está removido.

Figura 22. Negócios - exclusão de um pedido pendente

Fig. 22 Operações de negociação - remoção de uma ordem pendente


Conclusão

Finalmente todas as funções do painel foram testadas e estão funcionando com sucesso.

Espera-se que o conhecimento adquirido pela leitura deste artigo ajudará você com o desenvolvimento de painéis de controle ativos os quais servirão a você como auxílios insubstituíveis para trabalhar no mercado.

Para começar com o painel você precisa descompactar o arquivo dentro da uma pasta no terminal, depois aplicar o indicador AP ao gráfico, e somente depois inicializar o Painel Ativo do Consultor Especialista.

Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/62

Arquivos anexados |
Criação e publicação de relatórios de negócios e notificação SMS Criação e publicação de relatórios de negócios e notificação SMS

Os negociantes nem sempre têm a habilidade e desejam ficar sentados na frente do terminal de negócio por horas. Especialmente se o sistema de negócio for mais ou menos formalizado e puder automaticamente identificar alguns dos estados do mercado. Este artigo descreve como gerar um relatório de resultados de negócios (utilizando o Consultor Especialista, o indicador ou o script) como um arquivo HTML e carregá-lo por FTP para o servidor WWW. Também levaremos em consideração o envio de notificações de eventos de negócios por SMS para o celular.

Um exemplo de uma estratégia de negociação baseada na diferença de fuso horário em diferentes continentes Um exemplo de uma estratégia de negociação baseada na diferença de fuso horário em diferentes continentes

Navegando na internet é fácil encontrar muitas estratégias que darão a você uma série de recomendações. Vamos pegar uma abordagem interna e ver o processo de criação de estratégia, com base nas diferenças de fusos horários em diferentes continentes.

Conexão do Expert Advisor com ICQ no MQL5 Conexão do Expert Advisor com ICQ no MQL5

Este artigo descreve o método de troca de informação entre o Expert Advisor e os usuários do ICQ, são apresentados vários exemplos. O material fornecido será interessante para aqueles que queiram receber informações de negócio remotamente de um terminal de cliente através de um cliente ICQ em seus celulares ou PDA.

Criando um jogo de "Snake" no MQL5 Criando um jogo de "Snake" no MQL5

Este artigo descreve um exemplo da programação do jogo "Snake". No MQL5, a programação do jogo tornou-se possível principalmente por causa dos recursos de manuseio de evento. A programação orientada a objeto simplifica em muito este processo. Neste artigo, você aprenderá os recursos de processamento de eventos, os exemplos de uso de classes da Biblioteca MQL5 Padrão e detalhes das chamadas de função periódica.