English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
preview
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)

MetaTrader 5Esempi | 21 febbraio 2024, 10:26
220 0
Daniel Jose
Daniel Jose

Introduzione

Nell'articolo precedente, Sviluppare un Expert Advisor per il trading da zero (Parte 26), abbiamo corretto un errore catastrofico che esisteva nel sistema degli ordini. Abbiamo anche iniziato ad implementare modifiche per consentire il funzionamento del nuovo sistema di ordini. Sebbene il sistema originariamente implementato in questa serie di articoli sia piuttosto interessante, presenta un difetto che lo rende inutilizzabile. Questo difetto è stato mostrato alla fine dell'articolo precedente. Il motivo era non sapere come fare trading, più specificamente come scegliere una data di espirazione per un ordine o una posizione, oltre ad altri problemi minori. Il sistema è stato corretto come un ordine o una posizione che dovrebbe essere chiusa alla fine della sessione di negoziazione o nel giorno corrente. Ma a volte vogliamo fare operazioni a lungo termine, quindi lasciare tutto così com’è non aiuta certamente.

Pertanto, in questo articolo, ti mostrerò una soluzione. Vedremo come rendere il sistema degli ordini più intuitivo in modo che tu possa determinare immediatamente e con precisione cos'è ciascun ordine, come viene processato e che tipo di movimento è previsto.

Questo sistema è così interessante, semplice e intuitivo che una volta visto in azione, non vorrai più farne a meno. Quella che ti mostrerò in questo articolo è solo una delle tante possibilità che puoi implementare nel sistema degli ordini. Magari più avanti mostrerò altre cose, ma quello che vedremo in questo articolo può fornire un'eccellente base per realizzare altre modifiche utili ed interessanti per il vostro caso particolare. Ad ogni modo, cerco di mantenere ogni cosa in questi articoli il più generale possibile.


2.0. Modello intuitivo

Finora abbiamo lavorato con gli ordini come segue:

Le indicazioni Take Profit e Stop Loss hanno una forma riconoscibile e abbastanza intuitiva: il verde indica quanto verrà guadagnato sul nostro conto e il rosso indica quanto verrà detratto. È tutto chiaro. Se abbiamo un'indicazione di Stop come mostrato di seguito, dimostra comunque che l'attivazione dello stop loss comporterà l'aggiunta di questa somma sul nostro conto. In altre parole, i livelli degli ordini di stop non richiedono il nostro impegno, almeno per ora. Forse in futuro potresti voler cambiare qualcosa in loro, ma al momento sono abbastanza adatti all'uso.

Questo modo di utilizzare i livelli di stop è abbastanza semplice e intuitivo da analizzare per qualsiasi trader. Ma abbiamo qualcosa che non è molto chiaro. Il primo è l'indicazione del punto di ingresso dell'ordine pendente.

Possiamo sapere se questo ordine pendente è un ordine di acquisto o di vendita? E ancora una cosa: è possibile sapere se questo ordine pendente verrà chiuso a fine giornata o verrà aperta una posizione a lungo termine? È facile. Ora, se abbiamo già posizioni aperte, l'indicatore avrà un aspetto simile al seguente:

E ancora, abbiamo gli stessi problemi come con gli indicatori degli ordini pendenti. Quando guardi il grafico e vedi questi indicatori, non puoi dire con certezza se una posizione verrà chiusa alla fine della giornata o se durerà più a lungo. Se chiude a fine giornata, non vorrai che il broker lo fermi obbligatoriamente, poiché dovrai pagare per questo. E non è molto ragionevole chiudere gli ordini senza alcun criterio, poiché anche il box strumenti MetaTrader non mostra queste informazioni, così avendolo sul grafico tramite l'indicatore è semplicemente fantastico.

Dovremo quindi apportare modifiche qui, soprattutto negli indicatori che mostrano il punto di ingresso della posizione, in modo da comprendere meglio cosa sta succedendo.


2.0.1. Come aggiungere nuove informazioni agli indicatori

Il modo più semplice per aggiungere nuove informazioni senza occupare molto spazio sul grafico è utilizzare le bitmap, poiché sono facili da comprendere e abbastanza rappresentative. Pertanto, senza inserire alcun codice aggiuntivo, aggiungiamo quattro nuove bitmap all'EA, che possono essere visualizzate nella classe C_IndicatorTradeView.

#define def_BtnClose            "Images\\NanoEA-SIMD\\Btn_Close.bmp"
#define def_BtnCheckEnabled     "Images\\NanoEA-SIMD\\CheckBoxEnabled.bmp"
#define def_BtnCheckDisabled    "Images\\NanoEA-SIMD\\CheckBoxDisabled.bmp"
#define def_BtnDayTrade         "Images\\NanoEA-SIMD\\Inf_DayTrade.bmp"
#define def_BtnSwing            "Images\\NanoEA-SIMD\\Inf_Swing.bmp"
#define def_BtnInfoBuy          "Images\\NanoEA-SIMD\\Inf_Buy.bmp"
#define def_BtnInfoSell         "Images\\NanoEA-SIMD\\Inf_Sell.bmp"
//+------------------------------------------------------------------+
#resource "\\" + def_BtnClose
#resource "\\" + def_BtnCheckEnabled
#resource "\\" + def_BtnCheckDisabled
#resource "\\" + def_BtnDayTrade
#resource "\\" + def_BtnSwing
#resource "\\" + def_BtnInfoBuy
#resource "\\" + def_BtnInfoSell

Inoltre, dobbiamo solo implementare due nuovi oggetti nel sistema degli ordini.

//+------------------------------------------------------------------+
enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PENDING, IT_RESULT};
enum eEventType {EV_NULL, EV_GROUND = 65, EV_LINE, EV_CLOSE, EV_EDIT, EV_PROFIT, EV_MOVE, EV_CHECK, EV_TYPE, EV_DS};
//+------------------------------------------------------------------+
C_Object_BackGround     m_BackGround;
C_Object_TradeLine      m_TradeLine;
C_Object_BtnBitMap      m_BtnClose,
                        m_BtnCheck,
                        m_BtnInfoType,
                        m_BtnInfo_DS;
C_Object_Edit           m_EditInfo1,
                        m_EditInfo2;
C_Object_Label          m_BtnMove;

Ogni volta che aggiungeremo un nuovo oggetto, dovremo anche aggiungere un EVENTO collegato all'oggetto, che garantirà che l'oggetto abbia un nome univoco.

Ora arriva la parte più interessante della programmazione. La prima cosa che dobbiamo fare è prenderci cura dei fantasmi. Dobbiamo aggiornarli in modo che mantengano le informazioni al loro interno. Naturalmente, potrebbe essere cancellato, ma penso che sia meglio mantenere i dati di base. Dai un'occhiata al seguente codice:

#define macroSwapName(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorGhost, A, B));
                void CreateGhostIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                if (GetInfosTradeServer(m_Selection.ticket = ticket) != 0)
                                {
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                        macroSwapName(it, EV_LINE);
                                        macroSwapName(it, EV_GROUND);
                                        macroSwapName(it, EV_MOVE);
                                        macroSwapName(it, EV_EDIT);
                                        macroSwapName(it, EV_CLOSE);
                                        if (it == IT_PENDING)
                                        {
                                                macroSwapName(it, EV_CHECK);
                                                macroSwapName(it, EV_TYPE);
                                                macroSwapName(it, EV_DS);
                                        }
                                        m_TradeLine.SetColor(macroMountName(def_IndicatorGhost, it, EV_LINE), def_IndicatorGhostColor);
                                        m_BackGround.SetColor(macroMountName(def_IndicatorGhost, it, EV_GROUND), def_IndicatorGhostColor);
                                        m_BtnMove.SetColor(macroMountName(def_IndicatorGhost, it, EV_MOVE), def_IndicatorGhostColor);
                                        ObjectDelete(Terminal.Get_ID(), macroMountName(def_IndicatorGhost, it, EV_CLOSE));
                                        m_TradeLine.SpotLight();
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                                        m_Selection.it = it;
                                }else m_Selection.ticket = 0;
                        }
#undef macroSwapName

Le linee evidenziate passano gli oggetti al fantasma, è una cosa molto semplice e chiara. Un altro semplice codice trasforma gli indicatori da pendenti a variabili.

#define macroSwapAtFloat(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorFloat, A, B));
                bool PendingAtFloat(ulong ticket)
                        {
                                eIndicatorTrade it;
                                
                                if (macroGetLinePrice(def_IndicatorFloat, IT_PENDING) > 0) return false;
                                macroSwapAtFloat(IT_PENDING, EV_CHECK);
                                macroSwapAtFloat(IT_PENDING, EV_TYPE);
                                macroSwapAtFloat(IT_PENDING, EV_DS);
                                for (char c0 = 0; c0 < 3; c0++)
                                {
                                        switch(c0)
                                        {
                                                case 0: it = IT_PENDING;        break;
                                                case 1: it = IT_STOP;           break;
                                                case 2: it = IT_TAKE;           break;
                                                default:
                                                        return false;
                                        }
                                        macroSwapAtFloat(it, EV_CLOSE);
                                        macroSwapAtFloat(it, EV_MOVE);
                                        macroSwapAtFloat(it, EV_EDIT);
                                        macroSwapAtFloat(it, EV_GROUND);
                                        macroSwapAtFloat(it, EV_LINE);
                                        m_EditInfo1.SetOnlyRead(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT), false);
                                }
                                return true;
                        }
#undef macroSwapAtFloat

Le linee evidenziate convertono gli oggetti nell'indicatore variabile, permettendoci di eseguire le azioni richieste in seguito. Ora dobbiamo implementare alcune modifiche nel codice che crea l'indicatore. È qualcosa che testerai e adatterai finché non ti piacerà. Sostanzialmente le modifiche sono state fatte nei punti evidenziati nel codice sottostante:

#define macroCreateIndicator(A, B, C, D)        {                                                                               \
                m_TradeLine.Create(ticket, sz0 = macroMountName(ticket, A, EV_LINE), C);                                        \
                m_BackGround.Create(ticket, sz0 = macroMountName(ticket, A, EV_GROUND), B);                                     \
                m_BackGround.Size(sz0, (A == IT_RESULT ? 100 : (A == IT_PENDING ? 144 : 92)), (A == IT_RESULT ? 34 : 22));      \
                m_EditInfo1.Create(ticket, sz0 = macroMountName(ticket, A, EV_EDIT), D, 0.0);                                   \
                m_EditInfo1.Size(sz0, 60, 14);                                                                                  \
                if (A != IT_RESULT)     {                                                                                       \
                        m_BtnMove.Create(ticket, sz0 = macroMountName(ticket, A, EV_MOVE), "Wingdings", "u", 17, C);            \
                        m_BtnMove.Size(sz0, 21, 23);                                                                            \
                                        }else                   {                                                               \
                        m_EditInfo2.Create(ticket, sz0 = macroMountName(ticket, A, EV_PROFIT), clrNONE, 0.0);                   \
                        m_EditInfo2.Size(sz0, 60, 14);  }                                                                       \
                                                }
                                                                                                                
#define macroInfoBase(A)        {                                                                                               \
                m_BtnInfoType.Create(ticket, sz0 = macroMountName(ticket, A, EV_TYPE), def_BtnInfoBuy, def_BtnInfoSell);        \
                m_BtnInfoType.SetStateButton(sz0, m_Selection.bIsBuy);                                                          \
                m_BtnInfo_DS.Create(ticket, sz0 = macroMountName(ticket, A, EV_DS), def_BtnDayTrade, def_BtnSwing);             \
                m_BtnInfo_DS.SetStateButton(sz0, m_Selection.bIsDayTrade);                                                      \
                                }

                void CreateIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                string sz0;
                                
                                switch (it)
                                {
                                        case IT_TAKE    : macroCreateIndicator(it, clrForestGreen, clrDarkGreen, clrNONE); break;
                                        case IT_STOP    : macroCreateIndicator(it, clrFireBrick, clrMaroon, clrNONE); break;
                                        case IT_PENDING:
                                                macroCreateIndicator(it, clrCornflowerBlue, clrDarkGoldenrod, def_ColorVolumeEdit);
                                                m_BtnCheck.Create(ticket, sz0 = macroMountName(ticket, it, EV_CHECK), def_BtnCheckEnabled, def_BtnCheckDisabled);
                                                m_BtnCheck.SetStateButton(sz0, true);
                                                macroInfoBase(IT_PENDING);
                                                break;
                                        case IT_RESULT  :
                                                macroCreateIndicator(it, clrSlateBlue, clrSlateBlue, def_ColorVolumeResult);
                                                macroInfoBase(IT_RESULT);
                                                break;
                                }
                                m_BtnClose.Create(ticket, macroMountName(ticket, it, EV_CLOSE), def_BtnClose);
                        }
#undef macroInfoBase
#undef macroCreateIndicator

Notare che macroInfoBase crea gli oggetti che vengono utilizzati nell'indicatore, ma questi oggetti verranno creati solo negli indicatori di apertura di posizione e di risultato di posizione, mentre non è necessario creare questi oggetti in altri indicatori. Ma nota che non posizioniamo gli oggetti nel luogo in cui li abbiamo creati. Questo viene fatto in un altro posto, mostrato di seguito.

#define macroSetAxleY(A)                {                                                                               \
                m_BackGround.PositionAxleY(macroMountName(ticket, A, EV_GROUND), y);                                    \
                m_TradeLine.PositionAxleY(macroMountName(ticket, A, EV_LINE), y);                                       \
                m_BtnClose.PositionAxleY(macroMountName(ticket, A, EV_CLOSE), y);                                       \
                if (A != IT_RESULT)m_BtnMove.PositionAxleY(macroMountName(ticket, A, EV_MOVE), y, 1);                   \
                else m_EditInfo2.PositionAxleY(macroMountName(ticket, A, EV_PROFIT), y, 1);                             \
                m_EditInfo1.PositionAxleY(macroMountName(ticket, A, EV_EDIT), y, (A == IT_RESULT ? -1 : 0));            \
                if (A == IT_PENDING) m_BtnCheck.PositionAxleY(macroMountName(ticket, A, EV_CHECK), y);                  \
                if ((A == IT_PENDING) || (A == IT_RESULT))      {                                                       \
                        m_BtnInfoType.PositionAxleY(macroMountName(ticket, A, EV_TYPE), y + (A == IT_PENDING ? 0 : 8)); \
                        m_BtnInfo_DS.PositionAxleY(macroMountName(ticket, A, EV_DS), y - (A == IT_PENDING ? 0: 8));     \
                                                                }                                                       \
                                        }
                                                                        
#define macroSetAxleX(A, B)             {                                                                                               \
                m_BackGround.PositionAxleX(macroMountName(ticket, A, EV_GROUND), B);                                                    \
                m_TradeLine.PositionAxleX(macroMountName(ticket, A, EV_LINE), B);                                                       \
                m_BtnClose.PositionAxleX(macroMountName(ticket, A, EV_CLOSE), B + 3);                                                   \
                m_EditInfo1.PositionAxleX(macroMountName(ticket, A, EV_EDIT), B + 21);                                                  \
                if (A != IT_RESULT) m_BtnMove.PositionAxleX(macroMountName(ticket, A, EV_MOVE), B + 80 + (A == IT_PENDING ? 52 : 0));   \
                else m_EditInfo2.PositionAxleX(macroMountName(ticket, A, EV_PROFIT), B + 21);                                           \
                if (A == IT_PENDING) m_BtnCheck.PositionAxleX(macroMountName(ticket, A, EV_CHECK), B + 82);                             \
                if ((A == IT_PENDING) || (A == IT_RESULT))      {                                                                       \
                        m_BtnInfoType.PositionAxleX(macroMountName(ticket, A, EV_TYPE), B + (A == IT_PENDING ? 100 : 82));              \
                        m_BtnInfo_DS.PositionAxleX(macroMountName(ticket, A, EV_DS), B + (A == IT_PENDING ? 118 : 82));                 \
                                                                }                                                                       \
                                        }
//---
        void ReDrawAllsIndicator(void)
                        {
                                C_IndicatorTradeView::st00 Local;
                                int             max = ObjectsTotal(Terminal.Get_ID(), -1, OBJ_EDIT);
                                ulong           ticket;
                                eIndicatorTrade it;
                                eEventType ev;
                                
                                Local = m_Selection;
                                m_Selection.ticket = 0;
                                for (int c0 = 0; c0 <= max; c0++)
                                   if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, OBJ_EDIT), ticket, it, ev))
                                      if ((it == IT_PENDING) || (it == IT_RESULT))
                                      {
                                        PositionAxlePrice(ticket, IT_STOP, macroGetLinePrice(ticket, IT_STOP));
                                        PositionAxlePrice(ticket, IT_TAKE, macroGetLinePrice(ticket, IT_TAKE));
                                        PositionAxlePrice(ticket, it, macroGetLinePrice(ticket, it));
                                        }
                                m_Selection = Local;
                                ChartRedraw();
                        }
//---
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                macroSetLinePrice(ticket, it, 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));
                        }
#undef macroSetAxleX
#undef macroSetAxleY

Ci tengo inoltre a sottolineare che non mi piace apportare modifiche drastiche e radicali al codice. Fondamentalmente, le uniche modifiche sono state evidenziate nel codice sopra.


2.0.2. Problemi in vista

Sebbene tutto funzioni perfettamente, abbiamo un problema. Ho cercato in tutta la documentazione di MQL5, ma non ho trovato alcun modo per risolvere la cosa in modo semplice. Il problema è come sapere se una posizione aperta recentemente è un Day Trade (operazioni brevi nello stesso giorno) o Swing Trade (operazioni più lunghe). Nel caso di una posizione più vecchia, aperta il giorno prima, è abbastanza semplice fare questo tipo di analisi, perché basterebbe confrontare il giorno corrente e il giorno di apertura della posizione: se sono diversi, la posizione è uno Swing Trade. Ma cosa succede se l'EA viene chiuso e lo si avvia lo stesso giorno in cui è stata aperta la posizione? In questo caso non c'è modo di sapere se una posizione è un Day Trade o uno Swing Trade.

Questo problema non esiste per gli ordini pendenti poiché esiste un modo per verificarlo. Quando si chiama OrderGetInteger utilizzando il parametro ORDER_TYPE_TIME, viene restituito un valore dall’enumerazione ENUM_ORDER_TYPE_TIME, che indica se un ordine è Day Trade o Swing Trade. Ma lo stesso non accade per le posizioni.

Per questo motivo, la mia soluzione in questo caso è aggiungere qualcosa all'ordine o alla posizione per far conoscere all'EA la durata dell'operazione, indipendentemente da qualsiasi altra informazione. Ma questa non è una soluzione perfetta, poiché risolve il problema in diversi casi, ma non in tutti. Perché il trader può modificare il sistema utilizzato dall'EA per identificare se un'operazione è uno Swing o un Day Trade prima del periodo richiesto per l'analisi.

Per capire meglio vediamo come viene implementata la soluzione.

inline char GetInfosTradeServer(ulong ticket)
{
        long info;
                                
        if (ticket == 0) return 0;
        if (OrderSelect(ticket))
        {
                if (OrderGetString(ORDER_SYMBOL) != Terminal.GetSymbol()) return 0;
                info = OrderGetInteger(ORDER_TYPE);
                m_Selection.bIsBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                m_Selection.pr = OrderGetDouble(ORDER_PRICE_OPEN);
                m_Selection.tp = OrderGetDouble(ORDER_TP);
                m_Selection.sl = OrderGetDouble(ORDER_SL);
                m_Selection.vol = OrderGetDouble(ORDER_VOLUME_CURRENT);
                m_Selection.bIsDayTrade = ((ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME) == ORDER_TIME_DAY);
                                        
                return -1;
        }
        if (PositionSelectByTicket(ticket))
        {
                if (PositionGetString(POSITION_SYMBOL) != Terminal.GetSymbol()) return 0;
                m_Selection.bIsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
                m_Selection.pr = PositionGetDouble(POSITION_PRICE_OPEN);
                m_Selection.tp = PositionGetDouble(POSITION_TP);
                m_Selection.sl = PositionGetDouble(POSITION_SL);
                m_Selection.vol = PositionGetDouble(POSITION_VOLUME);
                if (macroGetDate(PositionGetInteger(POSITION_TIME)) == macroGetDate(TimeTradeServer()))
                        m_Selection.bIsDayTrade = PositionGetString(POSITION_COMMENT) == def_COMMENT_TO_DAYTRADE;
                else m_Selection.bIsDayTrade = false;
                                        
                return 1;
        }
        return 0;
}

Come accennato in precedenza, nel caso di ordini pendenti, è sufficiente chiamare OrderGetInteger e ottenere il valore di cui abbiamo bisogno. Con le posizioni è un po’ più complicato. Funziona così: controlla il giorno di apertura della posizione e il giorno corrente del server di trading. Se entrambi sono uguali, controlla il commento nell'ordine. Se il commento indica la stringa utilizzata nella classe C_Router per indicare che se la posizione viene aperta sarà Day Trade, l'EA la interpreterà e la visualizzerà nell'indicatore di posizione. Ma il commento non deve cambiare fino alla fine della giornata, perché se cambia, allora l'EA potrebbe segnalare che la posizione Day Trade è in realtà uno Swing Trade, nel qual caso non è colpa dell'EA, ma del trader che ha cambiato il commento troppo presto.

Questo è lo svantaggio di questa soluzione, ma se qualcuno ha un'idea su come determinare se una posizione è un Day Trade o meno semplicemente osservando la data della posizione, condividilo nei commenti.

Il modo in cui appare nel caso di ordini pendenti è visto nel video qui sotto:


Ora abbiamo quasi tutto pronto, dobbiamo solo fare qualche aggiunta in più al codice in modo che l'EA diventi interessante da usare.


2.0.3. Rispondere ai messaggi della piattaforma

Il nostro intero sistema di ordini si basa sui messaggi inviati da MetaTrader 5 in modo che l'EA possa sapere cosa dovrebbe o non dovrebbe essere fatto. Ecco perché è così importante sapere come implementare il sistema di messaggi.

Il codice completo relativo alla messaggistica è mostrato di seguito:

#define macroGetDataIndicatorFloat      {                                                                                                               \
                m_Selection.vol = m_EditInfo1.GetTextValue(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT)) * Terminal.GetVolumeMinimal();      \
                m_Selection.bIsBuy = m_BtnInfoType.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_TYPE));                             \
                m_Selection.pr = macroGetLinePrice(def_IndicatorFloat, IT_PENDING);                                                                     \
                m_Selection.sl = macroGetLinePrice(def_IndicatorFloat, IT_STOP);                                                                        \
                m_Selection.tp = macroGetLinePrice(def_IndicatorFloat, IT_TAKE);                                                                        \
                m_Selection.bIsDayTrade = m_BtnInfo_DS.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_DS));                           \
                                        }
                                                                                                
                void DispatchMessage(int id, long lparam, double dparam, string sparam)
                        {
                                ulong   ticket;
                                double  price;
                                bool   	bKeyBuy,
                                        bKeySell,
                                        bEClick;
                                datetime dt;
                                uint     mKeys;
                                char     cRet;
                                eIndicatorTrade  it;
                                eEventType       ev;
                                
                                static bool bMounting = false;
                                static double valueTp = 0, valueSl = 0, memLocal = 0;
                                
                                switch (id)
                                {
                                        case CHARTEVENT_MOUSE_MOVE:
                                                Mouse.GetPositionDP(dt, price);
                                                mKeys   = Mouse.GetButtonStatus();
                                                bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse 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 = m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp));
                                                        m_Selection.sl = 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(price); else MoveSelection(price);
                                                }
                                                break;
                                        case CHARTEVENT_OBJECT_DELETE:
                                                if (GetIndicatorInfos(sparam, ticket, it, ev))
                                                {
                                                        if (GetInfosTradeServer(ticket) == 0) break;
                                                        CreateIndicator(ticket, it);
                                                        if ((it == IT_PENDING) || (it == IT_RESULT))
                                                                PositionAxlePrice(ticket, it, m_Selection.pr);
                                                        ChartRedraw();
                                                        m_TradeLine.SpotLight();
                                                        m_Selection.ticket = 0;
                                                        UpdateIndicators(ticket, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                }
                                                break;
                                        case CHARTEVENT_OBJECT_ENDEDIT:
                                                macroGetDataIndicatorFloat;
                                                m_Selection.ticket = 0;
                                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                break;
                                        case CHARTEVENT_CHART_CHANGE:
                                                ReDrawAllsIndicator();
                                                break;
                                        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) RemoveIndicator(def_IndicatorFloat, it);
                                                                else if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it)
                                                                {
                                                                        case IT_PENDING:
                                                                        case IT_RESULT:
                                                                                if (cRet < 0) RemoveOrderPendent(ticket); else ClosePosition(ticket);
                                                                                break;
                                                                        case IT_TAKE:
                                                                        case IT_STOP:
                                                                                m_Selection.ticket = ticket;
                                                                                m_Selection.it = it;
                                                                                SetPriceSelection(0);
                                                                                break;
                                                                }
                                                                break;
                                                        case EV_MOVE:
                                                                        if (ticket == def_IndicatorFloat)
                                                                        {
                                                                                macroGetDataIndicatorFloat;
                                                                                m_Selection.ticket = ticket;
                                                                                m_Selection.it = it;
                                                                        }else   CreateGhostIndicator(ticket, it);
                                                                break;
                                                        case EV_CHECK:
                                                                if (ticket != def_IndicatorFloat)
                                                                {
                                                                        if (PendingAtFloat(ticket)) RemoveOrderPendent(ticket);
                                                                        else m_BtnCheck.SetStateButton(macroMountName(ticket, IT_PENDING, EV_CHECK), true);
                                                                } else
                                                                {
                                                                        macroGetDataIndicatorFloat;
                                                                        m_Selection.ticket = def_IndicatorTicket0;
                                                                        m_Selection.it = IT_PENDING;
                                                                        SetPriceSelection(m_Selection.pr);
                                                                        RemoveIndicator(def_IndicatorFloat);
                                                                }
                                                                break;
                                                }
                                                break;
                                }
                        }
#undef macroGetDataIndicatorFloat

Non lasciarti spaventare da questo codice. Anche se sembra grande e complicato, in realtà è alquanto semplice. Mi concentrerò sulle parti evidenziate per spiegare le novità nel codice di elaborazione dei messaggi.

La prima cosa nuova è nel codice di gestione dell'evento CHARTEVENT_OBJECT_ENDEDIT . Viene attivato da MetaTrader 5 ogni volta che finiamo di modificare il contenuto presente nell'oggetto EDIT. Che cosa significa? È molto importante, perché se non elaboriamo questo evento e proviamo a manipolare i dati degli indicatori del livello di stop dopo aver modificato il valore della leva, avremo una mancata corrispondenza dei valori. Sebbene l'EA forzerà il valore a tornare al suo valore originale, se gestiamo questo evento come mostrato nel codice, questo problema non esisterà e potremo tradare senza problemi con i dati di leva. Tieni presente che quando chiedi all'EA di consentirti di apportare queste modifiche, in realtà vorrai verificare se è o meno una buona idea entrare nell'operazione con più o meno leva finanziaria. In questo modo potrai controllare senza correre rischi, poiché l'EA invierà l'ordine al server solo quando glielo chiedi, e nel momento in cui ciò accade è quando la casella di controllo viene attivata.

Ora diamo uno sguardo più da vicino all’evento CHARTEVENT_OBJECT_CLICK. Per questo prendiamo il frammento evidenziato nel codice precedente.

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;

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

Cosa fa effettivamente questo codice? Hai qualche idea? Bene, i video in questo articolo lo dimostrano, ma riesci a capire come si fa questo genere di cose? Molti immaginerebbero che fosse un codice estremamente complesso, ma eccolo lì, appena sopra.

Ci sono due cose che dobbiamo fare. Il primo è che quando si clicca su un oggetto BitMap, il suo stato cambia e dobbiamo verificare se il suo ticket proviene da qualcosa già esistente sul server o è qualcosa che è solo sul grafico. Questo viene fatto dai punti evidenziati in verde. Se il ticket esiste sul server, la modifica dello stato deve essere annullata, quindi l'EA lo correggerà apportando la modifica richiesta.

Ora dai un'occhiata alla sezione evidenziata in giallo. L’idea si basa su quanto segue: Perché dovrei piazzare un altro ordine sul grafico, se esiste già sul grafico, e voglio solo invertire la direzione? In altre parole, se era Buy, ora voglio che sia Sell e viceversa. Il frammento giallo fa proprio questo: quando si fa clic sul BitMap responsabile dell'acquisto o della vendita, la direzione cambia automaticamente. Un dettaglio: questo è possibile solo per un ordine variabile; è vietato per ordini già presenti sul server.

Con tutti questi cambiamenti, gli indicatori ora appaiono così:

Tipi di ordini pendenti

 

Indicatore di posizione:

Ora è molto più semplice determinare cosa sta facendo un ordine pendente o una posizione aperta, perché puoi conoscere esattamente il movimento previsto o la durata della posizione. La freccia verde rivolta verso l'alto indica una posizione di acquisto; una freccia rossa rivolta verso il basso indica una posizione di vendita. La lettera D indica un Day Trade, che verrà chiusa alla fine della giornata. Se è S allora è uno Swing Trade e l'operazione non verrà necessariamente chiusa a fine giornata.

Il prossimo video mostra come funziona il nuovo sistema di ordini. Mi sono concentrato sugli ordini pendenti perché sono soggetti a ulteriori modifiche mentre gli indicatori di posizione non possono essere modificati. Mostreranno solo i dati forniti dal server per quanto riguarda la posizione. Dai un'occhiata più da vicino a come funziona prima di provarlo su un conto reale, perché il sistema è pratico, ma è necessario famigliarizzare con esso per ottenere il massimo dalle sue funzionalità.




Conclusioni

Bene, il nostro sistema di ordini ora è abbastanza versatile. Può fare diverse cose che aiutano molto, ma manca ancora un dettaglio importante che verrà implementato nel prossimo articolo. Quindi, a presto...


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

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)
C'è ancora un compito per la quale il nostro sistema di ordini non è all'altezza, ma FINALMENTE lo scopriremo. 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.
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.
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.
Impara come progettare un sistema di trading tramite Fractals Impara come progettare un sistema di trading tramite Fractals
Ecco un nuovo articolo della nostra serie su come progettare un sistema di trading basato sugli indicatori tecnici più popolari. Impareremo a conoscere un nuovo indicatore, l'indicatore Fractals e a progettare un sistema di trading basato su di esso, da eseguire nel terminale MetaTrader 5.