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

Sviluppare un Expert Advisor per il trading da zero (Parte 28): Verso il futuro (III)

MetaTrader 5Esempi | 28 febbraio 2024, 11:33
331 0
Daniel Jose
Daniel Jose

Introduzione

Quando ho iniziato a sviluppare il sistema degli ordini, dopo l'articolo Sviluppare un Expert Advisor per il trading da zero (Parte 18), non avevo idea di quanto tempo ci sarebbe voluto per arrivare a questo punto. Abbiamo attraversato vari momenti, cambiamenti, emendamenti, ecc. Ti ho mostrato come fare alcune cose specifiche, come contrassegnare le cose o rendere il sistema più intuitivo. Ma c'erano anche cose che non potevo mostrare in quella fase perché il percorso non era del tutto preparato. Questo viaggio ci ha permesso di costruire il concetto in modo tale che tutti possano comprendere l'idea e sapere come funziona il sistema.

In tutti gli articoli precedenti, ho preparato il terreno per arrivare a questo articolo con lo stesso livello di comprensione di come funziona il sistema. Quindi, spero che questo materiale non sia estremamente confuso o complesso. C'era una domanda fin dall'inizio e ho evitato di analizzarla in dettaglio. Ma è molto importante per i trader più esperti. A prima vista, questo può sembrare sciocco, ma quando arriverà il momento di fare trading, capiremo che ci manca qualcosa nell'EA. Allora ci chiederemo “cosa manca qui?”. Sto parlando di un modo per ripristinare i valori Take Profit e Stop Loss che sono stati cancellati per qualche motivo e che vogliamo ripristinare sul grafico.

Se hai mai provato a farlo, allora puoi capire che si tratta di un compito piuttosto difficile e lento, poiché è necessario "seguire un determinato scenario" in modo che tutto funzioni bene, altrimenti finiremo per commettere errori tutto il tempo.

MetaTrader 5 fornisce un sistema di ticket che consente di creare e correggere i valori degli ordini. L’idea è quella di avere un Expert Advisor che renda lo stesso sistema di ticket più veloce ed efficiente. Il sistema MetaTrader 5 non è perfetto; a volte può essere più lento e più soggetto a errori rispetto all'utilizzo dell'EA che stiamo sviluppando.

Ma fino ad ora non ho mai spiegato come generare i valori per i livelli di TP e SL (Take Profit e Stop Loss). Penso che la cancellazione dei livelli di stop sia abbastanza chiara e intuitiva. Ma come implementarlo, cioè come procedere per impostare gli stop o per ripristinarli direttamente sul grafico? Questo è qualcosa di molto intrigante, che pone alcune domande, e, naturalmente, questo è il motivo per creare questo articolo: mostrare uno dei tanti modi per creare i livelli di stop direttamente sul grafico, senza ricorrere a nessuna risorsa esterna, semplicemente utilizzando il sistema degli ordini dell'EA.


2.0. Operazioni preliminari: Implementazione

Innanzitutto dobbiamo costringere l'EA a smettere di controllare, cosa che ha fatto per molto tempo sin dai primi giorni del suo sviluppo. Per rimuovere questo controllo è necessario rimuovere tutto il codice cancellato e aggiungere il codice evidenziato:

inline double SecureChannelPosition(void)
{
        double Res = 0, sl, profit, bid, ask;
        ulong ticket;
                                
        bid = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_BID);
        ask = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_ASK);
        for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                IndicatorAdd(ticket = PositionGetInteger(POSITION_TICKET));
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), Res += PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), profit = PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                sl = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                        if (ask < sl) ClosePosition(ticket);
                }else
                {
                        if ((bid > sl) && (sl > 0)) ClosePosition(ticket);
                }
                Res += profit;
        }
        return Res;
};

Il codice cancellato non cesserà di esistere ma ritornerà in un altro momento. Tuttavia in questo momento è più un ostacolo che un vantaggio. Una volta fatto ciò, possiamo iniziare a pensare a come implementare il sistema di TP e SL direttamente sul grafico, senza l'aiuto di alcuna risorsa diversa dall'EA.

Ogni sviluppatore avrà la propria idea per risolvere questo problema: alcune saranno più facili da comprendere per un trader, mentre altre saranno più difficili; alcune saranno più difficili da mettere in pratica, mentre altre saranno più facili. Non sto dicendo che il modo che utilizzerò e mostrerò qui sia il più appropriato o il più semplice, ma è di gran lunga il più adatto al mio modo di lavorare e di utilizzare la piattaforma. Inoltre, non avrò bisogno di creare nuovi elementi. Correggeremo solo alcune cose nel codice.


2.0.1. Modellazione del sistema di trascinamento

Lo stesso codice EA nell'attuale fase di sviluppo fornisce alcuni suggerimenti su come dovremmo modellare il sistema che creeremo. Guarda il seguente codice:

#define macroUpdate(A, B) if (B > 0) {                                                                  \
                if (b0 = (macroGetLinePrice(ticket, A) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, B);                                                        \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                \
                                     } else RemoveIndicator(ticket, A);
                                                                        
                void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
                        {
                                double pr;
                                bool b0 = false;
                                
                                if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
                                {
                                        pr = macroGetLinePrice(ticket, IT_RESULT);
                                        if ((pr == 0) && (macroGetLinePrice(ticket, IT_PENDING) == 0))
                                        {
                                                CreateIndicator(ticket, IT_PENDING);
                                                PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                                                ChartRedraw();
                                        }
                                        pr = (pr > 0 ? pr : macroGetLinePrice(ticket, IT_PENDING));
                                        SetTextValue(ticket, IT_PENDING, vol);
                                }
                                if (m_Selection.tp > 0) macroUpdate(IT_TAKE, tp);
                                if (m_Selection.sl > 0) macroUpdate(IT_STOP, sl);
                                if (b0) ChartRedraw();
                        }
#undef macroUpdate

Le righe evidenziate contengono una macro che eseguirà il compito. Dobbiamo modificarlo in modo che fornisca l'aiuto necessario per implementare ciò di cui abbiamo bisogno, vale a dire l'indicatore del livello di stop. Dai un'occhiata più da vicino al codice macro. È mostrata di seguito:

#define macroUpdate(A, B){ if (B > 0) {                                                                 \
                if (b0 = (macroGetLinePrice(ticket, A) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, B);                                                        \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                \
                                        } else RemoveIndicator(ticket, A); }

Facciamo quanto segue: Quando il valore B, che può essere il Take Profit o lo Stop Loss, è maggiore di 0, controlla se l'indicatore è sul grafico. In caso contrario, lo crea, lo posiziona e imposta il valore che verrà visualizzato. Se il valore B è uguale a 0, rimuoveremo completamente l'indicatore dal grafico e lo faremo nel punto evidenziato nel codice macro. Ma sarebbe sufficiente se, invece di rimuovere completamente l'indicatore dal grafico, manteniamo il suo elemento e se questo elemento può essere configurato per visualizzare ciò che vogliamo fare (ovvero creare lo/gli stop mancante/i per un ordine o una posizione), l'elemento tornerà ad un ordine o ad una posizione OCO? Sì, sarebbe abbastanza e questa è l'idea: lasciare un elemento, in questo caso un oggetto che serve per spostare i livelli di stop e creare il livello di stop che ci manca. Dobbiamo solo trascinare questo elemento e il limite verrà creato. Questo è il quadro teorico che useremo per costruire il sistema.

Ma semplicemente facendo questo non possiamo ottenere tutto ciò di cui abbiamo bisogno. È necessaria un'altra modifica. Lo faremo prima di continuare. Questa modifica è evidenziata nel codice seguente:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{

//... Internal code...

        m_Selection.tp = (valueTp == 0 ? 0 : m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp)));
        m_Selection.sl = (valueSl == 0 ? 0 : m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl));

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

}

Controlliamo se i valori iniziali inseriti da Chart Trade sono nulli o meno. In tal caso, non verrà creato alcun indicatore e il grafico mostrerà solo il punto di ingresso.

Ciò fornisce un lavoro più lineare nel resto del sistema. Se non viene specificato alcun livello di stop, l'intero modello di ordine avrà lo stesso comportamento di quando il valore viene specificato fin dall'inizio, quando inseriremo un ordine pendente sul grafico.

Pertanto, non un singolo trader si chiederà: quali sono queste cifre allegate all'ordine o alla posizione? Perché il trader saprà che rappresentano gli elementi che possono essere spostati sul grafico.

Ma nonostante tutto questo, abbiamo ancora un piccolo problema, per il quale modificheremo due macro. Vedi di seguito:

#define macroSetLinePrice(ticket, it, price)    ObjectSetDouble(Terminal.Get_ID(), macroMountName(ticket, it, EV_LINE), OBJPROP_PRICE, price)
#define macroGetLinePrice(ticket, it)           ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, it, EV_LINE), OBJPROP_PRICE)
#define macroGetPrice(ticket, it, ev)           ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, it, ev), OBJPROP_PRICE)

Questa modifica è molto importante per il resto del sistema che costruiremo. Ma perché sto eliminando le linee tagliate? Il motivo è che dobbiamo rendere il sistema ancora più flessibile e per poter raggiungere questo obiettivo, il codice barrato è stato rimosso e la linea evidenziata è comparsa al suo posto. Perché abbiamo bisogno di macroGetPrice se non disponiamo di una macro per assegnare un valore al prezzo? In effetti, c'è solo un punto che effettua effettivamente questo aggiustamento del prezzo, scrivendolo sull'oggetto grafico. Questo punto può essere visto nel codice seguente:

#define macroSetPrice(ticket, it, ev, price) ObjectSetDouble(Terminal.Get_ID(), macroMountName(ticket, it, ev), OBJPROP_PRICE, price)
//---

// ... Additional code inside the class....

//---
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                if (it != IT_RESULT) macroSetPrice(ticket, it, EV_MOVE, price);
                                macroSetPrice(ticket, it, EV_LINE, price);
                                macroSetAxleY(it);
                                switch (it)
                                {
                                        case IT_TAKE: desl = 160; break;
                                        case IT_STOP: desl = 270; break;
                                        default: desl = 0;
                                }
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2));
                        }

Per questo motivo non è necessario che la macro modifichi il prezzo degli oggetti affinché siano visibili in tutti gli altri punti del codice. In realtà, non deve nemmeno essere una macro in questo momento, ma lo lascio così com'è per ridurre la possibilità di un errore in seguito quando questo codice cambia.

Ora che il nostro sistema è aggiornato, possiamo passare all'argomento successivo e far funzionare tutto come pianificato.


2.0.2. Nuova funzionalità di aggiornamento

Ne ho già parlato nell'argomento precedente: non dobbiamo fare altro che configurare la funzione Update mentre l'EA risolverà tutti i problemi relativi. Poiché l'attenzione principale è rivolta solo agli indicatori Take Profit e Stop Loss e questi vengono eseguiti all'interno della macro, dobbiamo solo impostare correttamente la macro.

Ma c’è una questione che deve ancora essere risolta. Dobbiamo creare il pulsante di spostamento indipendentemente dal resto dell'indicatore. Per fare ciò isoliamo il codice di creazione del pulsante:

// ... class code ...

#define def_ColorLineTake       clrDarkGreen
#define def_ColorLineStop       clrMaroon

// ... class code ....

inline void CreateBtnMoveIndicator(ulong ticket, eIndicatorTrade it, color C = clrNONE)
                        {
                                string sz0 = macroMountName(ticket, it, EV_MOVE);

                                ObjectDelete(Terminal.Get_ID(), macroMountName(ticket, it, EV_MOVE));
                                m_BtnMove.Create(ticket, sz0, "Wingdings", "u", 17, (C == clrNONE ? (it == IT_TAKE ? def_ColorLineTake : def_ColorLineStop) : C));
                                m_BtnMove.Size(sz0, 21, 23);
                        }

// ... the rest of the class code ...

Questo codice creerà solo il pulsante di spostamento e nient'altro. È molto semplice e diretto. Ho anche pensato di lasciare questo codice come macro, ma ho deciso di renderlo una funzione. È dichiarata come funzione integrata, quindi il compilatore la tratterà nello stesso modo in cui tratterebbe una macro. Per semplificarci la vita, la stessa parte di codice ha due nuove definizioni, perché ad un certo punto creeremo solo un pulsante di spostamento e vogliamo che abbia gli stessi colori utilizzati in tutto il sistema. Non è auspicabile che il sistema si comporti o appaia diversamente in casi simili. Per ridurre i problemi, lasciamo i colori come mostrato sopra.

Adesso possiamo passare alla funzione Update; il suo codice completo è mostrato sopra. Tieni presente che l'unica differenza tra la versione seguente e quella presentata in precedenza nell'articolo è il codice evidenziato. Questo codice è una macro utilizzata dalla stessa funzione Update.

#define macroUpdate(A, B){                                                                                                      \
                if (B == 0) {   if (macroGetPrice(ticket, A, EV_LINE) > 0) RemoveIndicator(ticket, A);                          \
                                if (macroGetPrice(ticket, A, EV_MOVE) == 0) CreateBtnMoveIndicator(ticket, A);                  \
                            } else if (b0 = (macroGetPrice(ticket, A, EV_LINE) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, (B == 0 ? pr : B));                                                                \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                                        \
                        }
                                                                        
                void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
                        {
                                double pr;
                                bool b0 = false;
                                
                                if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
                                {
                                        pr = macroGetPrice(ticket, IT_RESULT, EV_LINE);
                                        if ((pr == 0) && (macroGetPrice(ticket, IT_PENDING, EV_MOVE) == 0))
                                        {
                                                CreateIndicator(ticket, IT_PENDING);
                                                PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                                                ChartRedraw();
                                        }
                                        pr = (pr > 0 ? pr : macroGetPrice(ticket, IT_PENDING, EV_MOVE));
                                        SetTextValue(ticket, IT_PENDING, vol);
                                }
                                macroUpdate(IT_TAKE, tp);
                                macroUpdate(IT_STOP, sl);
                                if (b0) ChartRedraw();
                        }
#undef macroUpdate

Diamo un'occhiata a questa macro in modo più dettagliato per capire cosa sta realmente accadendo. Comprenderlo è necessario per poter risolvere alcuni dei problemi che sorgono ancora durante l'utilizzo del sistema. Non è ancora pronto e dobbiamo apportare ulteriori modifiche.

Nella prima fase, abbiamo il seguente comportamento: quando rimuoviamo uno degli ordini stop (Take Profit o Stop Loss), rimuoveremo immediatamente l'indicatore pertinente dal grafico del simbolo. Per fare ciò controlliamo se esiste una linea che è uno dei punti che indica la presenza dell'indicatore. Quindi controlliamo se esiste un oggetto di movimento di questo indicatore. Se non esiste, lo creiamo. Quindi non avremo l'indicatore sul grafico, ma ne avremo un residuo ancora presente sul grafico, che è l'oggetto del movimento.

Il secondo passaggio avviene in caso di creazione di un livello di stop: l'ordine sul server avrà un livello di stop che dovrebbe comparire sul grafico. In questo caso verrà rimosso l'oggetto che rappresentava il movimento e verrà creato e posizionato nell'apposito posto un indicatore completo, che indicherà dove sarà l'attuale livello di stop (Take Profit o Stop Loss).

Nell'ultimo passaggio posizioniamo l'indicatore nel punto corretto. Un dettaglio interessante: se l'indicatore del livello di stop è solo un oggetto che rappresenta la possibilità di movimento, il punto in cui verrà posizionato sarà esattamente il prezzo dell'ordine o della posizione. In altre parole, l'oggetto movimento che permette di creare Take Profit e Stop Loss sarà legato alla linea del prezzo dell'ordine o della posizione a cui appartiene. Così, sarà facile notare se un ordine o una posizione ha uno dei livelli di stop mancanti.

In pratica quello che dobbiamo fare è questo: quando clicchiamo sull'oggetto che indica il movimento, viene creato, come al solito, un fantasma e contemporaneamente viene creata anche una rappresentazione completa dell'indicatore. Questo viene fatto senza aggiungere o modificare alcun codice. D'ora in poi possiamo spostare e regolare i livelli di stop nel solito modo, proprio come prima. Ma finché non clicchiamo su un certo punto, l'ordine di stop non esisterà. Ciò è chiaramente mostrato nel video dimostrativo alla fine dell'articolo, dove mostro come funziona il sistema su un conto reale.

Anche se tutto sembra andare bene, qui abbiamo alcune incongruenze che ci costringono a creare o meglio modificare il codice in alcuni punti. Lo vedremo nel prossimo argomento poiché questo passaggio è ormai terminato.


2.0.3. Risolvere l'inconveniente degli indicatori mobili

Il primo degli inconvenienti si presenta quando siamo in modalità indicatore mobile: è sul grafico ma non sul server. Per ulteriori informazioni, vedi gli articoli Sviluppare un Expert Advisor per il trading da zero (Parte 26) e (Parte 27), in cui mostro come funziona l'indicatore mobile e come è stato implementato. Questi indicatori sono utili e quindi non verranno rimossi dall'EA. Ma non si adattano al sistema che abbiamo visto sopra poiché il loro funzionamento è diverso dagli indicatori che effettivamente rappresentano ordini e posizioni esistenti sul server di trading. Per risolvere i problemi che compaiono quando si utilizza un indicatore mobile, dovremo accedere alla funzione DispatchMessage e modificare le cose lì, come mostrato di seguito.

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;
        double  price;

// ... Internal code...
                        
        switch (id)
        {

// ... Internal code...

                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev)
                        {
                                case EV_TYPE:
                                        if (ticket == def_IndicatorFloat)
                                        {
                                                macroGetDataIndicatorFloat;
                                                m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1)));
                                                m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1)));
                                                m_Selection.ticket = 0;
                                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                        } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam));
                                        break;
                                case EV_DS:
                                        if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam));
                                        break;
                                case EV_CLOSE:
                                        if (ticket == def_IndicatorFloat)
                                        {
                                                macroGetDataIndicatorFloat;
                                                RemoveIndicator(def_IndicatorFloat, it);
                                                if (it != IT_PENDING) UpdateIndicators(def_IndicatorFloat, (it == IT_TAKE ? 0 : m_Selection.tp), (it == IT_STOP ? 0 : m_Selection.sl), m_Selection.vol, m_Selection.bIsBuy);
                                        }else if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it)

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

Apportando le modifiche evidenziate sopra eliminiamo praticamente qualsiasi altro problema relativo all'impostazione degli indicatori mobili, poiché ora abbiamo lo stesso modo per regolare i dati degli indicatori mobili e quello che rappresenta i dati esistenti sul server di trading. Ma questo non risolve completamente i nostri problemi. Dobbiamo risolvere un altro inconveniente che persisteva da molto tempo. Per parlare di questo passiamo al prossimo argomento, perché merita un discorso a parte.


2.0.4. Lo svantaggio del valore Take Profit negativo

L'ultimo inconveniente di cui parliamo in questo articolo è che il valore del Take Profit può spesso essere configurato come negativo, e questo sta accadendo da molto tempo. Ma questo non ha senso per il sistema di trading: se provi a inviare il valore al server, verrà restituito un messaggio di errore. Pertanto, dobbiamo correggere questo problema e risolverne anche un altro, ovvero che un ordine pendente può avere un valore di stop modificato in positivo.

L'EA lo consente ora e, quel che è peggio, il sistema degli ordini indica che questo valore è sul server, mentre di fatto il server restituisce un errore e l'EA semplicemente lo ignora. Il problema è più complicato nel caso degli ordini pendenti, perché nel caso delle posizioni il comportamento dovrebbe essere diverso e questo bug non è stato ancora risolto. Non appena avremo la possibilità di definire i livelli di stop direttamente sul grafico, questo inconveniente dovrebbe scomparire.

È opportuno qui precisare che in caso di una posizione aperta possiamo avere uno stop loss con valore positivo, e questo indica che se scatta lo stop loss, avremo il relativo valore accreditato sul nostro conto. Ma per un ordine pendente si tratterà di un errore che impedirà al server di creare un ordine corretto. Per risolvere questo problema dobbiamo controllare il valore del Take Profit: quando diventa uguale o inferiore a 0, allora dobbiamo evitare che si trasformi in un valore più piccolo. Inoltre, per gli ordini pendenti, non dovremmo consentire uno Stop Loss maggiore di 0. Infatti, forziamo l'EA a utilizzare un valore minimo consentito quando viene soddisfatta la condizione 0. In questo caso, l'ordine o la posizione avrà un senso per il sistema di trading, ma non ha senso aprire una posizione con uno stop loss o un take profit uguale al prezzo di apertura.

Per rendere tutto ciò il più semplice possibile, dobbiamo creare una variabile nel sistema, che può essere vista di seguito:

struct st00
{
        eIndicatorTrade it;
        bool            bIsBuy,
                        bIsDayTrade;
        ulong           ticket;
        double          vol,
                        pr,
                        tp,
                        sl,
                        MousePrice;
}m_Selection;

Perché non modifichiamo semplicemente il prezzo sulla linea del mouse? Il motivo è che per manipolare correttamente il mouse è necessario utilizzare una chiamata di sistema, cioè bisognerebbe manipolare i valori di posizione del mouse tramite le API di WINDOWS, e questo ci costringerebbe ad abilitare l'uso di dll esterne, e non voglio farlo. In questo modo è più semplice assemblare un valore locale all'interno dell'EA e i dati evidenziati memorizzeranno questo valore per noi.

Questo valore verrà utilizzato in tre differenti posti. Il primo posto è nella funzione di movimento. Il codice seguente mostra esattamente dove sta accadendo:

void MoveSelection(double price)
{
        if (m_Selection.ticket == 0) return;
        switch (m_Selection.it)
        {
                case IT_TAKE:
                        UpdateIndicators(m_Selection.ticket, price, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                        break;
                case IT_STOP:
                        UpdateIndicators(m_Selection.ticket, m_Selection.tp, price, m_Selection.vol, m_Selection.bIsBuy);
                        break;
                case IT_PENDING:
                        PositionAxlePrice(m_Selection.ticket, IT_PENDING, price);
                        UpdateIndicators(m_Selection.ticket, (m_Selection.tp == 0 ? 0 : price + m_Selection.tp - m_Selection.pr), (m_Selection.sl == 0 ? 0 : price + m_Selection.sl - m_Selection.pr), m_Selection.vol, m_Selection.bIsBuy);
                        m_Selection.MousePrice = price;
                        break;
        }
        if (Mouse.IsVisible())
        {
                m_TradeLine.SpotLight(macroMountName(m_Selection.ticket, m_Selection.it, EV_LINE));
                Mouse.Hide();
        }
}

Perché non inseriamo tutto nella funzione sopra, visto che è responsabile dello spostamento dei punti stop dell’ordine? Il motivo è che dobbiamo fare alcuni calcoli per impostare correttamente i livelli di take profit o stop loss, ed è molto più semplice farlo in un punto diverso. Dobbiamo anche modificare questo valore in un altro posto, quindi ecco il secondo posto in cui viene fatto riferimento al valore. Vedi il codice qui sotto:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;
        double  price;
        bool    bKeyBuy,

// ... Internal code ....      
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse button click
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT pressed
                        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                        m_Selection.it = IT_PENDING;
                                        m_Selection.pr = price;
                                }
                                m_Selection.tp = (valueTp == 0 ? 0 : m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp)));
                                m_Selection.sl = (valueSl == 0 ? 0 : m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl));
                                m_Selection.bIsBuy = bKeyBuy;
                                m_BtnInfoType.SetStateButton(macroMountName(def_IndicatorTicket0, IT_PENDING, EV_TYPE), bKeyBuy);
                                if (!bMounting)
                                {
                                        IndicatorAdd(m_Selection.ticket = def_IndicatorTicket0);
                                        bMounting = true;
                                }
                                MoveSelection(price);
                                if ((bEClick) && (memLocal == 0)) SetPriceSelection(memLocal = price);
                        }else if (bMounting)
                        {
                                RemoveIndicator(def_IndicatorTicket0);
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell) && (m_Selection.ticket > def_IndicatorGhost))
                        {
                                if (bEClick) SetPriceSelection(m_Selection.MousePrice); else MoveSelection(price);
                        }
                        break;

// ... Rest of the code...

Grazie a ciò, abbiamo un comportamento prevedibile nel sistema. C'è un altro punto in cui viene fatto riferimento al valore, ma a causa della complessità ho deciso di modificare il tutto in modo che la macro non esista più. Ora sarà una funzione. Quindi, la nuova funzione Update è mostrata di seguito:

void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
{
        double pr;
        bool b0, bPen = true;
                                
        if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
        {
                bPen = (pr = macroGetPrice(ticket, IT_RESULT, EV_LINE)) == 0;
                if (bPen && (macroGetPrice(ticket, IT_PENDING, EV_MOVE) == 0))
                {
                        CreateIndicator(ticket, IT_PENDING);
                        PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                        ChartRedraw();
                }
                pr = (pr > 0 ? pr : macroGetPrice(ticket, IT_PENDING, EV_MOVE));
                SetTextValue(ticket, IT_PENDING, vol);
        }
        b0 = UpdateIndicatorsLimits(ticket, IT_TAKE, tp, vol, pr, isBuy, bPen);
        b0 = (UpdateIndicatorsLimits(ticket, IT_STOP, sl, vol, pr, isBuy, bPen) ? true : b0);
        if (b0) ChartRedraw();
}

I punti evidenziati sostituiscono la macro precedente, ma poiché ho detto che il codice richiesto era molto più complicato, vediamo dove il terzo e ultimo punto è effettivamente indicato. Nel codice qui sopra, non c'è differenza tra gli indicatori di take profit e di stop loss: sono entrambi gestiti allo stesso modo. Dai un'occhiata al codice qui sotto.

inline bool UpdateIndicatorsLimits(ulong ticket, eIndicatorTrade it, double price, double vol, double pr, bool isBuy, bool isPen)
{
        bool b0 = false;
        double d1 = Terminal.GetPointPerTick();
                
        if (
    price  == 0)
        {
                if (macroGetPrice(ticket, it, EV_LINE) > 0) RemoveIndicator(ticket, it);
                if (macroGetPrice(ticket, it, EV_MOVE) == 0) CreateBtnMoveIndicator(ticket, it);
        } else if (b0 = (macroGetPrice(ticket, it, EV_LINE) == 0 ? true : b0)) CreateIndicator(ticket, it);
        switch (it)
        {
                case IT_TAKE:
                        price = (price == 0 ? 0 : (((isBuy ? price - pr : pr - price) > 0) ? price : (isBuy ? pr + d1 : pr - d1)));
                        break;
                case IT_STOP:
                        price = (price == 0 ? 0 : (isPen ? (((isBuy ? price - pr : pr - price) < 0) ? price : (isBuy ? pr - d1 : pr + d1)) : price));
                        break;
        }
        if (m_Selection.it == it) m_Selection.MousePrice = price;
        PositionAxlePrice(ticket, it, (price == 0 ? pr : price));
        SetTextValue(ticket, it, vol, (isBuy ? price - pr : pr - price));
                        
        return b0;
}

D'ora in poi il valore di take profit di un ordine pendente non può essere sbagliato poiché abbiamo un limite per i valori consentiti. Non è più possibile inserire un ordine di acquisto pendente e poi spostare il take profit su un valore negativo (cioè al di sotto del punto di ingresso), poiché il calcolo dell'indicatore take profit lo impedisce. Il vantaggio di scrivere il codice in questo modo è che, non importa se si tratta di un ordine o di una posizione, il valore di take non potrà mai essere negativo, perché l'EA stesso non lo consentirà.

Ora, per quanto riguarda lo stop loss, c’è una leggera differenza nei calcoli, in cui controlliamo ciò che stiamo gestendo - un ordine o una posizione. Se si tratta di un ordine, il valore di stop non sarà mai positivo e, se si tratta di una posizione, l'EA ignorerà semplicemente qualsiasi altra condizione. In questo caso, l'EA deve accettare il valore specificato dal trader. Quindi ora possiamo avere uno stop loss positivo, ma solo nel caso di posizioni, senza alcun danno al resto del codice del sistema degli ordini, e in questo modo l'EA potrà finalmente interagire con il server di trade senza che i dati inviati vengano rifiutati.


Conclusioni

Finalmente, dopo diversi articoli, abbiamo raggiunto il culmine e ora disponiamo di un sistema di ordini quasi completo e abbastanza adattabile a varie situazioni e condizioni di mercato. D'ora in poi, potremo fare trading con un sistema completamente grafico utilizzando il mouse e la tastiera, insieme alla tua analisi di mercato, per entrare o uscire dalle operazioni.

Per coloro che sono appena arrivati ​​e vogliono vedere come si comporta il sistema o come appare nell'attuale fase di sviluppo, guardate il video qui sotto. E grazie a tutti coloro che hanno seguito finora questa serie di articoli. Ma il lavoro non è ancora finito e abbiamo molto lavoro da fare affinché questo Expert Advisor diventi qualcosa di memorabile. Ci vediamo al prossimo articolo! 👍



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

Il mercato e la fisica dei suoi modelli globali Il mercato e la fisica dei suoi modelli globali
In questo articolo cercherò di verificare l'ipotesi che qualsiasi sistema con una comprensione anche minima del mercato possa operare su scala globale. Non inventerò teorie o modelli, ma utilizzerò solo fatti noti, traducendoli gradualmente nel linguaggio dell'analisi matematica.
Sviluppare un Expert Advisor per il trading da zero (Parte 27): Verso il futuro (II) Sviluppare un Expert Advisor per il trading da zero (Parte 27): Verso il futuro (II)
Passiamo ad un sistema di ordini più completo direttamente sul grafico. In questo articolo mostrerò un modo per correggere il sistema degli ordini, o meglio, per renderlo più intuitivo.
Combinatoria e probabilità per il trading (Parte IV): Logica di Bernoulli Combinatoria e probabilità per il trading (Parte IV): Logica di Bernoulli
In questo articolo ho deciso di mettere in evidenza il noto schema di Bernoulli e di mostrare come può essere utilizzato per descrivere gli array di dati relativi al trading. Tutto questo verrà poi utilizzato per creare un sistema di trading auto-adattante. Cercheremo anche di trovare un algoritmo più generico, un caso speciale di cui è la formula di Bernoulli, e ne troveremo un'applicazione.
Sviluppare un Expert Advisor per il trading da zero (Parte 26): Verso il futuro (I) Sviluppare un Expert Advisor per il trading da zero (Parte 26): Verso il futuro (I)
Oggi porteremo il nostro sistema di ordini ad un livello superiore. Ma prima di ciò, dobbiamo risolvere alcuni problemi. Ora abbiamo alcune domande che riguardano il modo in cui vogliamo lavorare e le cose che facciamo durante la giornata di trading.