English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
preview
Sviluppare un Expert Advisor per il trading da zero (Parte 19): Nuovo sistema di ordini (II)

Sviluppare un Expert Advisor per il trading da zero (Parte 19): Nuovo sistema di ordini (II)

MetaTrader 5Trading | 4 luglio 2023, 16:44
446 0
Daniel Jose
Daniel Jose

Introduzione

Nel precedente articolo, Sviluppare un Expert Advisor per il trading da zero (Parte 18), abbiamo implementato alcune correzioni, modifiche e aggiustamenti nel sistema degli ordini, con l'obiettivo di creare un sistema che consentisse di fare trading differenti sui conti NETTING e HEDGING, poiché ci sono differenze nell’operatività del conto. Per il tipo NETTING, il sistema genera un prezzo medio e hai una sola posizione aperta per un asset. Sui conti HEDGING, puoi avere più posizioni aperte, ognuna delle quali ha limiti individuali. Puoi acquistare e vendere gli stessi beni contemporaneamente. Questo può essere fatto solo su conti HEDGING. Questa è il fondamento, sulla base della quale si possono comprendere le opzioni di trading.

Ma ora è giunto il momento di rendere finalmente il sistema degli ordini completamente visivo in modo da poter eliminare la finestra di messaggio e analizzare quali valori sono in ogni posizione, senza di essa. Possiamo farlo solo osservando il nuovo sistema di ordini. Questo ci permetterà di regolare diverse cose alla volta. Inoltre, saremo in grado di conoscere facilmente i limiti di profitto e perdita di una posizione OCO o di un ordine OCO pendente, poiché l'EA visualizzerà le relative informazioni in tempo reale, senza richiedere calcoli aggiuntivi.

Sebbene questa sia la prima parte dell'implementazione, non stiamo partendo da zero: modificheremo il sistema esistente aggiungendo più oggetti ed eventi al grafico dell'asset che stiamo tradando.


1.0. Pianificazione

La pianificazione del sistema che stiamo utilizzando qui non è particolarmente difficile: modificheremo il sistema esistente cambiando solo il sistema che rappresenta gli ordini sul grafico. Questa è l'idea principale che sembra abbastanza semplice. Ma in pratica richiede molta creatività, dal momento che manipoleremo e modelleremo i dati in modo che la piattaforma MetaTrader 5 faccia tutto il duro lavoro per noi.

Esistono diversi modi per modellare i dati, ognuno con i suoi pro e contro.

  • Il primo modo è quello di utilizzare una lista. Può essere un ciclo singolo, un ciclo doppio o anche un sistema di hashing. Il vantaggio di utilizzare uno qualsiasi di questi approcci è che il sistema è facilmente implementabile. Tuttavia, lo svantaggio è che ciò impedirà la manipolazione dei dati o limiterà il numero di ordini. Inoltre, in questo caso, dovremmo creare tutta la logica aggiuntiva solo per salvare la lista.
  • Il secondo modo è creare un array di classi, mentre la classe conterrà e manterrà tutti gli oggetti appena creati. In questo caso, l'array funzionerà come una lista, ma dobbiamo scrivere meno codice, perché MQL5 supporta già alcune cose che dovremmo codificare nel caso di utilizzo di una lista. Tuttavia, avremmo altri problemi, come la gestione degli eventi, che in questa situazione sarebbe abbastanza difficile.
  • La terza via è quella che useremo. Forzeremo il codice creato in MQL5 per supportare oggetti dinamici. Sembra qualcosa di irreale, ma se facciamo la giusta modellazione dei dati da utilizzare, allora il linguaggio MQL5 ci consentirà di creare un sistema in cui non ci saranno restrizioni sul numero di oggetti sullo schermo. Inoltre tutti gli oggetti saranno in grado di generare e ricevere eventi. E nonostante la loro individualità, la piattaforma li vedrà tutti collegati come se fossero in una lista o in un indice di array.

Se pensi che questo non sia facile da implementare, dai un'occhiata alla seguente parte di codice della classe C_HLineTrade:

inline void SetLineOrder(ulong ticket, double price, eHLineTrade hl, bool select)
{
        string sz0 = def_NameHLineTrade + (string)hl + (string)ticket, sz1;
                                
        ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0);

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

La parte evidenziata mostra esattamente che possiamo creare tutte le linee orizzontali che vogliamo e riceveranno gli eventi in modo completamente indipendente. Tutto quello che dobbiamo fare è implementare gli eventi in base al nome che ciascuna linea avrà, poiché i nomi saranno univoci. La piattaforma MetaTrader 5 si prenderà cura di tutto il resto. Il risultato sarà simile a questo:


Anche se questo sembra già qualcosa di ideale, questa modellazione non sarà sufficiente per ottenere il risultato di cui abbiamo realmente bisogno. L'idea può essere implementata. Ma la modellazione dei dati attualmente disponibile nell'EA non è l'ideale, perché non possiamo avere un numero illimitato di oggetti basati su un nome. Dobbiamo apportare alcune modifiche che richiedono una modifica del codice abbastanza profonda.

Inizieremo ora a implementare questo nuovo metodo di modellazione dei dati, ma cambieremo soltanto ciò che è strettamente necessario, mantenendo stabile l'intero codice, perché dovrebbe continuare a funzionare il più costantemente possibile. Tutto il lavoro sarà eseguito dalla piattaforma MetaTrader 5, indicheremo solo come la piattaforma dovrebbe comprendere la nostra modellazione.


2.0. Implementazione

La prima modifica è quella di cambiare C_HLineTrade nella nuova classe C_ObjectsTrade. Questa nuova classe sarà in grado di supportare ciò di cui abbiamo bisogno — un modo per collegare un numero illimitato di oggetti.

Iniziamo osservando le definizioni originali nel codice seguente.

class C_ObjectsTrade
{
//+------------------------------------------------------------------+
#define def_NameObjectsTrade 	"SMD_OT"
#define def_SeparatorInfo       '*'
#define def_IndicatorTicket0    1
//+------------------------------------------------------------------+
        protected:
                enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PRICE};
//+------------------------------------------------------------------+

// ... The rest of the class code

Qui abbiamo la base iniziale che implementeremo. Lo espanderemo in futuro, ma per ora voglio che il sistema rimanga stabile nonostante sia in fase di modifica e abbia una nuova modellazione dei dati.

Anche all'interno della dichiarazione 'protected' abbiamo le seguenti funzioni:

inline double GetLimitsTake(void) const { return m_Limits.TakeProfit; }
//+------------------------------------------------------------------+
inline double GetLimitsStop(void) const { return m_Limits.StopLoss; }
//+------------------------------------------------------------------+
inline bool GetLimitsIsBuy(void) const { return m_Limits.IsBuy; }
//+------------------------------------------------------------------+
inline void SetLimits(double take, double stop, bool isbuy)
{
        m_Limits.IsBuy = isbuy;
        m_Limits.TakeProfit = (m_Limits.TakeProfit < 0 ? take : (isbuy ? (m_Limits.TakeProfit > take ? m_Limits.TakeProfit : take) : (take > m_Limits.TakeProfit ? m_Limits.TakeProfit : take)));
        m_Limits.StopLoss = (m_Limits.StopLoss < 0 ? stop : (isbuy ? (m_Limits.StopLoss < stop ? m_Limits.StopLoss : stop) : (stop < m_Limits.StopLoss ? m_Limits.StopLoss : stop)));
}
//+------------------------------------------------------------------+
inline int GetBaseFinanceLeveRange(void) const { return m_BaseFinance.Leverange; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceIsDayTrade(void) const { return m_BaseFinance.IsDayTrade; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceTakeProfit(void) const { return m_BaseFinance.FinanceTake; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceStopLoss(void) const { return m_BaseFinance.FinanceStop; }

Attualmente, queste funzioni servono solo come misura di sicurezza per un altro schema che implementeremo in futuro. Anche se possiamo implementare i dati e l'analisi eseguita in un'altra posizione, è bene lasciare alcune cose il più in basso possibile nella catena di ereditarietà. Anche se i valori restituiti verranno utilizzati solo dalle classi derivate, non voglio consentirlo direttamente: Non voglio che la classe derivata acceda ai valori che si trovano all'interno di questa classe oggetto C_ObjectsTrade, perché ciò spezzerebbe l'idea di incapsulamento della classe oggetto, rendendo difficile future modifiche o correzioni di bug, se la classe derivata cambia il valore della classe base senza apportare le relative modifiche tramite una chiamata di procedura.

Per minimizzare il più possibile la sovrapposizione delle chiamate, tutte le funzioni sono dichiarate inline: questo aumenta leggermente la dimensione dell'eseguibile, ma si traduce in un sistema più sicuro.

Veniamo ora alle dichiarazioni private.

//+------------------------------------------------------------------+
        private :
                string  m_SelectObj;
                struct st00
                {
                        double  TakeProfit,
                                StopLoss;
                        bool    IsBuy;
                }m_Limits;
                struct st01
                {
                        int     FinanceTake,
                                FinanceStop,
                                Leverange;
                        bool    IsDayTrade;
                }m_BaseFinance;
//+------------------------------------------------------------------+
                string MountName(ulong ticket, eIndicatorTrade it)
                {
                        return StringFormat("%s%c%c%c%d", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket);
                }
//+------------------------------------------------------------------+

La parte più importante è il frammento evidenziato, che modellerà i nomi degli oggetti. Sto mantenendo le basi che sono ancora disponibili nel sistema. Questo perché prima creiamo e modifichiamo la modellazione, mantenendo stabile il sistema. Poi aggiungeremo nuovi oggetti, mentre questo sarà fatto abbastanza facilmente e rapidamente. Inoltre, manterremo la stabilità già raggiunta.

Sebbene il codice abbia subito molte più modifiche di quelle mostrate qui, mi concentrerò solo sulle nuove funzioni e sui cambiamenti che sono stati considerevoli rispetto ai codici precedenti.

La prima funzione è mostrata di seguito:

inline string CreateIndicatorTrade(ulong ticket, eIndicatorTrade it, bool select)
{
        string sz0 = MountName(ticket, it);
                                
        ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_COLOR, (it == IT_PRICE ? clrBlue : (it == IT_STOP ? clrFireBrick : clrForestGreen)));
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_WIDTH, 1);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_STYLE, STYLE_DASHDOT);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTABLE, select);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTED, false);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_BACK, true);
        ObjectSetString(Terminal.Get_ID(), sz0, OBJPROP_TOOLTIP, (string)ticket + " "+StringSubstr(EnumToString(it), 3, 10));
                                
        return sz0;
}

Per ora, creerà solo una linea orizzontale. Presta attenzione al codice di generazione del nome; da notare inoltre che i colori saranno ora definiti internamente dal codice e non dall'utente.

Quindi sovraccarichiamo la stessa funzione, come si può vedere di seguito.

inline string CreateIndicatorTrade(ulong ticket, double price, eIndicatorTrade it, bool select)
{
        if (price <= 0)
        {
                RemoveIndicatorTrade(ticket, it);
                return NULL;
        }
        string sz0 = CreateIndicatorTrade(ticket, it, select);
        ObjectMove(Terminal.Get_ID(), sz0, 0, 0, price);
                                
        return sz0;
}

Non confondere queste due funzioni, perché sebbene sembrino uguali, in realtà sono differenti. Il sovraccarico è abbastanza comune: creiamo una semplice funzione e poi vi aggiungiamo nuovi parametri per accumulare un certo tipo di modellazione. Se non lo implementassimo tramite sovraccarico, a volte dovremmo ripetere la stessa sequenza di codice. Questo è pericoloso, perché possiamo dimenticarci di dichiarare qualcosa. Inoltre, non è molto pratico, quindi sovraccarichiamo la funzione per effettuare una chiamata anziché diverse.

Una cosa che dovrebbe essere menzionata qui è la parte evidenziata in questa seconda versione. Non è necessario crearla qui, potremmo farlo in un altro posto. Ma, come può essere visto, quando proviamo a creare qualche oggetto con il prezzo zero, di fatto deve essere distrutto.

Per vedere effettivamente il momento in cui ciò accade, dai un'occhiata al codice qui sotto:

class C_Router : public C_ObjectsTrade
{

// ... Internal class code ....

                void UpdatePosition(int iAdjust = -1)
                        {

// ... Internal function code ...

                                for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol())
                                {
                                        ul = PositionGetInteger(POSITION_TICKET);
                                        m_bContainsPosition = true;
                                        CreateIndicatorTrade(ul, PositionGetDouble(POSITION_PRICE_OPEN), IT_PRICE, false);
                                        CreateIndicatorTrade(ul, take = PositionGetDouble(POSITION_TP), IT_TAKE, true);
                                        CreateIndicatorTrade(ul, stop = PositionGetDouble(POSITION_SL), IT_STOP, true);

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

Ogni volta che l'EA riceve l'evento OnTrade, eseguirà la funzione di cui sopra e proverà a creare un indicatore sui punti selezionati, ma se l'utente rimuove il limite, diventerà zero. Pertanto, quando chiamato, cancellerà effettivamente l'indicatore dal grafico, salvandoci da inutili oggetti in memoria. Quindi abbiamo un guadagno in alcuni punti, poiché il controllo sarà fatto proprio al momento della creazione.

Ma abbiamo ancora un problema con il sovraccarico, perché alcune persone potrebbero non comprendere appieno com’è utilizzato nel codice reale. Per capirlo, dai un'occhiata alle due parti di codice qui sotto:

class C_OrderView : public C_Router
{
        private  :
//+------------------------------------------------------------------+
        public   :
//+------------------------------------------------------------------+
                void InitBaseFinance(int nContracts, int FinanceTake, int FinanceStop, bool b1)
                        {                       
                                SetBaseFinance(nContracts, FinanceTake, FinanceStop, b1);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_PRICE, false);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE, false);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP, false);
                        }
//+------------------------------------------------------------------+

// ... Rest of the code...
class C_Router : public C_ObjectsTrade
{

// ... Class code ...

                void UpdatePosition(int iAdjust = -1)
                        {
// ... Function code ....
                                for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol())
                                {
                                        ul = PositionGetInteger(POSITION_TICKET);
                                        m_bContainsPosition = true;
                                        CreateIndicatorTrade(ul, PositionGetDouble(POSITION_PRICE_OPEN), IT_PRICE, false);

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

Notare che in entrambi i casi abbiamo lo stesso nome della funzione utilizzata. Inoltre, fanno entrambi parte della stessa classe C_ObjectsTrade. Tuttavia, anche in questo caso il compilatore può distinguerli, a causa del numero di parametri. Se guardi da vicino, vedrai che l'unica differenza è un parametro addizionale "price", ma potrebbero essercene anche altri. Come puoi vedere, è molto più semplice utilizzare una chiamata per copiare tutto il codice presente in una delle versioni sovraccaricate, così alla fine abbiamo un codice più pulito e più facile da mantenere.

Ora torniamo alla classe C_ObjectsTrade. La prossima funzione che dobbiamo capire è simile a questa:

bool GetInfosOrder(const string &sparam, ulong &ticket, double &price, eIndicatorTrade &it)
{
        string szRet[];
        char szInfo[];
                                
        if (StringSplit(sparam, def_SeparatorInfo, szRet) < 2) return false;
        if (szRet[0] != def_NameObjectsTrade) return false;
        StringToCharArray(szRet[1], szInfo);
        it = (eIndicatorTrade)szInfo[0];
        ticket = (ulong) StringToInteger(szRet[2]);
        price = ObjectGetDouble(Terminal.Get_ID(), sparam, OBJPROP_PRICE);
                                
        return true;
}

In effetti, è il cuore, la mente e il corpo dell'intero nuovo sistema. Sebbene sembri abbastanza semplice, svolge un lavoro essenziale affinché l'intero EA funzioni come richiesto dal nostro nuovo sistema di modellazione.

Presta molta attenzione al codice evidenziato, in particolare alla funzione StringSplit . Se non esistesse in MQL5, dovremmo codificarla. Fortunatamente, MQL5 ce l'ha, quindi useremo questa funzione al massimo. Quello che fa è scomporre il nome dell'oggetto nei dati richiesti. Quando viene creato il nome di un oggetto, viene modellato in un modo molto specifico e per questo motivo possiamo annullare questo modello di codifica in modo che StringSplit annulli ciò che fa la funzione StringFormat .

Il resto della funzione cattura i dati presenti nel nome dell'oggetto in modo da poterlo testare e usarlo in seguito. Cioè, MetaTrader 5 genera i dati per noi, li decomponiamo in modo da sapere cosa è successo e poi diciamo a MetaTrader 5 quali passaggi dovrebbe compiere. Il nostro scopo è quello di far lavorare MetaTrader 5 per noi. Non creo un modello da zero; invece, sto modellando l'interfaccia e l'EA da zero. Pertanto, dovremmo beneficiare del supporto offerto da MetaTrader 5 invece di cercare una soluzione esterna.

Nel codice qui sotto faremo qualcosa di molto simile a quello che abbiamo fatto sopra:

inline void RemoveAllsIndicatorTrade(bool bFull)
{
        string sz0, szRet[];
        int i0 = StringLen(def_NameObjectsTrade);
                                
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
        for (int c0 = ObjectsTotal(Terminal.Get_ID(), -1, -1); c0 >= 0; c0--)
        {
                sz0 = ObjectName(Terminal.Get_ID(), c0, -1, -1);
                if (StringSubstr(sz0, 0, i0) == def_NameObjectsTrade)
                {
                        if (!bFull)
                        {
                                StringSplit(sz0, def_SeparatorInfo, szRet);
                                if (StringToInteger(szRet[2]) == def_IndicatorTicket0) continue;
                        }
                }else continue;                                         
                ObjectDelete(Terminal.Get_ID(), sz0);
        }
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
}

Ogni volta che rimuoviamo una linea dal grafico, sia che si tratti di una posizione che verrà chiusa o di un livello limite che sarà rimosso, l'oggetto corrispondente deve essere rimosso, proprio come quando l'EA viene rimosso dal grafico. Dobbiamo cancellare gli oggetti, ma abbiamo anche un insieme di righe che non vanno cancellate se non assolutamente necessario: questa è Ticket0, non va cancellata se non strettamente necessario. Per evitare la cancellazione, utilizziamo il codice evidenziato. Senza questo, avremmo bisogno di creare nuovamente questo Ticket0 ogni volta, perché questo ticket è molto importante in un'altra parte di codice che discuteremo più avanti.

In tutte le altre volte abbiamo bisogno di eliminare qualcosa di specifico. Per questo, utilizzeremo un'altra funzione di rimozione mostrata di seguito.

inline void RemoveIndicatorTrade(ulong ticket, eIndicatorTrade it = IT_NULL)
{
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
        if ((it != NULL) && (it != IT_PRICE))
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, it));
        else
        {
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_PRICE));
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_TAKE));
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_STOP));
        }
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
}

La prossima nuova routine può essere vista di seguito:

inline void PositionAxlePrice(double price, ulong ticket, eIndicatorTrade it, int FinanceTake, int FinanceStop, int Leverange, bool isBuy)
{
        double ad = Terminal.GetAdjustToTrade() / (Leverange * Terminal.GetVolumeMinimal());
        ObjectMove(Terminal.Get_ID(), MountName(ticket, it), 0, 0, price);
        if (it == IT_PRICE)
        {
                ObjectMove(Terminal.Get_ID(), MountName(ticket, IT_TAKE), 0, 0, price + Terminal.AdjustPrice(FinanceTake * (isBuy ? ad : (-ad))));
                ObjectMove(Terminal.Get_ID(), MountName(ticket, IT_STOP), 0, 0, price + Terminal.AdjustPrice(FinanceStop * (isBuy ? (-ad) : ad)));
        }
}

Metterà gli oggetti sull'asse dei prezzi. Ma non affezionarti troppo, poiché presto cesserà di esistere per vari motivi. Tra questi c'è quello di cui abbiamo discusso in un altro articolo di questa serie: Indicatori multipli su un grafico (Parte 05): Conversione di MetaTrader 5 in un sistema RAD(I).. Questo articolo ha una tabella che mostra gli oggetti che possono utilizzare le coordinate cartesiane per il posizionamento, e queste coordinate sono X e Y. Le coordinate di prezzo e tempo, pur essendo utili in alcuni casi, non sono sempre convenienti: quando vogliamo posizionare elementi che devono essere posizionati in determinati punti dello schermo, anche se sarà più veloce sviluppare le cose usando le coordinate di prezzo e tempo, sono molto più difficili da lavorare rispetto al sistema X e Y.

Faremo delle modifiche la prossima volta, mentre ora il nostro scopo è quello di creare un sistema alternativo a quello utilizzato finora.

Successivamente, abbiamo l'ultima importante funzione nella classe C_ObjectsTrade. È mostrata nel seguente codice:

inline double GetDisplacement(const bool IsBuy, const double Vol, eIndicatorTrade it) const
{
        int i0 = (it == IT_TAKE ? m_BaseFinance.FinanceTake : m_BaseFinance.FinanceStop),
            i1 = (it == IT_TAKE ? (IsBuy ? 1 : -1) : (IsBuy ? -1 : 1));
        return (Terminal.AdjustPrice(i0 * (Vol / m_BaseFinance.Leverange) * Terminal.GetAdjustToTrade() / Vol) * i1);
}

Questa funzione effettuerà la conversione tra i valori specificati nel Chart Trader per un ordine pendente da piazzare o una posizione che verrà aperta a mercato.

Tutte queste modifiche sono state implementate per trasformare la funzione C_HLineTrade in C_ObjectsTrade. Tuttavia, queste modifiche ne hanno richieste alcune altre. Ad esempio, la classe che è cambiata notevolmente è C_ViewOrder. Alcune parti di questa classe hanno semplicemente cessato di esistere, perché non ha senso la loro esistenza, mentre le restanti funzioni sono state modificate. Di seguito sono evidenziate le funzioni che meritano particolare attenzione.

La prima è la funzione per inizializzare i dati provenienti dal Chart Trader.

void InitBaseFinance(int nContracts, int FinanceTake, int FinanceStop, bool b1)
{                       
        SetBaseFinance(nContracts, FinanceTake, FinanceStop, b1);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_PRICE, false);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE, false);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP, false);
}

Le parti evidenziate sono dove viene effettivamente creato Ticket0. Questo ticket viene utilizzato per piazzare un ordine pendente utilizzando il mouse e la tastiera: (SHIFT) per acquistare, (CTRL) per vendere. In precedenza, venivano create le linee in questo punto, che erano poi utilizzate per indicare dove si sarebbe posizionato l'ordine. Ora le cose sono molto più semplici: così come vediamo un ordine da piazzare, vedremo anche un ordine pendente o una posizione aperta. Significa che controlleremo sempre il sistema. È come se dovessi assemblare un veicolo e per tutto il tempo controllassi i suoi freni in modo che quando li devi effettivamente usare sapresti come si comporterebbe.

Il grosso problema con un codice lungo è che quando creiamo una funzione, possiamo solo sapere che sta funzionando nel momento in cui viene effettivamente utilizzata. Ma ora il sistema è sempre controllato — anche se non utilizziamo tutte le funzioni, vengono costantemente controllate a causa del riutilizzo del codice in posti differenti.

L'ultima routine che menzionerò in questo articolo è mostrata di seguito. Emetterà un ordine pendente. Si noti che è diventato estremamente compatto rispetto alla stessa funzione degli articoli precedenti.

inline void MoveTo(uint Key)
{
        static double local = 0;
        datetime dt;
        bool    bEClick, bKeyBuy, bKeySell, bCheck;
        double  take = 0, stop = 0, price;
                                
        bEClick  = (Key & 0x01) == 0x01;    //Let mouse button click
        bKeyBuy  = (Key & 0x04) == 0x04;    //Pressed SHIFT
        bKeySell = (Key & 0x08) == 0x08;    //Pressed CTRL  
        Mouse.GetPositionDP(dt, price);
        if (bKeyBuy != bKeySell)
        {
                Mouse.Hide();
                bCheck = CheckLimits(price);
        } else Mouse.Show();
        PositionAxlePrice((bKeyBuy != bKeySell ? price : 0), def_IndicatorTicket0, IT_PRICE, (bCheck ? 0 : GetBaseFinanceTakeProfit()), (bCheck ? 0 : GetBaseFinanceStopLoss()), GetBaseFinanceLeveRange(), bKeyBuy);
        if((bEClick) && (bKeyBuy != bKeySell) && (local == 0)) CreateOrderPendent(bKeyBuy, local = price);
        local = (local != price ? 0 : local);
}

Il motivo è che ora ci sarà una nuova regola nel sistema, quindi la funzione ha "perso un po' di peso" ed è diventata più compatta.


Conclusioni

Ho presentato qui alcune modifiche che verranno utilizzate nel prossimo articolo. Lo scopo di tutto questo è renderli più semplici e mostrare cose che possono essere differenti in momenti diversi. La mia idea è che tutti seguano e imparino a programmare un EA che sarà utilizzato per aiutarti nelle operazioni, motivo per cui non presento solo un sistema finito e pronto all'uso. Voglio mostrare che ci sono problemi da risolvere e presentare il percorso che ho intrapreso per risolvere le questioni e i problemi che sorgono durante lo sviluppo. Spero tu capisca questo punto. Perché se l'idea fosse quella di creare un sistema e presentarlo in una forma già pronta, farei meglio a farlo e vendere l'idea, ma non è questa la mia intenzione...

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

File allegati |
EA.zip (12023.87 KB)
Impara come progettare un sistema di trading tramite Force Index Impara come progettare un sistema di trading tramite Force Index
Benvenuti nel nostro nuovo articolo della nostra serie su come progettare un sistema di trading tramite gli indicatori tecnici più popolari. In questo articolo, impareremo a conoscere un nuovo indicatore tecnico e come creare un sistema di trading utilizzando l'indicatore Force Index.
Sviluppare un Expert Advisor per il trading da zero (Parte 18): Nuovo sistema di ordini (I) Sviluppare un Expert Advisor per il trading da zero (Parte 18): Nuovo sistema di ordini (I)
Questa è la prima parte del nuovo sistema di ordini. Da quando abbiamo iniziato a documentare questo EA nei nostri articoli, ha subito varie modifiche e miglioramenti mantenendo lo stesso modello di sistema degli ordini sul grafico.
Impara come progettare un sistema di trading tramite Bear’s Power Impara come progettare un sistema di trading tramite Bear’s Power
Benvenuti in un nuovo articolo della nostra serie sull'imparare a progettare un sistema di trading tramite gli indicatori tecnici più popolari, ecco un nuovo articolo su come progettare un sistema di trading tramite l'indicatore tecnico Bear's Power.
Algoritmi di ottimizzazione della popolazione: Sciame di particelle (PSO) Algoritmi di ottimizzazione della popolazione: Sciame di particelle (PSO)
In questo articolo, prenderò in considerazione il famoso algoritmo Particle Swarm Optimization (PSO). In precedenza, abbiamo discusso caratteristiche così importanti degli algoritmi di ottimizzazione come convergenza, tasso di convergenza, stabilità, scalabilità, nonché sviluppato un banco di prova e considerato il più semplice algoritmo RNG..