Creazione di Pannelli di Controllo Attivi in MQL5 per il Trading
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.
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.
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":
Figure 3. Elementi della scheda "Mercato"
Figura 4. Elementi della scheda "In sospeso"
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 2. 6. Elementi della Scheda "Modifica/Chiudi"
Successivamente creiamo le sottoschede. Per lavorare con i trade:
Figura 2. 7. Elementi per lavorare con le posizioni
E 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 ->:
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:
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.
Figure 11. Elementi della scheda "In sospeso"
Figure 12. Elementi della scheda "Modifica/Chiudi"
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"
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 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 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
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
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 - 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 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 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:
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.
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
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso