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

Sviluppare un Expert Advisor per il trading da zero (Parte 14): Aggiunta dei Volumi al Prezzo (II)

MetaTrader 5Sistemi di trading | 23 gennaio 2023, 17:10
457 0
Daniel Jose
Daniel Jose

Introduzione

Il nostro EA ha già alcune risorse che assistono nel trading — le abbiamo aggiunte nei nostri articoli precedenti. Tuttavia, questo EA ha alcuni problemi con la visualizzazione e il ridimensionamento. Non interferiscono con il trading, ma in alcuni punti questo porta a un pasticcio sullo schermo fino a quando non lo si forza ad aggiornare. Inoltre, ci sono ancora alcune cose mancanti che ci fornirebbero informazioni preziose. Queste sono cose specifiche, ma le informazioni possono essere necessarie.

Quindi, iniziamo a implementare questi nuovi miglioramenti. Questo interessante articolo può fornire alcune nuove idee e metodi di presentazione delle informazioni. Allo stesso tempo, può assistere a correggere piccoli difetti nei progetti.


Pianificazione e implementazione di una nuova funzione in Volume At Price

1. Pianificazione

C'è una cosa curiosa nel trading. Spesso vediamo che il mercato accumula in determinate regioni di prezzo e quando gli stop si attivano sul lato acquisto o vendita, si verifica un rapido movimento del prezzo. Questo movimento può essere visto tramite Times & Trade. Abbiamo considerato questo negli articoli precedenti, Times & Trade (I) e Times & Trade (II). In questi articoli, abbiamo visto come creare un sistema grafico alternativo per la lettura e l'analisi di un flusso di ordini eseguiti. Se guardi più da vicino, noterai che in alcuni momenti il prezzo tende a tornare nella regione di accumulazione, da dove non vuole andarsene in quel momento. Ma quando osserviamo l'indicatore Volume at Price, è difficile determinare quanto sia rimasto invariato di recente il prezzo in questa specifica regione. Questo indicatore è stato implementato nell'articolo "Aggiunta dei volumi al prezzo (I)". Usandolo, possiamo analizzare movimenti relativamente recenti semplicemente cambiando il punto di partenza dell'analisi, che a sua volta viene fatta regolando il valore dell'oggetto indicato nella figura sottostante:

Ma questo in realtà non è pratico, dal momento che siamo legati al timeframe principale, cioè, se hai un grafico con timeframe di 60 minuti, non sarai in grado di analizzare i movimenti dei prezzi al di sotto di questo periodo di tempo. Devi passare a un timeframe inferiore per poter regolare il punto di analisi. Ma quando si scambiano contratti futures, la maggior parte dei trader utilizza in realtà timeframe inferiori come 5, 10 o 30 minuti, quindi non ci sono problemi con l'adeguamento del punto di inizio dell'analisi. Ma come ho spiegato in precedenza, a volte il prezzo esce dall'accumulazione perché si sono attivati degli stop e un tale ritorno di solito si verifica in meno di 5 minuti. In questi casi compare sul grafico, una candela con una lunga ombra superiore o inferiore. In questi casi la Price Action ci dice che quello che è successo è stato un sondaggio di mercato, questo tipo di movimento può essere visto sotto sulle candele indicate dalle frecce:

   

Tipica mossa test dei compratori o attivazione degli stop short

 

Tipica mossa test dei venditori o attivazione dello stop lato compratore

Questo tipo di movimento si verifica frequentemente e analizzare il volume generato in ciascuna delle fasce di prezzo è molto importante in quanto consente di capire se il mercato sta testando o se il trend si sta davvero invertendo. Ma è impossibile farlo correttamente, o piuttosto rapidamente, utilizzando l'indicatore dei volumi proposto in precedenza.

Tuttavia, possiamo apportare una piccola modifica alla classe oggetto dell’indicatore per avere un'idea più chiara di cosa sta succedendo. Questo apparirà come una traccia di un trade che ha avuto luogo in un dato periodo di tempo.


2. Implementazione

La prima cosa da fare è analizzare quanto tempo di tracciamento vuoi impostare, se sarà 60, 45, 30, 19, 7 o 1 minuto. Indipendentemente da ciò, consigliamo di utilizzare valori abbastanza multipli in modo che il sistema di tracciamento possa essere davvero utile. Per motivi pratici lo implementeremo utilizzando 30 minuti di tracciamento, quindi lo definiremo nella seguente riga di codice:

#define def_MaxTrailMinutes     30

Ma perché esattamente 30 minuti? In realtà, il sistema di tracciamento verrà eseguito ogni minuto, ma il tempo massimo di tracciamento sarà di 30 minuti. Cioè, avrai sempre un tracciamento di 30 minuti, ad esempio, quando il tracciamento passa al 31° minuto, il primo minuto di negoziazione non verrà più visualizzato. Come viene implementato? Questo utilizza il sistema di acquisizione mostrato di seguito:

inline void SetMatrix(MqlTick &tick)
{
        int pos;
                                
        if ((tick.last == 0) || ((tick.flags & (TICK_FLAG_BUY | TICK_FLAG_SELL)) == (TICK_FLAG_BUY | TICK_FLAG_SELL))) return;
        pos = (int) ((tick.last - m_Infos.FirstPrice) / Terminal.GetPointPerTick()) * 2;
        pos = (pos >= 0 ? pos : (pos * -1) - 1);
        if ((tick.flags & TICK_FLAG_BUY) == TICK_FLAG_BUY) m_InfoAllVaP[pos].nVolBuy += tick.volume; else
        if ((tick.flags & TICK_FLAG_SELL) == TICK_FLAG_SELL) m_InfoAllVaP[pos].nVolSell += tick.volume;
        m_InfoAllVaP[pos].nVolDif = (long)(m_InfoAllVaP[pos].nVolBuy - m_InfoAllVaP[pos].nVolSell);
        m_InfoAllVaP[pos].nVolTotal = m_InfoAllVaP[pos].nVolBuy + m_InfoAllVaP[pos].nVolSell;
        m_Infos.MaxVolume = (m_Infos.MaxVolume > m_InfoAllVaP[pos].nVolTotal ? m_Infos.MaxVolume : m_InfoAllVaP[pos].nVolTotal);
        m_Infos.CountInfos = (m_Infos.CountInfos == 0 ? 1 : (m_Infos.CountInfos > pos ? m_Infos.CountInfos : pos));
        m_Infos.Momentum = macroGetMin(tick.time);
        m_Infos.Momentum = (m_Infos.Momentum > (def_MaxTrailMinutes - 1) ? m_Infos.Momentum - def_MaxTrailMinutes : m_Infos.Momentum);
        if (m_Infos.memMomentum != m_Infos.Momentum)
        {
                for (int c0 = 0; c0 <= m_Infos.CountInfos; c0++) m_TrailG30[m_Infos.Momentum].nVolume[c0] = 0;
                m_Infos.memMomentum = m_Infos.Momentum;
        }
        m_TrailG30[m_Infos.Momentum].nVolume[pos] += tick.volume;
}

Le linee evidenziate sono quelle che sono state aggiunte al codice sorgente della classe oggetto - implementano l'acquisizione della traccia del volume. Le righe seguenti garantiscono che il tracciamento verrà eseguito come previsto.

m_Infos.Momentum = macroGetMin(tick.time);
m_Infos.Momentum = (m_Infos.Momentum > (def_MaxTrailMinutes - 1) ? m_Infos.Momentum - def_MaxTrailMinutes : m_Infos.Momentum);

Il sistema di cattura delle tracce è pronto. Ora dobbiamo prendere una nuova decisione. Ricorda che la traccia viene catturata ogni minuto. Questo può essere presentato in modo da poter vedere il volume in ogni fascia di prezzo entro 1 minuto. Finché stiamo creando grafici in questo modo, potresti considerare di fare qualcosa come mostrato di seguito:

 

Tonalità più chiare rappresentano volumi più freschi, il che potrebbe essere una buona idea...

Anche se sembra essere una buona idea, quando il volume è basso, o il movimento è molto veloce, anche con un volume significativo per quel momento, potrebbe in realtà non essere visibile, poiché verrà tracciato regolandosi al volume massimo che è stato trovato fino a quel momento. Pertanto, potresti voler tracciare un grafico leggermente diverso per risolvere questo problema, così sarebbe simile a questo:

Ogni colore rappresenta un periodo specifico nella traccia del volume.

Questo può aiutare nell'analisi di bande di volume molto strette e corregge i problemi occasionali riscontrati nel primo caso. Ma avremmo comunque il problema di regolazione che si verifica quando il volume potrebbe non essere così espressivo rispetto al volume complessivo in un altro punto. Inoltre, i colori per ogni periodo devono essere scelti con cura in modo che l'analisi non venga confusa durante attività di scambio elevate.

Pertanto, qui utilizzeremo un modello più semplice, che ancora una volta può essere adattato per analizzare i movimenti di differenti periodi. Tuttavia, tieni presente i problemi sopra menzionati. Questo dipenderà da te. Quindi la traccia verrà visualizzata come segue:

Vediamo qui una traccia pura. Quando succede, dovremmo analizzare sia Times & Trade che Price Action per capire cosa sta succedendo.

Ad ogni modo, l'unica funzione che deve essere modificata per cambiare la visualizzazione del volume è la funzione seguente:

void Redraw(void)
{
        uint            x, y, y1, p;
        double  reason = (double) (m_Infos.MaxVolume > m_WidthMax ? (m_WidthMax / (m_Infos.MaxVolume * 1.0)) : 1.0);
        double  desl = Terminal.GetPointPerTick() / 2.0;
        ulong           uValue;
                                
        Erase();
        p = m_WidthMax - 8;
        for (int c0 = 0; c0 <= m_Infos.CountInfos; c0++)
        {
                if (m_InfoAllVaP[c0].nVolTotal == 0) continue;
                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, m_Infos.FirstPrice + (Terminal.GetPointPerTick() * (((c0 & 1) == 1 ? -(c0 + 1) : c0) / 2)) + desl, x, y);
                y1 = y + Terminal.GetHeightBar();
                FillRectangle(p + 2, y, p + 8, y1, macroColorRGBA(m_InfoAllVaP[c0].nVolDif > 0 ? m_Infos.ColorBuy : m_Infos.ColorSell, m_Infos.Transparency));
                FillRectangle((int)(p - (m_InfoAllVaP[c0].nVolTotal * reason)), y, p, y1, macroColorRGBA(m_Infos.ColorBars, m_Infos.Transparency));
                uValue = 0;
                for (int c1 = 0; c1 < def_MaxTrailMinutes; c1++) uValue += m_TrailG30[c1].nVolume[c0];
                FillRectangle((int) (p - (uValue * reason)), y, p, y1, macroColorRGBA(clrRoyalBlue, m_Infos.Transparency));
        }
        C_Canvas::Update();
};

Per essere più precisi, è necessario modificare solo il codice evidenziato. Puoi giocarci fino a quando non ottieni il risultato desiderato. Nient'altro nella classe necessita di essere modificato tranne la parte evidenziata. Dopo aver compilato il programma ed eseguito sul grafico, vedrai qualcosa di simile a questo:



Risoluzione del problema di rendering

Sebbene il codice non abbia problemi specifici, c'è un piccolo difetto durante il ridimensionamento di un grafico: quando un grafico ingrandito viene ridimensionato a qualsiasi altra dimensione e poi di nuovo ingrandito, alcuni oggetti sono come persi, non si comportano come previsto e vengono posizionati in posti sbagliati. Non ci sono molte cose da correggere. Il problema è nel codice seguente - lo abbiamo utilizzato negli articoli precedenti.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        ChartRedraw();
}

C'è una modifica molto semplice, ma potresti pensare "Non vedo nulla - il codice è corretto". A prima vista non vedevo nulla di sbagliato neanche io e il codice è rimasto con un errore di runtime. Ma quando stavo aggiungendo alcune funzionalità extra, ho notato il problema, che è esattamente quello che ho descritto sopra. Per risolvere questo problema il codice deve essere modificato come segue:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        ChartRedraw();
}

Potrebbe sembrare banale, ma per capirne il motivo basta vedere l'intero codice della funzione e la parte evidenziata. Ora che il sistema è stato corretto, possiamo passare al passaggio successivo.


Aggiunta di risorse extra

La funzione che aggiungeremo ora è molto semplice e molti potrebbero non vedere molti motivi per implementarla, ma implementandola, aiuterà molto quando si lavora con gli ordini, incluso il posizionamento, lo spostamento o solo l’osservazione dell'indicatore Volume at Price.

La prima cosa da fare è cambiare la classe di cui farà parte il codice della rettifica della linea del prezzo. Questo codice esce dalla classe C_OrderView ed entra nella classe C_Terminal, ma per questo subisce anche delle piccole modifiche, visto che inizia a lavorare con le variabili della classe stessa. Di seguito è riportato come apparirà il nuovo codice.

double AdjustPrice(const double arg)
{
        double v0, v1;
                                
        if(m_Infos.TypeSymbol == OTHER) return arg;
        v0 = (m_Infos.TypeSymbol == WDO ? round(arg * 10.0) : round(arg));
        v1 = fmod(round(v0), 5.0);
        v0 -= ((v1 != 0) || (v1 != 5) ? v1 : 0);
        return (m_Infos.TypeSymbol == WDO ? v0 / 10.0 : v0);
};

In questo modo possiamo creare una nuova classe EA - sarà la classe C_Mouse. Questa classe oggetto sarà responsabile e la base degli eventi del mouse, quindi vediamo come va in questa fase di sviluppo. Ma prima, diamo un'occhiata all'attuale struttura della classe del nostro Expert Advisor, che è mostrata nella figura seguente:

Per implementare il prossimo sistema, è necessario introdurre una nuova struttura...

Quindi, data la struttura di cui sopra, scomponiamo il codice della classe oggetto C_Mouse, partendo dalla dichiarazione delle variabili, che possono essere viste di seguito:

class C_Mouse
{
        private  :
                struct st00
                {
                color   cor01,
                        cor02,
                        cor03;
                string  szNameObjH,
                        szNameObjV,
                        szNameObjT,
                        szNameObjI,
                        szNameObjB;
                }m_Infos;
                struct st01
                {
                        int      X,
                                 Y;
                        datetime dt;
                        double   price;
                        uint     ButtonsStatus;
                }Position;

Visto che ce ne sono poche in questa fase di sviluppo, passiamo al punto successivo che merita la nostra attenzione

~C_Mouse()
{
// ... Internal code ...
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_MOUSE_MOVE, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, true);
}

Questo codice ripristina il mirino CHART_CROSSHAIR_TOOL e disabilita l'uso degli eventi del mouse da parte del grafico, il che vuol dire che MT5 non deve più preoccuparsi di inviare tali eventi al grafico perché saranno gestiti dalla piattaforma stessa.

Abbiamo anche due funzioni molto comuni che verranno utilizzate quando controlleremo il mouse:

inline void Show(void)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_COLOR, m_Infos.cor01);
}
//+------------------------------------------------------------------+
inline void Hide(void)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, clrNONE);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, clrNONE);
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, 0, 0);
}

La cosa interessante è che il mouse in realtà non scompare, solo gli oggetti che creiamo scompaiono dallo schermo, e quando "accendiamo" il mouse, solo la linea del prezzo sarà effettivamente visibile. Questo può sembrare curioso, ma ha i suoi usi in alcuni punti specifici dell'EA. Uno di questi punti è l'oggetto della classe C_OrderView nella parte evidenziata nel codice seguente:

inline void MoveTo(uint Key)
{
        static double local = 0;
        int w = 0;
        datetime dt;
        bool bEClick, bKeyBuy, bKeySell;
        double take = 0, stop = 0, price;
                                
        bEClick  = (Key & 0x01) == 0x01;    //Left click
        bKeyBuy  = (Key & 0x04) == 0x04;    //SHIFT pressed
        bKeySell = (Key & 0x08) == 0x08;    //CTRL pressed
        Mouse.GetPositionDP(dt, price);
        if (bKeyBuy != bKeySell) Mouse.Hide(); else Mouse.Show();
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLinePrice, 0, 0, price = (bKeyBuy != bKeySell ? price : 0));
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLineTake, 0, 0, take = price + (m_Infos.TakeProfit * (bKeyBuy ? 1 : -1)));
        ObjectMove(Terminal.Get_ID(), m_Infos.szHLineStop, 0, 0, stop = price + (m_Infos.StopLoss * (bKeyBuy ? -1 : 1)));
        if((bEClick) && (bKeyBuy != bKeySell) && (local == 0)) CreateOrderPendent(bKeyBuy, m_Infos.Volume, local = price, take, stop, m_Infos.IsDayTrade); else local = 0;
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLinePrice, OBJPROP_COLOR, (bKeyBuy != bKeySell ? m_Infos.cPrice : clrNONE));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineTake, OBJPROP_COLOR, (take > 0 ? m_Infos.cTake : clrNONE));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineStop, OBJPROP_COLOR, (stop > 0 ? m_Infos.cStop : clrNONE));
};

Prestate attenzione alla riga sopra la parte evidenziata:

Mouse.GetPositionDP(dt, price);

Questa linea catturerà il valore della posizione del mouse. Di seguito il codice che riporterà questi valori:

inline void GetPositionDP(datetime &dt, double &price)
{
        dt = Position.dt;
        price = Position.price;
}

Ma non è tutto. In alcuni casi, abbiamo bisogno delle coordinate cartesiane del grafico in termini di posizione sullo schermo. C'è un'altra funzione che consente di ottenere i relativi valori. È mostrata di seguito:

inline void GetPositionXY(int &X, int &Y)
{
        X = Position.X;
        Y = Position.Y;
}

Tornando alla classe C_OrderView, abbiamo un punto interessante che merita attenzione:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong           ticket;
        double          price, pp, pt, ps;
        eHLineTrade     hl;
                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        MoveTo(Mouse.GetButtonStatus());
                        break;

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

}

La funzione MoveTo può essere vista poco prima in questa funzione. Fa anche parte della classe C_OrderView. Ma più importante è la funzione Mouse.GetButtonsStatus . Questa funzione restituisce lo stato dei pulsanti e dei tasti associati agli eventi del mouse.

Questa funzione Mouse.GetButtonStatus è mostrata di seguito:

inline uint GetButtonStatus(void) const
{
        return Position.ButtonsStatus;
}

Questa è una riga che restituisce una variabile contenente i valori registrati dall'ultimo evento del mouse. Ora andremo al codice che registra questo valore. Ma prima, diamo un'occhiata al codice di inizializzazione del mouse, perché dovrebbe dire all'EA che vogliamo inizializzare il mouse e che d'ora in poi l'EA gestirà svariate cose relative al mouse. Il codice responsabile di ciò è mostrato di seguito:

// ... Other things ....

input group "Mouse"
input color     user50 = clrBlack;      //Price line
input color     user51 = clrDarkGreen;  //Positive move
input color     user52 = clrMaroon;     //Negative move
//+------------------------------------------------------------------+

// ... General information ...

//+------------------------------------------------------------------+
int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        Mouse.Init(user50, user51, user52);

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

Quindi, dobbiamo definire tre colori che il sistema utilizzerà. Questi colori dovrebbero essere scelti in modo che i dati siano chiari e visibili sul grafico. Dai un'occhiata al codice di Mouse.Init per capirne un po' di più. Può essere visto sotto.

void Init(color c1, color c2, color c3)
{
        m_Infos.cor01 = c1;
        m_Infos.cor02 = c2;
        m_Infos.cor03 = c3;
        if (m_Infos.szNameObjH != NULL) return;
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_MOUSE_MOVE, true);
        ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, false);
        m_Infos.szNameObjH = "H" + (string)MathRand();
        m_Infos.szNameObjV = "V" + (string)MathRand();
        m_Infos.szNameObjT = "T" + (string)MathRand();
        m_Infos.szNameObjB = "B" + (string)MathRand();
        m_Infos.szNameObjI = "I" + (string)MathRand();
//---
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjH, OBJ_HLINE, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjV, OBJ_VLINE, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjT, OBJ_TREND, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjB, OBJ_BITMAP, 0, 0, 0);
        ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjI, OBJ_TEXT, 0, 0, 0);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_TOOLTIP, "\n");
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TOOLTIP, "\n");
//---
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_WIDTH, 2);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_BMPFILE, "::" + def_Fillet);
//---
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_FONT, "Lucida Console");
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_FONTSIZE, 10);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_BACK, false);
        Hide();
        Show();
}

Non c'è niente di speciale in questo codice - stiamo solo creando alcuni oggetti che devono essere usati dalla classe. Ma la parte evidenziata può creare un po' di confusione, perché se la cerchi nella classe, non troverai nessun posto dove è dichiarata. Questo perché è effettivamente dichiarata nel codice del file dell’EA insieme alle dichiarazioni di altre risorse. Più avanti raggrupperò tutto questo in un file, ma per ora rimarrà così. Quindi, se guardi il codice dell’EA, troverai le seguenti righe:

#define def_Resource    "Resources\\SubSupport.ex5"
#define def_Fillet      "Resources\\Fillet.bmp"
//+------------------------------------------------------------------+
#resource def_Resource
#resource def_Fillet

La riga mostra la risorsa che era evidenziata nel codice di inizializzazione del mouse.

Bene, abbiamo raggiunto il nostro apice all'interno di questa classe, che è chiamata dal seguente frammento:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        ChartRedraw();
}

Poi la riga evidenziata nel codice dell’EA chiamerà il seguente codice:

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        int     w = 0;
        uint    key;
        static int b1 = 0;
        static double memPrice = 0;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Position.X = (int)lparam;
                        Position.Y = (int)dparam;
                        ChartXYToTimePrice(Terminal.Get_ID(), Position.X, Position.Y, w, Position.dt, Position.price);
                        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjH, 0, 0, Position.price = Terminal.AdjustPrice(Position.price));
                        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjV, 0, Position.dt, 0);
                        key = (uint) sparam;
                        if ((key & 0x10) == 0x10)
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, m_Infos.cor01);
                                b1 = 1;
                        }
                        if (((key & 0x01) == 0x01) && (b1 == 1))
                        {
                                ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, false);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, m_Infos.cor01);
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 0, Position.dt, memPrice = Position.price);
                                b1 = 2;
                        }
                        if (((key & 0x01) == 0x01) && (b1 == 2))
                        {
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 1, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
                                ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TEXT, StringFormat("%.2f ", Position.price - memPrice));
                                ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjI, 0, Position.dt, Position.price);
                                ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
                        }
                        if (((key & 0x01) != 0x01) && (b1 == 2))
                        {
                                b1 = 0;
                                ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, true);
                                Hide();
                                Show();
                        }
                        Position.ButtonsStatus = (b1 == 0 ? key : 0);
                        break;
        }
}

Si prega di notare che il codice sopra non è il codice pienamente implementato. Supporta e risolve solo i compiti principali per l'EA fino a questa fase di sviluppo. Per capirlo, presta attenzione a una cosa nel codice di inizializzazione del mouse - ha la seguente riga:

ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, false);

Questa linea impedisce che il mirino venga visualizzato quando si fa clic sul pulsante centrale del mouse. Ma perché impediamo la creazione di mirini? Per capirlo, diamo un'occhiata alla seguente gif:

Questo è il grafico WDO, si sposta da 0,5 a 0,5. Ma quando proviamo a fare un'analisi, vediamo che non abbiamo molta precisione, mentre in alcuni casi è importante avere una certa precisione per condurre l'analisi. Ma lo strumento mirino in MetaTrader 5 non è abbastanza adeguato per casi specifici. In questo caso, dovremmo ricorrere a un nuovo sistema e quindi forziamo MetaTrader 5 a smettere di creare mirini quando l'EA è in esecuzione. Invece, creiamo i nostri mirini per eseguire l'analisi. Questo ci consente di aggiungere dati e valori che sono più rilevanti per noi e presentarli nel modo che riteniamo sia più appropriato. Questo può essere visto nella figura sottostante, che mostra il risultato dell'utilizzo del sistema di modellazione dei dati con l'EA in esecuzione.


Come puoi vedere, i valori specificati corrispondono ai valori esatti del movimento. Inoltre, abbiamo un'indicazione visiva: se il valore è positivo, l'indicazione diventa verde, se negativa, allora l'indicazione diventa rossa. Viene creata una traccia e diventano visibili anche i punti iniziale e finale. Ma, come ho già accennato, il sistema non è ancora completo. Puoi ancora apportare miglioramenti se lo desideri e ne hai bisogno. Fino a quando non uscirà una nuova versione della classe C_Mouse, puoi migliorare questa versione e avere più dati di cui potresti aver bisogno. Ma per fare questo, devi capire come funzionano tutte le cose , perciò diamo un'occhiata più da vicino al codice del messaggio della classe C_Mouse. 


Comprensione del codice DispathMessage della classe C_Mouse

Il codice inizia catturando e regolando i valori delle variabili della posizione del mouse. È fatto nel codice seguente:

Position.X = (int)lparam;
Position.Y = (int)dparam;
ChartXYToTimePrice(Terminal.Get_ID(), Position.X, Position.Y, w, Position.dt, Position.price);

I valori della posizione sono riportati dalla piattaforma MetaTrader 5, ma i valori in realtà provengono dal sistema operativo e sono nelle coordinate dello schermo, cioè X e Y. Ma dobbiamo convertirli in coordinate del grafico e per questo utilizziamo la funzione ChartXYToTimePrice , disponibile in MQL5, che semplifica enormemente le nostre vite.

Una volta fatto ciò, spostiamo le linee di prezzo e di tempo.

ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjH, 0, 0, Position.price = Terminal.AdjustPrice(Position.price));
ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjV, 0, Position.dt, 0);

Ma la linea del tempo è inizialmente invisibile per noi, quindi non possiamo vederla sul grafico. Successivamente, acquisiamo lo stato del mouse

key = (uint) sparam;

Fin qui tutto bene. Ora facciamo quanto segue: controlleremo se il pulsante centrale è stato premuto. Se lo è, la linea del tempo diventa visibile sul grafico. Questo è implementato nel codice seguente:

if ((key & 0x10) == 0x10)
{
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, m_Infos.cor01);
        b1 = 1;
}

A tale scopo, utilizziamo la variabile static per memorizzare questo evento, quindi d'ora in poi nessun altro evento sarà accettato e gestito dall'EA. Si occuperà dello studio che vogliamo fare sul grafico. Ma infatti, lo studio parte solo quando premiamo il tasto sinistro del mouse, ovvero utilizzo la stessa modalità di lavoro già nota a tutti gli utenti della piattaforma MetaTrader 5 per la ricerca. Questo è il modo più appropriato, poiché se l'utente deve imparare un nuovo modo di fare linee di ricerca, potrebbe rinunciare al sistema. L'EA quindi attende questo clic sinistro, che viene eseguito dal seguente codice

if (((key & 0x01) == 0x01) && (b1 == 1))
{
        ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, false);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, m_Infos.cor01);
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 0, Position.dt, memPrice = Position.price);
        b1 = 2;
}

quando si verifica un clic, il sistema di movimento del grafico è bloccato. Quindi presentiamo una trendline che indica i punti di analisi. Poi il sistema passa al passaggio successivo, che può essere visto da un nuovo valore in b1. Ora è in effetti la parte in cui puoi aggiungere ulteriori informazioni o inserire ciò che ritieni più rilevante. Qui sto solo dimostrando il sistema, ma sentiti libero di inserire quello che vuoi. Questo dovrebbe essere fatto come mostrato di seguito:

if (((key & 0x01) == 0x01) && (b1 == 2))
{
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjT, 1, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, (memPrice > Position.price ? m_Infos.cor03 : m_Infos.cor02));
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, 0, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
        ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TEXT, StringFormat("%.2f ", Position.price - memPrice));
        ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjI, 0, Position.dt, Position.price);
        ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_ANCHOR, (memPrice > Position.price ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER));
}

Presta attenzione alla linea evidenziata, perché è lì che il valore che vedi nella schermata del grafico viene visualizzato e calcolato. Puoi aggiungere altre informazioni utili lì. Il funzionamento di questa parte fa sì che i dati vengano calcolati e presentati mentre viene premuto il tasto sinistro. Quindi questo è lo stesso comportamento predefinito in MetaTrader 5, ma i valori saranno regolati e modellati in accordo con i tuoi desideri e bisogni.

Ora dobbiamo fare un altro test, che è mostrato di seguito.

if (((key & 0x01) != 0x01) && (b1 == 2))
{
        b1 = 0;
        ChartSetInteger(Terminal.Get_ID(), CHART_MOUSE_SCROLL, true);
        Hide();
        Show();
}

Dopo aver rilasciato il tasto sinistro del mouse, il grafico viene liberato e può essere trascinato, e tutti quegli elementi che sono stati utilizzati per creare l'analisi vengono nascosti, solo la linea del prezzo ritorna nuovamente visibile. E infine, abbiamo l'ultima parte di codice, che è mostrata di seguito:

Position.ButtonsStatus = (b1 == 0 ? key : 0);

Se non viene chiamato nessuno studio, lo stato del tasto del mouse viene memorizzato e può essere utilizzato altrove nell'EA, ma se è stato chiamato uno studio, viene utilizzato un valore NULL come dati di stato, quindi non sarà possibile creare ordini, o cambiare la loro posizione.

Nel video qui sotto, puoi vedere come funziona effettivamente questa traccia e come regola il volume sullo schermo. L'indicatore aiuta molto e sarà fantastico se impari ad usarlo correttamente. Insieme a Times & Trade formano un doppio strumento di analisi del rumore, che è uno dei metodi di trading più avanzati sul mercato.




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

File allegati |
EA_-_Mouse.zip (5986.31 KB)
Sviluppare un Expert Advisor per il trading da zero (Parte 15): Accesso ai dati sul web (I) Sviluppare un Expert Advisor per il trading da zero (Parte 15): Accesso ai dati sul web (I)
Come accedere ai dati online tramite MetaTrader 5? Ci sono molti siti Web e luoghi sul Web, con un'enorme quantità di informazioni. Quello che devi sapere è dove cercare e come utilizzare nel modo migliore queste informazioni.
Scienza dei Dati e Apprendimento Automatico (Parte 05): Alberi Decisionali Scienza dei Dati e Apprendimento Automatico (Parte 05): Alberi Decisionali
Gli alberi decisionali imitano il modo in cui gli esseri umani pensano nel classificare i dati. Vediamo come costruire alberi e utilizzarli per classificare e prevedere alcuni dati. L'obiettivo principale dell'algoritmo degli alberi decisionali è separare i dati con impurità in nodi puri o vicini.
Scopri come progettare un sistema di trading tramite Ichimoku Scopri come progettare un sistema di trading tramite Ichimoku
Ecco un nuovo articolo della nostra serie su come progettare un sistema di trading con gli indicatori più comuni, parleremo in dettaglio dell'indicatore Ichimoku e di come progettare un sistema di trading con questo indicatore.
Scienza dei Dati e Apprendimento Automatico (Parte 04): Predire l'Attuale Crollo del Mercato Azionario Scienza dei Dati e Apprendimento Automatico (Parte 04): Predire l'Attuale Crollo del Mercato Azionario
In questo articolo cercherò di utilizzare il nostro modello logistico per prevedere il crollo del mercato azionario basato sui fondamentali dell'economia statunitense, NETFLIX e APPLE sono i titoli su cui ci concentreremo. Utilizzando i precedenti crolli del mercato del 2019 e 2020 vediamo come funzionerà il nostro modello nelle attuali sventure e tenebre.