English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Creazione di Pannelli di Controllo Attivi in MQL5 per il Trading

Creazione di Pannelli di Controllo Attivi in MQL5 per il Trading

MetaTrader 5Expert Advisors | 16 dicembre 2021, 10:09
241 0
Евгений
Евгений

Introduzione

L'efficienza è molto essenziale in un ambiente di lavoro, soprattutto nel lavoro dei trader, dove la velocità e la precisione giocano un ruolo importante. Durante la preparazione del terminal per il lavoro, ognuno rende il suo posto di lavoro il più confortevole possibile per se stesso, al fine di implementare le analisi ed entrare nel mercato il prima possibile. Ma la realtà della questione è che gli sviluppatori non possono sempre accontentare tutti ed è impossibile sintonizzarsi sul desiderio di determinate funzioni.

Ad esempio, per uno scalper, ogni frazione di secondo e ogni pressione del tasto "Nuovo ordine" è importante e la successiva impostazione di tutti i parametri potrebbe essere critica in termini di tempo.

Allora come troviamo una soluzione? La soluzione risiede nella personalizzazione degli elementi, poiché MetaTrader 5 fornisce componenti meravigliosi come "Pulsante", "Modifica" e "Etichetta". Facciamolo.


2. Opzioni del Pannello

Prima di tutto, decidiamo che tipo di funzioni sono essenziali per un pannello. Porremo l'accento principale sul trading, utilizzando il pannello e, quindi, includeremo le seguenti funzioni:

  • Apertura della posizione
  • Inserimento di un ordine in sospeso
  • Modifica della posizione/ordine
  • Chiusura della posizione
  • Eliminazione di un ordine in sospeso.

Inoltre, non verrà fatto alcun danno aggiungendo la possibilità di personalizzare il pannello della combinazione di colori, le dimensioni dei caratteri e le impostazioni di salvataggio. Diamo una descrizione più dettagliata di tutti gli elementi del pannello futuro. Specifichiamo il nome dell'oggetto, il suo tipo e la descrizione del suo scopo, per ciascuna delle funzioni del pannello. Il nome di ogni oggetto inizierà con "ActP" - questa sarà una specie di chiave, che indica che l'oggetto appartiene al pannello.

2.1. Posizione Aperte

Di seguito introdurremo tutti i parametri necessari per l'apertura della posizione, e la implementeremo facendo clic su un pulsante. Le linee ausiliarie, che si attivano spuntando una casella, ci aiuteranno nell'impostazione dei livelli di Stop Loss e Take Profit. La selezione del tipo di esecuzione verrà effettuata utilizzando i pulsanti di opzione.

Nome
Tipo
Descrizione
 ActP_buy_button1  Bottone
 Pulsante per il trade Buy
 ActP_sell_button1
 Bottone
 Pulsante per il trade Sale
 ActP_DealLines_check1
 Flag
 Set/reset flag delle linee ausiliarie
 ActP_Exe_radio1
 Pulsante di Opzione
 Gruppo di pulsanti di opzione per la selezione del tipo di operazione
 ActP_SL_edit1
 Campo di Input
 Campo per inserire uno Stop Loss
 ActP_TP_edit1
 Campo di Input
 Campo per inserire un Take Profit
 ActP_Lots_edit1
 Campo di Input
 Campo per inserire l'importo
 ActP_dev_edit1
 Campo di Input
 Campo per inserire una deviazione tollerabile durante l'apertura
 ActP_mag_edit1
 Campo di Input
 Campo per inserire un numero
 ActP_comm_edit1  Campo di input  Archiviato per l'inserimento di commenti

Tabella 1 Elenco degli elementi del pannello, "Apertura del Trading"

2.2 Effettuare un Ordine Pendente

Di seguito introdurremo tutti i parametri necessari per l'inserimento di un ordine in sospeso e li inseriremo premendo un tasto. Le linee di supporto, che vengono attivate selezionando un flag, aiuteranno a impostare Stop Loss, Take Profit, livelli di stop-limit e tempi di scadenza. La selezione del tipo di esecuzione e del tipo di scadenza, verrà effettuata con l'ausilio di un gruppo di pulsante di opzione.

Nome
Tipo
Descrizione
 ActP_buy_button2  Bottone
 Pulsante per impostare un ordine di Acquisto
 ActP_sell_button2
 Bottone
 Pulsante per impostare un ordine di trading
 ActP_DealLines_check2
 Flag
 Le linee ausiliarie impostano / resettano flag
 ActP_lim_check2  Flag  Ordine stop-limit set/reset flag 
 ActP_Exe_radio2
 Pulsante di Opzione
 Gruppo di pulsanti di opzione per la selezione del tipo di esecuzione dell'ordine
 ActP_exp_radio2  Pulsante di Opzione  Gruppo di pulsanti di opzione per la selezione del tipo di scadenza dell'ordine
 ActP_SL_edit2
 Campo di input
 Campo per inserire uno Stop Loss
 ActP_TP_edit2
 Campo di input
 Campo per inserire un Take Profit
 ActP_Lots_edit2
 Campo di input
 Campo per inserire l'importo
 ActP_limpr_edit2
 Campo di input  Campo per inserire il prezzo di un ordine stop-limit
 ActP_mag_edit2
 Campo di input
 Campo per inserire il numero magico
 ActP_comm_edit2  Campo di input  Campo per i commenti
 ActP_exp_edit2  Campo di input  Campo per inserire la data di scadenza
 ActP_Pr_edit2  Campo di input  Campo per l'inserimento del prezzo di esecuzione dell'ordine 

Tabella 2 Elenco degli elementi del pannello "Inserimento ordini pendenti"

2.3. Modifica/Chiusura dei Trade

Di seguito introdurremo tutti i parametri necessari per la modifica e la chiusura di un trade. Le linee ausiliarie, che si attivano spuntando una casella, ci aiuteranno nell'installazione dei livelli di Stop Loss e Take Profit. La selezione delle operazioni verrà generata da un elenco a discesa.

Nome
Tipo
Descrizione
 ActP_ord_button5   Elenco a tendina   Elenco delle selezioni per un trade
 ActP_mod_button4  Bottone
 Pulsante di modifica del trade 
 ActP_del_button4
 Bottone
 Pulsante di chiusura del trade 
 ActP_DealLines_check4
 Flag
 Flag set/reset linee ausiliarie
 ActP_SL_edit4
 Campo di input
 Campo per inserire uno Stop Loss
 ActP_TP_edit4
 Campo di input
 Campo per inserire un Take Profit
 ActP_Lots_edit4
 Campo di input
 Campo per inserire l'importo 
 ActP_dev_edit4
 Campo di input
 Campo per inserire una deviazione tollerabile 
 ActP_mag_edit4
 Campo di input
 Campo per la visualizzazione del numero magico (sola lettura)
 ActP_Pr_edit4  Campo di input  Campo per visualizzare il prezzo di apertura (sola lettura)

Tabella 3. Elenco degli elementi del pannello "Modifica/chiusura del Trade"

2.4. Modifica/Rimozione degli Ordini

Di seguito introdurremo tutti i parametri necessari per la modifica e la rimozione degli ordini in sospeso. Le linee di supporto, che vengono attivate selezionando una casella, aiuteranno nell'installazione di fermate, riprese, livelli di stop-limit e tempi di scadenza. La selezione del tipo di tempi di scadenza verrà generata con l'aiuto di un gruppo di pulsanti di opzione. La selezione degli ordini verrà generata da un elenco a discesa.

Nome
Tipo
Descrizione
 ActP_ord_button5  Elenco a tendina  Elenco per selezionare l'ordine
 ActP_mod_button3  Bottone
 Modifica dell'ordine 
 ActP_del_button3
 Bottone
 Pulsante di rimozione dell'ordine 
 ActP_DealLines_check3
 Flag
 Flag set/reset linee ausiliarie
 ActP_exp_radio3  Pulsante di Opzione  Gruppo di pulsanti di opzione per selezionare il tipo di scadenza di un ordine
 ActP_SL_edit3
 Campo di input
 Campo per inserire uno Stop Loss
 ActP_TP_edit3
 Campo di input
 Campo per l'inserimento di un take
 ActP_Lots_edit3
 Campo di input
 Campo che visualizza il volume (sola lettura)
 ActP_limpr_edit3
 Campo di input  Campo per l'inserimento del prezzo per un ordine stoplimit 
 ActP_mag_edit3
 Campo di input
 Campo che mostra i numeri magici (sola lettura)
 ActP_comm_edit3  Campo di input  Campo per i commenti
 ActP_exp_edit3  Campo di input  Campo per l'inserimento della data di scadenza
 ActP_Pr_edit3  Campo di input  Campo per inserire il prezzo di esecuzione dell'ordine 
 ActP_ticket_edit3  Campo di input  Campo che visualizza il ticket d'ordine (sola lettura) 

Tabella 4. Elenco degli elementi del pannello "Modifica/rimozione ordini"

2.5 Impostazioni

Di seguito sceglieremo il colore di pulsanti, etichette e testi dall'elenco a discesa, oltre a impostare varie dimensioni del carattere.

Nome
Tipo
Descrizione
 ActP_col1_button6  Elenco a tendina
 Elenco delle selezioni di colore per i pulsanti
 ActP_col2_button6
 Elenco a tendina
 Elenco di selezione del colore per i tag
 ActP_col3_button6
 Elenco a tendina
 Elenco di selezione del colore del testo
 ActP_font_edit6
 Campo di input
 Campo per specificare la dimensione del testo

Tabella 5. Elenco degli elementi del pannello "Impostazioni"

Viene anche aggiunto un pulsante per creare la possibilità di ridurre a icona il pannello se non viene utilizzato. Potresti aver notato la presenza di uno strumento come "linee di supporto". Cosa sono e perché ne abbiamo bisogno? Attraverso l'uso di queste linee, saremo in grado di impostare uno Stop Loss, Take Profit, il prezzo di attivazione di un ordine in sospeso, il prezzo di un ordine stop-limit (linee orizzontali), nonché il tempo di scadenza di un ordine posticipato (linea verticale), trascinando semplicemente con il mouse queste linee fino al prezzo/ora desiderato.

Dopotutto, un'installazione visuale è più conveniente di una testuale (inserire manualmente prezzi/tempo nell'apposito campo). Inoltre, queste righe ci serviranno come "punti salienti" del parametro di un ordine selezionato. Poiché possono esserci molti ordini, le linee ombreggiate del terminale standard, che di solito mostrano i prezzi, possono diventare molto confuse. 


3. L'Approccio Generale alla Creazione dell'Interfaccia

Quindi abbiamo stabilito con successo il nostro obiettivo: creare una forma di assistente grafico all'interno del settore. A tal fine, abbiamo bisogno dell'interfaccia più user-friendly. Innanzitutto, deve essere chiaro che tutti gli elementi di controllo (e saranno molti) dovranno essere creati tramite software, e quindi la posizione e la dimensione degli oggetti devono essere precalcolate.

Ora immaginate di aver passato un lungo, faticoso e difficile periodo, elaborando le coordinate degli oggetti, assicurandoci che non si sovrappongano e siano ben visibili; e poi c'è bisogno di aggiungere un nuovo oggetto, e il nostro intero schema ora deve essere ricostruito!

Chi ha familiarità con l'ambiente di sviluppo rapido di applicazioni (Delphi, C++ Builder, ecc.) sa quanto velocemente può essere creata l'interfaccia utente più complicata.

Proviamo ad implementarlo utilizzando MQL5. Innanzitutto, utilizzando un mouse, individuiamo gli oggetti di controllo nel modo più appropriato e ne regoliamo le dimensioni. Quindi, scriviamo un semplice script che legge le proprietà di tutti gli oggetti sul grafico e li registra in un file e, quando necessario, saremo facilmente in grado di recuperare tali proprietà e ricostruire completamente gli oggetti su qualsiasi grafico.

Il codice dello script potrebbe essere simile a questo:

//+------------------------------------------------------------------+
//|                                  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!");
     }
  }
//+------------------------------------------------------------------+

Come puoi vedere, il codice è estremamente semplice, scrive su un file binario alcune proprietà di tutti gli oggetti grafici. La cosa più importante è non dimenticare l'ordine di sequenza delle proprietà registrate durante la lettura del file. 

Lo script è pronto, quindi passiamo alla creazione dell'interfaccia.

E la prima cosa che faremo è organizzare il menu principale in base al tipo delle sue schede. Perché abbiamo bisogno di schede? Perché ci sono molti oggetti e montarli tutti sullo schermo sarebbe problematico. E poiché gli oggetti sono raggruppati di conseguenza (vedi tabella sopra), è più facile posizionare ciascun gruppo in una scheda separata.   

Pertanto, utilizzando il menu del terminale Inserisci -> Oggetti -> Pulsante, creeremo cinque pulsanti nella parte superiore del grafico, che fungeranno da nostro menu principale.

Figura 1. Schede del pannello

Fig. 1 Schede del pannello

Non dimentichiamo che gli oggetti possono essere facilmente duplicati, selezionandone uno e poi trascinandolo con il mouse, tenendo premuto il tasto "Ctrl". In questo modo creeremo una copia dell'oggetto anziché ricollocare il suo originale.

Particolare attenzione va data ai nomi degli oggetti, senza dimenticare che devono iniziare tutti con "ActP". Inoltre, aggiungiamo "main" al nome della stringa, che indica che l'oggetto appartiene alla barra dei menu principale.

Figura 2. Elenco degli oggetti (schede del pannello)

Figure 2. Lista degli oggetti (schede del pannello)

Allo stesso modo, applichiamo il contenuto della scheda al nuovo grafico. Il contenuto di ogni scheda dovrebbe essere posizionato su un grafico separato!

Tab "Market":

Figura 3. Elementi della scheda "Mercato"

Figure 3. Elementi della scheda "Mercato"

Scheda "In sospeso":

Figura 4. Elementi della scheda "In sospeso"

Figura 4. Elementi della scheda "In sospeso"

Scheda Impostazioni:

Figura 5. Elementi della scheda "Impostazioni"

Figura 5. Elementi della scheda "Impostazioni"

L'ultima scheda "Modifica / chiudi" è diversa, servirà per modificare / eliminare gli ordini in sospeso, nonché modificare e chiudere le operazioni di trading. Sarà ragionevole dividere il lavoro con trade e il lavoro con ordini in due sotto-tab separati. Per prima cosa creiamo un pulsante che attiverà l'elenco a discesa, dal quale sceglieremo un ordine o un trade con cui lavorare.

Figura 6. Elementi della scheda "Modifica/Chiudi"

Figura 2. 6. Elementi della Scheda "Modifica/Chiudi"

Successivamente creiamo le sottoschede. Per lavorare con i trade:

Figura 7. Elementi per lavorare con le posizioni

Figura 2. 7. Elementi per lavorare con le posizioni

E per lavorare con gli ordini:

Figura 8. Sottoscheda per lavorare con gli ordini

Figure 8. Sottoscheda per lavorare con gli ordini

Ecco fatto, l'interfaccia è stata creata.

Applichiamo lo script a ciascuno dei grafici per salvare ogni scheda in un file separato. Il parametro di input "interfaceID" deve essere diverso per ogni scheda:

  • 0 - Home page
  • 1 - Market
  • 2 - In sospeso
  • 3 - Pulsante per l'attivazione dell'elenco di selezione degli scambi / ordini
  • 4 - Impostazioni
  • 6 - Sottoscheda per lavorare con i trade
  • 7 - Sottoscheda per lavorare con gli ordini

La scheda numero 5 corrisponde al pulsante "Riduci finestra" nel menu principale, quindi non ci sono oggetti su di essa e possiamo saltarla.

Dopo tutte queste manipolazioni, i seguenti file appariranno nella cartella della directory del terminale -> MQL5 ->:

Figura 8. Elenco dei file degli schemi dei pannelli

Figure 9. Elenco dei file con schemi di pannelli


4. Download di Elementi dell'Interfaccia

Ora gli elementi dell'interfaccia sono archiviati in file e sono pronti per essere utilizzati. Per cominciare, determiniamo il luogo in cui si troverà il nostro pannello.  Se lo posizioniamo direttamente sul grafico principale, bloccherà il grafico dei prezzi, il che è molto scomodo. Pertanto, sarà più ragionevole posizionare il pannello nella finestra secondaria del grafico principale. Un indicatore può creare questo pannello.

Creiamolo:

#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);
  }

Il codice è molto semplice perché la funzione principale di questo indicatore è la creazione di finestre secondarie, piuttosto che effettuare vari calcoli. L'unica cosa che faremo è installare un nome "breve" dell'indicatore, con il quale possiamo trovare la sua finestra secondaria. Compileremo e applicheremo un grafico all'indicatore e apparirà una finestra.

Ora concentriamoci sul pannello Expert Advisor. Creeremo un nuovo Expert Advisor.

La funzione OnInit() conterrà i seguenti operatori:

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);
  }

Qui lanciamo un timer (il motivo per cui è fatto verrà spiegato di seguito), otteniamo gli ultimi prezzi dal mercato, utilizzando ChartWindowFind, individuiamo la finestra dell'indicatore e la salviamo come variabile. Flag last_loaded - indica se questa è la prima volta che l'Expert Advisor viene inizializzato. Queste informazioni saranno necessarie per evitare di ricaricare l'interfaccia durante una reinizializzazione. 

La funzione create_interface() ha il seguente aspetto:

//+------------------------------------------------------------------+
//| 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();
  }

Il primo passo è controllare il parametro di input "reset settings" e, se è installato, rimuovere le variabili globali, responsabili delle impostazioni. Di seguito verrà descritto come questa azione influisca sul pannello. Inoltre, la funzione ApplyScheme() creerà un'interfaccia da un file. 

//+------------------------------------------------------------------+
//| 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);
  }

Ancora una volta, non c'è niente di complicato in questo. La funzione aprirà il file desiderato, con uno schema di interfaccia pre-salvato e lo creerà nella finestra, che abbiamo precedentemente individuato (finestra indicatore). Inoltre selezioniamo i colori degli oggetti e le dimensioni dei caratteri dalle variabili globali del terminale. 

La funzione Objects_Selectable() rende tutti gli oggetti, ad eccezione delle linee ausiliarie, non contrassegnati, al fine di attivare le animazioni dei pulsanti ed evitare di eliminare accidentalmente un oggetto necessario.

//+------------------------------------------------------------------+
//| 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); 
        }
     }
  }

Ora diamo un'occhiata alla funzione OnTick(). Ci servirà per ottenere gli ultimi prezzi sul mercato.

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

La funzione get_prices() ha la 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 non dimenticare 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();
  }

Prima di tutto verifica la causa della reinizializzazione: se è dovuta a un cambio di timeframe e/o simboli, non elimineremo la voce del pannello. In tutti gli altri casi, rimuovere tutti gli elementi utilizzando la funzione 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);
     }
  }

Dopo aver compilato ed eseguito l'Expert Advisor, otteniamo il seguente risultato:

Figura 10. Esempio di lavoro di Expert Advisor

Figure 10. Esempio di lavoro di un Expert Advisor

Tuttavia, tutto ciò serve a poco finché non siamo in grado di far sì che questi oggetti rispondano alla nostra manipolazione.


5. Gestione degli Eventi

L'interfaccia è stata creata, ora dobbiamo farla funzionare. Tutte le nostre azioni con oggetti generano eventi specifici. La funzione OnChartEvent OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) è il meccanismo di gestione degli eventi ChartEvent . Di tutti gli eventi che ci interessano i seguenti: 

  • CHARTEVENT_CLICK - clicca sul grafico
  • CHARTEVENT_OBJECT_ENDEDIT - finito di modificare il campo di input 
  • CHARTEVENT_OBJECT_CLICK - clicca sull'oggetto grafico

Nel nostro caso, il parametro della funzione id indica l'ID dell'evento, sparam - indica il nome dell'oggetto, che genera questo evento, e tutti gli altri parametri non ci interessano.

Il primo evento che esploreremo è il - fare clic sul pulsante del menu principale.

5.1. Gestione degli eventi del menu principale

Ricordiamo che il menu principale è composto da cinque pulsanti. Quando si fa clic su uno di essi, dovrebbe andare in modalità premuta, indirizzarci all'interfaccia giusta e caricare le schede appropriate. Quindi, tutti gli altri pulsanti del menu dovrebbero andare in modalità non premuto.

//+------------------------------------------------------------------+
//| 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 c'è stato un clic sul pulsante del menu, allora abbiamo eseguito la funzione Main_controls_click(). Ridisegniamo il grafico utilizzando ChartRedraw() e completiamo la funzione. Dovremmo completare l'esecuzione perché è possibile fare clic su un solo oggetto alla volta e, pertanto, tutte le ulteriori implementazioni porteranno a uno spreco di tempo della 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);
  }

Siamo stati introdotti alle funzioni Objects_Selectable() e ApplyScheme() e in seguito passeremo alla funzione DeleteLists().

La funzione SaveScheme() salva un file di interfaccia, in modo che gli oggetti mantengano tutte le loro proprietà durante un ricaricamento:

//+------------------------------------------------------------------+
//| 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);
     }
  }

La funzione DeleteScheme() rimuove gli oggetti tab.

//+------------------------------------------------------------------+
//| 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);
     }
  }

Pertanto, eseguendo la funzione Main_controls_click(), rimuoveremo la vecchia scheda, salvandola in anticipo, e ne caricheremo una nuova.

Compilando l'Expert Advisor, vedremo i risultati.

Ora faremo clic sul pulsante del menu principale, caricheremo le nuove schede, mantenendole nello stato delle schede originali.

Figura 11. Elementi della scheda "In sospeso"

Figure 11. Elementi della scheda "In sospeso"

Figura 12. Elementi della scheda "Modifica/Chiudi"

Figure 12. Elementi della scheda "Modifica/Chiudi"


Figura 12. Elementi della scheda "Impostazioni"

Figure 13. Elementi della scheda "Impostazioni"

Con questo possiamo terminare la manipolazione del menu principale, poiché ora svolge pienamente le sue funzioni.

5.2. Gestione dell'Evento del Componente "Flag"

L'impostazione delle linee ausiliarie e degli ordini stoplimit viene effettuata utilizzando i componenti "flag", ma non è presente nell'elenco degli oggetti grafici di MT5. Quindi creiamolo. C'è un oggetto "etichetta grafica", che di fatto è un'immagine che ha uno stato "acceso" e uno stato "spento". Lo stato può essere variato facendo clic sull'oggetto. È possibile impostare un'immagine separata per ogni stato. Scegli un'immagine per ciascuno degli stati:

  •  Attivato
  •  Disattivato

Impostiamo le immagini nelle proprietà dell'oggetto:

Figura 13. Impostazione delle proprietà dell'elemento "flag"

Figura 13. Impostazione delle proprietà dell'elemento "flag"

Si ricorda che le immagini per essere disponibili nell'elenco devono essere posizionate nella cartella "Cartella Terminale->MQL5->Immagini" e avere l'estensione ".Bmp".

Passiamo agli eventi di elaborazione, che si verificano quando si fa clic su un oggetto. Useremo l'esempio della bandiera, che è responsabile del posizionamento delle linee ausiliarie all'apertura del trade.

      //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;
      }

Lo stesso metodo viene utilizzato per i flag, che sono responsabili dell'elaborazione e dell'installazione delle linee ausiliarie nella scheda di chiusura / modifica degli ordini in sospeso. Pertanto, non entreremo nei dettagli su di loro in questo articolo. Coloro che desiderano familiarizzarsi con essi possono utilizzare il codice Expert Advisor.

L'impostazione del flag degli ordini stoplimit nella scheda "In sospeso" ha il seguente handler:

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);
         }
      }       
   ...   
   }
...
}

Ora abbiamo finito di lavorare con le flag. Consideriamo il seguente oggetto di nostra produzione - "gruppo di pulsanti radio".

5.3. Gestione Evento Componente "Gruppo Pulsanti di Opzione"

Utilizzando questo componente, selezioniamo il tipo di trade e il tipo di scadenza dell'ordine. Proprio come nel caso delle flag, utilizzeremo tag grafici, ma questa volta con nuove immagini. 

  • Attivato
  • Disattivato

Ma qui il problema è complicato a causa della necessità di ripristinare tutti i pulsanti di opzione, tranne quello su cui si fa clic, in uno stato inattivo. Considera l'esempio del pulsante di opzione del tipo di esecuzione dell'ordine:

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;
      }     
   ...   
   }
...
}

I pulsanti di opzione del tipo di scadenza dell'ordine differiscono solo per il fatto che quando si fa clic sul terzo, è necessario eseguire un passaggio aggiuntivo: è necessario impostare una nuova data nell'ora di inserimento della scadenza di un ordine:

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;
   ...   
   }
...
}

Ora abbiamo finito di lavorare con i pulsanti di opzione.

5.4. Creazione e gestione di eventi di elenchi a discesa

Utilizzeremo l'elenco a discesa per la scelta di ordini / trade per la modifica / chiusura / rimozione e il pannello di selezione dei colori. Iniziamo con l'elenco delle trade/ordini.

La prima cosa che appare nella scheda "Modifica / chiusura" è un pulsante con l'etichetta "Seleziona un ordine ", questo sarà il pulsante che attiva l'elenco. Quando fai clic su di esso, l'elenco a discesa dovrebbe aprirsi e, dopo aver effettuato la selezione, dovrebbe ripiegarsi di nuovo.  Diamo un'occhiata al gestore CHARTEVENT_OBJECT_CLICK di questo pulsante:

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;
      }      
   ...   
   }
...
}

Il nostro obiettivo principale è determinare se i trade/gli ordini sono sul mercato e, in tal caso, estrarre informazioni da essi e visualizzarle nell'elenco. La funzione get_ord_info() esegue questo ruolo:

//+------------------------------------------------------------------+
//| 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++;
        }
     }
  }

Unirà in un blocco informazioni e ordinerà ticket e trade.

Inoltre, la funzione create_list() creerà un elenco sulla base di queste informazioni:

//+------------------------------------------------------------------+
//| 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 infine, le funzioni DeleteLists () rimuovono gli elementi dell'elenco:

//+------------------------------------------------------------------+
//| 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);
     }
  }

Quindi ora quando fai clic sul pulsante di attivazione viene creato un elenco. Dobbiamo farlo funzionare, poiché ad ogni clic su qualsiasi elemento dell'elenco, deve avvenire un'azione specifica. In particolare: il caricamento di un'interfaccia per lavorare con un ordine e il riempimento di questa interfaccia con le informazioni sull'ordine/trade. 

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();
      }
   ...   
   }
...
}

È qui che si complica. Poiché non conosciamo in anticipo la dimensione dell'elenco e i nomi dei suoi oggetti, dovremo recuperare informazioni da esso recuperando il nome dell'elemento dell'elenco. La funzione SetScheme() imposterà l'interfaccia appropriata - per lavorare con uno scambio o con un ordine in sospeso:

//+------------------------------------------------------------------+
//| 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);
        }
     }
  }

Le funzioni SetPositionParams() e SetOrderParams() installano le proprietà richieste dell'interfaccia caricata:

//+------------------------------------------------------------------+
//| 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 il tocco finale: l'elenco dovrebbe essere rimosso quando fai clic sul grafico, utilizzando CHARTEVENT_CLICK per questo 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;
   }
...
}

Di conseguenza, abbiamo un bel elenco a discesa:

Figura 14. Esempio di pannello a tendina "Modifica/Chiudi"

Figura 2. 14. Un esempio del pannello a tendina "Modifica/Chiudi"

Ora dobbiamo creare un elenco di selezione dei colori nella scheda Impostazioni. 

Considera gli handler dei pulsanti di attivazione:

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;
      }
   ...   
   }
...
}

Qui seguiamo lo stesso metodo dell'elenco di selezione degli ordini.

La funzione di creazione di un elenco è diversa:

//+------------------------------------------------------------------+
//| 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);
     }
  }

Inoltre, elaboriamo il processo di clic per l'elemento dell'elenco:

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();
      }
   ...   
   }
...
}

La funzione SetButtonsColor() imposta il colore dei pulsanti:

//+------------------------------------------------------------------+
//| 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);
  }

Vediamo i risultati di seguito:

Figura 15. Impostazione dei colori dei pulsanti

Figura 2. 15. Impostazione del colori dei pulsanti

Gli elenchi della selezione dei colori e delle etichette di testo sono simili. Di conseguenza, possiamo rendere il pannello piacevolmente colorato in pochi clic:

Figura 16. Colori modificati di pannelli, pulsanti e testo

Figura 16. Colori modificati di pannelli, pulsanti e testo

Ora abbiamo finito con le liste. Passiamo ai campi di input.

5.5. Gestione dell'Evento del Campo di Inserimento

Un campo di inserimento genererà un evento CHARTEVENT_OBJECT_ENDEDIT che si verifica al completamento della modifica del testo nel campo. L'unico motivo per cui dobbiamo gestire questo evento è dovuto all'impostazione di linee ausiliarie per i prezzi, relative ai prezzi nei campi di inserimento.

Consideriamo l'esempio di una linea di arresto:

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;
      }
   ...   
   }
...
}

Gli altri campi di inserimento vengono elaborati in modo simile.

5.6 Gestione degli Eventi del Timer

Il timer viene utilizzato per monitorare le linee ausiliarie. In questo modo, quando si spostano le righe, i valori dei prezzi a cui sono collegate, si spostano automaticamente nel campo di input. Ad ogni tick del timer, viene eseguita la funzione OnTimer().

Si consideri l'esempio del posizionamento del tracciamento delle linee Stop Loss e Take Profit con la scheda "Market" attiva:

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();
}
//+------------------------------------------------------------------+

Il monitoraggio di altre linee è implementato in modo simile.


6. Esecuzione delle Operazioni Commerciali

Quindi a questo punto abbiamo compilato tutti i campi di inserimento richiesti, le caselle di controllo, le righe e i pulsanti di opzione. Ora è il momento di provare un po' di trading basato su tutti i dati che abbiamo.

6.1. Apertura dell'Affare

La scheda "Dal mercato" contiene i pulsanti "Buy" e "Sell". Se tutti i campi sono compilati correttamente, un trade dovrebbe essere implementato quando si fa clic su uno dei pulsanti.

Diamo un'occhiata agli handler di questi pulsanti:

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;
      }     
   ...   
   }
...
}

Vedete, la funzione deal() sta funzionando.

//+------------------------------------------------------------------+
//| 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);
  }

Niente di soprannaturale. Prima leggiamo le informazioni richieste dagli oggetti e, sulla base di queste, creiamo una richiesta di trade.

Controlliamo il nostro lavoro:

Figura 17. Operazioni di trading - il risultato dell'esecuzione del trade Buy

Figura 17. Operazioni di trading - il risultato dell'esecuzione del trade Buy

Come puoi vedere, il trade di Buy è stata eseguito con successo.

6.2. Effettuare un Ordine Pendente

I pulsanti "Buy" e "Sell" nel tab "In attesa" sono responsabili dell'inserimento degli ordini in sospeso.

Consideriamo gli handler:

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;
      }        
   ...   
   }
...
}

Qui determiniamo il tipo di ordini futuri sulla base del rapporto tra il prezzo di mercato corrente e il prezzo impostato, dopodiché la funzione order() determina l'ordine:

//+------------------------------------------------------------------+
//| 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);
  }

Controlliamo il nostro lavoro:

Figura 18. Operazioni di trading - l'esito dell'ordine in sospeso

Figura 18. Operazioni di Trading - il risultato dell’inserimento dell’ordine in sospeso

Il limite di acquisto è impostato correttamente.

6.3. Modifica della Posizione

Il pulsante Modifica nella scheda "Modifica/Chiudi" è responsabile della modifica della posizione selezionata:

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;
      }     
   ...   
   }
...
}

La funzione Modify_pos() è direttamente responsabile della modifica:

//+------------------------------------------------------------------+
//| 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);
  }

Risultati:

Figura 19. Operazioni di trading - il risultato della modifica delle proprietà del trade (imposta TP e SL)

Figura 2. 19. Le operazioni di trading- il risultato della modifica delle proprietà del commercio (TP e SL)


I livelli di Stop Loss e Take Profit vengono modificati con successo.

6.4. Chiusura Posizioni

Il pulsante Chiudi della scheda "Modifica/Chiudi" è responsabile della chiusura (eventualmente parziale) della posizione:

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;
      }     
   ...   
   }
...
}

La funzione close_pos() è responsabile della chiusura:

//+------------------------------------------------------------------+
//| 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);
  }

Il risultato - ha chiuso 1,5 lotti di tre della transazione selezionata:

Figura 20. Trading - chiusura parziale della posizione

Figura 2. 20. Trading - chiusura parziale della posizione

6.5. Modifica dell'Ordine in Sospeso.

Il pulsante Modifica nella scheda "Modifica/chiusura" è responsabile della modifica dell'ordine selezionato:

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;
      }     
   ...   
   }
...
}

La funzione Modify_order() è responsabile della modifica:

//+------------------------------------------------------------------+
//| 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);
  }

Vediamo il risultato - un ordine è stato modificato con successo:

Figura 21. Modifica dell'ordine in sospeso

Figure 21. Modifica dell'ordine in sospeso

6.6. Eliminazione di un Ordine in Sospeso

Il pulsante Elimina nella scheda "Modifica/Chiusura" è responsabile dell'eliminazione dell'ordine selezionato:

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;
      }     
   ...   
   }
...
}

La funzione del_order() è responsabile della rimozione degli ordini:

//+------------------------------------------------------------------+
//| 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);
  }

Vediamo il risultato: l'ordine viene rimosso.

Figura 22. Trading - rimozione di un ordine in sospeso

Fig.22 Operazioni di trading - rimozione di un ordine in sospeso


Conclusione

Finalmente tutte le funzioni del pannello sono state testate e funzionano con successo.

Speriamo che le conoscenze acquisite dalla lettura di questo articolo ti aiutino nello sviluppo di pannelli di controllo attivi, che ti serviranno come aiuti insostituibili per lavorare sul mercato. 

Per iniziare con il pannello è necessario decomprimere l'archivio in una cartella con il terminale, quindi applicare l'indicatore APal grafico e solo successivamenteavviare Active PanelExpert Advisor.


Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/62

File allegati |
Collegamento di Expert Advisor con ICQ in MQL5 Collegamento di Expert Advisor con ICQ in MQL5
Questo articolo descrive il metodo di scambio di informazioni tra l'Expert Advisor e gli utenti ICQ, vengono presentati diversi esempi. Il materiale fornito sarà interessante per coloro che desiderano ricevere informazioni di trading in remoto da un terminale del cliente, tramite un client ICQ nel proprio telefono cellulare o PDA.
Creazione e Pubblicazione di Report di Trading e Notifiche SMS Creazione e Pubblicazione di Report di Trading e Notifiche SMS
I trader non hanno sempre la capacità e il desiderio di sedersi al terminale di trading per ore. Soprattutto se il sistema di trading è più o meno formalizzato e può identificare automaticamente alcuni degli stati del mercato. Questo articolo descrive come generare un report dei risultati di trading (utilizzando Expert Advisor, Indicator o Script) come file HTML e caricarlo tramite FTP sul server WWW. Prenderemo in considerazione anche l'invio di notifiche di eventi commerciali come SMS al telefono cellulare.
Creazione di un Gioco "Snack" in MQL5 Creazione di un Gioco "Snack" in MQL5
Questo articolo descrive un esempio di programmazione del gioco "Snake". In MQL5, la programmazione del gioco è diventata possibile principalmente grazie alle funzionalità di gestione degli eventi. La programmazione orientata agli oggetti semplifica notevolmente questo processo. In questo articolo imparerai le funzionalità di elaborazione degli eventi, gli esempi di utilizzo delle classi della libreria Standard MQL5 e i dettagli delle chiamate di funzione periodiche.
Un Esempio di Strategia di Trading Basata sulle Differenze di Fuso Orario nei Diversi Continenti Un Esempio di Strategia di Trading Basata sulle Differenze di Fuso Orario nei Diversi Continenti
Navigando in Internet, è facile trovare molte strategie che ti daranno vari consigli. Prendiamo l'approccio di un insider e esaminiamo il processo di creazione della strategia, basato sulle differenze nei fusi orari nei diversi continenti.