Guia prático do MQL5: Propriedades de posição no painel de informações personalizado
Introdução
Agora criaremos um Consultor Especialista simples que obterá propriedades de posição no símbolo atual e as exibirá no painel personalizado de informações durante as negociações manuais. O painel de informações será criado usando objetos gráficos e a informação exibida será atualizada a cada ponto. Isto será muito mais conveniente do que ter que executar manualmente todas as vezes o script descrito no artigo anterior da série, chamado "Guia prático do MQL5: Obtendo as propriedades das posições".
Desenvolvendo um Consultor Especialista
Vamos começar com objetos gráficos. Para criar o painel de informações, precisamos de objetos para o plano de fundo, cabeçalho, nomes e propriedades de valores de posição. O plano de fundo e o cabeçalho requerem um retângulo que não se move com o preço. O retângulo pode ser criado usando um objeto gráfico como a Etiqueta Retângulo ou Editar, enquanto os nomes e valores das propriedades de objeto serão feitas usando as Etiquetas de Texto.
Antes de prosseguirmos com o código, primeiro prepararemos uma disposição para o painel de informações. Sua conveniência está no fato de que podemos rapidamente modificar qualquer propriedade na janela de configurações e personalizar o visual do painel de informações.
Todo objeto tem uma janela de configurações que pode ser aberta do menu de contextos de um objeto selecionado. A janela de configurações pode também ser aberta da Lista de Objeto (Ctrl+B) ao selecionar o objeto necessário clicando em Propriedades. A disposição do painel de informações é mostrada abaixo. Também pode ser usado para estimar tamanhos e coordenadas quando escrevendo um código. Quando o código para o painel de informações está pronto, você terá de deletar os objetos de disposição manualmente já que o Consultor Especialista não será capaz de 'vê-los' e, portanto, não irá removê-los do gráfico.
Fig. 1. Preparando a disposição para o painel de informações.
Agora precisamos criar um modelo para o Consultor Especialista. Isto pode ser feito rapidamente para o script. No Assistente do MQL5, a opção Consultor Especialista (modelo) é selecionada por padrão. Passaremos pelas próximas etapas sem fazer quaisquer mudanças para as opções já que elas não são necessárias agora. Clique em Terminar e você verá um modelo como abaixo:
//+------------------------------------------------------------------+ //| PositionPropertiesPanel.mq5 | //| Copyright 2012, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2012, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
Você pode notar que o modelo de Consultor Especialista é diferente do modelo de script. Além das propriedades do programas (#property), há três funções principais: OnInit(), OnDeinit() e OnTick().
A função OnInit() é chamada quando carregando o programa, modificando parâmetros externos, compilando o programa, desde que o programa está naquele momento adicionado ao gráfico, e quando modificando o símbolo ou período. Caso necessário, você pode inicializar certas variáveis ou arranjos (arrays) nesta função para ser possível trabalhar com elas mais tarde.
A função OnDeinit() é chamada quando você exclui o programa do gráfico, muda a conta, símbolo ou período. Todos possíveis motivos de deinicialização são fornecidos na Referência MQL5. Este Consultor Especialista utilizará uma função definida pelo usuário, GetDeinitReasonText(), que converte o identificador do motivo de deinicialização (o parâmetro da função OnDeinit()) para texto.
E, finalmente, a função OnTick(). Ela é chamada toda vez em que há um novo ponto no símbolo cujo gráfico o Consultor Especialista está operando no momento.
Vamos agora preparar todas as constantes, variáveis e arranjos que usaremos em nosso Consultor Especialista. Iremos colocá-las no começo do programa. Primeiro, defina as variáveis cujos valores permanecem inalterados por todo o programa:
//--- #define INFOPANEL_SIZE 14 // Size of the array for info panel objects #define EXPERT_NAME MQL5InfoString(MQL5_PROGRAM_NAME) // Name of the Expert Advisor //---
Isto é seguido por variáveis globais para propriedades de posição:
//--- GLOBAL VARIABLES bool pos_open=false; // Flag of presence/absence of an open position string pos_symbol=""; // Symbol long pos_magic=0; // Magic number string pos_comment=""; // Comment double pos_swap=0.0; // Swap double pos_commission=0.0; // Commission double pos_price=0.0; // Position price double pos_cprice=0.0; // Current price of the position double pos_profit=0.0; // Profit/Loss of the position double pos_volume=0.0; // Position volume double pos_sl=0.0; // Stop Loss of the position double pos_tp=0.0; // Take Profit of the position datetime pos_time=NULL; // Position opening time long pos_id=0; // Position identifier ENUM_POSITION_TYPE pos_type=WRONG_VALUE; // Position type
Após as variáveis, declararemos arranjos de nomes de objetos gráficos. Estes objetos exibirão propriedades de posição e seus valores no gráfico. Para este propósito, criaremos dois arranjos de cadeias (strings) e imediatamente inicializaremos seus elementos para valores. Em colchetes, usamos o valor da constante INFOPANEL_SIZE declarado no início do programa. Isto é, haverá 14 elementos em cada arranjo.
// Array of names of objects that display names of position properties string positionPropertyNames[INFOPANEL_SIZE]= { "name_pos_symbol", "name_pos_magic", "name_pos_comment", "name_pos_swap", "name_pos_commission", "name_pos_price", "name_pos_cprice", "name_pos_profit", "name_pos_volume", "name_pos_sl", "name_pos_tp", "name_pos_time", "name_pos_id", "name_pos_type" }; //--- // Array of names of objects that display values of position properties string positionPropertyValues[INFOPANEL_SIZE]= { "value_pos_symbol", "value_pos_magic", "value_pos_comment", "value_pos_swap", "value_pos_commission", "value_pos_price", "value_pos_cprice", "value_pos_profit", "value_pos_volume", "value_pos_sl", "value_pos_tp", "value_pos_time", "value_pos_id", "value_pos_type" }; //---
Usando estes nomes, podemos programaticamente encontrar o objeto necessário no gráfico e definir ou modificar suas propriedades como texto exibido, cor, tamanho etc. Além disso, estes nomes serão exibidos na janela Lista de Objetos (Ctrl+B) após serem criados no gráfico. Mas agora você não será capaz de visualizá-los já que os objetos criados pelo programa MQL5 estão ocultos por padrão. Para fazê-los visíveis, você deve clicar em Listar Todos na janela Lista de Objetos. Esta característica ajuda a separar os objetos criados manualmente daqueles criados programaticamente o que é admitidamente bem conveniente.
Além disso, precisaremos de funções definidas pelo usuário as quais serão empregadas pelo Consultor Especialista para criar objetos gráficos. A função oferecida pelo MQL5 para criação de objetos gráficos é ObjectCreate(). Mas já que também precisamos definir propriedades de objeto, enquanto os próprios objetos podem precisar serem criados mais do que uma vez, será melhor pensar em um método mais conveniente e compacto que pode ser implementado em uma simples linha de código.
Para criar o plano de fundo e cabeçalho do painel de informação, usaremos o objeto gráfico Editar. Vamos agora escrever a função CreateEdit():
//+------------------------------------------------------------------+ //| CREATING THE EDIT OBJECT | //+------------------------------------------------------------------+ void CreateEdit(long chart_id, // chart id int sub_window, // (sub)window number string name, // object name string text, // displayed text ENUM_BASE_CORNER corner, // chart corner string font_name, // font int font_size, // font size color font_color, // font color int x_size, // width int y_size, // height int x_distance, // X-coordinate int y_distance, // Y-coordinate long z_order, // Z-order color background_color, // background color bool read_only) // Read Only flag { // If the object has been created successfully,... if(ObjectCreate(chart_id,name,OBJ_EDIT,sub_window,0,0)) { // ...set its properties ObjectSetString(chart_id,name,OBJPROP_TEXT,text); // displayed text ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner); // set the chart corner ObjectSetString(chart_id,name,OBJPROP_FONT,font_name); // set the font ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size); // set the font size ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color); // font color ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,background_color); // background color ObjectSetInteger(chart_id,name,OBJPROP_XSIZE,x_size); // width ObjectSetInteger(chart_id,name,OBJPROP_YSIZE,y_size); // height ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance); // set the X coordinate ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance); // set the Y coordinate ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false); // cannot select the object if FALSE ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order); // Z-order of the object ObjectSetInteger(chart_id,name,OBJPROP_READONLY,read_only); // Read Only ObjectSetInteger(chart_id,name,OBJPROP_ALIGN,ALIGN_LEFT); // align left ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n"); // no tooltip if "\n" } }
Agora o objeto gráfico Editar (OBJ_EDIT) pode ser criado usando uma simples linha de código. Ilustraremos isto com um exemplo quando criamos uma função que definirá o painel de informação no gráfico.
Vamos agora continuar para os objetos de Etiquetas de Texto os quais serão usados para exibir a lista das propriedades de posição e seus valores, e criar a função CreateLabel() de forma similar:
//+------------------------------------------------------------------+ //| CREATING THE LABEL OBJECT | //+------------------------------------------------------------------+ void CreateLabel(long chart_id, // chart id int sub_window, // (sub)window number string name, // object name string text, // displayed text ENUM_ANCHOR_POINT anchor, // anchor point ENUM_BASE_CORNER corner, // chart corner string font_name, // font int font_size, // font size color font_color, // font color int x_distance, // X-coordinate int y_distance, // Y-coordinate long z_order) // Z-order { // If the object has been created successfully,... if(ObjectCreate(chart_id,name,OBJ_LABEL,sub_window,0,0)) { // ...set its properties ObjectSetString(chart_id,name,OBJPROP_TEXT,text); // displayed text ObjectSetString(chart_id,name,OBJPROP_FONT,font_name); // set the font ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color); // set the font color ObjectSetInteger(chart_id,name,OBJPROP_ANCHOR,anchor); // set the anchor point ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner); // set the chart corner ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size); // set the font size ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance); // set the X-coordinate ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance); // set the Y-coordinate ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false); // cannot select the object if FALSE ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order); // Z-order of the object ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n"); // no tooltip if "\n" } }
É também aconselhável dar uma olhada nas descrições de função na Referência do MQL5.
Quando for excluído do gráfico, o Consultor Especialista deve, por sua vez, excluir todos os objetos que foram previamente adicionados a tabela. Para fazer isto, você pode simplesmente passar o nome do objeto para a função DeleteObjectByName(). Ela então procurará pelo objeto pelo nome especificado e irá deletá-lo, se encontrado, usando a função embutida ObjectFind() que procura pelo objeto e a função ObjectDelete() que exclui o objeto.
//+------------------------------------------------------------------+ //| DELETING THE OBJECT BY NAME | //+------------------------------------------------------------------+ void DeleteObjectByname(string name) { int sub_window=0; // Returns the number of the subwindow where the object is located bool res =false; // Result following an attempt to delete the object //--- Find the object by name sub_window=ObjectFind(ChartID(),name); //--- if(sub_window>=0) // If it has been found,.. { res=ObjectDelete(ChartID(),name); // ...delete it //--- // If an error occurred when deleting the object,.. if(!res) // ...print the relevant message { Print("Error deleting the object: ("+IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); } } }
Além, na função DeleteObjectByName(), também implementamos uma verificação por erros quando deletando um objeto. Se um erro ocorrer, uma mensagem relevante aparecerá contendo o código de erro e a descrição. Como você pode ver no código acima, usamos uma função adicional definida pelo usuário que converte o código de erro para descrição textual - a função ErrorDescription(). Já que há muitos códigos de erros, exemplificarei o acima usando apenas uma parte desta função (veja o código abaixo). A versão completa do código pode ser encontrada no código fonte anexado a este artigo.
//+------------------------------------------------------------------+ //| RETURNING THE ERROR DESCRIPTION | //+------------------------------------------------------------------+ string ErrorDescription(int error_code) { string error_string=""; //--- switch(error_code) { //--- Trade server return codes case 10004: error_string="Requote"; break; case 10006: error_string="Request rejected"; break; case 10007: error_string="Request canceled by trader"; break; case 10008: error_string="Order placed"; break; case 10009: error_string="Request executed"; break; case 10010: error_string="Request executed partially"; break; case 10011: error_string="Request processing error"; break; case 10012: error_string="Request timed out"; break; case 10013: error_string="Invalid request"; break; case 10014: error_string="Invalid request volume"; break; case 10015: error_string="Invalid request price"; break; case 10016: error_string="Invalid Stop orders in the request"; break; case 10017: error_string="Trading forbidden"; break; case 10018: error_string="Market is closed"; break; case 10019: error_string="Insufficient funds"; break; case 10020: error_string="Prices changed"; break; case 10021: error_string="No quotes to process the request"; break; case 10022: error_string="Invalid order expiration in the request"; break; case 10023: error_string="Order status changed"; break; case 10024: error_string="Too many requests"; break; case 10025: error_string="No changes in the request"; break; case 10026: error_string="Automated trading is disabled by trader"; break; case 10027: error_string="Automated trading is disabled by the client terminal"; break; case 10028: error_string="Request blocked for processing"; break; case 10029: error_string="Order or position frozen"; break; case 10030: error_string="The specified type of order execution by balance is not supported"; break; case 10031: error_string="No connection with trade server"; break; case 10032: error_string="Transaction is allowed for live accounts only"; break; case 10033: error_string="You have reached the maximum number of pending orders"; break; case 10034: error_string="You have reached the maximum order and position volume for this symbol"; break; ... } //--- return(error_string); }
No artigo anterior lidamos com a função GetPositionProperties() que obtém propriedades de posição. Agora a estrutura da função será um pouco mais complexa. Verificaremos por uma posição que está atualmente aberta, com a bandeira de presença/ausência se uma posição aberta sendo armazenada na variável global pos_open. Esta informação pode ser necessária em outras funções, sem ter que chamar a função PositionSelect() a todo momento.
Então, quando uma posição aberta existir, obteremos suas propriedades, em outro caso todas as variáveis serão zeradas. Agora vamos escrever uma simples função ZeroPositionProperties():
//+------------------------------------------------------------------+ //| ZEROING OUT VARIABLES FOR POSITION PROPERTIES | //+------------------------------------------------------------------+ void ZeroPositionProperties() { pos_symbol =""; pos_comment =""; pos_magic =0; pos_price =0.0; pos_cprice =0.0; pos_sl =0.0; pos_tp =0.0; pos_type =WRONG_VALUE; pos_volume =0.0; pos_commission =0.0; pos_swap =0.0; pos_profit =0.0; pos_time =NULL; pos_id =0; }
Além disso, no final da função GetPositionProperties(), chamaremos uma função definida pelo usuário SetInfoPanel() que extrai/atualiza o painel de informação no gráfico.
//+------------------------------------------------------------------+ //| GETTING POSITION PROPERTIES | //+------------------------------------------------------------------+ void GetPositionProperties() { // Check if there is an open position pos_open=PositionSelect(_Symbol); //--- if(pos_open) // If an open position exists, get its properties { pos_symbol =PositionGetString(POSITION_SYMBOL); pos_comment =PositionGetString(POSITION_COMMENT); pos_magic =PositionGetInteger(POSITION_MAGIC); pos_price =PositionGetDouble(POSITION_PRICE_OPEN); pos_cprice =PositionGetDouble(POSITION_PRICE_CURRENT); pos_sl =PositionGetDouble(POSITION_SL); pos_tp =PositionGetDouble(POSITION_TP); pos_type =(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); pos_volume =PositionGetDouble(POSITION_VOLUME); pos_commission =PositionGetDouble(POSITION_COMMISSION); pos_swap =PositionGetDouble(POSITION_SWAP); pos_profit =PositionGetDouble(POSITION_PROFIT); pos_time =(datetime)PositionGetInteger(POSITION_TIME); pos_id =PositionGetInteger(POSITION_IDENTIFIER); } else // If there is no open position, zero out variables for position properties ZeroPositionProperties(); //--- SetInfoPanel(); // Set/update the info panel }
Vamos agora escrever a função SetInfoPanel(). Abaixo está o código da função com comentários detalhados:
//+------------------------------------------------------------------+ //| SETTING THE INFO PANEL | //|------------------------------------------------------------------+ void SetInfoPanel() { int y_bg=18; // Y-coordinate for the background and header int y_property=32; // Y-coordinate for the list of properties and their values int line_height=12; // Line height //--- int font_size=8; // Font size string font_name="Calibri"; // Font color font_color=clrWhite; // Font color //--- ENUM_ANCHOR_POINT anchor=ANCHOR_RIGHT_UPPER; // Anchor point in the top right corner ENUM_BASE_CORNER corner=CORNER_RIGHT_UPPER; // Origin of coordinates in the top right corner of the chart //--- X-coordinates int x_first_column=120; // First column (names of properties) int x_second_column=10; // Second column (values of properties) //--- Array of Y-coordinates for the names of position properties and their values int y_prop_array[INFOPANEL_SIZE]={0}; //--- Fill the array with coordinates for each line on the info panel y_prop_array[0]=y_property; y_prop_array[1]=y_property+line_height; y_prop_array[2]=y_property+line_height*2; y_prop_array[3]=y_property+line_height*3; y_prop_array[4]=y_property+line_height*4; y_prop_array[5]=y_property+line_height*5; y_prop_array[6]=y_property+line_height*6; y_prop_array[7]=y_property+line_height*7; y_prop_array[8]=y_property+line_height*8; y_prop_array[9]=y_property+line_height*9; y_prop_array[10]=y_property+line_height*10; y_prop_array[11]=y_property+line_height*11; y_prop_array[12]=y_property+line_height*12; y_prop_array[13]=y_property+line_height*13; //--- Background of the info panel CreateEdit(0,0,"InfoPanelBackground","",corner,font_name,8,clrWhite,230,190,231,y_bg,0,C'15,15,15',true); //--- Header of the info panel CreateEdit(0,0,"InfoPanelHeader","POSITION PROPERTIES",corner,font_name,8,clrWhite,230,14,231,y_bg,1,clrFireBrick,true); //--- List of the names of position properties and their values // Property name CreateLabel(0,0,pos_prop_names[0],"Symbol :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[0],2); // Property value CreateLabel(0,0,pos_prop_values[0],GetValInfoPanel(0),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[0],2); //--- CreateLabel(0,0,pos_prop_names[1],"Magic Number :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[1],2); CreateLabel(0,0,pos_prop_values[1],GetValInfoPanel(1),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[1],2); //--- CreateLabel(0,0,pos_prop_names[2],"Comment :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[2],2); CreateLabel(0,0,pos_prop_values[2],GetValInfoPanel(2),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[2],2); //--- CreateLabel(0,0,pos_prop_names[3],"Swap :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[3],2); CreateLabel(0,0,pos_prop_values[3],GetValInfoPanel(3),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[3],2); //--- CreateLabel(0,0,pos_prop_names[4],"Commission :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[4],2); CreateLabel(0,0,pos_prop_values[4],GetValInfoPanel(4),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[4],2); //--- CreateLabel(0,0,pos_prop_names[5],"Open Price :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[5],2); CreateLabel(0,0,pos_prop_values[5],GetValInfoPanel(5),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[5],2); //--- CreateLabel(0,0,pos_prop_names[6],"Current Price :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[6],2); CreateLabel(0,0,pos_prop_values[6],GetValInfoPanel(6),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[6],2); //--- CreateLabel(0,0,pos_prop_names[7],"Profit :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[7],2); CreateLabel(0,0,pos_prop_values[7],GetValInfoPanel(7),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[7],2); //--- CreateLabel(0,0,pos_prop_names[8],"Volume :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[8],2); CreateLabel(0,0,pos_prop_values[8],GetValInfoPanel(8),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[8],2); //--- CreateLabel(0,0,pos_prop_names[9],"Stop Loss :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[9],2); CreateLabel(0,0,pos_prop_values[9],GetValInfoPanel(9),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[9],2); //--- CreateLabel(0,0,pos_prop_names[10],"Take Profit :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[10],2); CreateLabel(0,0,pos_prop_values[10],GetValInfoPanel(10),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[10],2); //--- CreateLabel(0,0,pos_prop_names[11],"Time :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[11],2); CreateLabel(0,0,pos_prop_values[11],GetValInfoPanel(11),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[11],2); //--- CreateLabel(0,0,pos_prop_names[12],"Identifier :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[12],2); CreateLabel(0,0,pos_prop_values[12],GetValInfoPanel(12),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[12],2); //--- CreateLabel(0,0,pos_prop_names[13],"Type :",anchor,corner,font_name,font_size,font_color,x_first_column,y_prop_array[13],2); CreateLabel(0,0,pos_prop_values[13],GetValInfoPanel(13),anchor,corner,font_name,font_size,font_color,x_second_column,y_prop_array[13],2); //--- ChartRedraw(); // Redraw the chart }
Vamos olhar mais de perto a função SetInfoPanel(). Variáveis que tem a ver com as propriedades dos objetos gráficos (coordenadas, cor, fontes, texto exibido etc.) são declaradas no início da função. Preste atenção ao processo de preencher o arranjo de coordenadas Y para a lista de propriedades de posição no painel de informação. É implementado de uma forma que é clara para iniciantes. Mas pode ser reduzida para um punhado de linhas de código quando usando um ciclo (loop). Você pode escrever como se segue:
//--- Fill the array with coordinates for each line on the info panel for(int i=0; i<infopanel_size; i++)="" ="" {="" if(i==0) y_prop_array[i]=y_property; else y_prop_array[i]=y_property+line_height*i; }
Então, todas as propriedades dos objetos que precisam ser exibidas no painel devem ser especificadas nos parâmetros das funções criadas anteriormente CreateLabel() e CreateEdit(), tomando um objeto por vez. A lista completa também pode ser implementada em algumas linhas de código usando um loop. Para fazer isto, precisamos criar outro arranjo para objetos que exibem o texto dos nomes de propriedades de posição no gráfico. Deixe isto como seu dever de casa.
A função GetPropertyValue() que recebe o número de objeto retorna o valor que é passado para a função CreateLabel() como o quarto parâmetro (texto exibido). Trata-se de todos os objetos que exibirão valores de propriedades de posição. O valor retornado pela função é um valor de string ajustado que será exibido por fim no painel. Abaixo está o código da função com comentários detalhados:
//+------------------------------------------------------------------+ //| RETURNING THE STRING WITH POSITION PROPERTY VALUE | //+------------------------------------------------------------------+ string GetPropertyValue(int number) { //--- Sign indicating the lack of an open position or a certain property // E.g. the lack of a comment, Stop Loss or Take Profit string empty="-"; //--- If an open position exists, return the value of the requested property if(pos_open) { switch(number) { case 0 : return(pos_symbol); break; case 1 : return(IntegerToString((int)pos_magic)); break; //--- return the value of the comment, if any, otherwise return the sign indicating the lack of comment case 2 : return(pos_comment!="" ? pos_comment : empty); break; case 3 : return(DoubleToString(pos_swap,2)); break; case 4 : return(DoubleToString(pos_commission,2)); break; case 5 : return(DoubleToString(pos_price,_Digits)); break; case 6 : return(DoubleToString(pos_cprice,_Digits)); break; case 7 : return(DoubleToString(pos_profit,2)); break; case 8 : return(DoubleToString(pos_volume,2)); break; case 9 : return(pos_sl!=0.0 ? DoubleToString(pos_sl,_Digits) : empty); break; case 10 : return(pos_tp!=0.0 ? DoubleToString(pos_tp,_Digits) : empty); break; case 11 : return(TimeToString(pos_time,TIME_DATE|TIME_MINUTES)); break; case 12 : return(IntegerToString((int)pos_id)); break; case 13 : return(PositionTypeToString(pos_type)); break; default : return(empty); } } //--- // If there is no open position, return the sign indicating the lack of the open position "-" return(empty); }
O código acima sugere que um certo valor é preparado para cada número passado para a função, desde que haja uma posição aberta. Se não há posição aberta atualmente, a função retornará um traço (-) exibido para todos objetos que tem a ver com os valores da propriedade da posição.
No final da função SetInfoPanel(), chamamos a função ChartRedraw() designada para uma redefinição forçada do gráfico. A não ser que seja chamada, você não será capaz de visualizar as modificações realizadas.
Agora precisamos escrever uma função que excluirá todos os objetos gráficos criados pelo Consultor Especialista. Vamos chamá-la DeleteInfoPanel():
//+------------------------------------------------------------------+ //| DELETING THE INFO PANEL | //+------------------------------------------------------------------+ void DeleteInfoPanel() { DeleteObjectByName("InfoPanelBackground"); // Delete the panel background DeleteObjectByName("InfoPanelHeader"); // Delete the panel header //--- Delete position properties and their values for(int i=0; i<infopanel_size; i++)="" ="" {="" deleteobjectbyname(pos_prop_names[i]); // Delete the property DeleteObjectByName(pos_prop_values[i]); // Delete the value } //--- ChartRedraw(); // Redraw the chart }
Agora apenas precisamos distribuir os métodos que criamos entre as funções principais do Consultor Especialista que estavam originalmente presentes no modelo após sua criação no Assistente do MQL5. Esta é a parte mais fácil:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Get the properties and set the panel GetPositionProperties(); //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Print the deinitialization reason to the journal Print(GetDeinitReasonText(reason)); //--- When deleting from the chart if(reason==REASON_REMOVE) //--- Delete all objects relating to the info panel from the chart DeleteInfoPanel(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Get the properties and update the values on the panel GetPositionProperties(); } //+------------------------------------------------------------------+
A única coisa que você pode esbarrar aqui é a função GetDeinitReasonText() que retorna uma descrição textual do código do motivo de deinicialização:
//+---------------------------------------------------------------------+ //| RETURNING A TEXTUAL DESCRIPTION OF THE DEINITIALIZATION REASON CODE | //+---------------------------------------------------------------------+ string GetDeinitReasonText(int reason_code) { string text=""; //--- switch(reason_code) { case REASON_PROGRAM : // 0 text="The Expert Advisor has stopped working calling the ExpertRemove() function."; break; case REASON_REMOVE : // 1 text="The '"+EXPERT_NAME+"' program has been removed from the chart."; break; case REASON_RECOMPILE : // 2 text="The '"+EXPERT_NAME+"' program has been recompiled."; break; case REASON_CHARTCHANGE : // 3 text="Chart symbol or period has been changed."; break; case REASON_CHARTCLOSE : // 4 text="The chart is closed."; break; case REASON_PARAMETERS : // 5 text="Input parameters have been changed by the user."; break; case REASON_ACCOUNT : // 6 text="A different account has been activated."; break; case REASON_TEMPLATE : // 7 text="A different chart template has been applied."; break; case REASON_INITFAILED : // 8 text="A flag specifying that the OnInit() handler returned zero value."; break; case REASON_CLOSE : // 9 text="The terminal has been closed."; break; default : text="The reason is undefined."; } //--- return text; }
Se você tentar usar o Consultor Especialista no símbolo gráfico que atualmente não tem posição aberta, você verá traços ao invés dos valores de propriedade de posição no painel. O painel parecerá o mesmo após você fechar uma certa posição.
Fig. 2. Painel de informação na ausência de uma posição aberta.
Se o Consultor Especialista é adicionado ao gráfico do símbolo que tem uma posição aberta ou se uma posição é aberta após adicionar o Consultor Especialista ao gráfico, todos os traços serão substituídos pelos valores de propriedade de posição apropriados:
Fig. 3. Painel de informação exibindo propriedades da posição aberta.
Há uma pequena peculiaridade. Após fechar a posição, só no novo ponto que os valores no painel serão atualizados. Há uma maneira de fazer os valores serem atualizados imediatamente mas o que precisa ser feito para implementar isto será discutido no próximo artigo da série.
Conclusão
Algumas das funções introduzidas neste artigo também serão usadas nos artigos seguintes da série Guia prático do MQL5, enquanto outras serão modificadas e melhoradas dependendo da tarefa em mãos. É aconselhável ler os artigos em ordem, um após o outro já que cada novo artigo é uma continuação lógica do anterior. Certamente também depende do seu nível de competência e habilidades, portanto, pode ser mais racional e interessante começar com publicações mais recentes.
O arquivo de código-fonte está anexado ao artigo.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/641
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Novo artigo Guia prático do MQL5: Propriedades de posição no painel de informações personalizado foi publicado:
Autor: Anatoli Kazharski