English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
preview
Indicatori multipli su un grafico (Parte 06): Trasformare MetaTrader 5 in un sistema RAD (II)

Indicatori multipli su un grafico (Parte 06): Trasformare MetaTrader 5 in un sistema RAD (II)

MetaTrader 5Sistemi di trading | 18 agosto 2022, 09:16
195 1
Daniel Jose
Daniel Jose

Introduzione

Nel mio precedente articolo, ti ho mostrato come creare una Chart Trade utilizzando gli oggetti della MetaTrader 5 e quindi trasformare la piattaforma in un sistema RAD. Il sistema funziona molto bene, e sicuramente molti dei lettori potrebbero aver pensato alla creazione di una libreria, che consentirebbe di avere funzionalità estese nel sistema proposto. Partendo da questo presupposto, sarebbe possibile sviluppare un Expert Advisor più intuitivo con un'interfaccia più piacevole e facile da usare.

L'idea è così buona che mi ha spinto a mostrarti passo dopo passo come iniziare ad aggiungere funzionalità. Qui sto andando ad implementare due nuove e importanti funzionalità (queste informazioni serviranno come base per l'implementazione di altre funzionalità secondo le nostre necessità e esigenze). L'unico limite è la nostra creatività poiché gli elementi stessi possono essere utilizzati in un'ampia varietà di modi.


Progettazione

Le modifiche del nostro IDE saranno come mostrato nelle immagini seguenti:

         

Come puoi vedere, ci sono piccole modifiche al design stesso. Sono state aggiunte due nuove aree: una riceverà il nome dell'asset, e l'altra riceverà il valore accumulato della giornata. Bene, queste sono cose di cui possiamo farne a meno, e non influenzeranno le nostre decisioni. Ma possono essere interessanti ugualmente. Mostrerò il modo più semplice e corretto per aggiungere funzionalità al nostro IDE. Quindi, apri l'elenco degli oggetti nella nuova interfaccia. Appare come segue:


I due oggetti cerchiati non hanno eventi associati, il che significa che non funzionano nell'IDE. Tutti gli altri oggetti sono già correttamente associati a determinati eventi, e MetaTrader 5 può forzare la corretta esecuzione di questi eventi quando si verificano nell' EA. Cioè, possiamo modificare l'interfaccia IDE come desideriamo, ma se la funzionalità non è ancora implementata, MetaTrader 5 non farà niente ma visualizza l'oggetto sul grafico. Abbiamo bisogno che l'oggetto EDIT 00 riceva il nome dell'asset che stiamo tradando, e questo nome dovrebbe comparire al centro dell'oggetto. L'oggetto EDIT 01 riceverà il valore cumulativo di un certo periodo. Utilizzeremo il periodo giornaliero per conoscere se siamo in profitto o in perdita durante la giornata. Se il valore è negativo, sarà visualizzato in un colore, e se positivo, sarà utilizzato un'altro colore.

Entrambi i valori ovviamente non possono essere modificati dall'utente, così è possibile lasciare le loro proprietà in sola lettura, come mostrato nella figura seguente.


Tuttavia, tieni a mente che è impossibile specificare come verranno presentate le informazioni, ovvero, non possiamo allineare il testo in modo che appaia al centro dell'oggetto. Se desiderato, può essere fatto utilizzando codice, c'è una proprietà che allinea il testo impostandolo al centro. Vedere "Object properties" per ulteriori dettagli. Notare che ENUM_ALIGN_MODE nella tabella — contiene gli oggetti su cui è possibile utilizzare il testo giustificato.

Quindi, qualunque modifica intendiamo implementare, la prima cosa che dobbiamo fare è preparare un piano: definire le nuove funzionalità, la loro forma di presentazione e le modalità con cui l'utente interagirà con esse. Questo consentirà la selezione dell'oggetto giusto e la sua configurazione utilizzando il più possibile l'interfaccia propria di MetaTrader 5. Come risultato, avremo un IDE già pronto, e dovremo solo modificarlo tramite il codice MQL5, in modo che l'IDE finisca col funzionare al 100%. Quindi, procediamo con le modifiche.


Modifiche

Per evitare che il codice diventi un vero e proprio Frankenstein, dobbiamo organizzarci il più possibile per verificare quali funzionalità esistono già e quali effettivamente necessitano di essere implementate. In molti casi, può essere sufficiente apportare piccole modifiche al codice esistente, ottenerne uno nuovo e testarlo. Questo nuovo codice sarà riutilizzato in quello che stiamo per implementare, e l'unica cosa che ci resta da testare sono le piccole funzioni di controllo che aggiungeremo per creare funzionalità completamente nuove. I bravi programmatori lo fanno sempre: provano in qualche modo a riutilizzare codice esistente aggiungendo dei punti di controllo ad esso.


Modifica 1. Aggiungere il nome dell'asset

Per implementare questa parte, non abbiamo bisogno di sostanziali cambiamenti, ma questi cambiamenti dovrebbero essere implementati nei posti giusti. Per prima cosa, andiamo ad aggiungere un nuovo valore all'enumerazione, il codice sorgente è mostrato di seguito:

enum eObjectsIDE {eRESULT, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};



Sotto c'è il nuovo codice; la parte evidenziata è quello che è stato aggiunto. Nota che non ho aggiunto il nuovo valore ne all'inizio ne alla fine dell'enumerazione. Questo viene fatto per evitare di dover pasticciare con altre parti di codice che già esistono e funzionano.

enum eObjectsIDE {eRESULT, eLABEL_SYMBOL, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};



Se aggiungi il nuovo valore all'inizio o alla fine dell'enumerazione, dovrai trovare e modificare tutte le posizioni in cui sono stati utilizzati questi limiti. In molti casi possono essere commesse omissioni dovute a dimenticanze, che porteranno a errori difficili da trovare. Penseresti che gli errori sembreranno dovuti a nuove aggiunte, quando in realtà sono dovuti a dimenticanze. Per questo, aggiungiamo le modifiche più o meno tra i valori estremi.

Subito dopo, dobbiamo aggiungere un messaggio al sistema, altrimenti potremmo avere un errore di RunTime. Ora, aggiungiamo la seguente riga al nostro codice sorgente.

static const string C_Chart_IDE::szMsgIDE[] = {
                                                "MSG_RESULT",
                                                "MSG_NAME_SYMBOL",
                                                "MSG_BUY_MARKET",
                                                "MSG_SELL_MARKET",
                                                "MSG_DAY_TRADE",
                                                "MSG_CLOSE_POSITION",
                                                "MSG_LEVERAGE_VALUE",
                                                "MSG_TAKE_VALUE",
                                                "MSG_STOP_VALUE"
                                              };



Come puoi vedere, l'abbiamo aggiunto nello stesso posto per mantenere l'ordine. Ma al momento la costante può essere aggiunta in un qualsiasi punto, non farà alcuna differenza dato che viene utilizzata solo per verificare quale oggetto riceverà il messaggio. Ai fini strutturali, lo abbiamo aggiunto come secondo messaggio.

Ora ritorniamo alla MetaTrader 5 e effettuiamo le modifiche come mostrato di seguito:

         

Adesso, MetaTrader 5 riconosce già l'oggetto nel nostro IDE come un oggetto per ricevere un messaggio, e rimane solo da creare una procedura per mandare il messaggio. Il testo del messaggio dovrebbe essere aggiunto solo una volta, e può essere inviato non appena MetaTrader 5 posiziona il nostro IDE sul grafico. Questo potrebbe essere fatto aggiungendo semplicemente il codice necessario alla fine della funzione Create della nostra classe di oggetti. Ma ancora, così che il codice non diventi un Frankenstein pieno di correzzioni, aggiungeremo il nuovo codice all'interno della funzione DispatchMessage. La funzione originale si presenta così:

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if (szArg == szMsgIDE[eRESULT])
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:

// ... The rest of the code...

        }
}



Di seguito il codice dopo le relative modifiche:

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if (szArg == szMsgIDE[eRESULT])
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                        }else if (szArg == szMsgIDE[eLABEL_SYMBOL])
                        {
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_TEXT, Terminal.GetSymbol());
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_ALIGN, ALIGN_CENTER);
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:

// ... The rest of the code

        }
}



Dopo aver creato la funzione di invio, possiamo scegliere il punto in cui invieremo questo messaggio. Il punto migliore effettivamente è alla fine della funzione Create della nostra classe di oggetti, quindi il codice finale sarà simile a questo:

bool Create(int nSub)
{
        m_CountObject = 0;
        if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
        FileReadInteger(m_fp, SHORT_VALUE);
                                
        for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
        m_SubWindow = nSub;
        m_szLine = "";
        while (m_szLine != "</chart>")
        {
                if (!FileReadLine()) return false;
                if (m_szLine == "<object>")
                {
                        if (!FileReadLine()) return false;
                        if (m_szLine == "type")
                        {
                                if (m_szValue == "102") if (!LoopCreating(OBJ_LABEL)) return false;
                                if (m_szValue == "103") if (!LoopCreating(OBJ_BUTTON)) return false;
                                if (m_szValue == "106") if (!LoopCreating(OBJ_BITMAP_LABEL)) return false;
                                if (m_szValue == "107") if (!LoopCreating(OBJ_EDIT)) return false;
                                if (m_szValue == "110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) return false;
                        }
                }
        }
        FileClose(m_fp);
        DispatchMessage(CHARTEVENT_CHART_CHANGE, szMsgIDE[eLABEL_SYMBOL]);
        return true;
}


Ciò che è stato aggiunto è evidenziato in verde. Notare che senza apportare quasi nessuna modifica, abbiamo già implementato al 100% un flusso di messaggi, e possiamo passare al prossimo messaggio che necessita di essere implementato.


Modifica 2. Aggiunta del valore accumulato per la giornata (Punto di copertura)

Ancora, seguiamo la stessa logica che abbiamo usato quando abbiamo aggiunto il nome dell'asset e quindi il nuovo codice sarà simile a questo:

enum eObjectsIDE {eRESULT, eLABEL_SYMBOL, eROOF_DIARY, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};

// ... Rest of the code

static const string C_Chart_IDE::szMsgIDE[] = {
                                                "MSG_RESULT",
                                                "MSG_NAME_SYMBOL",
                                                "MSG_ROOF_DIARY",
                                                "MSG_BUY_MARKET",
                                                "MSG_SELL_MARKET",
                                                "MSG_DAY_TRADE",
                                                "MSG_CLOSE_POSITION",
                                                "MSG_LEVERAGE_VALUE",
                                                "MSG_TAKE_VALUE",
                                                "MSG_STOP_VALUE"
                                              };



Dopodiché, andiamo a cambiare l'IDE con un nuovo messaggio:

         

Il nostro nuovo IDE è pronto. Ora implementeremo il codice che creerà un messaggio contenente il valore accumulato per la giornata. Dovremmo prima decidere in quale classe verrà implementata questa funzionalità. Molte persone probabilmente creerebbero questa funzione qui, nella classe C_Chart_IDE, ma per motivi organizzativi, sarebbe meglio unirla alla funzione che lavora con gli ordini. Perciò, il codice viene implementato nella classe C_OrderView. Il suo codice è mostrato di seguito:

double UpdateRoof(void)
{
        ulong   ticket;
        int     max;
        string  szSymbol = Terminal.GetSymbol();
        double  Accumulated = 0;
                                
        HistorySelect(macroGetDate(TimeLocal()), TimeLocal());
        max = HistoryDealsTotal();
        for (int c0 = 0; c0 < max; c0++) if ((ticket = HistoryDealGetTicket(c0)) > 0)
                if (HistoryDealGetString(ticket, DEAL_SYMBOL) == szSymbol)
                        Accumulated += HistoryDealGetDouble(ticket, DEAL_PROFIT);
                                                
        return Accumulated;
}



Ora che il codice è stato implementato, dobbiamo aggiungere il messaggio al sistema. Per semplificare la vita dell'operatore, ho già inserito il codice per riportare i risultati già finalizzati. Qui c'è il suo codice:

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        static double AccumulatedRoof = 0.0;
        bool    b0;
        double  d0;

        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if ((b0 = (szArg == szMsgIDE[eRESULT])) || (szArg == szMsgIDE[eROOF_DIARY]))
                        {
                                if (b0)
                                {
                                        ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                                }else
                                {
                                        AccumulatedRoof = dValue;
                                        dValue = 0;
                                }
                                d0 = AccumulatedRoof + dValue;
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eROOF_DIARY].szName, OBJPROP_TEXT, DoubleToString(MathAbs(d0), 2));
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eROOF_DIARY].szName, OBJPROP_BGCOLOR, (d0 >= 0 ? clrForestGreen : clrFireBrick));
                        }else   if (szArg == szMsgIDE[eLABEL_SYMBOL])
                        {
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_TEXT, Terminal.GetSymbol());
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_ALIGN, ALIGN_CENTER);
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:

// .... The rest of the code....

        }
}



Le parti evidenziate supportano il sistema come descritto sopra. Se l'implementazione non fosse implementata in questo modo, dovremmo inviare due messaggi al sistema per aggiornare correttamente le informazioni. Ma utilizzando il modo in cui il codice è stato implementato, possiamo tracciare il risultato sia della posizione aperta che del risultato della giornata con un unico messaggio.

Un'altra modifica nell' EA riguarda la funzione OnTrade. Si presenta così:

void OnTrade()
{
        SubWin.DispatchMessage(CHARTEVENT_CHART_CHANGE, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eROOF_DIARY], NanoEA.UpdateRoof());
        NanoEA.UpdatePosition();
}



Sebbene questo sistema funzioni, bisogna fare attenzione con i tempi di esecuzione della funzione OnTrade, la quale insieme alla OnTick può degradare la performance dell'EA. Nel caso del codice contenuto in OnTick, non è molto buono, e l'ottimizzazione è fondamentale. Ma è più facile con OnTrade, la funzione viene effettivamente chiamata quando c'è un cambio di posizione. Sapendo questo, abbiamo due alternative. La prima è modificare la posizione di UpdateRoof per limitarne il tempo di esecuzione. L'altra alternativa è di modificare la funzione OnTrade in se. Per motivi pratici modificheremo la funzione Update Roof e quindi miglioreremo almeno leggermente il tempo di esecuzione quando avremo una posizione aperta. La nuova funzione è la seguente:

double UpdateRoof(void)
{
        ulong           ticket;
        string  szSymbol = Terminal.GetSymbol();
        int             max;
        static int      memMax = 0;
        static double   Accumulated = 0;
                
        HistorySelect(macroGetDate(TimeLocal()), TimeLocal());
        max = HistoryDealsTotal();
        if (memMax == max) return Accumulated; else memMax = max;
        for (int c0 = 0; c0 < max; c0++) if ((ticket = HistoryDealGetTicket(c0)) > 0)
                if (HistoryDealGetString(ticket, DEAL_SYMBOL) == szSymbol)
                        Accumulated += HistoryDealGetDouble(ticket, DEAL_PROFIT);
                                                
        return Accumulated;
}


Le righe evidenziate mostrano il codice aggiunto alla funzione originale. Anche se può sembrare che non facciano molta differenza, fanno molta differenza. Andiamo a vedere perchè. Quando il codice è chiamato per la prima volta,sia la variabile statica memMax che Accumulated saranno impostate a zero se non ci sono valori nella cronologia degli ordini per il periodo specificato. Il test ne terrà conto di questo, e la routine ritornerà, ma se c'è qualche dato questo sarà testato, e sia memMax che Accumulated terranno conto della nuova condizione. Il fatto che queste variabili siano statiche significa che i loro valori vengono mantenuti tra le chiamate. Pertanto, quando il valore di una posizione cambia a seguito del movimento naturale di un asset, MetaTrader 5 genera un evento che chiamerà la funzione OnTrade. A questo punto abbiamo una nuova chiamata della funzione UpdateRoof, e se la posizione non era chiusa, la funzione tornerà al punto di controllo, che accelererà il processo di ritorno.


Conclusioni

In questo articolo, abbiamo visto come aggiungere nuove funzionalità al sistema RAD consentendo così la creazione di una libreria che rende il sistema ideale per creare un'interfaccia IDE con molta più semplicità e meno errori durante la creazione di un'interfaccia di interazione e controllo. D'ora in poi, l'unico vero limite sarà la tua creatività, poiché qui abbiamo considerato solo di lavorare con MQL5, ma puoi integrare la stessa idea in librerie esterne, ampliando così notevolmente le possibilità di creare un IDE.


Tradotto dal portoghese da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/pt/articles/10301

File allegati |
EA_1.05.zip (3274.9 KB)
Ultimi commenti | Vai alla discussione (1)
Caltu Doru Marian
Caltu Doru Marian | 12 dic 2022 a 17:24
Sono solo parole come come meta trader4 non siamo contenti di voi 
Impara come progettare un sistema di trading tramite l'utilizzo di MACD Impara come progettare un sistema di trading tramite l'utilizzo di MACD
In questo articolo, impareremo un nuovo strumento dalla nostra serie: impareremo come progettare un sistema di trading basato su uno degli indicatori tecnici più popolari Moving Average Convergence Divergence (MACD).
Scopri come progettare un sistema di trading con il CCI Scopri come progettare un sistema di trading con il CCI
In questo nuovo articolo della nostra serie dedicata a come progettare sistemi di trading, presenterò il Commodities Channel Index (CCI), spiegherò le sue caratteristiche e condividerò con voi come creare un sistema di trading basato su questo indicatore.
Impara come progettare un sistema di trading con lo Stocastico Impara come progettare un sistema di trading con lo Stocastico
In questo articolo, continuiamo la nostra serie di apprendimento: questa volta impareremo come progettare un sistema di trading utilizzando uno degli indicatori più popolari e utili, che è l'indicatore Oscillatore Stocastico, per aggiungere un nuovo mattone alla nostra conoscenza delle basi.
Indicatori multipli su un grafico (Parte 05): Trasformiamo MetaTrader 5 in un sistema RAD (I) Indicatori multipli su un grafico (Parte 05): Trasformiamo MetaTrader 5 in un sistema RAD (I)
Tante persone non sanno programmare ma sono molto creative e hanno grandi idee. Purtroppo però, la mancanza di conoscenze di programmazione impedisce loro di realizzare queste idee. Vediamo insieme come creare un Chart Trade utilizzando la piattaforma MetaTrader 5 stessa, come se fosse un IDE.