Alterar os parâmetros do Expert Advisor instantaneamente a partir do painel de usuário
Conteúdo
Introdução
1. Assuntos em foco
2. Estrutura do Expert Advisor
3. Interação com o painel de usuário
Conclusão
Introdução
Durante o desenvolvimento de Expert Advisors complexos, o número de parâmetros externos pode ser muito grande. E, muito frequentemente, é necessário alterar as configurações manualmente, o que torna todo o processo muito demorado devido a enorme lista de parâmetros. É claro que é possível preparar conjuntos com antecedência e salvá-los, mas isso pode não ser exatamente o requerido em alguns casos. É aqui que o MQL5 é útil, como sempre.
Vamos tentar criar um painel de usuário que permitirá alterarmos os parâmetros de um Expert Advisor instantaneamente enquanto negociamos. Isso pode ser relevante para aqueles que negociam em modo manual ou semi-automático. Após cada alteração, os parâmetros serão escritos em um arquivo a partir do qual eles serão lidos pelo Expert Advisor para que sejam posteriormente exibidos no painel.
1. Assuntos em foco
Como exemplo, vamos desenvolver um EA simples que abre uma posição na direção do indicador JMA. O EA irá trabalhar em barras completas no símbolo e período de tempo atual. Os parâmetros externos incluirão Indicator Period (período do indicador), Stop Loss (interromper perda), Take Profit (obter lucro), Reverse (inverso) e Lot (lote). Essas opções serão suficientes para o nosso exemplo.
Vamos incluir dois parâmetros adicionais para que seja possível ligar e desligar o painel (On/Off Info Panel) e habilitar/desabilitar o modo de configuração de parâmetros do Expert Advisor (Configuração "On the Fly"). Quando o número de parâmetros é grande, é sempre mais conveniente colocar posições adicionais no início do final da lista para acesso mais fácil e rápido.
Figura 1. Painel de informações com parâmetros do Expert Advisor
O modo de configuração "On The Fly" (instantâneo) está automaticamente desabilitado. Quando você habilita esse modo pela primeira vez, o Expert Advisor cria um arquivo para salvar todos os seus parâmetros atuais. O mesmo acontece caso o arquivo seja acidentalmente apagado. O Expert Advisor irá detectar o apagamento e criar o arquivo novamente. Com o modo de configuração "On The Fly" desabilitado, o Expert Advisor será dirigido por parâmetros externos.
Se esse modo for habilitado, o Expert Advisor lerá os parâmetros do arquivo e, com um simples clique sobre qualquer parâmetro do painel de informações, você poderá ou selecionar o valor requerido ou inserir um novo valor na janela de diálogo que aparecerá. Os dados do arquivo serão atualizados cada vez que um novo valor for selecionado.
2. Estrutura do Expert Advisor
Embora o programa seja pequeno e todas as funções possam facilmente estar em um arquivo único, ainda é muito mais conveniente navegar por todas as informações do projeto quando elas estão devidamente categorizadas. Assim, é melhor categorizar as funções pelo tipo e tê-las em diferentes arquivos desde o início para posteriormente incluí-los no arquivo mestre. A figura abaixo mostra uma pasta de projeto compartilhada com o Expert Advisor OnTheFly e todos os arquivos inclusos. Os arquivos inclusos são colocados em uma pasta separada (Include).
Figura 2. Arquivos de projeto na janela do navegador do MetaEditor
Quando os arquivos inclusos estão na mesma pasta que o arquivo mestre, o código é o seguinte:
//+------------------------------------------------------------------+ //| CUSTOM LIBRARIES | //+------------------------------------------------------------------+ #include "Include/!OnChartEvent.mqh" #include "Include/CREATE_PANEL.mqh" #include "Include/FILE_OPERATIONS.mqh" #include "Include/ERRORS.mqh" #include "Include/ARRAYS.mqh" #include "Include/TRADE_SIGNALS.mqh" #include "Include/TRADE_FUNCTIONS.mqh" #include "Include/GET_STRING.mqh" #include "Include/GET_COLOR.mqh" #include "Include/ADD_FUNCTIONS.mqh"
Mais informações sobre como incluir arquivos podem ser encontradas na Referência MQL5.
Precisaremos de variáveis globais - cópias de parâmetros externos. Os seus valores ou serão designados a partir dos parâmetros externos ou do arquivo, dependendo do modo do Expert Advisor. Essas variáveis são utilizadas ao longo de todo o código do programa, por exemplo, para exibir valores no painel de informações, nas funções de negociação, etc.
// COPY OF EXTERNAL PARAMETERS int gPeriod_Ind = 0; double gTakeProfit = 0.0; double gStopLoss = 0.0; bool gReverse = false; double gLot = 0.0;
Como em todos os outros Expert Advisors, teremos as funções principais: OnInit, OnTick e OnDeinit. E também haverá a função OnTimer. A cada segundo, ela irá verificar a existência do arquivo de parâmetro e recuperá-lo caso ele tenha sido acidentalmente apagado. Visto que precisamos interagir com o painel de usuário, a função OnChartEvent também será usada. Essa função, juntamente com outras funções relacionadas, foi colocada em um arquivo separado (!OnChartEvent.mqh).
O código principal do arquivo mestre é o seguinte:
#define szArrIP 5 // Size of the parameter array #define NAME_EXPERT MQL5InfoString(MQL5_PROGRAM_NAME) // Name of EA #define TRM_DP TerminalInfoString(TERMINAL_DATA_PATH) // Folder that contains the terminal data //+------------------------------------------------------------------+ //| STANDARD LIBRARIES | //+------------------------------------------------------------------+ #include #include //+------------------------------------------------------------------+ //| CUSTOM LIBRARIES | //+------------------------------------------------------------------+ #include "Include/!OnChartEvent.mqh" #include "Include/CREATE_PANEL.mqh" #include "Include/FILE_OPERATIONS.mqh" #include "Include/ERRORS.mqh" #include "Include/ARRAYS.mqh" #include "Include/TRADE_SIGNALS.mqh" #include "Include/TRADE_FUNCTIONS.mqh" #include "Include/GET_STRING.mqh" #include "Include/GET_COLOR.mqh" #include "Include/ADD_FUNCTIONS.mqh" //+------------------------------------------------------------------+ //| CREATING CLASS INSTANCES | //+------------------------------------------------------------------+ CSymbolInfo mysymbol; // CSymbolInfo class object CTrade mytrade; // CTrade class object //+------------------------------------------------------------------+ //| EXTERNAL PARAMETERS | //+------------------------------------------------------------------+ input int Period_Ind = 10; // Indicator Period input double TakeProfit = 100; // Take Profit (p) input double StopLoss = 30; // Stop Loss (p) input bool Reverse = false; // Reverse Position input double Lot = 0.1; // Lot //--- input string slash=""; // * * * * * * * * * * * * * * * * * * * sinput bool InfoPanel = true; // On/Off Info Panel sinput bool SettingOnTheFly = false; // "On The Fly" Setting //+------------------------------------------------------------------+ //| GLOBAL VARIABLES | //+------------------------------------------------------------------+ int hdlSI=INVALID_HANDLE; // Signal indicator handle double lcheck=0; // For the check of parameter values bool isPos=false; // Position availability //--- COPY OF EXTERNAL PARAMETERS int gPeriod_Ind = 0; double gTakeProfit = 0.0; double gStopLoss = 0.0; bool gReverse = false; double gLot = 0.0; //+------------------------------------------------------------------+ //| EXPERT ADVISOR INITIALIZATION | //+------------------------------------------------------------------+ void OnInit() { if(NotTest()) { EventSetTimer(1); } // If it's not the tester, set the timer //--- Init_arr_vparams(); // Initialization of the array of parameter values SetParameters(); // Set the parameters GetIndicatorsHandles(); // Get indicator handles NewBar(); // New bar initialization SetInfoPanel(); // Info panel } //+------------------------------------------------------------------+ //| CURRENT SYMBOL TICKS | //+------------------------------------------------------------------+ void OnTick() { // If the bar is not new, exit if(!NewBar()) { return; } else { TradingBlock(); } } //+------------------------------------------------------------------+ //| TIMER | //+------------------------------------------------------------------+ void OnTimer() { SetParameters(); SetInfoPanel(); } //+------------------------------------------------------------------+ //| EXPERT ADVISOR DEINITIALIZATION | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Get the deinitialization reason code if(NotTest()) { { Print(getUnitReasonText(reason)); } //--- // When deleting from the chart if(reason==REASON_REMOVE) { // Delete all objects created by the Expert Advisor DeleteAllExpertObjects(); //--- if(NotTest()) { EventKillTimer(); } // Stop the timer IndicatorRelease(hdlSI); // Delete the indicator handle } }
Também inclui mais algumas funções no arquivo mestre:
- GetIndicatorsHandles – obtém o handle do indicador.
- NewBar – determina o evento de nova barra.
- SetParameters – estabelece os parâmetros de acordo com o modo.
- iZeroMemory – zera algumas variáveis e arrays.
//+------------------------------------------------------------------+ //| SETTING PARAMETERS IN TWO MODES | //+------------------------------------------------------------------+ // If this variable is set to false, the parameters in the file are read from the array // where they are saved for quick access after they have been read for the first time. // The variable is zeroed out upon the change in the value on the panel. bool flgRead=false; double arrParamIP[]; // Array where the parameters from the file are saved //--- void SetParameters() { // If currently in the tester or // in real time but with the "On The Fly" Setting mode disabled if(!NotTest() || (NotTest() && !SettingOnTheFly)) { // Zero out the variable and parameter array flgRead=false; ArrayResize(arrParamIP,0); //--- // Check the Indicator Period for correctness if(Period_Ind<=0) { lcheck=10; } else { lcheck=Period_Ind; } gPeriod_Ind=(int)lcheck; //--- gStopLoss=StopLoss; gTakeProfit=TakeProfit; gReverse=Reverse; //--- // Check the Lot for correctness if(Lot<=0) { lcheck=0.1; } else { lcheck=Lot; } gLot=lcheck; } else // If "On The Fly" Setting mode is enabled { // Check whether there is a file to write/read parameters to/from the file string lpath=""; //--- // If the folder exists if((lpath=CheckCreateGetPath())!="") { // Write or read the file WriteReadParameters(lpath); } } }
O código fonte para a função SetParameters é simples e direto. Vamos analisar a função WriteReadParameters de perto. Tudo aqui é muito simples. Primeiramente, verificamos se o arquivo com os parâmetros existe. Caso exista, lemos o arquivo e escrevemos os valores de parâmetro em um array, utilizando a função GetValuesParamsFromFile. Se o arquivo não existir, ele será criado, com os parâmetros externos atuais escritos nele.
Abaixo, segue o código com mais comentários detalhados para a implementação das ações descritas acima:
//+------------------------------------------------------------------+ //| WRITE DATA TO FILE | //+------------------------------------------------------------------+ void WriteReadParameters(string pth) { string nm_fl=pth+"ParametersOnTheFly.ini"; // File name and path //--- // Get the file handle to read the file int hFl=FileOpen(nm_fl,FILE_READ|FILE_ANSI); //--- if(hFl!=INVALID_HANDLE) // If the handle has been obtained, the file exists { // Get parameters from the file if(!flgRead) { // Set the array size ArrayResize(arrParamIP,szArrIP); //--- // Fill the array with values from the file flgRead=GetValuesParamsFromFile(hFl,arrParamIP); } //--- // If the array size is correct,... if(ArraySize(arrParamIP)==szArrIP) { // ...set the parameters to the variables //--- // Check the Indicator Period for correctness if((int)arrParamIP[0]<=0) { lcheck=10; } else { lcheck=(int)arrParamIP[0]; } gPeriod_Ind=(int)lcheck; //--- gTakeProfit=arrParamIP[1]; gStopLoss=arrParamIP[2]; gReverse=arrParamIP[3]; //--- // Check the Lot for correctness if(arrParamIP[4]<=0) { lcheck=0.1; } else { lcheck=arrParamIP[4]; } gLot=lcheck; } } else // If the file does not exist { iZeroMemory(); // Zero out variables //--- // When creating the file, write current parameters of the Expert Advisor //--- // Get the file handle to write to the file int hFl2=FileOpen(nm_fl,FILE_WRITE|FILE_CSV|FILE_ANSI,""); //--- if(hFl2!=INVALID_HANDLE) // If the handle has been obtained { string sep="="; //--- // Parameter names and values are obtained from arrays in the ARRAYS.mqh file for(int i=0; i<szarrip; i++)="" ="" {="" FileWrite(hFl2,arr_nmparams[i],sep,arr_vparams[i]); } //--- FileClose(hFl2); // Close the file //--- Print("File with parameters of the "+NAME_EXPERT+" Expert Advisor created successfully."); } } //--- FileClose(hFl); // Close the file }
As funções The WriteReadParameters e GetValuesParamsFromFile podem ser encontradas no arquivo FILE_OPERATIONS.mqh.
Algumas das funções já foram descritas em meu artigo anterior "Como preparar cotações do MetaTrader 5 para outros aplicativos", de forma que não vou me ater a elas aqui. Você também não deverá ter qualquer dificuldade com as funções de negociação, visto que elas são muito diretas e estão detalhadamente comentadas. O que vamos fazer é focar no assunto principal do artigo.
3. Interação com o painel de usuário
O arquivo !OnChartEvent.mqh contém funções para interação com o painel de usuário. Variáveis e arrays que são usados em muitas funções são declarados no escopo global logo no início:
// Current value on the panel or // entered in the input box string currVal=""; bool flgDialogWin=false; // Flag for panel existence int szArrList=0,// Size of the option list array number=-1; // Parameter number in the panel list string nmMsgBx="", // Name of the dialog window nmValObj=""; // Name of the selected object //--- // Option list arrays in the dialog window string lenum[],lenmObj[]; //--- // colors of the dialog window elements color clrBrdBtn=clrWhite, clrBrdFonMsg=clrDimGray,clrFonMsg=C'15,15,15', clrChoice=clrWhiteSmoke,clrHdrBtn=clrBlack, clrFonHdrBtn=clrGainsboro,clrFonStr=C'22,39,38';
Isso é seguido pela função principal que gerencia os eventos. Em nosso exemplo, precisaremos gerenciar dois eventos:
- O evento CHARTEVENT_OBJECT_CLICK – clique com o botão esquerdo sobre o objeto gráfico.
- O evento CHARTEVENT_OBJECT_EDIT – fim da edição de texto no objeto gráfico Editar.
Você pode ler mais sobre outros eventos MQL5 na Referência MQL5.
Primeiramente, vamos colocar uma marca somente para gerenciamento de eventos em tempo real, sempre com a condição de que o modo de configuração "On The Fly" esteja habilitado (SettingOnTheFly). O gerenciamento de eventos será feito por duas funções separadas: ChartEvent_ObjectClick e ChartEvent_ObjectEndEdit.
//+------------------------------------------------------------------+ //| USER EVENTS | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { // If the event is real time and the "On The Fly" setting mode is enabled if(NotTest() && SettingOnTheFly) { //+------------------------------------------------------------------+ //| THE CHARTEVENT_OBJECT_CLICK EVENT | //+------------------------------------------------------------------+ if(ChartEvent_ObjectClick(id,lparam,dparam,sparam)) { return; } //--- //+------------------------------------------------------------------+ //| THE CHARTEVENT_OBJECT_ENDEDIT EVENT | //+------------------------------------------------------------------+ if(ChartEvent_ObjectEndEdit(id,lparam,dparam,sparam)) { return; } } //--- return; }
Quando você clica no objeto que pertence à lista, uma janela de diálogo aparecerá no painel de informações, permitindo que você selecione outro valor ou insira um novo valor na caixa de entrada.
Figura 3. Janela de diálogo para modificações do valor do parâmetro selecionado.
Vamos analisar o funcionamento de perto. Quando se clica sobre um objeto gráfico, o programa primeiramente usa a função ChartEvent_ObjectClick para verificar com o identificador do evento se realmente houve um clique sobre um objeto gráfico.
Se você deseja que a janela de diálogo abra no meio do gráfico, você precisa saber o tamanho dele. Isso pode ser obtido pela indicação das propriedades CHART_WIDTH_IN_PIXELS e CHART_HEIGHT_IN_PIXELS na função ChartGetInteger. O programa então comuta para o DialogWindowInfoPanel. Você pode se familiarizar com as propriedades do gráfico na Referência MQL5.
Segue abaixo o código para a implementação das ações acima:
//+------------------------------------------------------------------+ //| THE CHARTEVENT_OBJECT_CLICK EVENT | //+------------------------------------------------------------------+ bool ChartEvent_ObjectClick(int id,long lparam,double dparam,string sparam) { // If there was an event of clicking on a graphical object if(id==CHARTEVENT_OBJECT_CLICK) // id==1 { Get_STV(); // Get all data on the symbol //--- string clickedChartObject=sparam; // Name of the clicked object //--- // Get the chart size width_chart=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS,0); height_chart=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,0); //--- DialogWindowInfoPanel(clickedChartObject); } //--- return(false); }
Utilizando a função DialogWindowInfoPanel, primeiramente verificamos se a janela de diálogo está aberta no momento. Se a janela não for encontrada, a função GetNumberClickedObjIP verifica se o clique foi em relação a um objeto da lista do painel de informações. Se o objeto clicado está na lista, a função retornará o número de elemento relevante do array de objetos. Utilizando esse número, a função InitArraysAndDefault determina o tamanho do array da lista na janela de diálogo e os valores padrão. Se todas as ações forem bem sucedidas, a janela de diálogo aparecerá.
Se a função DialogWindowInfoPanel determina que a janela de diálogo já está aberta, o programa verificará se houve ou não um clique sobre um objeto da janela de diálogo. Por exemplo, após abrir a janela de diálogo, a linha cujo valor está sendo exibido atualmente no painel aparecerá como selecionada. Se você clicar sobre outra opção da lista, o programa usará a função SelectionOptionInDialogWindow que seleciona a opção da lista que foi clicada na janela de diálogo.
Se você clicar sobre a opção da lista que está selecionada no momento, esse objeto será identificado como objeto a ser editado e uma caixa de entrada aparecerá para que um novo valor possa ser inserido quando você clicar nela. A função SetEditObjInDialogWindow é responsável pela configuração da caixa de entrada.
E finalmente, se houve um clique sobre o botão Aplicar, o programa verificará se o valor foi modificado. Caso tenha sido, o novo valor aparecerá no painel e será escrito no arquivo.
O código da função principal da janela de diálogo é fornecido abaixo:
//+------------------------------------------------------------------+ //| DIALOG WINDOW OF THE INFO PANEL | //+------------------------------------------------------------------+ void DialogWindowInfoPanel(string clickObj) { // If there is currently no dialog window if(!flgDialogWin) { // Get the object number in the array // Exit if none of the parameters displayed on the panel has been clicked if((number=GetNumberClickedObjIP(clickObj))==-1) { return; } //--- // Initialization of default values //and determination of the list array size if(!InitArraysAndDefault()) { return; } //--- // Set the dialog window SetDialogWindow(); //--- flgDialogWin=true; // Mark the dialog window as open ChartRedraw(); } else // If the dialog window is open { // Set the input box for the modification of the value SetEditObjInDialogWindow(clickObj); //--- // If one of the buttons in the dialog box is clicked if(clickObj=="btnApply" || clickObj=="btnCancel") { // If the Apply button is clicked if(clickObj=="btnApply") { // Compare values on the panel with the ones on the list // If the value on the list is different from the one that is currently displayed on the panel // (which means it is different from the one in the file), // ...change the value on the panel and update the file if(currVal!=ObjectGetString(0,nmValObj,OBJPROP_TEXT)) { // Update the value on the panel ObjectSetString(0,nmValObj,OBJPROP_TEXT,currVal); ChartRedraw(); //--- // Read all data on the panel and write it to the file WriteNewData(); } } //--- DelDialogWindow(lenmObj); // Delete the dialog window iZeroMemory(); // Zero out the variables //--- // Update the data SetParameters(); GetHandlesIndicators(); SetInfoPanel(); //--- ChartRedraw(); } else // If neither Apply nor Cancel has been clicked { // Selection of the dialog window list option SelectionOptionInDialogWindow(clickObj); //--- ChartRedraw(); } } }
Cada vez que um novo valor é inserido na caixa de entrada, o evento CHARTEVENT_OBJECT_EDIT é gerado e o programa comuta para a função ChartEvent_ObjectEndEdit. Se o valor da janela de diálogo foi modificado, o valor inserido será armazenado, sua exatidão será verificada e ele será atribuído ao objeto da lista. Você pode visualizar mais detalhes no código abaixo:
//+------------------------------------------------------------------+ //| THE CHARTEVENT_OBJECT_ENDEDIT EVENT | //+------------------------------------------------------------------+ bool ChartEvent_ObjectEndEdit(int id,long lparam,double dparam,string sparam) { if(id==CHARTEVENT_OBJECT_ENDEDIT) // id==3 { string editObject=sparam; // Name of the edited object //--- // If the value has been entered in the input box in the dialog window if(editObject=="editValIP") { // Get the entered value currVal=ObjectGetString(0,"editValIP",OBJPROP_TEXT); //--- // (0) Period Indicator if(number==0) { // Correct the value if it is wrong if(currVal=="0" || currVal=="" || SD(currVal)<=0) { currVal="1"; } //--- // Set the entered value ObjectSetString(0,"enumMB0",OBJPROP_TEXT,currVal); } //--- // (4) Lot if(number==4) { // Correct the value if it is wrong if(currVal=="0" || currVal=="" || SD(currVal)<=0) { currVal=DS(SS.vol_min,2); } //--- // Set the entered value ObjectSetString(0,"enumMB0",OBJPROP_TEXT,DS2(SD(currVal))); } //--- // (1) Take Profit (p) // (2) Stop Loss (p) if(number==1 || number==2) { // Correct the value if it is wrong if(currVal=="0" || currVal=="" || SD(currVal)<=0) { currVal="1"; } //--- // Set the entered value ObjectSetString(0,"enumMB1",OBJPROP_TEXT,currVal); } //--- DelObjbyName("editValIP"); ChartRedraw(); } } //--- return(false); }
O Expert Advisor em ação pode ser visto no vídeo abaixo:
Conclusão
Você poderá fazer o download dos arquivos zipados ao final do artigo para um estudo mais detalhado.
Espero que este artigo ajude quem começou a aprender MQL5 agora a encontrar respostas rápidas para diversas questões, através do uso dos exemplos simples apresentados. Intencionalmente, deixei de fora algumas verificações dos fragmentos de código apresentados.
Por exemplo, se você alterar a altura/largura quando a janela de diálogo estiver aberta, a janela não ficará automaticamente centralizada. E se você completa isso com a seleção de outra opção da lista, o objeto que serve para selecionar a linha relevante será consideravelmente deslocada. Deixe que isso seja a sua lição de casa. É muito importante praticar programação. Quando mais você praticar, melhor.
Boa sorte!
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/572
- 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