Modifica "al volo" i parametri dell'Expert Advisor dal pannello utente
Contenuti
Introduzione
1. Problemi in primo piano
2. Struttura dell'Expert Advisor
3. Interazione con il pannello utente
Conclusione
Introduzione
Quando si sviluppano Expert Advisor complessi, il numero di parametri esterni può essere molto elevato. E le impostazioni molto spesso devono essere modificate manualmente, rendendo l'intero processo molto dispendioso in termini di tempo, dato l’enorme elenco di parametri. Ovviamente è possibile preparare i set in anticipo e salvarli, ma in alcuni casi potrebbe non essere esattamente ciò che è richiesto. È qui che MQL5 torna utile, come sempre.
Proviamo a creare un pannello utente che ci permetta di modificare i parametri di un Expert Advisor "al volo" durante il trading. Questo può essere rilevante per coloro che fanno trading manualmente o in modalità semi-automatica. Ad ogni modifica effettuata, i parametri verranno scritti in un file dal quale verranno poi letti dall'Expert Advisor per essere ulteriormente visualizzati sul pannello.
1. Problemi in primo piano
A titolo illustrativo, svilupperemo un semplice EA che apre una posizione nella direzione dell'indicatore JMA. L'EA lavorerà sulle barre completate sul simbolo e sull'intervallo di tempo correnti. I parametri esterni includeranno Indicator Period, Stop Loss, Take Profit, Reverse e Lot. Queste opzioni saranno abbastanza sufficienti nel nostro esempio.
Aggiungiamo due parametri aggiuntivi per poter accendere/spegnere il pannello (pannello Info On/Off) e abilitare/disabilitare la modalità di impostazione dei parametri Expert Advisor (impostazione "On The Fly"). Laddove il numero di parametri è elevato, è sempre più conveniente posizionare opzioni aggiuntive all'inizio o alla fine dell'elenco per un accesso facile e veloce.
Fig. 1. Pannello informativo con i parametri dell'Expert Advisor
La modalità di impostazione "Al volo" è disabilitata per impostazione predefinita. Quando abiliti questa modalità per la prima volta, Expert Advisor crea un file per salvare tutti i parametri che ha attualmente. Lo stesso accadrà se il file viene cancellato accidentalmente. L'Expert Advisor rileverà l'eliminazione e ricreerà il file. Con la modalità di impostazione "On The Fly" disabilitata, l'Expert Advisor sarà guidato da parametri esterni.
Se questa modalità è abilitata, l'Expert Advisor leggerà i parametri dal file e, semplicemente facendo clic su qualsiasi parametro nel pannello delle informazioni, sarà possibile selezionare il valore richiesto o inserire un nuovo valore nella finestra di dialogo che si apre . I dati del file verranno aggiornati ogni volta che viene selezionato un nuovo valore.
2. Struttura dell'Expert Advisor
Sebbene il programma sia piccolo e tutte le funzioni possano essere facilmente inserite in un unico file, è comunque molto più comodo navigare in tutte le informazioni del progetto quando sono categorizzate correttamente. Pertanto, è meglio classificare le funzioni per tipo e averle in file diversi fin dall'inizio per includerle successivamente nel file principale. La figura seguente mostra una cartella di progetto condivisa con OnTheFly Expert Advisor e tutti i file di inclusione. I file di inclusione vengono inseriti in una cartella separata (Includi).
Fig. 2. File di progetto nella finestra Navigatore di MetaEditor
Quando i file di inclusione si trovano nella stessa cartella con il file master, il codice è il seguente:
//+------------------------------------------------------------------+ //| 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"
Ulteriori informazioni su come includere i file sono disponibili in Riferimento MQL5.
Avremo bisogno di variabili globali - copie di parametri esterni. I loro valori saranno assegnati dai parametri esterni o dal file, a seconda della modalità dell'Expert Advisor. Queste variabili vengono utilizzate in tutto il codice del programma, ad esempio nella visualizzazione dei valori sul pannello delle informazioni, nelle funzioni di trading, ecc.
// COPY OF EXTERNAL PARAMETERS int gPeriod_Ind = 0; double gTakeProfit = 0.0; double gStopLoss = 0.0; bool gReverse = false; double gLot = 0.0;
Come in tutti gli altri Expert Advisor, avremo le principali funzioni: OnInit, OnTick e OnDeinit. E ci sarà anche la funzione OnTimer. Ogni secondo verificherà l'esistenza del file dei parametri e lo ripristinerà nel caso sia stato cancellato accidentalmente. Poiché dobbiamo interagire con il pannello utente, verrà utilizzata anche la funzione OnChartEvent. Questa funzione insieme ad altre funzioni correlate è stata inserita in un file separato (!OnChartEvent.mqh).
Il codice principale del file master è il seguente:
#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 <Trade/SymbolInfo.mqh> #include <Trade/Trade.mqh> //+------------------------------------------------------------------+ //| 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 } }
Ho anche incluso alcune altre funzioni nel file principale:
- GetIndicatorsHandles: ottiene l'handle dell'indicatore.
- NewBar – determina il nuovo evento bar.
- SetParameters: imposta i parametri in base alla modalità.
- iZeroMemory – azzera alcune variabili e array.
//+------------------------------------------------------------------+ //| 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); } } }
Il codice sorgente per la funzione SetParameters è semplice e diretto. Diamo un'occhiata più da vicino alla funzione WriteReadParameters. Tutto è abbastanza semplice qui. Per prima cosa controlliamo se il file con i parametri esiste. In tal caso, leggiamo il file e scriviamo i valori dei parametri in un array utilizzando la funzione GetValuesParamsFromFile. Se il file non esiste, verrà creato, con i parametri esterni correnti scritti su di esso.
Di seguito il codice con commenti più dettagliati per l'attuazione delle azioni sopra descritte:
//+------------------------------------------------------------------+ //| 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 }
Le funzioni WriteReadParameters e GetValuesParamsFromFile si trovano nel file FILE_OPERATIONS.mqh.
Alcune delle funzioni sono già state descritte nel mio precedente articolo "Come preparare le quotazioni di MetaTrader 5 per altre applicazioni", pertanto non ci soffermeremo qui. Non dovresti incontrare alcuna difficoltà nemmeno con le funzioni di trading, poiché sono molto semplici e commentate ampiamente. Quello su cui ci concentreremo è l'argomento principale dell'articolo.
3. Interazione con il pannello utente
Il file !OnChartEvent.mqh contiene funzioni per l'interazione con il pannello utente. Le variabili e gli array utilizzati in molte funzioni sono dichiarati nell'ambito globale all'inizio:
// 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';
Questo è seguito dalla funzione principale che gestisce gli eventi. Nel nostro esempio, dovremo gestire due eventi:
- L'evento CHARTEVENT_OBJECT_CLICK – click sinistro sull'oggetto grafico.
- L'evento CHARTEVENT_OBJECT_EDIT – fine della modifica del testo nell'oggetto grafico Modifica.
Puoi leggere di più su altri eventi MQL5 in Riferimento MQL5.
Impostiamo prima un controllo per la gestione degli eventi solo in tempo reale, sempre che sia abilitata la modalità di impostazione "On The Fly" (SettingOnTheFly). La gestione degli eventi sarà curata da funzioni separate: 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 si fa clic sull'oggetto che appartiene all'elenco, sul pannello delle informazioni verrà visualizzata una finestra di dialogo che consente di selezionare un altro valore o di inserire un nuovo valore nella casella di input.
Fig. 3. Finestra di dialogo per la modifica del valore del parametro selezionato
Diamo un'occhiata più da vicino a come funziona. Quando si fa clic su un oggetto grafico, il programma utilizza prima la funzione ChartEvent_ObjectClick per verificare tramite l'identificatore dell'evento se c'è stato davvero un clic su un oggetto grafico.
Se vuoi che la finestra di dialogo si apra al centro del grafico, devi conoscere le dimensioni del grafico. Può essere ottenuto indicando le proprietà CHART_WIDTH_IN_PIXELS e CHART_HEIGHT_IN_PIXELS nella funzione ChartGetInteger. Il programma passa quindi al DialogWindowInfoPanel. Puoi familiarizzare con tutte le proprietà del grafico in MQL5 Riferimento.
Di seguito è riportato il codice per l'attuazione delle azioni di cui sopra:
//+------------------------------------------------------------------+ //| 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); }
Utilizzando la funzione DialogWindowInfoPanel, controlliamo prima se la finestra di dialogo è attualmente aperta. Se la finestra non viene trovata, la funzione GetNumberClickedObjIP verifica se il clic era in relazione ad un oggetto della lista nel pannello info. Se l'oggetto cliccato è l'oggetto dell'elenco, la funzione restituirà il numero dell'elemento rilevante dall'array di oggetti. Utilizzando quel numero, la funzione InitArraysAndDefault determina quindi la dimensione dell'array dell'elenco nella finestra di dialogo e i valori predefiniti. Se tutte le azioni hanno successo, apparirà la finestra di dialogo.
Se la funzione DialogWindowInfoPanel determina che la finestra di dialogo è già aperta, il programma verificherà se c'è stato un clic su un oggetto nella finestra di dialogo. Ad esempio, aprendo la finestra di dialogo, la riga il cui valore è attualmente visualizzato sul pannello apparirà come selezionata. Se si fa clic su un'altra opzione nell'elenco, il programma utilizzerà la funzione SelectionOptionInDialogWindow che seleziona l'opzione dell'elenco della finestra di dialogo selezionata.
Se si fa clic sull'opzione dell'elenco attualmente selezionata, questo oggetto verrà identificato come un oggetto da modificare e apparirà una casella di input in modo che sia possibile inserire un nuovo valore quando si fa clic sulla casella. La funzione SetEditObjInDialogWindow è responsabile dell'impostazione della casella di input.
E infine, se è stato cliccato il pulsante Applica, il programma verificherà se il valore è stato modificato. In caso affermativo, il nuovo valore apparirà sul pannello e verrà scritto nel file.
Di seguito è riportato il codice della funzione principale della finestra di dialogo:
//+------------------------------------------------------------------+ //| 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(); } } }
Ogni volta che viene inserito un nuovo valore nella casella di input, viene generato l'evento CHARTEVENT_OBJECT_EDIT e il programma passa alla funzione ChartEvent_ObjectEndEdit. Se il valore della finestra di dialogo è stato modificato, il valore inserito verrà memorizzato, verificato la correttezza e assegnato all'oggetto nella lista. Puoi vederlo più in dettaglio nel codice qui sotto:
//+------------------------------------------------------------------+ //| 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); }
L'Expert Advisor in azione può essere visto nel video qui sotto:
Conclusione
I file compressi allegati alla fine dell'articolo possono essere scaricati per uno studio più approfondito.
Spero che questo articolo aiuterà quelli di voi che iniziano a imparare MQL5 solo a trovare risposte rapide a molte domande usando i semplici esempi forniti. Ho intenzionalmente omesso alcuni controlli dagli snippet di codice forniti.
Ad esempio, se si modifica l'altezza/larghezza del grafico quando la finestra di dialogo è aperta, la finestra di dialogo non verrà centrata automaticamente. E se lo rabbocchi selezionando un'altra opzione dall'elenco, l'oggetto che serve a selezionare la relativa riga verrà notevolmente spostato. Lascia che questo sia il tuo compito. È molto importante esercitarsi nella programmazione e più ti eserciti, meglio è.
Buona fortuna!
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/572
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso