Criando Painéis de Controle Ativo no MQL5 para Negociação
Евгений | 9 janeiro, 2014
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.
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)
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 "Mercado"
Figura 4. Elementos da aba "Pendente"
Aba de configurações:
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.
Figura6. Elementos da aba "Modificar/Fechar"
Posteriormente criamos as sub abas. Para trabalhar com negócios:
Figura 7. Elementos para trabalhar com posições
E para trabalhar com pedido:
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 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
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 "Pendente"
Figura 12. Elementos de uma aba "Modificar/Fechar"
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 "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:
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:
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
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
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
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:
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:
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
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.
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.